activesupport 6.1.3.1 → 7.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +151 -485
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +0 -2
  6. data/lib/active_support/benchmarkable.rb +2 -2
  7. data/lib/active_support/cache/file_store.rb +16 -10
  8. data/lib/active_support/cache/mem_cache_store.rb +119 -28
  9. data/lib/active_support/cache/memory_store.rb +21 -13
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +39 -59
  12. data/lib/active_support/cache/strategy/local_cache.rb +29 -49
  13. data/lib/active_support/cache.rb +196 -46
  14. data/lib/active_support/callbacks.rb +35 -31
  15. data/lib/active_support/concern.rb +5 -5
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  17. data/lib/active_support/concurrency/share_lock.rb +2 -2
  18. data/lib/active_support/configurable.rb +6 -3
  19. data/lib/active_support/configuration_file.rb +7 -2
  20. data/lib/active_support/core_ext/array/access.rb +1 -5
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -6
  22. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  23. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  24. data/lib/active_support/core_ext/date/blank.rb +1 -1
  25. data/lib/active_support/core_ext/date/calculations.rb +2 -2
  26. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  27. data/lib/active_support/core_ext/digest/uuid.rb +13 -12
  28. data/lib/active_support/core_ext/enumerable.rb +64 -12
  29. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  30. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  31. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  32. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  33. data/lib/active_support/core_ext/name_error.rb +2 -8
  34. data/lib/active_support/core_ext/numeric/conversions.rb +2 -2
  35. data/lib/active_support/core_ext/object/blank.rb +2 -2
  36. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  37. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  38. data/lib/active_support/core_ext/object/json.rb +29 -24
  39. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  40. data/lib/active_support/core_ext/object/try.rb +20 -20
  41. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  42. data/lib/active_support/core_ext/range/each.rb +1 -1
  43. data/lib/active_support/core_ext/range/include_time_with_zone.rb +1 -1
  44. data/lib/active_support/core_ext/string/filters.rb +1 -1
  45. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  46. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  47. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  48. data/lib/active_support/core_ext/time/calculations.rb +5 -9
  49. data/lib/active_support/core_ext/time/zones.rb +2 -17
  50. data/lib/active_support/core_ext/uri.rb +1 -15
  51. data/lib/active_support/current_attributes.rb +18 -1
  52. data/lib/active_support/dependencies/interlock.rb +10 -18
  53. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  54. data/lib/active_support/dependencies.rb +58 -788
  55. data/lib/active_support/deprecation/behaviors.rb +4 -1
  56. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  57. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  58. data/lib/active_support/deprecation.rb +1 -1
  59. data/lib/active_support/descendants_tracker.rb +12 -9
  60. data/lib/active_support/digest.rb +5 -3
  61. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  62. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  63. data/lib/active_support/duration.rb +77 -48
  64. data/lib/active_support/encrypted_configuration.rb +11 -1
  65. data/lib/active_support/encrypted_file.rb +1 -1
  66. data/lib/active_support/environment_inquirer.rb +1 -1
  67. data/lib/active_support/evented_file_update_checker.rb +1 -1
  68. data/lib/active_support/fork_tracker.rb +2 -2
  69. data/lib/active_support/gem_version.rb +4 -4
  70. data/lib/active_support/hash_with_indifferent_access.rb +8 -1
  71. data/lib/active_support/i18n.rb +1 -0
  72. data/lib/active_support/inflector/inflections.rb +11 -4
  73. data/lib/active_support/inflector/methods.rb +23 -47
  74. data/lib/active_support/json/encoding.rb +3 -3
  75. data/lib/active_support/key_generator.rb +18 -1
  76. data/lib/active_support/locale/en.yml +2 -2
  77. data/lib/active_support/log_subscriber.rb +13 -3
  78. data/lib/active_support/logger_thread_safe_level.rb +5 -13
  79. data/lib/active_support/message_encryptor.rb +3 -3
  80. data/lib/active_support/message_verifier.rb +4 -4
  81. data/lib/active_support/messages/metadata.rb +2 -2
  82. data/lib/active_support/multibyte/chars.rb +10 -11
  83. data/lib/active_support/multibyte/unicode.rb +2 -2
  84. data/lib/active_support/multibyte.rb +1 -1
  85. data/lib/active_support/notifications/fanout.rb +31 -11
  86. data/lib/active_support/notifications/instrumenter.rb +17 -0
  87. data/lib/active_support/notifications.rb +10 -0
  88. data/lib/active_support/number_helper/number_converter.rb +1 -3
  89. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  90. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  91. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  92. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  93. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  94. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  95. data/lib/active_support/number_helper.rb +0 -2
  96. data/lib/active_support/option_merger.rb +4 -16
  97. data/lib/active_support/ordered_hash.rb +1 -1
  98. data/lib/active_support/parameter_filter.rb +5 -0
  99. data/lib/active_support/per_thread_registry.rb +1 -0
  100. data/lib/active_support/railtie.rb +34 -11
  101. data/lib/active_support/rescuable.rb +2 -2
  102. data/lib/active_support/secure_compare_rotator.rb +1 -1
  103. data/lib/active_support/security_utils.rb +1 -1
  104. data/lib/active_support/string_inquirer.rb +0 -2
  105. data/lib/active_support/subscriber.rb +5 -0
  106. data/lib/active_support/tagged_logging.rb +1 -1
  107. data/lib/active_support/test_case.rb +9 -21
  108. data/lib/active_support/testing/assertions.rb +35 -5
  109. data/lib/active_support/testing/deprecation.rb +1 -1
  110. data/lib/active_support/testing/isolation.rb +1 -1
  111. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  112. data/lib/active_support/testing/parallelization/server.rb +4 -0
  113. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  114. data/lib/active_support/testing/parallelization.rb +4 -0
  115. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  116. data/lib/active_support/testing/stream.rb +3 -5
  117. data/lib/active_support/testing/tagged_logging.rb +1 -1
  118. data/lib/active_support/testing/time_helpers.rb +13 -2
  119. data/lib/active_support/time_with_zone.rb +19 -6
  120. data/lib/active_support/values/time_zone.rb +25 -9
  121. data/lib/active_support/xml_mini/jdom.rb +1 -1
  122. data/lib/active_support/xml_mini/libxml.rb +5 -5
  123. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  124. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  125. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  126. data/lib/active_support/xml_mini/rexml.rb +1 -1
  127. data/lib/active_support/xml_mini.rb +2 -1
  128. data/lib/active_support.rb +14 -1
  129. metadata +11 -25
  130. data/lib/active_support/core_ext/marshal.rb +0 -26
  131. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -21,7 +21,7 @@ module ActiveSupport
