activesupport 7.0.3.1 → 7.2.3.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 +261 -246
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +39 -7
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +238 -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 +51 -19
- data/lib/active_support/cache/mem_cache_store.rb +114 -134
- data/lib/active_support/cache/memory_store.rb +85 -30
- data/lib/active_support/cache/null_store.rb +8 -2
- data/lib/active_support/cache/redis_cache_store.rb +175 -154
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +64 -13
- data/lib/active_support/cache.rb +455 -378
- data/lib/active_support/callbacks.rb +121 -136
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +10 -0
- data/lib/active_support/core_ext/array/conversions.rb +1 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/benchmark.rb +1 -0
- data/lib/active_support/core_ext/class/attribute.rb +2 -2
- data/lib/active_support/core_ext/class/subclasses.rb +17 -34
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/calculations.rb +15 -0
- data/lib/active_support/core_ext/date/conversions.rb +1 -2
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +6 -4
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +25 -80
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- 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/keys.rb +7 -7
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +3 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/duplicable.rb +25 -16
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/to_query.rb +0 -2
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +9 -9
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +28 -7
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +2 -2
- data/lib/active_support/core_ext/securerandom.rb +24 -12
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +24 -18
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -9
- data/lib/active_support/core_ext/string/multibyte.rb +3 -3
- data/lib/active_support/core_ext/string/output_safety.rb +41 -178
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +40 -30
- data/lib/active_support/core_ext/time/compatibility.rb +24 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -3
- data/lib/active_support/core_ext/time/zones.rb +7 -8
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +60 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +64 -41
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -172
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +63 -11
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +163 -36
- data/lib/active_support/evented_file_update_checker.rb +17 -3
- data/lib/active_support/execution_wrapper.rb +5 -6
- data/lib/active_support/file_update_checker.rb +6 -4
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +50 -30
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +28 -18
- data/lib/active_support/inflector/transliterate.rb +3 -1
- data/lib/active_support/isolated_execution_state.rb +26 -22
- data/lib/active_support/json/decoding.rb +3 -2
- data/lib/active_support/json/encoding.rb +48 -48
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +22 -7
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +74 -34
- data/lib/active_support/logger.rb +22 -59
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +197 -53
- 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 +305 -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 +229 -89
- data/lib/active_support/message_verifiers.rb +137 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +38 -31
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +8 -3
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +36 -29
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +17 -2
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -317
- data/lib/active_support/option_merger.rb +4 -4
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +67 -15
- data/lib/active_support/parameter_filter.rb +103 -84
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +25 -20
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +10 -8
- data/lib/active_support/secure_compare_rotator.rb +16 -9
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -40
- data/lib/active_support/test_case.rb +160 -7
- data/lib/active_support/testing/assertions.rb +28 -12
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +46 -33
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +18 -2
- data/lib/active_support/testing/parallelization/worker.rb +2 -2
- data/lib/active_support/testing/parallelization.rb +12 -1
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +18 -44
- data/lib/active_support/values/time_zone.rb +25 -14
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +14 -3
- data/lib/active_support.rb +15 -3
- metadata +148 -24
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
# If the constant doesn't already exists, but you need it set for the duration of the block
|
|
19
|
+
# you can do so by passing `exists: false`.
|
|
20
|
+
#
|
|
21
|
+
# stub_const(object, :SOME_CONST, 1, exists: false) do
|
|
22
|
+
# assert_equal 1, SOME_CONST
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# Note: Stubbing a const will stub it across all threads. So if you have concurrent threads
|
|
26
|
+
# (like separate test suites running in parallel) that all depend on the same constant, it's possible
|
|
27
|
+
# divergent stubbing will trample on each other.
|
|
28
|
+
def stub_const(mod, constant, new_value, exists: true)
|
|
29
|
+
if exists
|
|
30
|
+
begin
|
|
31
|
+
old_value = mod.const_get(constant, false)
|
|
32
|
+
mod.send(:remove_const, constant)
|
|
33
|
+
mod.const_set(constant, new_value)
|
|
34
|
+
yield
|
|
35
|
+
ensure
|
|
36
|
+
mod.send(:remove_const, constant)
|
|
37
|
+
mod.const_set(constant, old_value)
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
if mod.const_defined?(constant)
|
|
41
|
+
raise NameError, "already defined constant #{constant} in #{mod.name}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
mod.const_set(constant, new_value)
|
|
46
|
+
yield
|
|
47
|
+
ensure
|
|
48
|
+
mod.send(:remove_const, constant)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -5,6 +5,11 @@ require "active_support/deprecation"
|
|
|
5
5
|
module ActiveSupport
|
|
6
6
|
module Testing
|
|
7
7
|
module Deprecation
|
|
8
|
+
##
|
|
9
|
+
# :call-seq:
|
|
10
|
+
# assert_deprecated(deprecator, &block)
|
|
11
|
+
# assert_deprecated(match, deprecator, &block)
|
|
12
|
+
#
|
|
8
13
|
# Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block.
|
|
9
14
|
#
|
|
10
15
|
# assert_deprecated(/foo/, CustomDeprecator) do
|
|
@@ -19,16 +24,16 @@ module ActiveSupport
|
|
|
19
24
|
#
|
|
20
25
|
# If the +match+ is omitted (or explicitly +nil+), any deprecation warning will match.
|
|
21
26
|
#
|
|
22
|
-
# assert_deprecated(
|
|
27
|
+
# assert_deprecated(CustomDeprecator) do
|
|
23
28
|
# CustomDeprecator.warn "foo should no longer be used"
|
|
24
29
|
# end
|
|
25
|
-
#
|
|
26
|
-
# If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
|
|
27
|
-
#
|
|
28
|
-
# assert_deprecated do
|
|
29
|
-
# ActiveSupport::Deprecation.warn "foo should no longer be used"
|
|
30
|
-
# end
|
|
31
30
|
def assert_deprecated(match = nil, deprecator = nil, &block)
|
|
31
|
+
match, deprecator = nil, match if match.is_a?(ActiveSupport::Deprecation)
|
|
32
|
+
|
|
33
|
+
unless deprecator
|
|
34
|
+
raise ArgumentError, "No deprecator given"
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
result, warnings = collect_deprecations(deprecator, &block)
|
|
33
38
|
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
|
|
34
39
|
if match
|
|
@@ -44,36 +49,24 @@ module ActiveSupport
|
|
|
44
49
|
# CustomDeprecator.warn "message" # fails assertion
|
|
45
50
|
# end
|
|
46
51
|
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# assert_not_deprecated do
|
|
50
|
-
# ActiveSupport::Deprecation.warn "message" # fails assertion
|
|
51
|
-
# end
|
|
52
|
-
#
|
|
53
|
-
# assert_not_deprecated do
|
|
54
|
-
# CustomDeprecator.warn "message" # passes assertion
|
|
52
|
+
# assert_not_deprecated(ActiveSupport::Deprecation.new) do
|
|
53
|
+
# CustomDeprecator.warn "message" # passes assertion, different deprecator
|
|
55
54
|
# end
|
|
56
|
-
def assert_not_deprecated(deprecator
|
|
55
|
+
def assert_not_deprecated(deprecator, &block)
|
|
57
56
|
result, deprecations = collect_deprecations(deprecator, &block)
|
|
58
57
|
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
|
|
59
58
|
result
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
# Returns an array of all the deprecation warnings emitted by the given
|
|
61
|
+
# Returns the return value of the block and an array of all the deprecation warnings emitted by the given
|
|
63
62
|
# +deprecator+ during the execution of the yielded block.
|
|
64
63
|
#
|
|
65
64
|
# collect_deprecations(CustomDeprecator) do
|
|
66
65
|
# CustomDeprecator.warn "message"
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
# collect_deprecations do
|
|
72
|
-
# CustomDeprecator.warn "custom message"
|
|
73
|
-
# ActiveSupport::Deprecation.warn "message"
|
|
74
|
-
# end # => ["message"]
|
|
75
|
-
def collect_deprecations(deprecator = nil)
|
|
76
|
-
deprecator ||= ActiveSupport::Deprecation
|
|
66
|
+
# ActiveSupport::Deprecation.new.warn "other message"
|
|
67
|
+
# :result
|
|
68
|
+
# end # => [:result, ["message"]]
|
|
69
|
+
def collect_deprecations(deprecator)
|
|
77
70
|
old_behavior = deprecator.behavior
|
|
78
71
|
deprecations = []
|
|
79
72
|
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
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/testing/parallelize_executor"
|
|
4
|
+
|
|
3
5
|
module ActiveSupport
|
|
4
6
|
module Testing
|
|
5
7
|
module Isolation
|
|
6
8
|
require "thread"
|
|
7
9
|
|
|
10
|
+
SubprocessCrashed = Class.new(StandardError)
|
|
11
|
+
|
|
8
12
|
def self.included(klass) # :nodoc:
|
|
9
13
|
klass.class_eval do
|
|
10
|
-
parallelize_me!
|
|
14
|
+
parallelize_me! unless Minitest.parallel_executor.is_a?(ActiveSupport::Testing::ParallelizeExecutor)
|
|
11
15
|
end
|
|
12
16
|
end
|
|
13
17
|
|
|
@@ -16,54 +20,62 @@ module ActiveSupport
|
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
def run
|
|
19
|
-
serialized = run_in_isolation do
|
|
23
|
+
status, serialized = run_in_isolation do
|
|
20
24
|
super
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
unless status&.success?
|
|
28
|
+
error = SubprocessCrashed.new("Subprocess exited with an error: #{status.inspect}\noutput: #{serialized.inspect}")
|
|
29
|
+
error.set_backtrace(caller)
|
|
30
|
+
self.failures << Minitest::UnexpectedError.new(error)
|
|
31
|
+
return defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
|
32
|
+
end
|
|
33
|
+
|
|
23
34
|
Marshal.load(serialized)
|
|
24
35
|
end
|
|
25
36
|
|
|
26
37
|
module Forking
|
|
27
38
|
def run_in_isolation(&blk)
|
|
28
|
-
read, write
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
IO.pipe do |read, write|
|
|
40
|
+
read.binmode
|
|
41
|
+
write.binmode
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
pid = fork do
|
|
44
|
+
read.close
|
|
45
|
+
yield
|
|
46
|
+
begin
|
|
47
|
+
if error?
|
|
48
|
+
failures.map! { |e|
|
|
49
|
+
begin
|
|
50
|
+
Marshal.dump e
|
|
51
|
+
e
|
|
52
|
+
rescue TypeError
|
|
53
|
+
ex = Exception.new e.message
|
|
54
|
+
ex.set_backtrace e.backtrace
|
|
55
|
+
Minitest::UnexpectedError.new ex
|
|
56
|
+
end
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
|
60
|
+
result = Marshal.dump(test_result)
|
|
47
61
|
end
|
|
48
|
-
|
|
49
|
-
result
|
|
62
|
+
|
|
63
|
+
write.puts [result].pack("m")
|
|
64
|
+
exit!(0)
|
|
50
65
|
end
|
|
51
66
|
|
|
52
|
-
write.
|
|
53
|
-
|
|
67
|
+
write.close
|
|
68
|
+
result = read.read
|
|
69
|
+
_, status = Process.wait2(pid)
|
|
70
|
+
return status, result.unpack1("m")
|
|
54
71
|
end
|
|
55
|
-
|
|
56
|
-
write.close
|
|
57
|
-
result = read.read
|
|
58
|
-
Process.wait2(pid)
|
|
59
|
-
result.unpack1("m")
|
|
60
72
|
end
|
|
61
73
|
end
|
|
62
74
|
|
|
63
75
|
module Subprocess
|
|
64
76
|
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
|
|
65
77
|
|
|
66
|
-
# Complicated H4X to get this working in
|
|
78
|
+
# Complicated H4X to get this working in Windows / JRuby with
|
|
67
79
|
# no forking.
|
|
68
80
|
def run_in_isolation(&blk)
|
|
69
81
|
require "tempfile"
|
|
@@ -74,7 +86,7 @@ module ActiveSupport
|
|
|
74
86
|
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
|
|
75
87
|
file.puts [Marshal.dump(test_result)].pack("m")
|
|
76
88
|
end
|
|
77
|
-
exit!
|
|
89
|
+
exit!(0)
|
|
78
90
|
else
|
|
79
91
|
Tempfile.open("isolation") do |tmpfile|
|
|
80
92
|
env = {
|
|
@@ -92,13 +104,14 @@ module ActiveSupport
|
|
|
92
104
|
|
|
93
105
|
child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
|
|
94
106
|
|
|
107
|
+
status = nil
|
|
95
108
|
begin
|
|
96
|
-
Process.
|
|
109
|
+
_, status = Process.wait2(child.pid)
|
|
97
110
|
rescue Errno::ECHILD # The child process may exit before we wait
|
|
98
111
|
nil
|
|
99
112
|
end
|
|
100
113
|
|
|
101
|
-
return tmpfile.read.unpack1("m")
|
|
114
|
+
return status, tmpfile.read.unpack1("m")
|
|
102
115
|
end
|
|
103
116
|
end
|
|
104
117
|
end
|
|
@@ -17,24 +17,23 @@ 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
|
-
|
|
23
|
-
if args.all?(Array)
|
|
24
|
-
args.each { |arg| mock.expect(:call, returns, arg) }
|
|
25
|
-
else
|
|
26
|
-
mock.expect(:call, returns, args)
|
|
27
|
-
end
|
|
22
|
+
expect_called_with(mock, args, returns: returns, **kwargs)
|
|
28
23
|
|
|
29
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
|
+
def expect_called_with(mock, args, returns: false, **kwargs)
|
|
34
|
+
mock.expect(:call, returns, args, **kwargs)
|
|
35
|
+
end
|
|
36
|
+
|
|
38
37
|
def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
|
|
39
38
|
times_called = 0
|
|
40
39
|
klass.define_method("stubbed_#{method_name}") do |*|
|
|
@@ -6,12 +6,15 @@ require "drb/unix" unless Gem.win_platform?
|
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
module Testing
|
|
8
8
|
class Parallelization # :nodoc:
|
|
9
|
+
PrerecordResultClass = Struct.new(:name)
|
|
10
|
+
|
|
9
11
|
class Server
|
|
10
12
|
include DRb::DRbUndumped
|
|
11
13
|
|
|
12
14
|
def initialize
|
|
13
15
|
@queue = Queue.new
|
|
14
16
|
@active_workers = Concurrent::Map.new
|
|
17
|
+
@worker_pids = Concurrent::Map.new
|
|
15
18
|
@in_flight = Concurrent::Map.new
|
|
16
19
|
end
|
|
17
20
|
|
|
@@ -21,6 +24,7 @@ module ActiveSupport
|
|
|
21
24
|
@in_flight.delete([result.klass, result.name])
|
|
22
25
|
|
|
23
26
|
reporter.synchronize do
|
|
27
|
+
reporter.prerecord(PrerecordResultClass.new(result.klass), result.name)
|
|
24
28
|
reporter.record(result)
|
|
25
29
|
end
|
|
26
30
|
end
|
|
@@ -37,12 +41,24 @@ module ActiveSupport
|
|
|
37
41
|
end
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
def start_worker(worker_id)
|
|
44
|
+
def start_worker(worker_id, worker_pid)
|
|
41
45
|
@active_workers[worker_id] = true
|
|
46
|
+
@worker_pids[worker_id] = worker_pid
|
|
42
47
|
end
|
|
43
48
|
|
|
44
|
-
def stop_worker(worker_id)
|
|
49
|
+
def stop_worker(worker_id, worker_pid)
|
|
45
50
|
@active_workers.delete(worker_id)
|
|
51
|
+
@worker_pids.delete(worker_id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def remove_dead_workers(dead_pids)
|
|
55
|
+
dead_pids.each do |dead_pid|
|
|
56
|
+
worker_id = @worker_pids.key(dead_pid)
|
|
57
|
+
if worker_id
|
|
58
|
+
@active_workers.delete(worker_id)
|
|
59
|
+
@worker_pids.delete(worker_id)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
46
62
|
end
|
|
47
63
|
|
|
48
64
|
def active_workers?
|
|
@@ -18,7 +18,7 @@ module ActiveSupport
|
|
|
18
18
|
DRb.stop_service
|
|
19
19
|
|
|
20
20
|
@queue = DRbObject.new_with_uri(@url)
|
|
21
|
-
@queue.start_worker(@id)
|
|
21
|
+
@queue.start_worker(@id, Process.pid)
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
after_fork
|
|
@@ -29,7 +29,7 @@ module ActiveSupport
|
|
|
29
29
|
set_process_title("(stopping)")
|
|
30
30
|
|
|
31
31
|
run_cleanup
|
|
32
|
-
@queue.stop_worker(@id)
|
|
32
|
+
@queue.stop_worker(@id, Process.pid)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -47,8 +47,19 @@ module ActiveSupport
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def shutdown
|
|
50
|
+
dead_worker_pids = @worker_pool.filter_map do |pid|
|
|
51
|
+
Process.waitpid(pid, Process::WNOHANG)
|
|
52
|
+
rescue Errno::ECHILD
|
|
53
|
+
pid
|
|
54
|
+
end
|
|
55
|
+
@queue_server.remove_dead_workers(dead_worker_pids)
|
|
56
|
+
|
|
50
57
|
@queue_server.shutdown
|
|
51
|
-
@worker_pool.each
|
|
58
|
+
@worker_pool.each do |pid|
|
|
59
|
+
Process.waitpid(pid)
|
|
60
|
+
rescue Errno::ECHILD
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
52
63
|
end
|
|
53
64
|
end
|
|
54
65
|
end
|
|
@@ -9,6 +9,7 @@ module ActiveSupport
|
|
|
9
9
|
@size = size
|
|
10
10
|
@parallelize_with = with
|
|
11
11
|
@threshold = threshold
|
|
12
|
+
@parallelized = false
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def start
|
|
@@ -49,11 +50,15 @@ module ActiveSupport
|
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def parallelized?
|
|
52
|
-
@parallelized
|
|
53
|
+
@parallelized
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def should_parallelize?
|
|
56
|
-
ENV["PARALLEL_WORKERS"] || tests_count > threshold
|
|
57
|
+
(ENV["PARALLEL_WORKERS"] || tests_count > threshold) && many_workers?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def many_workers?
|
|
61
|
+
size > 1
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
def tests_count
|
|
@@ -67,7 +72,7 @@ module ActiveSupport
|
|
|
67
72
|
def execution_info
|
|
68
73
|
if parallelized?
|
|
69
74
|
"Running #{tests_count} tests in parallel using #{parallel_executor.size} #{parallelize_with}"
|
|
70
|
-
|
|
75
|
+
elsif many_workers?
|
|
71
76
|
"Running #{tests_count} tests in a single process (parallelization threshold is #{threshold})"
|
|
72
77
|
end
|
|
73
78
|
end
|
|
@@ -23,7 +23,7 @@ module ActiveSupport
|
|
|
23
23
|
def capture(stream)
|
|
24
24
|
stream = stream.to_s
|
|
25
25
|
captured_stream = Tempfile.new(stream)
|
|
26
|
-
stream_io = eval("$#{stream}")
|
|
26
|
+
stream_io = eval("$#{stream}", binding, __FILE__, __LINE__)
|
|
27
27
|
origin_stream = stream_io.dup
|
|
28
28
|
stream_io.reopen(captured_stream)
|
|
29
29
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
# Warns when a test case does not perform any assertions.
|
|
6
|
+
#
|
|
7
|
+
# This is helpful in detecting broken tests that do not perform intended assertions.
|
|
8
|
+
module TestsWithoutAssertions # :nodoc:
|
|
9
|
+
def after_teardown
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
if assertions.zero? && !skipped? && !error?
|
|
13
|
+
file, line = method(name).source_location
|
|
14
|
+
warn "Test is missing assertions: `#{name}` #{file}:#{line}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|