activesupport 6.1.0 → 7.1.5.1
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/CHANGELOG.md +1075 -325
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +4 -2
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +32 -7
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +53 -20
- data/lib/active_support/cache/mem_cache_store.rb +201 -62
- data/lib/active_support/cache/memory_store.rb +86 -24
- data/lib/active_support/cache/null_store.rb +16 -2
- data/lib/active_support/cache/redis_cache_store.rb +186 -193
- data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
- data/lib/active_support/cache/strategy/local_cache.rb +63 -71
- data/lib/active_support/cache.rb +487 -249
- data/lib/active_support/callbacks.rb +227 -105
- data/lib/active_support/code_generator.rb +70 -0
- data/lib/active_support/concern.rb +9 -7
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +18 -5
- data/lib/active_support/configuration_file.rb +7 -2
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +15 -13
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +37 -26
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +24 -9
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
- data/lib/active_support/core_ext/digest/uuid.rb +30 -13
- data/lib/active_support/core_ext/enumerable.rb +85 -83
- data/lib/active_support/core_ext/erb/util.rb +196 -0
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +81 -43
- data/lib/active_support/core_ext/module/deprecation.rb +15 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
- data/lib/active_support/core_ext/object/duplicable.rb +31 -11
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
- data/lib/active_support/core_ext/object/json.rb +49 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -4
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with.rb +44 -0
- data/lib/active_support/core_ext/object/with_options.rb +25 -6
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +16 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/conversions.rb +34 -13
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +25 -13
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +17 -10
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +85 -165
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +30 -8
- data/lib/active_support/core_ext/time/conversions.rb +15 -13
- data/lib/active_support/core_ext/time/zones.rb +12 -28
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes.rb +47 -20
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/dependencies/autoload.rb +17 -12
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -788
- data/lib/active_support/deprecation/behaviors.rb +66 -40
- data/lib/active_support/deprecation/constant_accessor.rb +5 -4
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/instance_delegator.rb +31 -4
- data/lib/active_support/deprecation/method_wrappers.rb +9 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
- data/lib/active_support/deprecation/reporting.rb +43 -26
- data/lib/active_support/deprecation.rb +32 -5
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +150 -72
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -3
- data/lib/active_support/duration.rb +83 -52
- data/lib/active_support/encrypted_configuration.rb +72 -9
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +23 -3
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +203 -0
- data/lib/active_support/evented_file_update_checker.rb +20 -7
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -22
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +4 -2
- data/lib/active_support/fork_tracker.rb +28 -11
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +44 -19
- data/lib/active_support/html_safe_translation.rb +53 -0
- data/lib/active_support/i18n.rb +2 -1
- data/lib/active_support/i18n_railtie.rb +21 -14
- data/lib/active_support/inflector/inflections.rb +25 -7
- data/lib/active_support/inflector/methods.rb +50 -64
- data/lib/active_support/inflector/transliterate.rb +4 -2
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +27 -45
- data/lib/active_support/key_generator.rb +31 -6
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +4 -2
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +97 -35
- data/lib/active_support/logger.rb +9 -60
- data/lib/active_support/logger_thread_safe_level.rb +11 -34
- data/lib/active_support/message_encryptor.rb +206 -56
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +292 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +235 -84
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +112 -46
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +12 -11
- data/lib/active_support/multibyte/unicode.rb +9 -49
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +304 -114
- data/lib/active_support/notifications/instrumenter.rb +117 -35
- data/lib/active_support/notifications.rb +25 -25
- data/lib/active_support/number_helper/number_converter.rb +14 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
- data/lib/active_support/number_helper/rounding_helper.rb +2 -6
- data/lib/active_support/number_helper.rb +379 -319
- data/lib/active_support/option_merger.rb +10 -18
- data/lib/active_support/ordered_hash.rb +4 -4
- data/lib/active_support/ordered_options.rb +15 -1
- data/lib/active_support/parameter_filter.rb +105 -81
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/railtie.rb +83 -21
- data/lib/active_support/reloader.rb +13 -5
- data/lib/active_support/rescuable.rb +18 -16
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +18 -11
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +3 -3
- data/lib/active_support/subscriber.rb +11 -40
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +65 -25
- data/lib/active_support/test_case.rb +166 -27
- data/lib/active_support/testing/assertions.rb +61 -15
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +32 -0
- data/lib/active_support/testing/deprecation.rb +53 -2
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +30 -29
- data/lib/active_support/testing/method_call_assertions.rb +24 -11
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +4 -6
- data/lib/active_support/testing/strict_warnings.rb +39 -0
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +49 -16
- data/lib/active_support/time_with_zone.rb +39 -28
- data/lib/active_support/values/time_zone.rb +50 -18
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +4 -11
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +5 -5
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- data/lib/active_support/xml_mini.rb +7 -6
- data/lib/active_support.rb +28 -1
- metadata +150 -18
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/uri.rb +0 -29
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
- data/lib/active_support/per_thread_registry.rb +0 -60
@@ -7,9 +7,9 @@ module ActiveSupport
|
|
7
7
|
module Assertions
|
8
8
|
UNTRACKED = Object.new # :nodoc:
|
9
9
|
|
10
|
-
# Asserts that an expression is not truthy. Passes if
|
11
|
-
# +
|
12
|
-
#
|
10
|
+
# Asserts that an expression is not truthy. Passes if +object+ is +nil+ or
|
11
|
+
# +false+. "Truthy" means "considered true in a conditional" like <tt>if
|
12
|
+
# foo</tt>.
|
13
13
|
#
|
14
14
|
# assert_not nil # => true
|
15
15
|
# assert_not false # => true
|
@@ -23,6 +23,21 @@ module ActiveSupport
|
|
23
23
|
assert !object, message
|
24
24
|
end
|
25
25
|
|
26
|
+
# Asserts that a block raises one of +exp+. This is an enhancement of the
|
27
|
+
# standard Minitest assertion method with the ability to test error
|
28
|
+
# messages.
|
29
|
+
#
|
30
|
+
# assert_raises(ArgumentError, match: /incorrect param/i) do
|
31
|
+
# perform_service(param: 'exception')
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
def assert_raises(*exp, match: nil, &block)
|
35
|
+
error = super(*exp, &block)
|
36
|
+
assert_match(match, error.message) if match
|
37
|
+
error
|
38
|
+
end
|
39
|
+
alias :assert_raise :assert_raises
|
40
|
+
|
26
41
|
# Assertion that the block should not raise an exception.
|
27
42
|
#
|
28
43
|
# Passes if evaluated code in the yielded block raises no exception.
|
@@ -31,7 +46,7 @@ module ActiveSupport
|
|
31
46
|
# perform_service(param: 'no_exception')
|
32
47
|
# end
|
33
48
|
def assert_nothing_raised
|
34
|
-
yield
|
49
|
+
yield.tap { assert(true) }
|
35
50
|
rescue => error
|
36
51
|
raise Minitest::UnexpectedError.new(error)
|
37
52
|
end
|
@@ -50,7 +65,7 @@ module ActiveSupport
|
|
50
65
|
# end
|
51
66
|
#
|
52
67
|
# An arbitrary positive or negative difference can be specified.
|
53
|
-
# The default is
|
68
|
+
# The default is +1+.
|
54
69
|
#
|
55
70
|
# assert_difference 'Article.count', -1 do
|
56
71
|
# post :delete, params: { id: ... }
|
@@ -99,12 +114,13 @@ module ActiveSupport
|
|
99
114
|
}
|
100
115
|
before = exps.map(&:call)
|
101
116
|
|
102
|
-
retval =
|
117
|
+
retval = _assert_nothing_raised_or_warn("assert_difference", &block)
|
103
118
|
|
104
119
|
expressions.zip(exps, before) do |(code, diff), exp, before_value|
|
105
|
-
|
120
|
+
actual = exp.call
|
121
|
+
error = "#{code.inspect} didn't change by #{diff}, but by #{actual - before_value}"
|
106
122
|
error = "#{message}.\n#{error}" if message
|
107
|
-
assert_equal(before_value + diff,
|
123
|
+
assert_equal(before_value + diff, actual, error)
|
108
124
|
end
|
109
125
|
|
110
126
|
retval
|
@@ -159,7 +175,7 @@ module ActiveSupport
|
|
159
175
|
# @object = 42
|
160
176
|
# end
|
161
177
|
#
|
162
|
-
# The keyword arguments
|
178
|
+
# The keyword arguments +:from+ and +:to+ can be given to specify the
|
163
179
|
# expected initial value and the expected value after the block was
|
164
180
|
# executed.
|
165
181
|
#
|
@@ -176,10 +192,10 @@ module ActiveSupport
|
|
176
192
|
exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
|
177
193
|
|
178
194
|
before = exp.call
|
179
|
-
retval =
|
195
|
+
retval = _assert_nothing_raised_or_warn("assert_changes", &block)
|
180
196
|
|
181
197
|
unless from == UNTRACKED
|
182
|
-
error = "Expected change from #{from.inspect}"
|
198
|
+
error = "Expected change from #{from.inspect}, got #{before}"
|
183
199
|
error = "#{message}.\n#{error}" if message
|
184
200
|
assert from === before, error
|
185
201
|
end
|
@@ -189,10 +205,10 @@ module ActiveSupport
|
|
189
205
|
error = "#{expression.inspect} didn't change"
|
190
206
|
error = "#{error}. It was already #{to}" if before == to
|
191
207
|
error = "#{message}.\n#{error}" if message
|
192
|
-
|
208
|
+
refute_equal before, after, error
|
193
209
|
|
194
210
|
unless to == UNTRACKED
|
195
|
-
error = "Expected change to #{to}\n"
|
211
|
+
error = "Expected change to #{to}, got #{after}\n"
|
196
212
|
error = "#{message}.\n#{error}" if message
|
197
213
|
assert to === after, error
|
198
214
|
end
|
@@ -207,16 +223,30 @@ module ActiveSupport
|
|
207
223
|
# post :create, params: { status: { ok: true } }
|
208
224
|
# end
|
209
225
|
#
|
226
|
+
# Provide the optional keyword argument +:from+ to specify the expected
|
227
|
+
# initial value.
|
228
|
+
#
|
229
|
+
# assert_no_changes -> { Status.all_good? }, from: true do
|
230
|
+
# post :create, params: { status: { ok: true } }
|
231
|
+
# end
|
232
|
+
#
|
210
233
|
# An error message can be specified.
|
211
234
|
#
|
212
235
|
# assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
|
213
236
|
# post :create, params: { status: { ok: false } }
|
214
237
|
# end
|
215
|
-
def assert_no_changes(expression, message = nil, &block)
|
238
|
+
def assert_no_changes(expression, message = nil, from: UNTRACKED, &block)
|
216
239
|
exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
|
217
240
|
|
218
241
|
before = exp.call
|
219
|
-
retval =
|
242
|
+
retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
|
243
|
+
|
244
|
+
unless from == UNTRACKED
|
245
|
+
error = "Expected initial value of #{from.inspect}"
|
246
|
+
error = "#{message}.\n#{error}" if message
|
247
|
+
assert from === before, error
|
248
|
+
end
|
249
|
+
|
220
250
|
after = exp.call
|
221
251
|
|
222
252
|
error = "#{expression.inspect} changed"
|
@@ -230,6 +260,22 @@ module ActiveSupport
|
|
230
260
|
|
231
261
|
retval
|
232
262
|
end
|
263
|
+
|
264
|
+
private
|
265
|
+
def _assert_nothing_raised_or_warn(assertion, &block)
|
266
|
+
assert_nothing_raised(&block)
|
267
|
+
rescue Minitest::UnexpectedError => e
|
268
|
+
if tagged_logger && tagged_logger.warn?
|
269
|
+
warning = <<~MSG
|
270
|
+
#{self.class} - #{name}: #{e.error.class} raised.
|
271
|
+
If you expected this exception, use `assert_raises` as near to the code that raises as possible.
|
272
|
+
Other block based assertions (e.g. `#{assertion}`) can be used, as long as `assert_raises` is inside their block.
|
273
|
+
MSG
|
274
|
+
tagged_logger.warn warning
|
275
|
+
end
|
276
|
+
|
277
|
+
raise
|
278
|
+
end
|
233
279
|
end
|
234
280
|
end
|
235
281
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
module Testing
|
5
|
+
module ConstantStubbing
|
6
|
+
# Changes the value of a constant for the duration of a block. Example:
|
7
|
+
#
|
8
|
+
# # World::List::Import::LARGE_IMPORT_THRESHOLD = 5000
|
9
|
+
# stub_const(World::List::Import, :LARGE_IMPORT_THRESHOLD, 1) do
|
10
|
+
# assert_equal 1, World::List::Import::LARGE_IMPORT_THRESHOLD
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# assert_equal 5000, World::List::Import::LARGE_IMPORT_THRESHOLD
|
14
|
+
#
|
15
|
+
# Using this method rather than forcing <tt>World::List::Import::LARGE_IMPORT_THRESHOLD = 5000</tt> prevents
|
16
|
+
# warnings from being thrown, and ensures that the old value is returned after the test has completed.
|
17
|
+
#
|
18
|
+
# Note: Stubbing a const will stub it across all threads. So if you have concurrent threads
|
19
|
+
# (like separate test suites running in parallel) that all depend on the same constant, it's possible
|
20
|
+
# divergent stubbing will trample on each other.
|
21
|
+
def stub_const(mod, constant, new_value)
|
22
|
+
old_value = mod.const_get(constant, false)
|
23
|
+
mod.send(:remove_const, constant)
|
24
|
+
mod.const_set(constant, new_value)
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
mod.send(:remove_const, constant)
|
28
|
+
mod.const_set(constant, old_value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -4,8 +4,35 @@ require "active_support/deprecation"
|
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
6
|
module Testing
|
7
|
-
module Deprecation
|
7
|
+
module Deprecation
|
8
|
+
##
|
9
|
+
# :call-seq:
|
10
|
+
# assert_deprecated(deprecator, &block)
|
11
|
+
# assert_deprecated(match, deprecator, &block)
|
12
|
+
#
|
13
|
+
# Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block.
|
14
|
+
#
|
15
|
+
# assert_deprecated(/foo/, CustomDeprecator) do
|
16
|
+
# CustomDeprecator.warn "foo should no longer be used"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The +match+ object may be a +Regexp+, or +String+ appearing in the message.
|
20
|
+
#
|
21
|
+
# assert_deprecated('foo', CustomDeprecator) do
|
22
|
+
# CustomDeprecator.warn "foo should no longer be used"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# If the +match+ is omitted (or explicitly +nil+), any deprecation warning will match.
|
26
|
+
#
|
27
|
+
# assert_deprecated(CustomDeprecator) do
|
28
|
+
# CustomDeprecator.warn "foo should no longer be used"
|
29
|
+
# end
|
8
30
|
def assert_deprecated(match = nil, deprecator = nil, &block)
|
31
|
+
match, deprecator = nil, match if match.is_a?(ActiveSupport::Deprecation)
|
32
|
+
unless deprecator
|
33
|
+
ActiveSupport.deprecator.warn("assert_deprecated without a deprecator is deprecated")
|
34
|
+
deprecator = ActiveSupport::Deprecation._instance
|
35
|
+
end
|
9
36
|
result, warnings = collect_deprecations(deprecator, &block)
|
10
37
|
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
|
11
38
|
if match
|
@@ -15,14 +42,38 @@ module ActiveSupport
|
|
15
42
|
result
|
16
43
|
end
|
17
44
|
|
45
|
+
# Asserts that no deprecation warnings are emitted by the given deprecator during the execution of the yielded block.
|
46
|
+
#
|
47
|
+
# assert_not_deprecated(CustomDeprecator) do
|
48
|
+
# CustomDeprecator.warn "message" # fails assertion
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# assert_not_deprecated(ActiveSupport::Deprecation.new) do
|
52
|
+
# CustomDeprecator.warn "message" # passes assertion, different deprecator
|
53
|
+
# end
|
18
54
|
def assert_not_deprecated(deprecator = nil, &block)
|
55
|
+
unless deprecator
|
56
|
+
ActiveSupport.deprecator.warn("assert_not_deprecated without a deprecator is deprecated")
|
57
|
+
deprecator = ActiveSupport::Deprecation._instance
|
58
|
+
end
|
19
59
|
result, deprecations = collect_deprecations(deprecator, &block)
|
20
60
|
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
|
21
61
|
result
|
22
62
|
end
|
23
63
|
|
64
|
+
# Returns the return value of the block and an array of all the deprecation warnings emitted by the given
|
65
|
+
# +deprecator+ during the execution of the yielded block.
|
66
|
+
#
|
67
|
+
# collect_deprecations(CustomDeprecator) do
|
68
|
+
# CustomDeprecator.warn "message"
|
69
|
+
# ActiveSupport::Deprecation.new.warn "other message"
|
70
|
+
# :result
|
71
|
+
# end # => [:result, ["message"]]
|
24
72
|
def collect_deprecations(deprecator = nil)
|
25
|
-
deprecator
|
73
|
+
unless deprecator
|
74
|
+
ActiveSupport.deprecator.warn("collect_deprecations without a deprecator is deprecated")
|
75
|
+
deprecator = ActiveSupport::Deprecation._instance
|
76
|
+
end
|
26
77
|
old_behavior = deprecator.behavior
|
27
78
|
deprecations = []
|
28
79
|
deprecator.behavior = Proc.new do |message, callstack|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
module Testing
|
5
|
+
module ErrorReporterAssertions
|
6
|
+
module ErrorCollector # :nodoc:
|
7
|
+
@subscribed = false
|
8
|
+
@mutex = Mutex.new
|
9
|
+
|
10
|
+
Report = Struct.new(:error, :handled, :severity, :context, :source, keyword_init: true)
|
11
|
+
class Report
|
12
|
+
alias_method :handled?, :handled
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def record
|
17
|
+
subscribe
|
18
|
+
recorders = ActiveSupport::IsolatedExecutionState[:active_support_error_reporter_assertions] ||= []
|
19
|
+
reports = []
|
20
|
+
recorders << reports
|
21
|
+
begin
|
22
|
+
yield
|
23
|
+
reports
|
24
|
+
ensure
|
25
|
+
recorders.delete_if { |r| reports.equal?(r) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def report(error, **kwargs)
|
30
|
+
report = Report.new(error: error, **kwargs)
|
31
|
+
ActiveSupport::IsolatedExecutionState[:active_support_error_reporter_assertions]&.each do |reports|
|
32
|
+
reports << report
|
33
|
+
end
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def subscribe
|
39
|
+
return if @subscribed
|
40
|
+
@mutex.synchronize do
|
41
|
+
return if @subscribed
|
42
|
+
|
43
|
+
if ActiveSupport.error_reporter
|
44
|
+
ActiveSupport.error_reporter.subscribe(self)
|
45
|
+
@subscribed = true
|
46
|
+
else
|
47
|
+
raise Minitest::Assertion, "No error reporter is configured"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Assertion that the block should not cause an exception to be reported
|
55
|
+
# to +Rails.error+.
|
56
|
+
#
|
57
|
+
# Passes if evaluated code in the yielded block reports no exception.
|
58
|
+
#
|
59
|
+
# assert_no_error_reported do
|
60
|
+
# perform_service(param: 'no_exception')
|
61
|
+
# end
|
62
|
+
def assert_no_error_reported(&block)
|
63
|
+
reports = ErrorCollector.record do
|
64
|
+
_assert_nothing_raised_or_warn("assert_no_error_reported", &block)
|
65
|
+
end
|
66
|
+
assert_predicate(reports, :empty?)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Assertion that the block should cause at least one exception to be reported
|
70
|
+
# to +Rails.error+.
|
71
|
+
#
|
72
|
+
# Passes if the evaluated code in the yielded block reports a matching exception.
|
73
|
+
#
|
74
|
+
# assert_error_reported(IOError) do
|
75
|
+
# Rails.error.report(IOError.new("Oops"))
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# To test further details about the reported exception, you can use the return
|
79
|
+
# value.
|
80
|
+
#
|
81
|
+
# report = assert_error_reported(IOError) do
|
82
|
+
# # ...
|
83
|
+
# end
|
84
|
+
# assert_equal "Oops", report.error.message
|
85
|
+
# assert_equal "admin", report.context[:section]
|
86
|
+
# assert_equal :warning, report.severity
|
87
|
+
# assert_predicate report, :handled?
|
88
|
+
def assert_error_reported(error_class = StandardError, &block)
|
89
|
+
reports = ErrorCollector.record do
|
90
|
+
_assert_nothing_raised_or_warn("assert_error_reported", &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
if reports.empty?
|
94
|
+
assert(false, "Expected a #{error_class.name} to be reported, but there were no errors reported.")
|
95
|
+
elsif (report = reports.find { |r| error_class === r.error })
|
96
|
+
self.assertions += 1
|
97
|
+
report
|
98
|
+
else
|
99
|
+
message = "Expected a #{error_class.name} to be reported, but none of the " \
|
100
|
+
"#{reports.size} reported errors matched: \n" \
|
101
|
+
"#{reports.map { |r| r.error.class.name }.join("\n ")}"
|
102
|
+
assert(false, message)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -5,7 +5,7 @@ module ActiveSupport
|
|
5
5
|
module Isolation
|
6
6
|
require "thread"
|
7
7
|
|
8
|
-
def self.included(klass)
|
8
|
+
def self.included(klass) # :nodoc:
|
9
9
|
klass.class_eval do
|
10
10
|
parallelize_me!
|
11
11
|
end
|
@@ -25,45 +25,46 @@ module ActiveSupport
|
|
25
25
|
|
26
26
|
module Forking
|
27
27
|
def run_in_isolation(&blk)
|
28
|
-
read, write
|
29
|
-
|
30
|
-
|
28
|
+
IO.pipe do |read, write|
|
29
|
+
read.binmode
|
30
|
+
write.binmode
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
32
|
+
pid = fork do
|
33
|
+
read.close
|
34
|
+
yield
|
35
|
+
begin
|
36
|
+
if error?
|
37
|
+
failures.map! { |e|
|
38
|
+
begin
|
39
|
+
Marshal.dump e
|
40
|
+
e
|
41
|
+
rescue TypeError
|
42
|
+
ex = Exception.new e.message
|
43
|
+
ex.set_backtrace e.backtrace
|
44
|
+
Minitest::UnexpectedError.new ex
|
45
|
+
end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
49
|
+
result = Marshal.dump(test_result)
|
47
50
|
end
|
48
|
-
|
49
|
-
result
|
51
|
+
|
52
|
+
write.puts [result].pack("m")
|
53
|
+
exit!
|
50
54
|
end
|
51
55
|
|
52
|
-
write.
|
53
|
-
|
56
|
+
write.close
|
57
|
+
result = read.read
|
58
|
+
Process.wait2(pid)
|
59
|
+
result.unpack1("m")
|
54
60
|
end
|
55
|
-
|
56
|
-
write.close
|
57
|
-
result = read.read
|
58
|
-
Process.wait2(pid)
|
59
|
-
result.unpack1("m")
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
64
|
module Subprocess
|
64
65
|
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
|
65
66
|
|
66
|
-
#
|
67
|
+
# Complicated H4X to get this working in Windows / JRuby with
|
67
68
|
# no forking.
|
68
69
|
def run_in_isolation(&blk)
|
69
70
|
require "tempfile"
|
@@ -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 })
|
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,24 +17,37 @@ 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:
|
20
|
+
def assert_called_with(object, method_name, args, returns: false, **kwargs, &block)
|
21
21
|
mock = Minitest::Mock.new
|
22
|
+
expect_called_with(mock, args, returns: returns, **kwargs)
|
22
23
|
|
23
|
-
|
24
|
-
args.each { |arg| mock.expect(:call, returns, arg) }
|
25
|
-
else
|
26
|
-
mock.expect(:call, returns, args)
|
27
|
-
end
|
28
|
-
|
29
|
-
object.stub(method_name, mock) { yield }
|
24
|
+
object.stub(method_name, mock, &block)
|
30
25
|
|
31
|
-
mock
|
26
|
+
assert_mock(mock)
|
32
27
|
end
|
33
28
|
|
34
29
|
def assert_not_called(object, method_name, message = nil, &block)
|
35
30
|
assert_called(object, method_name, message, times: 0, &block)
|
36
31
|
end
|
37
32
|
|
33
|
+
#--
|
34
|
+
# This method is a temporary wrapper for mock.expect as part of
|
35
|
+
# the Minitest 5.16 / Ruby 3.0 kwargs transition. It can go away
|
36
|
+
# when we drop support for Ruby 2.7.
|
37
|
+
if Minitest::Mock.instance_method(:expect).parameters.map(&:first).include?(:keyrest)
|
38
|
+
def expect_called_with(mock, args, returns: false, **kwargs)
|
39
|
+
mock.expect(:call, returns, args, **kwargs)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
def expect_called_with(mock, args, returns: false, **kwargs)
|
43
|
+
if !kwargs.empty?
|
44
|
+
mock.expect(:call, returns, [*args, kwargs])
|
45
|
+
else
|
46
|
+
mock.expect(:call, returns, args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
38
51
|
def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
|
39
52
|
times_called = 0
|
40
53
|
klass.define_method("stubbed_#{method_name}") do |*|
|
@@ -0,0 +1,81 @@
|
|
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
|
+
@parallelized = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
parallelize if should_parallelize?
|
17
|
+
show_execution_info
|
18
|
+
|
19
|
+
parallel_executor.start if parallelized?
|
20
|
+
end
|
21
|
+
|
22
|
+
def <<(work)
|
23
|
+
parallel_executor << work if parallelized?
|
24
|
+
end
|
25
|
+
|
26
|
+
def shutdown
|
27
|
+
parallel_executor.shutdown if parallelized?
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def parallel_executor
|
32
|
+
@parallel_executor ||= build_parallel_executor
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_parallel_executor
|
36
|
+
case parallelize_with
|
37
|
+
when :processes
|
38
|
+
Testing::Parallelization.new(size)
|
39
|
+
when :threads
|
40
|
+
ActiveSupport::TestCase.lock_threads = false if defined?(ActiveSupport::TestCase.lock_threads)
|
41
|
+
Minitest::Parallel::Executor.new(size)
|
42
|
+
else
|
43
|
+
raise ArgumentError, "#{parallelize_with} is not a supported parallelization executor."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parallelize
|
48
|
+
@parallelized = true
|
49
|
+
Minitest::Test.parallelize_me!
|
50
|
+
end
|
51
|
+
|
52
|
+
def parallelized?
|
53
|
+
@parallelized
|
54
|
+
end
|
55
|
+
|
56
|
+
def should_parallelize?
|
57
|
+
(ENV["PARALLEL_WORKERS"] || tests_count > threshold) && many_workers?
|
58
|
+
end
|
59
|
+
|
60
|
+
def many_workers?
|
61
|
+
size > 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def tests_count
|
65
|
+
@tests_count ||= Minitest::Runnable.runnables.sum { |runnable| runnable.runnable_methods.size }
|
66
|
+
end
|
67
|
+
|
68
|
+
def show_execution_info
|
69
|
+
puts execution_info
|
70
|
+
end
|
71
|
+
|
72
|
+
def execution_info
|
73
|
+
if parallelized?
|
74
|
+
"Running #{tests_count} tests in parallel using #{parallel_executor.size} #{parallelize_with}"
|
75
|
+
elsif many_workers?
|
76
|
+
"Running #{tests_count} tests in a single process (parallelization threshold is #{threshold})"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module Testing
|
5
|
-
module Stream
|
5
|
+
module Stream # :nodoc:
|
6
6
|
private
|
7
7
|
def silence_stream(stream)
|
8
8
|
old_stream = stream.dup
|
@@ -14,18 +14,16 @@ module ActiveSupport
|
|
14
14
|
old_stream.close
|
15
15
|
end
|
16
16
|
|
17
|
-
def quietly
|
17
|
+
def quietly(&block)
|
18
18
|
silence_stream(STDOUT) do
|
19
|
-
silence_stream(STDERR)
|
20
|
-
yield
|
21
|
-
end
|
19
|
+
silence_stream(STDERR, &block)
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
25
23
|
def capture(stream)
|
26
24
|
stream = stream.to_s
|
27
25
|
captured_stream = Tempfile.new(stream)
|
28
|
-
stream_io = eval("$#{stream}")
|
26
|
+
stream_io = eval("$#{stream}", binding, __FILE__, __LINE__)
|
29
27
|
origin_stream = stream_io.dup
|
30
28
|
stream_io.reopen(captured_stream)
|
31
29
|
|