21
21
  #
22
22
  # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
23
23
  # with other implementations.
24
- class OrderedHash < ::Hash
24
+ class OrderedHash < ::Hash # :nodoc:
25
25
  def to_yaml_type
26
26
  "!tag:yaml.org,2002:omap"
27
27
  end
@@ -16,6 +16,11 @@ module ActiveSupport
16
16
  # ActiveSupport::ParameterFilter.new([:foo, "bar"])
17
17
  # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
18
18
  #
19
+ # ActiveSupport::ParameterFilter.new([/\Apin\z/i, /\Apin_/i])
20
+ # => replaces the value for the exact (case-insensitive) key 'pin' and all
21
+ # (case-insensitive) keys beginning with 'pin_', with "[FILTERED]".
22
+ # Does not match keys with 'pin' as a substring, such as 'shipping_id'.
23
+ #
19
24
  # ActiveSupport::ParameterFilter.new(["credit_card.code"])
20
25
  # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
21
26
  # change { file: { code: "xxxx"} }
@@ -56,5 +56,6 @@ module ActiveSupport
56
56
 
57
57
  send(name, *args, &block)
58
58
  end
59
+ ruby2_keywords(:method_missing)
59
60
  end
60
61
  end
@@ -9,6 +9,15 @@ module ActiveSupport
9
9
 
10
10
  config.eager_load_namespaces << ActiveSupport
11
11
 
