spinel_test-unit 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +5 -1
- data/lib/spinel_test/unit/cli.rb +42 -10
- data/lib/spinel_test/unit/diff.rb +5 -1
- data/lib/spinel_test/unit/generator.rb +5 -1
- data/lib/spinel_test/unit/runner.rb +24 -3
- data/lib/spinel_test/unit/test_case.rb +31 -25
- data/lib/spinel_test/unit/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef62e948140f04c676b0709a5780239556c38dd521fbdd0fce4968f82b643fe4
|
|
4
|
+
data.tar.gz: 3212cc091c366bc33b3fc1854f3fbd6a4a164248baa20cdc3816d3896dcd8519
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9dcc12b871aa16d10cea18c038de5d335a4e07121098f3de6b3449be0020e82274d39593698b79a39457dd9049ccdb1fcc06c740458d536151dd592ba93b66e9
|
|
7
|
+
data.tar.gz: 588963b23dc23425e40721efad1070800e577aec65d9dbba959aa537f3055ddb0a06db18df1671ae484733d5d7055f2f0da8e102213cbc2598897a618be0e43b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
* Add `match:` to `assert_raise` to check the exception message, as in `assert_match` (a Regexp, or a String meaning a substring)
|
|
6
|
+
* `assert_raise` passes only for the given class itself, not a subclass, as in test-unit. Use the new `assert_raise_kind_of` to also accept a subclass
|
|
7
|
+
* Add `-t`/`--testcase CLASS` to run only the tests of the class named exactly CLASS (`-n FooTest#` also selects `MyFooTest`)
|
|
8
|
+
* `-n` and `-t` take a regexp written as `/regexp/`, as in test-unit
|
|
9
|
+
|
|
3
10
|
## 0.2.0
|
|
4
11
|
|
|
5
12
|
* Report where a test failed: `at file:line` lines after the message (the test method's line with Spinel, the filtered backtrace with `--ruby`)
|
data/README.md
CHANGED
|
@@ -39,6 +39,9 @@ end
|
|
|
39
39
|
$ bundle exec spinel_test-unit # test/**/*_test.rb and test/**/test_*.rb
|
|
40
40
|
$ bundle exec spinel_test-unit test/calc_test.rb
|
|
41
41
|
$ bundle exec spinel_test-unit -n test_add # only tests whose "Class#method" name includes it
|
|
42
|
+
$ bundle exec spinel_test-unit -n '/#test_add\z/' # or matches the regexp
|
|
43
|
+
$ bundle exec spinel_test-unit -t CalcTest # only the tests of the class named exactly CalcTest
|
|
44
|
+
$ bundle exec spinel_test-unit -t '/\ACalc/' # or of the classes whose name matches the regexp
|
|
42
45
|
$ bundle exec spinel_test-unit --ruby # run the same tests with the current Ruby, no Spinel needed
|
|
43
46
|
```
|
|
44
47
|
|
|
@@ -93,7 +96,8 @@ Available assertions:
|
|
|
93
96
|
- `assert_include(collection, item, message = nil)` (`assert_includes` is an alias)
|
|
94
97
|
- `assert_not_include(collection, item, message = nil)`
|
|
95
98
|
- `refute(test, message = nil)`
|
|
96
|
-
- `assert_raise(klass) { ... }` (passes for `klass`
|
|
99
|
+
- `assert_raise(klass, match: nil) { ... }` (passes for `klass` itself, not a subclass, as in test-unit; returns the exception; assertion failures raised inside the block propagate). With `match:`, the exception's message must also match it, as in `assert_match` (a Regexp, or a String meaning a substring)
|
|
100
|
+
- `assert_raise_kind_of(klass, match: nil) { ... }` (as `assert_raise`, but also passes for a subclass of `klass`)
|
|
97
101
|
|
|
98
102
|
`setup` and `teardown` hooks are supported.
|
|
99
103
|
|
data/lib/spinel_test/unit/cli.rb
CHANGED
|
@@ -10,7 +10,7 @@ require_relative "toolchain"
|
|
|
10
10
|
|
|
11
11
|
module SpinelTest
|
|
12
12
|
module Unit
|
|
13
|
-
# spinel_test-unit [PATH...] [-n NAME] [-I DIR] [--ruby]
|
|
13
|
+
# spinel_test-unit [PATH...] [-n NAME] [-t CLASS] [-I DIR] [--ruby]
|
|
14
14
|
#
|
|
15
15
|
# Scans the test files with Prism, generates a main.rb that calls every
|
|
16
16
|
# test, compiles it with Spinel and runs the binary. With --ruby it runs the
|
|
@@ -25,7 +25,7 @@ module SpinelTest
|
|
|
25
25
|
@root = File.expand_path(root)
|
|
26
26
|
@paths = []
|
|
27
27
|
@filter = nil
|
|
28
|
-
@
|
|
28
|
+
@testcase = nil
|
|
29
29
|
@ruby = false
|
|
30
30
|
@includes = []
|
|
31
31
|
@exit = nil
|
|
@@ -33,13 +33,17 @@ module SpinelTest
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
USAGE = <<~TEXT
|
|
36
|
-
Usage: spinel_test-unit [PATH...] [-n NAME] [-I DIR] [--ruby]
|
|
36
|
+
Usage: spinel_test-unit [PATH...] [-n NAME] [-t CLASS] [-I DIR] [--ruby]
|
|
37
37
|
|
|
38
38
|
Compiles the tests with Spinel and runs them (with --ruby, runs them with the
|
|
39
39
|
current Ruby instead).
|
|
40
40
|
|
|
41
41
|
PATH test file or directory (default: test)
|
|
42
|
-
-n, --name NAME run only the tests whose "Class#test_name" includes NAME
|
|
42
|
+
-n, --name NAME run only the tests whose "Class#test_name" includes NAME,
|
|
43
|
+
or matches it when NAME is /regexp/
|
|
44
|
+
-t, --testcase CLASS
|
|
45
|
+
run only the tests of the class named CLASS (exact match),
|
|
46
|
+
or whose class name matches it when CLASS is /regexp/
|
|
43
47
|
-I DIR add DIR to the load path (repeatable)
|
|
44
48
|
--ruby run with the current Ruby instead of compiling with Spinel
|
|
45
49
|
-h, --help show this help
|
|
@@ -93,7 +97,9 @@ module SpinelTest
|
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
def run_command(command)
|
|
96
|
-
args =
|
|
100
|
+
args = []
|
|
101
|
+
args += ["-n", @filter] if @filter
|
|
102
|
+
args += ["-t", @testcase] if @testcase
|
|
97
103
|
system(*command, *args) ? 0 : ($?.exitstatus || 1)
|
|
98
104
|
end
|
|
99
105
|
|
|
@@ -106,17 +112,27 @@ module SpinelTest
|
|
|
106
112
|
@exit = 0
|
|
107
113
|
return
|
|
108
114
|
when "-n", "--name"
|
|
109
|
-
if args.empty?
|
|
110
|
-
|
|
111
|
-
end
|
|
115
|
+
return usage_error("#{arg} requires a value") if args.empty?
|
|
116
|
+
|
|
112
117
|
@filter = args.shift
|
|
118
|
+
return unless valid_pattern?(@filter)
|
|
119
|
+
when "-t", "--testcase"
|
|
120
|
+
return usage_error("#{arg} requires a value") if args.empty?
|
|
121
|
+
|
|
122
|
+
@testcase = args.shift
|
|
123
|
+
return unless valid_pattern?(@testcase)
|
|
113
124
|
when "--ruby" then @ruby = true
|
|
114
125
|
when "-I"
|
|
115
126
|
return usage_error("-I requires a value") if args.empty?
|
|
116
127
|
|
|
117
128
|
@includes << File.expand_path(args.shift, @root)
|
|
118
129
|
when /\A-I(.+)\z/ then @includes << File.expand_path($1, @root)
|
|
119
|
-
when /\A--name=(.*)\z/
|
|
130
|
+
when /\A--name=(.*)\z/
|
|
131
|
+
@filter = $1
|
|
132
|
+
return unless valid_pattern?(@filter)
|
|
133
|
+
when /\A--testcase=(.*)\z/
|
|
134
|
+
@testcase = $1
|
|
135
|
+
return unless valid_pattern?(@testcase)
|
|
120
136
|
when "--" then @paths.concat(args.shift(args.size))
|
|
121
137
|
when /\A-/ then return usage_error("unknown option: #{arg}")
|
|
122
138
|
else @paths << arg
|
|
@@ -124,6 +140,16 @@ module SpinelTest
|
|
|
124
140
|
end
|
|
125
141
|
end
|
|
126
142
|
|
|
143
|
+
def valid_pattern?(value)
|
|
144
|
+
return true unless value.length >= 2 && value.start_with?("/") && value.end_with?("/")
|
|
145
|
+
|
|
146
|
+
Regexp.new(value[1..-2])
|
|
147
|
+
true
|
|
148
|
+
rescue RegexpError => e
|
|
149
|
+
usage_error("invalid regexp #{value}: #{e.message}")
|
|
150
|
+
false
|
|
151
|
+
end
|
|
152
|
+
|
|
127
153
|
def usage_error(message)
|
|
128
154
|
warn "spinel_test-unit: #{message}"
|
|
129
155
|
warn USAGE
|
|
@@ -245,7 +271,13 @@ module SpinelTest
|
|
|
245
271
|
|
|
246
272
|
def compile(spinel, main, bin, entries)
|
|
247
273
|
cmd = [spinel] + Toolchain.compile_flags + include_dirs(entries).flat_map { |d| ["-I", d] } + [main, "-o", bin]
|
|
248
|
-
|
|
274
|
+
begin
|
|
275
|
+
output, status = Open3.capture2e(*cmd)
|
|
276
|
+
rescue SystemCallError => e
|
|
277
|
+
hint = ENV["SPINEL"].to_s.empty? ? "" : " (check SPINEL)"
|
|
278
|
+
warn "spinel_test-unit: cannot run Spinel: #{e.message}#{hint}"
|
|
279
|
+
return false
|
|
280
|
+
end
|
|
249
281
|
unless status.success?
|
|
250
282
|
warn output unless output.empty?
|
|
251
283
|
warn "spinel_test-unit: compilation failed (#{cmd.join(" ")})"
|
|
@@ -59,10 +59,14 @@ module SpinelTest
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def ambiguous?(line)
|
|
62
|
-
line == "" || line == "..." ||
|
|
62
|
+
line == "" || line == "..." || control?(line) || line.start_with?("\"") ||
|
|
63
63
|
line.start_with?(" ", "\t") || line.end_with?(" ", "\t")
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
def control?(line)
|
|
67
|
+
line.bytes.any? { |b| (b < 32 && b != 9) || b == 127 }
|
|
68
|
+
end
|
|
69
|
+
|
|
66
70
|
def edits
|
|
67
71
|
lengths = lcs_lengths
|
|
68
72
|
entries = []
|
|
@@ -9,7 +9,7 @@ module SpinelTest
|
|
|
9
9
|
out = +"require \"spinel_test/unit\"\n"
|
|
10
10
|
files.each { |f| out << "require #{f[:require].dump}\n" }
|
|
11
11
|
out << ARGV_PARSER
|
|
12
|
-
out << "runner = SpinelTest::Unit::Runner.new(filter, #{root.dump})\n"
|
|
12
|
+
out << "runner = SpinelTest::Unit::Runner.new(filter, #{root.dump}, testcase)\n"
|
|
13
13
|
files.each do |f|
|
|
14
14
|
f[:classes].each do |klass|
|
|
15
15
|
locations = klass.locations || {}
|
|
@@ -25,11 +25,15 @@ module SpinelTest
|
|
|
25
25
|
|
|
26
26
|
ARGV_PARSER = <<~RUBY
|
|
27
27
|
filter = ""
|
|
28
|
+
testcase = ""
|
|
28
29
|
i = 0
|
|
29
30
|
while i < ARGV.length
|
|
30
31
|
if ARGV[i] == "-n" && i + 1 < ARGV.length
|
|
31
32
|
filter = ARGV[i + 1]
|
|
32
33
|
i += 1
|
|
34
|
+
elsif ARGV[i] == "-t" && i + 1 < ARGV.length
|
|
35
|
+
testcase = ARGV[i + 1]
|
|
36
|
+
i += 1
|
|
33
37
|
end
|
|
34
38
|
i += 1
|
|
35
39
|
end
|
|
@@ -6,9 +6,10 @@ module SpinelTest
|
|
|
6
6
|
MAX_FRAMES = 3
|
|
7
7
|
FRAMEWORK_DIR = File.dirname(File.expand_path(__FILE__)) + "/"
|
|
8
8
|
|
|
9
|
-
def initialize(filter = "", root = "")
|
|
9
|
+
def initialize(filter = "", root = "", testcase = "")
|
|
10
10
|
@filter = filter
|
|
11
11
|
@root = root
|
|
12
|
+
@testcase = testcase
|
|
12
13
|
@tests = 0
|
|
13
14
|
@assertions = 0
|
|
14
15
|
@failures = 0
|
|
@@ -17,7 +18,20 @@ module SpinelTest
|
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def selected?(name)
|
|
20
|
-
|
|
21
|
+
index = name.index("#")
|
|
22
|
+
klass = index ? name[0, index] : name
|
|
23
|
+
matches?(@testcase, klass, true) && matches?(@filter, name, false)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def matches?(filter, text, exact)
|
|
27
|
+
return true if filter == ""
|
|
28
|
+
return Regexp.new(filter[1, filter.length - 2]).match?(text) if pattern?(filter)
|
|
29
|
+
|
|
30
|
+
exact ? text == filter : text.include?(filter)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def pattern?(filter)
|
|
34
|
+
filter.length >= 2 && filter.start_with?("/") && filter.end_with?("/")
|
|
21
35
|
end
|
|
22
36
|
|
|
23
37
|
# Runs one test: setup, the block (which calls the test method), teardown.
|
|
@@ -120,6 +134,13 @@ module SpinelTest
|
|
|
120
134
|
e.is_a?(SystemExit) || e.is_a?(Interrupt)
|
|
121
135
|
end
|
|
122
136
|
|
|
137
|
+
def filter_description
|
|
138
|
+
parts = []
|
|
139
|
+
parts.push("testcase: " + @testcase) if @testcase != ""
|
|
140
|
+
parts.push("filter: " + @filter) if @filter != ""
|
|
141
|
+
parts.empty? ? "" : " (" + parts.join(", ") + ")"
|
|
142
|
+
end
|
|
143
|
+
|
|
123
144
|
def passed?
|
|
124
145
|
@failures == 0 && @errors == 0
|
|
125
146
|
end
|
|
@@ -138,7 +159,7 @@ module SpinelTest
|
|
|
138
159
|
puts ""
|
|
139
160
|
puts summary
|
|
140
161
|
if @tests == 0
|
|
141
|
-
puts "No tests were run#{
|
|
162
|
+
puts "No tests were run#{filter_description}"
|
|
142
163
|
return 1
|
|
143
164
|
end
|
|
144
165
|
passed? ? 0 : 1
|
|
@@ -38,55 +38,42 @@ module SpinelTest
|
|
|
38
38
|
@assertions += 1
|
|
39
39
|
return true if expected == actual
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
msg += diff_section(expected, actual)
|
|
43
|
-
msg = message + "\n" + msg if message
|
|
44
|
-
raise AssertionFailedError, msg
|
|
41
|
+
failure("Expected: #{expected.inspect}\n Actual: #{actual.inspect}" + diff_section(expected, actual), message)
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
def assert_not_equal(expected, actual, message = nil)
|
|
48
45
|
@assertions += 1
|
|
49
46
|
return true unless expected == actual
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
msg = message + "\n" + msg if message
|
|
53
|
-
raise AssertionFailedError, msg
|
|
48
|
+
failure("Expected #{actual.inspect} not to equal #{expected.inspect}", message)
|
|
54
49
|
end
|
|
55
50
|
|
|
56
51
|
def assert_match(pattern, string, message = nil)
|
|
57
52
|
@assertions += 1
|
|
58
53
|
return true if pattern_matches?(pattern, string)
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
msg = message + "\n" + msg if message
|
|
62
|
-
raise AssertionFailedError, msg
|
|
55
|
+
failure("Expected #{pattern.inspect} to match #{string.inspect}", message)
|
|
63
56
|
end
|
|
64
57
|
|
|
65
58
|
def assert_nil(value, message = nil)
|
|
66
59
|
@assertions += 1
|
|
67
60
|
return true if value.nil?
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
msg = message + "\n" + msg if message
|
|
71
|
-
raise AssertionFailedError, msg
|
|
62
|
+
failure("Expected nil, got #{value.inspect}", message)
|
|
72
63
|
end
|
|
73
64
|
|
|
74
65
|
def assert_not_nil(value, message = nil)
|
|
75
66
|
@assertions += 1
|
|
76
67
|
return true unless value.nil?
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
msg = message + "\n" + msg if message
|
|
80
|
-
raise AssertionFailedError, msg
|
|
69
|
+
failure("Expected a value other than nil", message)
|
|
81
70
|
end
|
|
82
71
|
|
|
83
72
|
def assert_include(collection, item, message = nil)
|
|
84
73
|
@assertions += 1
|
|
85
74
|
return true if collection.include?(item)
|
|
86
75
|
|
|
87
|
-
|
|
88
|
-
msg = message + "\n" + msg if message
|
|
89
|
-
raise AssertionFailedError, msg
|
|
76
|
+
failure("#{collection.inspect} does not include #{item.inspect}", message)
|
|
90
77
|
end
|
|
91
78
|
|
|
92
79
|
def assert_includes(collection, item, message = nil)
|
|
@@ -97,12 +84,10 @@ module SpinelTest
|
|
|
97
84
|
@assertions += 1
|
|
98
85
|
return true unless collection.include?(item)
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
msg = message + "\n" + msg if message
|
|
102
|
-
raise AssertionFailedError, msg
|
|
87
|
+
failure("#{collection.inspect} includes #{item.inspect}", message)
|
|
103
88
|
end
|
|
104
89
|
|
|
105
|
-
def assert_raise(klass)
|
|
90
|
+
def assert_raise(klass, match: nil)
|
|
106
91
|
@assertions += 1
|
|
107
92
|
begin
|
|
108
93
|
yield
|
|
@@ -110,15 +95,36 @@ module SpinelTest
|
|
|
110
95
|
# a failed assertion inside the block is not "the expected error"
|
|
111
96
|
raise e
|
|
112
97
|
rescue StandardError => e
|
|
113
|
-
return e
|
|
98
|
+
return expected_exception(e, klass, match, e.instance_of?(klass))
|
|
99
|
+
end
|
|
100
|
+
raise AssertionFailedError, "Expected #{klass} but nothing was raised"
|
|
101
|
+
end
|
|
114
102
|
|
|
115
|
-
|
|
103
|
+
def assert_raise_kind_of(klass, match: nil)
|
|
104
|
+
@assertions += 1
|
|
105
|
+
begin
|
|
106
|
+
yield
|
|
107
|
+
rescue AssertionFailedError => e
|
|
108
|
+
raise e
|
|
109
|
+
rescue StandardError => e
|
|
110
|
+
return expected_exception(e, klass, match, e.is_a?(klass))
|
|
116
111
|
end
|
|
117
112
|
raise AssertionFailedError, "Expected #{klass} but nothing was raised"
|
|
118
113
|
end
|
|
119
114
|
|
|
120
115
|
private
|
|
121
116
|
|
|
117
|
+
def failure(text, message)
|
|
118
|
+
raise AssertionFailedError, message ? message + "\n" + text : text
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def expected_exception(e, klass, match, class_matches)
|
|
122
|
+
raise AssertionFailedError, "Expected #{klass} but got #{e.class}" unless class_matches
|
|
123
|
+
return e if match.nil? || pattern_matches?(match, e.message)
|
|
124
|
+
|
|
125
|
+
raise AssertionFailedError, "Expected #{klass} with a message matching #{match.inspect} but got #{e.message.inspect}"
|
|
126
|
+
end
|
|
127
|
+
|
|
122
128
|
def diff_section(expected, actual)
|
|
123
129
|
return "" unless multiline_strings?(expected, actual)
|
|
124
130
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spinel_test-unit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuji Yaginuma
|
|
@@ -71,6 +71,7 @@ licenses:
|
|
|
71
71
|
metadata:
|
|
72
72
|
homepage_uri: https://github.com/y-yagi/spinel_test-unit
|
|
73
73
|
source_code_uri: https://github.com/y-yagi/spinel_test-unit
|
|
74
|
+
rubygems_mfa_required: 'true'
|
|
74
75
|
rdoc_options: []
|
|
75
76
|
require_paths:
|
|
76
77
|
- lib
|