minitest-distributed 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3898c3d3b163b66922daf90468041da6e76e07c8c102971c8f3cc71ad20e6bd4
4
- data.tar.gz: 19c90e644ce560e5d1981dfb6a72d99bfa98daf95997e684954e3cd4d38906ff
3
+ metadata.gz: '0208858e32026a813e488ae30c72ae1ab9e274574aa55f5eb873f50db170a9d4'
4
+ data.tar.gz: 2e12cb8be3eb47009eb6aa30a5d58b9e7ef796a4e6851b02754fed3d3e261fdb
5
5
  SHA512:
6
- metadata.gz: e772261c32992207573b5e76a12ae50eb26120c4d007f013246c6058821c200900a5a914d2bf474d37ffb571b5b03ae95fcc1e95d73a7eaa5e51ccfc15dc08d4
7
- data.tar.gz: 905aff0608a7cda2910a920bd9650a9097e9da85104795f3b9ff4c143f4d4ea4dd4608f71d8a103275e821d17b9bf3340c68d33abcc0ef44cc304cda089f79c5
6
+ metadata.gz: e18ffe94425ae0726468df99f69d748a4c4d6744960d7f731079bd9dd2a927a924108d6a26e9e6383abe3d91bf311ae7619c666ae563605cacfd7f41c1b7de02
7
+ data.tar.gz: 83ad27805dd3285a398e9266f7ed9d3a0f7e67468d3a9b8020300c7cc81b89b94222b424aa0ec2e5cb3dd231563d75d1c342f55c9c4918435a490697a326318e
@@ -0,0 +1,20 @@
1
+ version: 2
2
+ registries:
3
+ rubygems-server-pkgs-shopify-io:
4
+ type: rubygems-server
5
+ url: https://pkgs.shopify.io
6
+ username: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_USERNAME}}
7
+ password: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_PASSWORD}}
8
+ github-com:
9
+ type: git
10
+ url: https://github.com
11
+ username: ${{secrets.DEPENDENCIES_GITHUB_USER}}
12
+ password: ${{secrets.DEPENDENCIES_GITHUB_TOKEN}}
13
+ updates:
14
+ - package-ecosystem: bundler
15
+ directory: "/"
16
+ schedule:
17
+ interval: daily
18
+ open-pull-requests-limit: 100
19
+ insecure-external-code-execution: allow
20
+ registries: "*"
data/.rubocop.yml CHANGED
@@ -36,6 +36,8 @@ Sorbet/FalseSigil:
36
36
 
37
37
  Sorbet/TrueSigil:
38
38
  Enabled: true
39
+ Exclude:
40
+ - 'test/fixtures/*'
39
41
 
40
42
  Sorbet/EnforceSigilOrder:
41
43
  Enabled: true
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  source "https://rubygems.org"
3
4
 
4
5
  # Specify your gem's dependencies in minitest-distributed.gemspec