12
+ initializer "active_support.remove_deprecated_time_with_zone_name" do |app|
13
+ config.after_initialize do
14
+ if app.config.active_support.remove_deprecated_time_with_zone_name
15
+ require "active_support/time_with_zone"
16
+ TimeWithZone.singleton_class.remove_method(:name)
17
+ end
18
+ end
19
+ end
20
+
12
21
  initializer "active_support.set_authenticated_message_encryption" do |app|
13
22
  config.after_initialize do
14
23
  unless app.config.active_support.use_authenticated_message_encryption.nil?
@@ -30,16 +39,22 @@ module ActiveSupport
30
39
  end
31
40
 
32
41
  initializer "active_support.deprecation_behavior" do |app|
33
- if deprecation = app.config.active_support.deprecation
34
- ActiveSupport::Deprecation.behavior = deprecation
35
- end
42
+ if app.config.active_support.report_deprecations == false
43
+ ActiveSupport::Deprecation.silenced = true
44
+ ActiveSupport::Deprecation.behavior = :silence
45
+ ActiveSupport::Deprecation.disallowed_behavior = :silence
46
+ else
47
+ if deprecation = app.config.active_support.deprecation
48
+ ActiveSupport::Deprecation.behavior = deprecation
49
+ end
36
50
 
37
- if disallowed_deprecation = app.config.active_support.disallowed_deprecation
38
- ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
39
- end
51
+ if disallowed_deprecation = app.config.active_support.disallowed_deprecation
52
+ ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
53
+ end
40
54
 
41
- if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
42
- ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
55
+ if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
56
+ ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
57
+ end
43
58
  end
44
59
  end
45
60
 
@@ -87,10 +102,10 @@ module ActiveSupport
87
102
  if app.config.active_support.use_sha1_digests
88
103
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
89
104
  config.active_support.use_sha1_digests is deprecated and will
90
- be removed from Rails 6.2. Use
91
- config.active_support.hash_digest_class = ::Digest::SHA1 instead.
105
+ be removed from Rails 7.0. Use
106
+ config.active_support.hash_digest_class = OpenSSL::Digest::SHA1 instead.
92
107
  MSG
93
- ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
108
+ ActiveSupport::Digest.hash_digest_class = OpenSSL::Digest::SHA1
94
109
  end
95
110
 
96
111
  if klass = app.config.active_support.hash_digest_class
@@ -98,5 +113,13 @@ module ActiveSupport
98
113
  end
99
114
  end
100
115
  end
116
+
117
+ initializer "active_support.set_key_generator_hash_digest_class" do |app|
118
+ config.after_initialize do
119
+ if klass = app.config.active_support.key_generator_hash_digest_class
120
+ ActiveSupport::KeyGenerator.hash_digest_class = klass
121
+ end
122
+ end
123
+ end
101
124
  end
102
125
  end
@@ -100,7 +100,7 @@ module ActiveSupport
100
100
  end
101
101
  end
102
102
 
103
- def handler_for_rescue(exception, object: self) #:nodoc:
103
+ def handler_for_rescue(exception, object: self) # :nodoc:
104
104
  case rescuer = find_rescue_handler(exception)
105
105
  when Symbol
106
106
  method = object.method(rescuer)
@@ -167,7 +167,7 @@ module ActiveSupport
167
167
 
168
168
  # Internal handler lookup. Delegates to class method. Some libraries call
169
169
  # this directly, so keeping it around for compatibility.
170
- def handler_for_rescue(exception) #:nodoc:
170
+ def handler_for_rescue(exception) # :nodoc:
171
171
  self.class.handler_for_rescue exception, object: self
172
172
  end
173
173
  end
@@ -17,7 +17,7 @@ module ActiveSupport
17
17
  #
18
18
  # class MyController < ApplicationController
19
19
  # def authenticate_request
20
- # rotator = ActiveSupport::SecureComparerotator.new('new_password')
20
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_password')
21
21
  # rotator.rotate('old_password')
22
22
  #
23
23
  # authenticate_or_request_with_http_basic do |username, password|
@@ -31,7 +31,7 @@ module ActiveSupport
31
31
  # the secret length. This should be considered when using secure_compare
