minitest-distributed 0.1.2 → 0.2.2

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/Gemfile +2 -2
  4. data/README.md +35 -13
  5. data/bin/setup +0 -2
  6. data/lib/minitest/distributed/configuration.rb +66 -4
  7. data/lib/minitest/distributed/coordinators/coordinator_interface.rb +3 -0
  8. data/lib/minitest/distributed/coordinators/memory_coordinator.rb +30 -9
  9. data/lib/minitest/distributed/coordinators/redis_coordinator.rb +259 -154
  10. data/lib/minitest/distributed/enqueued_runnable.rb +197 -40
  11. data/lib/minitest/distributed/filters/exclude_file_filter.rb +18 -0
  12. data/lib/minitest/distributed/filters/exclude_filter.rb +4 -4
  13. data/lib/minitest/distributed/filters/file_filter_base.rb +29 -0
  14. data/lib/minitest/distributed/filters/filter_interface.rb +3 -3
  15. data/lib/minitest/distributed/filters/include_file_filter.rb +18 -0
  16. data/lib/minitest/distributed/filters/include_filter.rb +4 -4
  17. data/lib/minitest/distributed/reporters/distributed_progress_reporter.rb +13 -5
  18. data/lib/minitest/distributed/reporters/distributed_summary_reporter.rb +49 -10
  19. data/lib/minitest/distributed/reporters/junitxml_reporter.rb +150 -0
  20. data/lib/minitest/distributed/reporters/redis_coordinator_warnings_reporter.rb +11 -16
  21. data/lib/minitest/distributed/result_aggregate.rb +38 -9
  22. data/lib/minitest/distributed/result_type.rb +76 -2
  23. data/lib/minitest/distributed/test_selector.rb +12 -6
  24. data/lib/minitest/distributed/version.rb +1 -1
  25. data/lib/minitest/distributed.rb +3 -0
  26. data/lib/minitest/distributed_plugin.rb +1 -25
  27. data/lib/minitest/junitxml_plugin.rb +21 -0
  28. data/sorbet/rbi/minitest.rbi +29 -11
  29. data/sorbet/rbi/redis.rbi +19 -4
  30. metadata +11 -7
  31. data/.travis.yml +0 -6
