test-unit 3.6.2 → 3.6.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/Rakefile +5 -5
- data/doc/text/news.md +36 -0
- data/lib/test/unit/assertions.rb +3 -3
- data/lib/test/unit/autorunner.rb +23 -0
- data/lib/test/unit/failure.rb +1 -1
- data/lib/test/unit/fault-location-detector.rb +5 -1
- data/lib/test/unit/priority.rb +1 -1
- data/lib/test/unit/runner/console.rb +9 -1
- data/lib/test/unit/sub-test-result.rb +51 -0
- data/lib/test/unit/test-suite-runner.rb +120 -0
- data/lib/test/unit/test-suite-thread-runner.rb +69 -0
- data/lib/test/unit/testcase.rb +26 -1
- data/lib/test/unit/testresult.rb +2 -2
- data/lib/test/unit/testsuite.rb +18 -85
- data/lib/test/unit/ui/console/testrunner.rb +92 -5
- data/lib/test/unit/ui/testrunner.rb +1 -1
- data/lib/test/unit/ui/testrunnermediator.rb +13 -8
- data/lib/test/unit/util/observable.rb +3 -7
- data/lib/test/unit/version.rb +1 -1
- metadata +7 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db526a6169793f43216a4519521037141c9243f0d7b39d520d2ddc09d59576da
|
4
|
+
data.tar.gz: 5351d58e84881dc2faaaf6ad395a2c00b8964ce378a7dfff410d76b83e925b84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f028832fae917599c17ab226de5376c103ad7d6059850fe8af81d1a0b3df77ce9a66d84367e2c61106cb92150883b4031c47119a34a97cd8e3def17f6b84d6b
|
7
|
+
data.tar.gz: c9a01ad68395703aa58d0b01ac53d0d01c3a25def3a6edc11af6267f37839039a5670f79af74b05182e2eef96bab59cdc9d6612d762e99aff6eacbafc9b64e5c
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2008-
|
3
|
+
# Copyright (C) 2008-2024 Sutou Kouhei <kou@clear-code.com>
|
4
4
|
|
5
5
|
Encoding.default_internal = "UTF-8" if defined?(Encoding.default_internal)
|
6
6
|
|
@@ -48,10 +48,6 @@ Packnga::ReleaseTask.new(spec) do |task|
|
|
48
48
|
task.index_html_dir = test_unit_github_io_dir
|
49
49
|
end
|
50
50
|
|
51
|
-
def rake(*arguments)
|
52
|
-
ruby($0, *arguments)
|
53
|
-
end
|
54
|
-
|
55
51
|
task :test do
|
56
52
|
ruby("test/run-test.rb")
|
57
53
|
end
|
@@ -66,3 +62,7 @@ namespace :doc do
|
|
66
62
|
File.write("doc/text/news.md", applied_permalink)
|
67
63
|
end
|
68
64
|
end
|
65
|
+
|
66
|
+
release_task = Rake.application["release"]
|
67
|
+
# We use Trusted Publishing.
|
68
|
+
release_task.prerequisites.delete("release:rubygem_push")
|
data/doc/text/news.md
CHANGED
@@ -1,5 +1,41 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 3.6.4 - 2024-11-28 {#version-3-6-4}
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Added support for Ruby 3.4.0.
|
8
|
+
|
9
|
+
## 3.6.3 - 2024-11-24 {#version-3-6-3}
|
10
|
+
|
11
|
+
### Improvements
|
12
|
+
|
13
|
+
* Added support for thread based parallel test running. You can use
|
14
|
+
it by the `--parallel=thread` option. You can disable parallel
|
15
|
+
test running per test case by defining `parallel_safe?` class
|
16
|
+
method that returns `false`.
|
17
|
+
* GH-235
|
18
|
+
* Patch by Tsutomu Katsube
|
19
|
+
|
20
|
+
* Added the `--n-workers` option.
|
21
|
+
|
22
|
+
* Added the `--[no-]report-slow-tests` option. You can show the top
|
23
|
+
5 slow tests with this option.
|
24
|
+
* GH-253
|
25
|
+
* Patch by Tsutomu Katsube
|
26
|
+
|
27
|
+
* UI: console: Add support for outputting `Exception#cause`.
|
28
|
+
|
29
|
+
* Added support for inspecting `BasicObject`.
|
30
|
+
* GH-262.
|
31
|
+
* Patch by Yuta Saito
|
32
|
+
|
33
|
+
### Thanks
|
34
|
+
|
35
|
+
* Tsutomu Katsube
|
36
|
+
|
37
|
+
* Yuta Saito
|
38
|
+
|
3
39
|
## 3.6.2 - 2024-02-16 {#version-3-6-2}
|
4
40
|
|
5
41
|
### Improvements
|
data/lib/test/unit/assertions.rb
CHANGED
@@ -2093,7 +2093,7 @@ EOT
|
|
2093
2093
|
|
2094
2094
|
class << self
|
2095
2095
|
def cached_new(object, inspected_objects)
|
2096
|
-
inspected_objects[object.
|
2096
|
+
inspected_objects[object.__id__] ||=
|
2097
2097
|
new(object, inspected_objects)
|
2098
2098
|
end
|
2099
2099
|
|
@@ -2115,7 +2115,7 @@ EOT
|
|
2115
2115
|
def initialize(object, inspected_objects={})
|
2116
2116
|
@inspected_objects = inspected_objects
|
2117
2117
|
@object = object
|
2118
|
-
@inspected_objects[@object.
|
2118
|
+
@inspected_objects[@object.__id__] = self
|
2119
2119
|
@inspect_target = inspect_target
|
2120
2120
|
end
|
2121
2121
|
|
@@ -2317,7 +2317,7 @@ EOT
|
|
2317
2317
|
|
2318
2318
|
def initialize(parts)
|
2319
2319
|
@parts = parts
|
2320
|
-
@count = parts.
|
2320
|
+
@count = parts.count('?')
|
2321
2321
|
end
|
2322
2322
|
|
2323
2323
|
def result(parameters)
|
data/lib/test/unit/autorunner.rb
CHANGED
@@ -5,6 +5,7 @@ require "test/unit/color-scheme"
|
|
5
5
|
require "test/unit/priority"
|
6
6
|
require "test/unit/attribute-matcher"
|
7
7
|
require "test/unit/testcase"
|
8
|
+
require "test/unit/test-suite-thread-runner"
|
8
9
|
|
9
10
|
module Test
|
10
11
|
module Unit
|
@@ -163,6 +164,7 @@ module Test
|
|
163
164
|
@stop_on_failure = false
|
164
165
|
@debug_on_failure = false
|
165
166
|
@gc_stress = false
|
167
|
+
@test_suite_runner_class = TestSuiteRunner
|
166
168
|
config_file = "test-unit.yml"
|
167
169
|
if File.exist?(config_file)
|
168
170
|
load_config(config_file)
|
@@ -401,6 +403,26 @@ module Test
|
|
401
403
|
@gc_stress = boolean
|
402
404
|
end
|
403
405
|
|
406
|
+
parallel_options = [
|
407
|
+
:thread,
|
408
|
+
]
|
409
|
+
o.on("--[no-]parallel=[thread]", parallel_options,
|
410
|
+
"Runs tests in parallel",
|
411
|
+
"(#{parallel_options.first})") do |parallel|
|
412
|
+
case parallel
|
413
|
+
when nil, :thread
|
414
|
+
@test_suite_runner_class = TestSuiteThreadRunner
|
415
|
+
else
|
416
|
+
@test_suite_runner_class = TestSuiteRunner
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
o.on("--n-workers=N", Integer,
|
421
|
+
"The number of parallelism",
|
422
|
+
"(#{TestSuiteRunner.n_workers})") do |n|
|
423
|
+
TestSuiteRunner.n_workers = n
|
424
|
+
end
|
425
|
+
|
404
426
|
ADDITIONAL_OPTIONS.each do |option_builder|
|
405
427
|
option_builder.call(self, o)
|
406
428
|
end
|
@@ -465,6 +487,7 @@ module Test
|
|
465
487
|
if @gc_stress
|
466
488
|
@runner_options[:listeners] << GCStressListener.new
|
467
489
|
end
|
490
|
+
@runner_options[:test_suite_runner_class] = @test_suite_runner_class
|
468
491
|
change_work_directory do
|
469
492
|
runner.run(suite, @runner_options).passed?
|
470
493
|
end
|
data/lib/test/unit/failure.rb
CHANGED
@@ -71,7 +71,11 @@ module Test
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def target_method?(method_name)
|
74
|
-
|
74
|
+
return false if method_name.nil?
|
75
|
+
# method_name may be test_XXX or MyTest#test_XXX (Ruby >= 3.4).
|
76
|
+
return true if @fault_method_name == method_name
|
77
|
+
return true if method_name.end_with?("\##{@fault_method_name}")
|
78
|
+
false
|
75
79
|
end
|
76
80
|
|
77
81
|
def guess_indent_level(line)
|
data/lib/test/unit/priority.rb
CHANGED
@@ -9,6 +9,7 @@ module Test
|
|
9
9
|
|
10
10
|
AutoRunner.setup_option do |auto_runner, opts|
|
11
11
|
require 'test/unit/ui/console/outputlevel'
|
12
|
+
require 'test/unit/ui/console/testrunner'
|
12
13
|
|
13
14
|
output_levels = [
|
14
15
|
["silent", UI::Console::OutputLevel::SILENT],
|
@@ -58,7 +59,7 @@ module Test
|
|
58
59
|
]
|
59
60
|
opts.on("--progress-style=STYLE", progress_styles,
|
60
61
|
"Uses STYLE as progress style",
|
61
|
-
"(#{auto_runner.keyword_display(progress_styles)}") do |style|
|
62
|
+
"(#{auto_runner.keyword_display(progress_styles)})") do |style|
|
62
63
|
auto_runner.runner_options[:progress_style] = style
|
63
64
|
end
|
64
65
|
|
@@ -73,6 +74,13 @@ module Test
|
|
73
74
|
"(default is yes for tty output, no otherwise)") do |boolean|
|
74
75
|
auto_runner.runner_options[:reverse_output] = boolean
|
75
76
|
end
|
77
|
+
|
78
|
+
n_report_slow_tests = UI::Console::TestRunner::N_REPORT_SLOW_TESTS
|
79
|
+
opts.on("--[no-]report-slow-tests",
|
80
|
+
"Shows the top #{n_report_slow_tests} slow tests in the summary output",
|
81
|
+
"(false)") do |boolean|
|
82
|
+
auto_runner.runner_options[:report_slow_tests] = boolean
|
83
|
+
end
|
76
84
|
end
|
77
85
|
end
|
78
86
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Tsutomu Katsube.
|
4
|
+
# Copyright:: Copyright (c) 2024 Tsutomu Katsube. All rights reserved.
|
5
|
+
# License:: Ruby license.
|
6
|
+
|
7
|
+
module Test
|
8
|
+
module Unit
|
9
|
+
class SubTestResult
|
10
|
+
attr_accessor :stop_tag
|
11
|
+
|
12
|
+
def initialize(parent_test_result)
|
13
|
+
@parent_test_result = parent_test_result
|
14
|
+
@stop_tag = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_run(result=self)
|
18
|
+
@parent_test_result.add_run(result)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_pass
|
22
|
+
@parent_test_result.add_pass
|
23
|
+
end
|
24
|
+
|
25
|
+
# Records an individual assertion.
|
26
|
+
def add_assertion
|
27
|
+
@parent_test_result.add_assertion
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_error(error)
|
31
|
+
@parent_test_result.add_error(error)
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_failure(failure)
|
35
|
+
@parent_test_result.add_failure(failure)
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_omission(omission)
|
39
|
+
@parent_test_result.add_omission(omission)
|
40
|
+
end
|
41
|
+
|
42
|
+
def passed?
|
43
|
+
@parent_test_result.passed?
|
44
|
+
end
|
45
|
+
|
46
|
+
def stop
|
47
|
+
throw @stop_tag
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Nathaniel Talbott.
|
4
|
+
# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
|
5
|
+
# Copyright:: Copyright (c) 2008-2011 Kouhei Sutou. All rights reserved.
|
6
|
+
# Copyright:: Copyright (c) 2024 Tsutomu Katsube. All rights reserved.
|
7
|
+
# License:: Ruby license.
|
8
|
+
|
9
|
+
require "etc"
|
10
|
+
|
11
|
+
module Test
|
12
|
+
module Unit
|
13
|
+
class TestSuiteRunner
|
14
|
+
@n_workers = Etc.respond_to?(:nprocessors) ? Etc.nprocessors : 1
|
15
|
+
class << self
|
16
|
+
def run_all_tests
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
|
20
|
+
def n_workers
|
21
|
+
@n_workers
|
22
|
+
end
|
23
|
+
|
24
|
+
def n_workers=(n)
|
25
|
+
@n_workers = n
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(test_suite)
|
30
|
+
@test_suite = test_suite
|
31
|
+
end
|
32
|
+
|
33
|
+
def run(result, &progress_block)
|
34
|
+
yield(TestSuite::STARTED, @test_suite.name)
|
35
|
+
yield(TestSuite::STARTED_OBJECT, @test_suite)
|
36
|
+
run_startup(result)
|
37
|
+
run_tests(result, &progress_block)
|
38
|
+
ensure
|
39
|
+
begin
|
40
|
+
run_shutdown(result)
|
41
|
+
ensure
|
42
|
+
yield(TestSuite::FINISHED, @test_suite.name)
|
43
|
+
yield(TestSuite::FINISHED_OBJECT, @test_suite)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def run_startup(result)
|
49
|
+
test_case = @test_suite.test_case
|
50
|
+
return if test_case.nil? or !test_case.respond_to?(:startup)
|
51
|
+
begin
|
52
|
+
test_case.startup
|
53
|
+
rescue Exception
|
54
|
+
raise unless handle_exception($!, result)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_tests(result, &progress_block)
|
59
|
+
@test_suite.tests.each do |test|
|
60
|
+
run_test(test, result, &progress_block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_test(test, result)
|
65
|
+
finished_is_yielded = false
|
66
|
+
finished_object_is_yielded = false
|
67
|
+
previous_event_name = nil
|
68
|
+
test.run(result, runner: self.class) do |event_name, *args|
|
69
|
+
case previous_event_name
|
70
|
+
when Test::Unit::TestCase::STARTED
|
71
|
+
if event_name != Test::Unit::TestCase::STARTED_OBJECT
|
72
|
+
yield(Test::Unit::TestCase::STARTED_OBJECT, test)
|
73
|
+
end
|
74
|
+
when Test::Unit::TestCase::FINISHED
|
75
|
+
if event_name != Test::Unit::TestCase::FINISHED_OBJECT
|
76
|
+
yield(Test::Unit::TestCase::FINISHED_OBJECT, test)
|
77
|
+
end
|
78
|
+
finished_object_is_yielded = true
|
79
|
+
end
|
80
|
+
|
81
|
+
case event_name
|
82
|
+
when Test::Unit::TestCase::STARTED
|
83
|
+
finished_is_yielded = false
|
84
|
+
finished_object_is_yielded = false
|
85
|
+
when Test::Unit::TestCase::FINISHED
|
86
|
+
finished_is_yielded = true
|
87
|
+
end
|
88
|
+
|
89
|
+
previous_event_name = event_name
|
90
|
+
yield(event_name, *args)
|
91
|
+
end
|
92
|
+
|
93
|
+
if finished_is_yielded and not finished_object_is_yielded
|
94
|
+
yield(Test::Unit::TestCase::FINISHED_OBJECT, test)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def run_shutdown(result)
|
99
|
+
test_case = @test_suite.test_case
|
100
|
+
return if test_case.nil? or !test_case.respond_to?(:shutdown)
|
101
|
+
begin
|
102
|
+
test_case.shutdown
|
103
|
+
rescue Exception
|
104
|
+
raise unless handle_exception($!, result)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def handle_exception(exception, result)
|
109
|
+
case exception
|
110
|
+
when *ErrorHandler::PASS_THROUGH_EXCEPTIONS
|
111
|
+
false
|
112
|
+
else
|
113
|
+
result.add_error(Error.new(@test_suite.test_case.name, exception))
|
114
|
+
true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Tsutomu Katsube.
|
4
|
+
# Copyright:: Copyright (c) 2024 Tsutomu Katsube. All rights reserved.
|
5
|
+
# License:: Ruby license.
|
6
|
+
|
7
|
+
require_relative "sub-test-result"
|
8
|
+
require_relative "test-suite-runner"
|
9
|
+
|
10
|
+
module Test
|
11
|
+
module Unit
|
12
|
+
class TestSuiteThreadRunner < TestSuiteRunner
|
13
|
+
@task_queue = Thread::Queue.new
|
14
|
+
class << self
|
15
|
+
def task_queue
|
16
|
+
@task_queue
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_all_tests
|
20
|
+
n_consumers = TestSuiteRunner.n_workers
|
21
|
+
|
22
|
+
consumers = []
|
23
|
+
sub_exceptions = []
|
24
|
+
n_consumers.times do |i|
|
25
|
+
consumers << Thread.new(i) do |worker_id|
|
26
|
+
begin
|
27
|
+
loop do
|
28
|
+
task = @task_queue.pop
|
29
|
+
break if task.nil?
|
30
|
+
catch do |stop_tag|
|
31
|
+
task.call(stop_tag)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
rescue Exception => exception
|
35
|
+
sub_exceptions << exception
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
yield
|
41
|
+
|
42
|
+
n_consumers.times do
|
43
|
+
@task_queue << nil
|
44
|
+
end
|
45
|
+
consumers.each(&:join)
|
46
|
+
sub_exceptions.each do |exception|
|
47
|
+
raise exception
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def run_tests(result, &progress_block)
|
54
|
+
@test_suite.tests.each do |test|
|
55
|
+
if test.is_a?(TestSuite) or not @test_suite.parallel_safe?
|
56
|
+
run_test(test, result, &progress_block)
|
57
|
+
else
|
58
|
+
task = lambda do |stop_tag|
|
59
|
+
sub_result = SubTestResult.new(result)
|
60
|
+
sub_result.stop_tag = stop_tag
|
61
|
+
run_test(test, sub_result, &progress_block)
|
62
|
+
end
|
63
|
+
self.class.task_queue << task
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/test/unit/testcase.rb
CHANGED
@@ -125,6 +125,31 @@ module Test
|
|
125
125
|
AVAILABLE_ORDERS = [:alphabetic, :random, :defined] # :nodoc:
|
126
126
|
|
127
127
|
class << self
|
128
|
+
# Indicates whether the test is parallel safe.
|
129
|
+
#
|
130
|
+
# Tests that this method returns `false` are executed sequentially
|
131
|
+
# before parallel safe tests run. This only works when the `--parallel`
|
132
|
+
# option is specified.
|
133
|
+
#
|
134
|
+
# @example Indicates that test_parallel_unsafe is parallel unsafe
|
135
|
+
#
|
136
|
+
# class TestMyClass < Test::Unit::TestCase
|
137
|
+
# class << self
|
138
|
+
# def parallel_safe?
|
139
|
+
# false
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# def test_parallel_unsafe
|
144
|
+
# # ...
|
145
|
+
# end
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# @since 3.6.3
|
149
|
+
def parallel_safe?
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
128
153
|
def inherited(sub_class) # :nodoc:
|
129
154
|
DESCENDANTS << sub_class
|
130
155
|
super
|
@@ -552,7 +577,7 @@ module Test
|
|
552
577
|
# Runs the individual test method represented by this
|
553
578
|
# instance of the fixture, collecting statistics, failures
|
554
579
|
# and errors in result.
|
555
|
-
def run(result)
|
580
|
+
def run(result, runner: nil)
|
556
581
|
begin
|
557
582
|
@_result = result
|
558
583
|
@internal_data.test_started
|
data/lib/test/unit/testresult.rb
CHANGED
data/lib/test/unit/testsuite.rb
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
# License:: Ruby license.
|
7
7
|
|
8
8
|
require 'test/unit/error'
|
9
|
+
require_relative 'test-suite-runner'
|
9
10
|
|
10
11
|
module Test
|
11
12
|
module Unit
|
@@ -34,32 +35,28 @@ module Test
|
|
34
35
|
@name = name
|
35
36
|
@tests = []
|
36
37
|
@test_case = test_case
|
37
|
-
@n_tests = 0
|
38
38
|
@priority = 0
|
39
39
|
@start_time = nil
|
40
40
|
@elapsed_time = nil
|
41
|
-
|
41
|
+
end
|
42
|
+
|
43
|
+
def parallel_safe?
|
44
|
+
return true if @test_case.nil?
|
45
|
+
@test_case.parallel_safe?
|
42
46
|
end
|
43
47
|
|
44
48
|
# Runs the tests and/or suites contained in this
|
45
49
|
# TestSuite.
|
46
|
-
def run(result, &progress_block)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
ensure
|
57
|
-
begin
|
58
|
-
run_shutdown(result)
|
59
|
-
ensure
|
60
|
-
@elapsed_time = Time.now - @start_time
|
61
|
-
yield(FINISHED, name)
|
62
|
-
yield(FINISHED_OBJECT, self)
|
50
|
+
def run(result, runner: nil, &progress_block)
|
51
|
+
runner ||= TestSuiteRunner
|
52
|
+
runner.new(self).run(result) do |event, *args|
|
53
|
+
case event
|
54
|
+
when STARTED
|
55
|
+
@start_time = Time.now
|
56
|
+
when FINISHED
|
57
|
+
@elapsed_time = Time.now - @start_time
|
58
|
+
end
|
59
|
+
yield(event, *args)
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
@@ -81,7 +78,7 @@ module Test
|
|
81
78
|
# i.e. if the suite contains other suites, it counts the
|
82
79
|
# tests within those suites, not the suites themselves.
|
83
80
|
def size
|
84
|
-
total_size =
|
81
|
+
total_size = 0
|
85
82
|
@tests.each { |test| total_size += test.size }
|
86
83
|
total_size
|
87
84
|
end
|
@@ -104,71 +101,7 @@ module Test
|
|
104
101
|
end
|
105
102
|
|
106
103
|
def passed?
|
107
|
-
@passed
|
108
|
-
end
|
109
|
-
|
110
|
-
private
|
111
|
-
def run_startup(result)
|
112
|
-
return if @test_case.nil? or !@test_case.respond_to?(:startup)
|
113
|
-
begin
|
114
|
-
@test_case.startup
|
115
|
-
rescue Exception
|
116
|
-
raise unless handle_exception($!, result)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def run_test(test, result)
|
121
|
-
finished_is_yielded = false
|
122
|
-
finished_object_is_yielded = false
|
123
|
-
previous_event_name = nil
|
124
|
-
test.run(result) do |event_name, *args|
|
125
|
-
case previous_event_name
|
126
|
-
when Test::Unit::TestCase::STARTED
|
127
|
-
if event_name != Test::Unit::TestCase::STARTED_OBJECT
|
128
|
-
yield(Test::Unit::TestCase::STARTED_OBJECT, test)
|
129
|
-
end
|
130
|
-
when Test::Unit::TestCase::FINISHED
|
131
|
-
if event_name != Test::Unit::TestCase::FINISHED_OBJECT
|
132
|
-
yield(Test::Unit::TestCase::FINISHED_OBJECT, test)
|
133
|
-
end
|
134
|
-
finished_object_is_yielded = true
|
135
|
-
end
|
136
|
-
|
137
|
-
case event_name
|
138
|
-
when Test::Unit::TestCase::STARTED
|
139
|
-
finished_is_yielded = false
|
140
|
-
finished_object_is_yielded = false
|
141
|
-
when Test::Unit::TestCase::FINISHED
|
142
|
-
finished_is_yielded = true
|
143
|
-
end
|
144
|
-
|
145
|
-
previous_event_name = event_name
|
146
|
-
yield(event_name, *args)
|
147
|
-
end
|
148
|
-
|
149
|
-
if finished_is_yielded and not finished_object_is_yielded
|
150
|
-
yield(Test::Unit::TestCase::FINISHED_OBJECT, test)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def run_shutdown(result)
|
155
|
-
return if @test_case.nil? or !@test_case.respond_to?(:shutdown)
|
156
|
-
begin
|
157
|
-
@test_case.shutdown
|
158
|
-
rescue Exception
|
159
|
-
raise unless handle_exception($!, result)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def handle_exception(exception, result)
|
164
|
-
case exception
|
165
|
-
when *ErrorHandler::PASS_THROUGH_EXCEPTIONS
|
166
|
-
false
|
167
|
-
else
|
168
|
-
result.add_error(Error.new(@test_case.name, exception))
|
169
|
-
@passed = false
|
170
|
-
true
|
171
|
-
end
|
104
|
+
@tests.all?(&:passed?)
|
172
105
|
end
|
173
106
|
end
|
174
107
|
end
|
@@ -10,6 +10,7 @@ begin
|
|
10
10
|
require 'io/console'
|
11
11
|
rescue LoadError
|
12
12
|
end
|
13
|
+
require "pathname"
|
13
14
|
|
14
15
|
require 'test/unit/color-scheme'
|
15
16
|
require 'test/unit/code-snippet-fetcher'
|
@@ -28,6 +29,8 @@ module Test
|
|
28
29
|
class TestRunner < UI::TestRunner
|
29
30
|
include OutputLevel
|
30
31
|
|
32
|
+
N_REPORT_SLOW_TESTS = 5
|
33
|
+
|
31
34
|
# Creates a new TestRunner for running the passed
|
32
35
|
# suite. If quiet_mode is true, the output while
|
33
36
|
# running is limited to progress dots, errors and
|
@@ -36,6 +39,7 @@ module Test
|
|
36
39
|
# STDOUT.
|
37
40
|
def initialize(suite, options={})
|
38
41
|
super
|
42
|
+
@on_github_actions = (ENV["GITHUB_ACTIONS"] == "true")
|
39
43
|
@output_level = @options[:output_level] || guess_output_level
|
40
44
|
@output = @options[:output] || STDOUT
|
41
45
|
@use_color = @options[:use_color]
|
@@ -57,6 +61,7 @@ module Test
|
|
57
61
|
@faults = []
|
58
62
|
@code_snippet_fetcher = CodeSnippetFetcher.new
|
59
63
|
@test_suites = []
|
64
|
+
@test_statistics = []
|
60
65
|
end
|
61
66
|
|
62
67
|
private
|
@@ -192,12 +197,71 @@ module Test
|
|
192
197
|
output(fault.test_name, fault_color(fault))
|
193
198
|
output_fault_backtrace(fault)
|
194
199
|
output_failure_message(fault)
|
200
|
+
output_fault_on_github_actions(fault)
|
195
201
|
else
|
196
202
|
output_single("#{fault.label}: ")
|
197
203
|
output_single(fault.test_name, fault_color(fault))
|
198
204
|
output_fault_message(fault)
|
199
205
|
output_fault_backtrace(fault)
|
206
|
+
output_fault_on_github_actions(fault) if fault.is_a?(Error)
|
207
|
+
if fault.is_a?(Error) and fault.exception.respond_to?(:cause)
|
208
|
+
cause = fault.exception.cause
|
209
|
+
i = 0
|
210
|
+
while cause
|
211
|
+
sub_fault = Error.new(fault.test_name,
|
212
|
+
cause,
|
213
|
+
method_name: fault.method_name)
|
214
|
+
output_single("Cause#{i}", fault_color(sub_fault))
|
215
|
+
output_fault_message(sub_fault)
|
216
|
+
output_fault_backtrace(sub_fault)
|
217
|
+
cause = cause.cause
|
218
|
+
i += 1
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def detect_target_location_on_github_actions(fault)
|
225
|
+
return nil unless @on_github_actions
|
226
|
+
|
227
|
+
base_dir = ENV["GITHUB_WORKSPACE"]
|
228
|
+
return nil unless base_dir
|
229
|
+
base_dir = Pathname(base_dir).expand_path
|
230
|
+
|
231
|
+
detector = FaultLocationDetector.new(fault, @code_snippet_fetcher)
|
232
|
+
backtrace = fault.location || []
|
233
|
+
backtrace.each_with_index do |entry, i|
|
234
|
+
next unless detector.target?(entry)
|
235
|
+
file, line, = detector.split_backtrace_entry(entry)
|
236
|
+
file = Pathname(file).expand_path
|
237
|
+
relative_file = file.relative_path_from(base_dir)
|
238
|
+
first_component = relative_file.descend do |component|
|
239
|
+
break component
|
240
|
+
end
|
241
|
+
# file isn't under base_dir
|
242
|
+
next if first_component.to_s == "..."
|
243
|
+
return [relative_file, line]
|
244
|
+
end
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
|
248
|
+
def output_fault_on_github_actions(fault)
|
249
|
+
location = detect_target_location_on_github_actions(fault)
|
250
|
+
return unless location
|
251
|
+
|
252
|
+
parameters = [
|
253
|
+
"file=#{location[0]}",
|
254
|
+
"line=#{location[1]}",
|
255
|
+
"title=#{fault.label}",
|
256
|
+
].join(",")
|
257
|
+
message = fault.message
|
258
|
+
if fault.is_a?(Error)
|
259
|
+
message = ([message] + (fault.location || [])).join("\n")
|
200
260
|
end
|
261
|
+
# We need to use URL encode for new line:
|
262
|
+
# https://github.com/actions/toolkit/issues/193
|
263
|
+
message = message.gsub("\n", "%0A")
|
264
|
+
output("::error #{parameters}::#{message}")
|
201
265
|
end
|
202
266
|
|
203
267
|
def output_fault_message(fault)
|
@@ -331,6 +395,22 @@ module Test
|
|
331
395
|
change_output_level(IMPORTANT_FAULTS_ONLY) do
|
332
396
|
output("Finished in #{elapsed_time} seconds.")
|
333
397
|
end
|
398
|
+
if @options[:report_slow_tests]
|
399
|
+
output_summary_marker
|
400
|
+
output("Top #{N_REPORT_SLOW_TESTS} slow tests")
|
401
|
+
@test_statistics.sort_by {|statistic| -statistic[:elapsed_time]}
|
402
|
+
.first(N_REPORT_SLOW_TESTS)
|
403
|
+
.each do |slow_statistic|
|
404
|
+
left_side = "#{slow_statistic[:name]}: "
|
405
|
+
right_width = @progress_row_max - left_side.size
|
406
|
+
output("%s%*f" % [
|
407
|
+
left_side,
|
408
|
+
right_width,
|
409
|
+
slow_statistic[:elapsed_time],
|
410
|
+
])
|
411
|
+
output("--location #{slow_statistic[:location]}")
|
412
|
+
end
|
413
|
+
end
|
334
414
|
output_summary_marker
|
335
415
|
change_output_level(IMPORTANT_FAULTS_ONLY) do
|
336
416
|
output(@result)
|
@@ -374,7 +454,6 @@ module Test
|
|
374
454
|
output_single("#{indent}#{name}#{separator}#{tab_stop}",
|
375
455
|
nil,
|
376
456
|
VERBOSE)
|
377
|
-
@test_start = Time.now
|
378
457
|
end
|
379
458
|
|
380
459
|
def test_finished(test)
|
@@ -391,9 +470,17 @@ module Test
|
|
391
470
|
end
|
392
471
|
@already_outputted = false
|
393
472
|
|
473
|
+
if @options[:report_slow_tests]
|
474
|
+
@test_statistics << {
|
475
|
+
name: test.name,
|
476
|
+
elapsed_time: test.elapsed_time,
|
477
|
+
location: test.method(test.method_name).source_location.join(":"),
|
478
|
+
}
|
479
|
+
end
|
480
|
+
|
394
481
|
return unless output?(VERBOSE)
|
395
482
|
|
396
|
-
output(": (%f)" %
|
483
|
+
output(": (%f)" % test.elapsed_time, nil, VERBOSE)
|
397
484
|
end
|
398
485
|
|
399
486
|
def suite_name(prefix, suite)
|
@@ -535,7 +622,7 @@ module Test
|
|
535
622
|
end
|
536
623
|
|
537
624
|
def guess_output_level
|
538
|
-
if
|
625
|
+
if @on_github_actions
|
539
626
|
IMPORTANT_FAULTS_ONLY
|
540
627
|
else
|
541
628
|
NORMAL
|
@@ -558,7 +645,7 @@ module Test
|
|
558
645
|
/x
|
559
646
|
|
560
647
|
def guess_color_availability
|
561
|
-
return true if
|
648
|
+
return true if @on_github_actions
|
562
649
|
return false unless @output.tty?
|
563
650
|
return true if windows? and ruby_2_0_or_later?
|
564
651
|
case ENV["TERM"]
|
@@ -576,7 +663,7 @@ module Test
|
|
576
663
|
if @output_level >= VERBOSE
|
577
664
|
:mark
|
578
665
|
else
|
579
|
-
return :fault_only if
|
666
|
+
return :fault_only if @on_github_actions
|
580
667
|
return :fault_only unless @output.tty?
|
581
668
|
:inplace
|
582
669
|
end
|
@@ -22,8 +22,11 @@ module Test
|
|
22
22
|
|
23
23
|
# Creates a new TestRunnerMediator initialized to run
|
24
24
|
# the passed suite.
|
25
|
-
def initialize(suite)
|
25
|
+
def initialize(suite, options={})
|
26
26
|
@suite = suite
|
27
|
+
@options = options
|
28
|
+
@test_suite_runner_class = @options[:test_suite_runner_class]
|
29
|
+
@test_suite_runner_class ||= TestSuiteRunner
|
27
30
|
end
|
28
31
|
|
29
32
|
# Runs the suite the TestRunnerMediator was created
|
@@ -36,13 +39,15 @@ module Test
|
|
36
39
|
Test::Unit.run_at_start_hooks
|
37
40
|
start_time = Time.now
|
38
41
|
begin
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
with_listener(result) do
|
43
|
+
@test_suite_runner_class.run_all_tests do
|
44
|
+
catch do |stop_tag|
|
45
|
+
result.stop_tag = stop_tag
|
46
|
+
notify_listeners(RESET, @suite.size)
|
47
|
+
notify_listeners(STARTED, result)
|
44
48
|
|
45
|
-
|
49
|
+
run_suite(result)
|
50
|
+
end
|
46
51
|
end
|
47
52
|
end
|
48
53
|
ensure
|
@@ -64,7 +69,7 @@ module Test
|
|
64
69
|
if result.nil?
|
65
70
|
run
|
66
71
|
else
|
67
|
-
@suite.run(result) do |channel, value|
|
72
|
+
@suite.run(result, runner: @test_suite_runner_class) do |channel, value|
|
68
73
|
notify_listeners(channel, value)
|
69
74
|
end
|
70
75
|
end
|
@@ -55,10 +55,7 @@ module Test
|
|
55
55
|
if (listener_key.instance_of?(Proc))
|
56
56
|
key = ProcWrapper.new(listener_key)
|
57
57
|
end
|
58
|
-
|
59
|
-
return channel.delete(key)
|
60
|
-
end
|
61
|
-
return nil
|
58
|
+
return channel.delete(key)
|
62
59
|
end
|
63
60
|
|
64
61
|
# Calls all the procs registered on the channel
|
@@ -74,9 +71,8 @@ module Test
|
|
74
71
|
def notify_listeners(channel_name, *arguments)
|
75
72
|
channel = channels[channel_name]
|
76
73
|
return 0 unless (channel)
|
77
|
-
|
78
|
-
|
79
|
-
return listeners.size
|
74
|
+
channel.each_value { |listener| listener.call(*arguments) }
|
75
|
+
return channel.size
|
80
76
|
end
|
81
77
|
|
82
78
|
private
|
data/lib/test/unit/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test-unit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.6.
|
4
|
+
version: 3.6.4
|
5
5
|
platform: ruby
|
6
|
+
original_platform: ''
|
6
7
|
authors:
|
7
8
|
- Kouhei Sutou
|
8
9
|
- Haruka Yoshihara
|
9
|
-
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-11-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: power_assert
|
@@ -25,76 +25,6 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: bundler
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
35
|
-
type: :development
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: '0'
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: rake
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: yard
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: kramdown
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '0'
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: packnga
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '0'
|
91
|
-
type: :development
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - ">="
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '0'
|
98
28
|
description: |-
|
99
29
|
test-unit (Test::Unit) is unit testing framework for Ruby, based on xUnit
|
100
30
|
principles. These were originally designed by Kent Beck, creator of extreme
|
@@ -147,7 +77,10 @@ files:
|
|
147
77
|
- lib/test/unit/runner/console.rb
|
148
78
|
- lib/test/unit/runner/emacs.rb
|
149
79
|
- lib/test/unit/runner/xml.rb
|
80
|
+
- lib/test/unit/sub-test-result.rb
|
150
81
|
- lib/test/unit/test-suite-creator.rb
|
82
|
+
- lib/test/unit/test-suite-runner.rb
|
83
|
+
- lib/test/unit/test-suite-thread-runner.rb
|
151
84
|
- lib/test/unit/testcase.rb
|
152
85
|
- lib/test/unit/testresult.rb
|
153
86
|
- lib/test/unit/testsuite.rb
|
@@ -180,7 +113,6 @@ metadata:
|
|
180
113
|
source_code_uri: https://github.com/test-unit/test-unit
|
181
114
|
documentation_uri: https://test-unit.github.io/test-unit/en/
|
182
115
|
bug_tracker_uri: https://github.com/test-unit/test-unit/issues
|
183
|
-
post_install_message:
|
184
116
|
rdoc_options: []
|
185
117
|
require_paths:
|
186
118
|
- lib
|
@@ -195,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
127
|
- !ruby/object:Gem::Version
|
196
128
|
version: '0'
|
197
129
|
requirements: []
|
198
|
-
rubygems_version: 3.
|
199
|
-
signing_key:
|
130
|
+
rubygems_version: 3.6.0.dev
|
200
131
|
specification_version: 4
|
201
132
|
summary: An xUnit family unit testing framework for Ruby.
|
202
133
|
test_files: []
|