32
32
  # to compare weak, short secrets to user input.
33
33
  def secure_compare(a, b)
34
- a.length == b.length && fixed_length_secure_compare(a, b)
34
+ a.bytesize == b.bytesize && fixed_length_secure_compare(a, b)
35
35
  end
36
36
  module_function :secure_compare
37
37
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/symbol/starts_ends_with"
4
-
5
3
  module ActiveSupport
6
4
  # Wrapping a string in this class gives you a prettier way to test
7
5
  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
@@ -150,6 +150,11 @@ module ActiveSupport
150
150
  send(method, event)
151
151
  end
152
152
 
153
+ def publish_event(event) # :nodoc:
154
+ method = event.name.split(".").first
155
+ send(method, event)
156
+ end
157
+
153
158
  private
154
159
  def event_stack
155
160
  SubscriberQueueRegistry.instance.get_queue(@queue_key)
@@ -79,7 +79,7 @@ module ActiveSupport
79
79
  end
80
80
 
81
81
  def self.new(logger)
82
- logger = logger.dup
82
+ logger = logger.clone
83
83
 
84
84
  if logger.formatter
85
85
  logger.formatter = logger.formatter.dup
@@ -12,6 +12,7 @@ require "active_support/testing/constant_lookup"
12
12
  require "active_support/testing/time_helpers"
13
13
  require "active_support/testing/file_fixtures"
14
14
  require "active_support/testing/parallelization"
15
+ require "active_support/testing/parallelize_executor"
15
16
  require "concurrent/utility/processor_counter"
16
17
 
17
18
  module ActiveSupport
@@ -71,26 +72,17 @@ module ActiveSupport
71
72
  #
72
73
  # The threaded parallelization uses minitest's parallel executor directly.
73
74
  # The processes parallelization uses a Ruby DRb server.
74
- def parallelize(workers: :number_of_processors, with: :processes)
75
+ #
76
+ # Because parallelization presents an overhead, it is only enabled when the
77
+ # number of tests to run is above the +threshold+ param. The default value is
78
+ # 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
79
+ def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
75
80
  workers = Concurrent.physical_processor_count if workers == :number_of_processors
76
81
  workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
77
82
 
78
83
  return if workers <= 1
79
84
 
80
- executor = case with
81
- when :processes
82
- Testing::Parallelization.new(workers)
83
- when :threads
84
- Minitest::Parallel::Executor.new(workers)
85
- else
86
- raise ArgumentError, "#{with} is not a supported parallelization executor."
87
- end
88
-
89
- self.lock_threads = false if defined?(self.lock_threads) && with == :threads
90
-
91
- Minitest.parallel_executor = executor
92
-
93
- parallelize_me!
85
+ Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
94
86
  end
95
87
 
96
88
  # Set up hook for parallel testing. This can be used if you have multiple
@@ -107,9 +99,7 @@ module ActiveSupport
107
99
  # end
108
100
  # end
109
101
  def parallelize_setup(&block)
110
- ActiveSupport::Testing::Parallelization.after_fork_hook do |worker|
111
- yield worker
112
- end
102
+ ActiveSupport::Testing::Parallelization.after_fork_hook(&block)
113
103
  end
114
104
 
115
105
  # Clean up hook for parallel testing. This can be used to drop databases
@@ -126,9 +116,7 @@ module ActiveSupport
126
116
  # end
127
117
  # end
128
118
  def parallelize_teardown(&block)
129
- ActiveSupport::Testing::Parallelization.run_cleanup_hook do |worker|
130
- yield worker
131
- end
119
+ ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
132
120
  end
133
121
  end
134
122
 
@@ -99,7 +99,7 @@ module ActiveSupport
99
99
  }
100
100
  before = exps.map(&:call)
101
101
 
102
- retval = assert_nothing_raised(&block)
102
+ retval = _assert_nothing_raised_or_warn("assert_difference", &block)
103
103
 
104
104
  expressions.zip(exps, before) do |(code, diff), exp, before_value|
105
105
  error = "#{code.inspect} didn't change by #{diff}"
@@ -176,7 +176,7 @@ module ActiveSupport
176
176
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
177
177
 
