minitest-distributed 0.2.13 → 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/README.md +30 -0
- data/lib/minitest/distributed/configuration.rb +46 -0
- data/lib/minitest/distributed/coordinators/coordinator_interface.rb +6 -0
- data/lib/minitest/distributed/coordinators/memory_coordinator.rb +10 -0
- data/lib/minitest/distributed/coordinators/redis_coordinator.rb +2129 -195
- data/lib/minitest/distributed/enqueued_runnable.rb +2 -1
- data/lib/minitest/distributed/reporters/distributed_summary_reporter.rb +81 -5
- data/lib/minitest/distributed/version.rb +1 -1
- data/sorbet/config +2 -0
- data/sorbet/rbi/minitest.rbi +5 -4
- data/sorbet/rbi/shims/prism.rbi +13 -0
- data/sorbet/typed_overrides.yaml +21 -0
- metadata +4 -2
|
@@ -85,6 +85,7 @@ module Minitest
|
|
|
85
85
|
const :enqueued_runnable, EnqueuedRunnable
|
|
86
86
|
const :initial_result, Minitest::Result
|
|
87
87
|
const :commit, Commit
|
|
88
|
+
const :force_discard, T::Boolean, default: false
|
|
88
89
|
|
|
89
90
|
sig { returns(String) }
|
|
90
91
|
def entry_id
|
|
@@ -104,7 +105,7 @@ module Minitest
|
|
|
104
105
|
sig { returns(Minitest::Result) }
|
|
105
106
|
def committed_result
|
|
106
107
|
@committed_result ||= T.let(
|
|
107
|
-
if final? && commit.failure?
|
|
108
|
+
if force_discard || (final? && commit.failure?)
|
|
108
109
|
# If a runnable result is final, but the acked failed, we will discard the result.
|
|
109
110
|
Minitest::Discard.wrap(
|
|
110
111
|
initial_result,
|
|
@@ -22,12 +22,44 @@ module Minitest
|
|
|
22
22
|
|
|
23
23
|
sig { override.void }
|
|
24
24
|
def report
|
|
25
|
-
print_discard_warning if local_results.discards > 0
|
|
25
|
+
print_discard_warning if local_results.discards > 0 && !current_attempt_truncated? && !superseded?
|
|
26
26
|
|
|
27
|
-
if
|
|
27
|
+
if superseded?
|
|
28
|
+
print_local_results("This worker was superseded; its local results do not affect the authoritative run.")
|
|
29
|
+
return
|
|
30
|
+
elsif registration_rejected?
|
|
31
|
+
print_local_results("Combined results are unavailable because coordinator registration was rejected.")
|
|
32
|
+
return
|
|
33
|
+
elsif truncation_state_invalid?
|
|
34
|
+
print_local_results("Combined results are unavailable because the retained truncation state is incomplete.")
|
|
35
|
+
return
|
|
36
|
+
elsif coordinator_stalled?
|
|
37
|
+
print_local_results("Combined results are unavailable because the Redis coordinator state is invalid.")
|
|
38
|
+
return
|
|
39
|
+
elsif retry_refused_due_to_truncation?
|
|
28
40
|
io.puts("Cannot retry a run that was cut short during the previous attempt.")
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
print_local_results("Combined results were not read for this rejected retry.")
|
|
42
|
+
return
|
|
43
|
+
elsif current_attempt_truncated?
|
|
44
|
+
io.puts("The run was cut short after another worker reached the max-failures limit.")
|
|
45
|
+
print_local_results("Combined results were not read after truncation.")
|
|
46
|
+
return
|
|
47
|
+
elsif configuration.coordinator.aborted?
|
|
48
|
+
print_local_results("Combined results are unavailable because the coordinator aborted this worker.")
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
persisted_truncation = configuration.coordinator.persisted_truncation?
|
|
53
|
+
@combined_results = nil
|
|
54
|
+
unless configuration.coordinator.valid_combined_results?
|
|
55
|
+
if persisted_truncation
|
|
56
|
+
io.puts("The run was cut short after reaching the limit of #{configuration.max_failures} test failures.")
|
|
57
|
+
end
|
|
58
|
+
print_local_results("Combined results are unavailable because terminal coordinator state is invalid.")
|
|
59
|
+
return
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if persisted_truncation
|
|
31
63
|
io.puts("The run was cut short after reaching the limit of #{configuration.max_failures} test failures.")
|
|
32
64
|
io.puts
|
|
33
65
|
end
|
|
@@ -43,6 +75,7 @@ module Minitest
|
|
|
43
75
|
|
|
44
76
|
sig { override.returns(T::Boolean) }
|
|
45
77
|
def passed?
|
|
78
|
+
return true if superseded?
|
|
46
79
|
return false if configuration.coordinator.aborted?
|
|
47
80
|
|
|
48
81
|
# Generally, we want the workers to fail that had at least one failed or errored
|
|
@@ -50,11 +83,18 @@ module Minitest
|
|
|
50
83
|
# encountered a failed test. We trust that the other worker will do this correctly,
|
|
51
84
|
# but we do verify that the statistics for the complete run are valid,
|
|
52
85
|
# to have some protection against unknown edge cases and bugs.
|
|
53
|
-
local_results.passed? &&
|
|
86
|
+
local_results.passed? && configuration.coordinator.valid_combined_results?
|
|
54
87
|
end
|
|
55
88
|
|
|
56
89
|
protected
|
|
57
90
|
|
|
91
|
+
sig { params(message: String).void }
|
|
92
|
+
def print_local_results(message)
|
|
93
|
+
formatted_duration = format("(in %0.3fs)", Minitest.clock_time - @start_time)
|
|
94
|
+
io.puts("This worker: #{local_results} #{formatted_duration}")
|
|
95
|
+
io.puts(message)
|
|
96
|
+
end
|
|
97
|
+
|
|
58
98
|
sig { void }
|
|
59
99
|
def print_discard_warning
|
|
60
100
|
io.puts(<<~WARNING)
|
|
@@ -65,6 +105,42 @@ module Minitest
|
|
|
65
105
|
WARNING
|
|
66
106
|
end
|
|
67
107
|
|
|
108
|
+
sig { returns(T::Boolean) }
|
|
109
|
+
def superseded?
|
|
110
|
+
coordinator = T.unsafe(configuration.coordinator)
|
|
111
|
+
coordinator.respond_to?(:superseded?) && !!coordinator.superseded?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
sig { returns(T::Boolean) }
|
|
115
|
+
def registration_rejected?
|
|
116
|
+
coordinator = T.unsafe(configuration.coordinator)
|
|
117
|
+
coordinator.respond_to?(:registration_rejected?) && !!coordinator.registration_rejected?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
sig { returns(T::Boolean) }
|
|
121
|
+
def truncation_state_invalid?
|
|
122
|
+
coordinator = T.unsafe(configuration.coordinator)
|
|
123
|
+
coordinator.respond_to?(:truncation_state_invalid?) && !!coordinator.truncation_state_invalid?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
sig { returns(T::Boolean) }
|
|
127
|
+
def retry_refused_due_to_truncation?
|
|
128
|
+
coordinator = T.unsafe(configuration.coordinator)
|
|
129
|
+
coordinator.respond_to?(:retry_refused_due_to_truncation?) && !!coordinator.retry_refused_due_to_truncation?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
sig { returns(T::Boolean) }
|
|
133
|
+
def current_attempt_truncated?
|
|
134
|
+
coordinator = T.unsafe(configuration.coordinator)
|
|
135
|
+
coordinator.respond_to?(:current_attempt_truncated?) && !!coordinator.current_attempt_truncated?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
sig { returns(T::Boolean) }
|
|
139
|
+
def coordinator_stalled?
|
|
140
|
+
coordinator = T.unsafe(configuration.coordinator)
|
|
141
|
+
coordinator.respond_to?(:stalled?) && !!coordinator.stalled?
|
|
142
|
+
end
|
|
143
|
+
|
|
68
144
|
sig { returns(ResultAggregate) }
|
|
69
145
|
def local_results
|
|
70
146
|
@local_results ||= T.let(configuration.coordinator.local_results, T.nilable(ResultAggregate))
|
data/sorbet/config
CHANGED
data/sorbet/rbi/minitest.rbi
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: true
|
|
2
2
|
|
|
3
3
|
module Minitest
|
|
4
4
|
class Runnable
|
|
@@ -171,7 +171,7 @@ module Minitest::Assertions
|
|
|
171
171
|
|
|
172
172
|
sig do
|
|
173
173
|
params(
|
|
174
|
-
collection: T
|
|
174
|
+
collection: T.untyped,
|
|
175
175
|
obj: BasicObject,
|
|
176
176
|
msg: T.nilable(String)
|
|
177
177
|
).returns(TrueClass)
|
|
@@ -188,10 +188,11 @@ module Minitest::Assertions
|
|
|
188
188
|
|
|
189
189
|
sig do
|
|
190
190
|
params(
|
|
191
|
-
exp: T.untyped
|
|
191
|
+
exp: T.untyped,
|
|
192
|
+
blk: T.untyped
|
|
192
193
|
).returns(TrueClass)
|
|
193
194
|
end
|
|
194
|
-
def assert_raises(*exp); end
|
|
195
|
+
def assert_raises(*exp, &blk); end
|
|
195
196
|
|
|
196
197
|
sig do
|
|
197
198
|
params(
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Shim for prism 1.9.0's bundled RBI, which references `Prism::LexCompat::Result`
|
|
5
|
+
# in a sig at rbi/prism.rbi:16. `LexCompat` was renamed to `LexResult` upstream
|
|
6
|
+
# but the sig wasn't updated, so Sorbet errors with 5002 (Unable to resolve
|
|
7
|
+
# constant `LexCompat`). Defining the namespace here makes the sig resolve
|
|
8
|
+
# without globally suppressing 5002 — keeping 5002 active for our own code.
|
|
9
|
+
module Prism
|
|
10
|
+
class LexCompat
|
|
11
|
+
class Result; end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Force autogenerated gem RBIs to `# typed: false` via --typed-override
|
|
2
|
+
# (sorbet/config). Editing the sigil in the files themselves would be
|
|
3
|
+
# wiped by `srb rbi gems`.
|
|
4
|
+
false:
|
|
5
|
+
- './sorbet/rbi/gems/ast.rbi'
|
|
6
|
+
- './sorbet/rbi/gems/connection_pool.rbi'
|
|
7
|
+
- './sorbet/rbi/gems/minitest-distributed.rbi'
|
|
8
|
+
- './sorbet/rbi/gems/minitest.rbi'
|
|
9
|
+
- './sorbet/rbi/gems/parallel.rbi'
|
|
10
|
+
- './sorbet/rbi/gems/parser.rbi'
|
|
11
|
+
- './sorbet/rbi/gems/rainbow.rbi'
|
|
12
|
+
- './sorbet/rbi/gems/redis-client.rbi'
|
|
13
|
+
- './sorbet/rbi/gems/redis.rbi'
|
|
14
|
+
- './sorbet/rbi/gems/rexml.rbi'
|
|
15
|
+
- './sorbet/rbi/gems/rubocop-ast.rbi'
|
|
16
|
+
- './sorbet/rbi/gems/rubocop-minitest.rbi'
|
|
17
|
+
- './sorbet/rbi/gems/rubocop-rake.rbi'
|
|
18
|
+
- './sorbet/rbi/gems/rubocop-sorbet.rbi'
|
|
19
|
+
- './sorbet/rbi/gems/ruby-progressbar.rbi'
|
|
20
|
+
- './sorbet/rbi/gems/toxiproxy.rbi'
|
|
21
|
+
- './sorbet/rbi/gems/unicode-display_width.rbi'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minitest-distributed
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Willem van Bergen
|
|
@@ -161,7 +161,9 @@ files:
|
|
|
161
161
|
- sorbet/rbi/minitest.rbi
|
|
162
162
|
- sorbet/rbi/rails.rbi
|
|
163
163
|
- sorbet/rbi/rbconfig.rbi
|
|
164
|
+
- sorbet/rbi/shims/prism.rbi
|
|
164
165
|
- sorbet/rbi/winsize.rbi
|
|
166
|
+
- sorbet/typed_overrides.yaml
|
|
165
167
|
homepage: https://github.com/Shopify/minitest-distributed
|
|
166
168
|
licenses:
|
|
167
169
|
- MIT
|
|
@@ -183,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
183
185
|
- !ruby/object:Gem::Version
|
|
184
186
|
version: '0'
|
|
185
187
|
requirements: []
|
|
186
|
-
rubygems_version: 4.0.
|
|
188
|
+
rubygems_version: 4.0.21
|
|
187
189
|
specification_version: 4
|
|
188
190
|
summary: Distributed test executor plugin for Minitest
|
|
189
191
|
test_files: []
|