@@ -0,0 +1,150 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'rexml/document'
5
+ require 'fileutils'
6
+
7
+ module Minitest
8
+ module Distributed
9
+ module Reporters
10
+ # Reporter that generates a JUnit XML report of the results it is presented.
11
+ #
12
+ # The JUnitXML schema is not very well standardized, and many implementations deviate
13
+ # from the schema (see https://www.ibm.com/support/knowledgecenter/SSQ2R2_14.2.0/com.ibm.rsar.analysis.codereview.cobol.doc/topics/cac_useresults_junit.html).
14
+ #
15
+ # This JunitXML importer embraces this flexibility, and extends the format with some additional
16
+ # information that we can use to create more meaningful annotations. For instance, the information
17
+ # can be use to set annotations on your build system or for annotations using the GitHub checks API.
18
+ #
19
+ # For the implementation, we use REXML to prevent the need of additional dependencies on this gem.
20
+ # We also use XML 1.1, which allows more characters to be valid. We are primarily interested in
21
+ # this so \e is an allowed character, which is used for ANSI color coding.
22
+ class JUnitXMLReporter < Minitest::Reporter
23
+ extend T::Sig
24
+
25
+ sig { returns(T::Hash[String, T::Array[Minitest::Result]]) }
26
+ attr_reader :results
27
+
28
+ sig { params(io: IO, options: T::Hash[Symbol, T.untyped]).void }
29
+ def initialize(io, options)
30
+ @io = io
31
+ @report_path = T.let(options.fetch(:junitxml), String)
32
+ @results = T.let(Hash.new { |hash, key| hash[key] = [] }, T::Hash[String, T::Array[Minitest::Result]])
33
+ end
34
+
35
+ sig { override.params(result: Minitest::Result).void }
36
+ def record(result)
37
+ case (result_type = ResultType.of(result))
38
+ when ResultType::Passed, ResultType::Failed, ResultType::Error
39
+ T.must(results[result.klass]) << result
40
+ when ResultType::Skipped, ResultType::Requeued, ResultType::Discarded
41
+ # We will not include skipped, requeued, and discarded tests in JUnitXML reports,
42
+ # because they will not fail builds, but also didn't pass.
43
+ else
44
+ T.absurd(result_type)
45
+ end
46
+ end
47
+
48
+ sig { override.void }
49
+ def report
50
+ FileUtils.mkdir_p(File.dirname(@report_path))
51
+ File.open(@report_path, 'w+') do |file|
52
+ format_document(generate_document, file)
53
+ end
54
+ end
55
+
56
+ sig { returns(REXML::Document) }
57
+ def generate_document
58
+ doc = REXML::Document.new(nil, prologue_quote: :quote, attribute_quote: :quote)
59
+ doc << REXML::XMLDecl.new('1.1', 'utf-8')
60
+
61
+ testsuites = doc.add_element('testsuites')
62
+ results.each do |suite, tests|
63
+ add_tests_to(testsuites, suite, tests)
64
+ end
65
+ doc
66
+ end
67
+
68
+ sig { params(doc: REXML::Document, io: IO).void }
69
+ def format_document(doc, io)
70
+ formatter = REXML::Formatters::Pretty.new
71
+ formatter.write(doc, io)
72
+ io << "\n"
73
+ end
74
+
75
+ private
76
+
77
+ sig { params(testsuites: REXML::Element, suite: String, results: T::Array[Minitest::Result]).void }
78
+ def add_tests_to(testsuites, suite, results)
79
+ # TODO: make path relative to project root
80
+ relative_path = T.must(results.first).source_location.first
81
+ lineno = T.must(results.first).source_location.last
82
+
83
+ testsuite = testsuites.add_element(
84
+ 'testsuite',
85
+ { 'name' => suite, 'filepath' => relative_path }.merge(aggregate_suite_results(results))
86
+ )
87
+
88
+ results.each do |test|
89
+ attributes = {
90
+ 'name' => test.name,
91
+ 'classname' => suite,
92
+ 'assertions' => test.assertions,
93
+ 'time' => test.time,
94
+ # 'run-command' => ... # TODO
95
+ }
96
+ attributes['lineno'] = lineno if lineno != -1
97
+
98
+ testcase_tag = testsuite.add_element('testcase', attributes)
99
+ add_failure_tag_if_needed(testcase_tag, test)
100
+ end
101
+ end
102
+
103
+ sig { params(testcase: REXML::Element, result: Minitest::Result).void }
104
+ def add_failure_tag_if_needed(testcase, result)
105
+ case (result_type = ResultType.of(result))
106
+ when ResultType::Passed, ResultType::Skipped, ResultType::Requeued, ResultType::Discarded
107
+ # noop
108
+ when ResultType::Error, ResultType::Failed
109
+ failure = T.must(result.failure)
110
+ failure_tag = testcase.add_element('failure',
111
+ 'type' => result_type.serialize,
112
+ 'message' => truncate_message(failure.message))
113
+ failure_tag.add_text(REXML::CData.new(result.to_s))
114
+ else
115
+ T.absurd(result_type)
116
+ end
117
+ end
118
+
119
+ sig { params(message: String).returns(String) }
120
+ def truncate_message(message)
121
+ T.must(message.lines.first).chomp.gsub(/\e\[[^m]+m/, '')
122
+ end
123
+
124
+ sig { params(results: T::Array[Minitest::Result]).returns(T::Hash[String, Numeric]) }
125
+ def aggregate_suite_results(results)
126
+ aggregate = Hash.new(0)
127
+ results.each do |result|
128
+ aggregate['assertions'] += result.assertions
129
+ aggregate['failures'] += 1 if failure?(ResultType.of(result))
130
+ aggregate['tests'] += 1
131
+ aggregate['time'] += result.time
132
+ end
133
+ aggregate
134
+ end
135
+
136
+ sig { params(result_type: ResultType).returns(T::Boolean) }
137
+ def failure?(result_type)
138
+ case result_type
139
+ when ResultType::Failed, ResultType::Error
140
+ true
141
+ when ResultType::Passed, ResultType::Skipped, ResultType::Discarded, ResultType::Requeued
142
+ false
143
+ else
144
+ T.absurd(result_type)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -9,9 +9,10 @@ module Minitest
9
9
 
10
10
  sig { override.void }
11
11
  def report
12
- [reclaim_warning, missing_acks_warning].compact.each do |warning|
13
- io.puts
12
+ warnings = [reclaim_timeout_warning, reclaim_failed_warning].compact
13
+ warnings.each do |warning|
14
14
  io.puts(warning)
15
+ io.puts
15
16
  end
16
17
  end
17
18
 
@@ -28,30 +29,24 @@ module Minitest
28
29
  end
29
30
 
30
31
  sig { returns(T.nilable(String)) }
31
- def reclaim_warning
32
- if redis_coordinator.reclaimed_tests.any?
32
+ def reclaim_timeout_warning
33
+ if redis_coordinator.reclaimed_timeout_tests.any?
33
34
  <<~WARNING
34
35
  WARNING: The following tests were reclaimed from another worker:
35
- #{redis_coordinator.reclaimed_tests.map { |test| "- #{test.identifier}" }.join("\n")}
36
+ #{redis_coordinator.reclaimed_timeout_tests.map { |test| "- #{test.identifier}" }.join("\n")}
36
37
 
37
- The original worker did not complete running this test in #{configuration.test_timeout}ms.
38
+ The original worker did not complete running these tests in #{configuration.test_timeout_seconds}s.
38
39
  This either means that the worker unexpectedly went away, or that the test is too slow.
39
40
  WARNING
40
41
  end
41
42
  end
42
43
 
43
44
  sig { returns(T.nilable(String)) }
44
- def missing_acks_warning
45
- local_results = redis_coordinator.local_results
46
- if local_results.acks < local_results.size
45
+ def reclaim_failed_warning
46
+ if redis_coordinator.reclaimed_failed_tests.any?
47
47
  <<~WARNING
48
- WARNING: This worker was not able to ack all the test it ran with the coordinator (#{local_results.acks}/#{local_results.size}).
49
-
50
- This means that this worker took too long to report the status of one or more tests,
51
- and these tests were claimed by other workers. As a result, the total number of
52
- reported runs may be larger than the size of the test suite.
53
-
54
- Make sure that all your tests complete within #{configuration.test_timeout}ms.
48
+ WARNING: The following tests were reclaimed from another worker because they failed:
49
+ #{redis_coordinator.reclaimed_failed_tests.map { |test| "- #{test.identifier}" }.join("\n")}
55
50
  WARNING
56
51
  end
57
52
  end
@@ -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
@@ -1,6 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'pathname'
5
+
4
6
  module Minitest
5
7
  module Distributed
6
8
  class TestSelector
@@ -24,18 +26,22 @@ module Minitest
24
26
  def initialize_filters
25
27
  @filters << Filters::IncludeFilter.new(options[:filter]) if options[:filter]
26
28
  @filters << Filters::ExcludeFilter.new(options[:exclude]) if options[:exclude]
29
+
30
+ exclude_file = options[:distributed].exclude_file
31
+ @filters << Filters::ExcludeFileFilter.new(Pathname.new(exclude_file)) if exclude_file
32
+
33
+ include_file = options[:distributed].include_file
34
+ @filters << Filters::IncludeFileFilter.new(Pathname.new(include_file)) if include_file
27
35
  end
28
36
 
29
- sig { returns(T::Array[EnqueuedRunnable]) }
37
+ sig { returns(T::Array[Minitest::Runnable]) }
30
38
  def discover_tests
31
39
  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
40
+ runnable.runnable_methods.map { |method_name| runnable.new(method_name) }
35
41
  end
36
42
  end
37
43
 
38
- sig { params(tests: T::Array[EnqueuedRunnable]).returns(T::Array[EnqueuedRunnable]) }
44
+ sig { params(tests: T::Array[Minitest::Runnable]).returns(T::Array[Minitest::Runnable]) }
39
45
  def select_tests(tests)
40
46
  return tests if filters.empty?
41
47
  tests.flat_map do |runnable_method|
@@ -45,7 +51,7 @@ module Minitest
45
51
  end.compact
46
52
  end
47
53
 
48
- sig { returns(T::Array[EnqueuedRunnable]) }
54
+ sig { returns(T::Array[Minitest::Runnable]) }
49
55
  def tests
50
56
  select_tests(discover_tests)
51
57
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Minitest
5
5
  module Distributed
6
- VERSION = "0.1.2"
6
+ VERSION = "0.2.2"
7
7
  end
8
8
  end
@@ -13,6 +13,9 @@ require "minitest/distributed/result_aggregate"
13
13
  require "minitest/distributed/filters/filter_interface"
14
14
  require "minitest/distributed/filters/include_filter"
15
15
  require "minitest/distributed/filters/exclude_filter"
16
+ require "minitest/distributed/filters/file_filter_base"
17
+ require "minitest/distributed/filters/exclude_file_filter"
18
+ require "minitest/distributed/filters/include_file_filter"
16
19
  require "minitest/distributed/coordinators/coordinator_interface"
17
20
  require "minitest/distributed/coordinators/memory_coordinator"
18
21
  require "minitest/distributed/coordinators/redis_coordinator"
@@ -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, options)
40
16
  end
41
17
 
42
18
  def plugin_distributed_init(options)
@@ -0,0 +1,21 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Minitest
5
+ class << self
6
+ def plugin_junitxml_options(opts, options)
7
+ options[:junitxml] = ENV['MINITEST_JUNITXML']
8
+
9
+ opts.on('--junitxml=PATH', "Generate a JUnitXML report at the specified path") do |path|
10
+ options[:junitxml] = path
11
+ end
12
+ end
13
+
14
+ def plugin_junitxml_init(options)
15
+ return if options[:junitxml].nil?
16
+
17
+ require 'minitest/distributed/reporters/junitxml_reporter'
18
+ reporter << Minitest::Distributed::Reporters::JUnitXMLReporter.new(options[:io], options)
19
+ end
20
+ end
21
+ end
@@ -1,23 +1,23 @@
1
- # This file is autogenerated. Do not edit it by hand. Regenerate it with:
2
- # srb rbi sorbet-typed
3
- #
4
- # If you would like to make changes to this file, great! Please upstream any changes you make here:
5
- #
6
- # https://github.com/sorbet/sorbet-typed/edit/master/lib/minitest/all/minitest.rbi
7
- #
8
- # typed: strong
1
+ # typed: true
9
2
 
10
3
  module Minitest
11
4
  class Runnable
12
5
  def self.run_one_method(klass, method_name, reporter); end
13
6
  def self.runnables; end
14
7
 
8
+ def initialize(method_name); end
9
+
15
10
  def name; end
16
11
  def time; end
17
12
  def time=(duration); end
18
13
  def failures; end
19
-
20
- def initialize(method_name); end
14
+ def failures=(failures); end
15
+ def assertions; end
16
+ def assertions=(assertions); end
17
+ def source_location; end
18
+ def source_location=(value); end
19
+ def klass; end
20
+ def klass=(value); end
21
21
  end
22
22
 
23
23
  class Test < Runnable
@@ -61,10 +61,15 @@ module Minitest
61
61
  def result_code; end
62
62
  end
63
63
 
64
- class Skip < Exception
64
+ class Skip < Assertion
65
65
  end
66
66
 
67
67
  class UnexpectedError < Assertion
68
+ sig { params(error: Exception).void }
69
+ def initialize(error); end
70
+
71
+ sig { returns(Exception) }
72
+ def error; end
68
73
  end
69
74
 
70
75
  class AbstractReporter
@@ -126,6 +131,9 @@ module Minitest
126
131
 
127
132
  sig { returns(Float) }
128
133
  def self.clock_time; end
134
+
135
+ def self.backtrace_filter; end
136
+ def self.backtrace_filter=(filter); end
129
137
  end
130
138
 
131
139
  module Minitest::Assertions
@@ -190,6 +198,16 @@ module Minitest::Assertions
190
198
  end
191
199
  def assert_predicate(obj, predicate, msg = nil); end
192
200
 
201
+ sig do
202
+ params(
203
+ value: BasicObject,
204
+ operator: Symbol,
205
+ comparison: BasicObject,
206
+ msg: T.nilable(String)
207
+ ).returns(TrueClass)
208
+ end
209
+ def assert_operator(value, operator, comparison, msg = nil); end
210
+
193
211
  sig { params(test: T.untyped, msg: T.nilable(String)).returns(TrueClass) }
194
212
  def refute(test, msg = nil); end
195
213
 
data/sorbet/rbi/redis.rbi CHANGED
@@ -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