minitest-distributed 0.1.2 → 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.
@@ -6,41 +6,51 @@ module Minitest
6
6
  class ResultAggregate < T::Struct
7
7
  extend T::Sig
8
8
 
9
+ const :max_failures, T.nilable(Integer)
10
+
11
+ # These are maintained between different attempts for the same run ID
9
12
  prop :runs, Integer, default: 0
10
13
  prop :assertions, Integer, default: 0
11
14
  prop :passes, Integer, default: 0
12
15
  prop :failures, Integer, default: 0
13
16
  prop :errors, Integer, default: 0
14
17
  prop :skips, Integer, default: 0
15
- prop :reruns, Integer, default: 0
18
+ prop :requeues, Integer, default: 0
19
+ prop :discards, Integer, default: 0
20
+
21
+ # These are reset between different attempts for the same run ID
16
22
  prop :acks, Integer, default: 0
17
23
  prop :size, Integer, default: 0
18
24
 
19
- sig { params(result: Minitest::Result).void }
20
- def update_with_result(result)
21
- case (result_type = ResultType.of(result))
25
+ sig { params(runnable_result: EnqueuedRunnable::Result).void }
26
+ def update_with_result(runnable_result)
27
+ case (result_type = ResultType.of(runnable_result.committed_result))
22
28
  when ResultType::Passed then self.passes += 1
23
29
  when ResultType::Failed then self.failures += 1
24
30
  when ResultType::Error then self.errors += 1
25
31
  when ResultType::Skipped then self.skips += 1
32
+ when ResultType::Discarded then self.discards += 1
33
+ when ResultType::Requeued then self.requeues += 1
26
34
  else T.absurd(result_type)
27
35
  end
28
36
 
37
+ self.acks += 1 if runnable_result.final? && runnable_result.commit.success?
29
38
  self.runs += 1
30
- self.assertions += result.assertions
39
+ self.assertions += runnable_result.committed_result.assertions
31
40
  end
32
41
 
33
42
  sig { returns(String) }
34
43
  def to_s
35
44
  str = +"#{runs} runs, #{assertions} assertions, #{passes} passes, #{failures} failures, #{errors} errors"
36
45
  str << ", #{skips} skips" if skips > 0
37
- str << ", #{reruns} re-runs" if reruns > 0
46
+ str << ", #{requeues} re-queued" if requeues > 0
47
+ str << ", #{discards} discarded" if discards > 0
38
48
  str
39
49
  end
40
50
 
41
51
  sig { returns(Integer) }
42
52
  def unique_runs
43
- runs - reruns
53
+ runs - requeues - discards
44
54
  end
45
55
 
46
56
  sig { returns(Integer) }
@@ -49,18 +59,37 @@ module Minitest
49
59
  end
50
60
 
51
61
  sig { returns(T::Boolean) }
52
- def completed?
62
+ def complete?
53
63
  acks == size
54
64
  end
55
65
 
66
+ sig { returns(T::Boolean) }
67
+ def abort?
68
+ if (max = max_failures)
69
+ total_failures >= max
70
+ else
71
+ false
72
+ end
73
+ end
74
+
56
75
  sig { returns(T::Boolean) }
57
76
  def valid?
77
+ all_runs_reported? && (complete? || abort?)
78
+ end
79
+
80
+ sig { returns(T::Boolean) }
81
+ def all_runs_reported?
58
82
  unique_runs == reported_results
59
83
  end
60
84
 
85
+ sig { returns(Integer) }
86
+ def total_failures
87
+ failures + errors
88
+ end
89
+
61
90
  sig { returns(T::Boolean) }
62
91
  def passed?
63
- completed? && valid? && self.failures == 0 && self.errors == 0
92
+ total_failures == 0
64
93
  end
65
94
  end
66
95
  end
