megatest 0.1.1 → 0.2.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 +6 -0
- data/TODO.md +2 -0
- data/lib/megatest/cli.rb +6 -0
- data/lib/megatest/config.rb +22 -0
- data/lib/megatest/executor.rb +6 -0
- data/lib/megatest/multi_process.rb +10 -2
- data/lib/megatest/reporters.rb +28 -3
- data/lib/megatest/runner.rb +2 -0
- data/lib/megatest/runtime.rb +10 -1
- data/lib/megatest/selector.rb +4 -0
- data/lib/megatest/state.rb +1 -1
- data/lib/megatest/stubs.rb +113 -0
- data/lib/megatest/test.rb +7 -0
- data/lib/megatest/version.rb +1 -1
- 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: 5ef5044b5a70005d87f32c0da4add1e5270d3a298b20a12f4da2fefcb3b64781
|
|
4
|
+
data.tar.gz: 273e19eed16d3ad7ade77b297ee8e5819629e48d4c106808d52fbf0572538643
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1179b7d68e1bfd7fd614404ef3ab4e9c724af6be4944a68160de44ec0857c120f08ba363acacee46efed133d532cc9aec27d8b8432831d61373a8c845df54a33
|
|
7
|
+
data.tar.gz: 81950c2704d1553a4f6239c3bf45b96213b1418c79293d8dd9070405f884c0514e2851cb8b528ce12e84525cd96b7e0eb59a76a3a24d25abe958a56ec5f142cd
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
- Make the VerboseReporter work with concurrent executors.
|
|
4
|
+
- Fix isolated tests on forkless platforms when the config contains procs.
|
|
5
|
+
- Add a `job_teardown callback` to to stand off for at_exit.
|
|
6
|
+
- Add `stub`, `stub_const` and `stub_any_instance_of`.
|
|
7
|
+
- Add support for `-I` in the CLI.
|
|
8
|
+
|
|
3
9
|
## [0.1.1] - 2024-08-20
|
|
4
10
|
|
|
5
11
|
- Fix `$PATH` prefix detection.
|
data/TODO.md
CHANGED
data/lib/megatest/cli.rb
CHANGED
|
@@ -196,6 +196,12 @@ module Megatest
|
|
|
196
196
|
opts.separator "Options:"
|
|
197
197
|
opts.separator ""
|
|
198
198
|
|
|
199
|
+
opts.on("-I PATHS", "specify $LOAD_PATH directory (may be used more than once)") do |paths|
|
|
200
|
+
paths.split(":").each do |path|
|
|
201
|
+
$LOAD_PATH.unshift(path)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
199
205
|
opts.on("-b", "--backtrace", "Print full backtraces") do
|
|
200
206
|
@config.backtrace.full!
|
|
201
207
|
end
|
data/lib/megatest/config.rb
CHANGED
|
@@ -158,6 +158,7 @@ module Megatest
|
|
|
158
158
|
@before_fork_callbacks = []
|
|
159
159
|
@global_setup_callbacks = []
|
|
160
160
|
@job_setup_callbacks = []
|
|
161
|
+
@job_teardown_callbacks = []
|
|
161
162
|
@heartbeat_frequency = 5
|
|
162
163
|
@backtrace = Backtrace.new
|
|
163
164
|
@program_name = nil
|
|
@@ -264,6 +265,14 @@ module Megatest
|
|
|
264
265
|
@job_setup_callbacks << block
|
|
265
266
|
end
|
|
266
267
|
|
|
268
|
+
def run_job_teardown_callbacks(job_index)
|
|
269
|
+
@job_teardown_callbacks.each { |c| c.call(self, job_index) }
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def job_teardown(&block)
|
|
273
|
+
@job_teardown_callbacks << block
|
|
274
|
+
end
|
|
275
|
+
|
|
267
276
|
def retries?
|
|
268
277
|
@max_retries.positive?
|
|
269
278
|
end
|
|
@@ -275,6 +284,19 @@ module Megatest
|
|
|
275
284
|
@max_retries * size
|
|
276
285
|
end
|
|
277
286
|
end
|
|
287
|
+
|
|
288
|
+
NOT_SERIALIZED = %i(@job_teardown_callbacks @job_setup_callbacks @global_setup_callbacks).freeze
|
|
289
|
+
def marshal_dump
|
|
290
|
+
instance_variables.reject { |k| NOT_SERIALIZED.include?(k) }.to_h do |name|
|
|
291
|
+
[name, instance_variable_get(name)]
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def marshal_load(hash)
|
|
296
|
+
hash.each do |name, value|
|
|
297
|
+
instance_variable_set(name, value)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
278
300
|
end
|
|
279
301
|
|
|
280
302
|
@config = Config.new({})
|
data/lib/megatest/executor.rb
CHANGED
|
@@ -53,6 +53,10 @@ module Megatest
|
|
|
53
53
|
@out = Output.new(out, colors: @config.colors)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def concurrent?
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
|
|
56
60
|
def run(queue, reporters)
|
|
57
61
|
start_time = Megatest.now
|
|
58
62
|
|
|
@@ -98,6 +102,8 @@ module Megatest
|
|
|
98
102
|
@out.error("Exited early because too many failures were encountered")
|
|
99
103
|
end
|
|
100
104
|
|
|
105
|
+
@config.run_job_teardown_callbacks(nil)
|
|
106
|
+
|
|
101
107
|
queue.cleanup
|
|
102
108
|
end
|
|
103
109
|
end
|
|
@@ -27,7 +27,7 @@ module Megatest
|
|
|
27
27
|
def <<(message)
|
|
28
28
|
begin
|
|
29
29
|
@socket.write(Marshal.dump(message))
|
|
30
|
-
rescue Errno::EPIPE
|
|
30
|
+
rescue Errno::EPIPE, Errno::ENOTCONN
|
|
31
31
|
return nil # Other side was closed
|
|
32
32
|
end
|
|
33
33
|
self
|
|
@@ -113,6 +113,7 @@ module Megatest
|
|
|
113
113
|
|
|
114
114
|
# We don't want to run at_exit hooks the app may have
|
|
115
115
|
# installed.
|
|
116
|
+
@config.run_job_teardown_callbacks(@index)
|
|
116
117
|
Process.exit!(0)
|
|
117
118
|
end
|
|
118
119
|
@child_socket.close
|
|
@@ -157,15 +158,17 @@ module Megatest
|
|
|
157
158
|
@parent_socket.close
|
|
158
159
|
when :pop
|
|
159
160
|
if @assigned_test = queue.pop_test
|
|
161
|
+
reporters.each { |r| r.before_test_case(queue, @assigned_test) }
|
|
160
162
|
@parent_socket << @assigned_test&.id
|
|
161
163
|
else
|
|
162
164
|
@idle = true
|
|
163
165
|
end
|
|
164
166
|
when :record
|
|
165
167
|
result = queue.record_result(*args)
|
|
168
|
+
test_case = @assigned_test
|
|
166
169
|
@assigned_test = nil
|
|
167
170
|
@parent_socket << result
|
|
168
|
-
reporters.each { |r| r.after_test_case(queue,
|
|
171
|
+
reporters.each { |r| r.after_test_case(queue, test_case, result) }
|
|
169
172
|
@config.circuit_breaker.record_result(result)
|
|
170
173
|
else
|
|
171
174
|
raise "Unexpected message: #{message.inspect}"
|
|
@@ -208,6 +211,10 @@ module Megatest
|
|
|
208
211
|
@out = Output.new(out, colors: config.colors)
|
|
209
212
|
end
|
|
210
213
|
|
|
214
|
+
def concurrent?
|
|
215
|
+
true
|
|
216
|
+
end
|
|
217
|
+
|
|
211
218
|
def after_fork_in_child(active_job)
|
|
212
219
|
@jobs.each do |job|
|
|
213
220
|
job.close unless job == active_job
|
|
@@ -217,6 +224,7 @@ module Megatest
|
|
|
217
224
|
def run(queue, reporters)
|
|
218
225
|
start_time = Megatest.now
|
|
219
226
|
@config.run_global_setup_callbacks
|
|
227
|
+
reporters.each { |r| r.start(self, queue) }
|
|
220
228
|
@jobs = @config.jobs_count.times.map { |index| Job.new(@config, index) }
|
|
221
229
|
|
|
222
230
|
@config.before_fork_callbacks.each(&:call)
|
data/lib/megatest/reporters.rb
CHANGED
|
@@ -125,12 +125,37 @@ module Megatest
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
class VerboseReporter < SimpleReporter
|
|
128
|
+
def start(executor, _queue)
|
|
129
|
+
@concurrent = executor.concurrent?
|
|
130
|
+
end
|
|
131
|
+
|
|
128
132
|
def before_test_case(_queue, test_case)
|
|
129
|
-
@
|
|
133
|
+
unless @concurrent
|
|
134
|
+
@out.print("#{test_case.id} = ")
|
|
135
|
+
end
|
|
130
136
|
end
|
|
131
137
|
|
|
132
|
-
def after_test_case(_queue,
|
|
133
|
-
|
|
138
|
+
def after_test_case(_queue, test_case, result)
|
|
139
|
+
if @concurrent
|
|
140
|
+
@out.print("#{test_case.id} = ")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if result.skipped?
|
|
144
|
+
@out.print(@out.yellow("SKIPPED"))
|
|
145
|
+
elsif result.retried?
|
|
146
|
+
@out.print(@out.yellow("RETRIED"))
|
|
147
|
+
elsif result.error?
|
|
148
|
+
@out.print(@out.red("ERROR"))
|
|
149
|
+
elsif result.failed?
|
|
150
|
+
@out.print(@out.red("FAILED"))
|
|
151
|
+
else
|
|
152
|
+
@out.print(@out.green("SUCCESS"))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if result.duration
|
|
156
|
+
@out.print " (in #{result.duration.round(3)}s)"
|
|
157
|
+
end
|
|
158
|
+
|
|
134
159
|
@out.puts
|
|
135
160
|
if result.bad?
|
|
136
161
|
@out.puts @out.colored(render_failure(result))
|
data/lib/megatest/runner.rb
CHANGED
data/lib/megatest/runtime.rb
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
module Megatest
|
|
6
6
|
class Runtime
|
|
7
|
-
attr_reader :test_case, :result
|
|
7
|
+
attr_reader :config, :test_case, :result, :on_teardown
|
|
8
8
|
|
|
9
9
|
def initialize(config, test_case, result)
|
|
10
10
|
@config = config
|
|
11
11
|
@test_case = test_case
|
|
12
12
|
@result = result
|
|
13
13
|
@asserting = false
|
|
14
|
+
@on_teardown = []
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
support_locations = begin
|
|
@@ -150,6 +151,14 @@ module Megatest
|
|
|
150
151
|
@config.diff(expected, actual)
|
|
151
152
|
end
|
|
152
153
|
|
|
154
|
+
def teardown
|
|
155
|
+
until @on_teardown.empty?
|
|
156
|
+
record_failures do
|
|
157
|
+
@on_teardown.pop.call
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
153
162
|
def record_failures(downlevel: 1, &block)
|
|
154
163
|
expect_no_failures(&block)
|
|
155
164
|
rescue Assertion => assertion
|
data/lib/megatest/selector.rb
CHANGED
data/lib/megatest/state.rb
CHANGED
|
@@ -232,7 +232,7 @@ module Megatest
|
|
|
232
232
|
class SharedSuite < Suite
|
|
233
233
|
def initialize(registry, test_suite)
|
|
234
234
|
super(registry)
|
|
235
|
-
@
|
|
235
|
+
@klass = test_suite
|
|
236
236
|
@test_cases = {}
|
|
237
237
|
test_suite.instance_methods.each do |name|
|
|
238
238
|
if name.start_with?("test_")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Megatest
|
|
4
|
+
class Stubber < Module
|
|
5
|
+
DEFAULT = ->(*) {}
|
|
6
|
+
DEFAULT.ruby2_keywords if DEFAULT.respond_to?(:ruby2_keywords)
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def for(object)
|
|
10
|
+
for_class(class << object; self; end)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def for_class(klass)
|
|
14
|
+
unless stubber = klass.included_modules.find { |m| Stubber === m }
|
|
15
|
+
stubber = Stubber.new
|
|
16
|
+
klass.prepend(stubber)
|
|
17
|
+
end
|
|
18
|
+
stubber
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def stub_method(method, proc)
|
|
23
|
+
proc ||= DEFAULT
|
|
24
|
+
|
|
25
|
+
if method_defined?(method, false) # Already stubbed that method
|
|
26
|
+
old_method = instance_method(method)
|
|
27
|
+
alias_method(method, method) # Silence redefinition warnings
|
|
28
|
+
define_method(method, &proc)
|
|
29
|
+
-> do
|
|
30
|
+
alias_method(method, method) # Silence redefinition warnings
|
|
31
|
+
define_method(method, old_method)
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
define_method(method, &proc)
|
|
35
|
+
-> { remove_method(method) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Stubs
|
|
41
|
+
def stub(object, method, proc = nil)
|
|
42
|
+
stubber = ::Megatest::Stubber.for(object)
|
|
43
|
+
teardown = stubber.stub_method(method, proc)
|
|
44
|
+
|
|
45
|
+
if block_given?
|
|
46
|
+
begin
|
|
47
|
+
yield
|
|
48
|
+
ensure
|
|
49
|
+
teardown.call
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
@__m.on_teardown << teardown
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def stub_any_instance_of(klass, method, proc = nil)
|
|
57
|
+
raise ArgumentError, "stub_any_instance_of expects a Module or Class" unless Module === klass
|
|
58
|
+
|
|
59
|
+
stubber = ::Megatest::Stubber.for_class(klass)
|
|
60
|
+
teardown = stubber.stub_method(method, proc)
|
|
61
|
+
|
|
62
|
+
if block_given?
|
|
63
|
+
begin
|
|
64
|
+
yield
|
|
65
|
+
ensure
|
|
66
|
+
teardown.call
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
@__m.on_teardown << teardown
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def stub_const(mod, constant, new_value, exists: true)
|
|
74
|
+
if exists
|
|
75
|
+
old_value = mod.const_get(constant, false)
|
|
76
|
+
teardown = -> do
|
|
77
|
+
mod.send(:remove_const, constant) if mod.const_defined?(constant, false)
|
|
78
|
+
mod.const_set(constant, old_value)
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
if mod.const_defined?(constant)
|
|
82
|
+
raise NameError, "already defined constant #{constant} in #{mod.name || mod.inspect}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
teardown = -> do
|
|
86
|
+
mod.send(:remove_const, constant) if mod.const_defined?(constant, false)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
apply = -> do
|
|
91
|
+
mod.send(:remove_const, constant) if exists
|
|
92
|
+
mod.const_set(constant, new_value)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if block_given?
|
|
96
|
+
begin
|
|
97
|
+
apply.call
|
|
98
|
+
yield
|
|
99
|
+
ensure
|
|
100
|
+
teardown.call
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
begin
|
|
104
|
+
apply.call
|
|
105
|
+
rescue
|
|
106
|
+
teardown.call
|
|
107
|
+
raise
|
|
108
|
+
end
|
|
109
|
+
@__m.on_teardown << teardown
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/lib/megatest/test.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "megatest/assertions"
|
|
4
|
+
require "megatest/stubs"
|
|
4
5
|
|
|
5
6
|
module Megatest
|
|
6
7
|
##
|
|
@@ -99,6 +100,7 @@ module Megatest
|
|
|
99
100
|
# :startdoc:
|
|
100
101
|
extend DSL
|
|
101
102
|
include Assertions
|
|
103
|
+
include Stubs
|
|
102
104
|
|
|
103
105
|
# Returns the current Megatest::State::TestCase instance
|
|
104
106
|
# Can be used for self introspection
|
|
@@ -106,6 +108,11 @@ module Megatest
|
|
|
106
108
|
@__m.test_case
|
|
107
109
|
end
|
|
108
110
|
|
|
111
|
+
# Returns the global megatest config object.
|
|
112
|
+
def __config__
|
|
113
|
+
@__m.config
|
|
114
|
+
end
|
|
115
|
+
|
|
109
116
|
# Returns the current Megatest::TestCaseResult instance
|
|
110
117
|
# Can be used for self introspection during teardown
|
|
111
118
|
def __result__
|
data/lib/megatest/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: megatest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jean Boussier
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-08-
|
|
11
|
+
date: 2024-08-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Largely API compatible with test-unit / minitest, but with lots of extra
|
|
14
14
|
modern niceties like a proper CLI, test distribution, etc.
|
|
@@ -45,6 +45,7 @@ files:
|
|
|
45
45
|
- lib/megatest/runtime.rb
|
|
46
46
|
- lib/megatest/selector.rb
|
|
47
47
|
- lib/megatest/state.rb
|
|
48
|
+
- lib/megatest/stubs.rb
|
|
48
49
|
- lib/megatest/subprocess.rb
|
|
49
50
|
- lib/megatest/subprocess/main.rb
|
|
50
51
|
- lib/megatest/test.rb
|
|
@@ -73,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
73
74
|
- !ruby/object:Gem::Version
|
|
74
75
|
version: '0'
|
|
75
76
|
requirements: []
|
|
76
|
-
rubygems_version: 3.
|
|
77
|
+
rubygems_version: 3.5.11
|
|
77
78
|
signing_key:
|
|
78
79
|
specification_version: 4
|
|
79
80
|
summary: Modern test-unit style test framework
|