data/README.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # minitest-distributed
2
2
 
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md)
4
+
5
+ [About this repo](#about-this-repo) | [Commands](#commands) | [How to use this repo](#how-to-use-this-repo) | [Contribute to this repo](#contribute-to-this-repo) | [License](#license)
6
+
7
+ ## About this repo
8
+ **Introduction:**
9
+
3
10
  `minitest-distributed` is a plugin for [minitest](https://github.com/seattlerb/minitest)
4
11
  for executing tests on a distributed set of unreliable workers.
5
12
 
@@ -16,14 +23,14 @@ flakiness. To combat flakiness, minitest-distributed implements resiliency
16
23
  patterns, like re-running a test on a different worker on failure, and a
17
24
  circuit breaker for misbehaving workers.
18
25
 
19
- ## Usage
20
-
21
- Add `minitest-distributed` to your `Gemfile`, and run `bundle install`. The
22
- plugin will be loaded by minitest automatically. The plugin exposes some
23
- command line arguments that you can use to influence its behavior. They can
24
- also be set using environment variables.
26
+ | | |
27
+ |----------------|--------------------------------------------------------------------------------------------------------------------------------------|
28
+ | Current status | Ongoing |
29
+ | Owner | [@Shopify/test-infra](https://github.com/orgs/Shopify/teams/test-infra) |
30
+ | Help | [#team-test-infra](https://shopify.slack.com/archives/team-test-infra) |
25
31
 
26
- ### Distributed invocation
32
+ ## Commands
33
+ **Distributed invocation**
27
34
 
28
35
  To actually run tests with multiple workers, you have to point every worker to
29
36
  a Redis coordinator, and use the same run identifier.
@@ -52,7 +59,7 @@ Rake::TestTask.new(:test) do |t|
52
59
  end
53
60
  ```
54
61
 
55
- ### Worker retries
62
+ **Worker retries**
56
63
 
57
64
  Many CI systems offer the options to retry jobs that fail. When jobs are
58
65
  retried that were previously part of a worker cluster, all the retried jobs
@@ -61,7 +68,7 @@ during the previous run attempt. This is to make it faster to re-run tests
61
68
  that failed due to flakiness, or confirm that it was not flakiness that caused
62
69
  them to fail.
63
70
 
64
- ### Other optional command line arguments
71
+ **Other optional command line arguments**
65
72
 
66
73
  - `--test-timeout=SECONDS` or `ENV[MINITEST_TEST_TIMEOUT_SECONDS]` (default: 30s):
67
74
  the maximum amount a test is allowed to run before it times out. In a distributed
@@ -87,7 +94,7 @@ them to fail.
87
94
  the test run. The file should include test identifiers seperated by
88
95
  newlines.
89
96
 
90
- ## Limitations
97
+ **Limitations**
91
98
 
92
99
  **Parallel tests not supported:** Minitest comes bundled with a parallel test
93
100
  executor, which will run tests that are specifically tagged as such in
@@ -96,7 +103,20 @@ in parallel using separate processes, generally on different VMs. For this
96
103
  reason, tests marked as `parallel` will not be treated any differently than
97
104
  other tests.
98
105
 
99
- ## Development
106
+ ## How to use this repo
107
+ Add `minitest-distributed` to your `Gemfile`, and run `bundle install`. The
108
+ plugin will be loaded by minitest automatically. The plugin exposes some
109
+ command line arguments that you can use to influence its behavior. They can
110
+ also be set using environment variables.
111
+
112
+ ## Contribute to this repo
113
+ Bug reports and pull requests are welcome on GitHub at
114
+ https://github.com/Shopify/minitest-distributed. This project is intended to
115
+ be a safe, welcoming space for collaboration, and contributors are expected to
116
+ adhere to the [code of
117
+ conduct](https://github.com/Shopify/minitest-distributed/blob/master/CODE_OF_CONDUCT.md).
118
+
119
+ **Development**
100
120
 
101
121
  To bootstrap a local development environment:
102
122
 
@@ -109,7 +129,7 @@ To bootstrap a local development environment:
109
129
  - You can also run `bin/console` for an interactive prompt that will allow you
110
130
  to experiment.
111
131
 
112
- ### Releasing a new version
132
+ **Releasing a new version**
113
133
 
114
134
  - To install this gem onto your local machine, run `bin/rake install`.
115
135
  - Only people at Shopify can release a new version to
@@ -117,21 +137,6 @@ To bootstrap a local development environment:
117
137
  in `version.rb`, and merge to master. Shipit will take care of building the
118
138
  `.gem` bundle, and pushing it to rubygems.org.
119
139
 
120
- ## Contributing
121
-
122
- Bug reports and pull requests are welcome on GitHub at
123
- https://github.com/Shopify/minitest-distributed. This project is intended to
124
- be a safe, welcoming space for collaboration, and contributors are expected to
125
- adhere to the [code of
126
- conduct](https://github.com/Shopify/minitest-distributed/blob/master/CODE_OF_CONDUCT.md).
127
-
128
140
  ## License
129
-
130
141
  The gem is available as open source under the terms of the [MIT
131
142
  License](https://opensource.org/licenses/MIT).
132
-
133
- ## Code of Conduct
134
-
135
- Everyone interacting in the `minitest-distributed` project's codebases, issue
136
- trackers, chat rooms and mailing lists is expected to follow the [code of
137
- conduct](https://github.com/Shopify/minitest-distributed/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "bundler/gem_tasks"
3
4
  require "rake/testtask"
4
5
 
@@ -62,7 +62,7 @@ module Minitest
62
62
 
63
63
  opts.on(
64
64
  "--[no-]retry-failures", "Retry failed and errored tests from a previous run attempt " \
65
- "with the same run ID (default: enabled)"
65
+ "with the same run ID (default: enabled)"
66
66
  ) do |enabled|
67
67
  configuration.retry_failures = enabled
68
68
  end
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "redis"
5
+ require "set"
5
6
 
6
7
  module Minitest
7
8
  module Distributed
@@ -128,7 +129,6 @@ module Minitest
128
129
  argv: [group_name],
129
130
  )
130
131
  keys_deleted == 0
131
-
132
132
  rescue Redis::CommandError => ce
133
133
  if ce.message.include?("BUSYGROUP")
134
134
  # If Redis returns a BUSYGROUP error, it means that the consumer group already
@@ -165,10 +165,10 @@ module Minitest
165
165
  adjust_combined_results(ResultAggregate.new(size: 0))
166
166
  T.let([], T::Array[Minitest::Runnable])
167
167
  else
168
- previous_failures, previous_errors, _deleted = redis.multi do
169
- redis.lrange(list_key(ResultType::Failed.serialize), 0, -1)
170
- redis.lrange(list_key(ResultType::Error.serialize), 0, -1)
171
- redis.del(list_key(ResultType::Failed.serialize), list_key(ResultType::Error.serialize))
168
+ previous_failures, previous_errors, _deleted = redis.multi do |pipeline|
169
+ pipeline.lrange(list_key(ResultType::Failed.serialize), 0, -1)
170
+ pipeline.lrange(list_key(ResultType::Error.serialize), 0, -1)
171
+ pipeline.del(list_key(ResultType::Failed.serialize), list_key(ResultType::Error.serialize))
172
172
  end
173
173
 
174
174
  # We set the `size` key to the number of tests we are planning to schedule.
@@ -199,9 +199,9 @@ module Minitest
199
199
  T.let([], T::Array[Minitest::Runnable])
200
200
  end
201
201
 
202
- redis.pipelined do
202
+ redis.pipelined do |pipeline|
203
203
  tests.each do |test|
204
- redis.xadd(stream_key, { class_name: T.must(test.class.name), method_name: test.name })
204
+ pipeline.xadd(stream_key, { class_name: T.must(test.class.name), method_name: test.name })
205
205
  end
206
206
  end
207
207
  end
@@ -292,6 +292,7 @@ module Minitest
292
292
  end
293
293
  def xclaim_messages(pending_messages, max_idle_time_ms:)
294
294
  return [] if pending_messages.empty?
295
+
295
296
  claimed = redis.xclaim(stream_key, group_name, configuration.worker_id,
296
297
  max_idle_time_ms, pending_messages.keys)
297
298
 
@@ -383,9 +384,9 @@ module Minitest
383
384
  # timeout. If the worker crashes between removing an item from the retry setm the test
384
385
  # will eventually be picked up by another worker.
385
386
  messages_in_retry_set = {}
386
- redis.multi do
387
+ redis.multi do |pipeline|
387
388
  active_messages.each do |key, message|
388
- messages_in_retry_set[key] = redis.srem(key("retry_set"), message.attempt_id)
389
+ messages_in_retry_set[key] = pipeline.srem(key("retry_set"), message.attempt_id)
389
390
  end
390
391
  end
391
392
 
@@ -407,17 +408,17 @@ module Minitest
407
408
 
408
409
  sig { params(results: ResultAggregate).void }
409
410
  def adjust_combined_results(results)
410
- updated = redis.multi do
411
- redis.incrby(key("runs"), results.runs)
412
- redis.incrby(key("assertions"), results.assertions)
413
- redis.incrby(key("passes"), results.passes)
414
- redis.incrby(key("failures"), results.failures)
415
- redis.incrby(key("errors"), results.errors)
416
- redis.incrby(key("skips"), results.skips)
417
- redis.incrby(key("requeues"), results.requeues)
418
- redis.incrby(key("discards"), results.discards)
419
- redis.incrby(key("acks"), results.acks)
420
- redis.incrby(key("size"), results.size)
411
+ updated = redis.multi do |pipeline|
412
+ pipeline.incrby(key("runs"), results.runs)
413
+ pipeline.incrby(key("assertions"), results.assertions)
414
+ pipeline.incrby(key("passes"), results.passes)
415
+ pipeline.incrby(key("failures"), results.failures)
416
+ pipeline.incrby(key("errors"), results.errors)
417
+ pipeline.incrby(key("skips"), results.skips)
418
+ pipeline.incrby(key("requeues"), results.requeues)
419
+ pipeline.incrby(key("discards"), results.discards)
420
+ pipeline.incrby(key("acks"), results.acks)
421
+ pipeline.incrby(key("size"), results.size)
421
422
  end
422
423
 
423
424
  @combined_results = ResultAggregate.new(max_failures: configuration.max_failures,
@@ -450,14 +451,14 @@ module Minitest
450
451
 
451
452
  # Try to commit all the results of this batch to Redis
452
453
  runnable_results = []
453
- redis.multi do
454
+ redis.multi do |pipeline|
454
455
  results.each do |enqueued_runnable, initial_result|
455
456
  runnable_results << enqueued_runnable.commit_result(initial_result) do |result_to_commit|
456
457
  if ResultType.of(result_to_commit) == ResultType::Requeued
457
- sadd_future = redis.sadd(key("retry_set"), enqueued_runnable.attempt_id)
458
+ sadd_future = pipeline.sadd(key("retry_set"), enqueued_runnable.attempt_id)
458
459
  EnqueuedRunnable::Result::Commit.new { sadd_future.value }
459
460
  else
460
- xack_future = redis.xack(stream_key, group_name, enqueued_runnable.entry_id)
461
+ xack_future = pipeline.xack(stream_key, group_name, enqueued_runnable.entry_id)
461
462
  EnqueuedRunnable::Result::Commit.new { xack_future.value == 1 }
462
463
  end
463
464
  end
@@ -29,7 +29,7 @@ module Minitest
29
29
  super
30
30
  end
31
31
 
32
- # Note: due to batching and parallel tests, we have no guarantee that `prerecord`
32
+ # NOTE: due to batching and parallel tests, we have no guarantee that `prerecord`
33
33
  # and `record` will be called in succession for the same test without calls to
34
34
  # either method being interjected for other tests.
35
35
  #
@@ -25,8 +25,8 @@ module Minitest
25
25
  if result.time > test_timeout_seconds
26
26
  message << format(
27
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.",
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
30
  result.time, test_timeout_seconds, test_timeout_seconds
31
31
  )
32
32
  end
@@ -44,6 +44,7 @@ module Minitest
44
44
  sig { params(tests: T::Array[Minitest::Runnable]).returns(T::Array[Minitest::Runnable]) }
45
45
  def select_tests(tests)
46
46
  return tests if filters.empty?
47
+
47
48
  tests.flat_map do |runnable_method|
48
49
  filters.flat_map do |filter|
49
50
  filter.call(runnable_method)
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Minitest
5
5
  module Distributed
6
- VERSION = "0.2.4"
6
+ VERSION = "0.2.5"
7
7
  end
8
8
  end
data/sorbet/rbi/redis.rbi CHANGED
@@ -1,4 +1,5 @@
1
1
  # typed: true
2
+ # frozen_string_literal: true
2
3
 
3
4
  class Redis
4
5
  class Error < StandardError
@@ -21,10 +22,10 @@ class Redis
21
22
  sig { void }
22
23
  def flushdb; end
23
24
 
24
- sig { params(block: T.proc.void).returns(T::Array[T.untyped]) }
25
+ sig { params(block: T.proc.params(arg0: Redis::PipelinedConnection).void).returns(T::Array[T.untyped]) }
25
26
  def pipelined(&block); end
26
27
 
27
- sig { params(block: T.proc.void).returns(T::Array[T.untyped]) }
28
+ sig { params(block: T.proc.params(arg0: Redis::PipelinedConnection).void).returns(T::Array[T.untyped]) }
28
29
  def multi(&block); end
29
30
 
30
31
  sig { params(script: String, keys: T::Array[String], argv: T::Array[String]).returns(T.untyped) }
@@ -83,3 +84,23 @@ class Redis
83
84
  def xclaim(*); end
84
85
  def xinfo(*); end
85
86
  end
87
+
88
+ class Redis::PipelinedConnection
89
+ sig { params(key: String, value: T.untyped).returns(T.untyped) }
90
+ def sadd(key, value); end
91
+
92
+ sig { params(key: String, amount: Integer).returns(Integer) }
93
+ def incrby(key, amount); end
94
+
95
+ sig { params(key: String, value: T.untyped).returns(T::Boolean) }
96
+ def srem(key, value); end
97
+
98
+ sig { params(keys: String).void }
99
+ def del(*keys); end
100
+
101
+ sig { params(key: String, start: Integer, stop: Integer).void }
102
+ def lrange(key, start, stop); end
103
+
104
+ def xack(stream_key, group_name, *entry_ids); end
105
+ def xadd(key, value); end
106
+ 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.2.4
4
+ version: 0.2.5
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: 2021-04-06 00:00:00.000000000 Z
11
+ date: 2022-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -74,6 +74,7 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
+ - ".github/dependabot.yml"
77
78
  - ".github/workflows/ruby.yml"
78
79
  - ".gitignore"
79
80
  - ".rubocop.yml"
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  - !ruby/object:Gem::Version
140
141
  version: '0'
141
142
  requirements: []
142
- rubygems_version: 3.0.3
143
+ rubygems_version: 3.2.20
143
144
  signing_key:
144
145
  specification_version: 4
145
146
  summary: Distributed test executor plugin for Minitest