@@ -2,6 +2,74 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Minitest
5
+ class Discard < Minitest::Skip
6
+ extend T::Sig
7
+
8
+ sig { returns(Minitest::Result) }
9
+ attr_reader :original_result
10
+
11
+ sig { params(message: String, original_result: Minitest::Result).void }
12
+ def initialize(message, original_result:)
13
+ @original_result = original_result
14
+ super(message)
15
+ end
16
+
17
+ sig { override.returns(String) }
18
+ def result_label
19
+ "Discarded"
20
+ end
21
+
22
+ sig { params(result: Minitest::Result, test_timeout_seconds: Float).returns(Minitest::Result) }
23
+ def self.wrap(result, test_timeout_seconds:)
24
+ message = +"This test result was discarded, because it could not be committed to the test run coordinator."
25
+ if result.time > test_timeout_seconds
26
+ message << format(
27
+ "\n\nThe test took %0.3fs to run, longer than the test timeout which is configured to be %0.1fs.\n" \
28
+ "Another worker likely claimed ownership of this test, and will commit the result instead.\n" \
29
+ "For best results, make sure that all your tests finish within %0.1fs.",
30
+ result.time, test_timeout_seconds, test_timeout_seconds
31
+ )
32
+ end
33
+
34
+ discard_assertion = Minitest::Discard.new(message, original_result: result)
35
+ discard_assertion.set_backtrace(caller)
36
+ discarded_result = result.dup
37
+ discarded_result.failures = [discard_assertion]
38
+ discarded_result
39
+ end
40
+ end
41
+
42
+ class Requeue < Minitest::Skip
43
+ extend T::Sig
44
+
45
+ sig { params(message: String, original_result: Minitest::Result).void }
46
+ def initialize(message, original_result:)
47
+ @original_result = original_result
48
+ super(message)
49
+ end
50
+
51
+ sig { override.returns(String) }
52
+ def result_label
53
+ "Requeued"
54
+ end
55
+
56
+ sig { params(result: Minitest::Result, attempt: Integer, max_attempts: Integer).returns(Minitest::Result) }
57
+ def self.wrap(result, attempt:, max_attempts:)
58
+ failure = T.must(result.failure)
59
+
60
+ message = "#{failure.message}\n\nThe test will be retried (attempt #{attempt} of #{max_attempts})"
61
+ requeue_assertion = Minitest::Requeue.new(message, original_result: result)
62
+ requeue_assertion.set_backtrace(failure.backtrace)
63
+
64
+ requeued_result = result.dup
65
+ requeued_result.failures = [requeue_assertion]
66
+ requeued_result
67
+ end
68
+ end
69
+
70
+ class AttemptsExhausted < Minitest::Assertion
71
+ end
72
+
5
73
  module Distributed
6
74
  class ResultType < T::Enum
7
75
  extend T::Sig
@@ -11,16 +79,22 @@ module Minitest
11
79
  Failed = new
12
80
  Error = new
13
81
  Skipped = new
82
+ Discarded = new
83
+ Requeued = new
14
84
  end
15
85
 
16
86
  sig { params(result: Minitest::Result).returns(ResultType) }
17
87
  def self.of(result)
18
88
  if result.passed?
19
89
  Passed
20
- elsif result.error?
21
- Error
90
+ elsif result.failure.is_a?(Minitest::Requeue)
91
+ Requeued
92
+ elsif result.failure.is_a?(Minitest::Discard)
93
+ Discarded
22
94
  elsif result.skipped?
23
95
  Skipped
96
+ elsif result.error?
97
+ Error
24
98
  else
25
99
  Failed
26
100
  end
@@ -26,16 +26,14 @@ module Minitest
26
26
  @filters << Filters::ExcludeFilter.new(options[:exclude]) if options[:exclude]
27
27
  end
28
28
 
29
- sig { returns(T::Array[EnqueuedRunnable]) }
29
+ sig { returns(T::Array[Minitest::Runnable]) }
30
30
  def discover_tests
31
31
  Minitest::Runnable.runnables.flat_map do |runnable|
32
- runnable.runnable_methods.map do |method_name|
33
- EnqueuedRunnable.new(class_name: runnable.name, method_name: method_name)
34
- end
32
+ runnable.runnable_methods.map { |method_name| runnable.new(method_name) }
35
33
  end
36
34
  end
37
35
 
38
- sig { params(tests: T::Array[EnqueuedRunnable]).returns(T::Array[EnqueuedRunnable]) }
36
+ sig { params(tests: T::Array[Minitest::Runnable]).returns(T::Array[Minitest::Runnable]) }
39
37
  def select_tests(tests)
40
38
  return tests if filters.empty?
41
39
  tests.flat_map do |runnable_method|
@@ -45,7 +43,7 @@ module Minitest
45
43
  end.compact
46
44
  end
47
45
 
48
- sig { returns(T::Array[EnqueuedRunnable]) }
46
+ sig { returns(T::Array[Minitest::Runnable]) }
49
47
  def tests
50
48
  select_tests(discover_tests)
51
49
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Minitest
5
5
  module Distributed
6
- VERSION = "0.1.2"
6
+ VERSION = "0.2.0"
7
7
  end
8
8
  end
@@ -12,31 +12,7 @@ module Minitest
12
12
  options[:disable_distributed] = true
13
13
  end
14
14
 