178
178
  before = exp.call
179
- retval = assert_nothing_raised(&block)
179
+ retval = _assert_nothing_raised_or_warn("assert_changes", &block)
180
180
 
181
181
  unless from == UNTRACKED
182
182
  error = "Expected change from #{from.inspect}"
@@ -189,7 +189,7 @@ module ActiveSupport
189
189
  error = "#{expression.inspect} didn't change"
190
190
  error = "#{error}. It was already #{to}" if before == to
191
191
  error = "#{message}.\n#{error}" if message
192
- assert_not_equal before, after, error
192
+ refute_equal before, after, error
193
193
 
194
194
  unless to == UNTRACKED
195
195
  error = "Expected change to #{to}\n"
@@ -207,16 +207,30 @@ module ActiveSupport
207
207
  # post :create, params: { status: { ok: true } }
208
208
  # end
209
209
  #
210
+ # Provide the optional keyword argument :from to specify the expected
211
+ # initial value.
212
+ #
213
+ # assert_no_changes -> { Status.all_good? }, from: true do
214
+ # post :create, params: { status: { ok: true } }
215
+ # end
216
+ #
210
217
  # An error message can be specified.
211
218
  #
212
219
  # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
213
220
  # post :create, params: { status: { ok: false } }
214
221
  # end
215
- def assert_no_changes(expression, message = nil, &block)
222
+ def assert_no_changes(expression, message = nil, from: UNTRACKED, &block)
216
223
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
217
224
 
218
225
  before = exp.call
219
- retval = assert_nothing_raised(&block)
226
+ retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
227
+
228
+ unless from == UNTRACKED
229
+ error = "Expected initial value of #{from.inspect}"
230
+ error = "#{message}.\n#{error}" if message
231
+ assert from === before, error
232
+ end
233
+
220
234
  after = exp.call
221
235
 
222
236
  error = "#{expression.inspect} changed"
@@ -230,6 +244,22 @@ module ActiveSupport
230
244
 
231
245
  retval
232
246
  end
247
+
248
+ private
249
+ def _assert_nothing_raised_or_warn(assertion, &block)
250
+ assert_nothing_raised(&block)
251
+ rescue Minitest::UnexpectedError => e
252
+ if tagged_logger && tagged_logger.warn?
253
+ warning = <<~MSG
254
+ #{self.class} - #{name}: #{e.error.class} raised.
255
+ If you expected this exception, use `assert_raises` as near to the code that raises as possible.
256
+ Other block based assertions (e.g. `#{assertion}`) can be used, as long as `assert_raises` is inside their block.
257
+ MSG
258
+ tagged_logger.warn warning
259
+ end
260
+
261
+ raise
262
+ end
233
263
  end
234
264
  end
235
265
  end
@@ -4,7 +4,7 @@ require "active_support/deprecation"
4
4
 
5
5
  module ActiveSupport
6
6
  module Testing
7
- module Deprecation #:nodoc:
7
+ module Deprecation # :nodoc:
8
8
  def assert_deprecated(match = nil, deprecator = nil, &block)
9
9
  result, warnings = collect_deprecations(deprecator, &block)
10
10
  assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
@@ -5,7 +5,7 @@ module ActiveSupport
5
5
  module Isolation
6
6
  require "thread"
7
7
 
8
- def self.included(klass) #:nodoc:
8
+ def self.included(klass) # :nodoc:
9
9
  klass.class_eval do
10
10
  parallelize_me!
11
11
  end
@@ -6,10 +6,10 @@ module ActiveSupport
6
6
  module Testing
7
7
  module MethodCallAssertions # :nodoc:
8
8
  private
9
- def assert_called(object, method_name, message = nil, times: 1, returns: nil)
9
+ def assert_called(object, method_name, message = nil, times: 1, returns: nil, &block)
10
10
  times_called = 0
11
11
 
12
- object.stub(method_name, proc { times_called += 1; returns }) { yield }
12
+ object.stub(method_name, proc { times_called += 1; returns }, &block)
13
13
 
14
14
  error = "Expected #{method_name} to be called #{times} times, " \
