test-unit 3.5.7 → 3.7.0
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/README.md +2 -2
- data/Rakefile +12 -6
- data/bin/test-unit +5 -0
- data/doc/text/getting-started.md +24 -150
- data/doc/text/how-to.md +4 -4
- data/doc/text/news.md +218 -0
- data/lib/test/unit/assertions.rb +8 -8
- data/lib/test/unit/auto-runner-loader.rb +2 -2
- data/lib/test/unit/autorunner.rb +77 -13
- data/lib/test/unit/collector/descendant.rb +1 -1
- data/lib/test/unit/collector/dir.rb +2 -2
- data/lib/test/unit/collector/load.rb +8 -6
- data/lib/test/unit/collector/objectspace.rb +1 -1
- data/lib/test/unit/collector/xml.rb +1 -1
- data/lib/test/unit/color-scheme.rb +1 -1
- data/lib/test/unit/data.rb +1 -1
- data/lib/test/unit/error.rb +1 -1
- data/lib/test/unit/failure.rb +1 -1
- data/lib/test/unit/fault-location-detector.rb +6 -2
- data/lib/test/unit/notification.rb +1 -1
- data/lib/test/unit/omission.rb +1 -1
- data/lib/test/unit/pending.rb +1 -1
- data/lib/test/unit/priority.rb +1 -1
- data/lib/test/unit/runner/console.rb +23 -4
- data/lib/test/unit/runner/emacs.rb +1 -1
- data/lib/test/unit/runner/xml.rb +1 -1
- data/lib/test/unit/sub-test-result.rb +59 -0
- data/lib/test/unit/test-run-context.rb +16 -0
- data/lib/test/unit/test-suite-creator.rb +1 -1
- data/lib/test/unit/test-suite-runner.rb +130 -0
- data/lib/test/unit/test-suite-thread-runner.rb +84 -0
- data/lib/test/unit/test-thread-run-context.rb +20 -0
- data/lib/test/unit/testcase.rb +65 -20
- data/lib/test/unit/testresult.rb +9 -9
- data/lib/test/unit/testsuite.rb +22 -85
- data/lib/test/unit/ui/console/testrunner.rb +165 -36
- data/lib/test/unit/ui/emacs/testrunner.rb +1 -1
- data/lib/test/unit/ui/testrunner.rb +2 -2
- data/lib/test/unit/ui/testrunnermediator.rb +16 -11
- data/lib/test/unit/ui/xml/testrunner.rb +3 -2
- data/lib/test/unit/util/observable.rb +4 -8
- data/lib/test/unit/version.rb +1 -1
- data/lib/test/unit.rb +6 -6
- data/lib/test-unit.rb +2 -2
- metadata +12 -78
@@ -0,0 +1,84 @@
|
|
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
|
+
require_relative "test-thread-run-context"
|
10
|
+
|
11
|
+
module Test
|
12
|
+
module Unit
|
13
|
+
class TestSuiteThreadRunner < TestSuiteRunner
|
14
|
+
class << self
|
15
|
+
def run_all_tests
|
16
|
+
n_workers = TestSuiteRunner.n_workers
|
17
|
+
|
18
|
+
queue = Thread::Queue.new
|
19
|
+
shutdowns = []
|
20
|
+
yield(TestThreadRunContext.new(self, queue, shutdowns))
|
21
|
+
n_workers.times do
|
22
|
+
queue << nil
|
23
|
+
end
|
24
|
+
|
25
|
+
workers = []
|
26
|
+
sub_exceptions = []
|
27
|
+
n_workers.times do |i|
|
28
|
+
workers << Thread.new(i) do |worker_id|
|
29
|
+
begin
|
30
|
+
loop do
|
31
|
+
task = queue.pop
|
32
|
+
break if task.nil?
|
33
|
+
catch do |stop_tag|
|
34
|
+
task.call(stop_tag)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
rescue Exception => exception
|
38
|
+
sub_exceptions << exception
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
workers.each(&:join)
|
43
|
+
|
44
|
+
shutdowns.each(&:call)
|
45
|
+
sub_exceptions.each do |exception|
|
46
|
+
raise exception
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run(result, run_context: nil, &progress_block)
|
52
|
+
yield(TestSuite::STARTED, @test_suite.name)
|
53
|
+
yield(TestSuite::STARTED_OBJECT, @test_suite)
|
54
|
+
run_startup(result)
|
55
|
+
run_tests(result, run_context: run_context, &progress_block)
|
56
|
+
ensure
|
57
|
+
run_context.shutdowns << lambda do
|
58
|
+
begin
|
59
|
+
run_shutdown(result)
|
60
|
+
ensure
|
61
|
+
yield(TestSuite::FINISHED, @test_suite.name)
|
62
|
+
yield(TestSuite::FINISHED_OBJECT, @test_suite)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def run_tests(result, run_context: nil, &progress_block)
|
69
|
+
@test_suite.tests.each do |test|
|
70
|
+
if test.is_a?(TestSuite) or not @test_suite.parallel_safe?
|
71
|
+
run_test(test, result, run_context: run_context, &progress_block)
|
72
|
+
else
|
73
|
+
task = lambda do |stop_tag|
|
74
|
+
sub_result = SubTestResult.new(result)
|
75
|
+
sub_result.stop_tag = stop_tag
|
76
|
+
run_test(test, sub_result, run_context: run_context, &progress_block)
|
77
|
+
end
|
78
|
+
run_context.queue << task
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Tsutomu Katsube.
|
4
|
+
# Copyright:: Copyright (c) 2025 Tsutomu Katsube. All rights reserved.
|
5
|
+
# License:: Ruby license.
|
6
|
+
|
7
|
+
require_relative "test-run-context"
|
8
|
+
|
9
|
+
module Test
|
10
|
+
module Unit
|
11
|
+
class TestThreadRunContext < TestRunContext
|
12
|
+
attr_reader :queue, :shutdowns
|
13
|
+
def initialize(runner_class, queue, shutdowns)
|
14
|
+
super(runner_class)
|
15
|
+
@queue = queue
|
16
|
+
@shutdowns = shutdowns
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/test/unit/testcase.rb
CHANGED
@@ -6,24 +6,24 @@
|
|
6
6
|
# * Copyright (c) 2008-2012 Kouhei Sutou <tt><kou@clear-code.com></tt>
|
7
7
|
# License:: Ruby license.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
9
|
+
require_relative 'attribute'
|
10
|
+
require_relative 'fixture'
|
11
|
+
require_relative 'exception-handler'
|
12
|
+
require_relative 'assertions'
|
13
|
+
require_relative 'failure'
|
14
|
+
require_relative 'error'
|
15
|
+
require_relative 'pending'
|
16
|
+
require_relative 'omission'
|
17
|
+
require_relative 'notification'
|
18
|
+
require_relative 'priority'
|
19
|
+
require_relative 'data'
|
20
|
+
require_relative 'testsuite'
|
21
|
+
require_relative 'test-suite-creator'
|
22
|
+
require_relative 'assertion-failed-error'
|
23
|
+
require_relative 'auto-runner-loader'
|
24
|
+
require_relative 'util/backtracefilter'
|
25
|
+
require_relative 'util/output'
|
26
|
+
require_relative 'util/method-owner-finder'
|
27
27
|
|
28
28
|
module Test
|
29
29
|
module Unit
|
@@ -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
|
@@ -357,7 +382,7 @@ module Test
|
|
357
382
|
# Ractor mode is enabled in the current process and it's not
|
358
383
|
# disabled even when only one Ractor is running after running
|
359
384
|
# a test that uses Ractor on Ruby 3.0. It will be solved in
|
360
|
-
#
|
385
|
+
# the future.
|
361
386
|
#
|
362
387
|
# This is implemented by setting the `:ractor` attribute of
|
363
388
|
# the test to `true`.
|
@@ -376,6 +401,14 @@ module Test
|
|
376
401
|
# end
|
377
402
|
# end
|
378
403
|
#
|
404
|
+
# @example Declares that test_do_something_with_ractor uses Ractor in one line
|
405
|
+
#
|
406
|
+
# ractor def test_do_something_with_ractor
|
407
|
+
# Ractor.new do
|
408
|
+
# # ...
|
409
|
+
# end
|
410
|
+
# end
|
411
|
+
#
|
379
412
|
# @since 3.4.6
|
380
413
|
def ractor(options={})
|
381
414
|
attribute(:ractor, true, options)
|
@@ -552,9 +585,11 @@ module Test
|
|
552
585
|
# Runs the individual test method represented by this
|
553
586
|
# instance of the fixture, collecting statistics, failures
|
554
587
|
# and errors in result.
|
555
|
-
def run(result)
|
588
|
+
def run(result, run_context: nil)
|
556
589
|
begin
|
557
590
|
@_result = result
|
591
|
+
instance_variables_before = instance_variables
|
592
|
+
@internal_data.run_context = run_context
|
558
593
|
@internal_data.test_started
|
559
594
|
yield(STARTED, name)
|
560
595
|
yield(STARTED_OBJECT, self)
|
@@ -596,6 +631,9 @@ module Test
|
|
596
631
|
yield(FINISHED_OBJECT, self)
|
597
632
|
ensure
|
598
633
|
# @_result = nil # For test-spec's after_all :<
|
634
|
+
(instance_variables - instance_variables_before).each do |name|
|
635
|
+
remove_instance_variable(name)
|
636
|
+
end
|
599
637
|
end
|
600
638
|
end
|
601
639
|
|
@@ -831,6 +869,11 @@ module Test
|
|
831
869
|
@internal_data.problem_occurred
|
832
870
|
end
|
833
871
|
|
872
|
+
# Returns test suite runner class for easy to test.
|
873
|
+
def runner_class
|
874
|
+
@internal_data.run_context.runner_class
|
875
|
+
end
|
876
|
+
|
834
877
|
# Notify that the test is passed. Normally, it is not needed
|
835
878
|
# because #run calls it automatically. If you want to override
|
836
879
|
# #run, it is not a good idea. Please contact test-unit
|
@@ -891,6 +934,7 @@ module Test
|
|
891
934
|
class InternalData
|
892
935
|
attr_reader :start_time, :elapsed_time
|
893
936
|
attr_reader :test_data_label, :test_data
|
937
|
+
attr_accessor :run_context
|
894
938
|
def initialize
|
895
939
|
@start_time = nil
|
896
940
|
@elapsed_time = nil
|
@@ -898,6 +942,7 @@ module Test
|
|
898
942
|
@interrupted = false
|
899
943
|
@test_data_label = nil
|
900
944
|
@test_data = nil
|
945
|
+
@run_context = nil
|
901
946
|
end
|
902
947
|
|
903
948
|
def passed?
|
data/lib/test/unit/testresult.rb
CHANGED
@@ -3,12 +3,12 @@
|
|
3
3
|
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
4
4
|
# License:: Ruby license.
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
require_relative 'util/observable'
|
7
|
+
require_relative 'failure'
|
8
|
+
require_relative 'error'
|
9
|
+
require_relative 'omission'
|
10
|
+
require_relative 'pending'
|
11
|
+
require_relative 'notification'
|
12
12
|
|
13
13
|
module Test
|
14
14
|
module Unit
|
@@ -51,9 +51,9 @@ module Test
|
|
51
51
|
end
|
52
52
|
|
53
53
|
# Records a test run.
|
54
|
-
def add_run
|
54
|
+
def add_run(result=self)
|
55
55
|
@run_count += 1
|
56
|
-
notify_listeners(FINISHED,
|
56
|
+
notify_listeners(FINISHED, result)
|
57
57
|
notify_changed
|
58
58
|
end
|
59
59
|
|
@@ -76,7 +76,7 @@ module Test
|
|
76
76
|
*@summary_generators.collect {|generator| __send__(generator)}].join(", ")
|
77
77
|
end
|
78
78
|
|
79
|
-
#
|
79
|
+
# Returns a string that shows result status.
|
80
80
|
def status
|
81
81
|
if passed?
|
82
82
|
if pending_count > 0
|
data/lib/test/unit/testsuite.rb
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
# Copyright:: Copyright (c) 2008-2011 Kouhei Sutou. All rights reserved.
|
6
6
|
# License:: Ruby license.
|
7
7
|
|
8
|
-
|
8
|
+
require_relative 'error'
|
9
|
+
require_relative 'test-suite-runner'
|
9
10
|
|
10
11
|
module Test
|
11
12
|
module Unit
|
@@ -34,32 +35,32 @@ 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
|
-
while test = @tests.shift
|
52
|
-
@n_tests += test.size
|
53
|
-
run_test(test, result, &progress_block)
|
54
|
-
@passed = false unless test.passed?
|
50
|
+
def run(result, run_context: nil, &progress_block)
|
51
|
+
if run_context
|
52
|
+
runner_class = run_context.runner_class
|
53
|
+
else
|
54
|
+
runner_class = TestSuiteRunner
|
55
55
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
56
|
+
runner_class.new(self).run(result, run_context: run_context) do |event, *args|
|
57
|
+
case event
|
58
|
+
when STARTED
|
59
|
+
@start_time = Time.now
|
60
|
+
when FINISHED
|
61
|
+
@elapsed_time = Time.now - @start_time
|
62
|
+
end
|
63
|
+
yield(event, *args)
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
@@ -81,7 +82,7 @@ module Test
|
|
81
82
|
# i.e. if the suite contains other suites, it counts the
|
82
83
|
# tests within those suites, not the suites themselves.
|
83
84
|
def size
|
84
|
-
total_size =
|
85
|
+
total_size = 0
|
85
86
|
@tests.each { |test| total_size += test.size }
|
86
87
|
total_size
|
87
88
|
end
|
@@ -104,71 +105,7 @@ module Test
|
|
104
105
|
end
|
105
106
|
|
106
107
|
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
|
108
|
+
@tests.all?(&:passed?)
|
172
109
|
end
|
173
110
|
end
|
174
111
|
end
|