megatest 0.3.0 → 0.5.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 +13 -0
- data/lib/megatest/assertions.rb +154 -8
- data/lib/megatest/autorun.rb +9 -0
- data/lib/megatest/cli.rb +5 -3
- data/lib/megatest/config.rb +17 -1
- data/lib/megatest/redis_queue.rb +6 -3
- data/lib/megatest/reporters.rb +2 -2
- data/lib/megatest/runtime.rb +90 -0
- data/lib/megatest/selector.rb +57 -46
- data/lib/megatest/state.rb +3 -1
- data/lib/megatest/version.rb +1 -1
- data/lib/megatest.rb +9 -4
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0be0dcf9dd26312cec897081522ae6ac404db5447b26d50286ccc8720422743
|
|
4
|
+
data.tar.gz: 2580f85814d9469dfd1df3c8d3601c8b5ac89b4e4faaf706b4af47154d3c9bdb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef25ab7ecc09133635d0ffaef659ce02574f01fd558f2437c187fd3f4f362b40cd13dfcb6d6ac455e0a1462b507dfb6e27efd74e72d18ed57e68f8c1434b25e2
|
|
7
|
+
data.tar.gz: 68b8dc14b4869a5dea9d51f2c507287acb5bbcce709b1514c965f2b0cc9a0d8edd4da82a89b8e120427e63ece0c159a2f4f27d92362f1b25ded3f9cbdd05c402
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2025-01-17
|
|
4
|
+
|
|
5
|
+
- Adds `megatest/autorun`
|
|
6
|
+
- Adds `assert_nothing_raised`.
|
|
7
|
+
- Adds `assert_difference`.
|
|
8
|
+
- Adds `assert_changes`.
|
|
9
|
+
- Adds `assert_not_*` aliases.
|
|
10
|
+
- Adds `match:` argument to `assert_raises`.
|
|
11
|
+
|
|
12
|
+
## [0.4.0] - 2025-11-12
|
|
13
|
+
|
|
14
|
+
- Allow configuring the test glob via `config.test_globs`.
|
|
15
|
+
|
|
3
16
|
## [0.3.0] - 2025-06-20
|
|
4
17
|
|
|
5
18
|
- Added missing MIT license.
|
data/lib/megatest/assertions.rb
CHANGED
|
@@ -67,6 +67,7 @@ module Megatest
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
|
+
alias_method :assert_not, :refute
|
|
70
71
|
|
|
71
72
|
def assert_nil(actual, msg = nil, message: nil)
|
|
72
73
|
message = @__m.msg(msg, message)
|
|
@@ -85,6 +86,7 @@ module Megatest
|
|
|
85
86
|
end
|
|
86
87
|
end
|
|
87
88
|
end
|
|
89
|
+
alias_method :assert_not_nil, :refute_nil
|
|
88
90
|
|
|
89
91
|
def assert_equal(expected, actual, msg = nil, message: nil, allow_nil: false)
|
|
90
92
|
message = @__m.msg(msg, message)
|
|
@@ -116,6 +118,7 @@ module Megatest
|
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
120
|
end
|
|
121
|
+
alias_method :assert_not_equal, :refute_equal
|
|
119
122
|
|
|
120
123
|
def assert_includes(collection, object, msg = nil, message: nil)
|
|
121
124
|
message = @__m.msg(msg, message)
|
|
@@ -134,6 +137,7 @@ module Megatest
|
|
|
134
137
|
end
|
|
135
138
|
end
|
|
136
139
|
end
|
|
140
|
+
alias_method :assert_not_includes, :refute_includes
|
|
137
141
|
|
|
138
142
|
def assert_empty(object, msg = nil, message: nil)
|
|
139
143
|
message = @__m.msg(msg, message)
|
|
@@ -152,6 +156,7 @@ module Megatest
|
|
|
152
156
|
end
|
|
153
157
|
end
|
|
154
158
|
end
|
|
159
|
+
alias_method :assert_not_empty, :refute_empty
|
|
155
160
|
|
|
156
161
|
def assert_instance_of(klass, actual, msg = nil, message: nil)
|
|
157
162
|
message = @__m.msg(msg, message)
|
|
@@ -170,6 +175,7 @@ module Megatest
|
|
|
170
175
|
end
|
|
171
176
|
end
|
|
172
177
|
end
|
|
178
|
+
alias_method :assert_not_instance_of, :refute_instance_of
|
|
173
179
|
|
|
174
180
|
def assert_kind_of(klass, actual, msg = nil, message: nil)
|
|
175
181
|
message = @__m.msg(msg, message)
|
|
@@ -188,6 +194,7 @@ module Megatest
|
|
|
188
194
|
end
|
|
189
195
|
end
|
|
190
196
|
end
|
|
197
|
+
alias_method :assert_not_kind_of, :refute_kind_of
|
|
191
198
|
|
|
192
199
|
def assert_predicate(actual, predicate, msg = nil, message: nil)
|
|
193
200
|
message = @__m.msg(msg, message)
|
|
@@ -206,6 +213,7 @@ module Megatest
|
|
|
206
213
|
end
|
|
207
214
|
end
|
|
208
215
|
end
|
|
216
|
+
alias_method :assert_not_predicate, :refute_predicate
|
|
209
217
|
|
|
210
218
|
def assert_match(original_matcher, obj, msg = nil, message: nil)
|
|
211
219
|
message = @__m.msg(msg, message)
|
|
@@ -238,6 +246,7 @@ module Megatest
|
|
|
238
246
|
end
|
|
239
247
|
end
|
|
240
248
|
end
|
|
249
|
+
alias_method :assert_no_match, :refute_match
|
|
241
250
|
|
|
242
251
|
def assert_respond_to(object, method, msg = nil, message: nil, include_all: false)
|
|
243
252
|
message = @__m.msg(msg, message)
|
|
@@ -256,6 +265,7 @@ module Megatest
|
|
|
256
265
|
end
|
|
257
266
|
end
|
|
258
267
|
end
|
|
268
|
+
alias_method :assert_not_respond_to, :refute_respond_to
|
|
259
269
|
|
|
260
270
|
def assert_same(expected, actual, msg = nil, message: nil)
|
|
261
271
|
message = @__m.msg(msg, message)
|
|
@@ -294,21 +304,36 @@ module Megatest
|
|
|
294
304
|
end
|
|
295
305
|
end
|
|
296
306
|
end
|
|
307
|
+
alias_method :assert_not_same, :refute_same
|
|
297
308
|
|
|
298
|
-
def assert_raises(expected = StandardError, *expected_exceptions, message: nil)
|
|
309
|
+
def assert_raises(expected = StandardError, *expected_exceptions, match: nil, message: nil)
|
|
299
310
|
msg = expected_exceptions.pop if expected_exceptions.last.is_a?(String)
|
|
300
311
|
message = @__m.msg(msg, message)
|
|
312
|
+
|
|
313
|
+
matcher = if match
|
|
314
|
+
if ::String === match
|
|
315
|
+
::Regexp.new(::Regexp.escape(match))
|
|
316
|
+
else
|
|
317
|
+
match
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
301
321
|
@__m.assert do
|
|
302
322
|
@__m.fail("assert_raises requires a block to capture errors.") unless block_given?
|
|
303
323
|
|
|
304
324
|
begin
|
|
325
|
+
before_yield = __LINE__
|
|
305
326
|
yield
|
|
306
327
|
rescue expected, *expected_exceptions => exception
|
|
328
|
+
if matcher && !matcher.match?(exception.message)
|
|
329
|
+
@__m.fail(message, "Expected", @__m.pp(match), "to match", @__m.pp(exception.message))
|
|
330
|
+
end
|
|
331
|
+
|
|
307
332
|
return exception
|
|
308
333
|
rescue ::Megatest::Assertion, *::Megatest::IGNORED_ERRORS
|
|
309
334
|
raise # Pass through
|
|
310
335
|
rescue ::Exception => unexepected_exception
|
|
311
|
-
error = @__m.strip_backtrace(unexepected_exception, __FILE__,
|
|
336
|
+
error = @__m.strip_backtrace(unexepected_exception, __FILE__, before_yield + 1, 0)
|
|
312
337
|
|
|
313
338
|
expected_pp = if expected_exceptions.empty?
|
|
314
339
|
@__m.pp(expected)
|
|
@@ -317,15 +342,26 @@ module Megatest
|
|
|
317
342
|
end
|
|
318
343
|
|
|
319
344
|
@__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
345
|
else
|
|
325
|
-
|
|
346
|
+
expected_pp = if expected_exceptions.empty?
|
|
347
|
+
@__m.pp(expected)
|
|
348
|
+
else
|
|
349
|
+
expected_exceptions.map { |e| @__m.pp(e) }.join(", ") << " or #{@__m.pp(expected)}"
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
@__m.fail(message, "Expected", expected_pp, "but nothing was raised.")
|
|
326
353
|
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
alias :assert_raise :assert_raises
|
|
327
357
|
|
|
328
|
-
|
|
358
|
+
def assert_nothing_raised
|
|
359
|
+
@__m.assert do
|
|
360
|
+
yield
|
|
361
|
+
rescue ::Megatest::Assertion, *::Megatest::IGNORED_ERRORS
|
|
362
|
+
raise # Pass through
|
|
363
|
+
rescue Exception => unexepected_exception
|
|
364
|
+
raise ::Megatest::UnexpectedError, unexepected_exception
|
|
329
365
|
end
|
|
330
366
|
end
|
|
331
367
|
|
|
@@ -367,6 +403,114 @@ module Megatest
|
|
|
367
403
|
end
|
|
368
404
|
end
|
|
369
405
|
end
|
|
406
|
+
alias_method :assert_not_operator, :refute_operator
|
|
407
|
+
|
|
408
|
+
def assert_difference(expression, difference = @__m.unset, message: nil, &block)
|
|
409
|
+
expressions = if @__m.set?(difference)
|
|
410
|
+
Array(expression).to_h { |e| [e, difference] }
|
|
411
|
+
elsif Hash === expression
|
|
412
|
+
expression
|
|
413
|
+
else
|
|
414
|
+
Array(expression).to_h { |e| [e, 1] }
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
exps = expressions.keys.map { |e| @__m.expression(e, block) }
|
|
418
|
+
|
|
419
|
+
@__m.assert do
|
|
420
|
+
before = exps.map(&:call)
|
|
421
|
+
|
|
422
|
+
retval = @__m.safe_yield(&block)
|
|
423
|
+
|
|
424
|
+
expressions.zip(exps, before) do |(code, diff), exp, before_value|
|
|
425
|
+
actual = exp.call
|
|
426
|
+
expected = before_value + diff
|
|
427
|
+
unless expected == actual
|
|
428
|
+
error = "`#{@__m.pp_expression(code)}` didn't change by #{diff}, but by #{actual - before_value}."
|
|
429
|
+
@__m.fail(message, error)
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
retval
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def refute_difference(expressions, message: nil, &block)
|
|
438
|
+
exps = Array(expressions).map { |e| @__m.expression(e, block) }
|
|
439
|
+
|
|
440
|
+
@__m.assert do
|
|
441
|
+
before = exps.map(&:call)
|
|
442
|
+
|
|
443
|
+
retval = @__m.safe_yield(&block)
|
|
444
|
+
|
|
445
|
+
exps.zip(before) do |exp, before_value|
|
|
446
|
+
actual = exp.call
|
|
447
|
+
unless before_value == actual
|
|
448
|
+
error = "Expected `#{@__m.pp_expression(exp)}` to not change, but it changed from #{@__m.pp(before_value)} to #{@__m.pp(actual)}."
|
|
449
|
+
@__m.fail(message, error)
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
retval
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
alias_method :assert_no_difference, :refute_difference
|
|
457
|
+
|
|
458
|
+
def assert_changes(expression, msg = nil, message: nil, from: @__m.unset, to: @__m.unset, &block)
|
|
459
|
+
message = @__m.msg(msg, message)
|
|
460
|
+
exp = @__m.expression(expression, block)
|
|
461
|
+
@__m.assert do
|
|
462
|
+
before = exp.call
|
|
463
|
+
if @__m.set?(from) && !(from === before)
|
|
464
|
+
@__m.fail(message, "Expected `#{@__m.pp_expression(exp)}` to starts from #{@__m.pp(from)}, but was #{@__m.pp(before)}")
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
retval = assert_nothing_raised(&block)
|
|
468
|
+
|
|
469
|
+
after = exp.call
|
|
470
|
+
|
|
471
|
+
if before == after
|
|
472
|
+
details = "Expected `#{@__m.pp_expression(exp)}` to change"
|
|
473
|
+
if @__m.set?(to)
|
|
474
|
+
details = "#{details} to #{@__m.pp(to)}"
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
if before == to
|
|
478
|
+
details = "#{details}, but it was already #{@__m.pp(to)}."
|
|
479
|
+
else
|
|
480
|
+
details = "#{details}, but it stayed #{@__m.pp(before)}"
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
@__m.fail(message, details)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
if @__m.set?(to) && !(to === after)
|
|
487
|
+
@__m.fail(message, "Expected `#{@__m.pp_expression(exp)}` to change to #{@__m.pp(to)}, got #{@__m.pp(after)}")
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
retval
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def refute_changes(expression, msg = nil, message: nil, from: @__m.unset, &block)
|
|
495
|
+
message = @__m.msg(msg, message)
|
|
496
|
+
exp = @__m.expression(expression, block)
|
|
497
|
+
@__m.assert do
|
|
498
|
+
before = exp.call
|
|
499
|
+
if @__m.set?(from) && !(from === before)
|
|
500
|
+
@__m.fail(message, "Expected `#{@__m.pp_expression(exp)}` to start from #{@__m.pp(from)}, but was #{@__m.pp(before)}.")
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
retval = assert_nothing_raised(&block)
|
|
504
|
+
|
|
505
|
+
after = exp.call
|
|
506
|
+
unless before == after
|
|
507
|
+
@__m.fail(message, "Expected `#{@__m.pp_expression(exp)}` to not change, but it changed from #{@__m.pp(before)} to #{@__m.pp(after)}.")
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
retval
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
alias_method :assert_no_changes, :refute_changes
|
|
370
514
|
|
|
371
515
|
def assert_in_delta(expected, actual, delta = 0.001, msg = nil, message: nil)
|
|
372
516
|
message = @__m.msg(msg, message)
|
|
@@ -387,6 +531,7 @@ module Megatest
|
|
|
387
531
|
end
|
|
388
532
|
end
|
|
389
533
|
end
|
|
534
|
+
alias_method :assert_not_in_delta, :refute_in_delta
|
|
390
535
|
|
|
391
536
|
def assert_in_epsilon(expected, actual, epsilon = 0.001, msg = nil, message: nil)
|
|
392
537
|
message = @__m.msg(msg, message)
|
|
@@ -409,6 +554,7 @@ module Megatest
|
|
|
409
554
|
end
|
|
410
555
|
end
|
|
411
556
|
end
|
|
557
|
+
alias_method :assert_not_epsilon, :refute_in_epsilon
|
|
412
558
|
|
|
413
559
|
def skip(message = nil)
|
|
414
560
|
message ||= "Skipped, no message given"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "megatest"
|
|
4
|
+
|
|
5
|
+
unless Megatest.running
|
|
6
|
+
root = File.expand_path("../../", __dir__)
|
|
7
|
+
rubyopt = "#{ENV.fetch("RUBYOPT", nil)} -I#{root}/lib"
|
|
8
|
+
exec({ "RUBYOPT" => rubyopt }, "#{root}/exe/megatest", $PROGRAM_NAME, *ARGV)
|
|
9
|
+
end
|
data/lib/megatest/cli.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Megatest
|
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
12
|
def run!
|
|
13
|
-
program_name = $PROGRAM_NAME
|
|
13
|
+
program_name = ENV.fetch("MEGATEST_PROGRAM_NAME", $PROGRAM_NAME)
|
|
14
14
|
if paths = ENV["PATH"]
|
|
15
15
|
paths.split(":").each do |path|
|
|
16
16
|
if program_name.start_with?(path)
|
|
@@ -69,6 +69,8 @@ module Megatest
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def configure
|
|
72
|
+
Megatest.running = true
|
|
73
|
+
|
|
72
74
|
if @runner = RUNNERS[@argv.first]
|
|
73
75
|
@argv.shift
|
|
74
76
|
end
|
|
@@ -92,7 +94,7 @@ module Megatest
|
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
|
|
95
|
-
@config.selectors = Selector.parse(@argv)
|
|
97
|
+
@config.selectors = Selector.new(@config).parse(@argv)
|
|
96
98
|
Megatest.load_config(@config)
|
|
97
99
|
Megatest.init(@config)
|
|
98
100
|
test_cases = Megatest.load_tests(@config)
|
|
@@ -125,7 +127,7 @@ module Megatest
|
|
|
125
127
|
queue = @config.build_queue
|
|
126
128
|
raise InvalidArgument, "Distributed queues can't be bisected" if queue.distributed?
|
|
127
129
|
|
|
128
|
-
@config.selectors = Selector.parse(@argv)
|
|
130
|
+
@config.selectors = Selector.new(@config).parse(@argv)
|
|
129
131
|
Megatest.load_config(@config)
|
|
130
132
|
Megatest.init(@config)
|
|
131
133
|
test_cases = Megatest.load_tests(@config)
|
data/lib/megatest/config.rb
CHANGED
|
@@ -139,7 +139,7 @@ module Megatest
|
|
|
139
139
|
attr_accessor :queue_url, :retry_tolerance, :max_retries, :jobs_count, :job_index, :load_paths, :deprecations,
|
|
140
140
|
:build_id, :heartbeat_frequency, :minitest_compatibility, :ci, :selectors
|
|
141
141
|
attr_reader :before_fork_callbacks, :global_setup_callbacks, :backtrace, :circuit_breaker, :seed,
|
|
142
|
-
:worker_id, :workers_count
|
|
142
|
+
:worker_id, :workers_count, :test_globs
|
|
143
143
|
attr_writer :differ, :pretty_printer, :program_name, :colors
|
|
144
144
|
|
|
145
145
|
def initialize(env)
|
|
@@ -168,12 +168,18 @@ module Megatest
|
|
|
168
168
|
@pretty_printer = PrettyPrint.new(self)
|
|
169
169
|
@minitest_compatibility = false
|
|
170
170
|
@selectors = nil
|
|
171
|
+
@test_globs = [DEFAULT_TEST_GLOB]
|
|
171
172
|
CIService.configure(self, env)
|
|
172
173
|
end
|
|
173
174
|
|
|
175
|
+
def test_globs=(patterns)
|
|
176
|
+
@test_globs = normalize_test_glob(patterns)
|
|
177
|
+
end
|
|
178
|
+
|
|
174
179
|
def initialize_dup(_)
|
|
175
180
|
super
|
|
176
181
|
@circuit_breaker = @circuit_breaker.dup
|
|
182
|
+
@test_globs = @test_globs.dup
|
|
177
183
|
end
|
|
178
184
|
|
|
179
185
|
def program_name
|
|
@@ -305,6 +311,16 @@ module Megatest
|
|
|
305
311
|
instance_variable_set(name, value)
|
|
306
312
|
end
|
|
307
313
|
end
|
|
314
|
+
|
|
315
|
+
private
|
|
316
|
+
|
|
317
|
+
def normalize_test_glob(patterns)
|
|
318
|
+
if patterns
|
|
319
|
+
Array(patterns).compact.map(&:to_s)
|
|
320
|
+
else
|
|
321
|
+
[DEFAULT_TEST_GLOB]
|
|
322
|
+
end
|
|
323
|
+
end
|
|
308
324
|
end
|
|
309
325
|
|
|
310
326
|
@config = Config.new({})
|
data/lib/megatest/redis_queue.rb
CHANGED
|
@@ -232,11 +232,14 @@ module Megatest
|
|
|
232
232
|
def populate(test_cases)
|
|
233
233
|
super
|
|
234
234
|
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
value = key("leader-setup", worker_id)
|
|
236
|
+
# NB: If we assumed redis 7+ this could be a single command: `SET <key> NX GET EX @ttl <value>`.
|
|
237
|
+
_, _, leader = @redis.multi do |pipeline|
|
|
238
|
+
pipeline.call("setnx", key("leader-status"), value)
|
|
237
239
|
pipeline.call("expire", key("leader-status"), @ttl)
|
|
240
|
+
pipeline.call("get", key("leader-status"))
|
|
238
241
|
end
|
|
239
|
-
@leader =
|
|
242
|
+
@leader = leader == value
|
|
240
243
|
|
|
241
244
|
if @leader
|
|
242
245
|
@redis.multi do |transaction|
|
data/lib/megatest/reporters.rb
CHANGED
|
@@ -113,8 +113,8 @@ module Megatest
|
|
|
113
113
|
p99 = sorted_results[(size * 0.99).to_i].duration
|
|
114
114
|
|
|
115
115
|
@out.puts "Finished in #{s(executor.wall_time.to_f)}, average: #{ms(average)}, median: #{ms(median)}, p90: #{ms(p90)}, p99: #{ms(p99)}"
|
|
116
|
-
|
|
117
|
-
slowest_tests = sorted_results.last(5).select { |r| r.duration >
|
|
116
|
+
cutoff = p90 * 10
|
|
117
|
+
slowest_tests = sorted_results.last(5).select { |r| r.duration > cutoff }
|
|
118
118
|
unless slowest_tests.empty?
|
|
119
119
|
@out.puts "Slowest tests:"
|
|
120
120
|
slowest_tests.reverse_each do |result|
|
data/lib/megatest/runtime.rb
CHANGED
|
@@ -139,6 +139,49 @@ module Megatest
|
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
def safe_yield
|
|
143
|
+
yield
|
|
144
|
+
rescue Assertion, *IGNORED_ERRORS
|
|
145
|
+
raise
|
|
146
|
+
rescue ::Exception => unexepected_exception
|
|
147
|
+
raise UnexpectedError, unexepected_exception
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
UNSET = BasicObject.new
|
|
151
|
+
|
|
152
|
+
def unset
|
|
153
|
+
UNSET
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def unset?(arg)
|
|
157
|
+
UNSET.equal?(arg)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def set?(arg)
|
|
161
|
+
!UNSET.equal?(arg)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class Expression
|
|
165
|
+
attr_reader :string
|
|
166
|
+
|
|
167
|
+
def initialize(string, block)
|
|
168
|
+
@string = string
|
|
169
|
+
@block = block
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def call
|
|
173
|
+
eval(@string, @block.binding)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def expression(expression, block)
|
|
178
|
+
if String === expression
|
|
179
|
+
Expression.new(expression, block)
|
|
180
|
+
else
|
|
181
|
+
expression
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
142
185
|
def minitest_compatibility?
|
|
143
186
|
@config.minitest_compatibility
|
|
144
187
|
end
|
|
@@ -147,6 +190,53 @@ module Megatest
|
|
|
147
190
|
@config.render_object(object)
|
|
148
191
|
end
|
|
149
192
|
|
|
193
|
+
def pp_expression(callable)
|
|
194
|
+
case callable
|
|
195
|
+
when Expression
|
|
196
|
+
callable.string
|
|
197
|
+
when Proc
|
|
198
|
+
# Logic borrowed from Active Support.
|
|
199
|
+
if defined?(RubyVM::InstructionSequence)
|
|
200
|
+
iseq = RubyVM::InstructionSequence.of(callable)
|
|
201
|
+
return pp(callable) unless iseq
|
|
202
|
+
|
|
203
|
+
source = if RubyVM::InstructionSequence.method_defined?(:script_lines) && iseq.script_lines
|
|
204
|
+
iseq.script_lines.join("\n")
|
|
205
|
+
elsif File.readable?(iseq.absolute_path)
|
|
206
|
+
File.read(iseq.absolute_path)
|
|
207
|
+
end
|
|
208
|
+
return pp(callable) unless source
|
|
209
|
+
|
|
210
|
+
location = iseq.to_a[4][:code_location]
|
|
211
|
+
return pp(callable) unless location
|
|
212
|
+
|
|
213
|
+
lines = source.lines[(location[0] - 1)..(location[2] - 1)]
|
|
214
|
+
lines[-1] = lines[-1].byteslice(0...location[3])
|
|
215
|
+
lines[0] = lines[0].byteslice(location[1]...-1)
|
|
216
|
+
source = lines.join.strip
|
|
217
|
+
|
|
218
|
+
# Ruby 4.1.0dev includes the `->`
|
|
219
|
+
source.delete_prefix!("->")
|
|
220
|
+
source.strip!
|
|
221
|
+
|
|
222
|
+
# We ignore procs defined with do/end as they are likely multi-line anyway.
|
|
223
|
+
if source.start_with?("{")
|
|
224
|
+
source.delete_suffix!("}")
|
|
225
|
+
source.delete_prefix!("{")
|
|
226
|
+
source.strip!
|
|
227
|
+
# It won't read nice if the callable contains multiple
|
|
228
|
+
# lines, and it should be a rare occurrence anyway.
|
|
229
|
+
# Same if it takes arguments.
|
|
230
|
+
if !source.include?("\n") && !source.start_with?("|")
|
|
231
|
+
return source
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
pp(callable)
|
|
238
|
+
end
|
|
239
|
+
|
|
150
240
|
def diff(expected, actual)
|
|
151
241
|
@config.diff(expected, actual)
|
|
152
242
|
end
|
data/lib/megatest/selector.rb
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
# :stopdoc:
|
|
4
4
|
|
|
5
5
|
module Megatest
|
|
6
|
-
|
|
6
|
+
class Selector
|
|
7
7
|
class List
|
|
8
|
-
def initialize(loaders, filters)
|
|
8
|
+
def initialize(config, loaders, filters)
|
|
9
9
|
@loaders = loaders
|
|
10
10
|
if loaders.empty?
|
|
11
|
-
@loaders = [Loader.new("test")]
|
|
11
|
+
@loaders = [Loader.new(config, "test")]
|
|
12
12
|
end
|
|
13
13
|
@filters = filters
|
|
14
14
|
end
|
|
@@ -56,14 +56,13 @@ module Megatest
|
|
|
56
56
|
class Loader
|
|
57
57
|
attr_reader :path
|
|
58
58
|
|
|
59
|
-
def initialize(path, filter = nil)
|
|
59
|
+
def initialize(config, path, filter = nil)
|
|
60
|
+
@config = config
|
|
60
61
|
@path = File.expand_path(path)
|
|
61
62
|
if @directory = File.directory?(@path)
|
|
62
63
|
@path = File.join(@path, "/")
|
|
63
|
-
@paths = Megatest.glob(@path)
|
|
64
|
-
else
|
|
65
|
-
@paths = [@path]
|
|
66
64
|
end
|
|
65
|
+
@paths = nil
|
|
67
66
|
@filter = filter
|
|
68
67
|
end
|
|
69
68
|
|
|
@@ -81,7 +80,7 @@ module Megatest
|
|
|
81
80
|
end
|
|
82
81
|
|
|
83
82
|
def append_paths(paths_to_load)
|
|
84
|
-
paths_to_load.concat(
|
|
83
|
+
paths_to_load.concat(paths)
|
|
85
84
|
end
|
|
86
85
|
|
|
87
86
|
def select(registry)
|
|
@@ -99,6 +98,16 @@ module Megatest
|
|
|
99
98
|
test_cases
|
|
100
99
|
end
|
|
101
100
|
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def paths
|
|
105
|
+
@paths ||= if @directory
|
|
106
|
+
Megatest.glob(@config.test_globs.map { |pattern| File.join(@path, pattern) })
|
|
107
|
+
else
|
|
108
|
+
[@path]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
102
111
|
end
|
|
103
112
|
|
|
104
113
|
class NegativeLoader
|
|
@@ -244,54 +253,56 @@ module Megatest
|
|
|
244
253
|
NameFilter,
|
|
245
254
|
].freeze
|
|
246
255
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return List.new([], [])
|
|
251
|
-
end
|
|
256
|
+
def initialize(config)
|
|
257
|
+
@config = config
|
|
258
|
+
end
|
|
252
259
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
260
|
+
def parse(argv)
|
|
261
|
+
if argv.empty?
|
|
262
|
+
return List.new(@config, [], [])
|
|
263
|
+
end
|
|
256
264
|
|
|
257
|
-
|
|
265
|
+
argv = argv.dup
|
|
266
|
+
loaders = []
|
|
267
|
+
filters = []
|
|
258
268
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
negative = false
|
|
270
|
+
|
|
271
|
+
until argv.empty?
|
|
272
|
+
case argument = argv.shift
|
|
273
|
+
when "!"
|
|
274
|
+
negative = true
|
|
275
|
+
else
|
|
276
|
+
loader_str, filter_str = argument.split(":", 2)
|
|
277
|
+
loader_str = nil if loader_str.empty?
|
|
278
|
+
|
|
279
|
+
filter = nil
|
|
280
|
+
if filter_str
|
|
281
|
+
FILTERS.each do |filter_class|
|
|
282
|
+
if filter = filter_class.parse(filter_str)
|
|
283
|
+
break
|
|
273
284
|
end
|
|
274
285
|
end
|
|
286
|
+
end
|
|
275
287
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
end
|
|
282
|
-
loaders << loader
|
|
283
|
-
else
|
|
284
|
-
if negative
|
|
285
|
-
filter = NegativeFilter.new(filter)
|
|
286
|
-
negative = false
|
|
287
|
-
end
|
|
288
|
-
filters << filter
|
|
288
|
+
if loader_str
|
|
289
|
+
loader = Loader.new(@config, loader_str, filter)
|
|
290
|
+
if negative
|
|
291
|
+
loader = NegativeLoader.new(loader)
|
|
292
|
+
negative = false
|
|
289
293
|
end
|
|
294
|
+
loaders << loader
|
|
295
|
+
else
|
|
296
|
+
if negative
|
|
297
|
+
filter = NegativeFilter.new(filter)
|
|
298
|
+
negative = false
|
|
299
|
+
end
|
|
300
|
+
filters << filter
|
|
290
301
|
end
|
|
291
302
|
end
|
|
292
|
-
|
|
293
|
-
List.new(loaders, filters)
|
|
294
303
|
end
|
|
304
|
+
|
|
305
|
+
List.new(@config, loaders, filters)
|
|
295
306
|
end
|
|
296
307
|
end
|
|
297
308
|
end
|
data/lib/megatest/state.rb
CHANGED
|
@@ -101,7 +101,9 @@ module Megatest
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def on_teardown(block)
|
|
104
|
-
|
|
104
|
+
if @teardown_callback
|
|
105
|
+
raise Error, "The teardown block was already defined as #{@teardown_callback}"
|
|
106
|
+
end
|
|
105
107
|
raise Error, "teardown blocks can't be defined in context blocks" if @current_context
|
|
106
108
|
|
|
107
109
|
@teardown_callback = block
|
data/lib/megatest/version.rb
CHANGED
data/lib/megatest.rb
CHANGED
|
@@ -12,8 +12,13 @@ module Megatest
|
|
|
12
12
|
ROOT = -File.expand_path("../", __FILE__)
|
|
13
13
|
PWD = File.join(Dir.pwd, "/")
|
|
14
14
|
IGNORED_ERRORS = [NoMemoryError, SignalException, SystemExit].freeze
|
|
15
|
+
DEFAULT_TEST_GLOB = "**/{test_*,*_test}.rb"
|
|
16
|
+
|
|
17
|
+
@running = false
|
|
15
18
|
|
|
16
19
|
class << self
|
|
20
|
+
attr_accessor :running
|
|
21
|
+
|
|
17
22
|
def fork?
|
|
18
23
|
Process.respond_to?(:fork) && !ENV["NO_FORK"]
|
|
19
24
|
end
|
|
@@ -90,12 +95,12 @@ module Megatest
|
|
|
90
95
|
end
|
|
91
96
|
|
|
92
97
|
if Dir.method(:glob).parameters.include?(%i(key sort)) # Ruby 2.7+
|
|
93
|
-
def glob(
|
|
94
|
-
Dir.glob(
|
|
98
|
+
def glob(pattern)
|
|
99
|
+
Dir.glob(pattern)
|
|
95
100
|
end
|
|
96
101
|
else
|
|
97
|
-
def glob(
|
|
98
|
-
paths = Dir.glob(
|
|
102
|
+
def glob(pattern)
|
|
103
|
+
paths = Dir.glob(pattern)
|
|
99
104
|
paths.sort!
|
|
100
105
|
paths
|
|
101
106
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: megatest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jean Boussier
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: Largely API compatible with test-unit / minitest, but with lots of extra
|
|
13
13
|
modern niceties like a proper CLI, test distribution, etc.
|
|
@@ -25,6 +25,7 @@ files:
|
|
|
25
25
|
- exe/megatest
|
|
26
26
|
- lib/megatest.rb
|
|
27
27
|
- lib/megatest/assertions.rb
|
|
28
|
+
- lib/megatest/autorun.rb
|
|
28
29
|
- lib/megatest/backtrace.rb
|
|
29
30
|
- lib/megatest/cli.rb
|
|
30
31
|
- lib/megatest/compat.rb
|
|
@@ -74,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
75
|
- !ruby/object:Gem::Version
|
|
75
76
|
version: '0'
|
|
76
77
|
requirements: []
|
|
77
|
-
rubygems_version:
|
|
78
|
+
rubygems_version: 4.0.3
|
|
78
79
|
specification_version: 4
|
|
79
80
|
summary: Modern test-unit style test framework
|
|
80
81
|
test_files: []
|