15
15
  "but was called #{times_called} times"
@@ -17,16 +17,16 @@ module ActiveSupport
17
17
  assert_equal times, times_called, error
18
18
  end
19
19
 
20
- def assert_called_with(object, method_name, args, returns: nil)
20
+ def assert_called_with(object, method_name, args, returns: nil, &block)
21
21
  mock = Minitest::Mock.new
22
22
 
23
- if args.all? { |arg| arg.is_a?(Array) }
23
+ if args.all?(Array)
24
24
  args.each { |arg| mock.expect(:call, returns, arg) }
25
25
  else
26
26
  mock.expect(:call, returns, args)
27
27
  end
28
28
 
29
- object.stub(method_name, mock) { yield }
29
+ object.stub(method_name, mock, &block)
30
30
 
31
31
  mock.verify
32
32
  end
@@ -49,6 +49,10 @@ module ActiveSupport
49
49
  @active_workers.size > 0
50
50
  end
51
51
 
52
+ def interrupt
53
+ @queue.clear
54
+ end
55
+
52
56
  def shutdown
53
57
  # Wait for initial queue to drain
54
58
  while @queue.length != 0
@@ -69,6 +69,9 @@ module ActiveSupport
69
69
  Minitest::UnexpectedError.new(error)
70
70
  end
71
71
  @queue.record(reporter, result)
72
+ rescue Interrupt
73
+ @queue.interrupt
74
+ raise
72
75
  end
73
76
 
74
77
  set_process_title("(idle)")
@@ -42,6 +42,10 @@ module ActiveSupport
42
42
  @queue_server << work
43
43
  end
44
44
 
45
+ def size
46
+ @worker_count
47
+ end
48
+
45
49
  def shutdown
46
50
  @queue_server.shutdown
47
51
  @worker_pool.each { |pid| Process.waitpid pid }
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ class ParallelizeExecutor # :nodoc:
6
+ attr_reader :size, :parallelize_with, :threshold
7
+
8
+ def initialize(size:, with:, threshold: ActiveSupport.test_parallelization_threshold)
9
+ @size = size
10
+ @parallelize_with = with
11
+ @threshold = threshold
12
+ end
13
+
14
+ def start
15
+ parallelize if should_parallelize?
16
+ show_execution_info
17
+
18
+ parallel_executor.start if parallelized?
19
+ end
20
+
21
+ def <<(work)
22
+ parallel_executor << work if parallelized?
23
+ end
24
+
25
+ def shutdown
26
+ parallel_executor.shutdown if parallelized?
27
+ end
28
+
29
+ private
30
+ def parallel_executor
31
+ @parallel_executor ||= build_parallel_executor
32
+ end
33
+
34
+ def build_parallel_executor
35
+ case parallelize_with
36
+ when :processes
37
+ Testing::Parallelization.new(size)
38
+ when :threads
39
+ ActiveSupport::TestCase.lock_threads = false if defined?(ActiveSupport::TestCase.lock_threads)
40
+ Minitest::Parallel::Executor.new(size)
41
+ else
42
+ raise ArgumentError, "#{parallelize_with} is not a supported parallelization executor."
43
+ end
44
+ end
45
+
46
+ def parallelize
47
+ @parallelized = true
48
+ Minitest::Test.parallelize_me!
49
+ end
50
+
51
+ def parallelized?
52
+ @parallelized if defined?(@parallelized)
53
+ end
54
+
55
+ def should_parallelize?
56
+ ENV["PARALLEL_WORKERS"] || tests_count > threshold
57
+ end
58
+
59
+ def tests_count
60
+ @tests_count ||= Minitest::Runnable.runnables.sum { |runnable| runnable.runnable_methods.size }
61
+ end
62
+
63
+ def show_execution_info
64
+ puts execution_info
65
+ end
66
+
67
+ def execution_info
68
+ if parallelized?
69
+ "Running #{tests_count} tests in parallel using #{parallel_executor.size} #{parallelize_with}"
70
+ else
71
+ "Running #{tests_count} tests in a single process (parallelization threshold is #{threshold})"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end