megatest 0.1.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +156 -0
- data/TODO.md +17 -0
- data/exe/megatest +7 -0
- data/lib/megatest/assertions.rb +474 -0
- data/lib/megatest/backtrace.rb +70 -0
- data/lib/megatest/cli.rb +249 -0
- data/lib/megatest/compat.rb +74 -0
- data/lib/megatest/config.rb +281 -0
- data/lib/megatest/differ.rb +136 -0
- data/lib/megatest/dsl.rb +164 -0
- data/lib/megatest/executor.rb +104 -0
- data/lib/megatest/multi_process.rb +263 -0
- data/lib/megatest/output.rb +158 -0
- data/lib/megatest/patience_diff.rb +340 -0
- data/lib/megatest/pretty_print.rb +309 -0
- data/lib/megatest/queue.rb +239 -0
- data/lib/megatest/queue_monitor.rb +35 -0
- data/lib/megatest/queue_reporter.rb +42 -0
- data/lib/megatest/redis_queue.rb +459 -0
- data/lib/megatest/reporters.rb +266 -0
- data/lib/megatest/runner.rb +119 -0
- data/lib/megatest/runtime.rb +168 -0
- data/lib/megatest/selector.rb +293 -0
- data/lib/megatest/state.rb +708 -0
- data/lib/megatest/subprocess/main.rb +8 -0
- data/lib/megatest/subprocess.rb +48 -0
- data/lib/megatest/test.rb +115 -0
- data/lib/megatest/test_task.rb +132 -0
- data/lib/megatest/version.rb +5 -0
- data/lib/megatest.rb +123 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 81186c745613e5b5ffe4ef277acaed9763ea27b308a2d25c9f155d2369b15624
|
|
4
|
+
data.tar.gz: d6034a325bce53488239699554363bad937fb2c4e77a34f07e2862476ebfad0c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1086dd76f6bf6ab4ae7e0974cf2c9e760bd20b104b5cbfba3e960d8fb9f3f0980c21c814ba09619a33be89edc6dadfae4edba8885c982354192fb54298301bb6
|
|
7
|
+
data.tar.gz: 06cd253892c7f76f28cf7feddf51f5135739040c6177abd9bc63ab5567b87832675a03aac3ad060831690df03824b0f07662b9d71ca7bd92419e70b764b66fd0
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Megatest
|
|
2
|
+
|
|
3
|
+
Megatest is a test-unit like framework with a focus on usability, and designed with continuous integration in mind.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
8
|
+
|
|
9
|
+
$ bundle add megatest
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Writing Tests
|
|
14
|
+
|
|
15
|
+
Test suites are Ruby classes that inherit from `Megatest::Test`.
|
|
16
|
+
|
|
17
|
+
Test cases are be defined with the `test` macro, or for compatibility with existing test suites,
|
|
18
|
+
by defining a method starting with `test_`.
|
|
19
|
+
|
|
20
|
+
All the classic `test-unit` and `minitest` assertion methods are available:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# test/some_test.rb
|
|
24
|
+
|
|
25
|
+
class SomeTest < MyApp::Test
|
|
26
|
+
setup do
|
|
27
|
+
@user = User.new("George")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test "the truth" do
|
|
31
|
+
assert_equal true, Some.truth
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_it_works
|
|
35
|
+
assert_predicate 2, :even?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
By convention, all the `test_helper.rb` files are automatically loaded,
|
|
41
|
+
which allows to centralize dependencies and define some helpers.
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# test/test_helper.rb
|
|
45
|
+
|
|
46
|
+
require "some_dependency"
|
|
47
|
+
|
|
48
|
+
module MyApp
|
|
49
|
+
class Test < Megatest::Test
|
|
50
|
+
|
|
51
|
+
def some_helper(arg)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
It also allow to define test inside `context` blocks, to make it easier to group
|
|
58
|
+
related tests together and have them share a common name prefix.
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
class SomeTest < MyApp::Test
|
|
62
|
+
context "when on earth" do
|
|
63
|
+
test "1 is odd" do
|
|
64
|
+
App.location = "earth"
|
|
65
|
+
assert_predicate 1, :odd?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
test "2 is even" do
|
|
69
|
+
App.location = "earth"
|
|
70
|
+
assert_predicate 2, :even?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Note however that context blocks aren't test suites, they don't have their own setup or teardown
|
|
77
|
+
blocks, nor their own namespaces.
|
|
78
|
+
|
|
79
|
+
### Command Line
|
|
80
|
+
|
|
81
|
+
Contrary to many alternatives, `megatest` provide a convenient CLI interface to easily run specific tests.
|
|
82
|
+
|
|
83
|
+
Run all tests in a directory:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
$ megatest # Run all tests in `test/`
|
|
87
|
+
$ megatest test/integration
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Runs tests using 8 processes:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
$ megatest -j 8
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Run a test at the specific line:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
$ megatest test/some_test.rb:42 test/other_test.rb:24
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Run all tests matching a pattern:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
$ megatest test/some_test.rb:/matching
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For more detailed usage, run `megatest --help`.
|
|
109
|
+
|
|
110
|
+
### CI Parallelization
|
|
111
|
+
|
|
112
|
+
Megatest offer multiple feature to allow running test suites in parallel across
|
|
113
|
+
many CI jobs.
|
|
114
|
+
|
|
115
|
+
#### Sharding
|
|
116
|
+
|
|
117
|
+
The simplest way is sharding. Each worker will run its share of the test cases.
|
|
118
|
+
|
|
119
|
+
Many CI systems provide a way to run the same command on multiple nodes,
|
|
120
|
+
and will generally expose environment variables to help split the workload.
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
- label: "Run Unit Tests"
|
|
124
|
+
run: megatest --workers-count $CI_NODE_INDEX --worker-id $CI_NODE_TOTAL
|
|
125
|
+
parallel: 8
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Note that Megatest makes no effort at balancing the shards as it has no
|
|
129
|
+
information about how long each individual test case is expected to take.
|
|
130
|
+
However it does shard test cases individually, so it avoids the most common issue which is
|
|
131
|
+
very large test suites containing lots of slow test cases being sharded as one unit.
|
|
132
|
+
|
|
133
|
+
If you are using CircleCI, Buildkite or HerokuCI, the workers count and worker id
|
|
134
|
+
will be automatically inferred from the environment.
|
|
135
|
+
|
|
136
|
+
### Redis Distribution
|
|
137
|
+
|
|
138
|
+
A more efficient way to parallelize tests on CI is to use a Redis server to act as a queue.
|
|
139
|
+
|
|
140
|
+
This allow to efficiently and dynamically ensure a near perfect test case balance across all
|
|
141
|
+
the workers. And if for some reason one of the worker is lost or crashes, no test is lost,
|
|
142
|
+
which for builds with hundreds of parallel jobs, is essential for stability.
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
- label: "Run Unit Tests"
|
|
146
|
+
run: megatest --queue redis://redis-ci.example.com --build-id $CI_BUILD_ID --worker-id $CI_JOB_ID
|
|
147
|
+
parallel: 128
|
|
148
|
+
soft_fail: true # Doesn't matter if they fail or crash, only the "Results" job status matters
|
|
149
|
+
|
|
150
|
+
- label: "Unit Test Results"
|
|
151
|
+
run: megatest report --queue redis://redis-ci.example.com --build-id $CI_BUILD_ID
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Contributing
|
|
155
|
+
|
|
156
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/byroot/megatest.
|
data/TODO.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
### Wants
|
|
2
|
+
|
|
3
|
+
- Test leak bisect
|
|
4
|
+
- See ci-queue bisect.
|
|
5
|
+
|
|
6
|
+
- List slow tests
|
|
7
|
+
- Not just X slowest test, but up to X tests that are significantly slower than average.
|
|
8
|
+
- Exclude them with `:slow` tag.
|
|
9
|
+
|
|
10
|
+
- `-j` for forkless environments (Windows / JRuby / TruffleRuby)
|
|
11
|
+
|
|
12
|
+
- `minitest/mocks`
|
|
13
|
+
- I'm not very fond of those, but could be worth offering it as a side gem or something, for easier transition.
|
|
14
|
+
|
|
15
|
+
### Maybe
|
|
16
|
+
|
|
17
|
+
- RSpec style pending?
|
data/exe/megatest
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Megatest
|
|
4
|
+
class Assertion < Exception
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class NoAssertion < Assertion
|
|
8
|
+
def initialize(message = "No assertions performed")
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
DidNotRun = Class.new(Assertion)
|
|
14
|
+
|
|
15
|
+
class LostTest < Assertion
|
|
16
|
+
def initialize(test_id)
|
|
17
|
+
super("#{test_id} never completed. Might be caused by a crash or early exit?")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Skip = Class.new(Assertion)
|
|
22
|
+
|
|
23
|
+
class UnexpectedError < Assertion
|
|
24
|
+
attr_reader :cause
|
|
25
|
+
|
|
26
|
+
def initialize(cause)
|
|
27
|
+
super("Unexpected exception")
|
|
28
|
+
@cause = cause
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def backtrace
|
|
32
|
+
cause.backtrace
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def backtrace_locations
|
|
36
|
+
cause.backtrace_locations
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Assertions
|
|
41
|
+
def pass
|
|
42
|
+
@__m.assert {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def assert(result, msg = nil, message: nil)
|
|
46
|
+
message = @__m.msg(msg, message)
|
|
47
|
+
@__m.assert do
|
|
48
|
+
return if result
|
|
49
|
+
|
|
50
|
+
if message
|
|
51
|
+
@__m.fail(message)
|
|
52
|
+
else
|
|
53
|
+
@__m.fail(message, "Expected", @__m.pp(result), "to be truthy")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def refute(result, msg = nil, message: nil)
|
|
59
|
+
message = @__m.msg(msg, message)
|
|
60
|
+
@__m.assert do
|
|
61
|
+
return unless result
|
|
62
|
+
|
|
63
|
+
if message
|
|
64
|
+
@__m.fail(message)
|
|
65
|
+
else
|
|
66
|
+
@__m.fail(message, "Expected", @__m.pp(result), "to be falsy")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def assert_nil(actual, msg = nil, message: nil)
|
|
72
|
+
message = @__m.msg(msg, message)
|
|
73
|
+
@__m.assert do
|
|
74
|
+
unless nil.equal?(actual)
|
|
75
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to be nil")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def refute_nil(actual, msg = nil, message: nil)
|
|
81
|
+
message = @__m.msg(msg, message)
|
|
82
|
+
@__m.assert do
|
|
83
|
+
if nil.equal?(actual)
|
|
84
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to not be nil")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def assert_equal(expected, actual, msg = nil, message: nil, allow_nil: false)
|
|
90
|
+
message = @__m.msg(msg, message)
|
|
91
|
+
@__m.assert do
|
|
92
|
+
if !allow_nil && nil == expected
|
|
93
|
+
@__m.fail(nil, "Use assert_nil if expecting nil, or pass `allow_nil: true`")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if expected != actual
|
|
97
|
+
@__m.fail(
|
|
98
|
+
message,
|
|
99
|
+
@__m.diff(expected, actual) ||
|
|
100
|
+
"Expected: #{@__m.pp(expected)}\n" \
|
|
101
|
+
" Actual: #{@__m.pp(actual)}",
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def refute_equal(expected, actual, msg = nil, message: nil, allow_nil: false)
|
|
108
|
+
message = @__m.msg(msg, message)
|
|
109
|
+
@__m.assert do
|
|
110
|
+
if !allow_nil && nil == expected && !@__m.minitest_compatibility?
|
|
111
|
+
@__m.fail(nil, "Use refute_nil if expecting to not be nil, or pass `allow_nil: true`")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if expected == actual
|
|
115
|
+
@__m.fail(message, "Expected", @__m.pp(expected), "to not equal", @__m.pp(actual))
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def assert_includes(collection, object, msg = nil, message: nil)
|
|
121
|
+
message = @__m.msg(msg, message)
|
|
122
|
+
@__m.assert do
|
|
123
|
+
unless collection.include?(object)
|
|
124
|
+
@__m.fail message, "Expected", @__m.pp(collection), "to include", @__m.pp(object)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def refute_includes(collection, object, msg = nil, message: nil)
|
|
130
|
+
message = @__m.msg(msg, message)
|
|
131
|
+
@__m.assert do
|
|
132
|
+
if collection.include?(object)
|
|
133
|
+
@__m.fail message, "Expected", @__m.pp(collection), "to not include", @__m.pp(object)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def assert_empty(object, msg = nil, message: nil)
|
|
139
|
+
message = @__m.msg(msg, message)
|
|
140
|
+
@__m.assert do
|
|
141
|
+
unless object.empty?
|
|
142
|
+
@__m.fail message, "Expected", @__m.pp(object), "to be empty"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def refute_empty(object, msg = nil, message: nil)
|
|
148
|
+
message = @__m.msg(msg, message)
|
|
149
|
+
@__m.assert do
|
|
150
|
+
if object.empty?
|
|
151
|
+
@__m.fail message, "Expected", @__m.pp(object), "to not be empty"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def assert_instance_of(klass, actual, msg = nil, message: nil)
|
|
157
|
+
message = @__m.msg(msg, message)
|
|
158
|
+
@__m.assert do
|
|
159
|
+
unless actual.instance_of?(klass)
|
|
160
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to be an instance of", @__m.pp(klass), "not", @__m.pp(actual.class))
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def refute_instance_of(klass, actual, msg = nil, message: nil)
|
|
166
|
+
message = @__m.msg(msg, message)
|
|
167
|
+
@__m.assert do
|
|
168
|
+
if actual.instance_of?(klass)
|
|
169
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to not be an instance of", @__m.pp(klass))
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def assert_kind_of(klass, actual, msg = nil, message: nil)
|
|
175
|
+
message = @__m.msg(msg, message)
|
|
176
|
+
@__m.assert do
|
|
177
|
+
unless actual.kind_of?(klass)
|
|
178
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to be a kind of", @__m.pp(klass), "not", @__m.pp(actual.class))
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def refute_kind_of(klass, actual, msg = nil, message: nil)
|
|
184
|
+
message = @__m.msg(msg, message)
|
|
185
|
+
@__m.assert do
|
|
186
|
+
if actual.kind_of?(klass)
|
|
187
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to not be a kind of", @__m.pp(klass))
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def assert_predicate(actual, predicate, msg = nil, message: nil)
|
|
193
|
+
message = @__m.msg(msg, message)
|
|
194
|
+
@__m.assert do
|
|
195
|
+
unless @__m.expect_no_failures { actual.__send__(predicate) }
|
|
196
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to be #{predicate}")
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def refute_predicate(actual, predicate, msg = nil, message: nil)
|
|
202
|
+
message = @__m.msg(msg, message)
|
|
203
|
+
@__m.assert do
|
|
204
|
+
if @__m.expect_no_failures { actual.__send__(predicate) }
|
|
205
|
+
@__m.fail(message, "Expected", @__m.pp(actual), "to not be #{predicate}")
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def assert_match(original_matcher, obj, msg = nil, message: nil)
|
|
211
|
+
message = @__m.msg(msg, message)
|
|
212
|
+
@__m.assert do
|
|
213
|
+
matcher = if ::String === original_matcher
|
|
214
|
+
::Regexp.new(::Regexp.escape(original_matcher))
|
|
215
|
+
else
|
|
216
|
+
original_matcher
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
unless match = matcher.match(obj)
|
|
220
|
+
@__m.fail(message, "Expected", @__m.pp(original_matcher), "to match", @__m.pp(obj))
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
match
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def refute_match(original_matcher, obj, msg = nil, message: nil)
|
|
228
|
+
message = @__m.msg(msg, message)
|
|
229
|
+
@__m.assert do
|
|
230
|
+
matcher = if ::String === original_matcher
|
|
231
|
+
::Regexp.new(::Regexp.escape(original_matcher))
|
|
232
|
+
else
|
|
233
|
+
original_matcher
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
if matcher.match?(obj)
|
|
237
|
+
@__m.fail(message, "Expected", @__m.pp(original_matcher), "to not match", @__m.pp(obj))
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def assert_respond_to(object, method, msg = nil, message: nil, include_all: false)
|
|
243
|
+
message = @__m.msg(msg, message)
|
|
244
|
+
@__m.assert do
|
|
245
|
+
unless object.respond_to?(method, include_all)
|
|
246
|
+
@__m.fail(message, "Expected", @__m.pp(object), "to respond to :#{method}")
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def refute_respond_to(object, method, msg = nil, message: nil, include_all: false)
|
|
252
|
+
message = @__m.msg(msg, message)
|
|
253
|
+
@__m.assert do
|
|
254
|
+
if object.respond_to?(method, include_all)
|
|
255
|
+
@__m.fail(message, "Expected", @__m.pp(object), "to not respond to :#{method}")
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def assert_same(expected, actual, msg = nil, message: nil)
|
|
261
|
+
message = @__m.msg(msg, message)
|
|
262
|
+
@__m.assert do
|
|
263
|
+
unless expected.equal?(actual)
|
|
264
|
+
@__m.fail message, begin
|
|
265
|
+
actual_pp = @__m.pp(actual)
|
|
266
|
+
expected_pp = @__m.pp(expected)
|
|
267
|
+
if actual_pp == expected_pp
|
|
268
|
+
actual_pp += " (id: #{actual.object_id})"
|
|
269
|
+
expected_pp += " (id: #{expected.object_id})"
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
"Expected #{actual_pp}\n" \
|
|
273
|
+
"To be the same as #{expected_pp}"
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def refute_same(expected, actual, msg = nil, message: nil)
|
|
280
|
+
message = @__m.msg(msg, message)
|
|
281
|
+
@__m.assert do
|
|
282
|
+
if expected.equal?(actual)
|
|
283
|
+
@__m.fail message, begin
|
|
284
|
+
actual_pp = @__m.pp(actual)
|
|
285
|
+
expected_pp = @__m.pp(expected)
|
|
286
|
+
if actual_pp == expected_pp
|
|
287
|
+
actual_pp += " (id: #{actual.object_id})"
|
|
288
|
+
expected_pp += " (id: #{expected.object_id})"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
"Expected #{actual_pp}\n" \
|
|
292
|
+
"To not be the same as #{expected_pp}"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def assert_raises(expected = StandardError, *expected_exceptions, message: nil)
|
|
299
|
+
msg = expected_exceptions.pop if expected_exceptions.last.is_a?(String)
|
|
300
|
+
message = @__m.msg(msg, message)
|
|
301
|
+
@__m.assert do
|
|
302
|
+
@__m.fail("assert_raises requires a block to capture errors.") unless block_given?
|
|
303
|
+
|
|
304
|
+
begin
|
|
305
|
+
yield
|
|
306
|
+
rescue expected, *expected_exceptions => exception
|
|
307
|
+
return exception
|
|
308
|
+
rescue ::Megatest::Assertion, *::Megatest::IGNORED_ERRORS
|
|
309
|
+
raise # Pass through
|
|
310
|
+
rescue ::Exception => unexepected_exception
|
|
311
|
+
error = @__m.strip_backtrace(unexepected_exception, __FILE__, __LINE__ - 6, 0)
|
|
312
|
+
|
|
313
|
+
expected_pp = if expected_exceptions.empty?
|
|
314
|
+
@__m.pp(expected)
|
|
315
|
+
else
|
|
316
|
+
expected_exceptions.map { |e| @__m.pp(e) }.join(", ") << " or #{@__m.pp(expected)}"
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
@__m.fail(message, "#{expected_pp} exception expected, not:\n#{@__m.pp(error)}")
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
expected_pp = if expected_exceptions.empty?
|
|
323
|
+
@__m.pp(expected)
|
|
324
|
+
else
|
|
325
|
+
expected_exceptions.map { |e| @__m.pp(e) }.join(", ") << " or #{@__m.pp(expected)}"
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
@__m.fail(message, "Expected", expected_pp, "but nothing was raised.")
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def assert_throws(thrown_object, msg = nil, message: nil)
|
|
333
|
+
message = @__m.msg(msg, message)
|
|
334
|
+
@__m.assert do
|
|
335
|
+
caught = true
|
|
336
|
+
value = catch(thrown_object) do
|
|
337
|
+
@__m.expect_no_failures do
|
|
338
|
+
yield
|
|
339
|
+
rescue UncaughtThrowError => error
|
|
340
|
+
@__m.fail(message, "Expected", @__m.pp(thrown_object), "to have been thrown, not:", @__m.pp(error.tag))
|
|
341
|
+
end
|
|
342
|
+
caught = false
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
unless caught
|
|
346
|
+
@__m.fail(message, "Expected", @__m.pp(thrown_object), "to have been thrown, but it wasn't")
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
value
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def assert_operator(left, operator, right, msg = nil, message: nil)
|
|
354
|
+
message = @__m.msg(msg, message)
|
|
355
|
+
@__m.assert do
|
|
356
|
+
unless left.__send__(operator, right)
|
|
357
|
+
@__m.fail(message, "Expected", @__m.pp(left), "to be #{operator}", @__m.pp(right))
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def refute_operator(left, operator, right, msg = nil, message: nil)
|
|
363
|
+
message = @__m.msg(msg, message)
|
|
364
|
+
@__m.assert do
|
|
365
|
+
if left.__send__(operator, right)
|
|
366
|
+
@__m.fail(message, "Expected", @__m.pp(left), "to not be #{operator}", @__m.pp(right))
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def assert_in_delta(expected, actual, delta = 0.001, msg = nil, message: nil)
|
|
372
|
+
message = @__m.msg(msg, message)
|
|
373
|
+
@__m.assert do
|
|
374
|
+
diff = (expected - actual).abs
|
|
375
|
+
unless delta >= diff
|
|
376
|
+
@__m.fail(message, "Expected", "|#{@__m.pp(expected)} - #{@__m.pp(actual)}| (#{diff})", "to be <= #{delta}")
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def refute_in_delta(expected, actual, delta = 0.001, msg = nil, message: nil)
|
|
382
|
+
message = @__m.msg(msg, message)
|
|
383
|
+
@__m.assert do
|
|
384
|
+
diff = (expected - actual).abs
|
|
385
|
+
if delta >= diff
|
|
386
|
+
@__m.fail(message, "Expected", "|#{@__m.pp(expected)} - #{@__m.pp(actual)}| (#{diff})", "to not be <= #{delta}")
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def assert_in_epsilon(expected, actual, epsilon = 0.001, msg = nil, message: nil)
|
|
392
|
+
message = @__m.msg(msg, message)
|
|
393
|
+
@__m.assert do
|
|
394
|
+
diff = (expected - actual).abs
|
|
395
|
+
delta = [expected.abs, actual.abs].min * epsilon
|
|
396
|
+
unless delta >= diff
|
|
397
|
+
@__m.fail(message, "Expected", "|#{@__m.pp(expected)} - #{@__m.pp(actual)}| (#{diff})", "to be <= #{delta}")
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def refute_in_epsilon(expected, actual, epsilon = 0.001, msg = nil, message: nil)
|
|
403
|
+
message = @__m.msg(msg, message)
|
|
404
|
+
@__m.assert do
|
|
405
|
+
diff = (expected - actual).abs
|
|
406
|
+
delta = [expected.abs, actual.abs].min * epsilon
|
|
407
|
+
if delta >= diff
|
|
408
|
+
@__m.fail(message, "Expected", "|#{@__m.pp(expected)} - #{@__m.pp(actual)}| (#{diff})", "to not be <= #{delta}")
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def skip(message = nil)
|
|
414
|
+
message ||= "Skipped, no message given"
|
|
415
|
+
::Kernel.raise(::Megatest::Skip, message, nil)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def flunk(msg = nil, message: nil)
|
|
419
|
+
message = @__m.msg(msg, message)
|
|
420
|
+
@__m.assert do
|
|
421
|
+
@__m.fail(message || "Failed")
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def assert_output(expected_stdout = nil, expected_stderr = nil, &block)
|
|
426
|
+
@__m.assert do
|
|
427
|
+
@__m.fail("assert_output requires a block to capture output.") unless block_given?
|
|
428
|
+
|
|
429
|
+
actual_stdout, actual_stderr = @__m.expect_no_failures do
|
|
430
|
+
capture_io(&block)
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
if expected_stderr
|
|
434
|
+
if Regexp === expected_stderr
|
|
435
|
+
assert_match(expected_stderr, actual_stderr, message: "In stderr")
|
|
436
|
+
else
|
|
437
|
+
assert_equal(expected_stderr, actual_stderr, message: "In stderr")
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
if expected_stdout
|
|
442
|
+
if Regexp === expected_stdout
|
|
443
|
+
assert_match(expected_stdout, actual_stdout, message: "In stdout")
|
|
444
|
+
else
|
|
445
|
+
assert_equal(expected_stdout, actual_stdout, message: "In stdout")
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def assert_silent(&block)
|
|
452
|
+
@__m.assert do
|
|
453
|
+
assert_output("", "", &block)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def capture_io
|
|
458
|
+
require "stringio" unless defined?(::StringIO)
|
|
459
|
+
captured_stdout, captured_stderr = ::StringIO.new, ::StringIO.new
|
|
460
|
+
|
|
461
|
+
orig_stdout, orig_stderr = $stdout, $stderr
|
|
462
|
+
$stdout, $stderr = captured_stdout, captured_stderr
|
|
463
|
+
|
|
464
|
+
begin
|
|
465
|
+
yield
|
|
466
|
+
|
|
467
|
+
[captured_stdout.string, captured_stderr.string]
|
|
468
|
+
ensure
|
|
469
|
+
$stdout = orig_stdout
|
|
470
|
+
$stderr = orig_stderr
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
end
|