15
- options[:distributed] = Minitest::Distributed::Configuration.from_env
16
-
17
- opts.on('--coordinator=URI', "The URI pointing to the coordinator") do |uri|
18
- options[:distributed].coordinator_uri = URI.parse(uri)
19
- end
20
-
21
- opts.on('--test-timeout=TIMEOUT', "The maximum run time for a single test in seconds") do |timeout|
22
- options[:distributed].test_timeout = Float(timeout)
23
- end
24
-
25
- opts.on('--max-attempts=ATTEMPTS', "The maximum number of attempts to run a test") do |attempts|
26
- options[:distributed].max_attempts = Integer(attempts)
27
- end
28
-
29
- opts.on('--test-batch-size=NUMBER', "The number of tests to process per batch") do |batch_size|
30
- options[:distributed].test_batch_size = Integer(batch_size)
31
- end
32
-
33
- opts.on('--run-id=ID', "The ID for this run shared between coordinated workers") do |id|
34
- options[:distributed].run_id = id
35
- end
36
-
37
- opts.on('--worker-id=ID', "The unique ID for this worker") do |id|
38
- options[:distributed].worker_id = id
39
- end
15
+ options[:distributed] = Minitest::Distributed::Configuration.from_command_line_options(opts)
40
16
  end
41
17
 
42
18
  def plugin_distributed_init(options)
@@ -12,12 +12,17 @@ module Minitest
12
12
  def self.run_one_method(klass, method_name, reporter); end
13
13
  def self.runnables; end
14
14
 
15
+ def initialize(method_name); end
16
+
15
17
  def name; end
16
18
  def time; end
17
19
  def time=(duration); end
18
20
  def failures; end
19
-
20
- def initialize(method_name); end
21
+ def failures=(failures); end
22
+ def source_location; end
23
+ def source_location=(value); end
24
+ def klass; end
25
+ def klass=(value); end
21
26
  end
22
27
 
23
28
  class Test < Runnable
@@ -61,7 +66,7 @@ module Minitest
61
66
  def result_code; end
62
67
  end
63
68
 
64
- class Skip < Exception
69
+ class Skip < Assertion
65
70
  end
66
71
 
67
72
  class UnexpectedError < Assertion
@@ -190,6 +195,16 @@ module Minitest::Assertions
190
195
  end
191
196
  def assert_predicate(obj, predicate, msg = nil); end
192
197
 
198
+ sig do
199
+ params(
200
+ value: BasicObject,
201
+ operator: Symbol,
202
+ comparison: BasicObject,
203
+ msg: T.nilable(String)
204
+ ).returns(TrueClass)
205
+ end
206
+ def assert_operator(value, operator, comparison, msg = nil); end
207
+
193
208
  sig { params(test: T.untyped, msg: T.nilable(String)).returns(TrueClass) }
194
209
  def refute(test, msg = nil); end
195
210
 
@@ -7,9 +7,17 @@ class Redis
7
7
  class CommandError < Error
8
8
  end
9
9
 
10
+ class Future
11
+ sig { returns(T.untyped) }
12
+ def value; end
13
+ end
14
+
10
15
  sig { params(options: T::Hash[Symbol, T.untyped]).void }
11
16
  def initialize(options); end
12
17
 
18
+ sig { params(block: T.proc.params(arg0: String).void).void }
19
+ def monitor(&block); end
20
+
13
21
  sig { void }
14
22
  def flushdb; end
15
23
 
@@ -49,7 +57,7 @@ class Redis
49
57
  sig { params(key_value_pairs: T.untyped).returns(T::Boolean) }
50
58
  def msetnx(*key_value_pairs); end
51
59
 
52
- sig { params(key: String).void }
60
+ sig { params(key: String).returns(Integer) }
53
61
  def incr(key); end
54
62
 
55
63
  sig { params(key: String, value: T.untyped).void }
@@ -58,13 +66,20 @@ class Redis
58
66
  sig { params(key: String, start: Integer, stop: Integer).void }
59
67
  def lrange(key, start, stop); end
60
68
 
61
- sig { params(key: String, amount: Integer).void }
69
+ sig { params(key: String, value: T.untyped).returns(T.untyped) }
70
+ def sadd(key, value); end
71
+
72
+ sig { params(key: String, value: T.untyped).returns(T::Boolean) }
73
+ def srem(key, value); end
74
+
75
+ sig { params(key: String, amount: Integer).returns(Integer) }
62
76
  def incrby(key, amount); end
63
77
 
64
- def xadd(*); end
65
- def xack(*); end
78
+ def xadd(key, value); end
79
+ def xack(stream_key, group_name, *entry_ids); end
66
80
  def xgroup(*); end
67
81
  def xpending(*); end
68
82
  def xreadgroup(*); end
69
83
  def xclaim(*); end
84
+ def xinfo(*); end
70
85
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-distributed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-18 00:00:00.000000000 Z
11
+ date: 2020-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest