activesupport 7.0.4.3 → 7.2.4
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 +270 -266
- 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 +101 -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 +168 -153
- 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 +364 -292
- 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 +18 -8
- 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 +37 -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 +65 -42
- 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 +17 -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 +3 -3
- 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 +30 -20
- 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 +4 -3
- data/lib/active_support/json/encoding.rb +49 -49
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +8 -6
- 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 -60
- 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 +30 -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 +18 -5
- 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 +2 -2
- 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 +2 -0
- 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 -25
- data/lib/active_support/test_case.rb +156 -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 +16 -41
- 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 +154 -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
|
@@ -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
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_support/core_ext/module/redefine_method"
|
|
4
4
|
require "active_support/core_ext/time/calculations"
|
|
5
|
-
require "concurrent/map"
|
|
6
5
|
|
|
7
6
|
module ActiveSupport
|
|
8
7
|
module Testing
|
|
@@ -11,7 +10,7 @@ module ActiveSupport
|
|
|
11
10
|
Stub = Struct.new(:object, :method_name, :original_method)
|
|
12
11
|
|
|
13
12
|
def initialize
|
|
14
|
-
@stubs =
|
|
13
|
+
@stubs = Hash.new { |h, k| h[k] = {} }
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
# Stubs object.method_name with the given block
|
|
@@ -26,7 +25,7 @@ module ActiveSupport
|
|
|
26
25
|
unstub_object(stub)
|
|
27
26
|
end
|
|
28
27
|
|
|
29
|
-
new_name = "__simple_stub__#{method_name}"
|
|
28
|
+
new_name = "__simple_stub__#{method_name}__#{object_id}"
|
|
30
29
|
|
|
31
30
|
@stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
|
|
32
31
|
|
|
@@ -76,6 +75,11 @@ module ActiveSupport
|
|
|
76
75
|
# stubbing +Time.now+, +Date.today+, and +DateTime.now+. The stubs are automatically removed
|
|
77
76
|
# at the end of the test.
|
|
78
77
|
#
|
|
78
|
+
# Note that the usec for the resulting time will be set to 0 to prevent rounding
|
|
79
|
+
# errors with external services, like MySQL (which will round instead of floor,
|
|
80
|
+
# leading to off-by-one-second errors), unless the <tt>with_usec</tt> argument
|
|
81
|
+
# is set to <tt>true</tt>.
|
|
82
|
+
#
|
|
79
83
|
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
|
80
84
|
# travel 1.day
|
|
81
85
|
# Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
|
|
@@ -90,11 +94,11 @@ module ActiveSupport
|
|
|
90
94
|
# User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
|
|
91
95
|
# end
|
|
92
96
|
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
|
93
|
-
def travel(duration, &block)
|
|
94
|
-
travel_to Time.now + duration, &block
|
|
97
|
+
def travel(duration, with_usec: false, &block)
|
|
98
|
+
travel_to Time.now + duration, with_usec: with_usec, &block
|
|
95
99
|
end
|
|
96
100
|
|
|
97
|
-
# Changes current time to the given time by stubbing +Time.now+,
|
|
101
|
+
# Changes current time to the given time by stubbing +Time.now+, +Time.new+,
|
|
98
102
|
# +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
|
|
99
103
|
# The stubs are automatically removed at the end of the test.
|
|
100
104
|
#
|
|
@@ -115,7 +119,8 @@ module ActiveSupport
|
|
|
115
119
|
#
|
|
116
120
|
# Note that the usec for the time passed will be set to 0 to prevent rounding
|
|
117
121
|
# errors with external services, like MySQL (which will round instead of floor,
|
|
118
|
-
# leading to off-by-one-second errors)
|
|
122
|
+
# leading to off-by-one-second errors), unless the <tt>with_usec</tt> argument
|
|
123
|
+
# is set to <tt>true</tt>.
|
|
119
124
|
#
|
|
120
125
|
# This method also accepts a block, which will return the current time back to its original
|
|
121
126
|
# state at the end of the block:
|
|
@@ -125,7 +130,7 @@ module ActiveSupport
|
|
|
125
130
|
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
|
|
126
131
|
# end
|
|
127
132
|
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
|
128
|
-
def travel_to(date_or_time)
|
|
133
|
+
def travel_to(date_or_time, with_usec: false)
|
|
129
134
|
if block_given? && in_block
|
|
130
135
|
travel_to_nested_block_call = <<~MSG
|
|
131
136
|
|
|
@@ -159,13 +164,30 @@ module ActiveSupport
|
|
|
159
164
|
elsif date_or_time.is_a?(String)
|
|
160
165
|
now = Time.zone.parse(date_or_time)
|
|
161
166
|
else
|
|
162
|
-
now = date_or_time
|
|
167
|
+
now = date_or_time
|
|
168
|
+
now = now.to_time unless now.is_a?(Time)
|
|
169
|
+
now = now.change(usec: 0) unless with_usec
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# +now+ must be in local system timezone, because +Time.at(now)+
|
|
173
|
+
# and +now.to_date+ (see stubs below) will use +now+'s timezone too!
|
|
174
|
+
now = now.getlocal
|
|
175
|
+
|
|
176
|
+
stubs = simple_stubs
|
|
177
|
+
stubbed_time = Time.now if stubs.stubbing(Time, :now)
|
|
178
|
+
stubs.stub_object(Time, :now) { at(now) }
|
|
179
|
+
|
|
180
|
+
stubs.stub_object(Time, :new) do |*args, **options|
|
|
181
|
+
if args.empty? && options.empty?
|
|
182
|
+
at(now)
|
|
183
|
+
else
|
|
184
|
+
stub = stubs.stubbing(Time, :new)
|
|
185
|
+
Time.send(stub.original_method, *args, **options)
|
|
186
|
+
end
|
|
163
187
|
end
|
|
164
188
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
|
|
168
|
-
simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
|
|
189
|
+
stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
|
|
190
|
+
stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
|
|
169
191
|
|
|
170
192
|
if block_given?
|
|
171
193
|
begin
|
|
@@ -215,7 +237,7 @@ module ActiveSupport
|
|
|
215
237
|
end
|
|
216
238
|
alias_method :unfreeze_time, :travel_back
|
|
217
239
|
|
|
218
|
-
# Calls +travel_to+ with +Time.now+.
|
|
240
|
+
# Calls +travel_to+ with +Time.now+. Forwards optional <tt>with_usec</tt> argument.
|
|
219
241
|
#
|
|
220
242
|
# Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
|
221
243
|
# freeze_time
|
|
@@ -231,8 +253,8 @@ module ActiveSupport
|
|
|
231
253
|
# User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
|
232
254
|
# end
|
|
233
255
|
# Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
|
|
234
|
-
def freeze_time(&block)
|
|
235
|
-
travel_to Time.now, &block
|
|
256
|
+
def freeze_time(with_usec: false, &block)
|
|
257
|
+
travel_to Time.now, with_usec: with_usec, &block
|
|
236
258
|
end
|
|
237
259
|
|
|
238
260
|
private
|