piko-max-mod 0.0.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 +7 -0
- data/piko-max-mod.gemspec +12 -0
- data/test-unit-3.7.8/BSDL +24 -0
- data/test-unit-3.7.8/COPYING +64 -0
- data/test-unit-3.7.8/PSFL +271 -0
- data/test-unit-3.7.8/README.md +108 -0
- data/test-unit-3.7.8/Rakefile +74 -0
- data/test-unit-3.7.8/bin/test-unit +5 -0
- data/test-unit-3.7.8/doc/text/getting-started.md +120 -0
- data/test-unit-3.7.8/doc/text/how-to.md +90 -0
- data/test-unit-3.7.8/doc/text/news.md +2070 -0
- data/test-unit-3.7.8/lib/test/unit/assertion-failed-error.rb +60 -0
- data/test-unit-3.7.8/lib/test/unit/assertions.rb +2536 -0
- data/test-unit-3.7.8/lib/test/unit/attribute-matcher.rb +26 -0
- data/test-unit-3.7.8/lib/test/unit/attribute.rb +228 -0
- data/test-unit-3.7.8/lib/test/unit/auto-runner-loader.rb +17 -0
- data/test-unit-3.7.8/lib/test/unit/autorunner.rb +654 -0
- data/test-unit-3.7.8/lib/test/unit/code-snippet-fetcher.rb +58 -0
- data/test-unit-3.7.8/lib/test/unit/collector/descendant.rb +20 -0
- data/test-unit-3.7.8/lib/test/unit/collector/dir.rb +110 -0
- data/test-unit-3.7.8/lib/test/unit/collector/load.rb +201 -0
- data/test-unit-3.7.8/lib/test/unit/collector/objectspace.rb +35 -0
- data/test-unit-3.7.8/lib/test/unit/collector/xml.rb +249 -0
- data/test-unit-3.7.8/lib/test/unit/collector.rb +104 -0
- data/test-unit-3.7.8/lib/test/unit/color-scheme.rb +225 -0
- data/test-unit-3.7.8/lib/test/unit/color.rb +134 -0
- data/test-unit-3.7.8/lib/test/unit/data-sets.rb +127 -0
- data/test-unit-3.7.8/lib/test/unit/data.rb +371 -0
- data/test-unit-3.7.8/lib/test/unit/diff.rb +745 -0
- data/test-unit-3.7.8/lib/test/unit/error.rb +158 -0
- data/test-unit-3.7.8/lib/test/unit/exception-handler.rb +82 -0
- data/test-unit-3.7.8/lib/test/unit/failure.rb +169 -0
- data/test-unit-3.7.8/lib/test/unit/fault-location-detector.rb +104 -0
- data/test-unit-3.7.8/lib/test/unit/fixture.rb +304 -0
- data/test-unit-3.7.8/lib/test/unit/notification.rb +138 -0
- data/test-unit-3.7.8/lib/test/unit/omission.rb +198 -0
- data/test-unit-3.7.8/lib/test/unit/pending.rb +155 -0
- data/test-unit-3.7.8/lib/test/unit/priority.rb +194 -0
- data/test-unit-3.7.8/lib/test/unit/process-test-result.rb +55 -0
- data/test-unit-3.7.8/lib/test/unit/process-worker.rb +87 -0
- data/test-unit-3.7.8/lib/test/unit/runner/console.rb +86 -0
- data/test-unit-3.7.8/lib/test/unit/runner/emacs.rb +8 -0
- data/test-unit-3.7.8/lib/test/unit/runner/xml.rb +15 -0
- data/test-unit-3.7.8/lib/test/unit/sub-test-result.rb +59 -0
- data/test-unit-3.7.8/lib/test/unit/test-process-run-context.rb +21 -0
- data/test-unit-3.7.8/lib/test/unit/test-run-context.rb +16 -0
- data/test-unit-3.7.8/lib/test/unit/test-suite-creator.rb +103 -0
- data/test-unit-3.7.8/lib/test/unit/test-suite-process-runner.rb +205 -0
- data/test-unit-3.7.8/lib/test/unit/test-suite-runner.rb +132 -0
- data/test-unit-3.7.8/lib/test/unit/test-suite-thread-runner.rb +96 -0
- data/test-unit-3.7.8/lib/test/unit/test-thread-run-context.rb +23 -0
- data/test-unit-3.7.8/lib/test/unit/testcase.rb +1058 -0
- data/test-unit-3.7.8/lib/test/unit/testresult.rb +132 -0
- data/test-unit-3.7.8/lib/test/unit/testsuite.rb +133 -0
- data/test-unit-3.7.8/lib/test/unit/ui/console/outputlevel.rb +15 -0
- data/test-unit-3.7.8/lib/test/unit/ui/console/testrunner.rb +891 -0
- data/test-unit-3.7.8/lib/test/unit/ui/emacs/testrunner.rb +49 -0
- data/test-unit-3.7.8/lib/test/unit/ui/testrunner.rb +53 -0
- data/test-unit-3.7.8/lib/test/unit/ui/testrunnermediator.rb +131 -0
- data/test-unit-3.7.8/lib/test/unit/ui/testrunnerutilities.rb +41 -0
- data/test-unit-3.7.8/lib/test/unit/ui/xml/testrunner.rb +225 -0
- data/test-unit-3.7.8/lib/test/unit/util/backtracefilter.rb +65 -0
- data/test-unit-3.7.8/lib/test/unit/util/memory-usage.rb +47 -0
- data/test-unit-3.7.8/lib/test/unit/util/method-owner-finder.rb +28 -0
- data/test-unit-3.7.8/lib/test/unit/util/observable.rb +86 -0
- data/test-unit-3.7.8/lib/test/unit/util/output.rb +32 -0
- data/test-unit-3.7.8/lib/test/unit/util/procwrapper.rb +48 -0
- data/test-unit-3.7.8/lib/test/unit/version.rb +5 -0
- data/test-unit-3.7.8/lib/test/unit/warning.rb +3 -0
- data/test-unit-3.7.8/lib/test/unit/worker-context.rb +20 -0
- data/test-unit-3.7.8/lib/test/unit.rb +521 -0
- data/test-unit-3.7.8/lib/test-unit.rb +19 -0
- data/test-unit-3.7.8/sample/adder.rb +13 -0
- data/test-unit-3.7.8/sample/subtracter.rb +12 -0
- data/test-unit-3.7.8/sample/test_adder.rb +20 -0
- data/test-unit-3.7.8/sample/test_subtracter.rb +20 -0
- data/test-unit-3.7.8/sample/test_user.rb +23 -0
- metadata +117 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
#--
|
|
2
|
+
#
|
|
3
|
+
# Author:: Nathaniel Talbott.
|
|
4
|
+
# Copyright::
|
|
5
|
+
# * Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
|
|
6
|
+
# * Copyright (c) 2008-2023 Sutou Kouhei <kou@clear-code.com>
|
|
7
|
+
# License:: Ruby license.
|
|
8
|
+
|
|
9
|
+
begin
|
|
10
|
+
require 'io/console'
|
|
11
|
+
rescue LoadError
|
|
12
|
+
end
|
|
13
|
+
require "pathname"
|
|
14
|
+
|
|
15
|
+
require_relative '../../color-scheme'
|
|
16
|
+
require_relative '../../code-snippet-fetcher'
|
|
17
|
+
require_relative '../../fault-location-detector'
|
|
18
|
+
require_relative '../../diff'
|
|
19
|
+
require_relative '../testrunner'
|
|
20
|
+
require_relative '../testrunnermediator'
|
|
21
|
+
require_relative 'outputlevel'
|
|
22
|
+
|
|
23
|
+
module Test
|
|
24
|
+
module Unit
|
|
25
|
+
module UI
|
|
26
|
+
module Console
|
|
27
|
+
|
|
28
|
+
# Runs a Test::Unit::TestSuite on the console.
|
|
29
|
+
class TestRunner < UI::TestRunner
|
|
30
|
+
include OutputLevel
|
|
31
|
+
|
|
32
|
+
N_REPORT_SLOW_TESTS = 5
|
|
33
|
+
|
|
34
|
+
# Creates a new TestRunner for running the passed
|
|
35
|
+
# suite. If quiet_mode is true, the output while
|
|
36
|
+
# running is limited to progress dots, errors and
|
|
37
|
+
# failures, and the final result. io specifies
|
|
38
|
+
# where runner output should go to; defaults to
|
|
39
|
+
# STDOUT.
|
|
40
|
+
def initialize(suite, options={})
|
|
41
|
+
super
|
|
42
|
+
@on_github_actions = (ENV["GITHUB_ACTIONS"] == "true")
|
|
43
|
+
@output_level = @options[:output_level] || guess_output_level
|
|
44
|
+
@output = @options[:output] || STDOUT
|
|
45
|
+
@use_color = @options[:use_color]
|
|
46
|
+
@use_color = guess_color_availability if @use_color.nil?
|
|
47
|
+
@color_scheme = @options[:color_scheme] || ColorScheme.default
|
|
48
|
+
@reset_color = Color.new("reset")
|
|
49
|
+
@progress_style = @options[:progress_style] || guess_progress_style
|
|
50
|
+
@progress_marks = ["|", "/", "-", "\\", "|", "/", "-", "\\"]
|
|
51
|
+
@progress_mark_index = 0
|
|
52
|
+
@progress_row = 0
|
|
53
|
+
@progress_row_max = @options[:progress_row_max]
|
|
54
|
+
@progress_row_max ||= guess_progress_row_max
|
|
55
|
+
@show_detail_immediately = @options[:show_detail_immediately]
|
|
56
|
+
@show_detail_immediately = true if @show_detail_immediately.nil?
|
|
57
|
+
@already_outputted = false
|
|
58
|
+
@indent = 0
|
|
59
|
+
@top_level = true
|
|
60
|
+
@current_output_level = NORMAL
|
|
61
|
+
@faults = []
|
|
62
|
+
@code_snippet_fetcher = CodeSnippetFetcher.new
|
|
63
|
+
@test_suites = []
|
|
64
|
+
@test_statistics = []
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
def change_output_level(level)
|
|
69
|
+
old_output_level = @current_output_level
|
|
70
|
+
@current_output_level = level
|
|
71
|
+
yield
|
|
72
|
+
@current_output_level = old_output_level
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def setup_mediator
|
|
76
|
+
super
|
|
77
|
+
output_setup_end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def output_setup_end
|
|
81
|
+
suite_name = @suite.to_s
|
|
82
|
+
suite_name = @suite.name if @suite.kind_of?(Module)
|
|
83
|
+
output("Loaded suite #{suite_name}")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def attach_to_mediator
|
|
87
|
+
@mediator.add_listener(TestResult::FAULT,
|
|
88
|
+
&method(:add_fault))
|
|
89
|
+
@mediator.add_listener(TestRunnerMediator::STARTED,
|
|
90
|
+
&method(:started))
|
|
91
|
+
@mediator.add_listener(TestRunnerMediator::FINISHED,
|
|
92
|
+
&method(:finished))
|
|
93
|
+
@mediator.add_listener(TestCase::STARTED_OBJECT,
|
|
94
|
+
&method(:test_started))
|
|
95
|
+
@mediator.add_listener(TestCase::FINISHED_OBJECT,
|
|
96
|
+
&method(:test_finished))
|
|
97
|
+
@mediator.add_listener(TestSuite::STARTED_OBJECT,
|
|
98
|
+
&method(:test_suite_started))
|
|
99
|
+
@mediator.add_listener(TestSuite::FINISHED_OBJECT,
|
|
100
|
+
&method(:test_suite_finished))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def add_fault(fault)
|
|
104
|
+
@faults << fault
|
|
105
|
+
output_progress(fault.single_character_display,
|
|
106
|
+
fault_marker_color(fault))
|
|
107
|
+
output_progress_in_detail(fault) if @show_detail_immediately
|
|
108
|
+
@already_outputted = true if fault.critical?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def started(result)
|
|
112
|
+
@result = result
|
|
113
|
+
output_started
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def output_started
|
|
117
|
+
output("Started")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def finished(elapsed_time)
|
|
121
|
+
unless @show_detail_immediately
|
|
122
|
+
nl if output?(NORMAL) and !output?(VERBOSE)
|
|
123
|
+
output_faults
|
|
124
|
+
end
|
|
125
|
+
case @progress_style
|
|
126
|
+
when :inplace
|
|
127
|
+
output_single("\r", nil, PROGRESS_ONLY)
|
|
128
|
+
when :mark
|
|
129
|
+
nl(PROGRESS_ONLY)
|
|
130
|
+
end
|
|
131
|
+
output_statistics(elapsed_time)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def output_faults
|
|
135
|
+
categorized_faults = categorize_faults
|
|
136
|
+
change_output_level(IMPORTANT_FAULTS_ONLY) do
|
|
137
|
+
output_faults_in_detail(categorized_faults[:need_detail_faults])
|
|
138
|
+
end
|
|
139
|
+
output_faults_in_short("Omissions", Omission,
|
|
140
|
+
categorized_faults[:omissions])
|
|
141
|
+
output_faults_in_short("Notifications", Notification,
|
|
142
|
+
categorized_faults[:notifications])
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def max_digit(max_number)
|
|
146
|
+
(Math.log10(max_number) + 1).truncate
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def output_faults_in_detail(faults)
|
|
150
|
+
return if faults.nil?
|
|
151
|
+
digit = max_digit(faults.size)
|
|
152
|
+
faults.each_with_index do |fault, index|
|
|
153
|
+
nl
|
|
154
|
+
output_single("%#{digit}d) " % (index + 1))
|
|
155
|
+
output_fault_in_detail(fault)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def output_faults_in_short(label, fault_class, faults)
|
|
160
|
+
return if faults.nil?
|
|
161
|
+
digit = max_digit(faults.size)
|
|
162
|
+
nl
|
|
163
|
+
output_single(label, fault_class_color(fault_class))
|
|
164
|
+
output(":")
|
|
165
|
+
faults.each_with_index do |fault, index|
|
|
166
|
+
output_single("%#{digit}d) " % (index + 1))
|
|
167
|
+
output_fault_in_short(fault)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def categorize_faults
|
|
172
|
+
faults = {}
|
|
173
|
+
@faults.each do |fault|
|
|
174
|
+
category = categorize_fault(fault)
|
|
175
|
+
faults[category] ||= []
|
|
176
|
+
faults[category] << fault
|
|
177
|
+
end
|
|
178
|
+
faults
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def categorize_fault(fault)
|
|
182
|
+
case fault
|
|
183
|
+
when Omission
|
|
184
|
+
:omissions
|
|
185
|
+
when Notification
|
|
186
|
+
:notifications
|
|
187
|
+
else
|
|
188
|
+
:need_detail_faults
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def output_fault_in_detail(fault)
|
|
193
|
+
if fault.is_a?(Failure) and
|
|
194
|
+
fault.inspected_expected and
|
|
195
|
+
fault.inspected_actual
|
|
196
|
+
output_single("#{fault.label}: ")
|
|
197
|
+
output(fault.test_name, fault_color(fault))
|
|
198
|
+
output_fault_backtrace(fault)
|
|
199
|
+
output_failure_message(fault)
|
|
200
|
+
output_fault_on_github_actions(fault)
|
|
201
|
+
else
|
|
202
|
+
output_single("#{fault.label}: ")
|
|
203
|
+
output_single(fault.test_name, fault_color(fault))
|
|
204
|
+
output_fault_message(fault)
|
|
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")
|
|
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}")
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def output_fault_message(fault)
|
|
268
|
+
message = fault.message
|
|
269
|
+
return if message.nil?
|
|
270
|
+
|
|
271
|
+
if message.include?("\n")
|
|
272
|
+
output(":")
|
|
273
|
+
message.each_line do |line|
|
|
274
|
+
output(" #{line.chomp}")
|
|
275
|
+
end
|
|
276
|
+
else
|
|
277
|
+
output(": #{message}")
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def output_fault_backtrace(fault)
|
|
282
|
+
detector = FaultLocationDetector.new(fault, @code_snippet_fetcher)
|
|
283
|
+
backtrace = fault.location
|
|
284
|
+
# workaround for test-spec. :<
|
|
285
|
+
# see also GitHub:#22
|
|
286
|
+
backtrace ||= []
|
|
287
|
+
|
|
288
|
+
code_snippet_backtrace_index = nil
|
|
289
|
+
code_snippet_lines = nil
|
|
290
|
+
backtrace.each_with_index do |entry, i|
|
|
291
|
+
next unless detector.target?(entry)
|
|
292
|
+
file, line_number, = detector.split_backtrace_entry(entry)
|
|
293
|
+
lines = fetch_code_snippet(file, line_number)
|
|
294
|
+
unless lines.empty?
|
|
295
|
+
code_snippet_backtrace_index = i
|
|
296
|
+
code_snippet_lines = lines
|
|
297
|
+
break
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
backtrace.each_with_index do |entry, i|
|
|
302
|
+
output(entry)
|
|
303
|
+
if i == code_snippet_backtrace_index
|
|
304
|
+
output_code_snippet(code_snippet_lines, fault_color(fault))
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def fetch_code_snippet(file, line_number)
|
|
310
|
+
@code_snippet_fetcher.fetch(file, line_number)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def output_code_snippet(lines, target_line_color=nil)
|
|
314
|
+
max_n = lines.collect {|n, line, attributes| n}.max
|
|
315
|
+
digits = (Math.log10(max_n) + 1).truncate
|
|
316
|
+
lines.each do |n, line, attributes|
|
|
317
|
+
if attributes[:target_line?]
|
|
318
|
+
line_color = target_line_color
|
|
319
|
+
current_line_mark = "=>"
|
|
320
|
+
else
|
|
321
|
+
line_color = nil
|
|
322
|
+
current_line_mark = ""
|
|
323
|
+
end
|
|
324
|
+
output(" %2s %*d: %s" % [current_line_mark, digits, n, line],
|
|
325
|
+
line_color)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def output_failure_message(failure)
|
|
330
|
+
if failure.expected.respond_to?(:encoding) and
|
|
331
|
+
failure.actual.respond_to?(:encoding) and
|
|
332
|
+
failure.expected.encoding != failure.actual.encoding
|
|
333
|
+
need_encoding = true
|
|
334
|
+
else
|
|
335
|
+
need_encoding = false
|
|
336
|
+
end
|
|
337
|
+
output(failure.user_message) if failure.user_message
|
|
338
|
+
output_single("<")
|
|
339
|
+
output_single(failure.inspected_expected, color("pass"))
|
|
340
|
+
output_single(">")
|
|
341
|
+
if need_encoding
|
|
342
|
+
output_single("(")
|
|
343
|
+
output_single(failure.expected.encoding.name, color("pass"))
|
|
344
|
+
output_single(")")
|
|
345
|
+
end
|
|
346
|
+
output(" expected but was")
|
|
347
|
+
output_single("<")
|
|
348
|
+
output_single(failure.inspected_actual, color("failure"))
|
|
349
|
+
output_single(">")
|
|
350
|
+
if need_encoding
|
|
351
|
+
output_single("(")
|
|
352
|
+
output_single(failure.actual.encoding.name, color("failure"))
|
|
353
|
+
output_single(")")
|
|
354
|
+
end
|
|
355
|
+
output("")
|
|
356
|
+
from, to = prepare_for_diff(failure.expected, failure.actual)
|
|
357
|
+
if from and to
|
|
358
|
+
if need_encoding
|
|
359
|
+
unless from.valid_encoding?
|
|
360
|
+
from = from.dup.force_encoding("ASCII-8BIT")
|
|
361
|
+
end
|
|
362
|
+
unless to.valid_encoding?
|
|
363
|
+
to = to.dup.force_encoding("ASCII-8BIT")
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
from_lines = from.split(/\r?\n/)
|
|
367
|
+
to_lines = to.split(/\r?\n/)
|
|
368
|
+
if need_encoding
|
|
369
|
+
from_lines << ""
|
|
370
|
+
to_lines << ""
|
|
371
|
+
from_lines << "Encoding: #{failure.expected.encoding.name}"
|
|
372
|
+
to_lines << "Encoding: #{failure.actual.encoding.name}"
|
|
373
|
+
end
|
|
374
|
+
differ = ColorizedReadableDiffer.new(from_lines, to_lines, self)
|
|
375
|
+
if differ.need_diff?
|
|
376
|
+
output("")
|
|
377
|
+
output("diff:")
|
|
378
|
+
differ.diff
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def output_fault_in_short(fault)
|
|
384
|
+
output_single("#{fault.label}: ")
|
|
385
|
+
output_single(fault.message, fault_color(fault))
|
|
386
|
+
output(" [#{fault.test_name}]")
|
|
387
|
+
output(fault.location.first)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def format_fault(fault)
|
|
391
|
+
fault.long_display
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def output_statistics(elapsed_time)
|
|
395
|
+
change_output_level(IMPORTANT_FAULTS_ONLY) do
|
|
396
|
+
output("Finished in #{elapsed_time} seconds.")
|
|
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
|
|
414
|
+
output_summary_marker
|
|
415
|
+
change_output_level(IMPORTANT_FAULTS_ONLY) do
|
|
416
|
+
output(@result)
|
|
417
|
+
end
|
|
418
|
+
output("%g%% passed" % @result.pass_percentage)
|
|
419
|
+
unless elapsed_time.zero?
|
|
420
|
+
output_summary_marker
|
|
421
|
+
test_throughput = @result.run_count / elapsed_time
|
|
422
|
+
assertion_throughput = @result.assertion_count / elapsed_time
|
|
423
|
+
throughput = [
|
|
424
|
+
"%.2f tests/s" % test_throughput,
|
|
425
|
+
"%.2f assertions/s" % assertion_throughput,
|
|
426
|
+
]
|
|
427
|
+
output(throughput.join(", "))
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def output_summary_marker
|
|
432
|
+
return if @progress_row_max <= 0
|
|
433
|
+
output("-" * @progress_row_max, summary_marker_color)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def test_started(test)
|
|
437
|
+
return unless output?(VERBOSE)
|
|
438
|
+
|
|
439
|
+
tab_width = 8
|
|
440
|
+
name = test.local_name
|
|
441
|
+
separator = ":"
|
|
442
|
+
left_used = indent.size + name.size + separator.size
|
|
443
|
+
right_space = tab_width * 2
|
|
444
|
+
left_space = @progress_row_max - right_space
|
|
445
|
+
if (left_used % tab_width).zero?
|
|
446
|
+
left_space -= left_used
|
|
447
|
+
n_tabs = 0
|
|
448
|
+
else
|
|
449
|
+
left_space -= ((left_used / tab_width) + 1) * tab_width
|
|
450
|
+
n_tabs = 1
|
|
451
|
+
end
|
|
452
|
+
n_tabs += [left_space, 0].max / tab_width
|
|
453
|
+
tab_stop = "\t" * n_tabs
|
|
454
|
+
output_single("#{indent}#{name}#{separator}#{tab_stop}",
|
|
455
|
+
nil,
|
|
456
|
+
VERBOSE)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def test_finished(test)
|
|
460
|
+
unless @already_outputted
|
|
461
|
+
case @progress_style
|
|
462
|
+
when :inplace
|
|
463
|
+
mark = @progress_marks[@progress_mark_index]
|
|
464
|
+
@progress_mark_index =
|
|
465
|
+
(@progress_mark_index + 1) % @progress_marks.size
|
|
466
|
+
output_progress(mark, color("pass-marker"))
|
|
467
|
+
when :mark
|
|
468
|
+
output_progress(".", color("pass-marker"))
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
@already_outputted = false
|
|
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
|
+
|
|
481
|
+
return unless output?(VERBOSE)
|
|
482
|
+
|
|
483
|
+
output(": (%f)" % test.elapsed_time, nil, VERBOSE)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def suite_name(prefix, suite)
|
|
487
|
+
name = suite.name
|
|
488
|
+
if name.nil?
|
|
489
|
+
"(anonymous)"
|
|
490
|
+
else
|
|
491
|
+
name.sub(/\A#{Regexp.escape(prefix)}/, "")
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def test_suite_started(suite)
|
|
496
|
+
last_test_suite = @test_suites.last
|
|
497
|
+
@test_suites << suite
|
|
498
|
+
if @top_level
|
|
499
|
+
@top_level = false
|
|
500
|
+
return
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
output_single(indent, nil, VERBOSE)
|
|
504
|
+
if suite.test_case.nil?
|
|
505
|
+
_color = color("suite")
|
|
506
|
+
else
|
|
507
|
+
_color = color("case")
|
|
508
|
+
end
|
|
509
|
+
prefix = "#{last_test_suite.name}::"
|
|
510
|
+
output_single(suite_name(prefix, suite), _color, VERBOSE)
|
|
511
|
+
output(": ", nil, VERBOSE)
|
|
512
|
+
@indent += 2
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def test_suite_finished(suite)
|
|
516
|
+
@indent -= 2
|
|
517
|
+
@test_suites.pop
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def indent
|
|
521
|
+
if output?(VERBOSE)
|
|
522
|
+
" " * @indent
|
|
523
|
+
else
|
|
524
|
+
""
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def nl(level=nil)
|
|
529
|
+
output("", nil, level)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def output(something, color=nil, level=nil)
|
|
533
|
+
return unless output?(level)
|
|
534
|
+
output_single(something, color, level)
|
|
535
|
+
@output.puts
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def output_single(something, color=nil, level=nil)
|
|
539
|
+
return false unless output?(level)
|
|
540
|
+
something.to_s.each_line do |line|
|
|
541
|
+
if @use_color and color
|
|
542
|
+
line = "%s%s%s" % [color.escape_sequence,
|
|
543
|
+
line,
|
|
544
|
+
@reset_color.escape_sequence]
|
|
545
|
+
end
|
|
546
|
+
@output.write(line)
|
|
547
|
+
end
|
|
548
|
+
@output.flush
|
|
549
|
+
true
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def output_progress(mark, color=nil)
|
|
553
|
+
if @progress_style == :inplace
|
|
554
|
+
output_single("\r#{mark}", color, PROGRESS_ONLY)
|
|
555
|
+
else
|
|
556
|
+
return unless output?(PROGRESS_ONLY)
|
|
557
|
+
output_single(mark, color)
|
|
558
|
+
return unless @progress_row_max > 0
|
|
559
|
+
@progress_row += mark.size
|
|
560
|
+
if @progress_row >= @progress_row_max
|
|
561
|
+
nl unless @output_level == VERBOSE
|
|
562
|
+
@progress_row = 0
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def output_progress_in_detail_marker(fault)
|
|
568
|
+
return if @progress_row_max <= 0
|
|
569
|
+
output("=" * @progress_row_max)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def output_progress_in_detail(fault)
|
|
573
|
+
return if @output_level == SILENT
|
|
574
|
+
need_detail_faults = (categorize_fault(fault) == :need_detail_faults)
|
|
575
|
+
if need_detail_faults
|
|
576
|
+
log_level = IMPORTANT_FAULTS_ONLY
|
|
577
|
+
else
|
|
578
|
+
log_level = @current_output_level
|
|
579
|
+
end
|
|
580
|
+
change_output_level(log_level) do
|
|
581
|
+
nl(NORMAL)
|
|
582
|
+
output_progress_in_detail_marker(fault)
|
|
583
|
+
if need_detail_faults
|
|
584
|
+
output_fault_in_detail(fault)
|
|
585
|
+
else
|
|
586
|
+
output_fault_in_short(fault)
|
|
587
|
+
end
|
|
588
|
+
output_progress_in_detail_marker(fault)
|
|
589
|
+
@progress_row = 0
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def output?(level)
|
|
594
|
+
(level || @current_output_level) <= @output_level
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def color(name)
|
|
598
|
+
_color = @color_scheme[name]
|
|
599
|
+
_color ||= @color_scheme["success"] if name == "pass"
|
|
600
|
+
_color ||= ColorScheme.default[name]
|
|
601
|
+
_color
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def fault_class_color_name(fault_class)
|
|
605
|
+
fault_class.name.split(/::/).last.downcase
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def fault_class_color(fault_class)
|
|
609
|
+
color(fault_class_color_name(fault_class))
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
def fault_color(fault)
|
|
613
|
+
fault_class_color(fault.class)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
def fault_marker_color(fault)
|
|
617
|
+
color("#{fault_class_color_name(fault.class)}-marker")
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def summary_marker_color
|
|
621
|
+
color("#{@result.status}-marker")
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def guess_output_level
|
|
625
|
+
if @on_github_actions
|
|
626
|
+
IMPORTANT_FAULTS_ONLY
|
|
627
|
+
else
|
|
628
|
+
NORMAL
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
TERM_COLOR_SUPPORT = /
|
|
633
|
+
color| # explicitly claims color support in the name
|
|
634
|
+
direct| # explicitly claims "direct color" (24 bit) support
|
|
635
|
+
#{ColorScheme::TERM_256}|
|
|
636
|
+
\Acygwin|
|
|
637
|
+
\Alinux|
|
|
638
|
+
\Ansterm-bce|
|
|
639
|
+
\Ansterm-c-|
|
|
640
|
+
\Aputty|
|
|
641
|
+
\Arxvt|
|
|
642
|
+
\Ascreen|
|
|
643
|
+
\Atmux|
|
|
644
|
+
\Axterm
|
|
645
|
+
/x
|
|
646
|
+
|
|
647
|
+
def guess_color_availability
|
|
648
|
+
return true if @on_github_actions
|
|
649
|
+
return false unless @output.tty?
|
|
650
|
+
return true if windows? and ruby_2_0_or_later?
|
|
651
|
+
case ENV["TERM"]
|
|
652
|
+
when /(?:term|screen)(?:-(?:256)?color)?\z/
|
|
653
|
+
true
|
|
654
|
+
when TERM_COLOR_SUPPORT
|
|
655
|
+
true
|
|
656
|
+
else
|
|
657
|
+
return true if ENV["EMACS"] == "t"
|
|
658
|
+
false
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def guess_progress_style
|
|
663
|
+
if @output_level >= VERBOSE
|
|
664
|
+
:mark
|
|
665
|
+
else
|
|
666
|
+
return :fault_only if @on_github_actions
|
|
667
|
+
return :fault_only unless @output.tty?
|
|
668
|
+
:inplace
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def windows?
|
|
673
|
+
/mswin|mingw/ === RUBY_PLATFORM
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
def ruby_2_0_or_later?
|
|
677
|
+
RUBY_VERSION >= "2.0.0"
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
def guess_progress_row_max
|
|
681
|
+
term_width = guess_term_width
|
|
682
|
+
if term_width.zero?
|
|
683
|
+
if ENV["EMACS"] == "t"
|
|
684
|
+
-1
|
|
685
|
+
else
|
|
686
|
+
79
|
|
687
|
+
end
|
|
688
|
+
else
|
|
689
|
+
term_width
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
def guess_term_width
|
|
694
|
+
guess_term_width_from_io || guess_term_width_from_env || 0
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def guess_term_width_from_io
|
|
698
|
+
if @output.respond_to?(:winsize)
|
|
699
|
+
begin
|
|
700
|
+
@output.winsize[1]
|
|
701
|
+
rescue SystemCallError
|
|
702
|
+
nil
|
|
703
|
+
end
|
|
704
|
+
else
|
|
705
|
+
nil
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def guess_term_width_from_env
|
|
710
|
+
env = ENV["COLUMNS"] || ENV["TERM_WIDTH"]
|
|
711
|
+
return nil if env.nil?
|
|
712
|
+
|
|
713
|
+
begin
|
|
714
|
+
Integer(env)
|
|
715
|
+
rescue ArgumentError
|
|
716
|
+
nil
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
class ColorizedReadableDiffer < Diff::ReadableDiffer
|
|
722
|
+
def initialize(from, to, runner)
|
|
723
|
+
@runner = runner
|
|
724
|
+
super(from, to)
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def need_diff?(options={})
|
|
728
|
+
return false if one_line_all_change?
|
|
729
|
+
operations.each do |tag,|
|
|
730
|
+
return true if [:replace, :equal].include?(tag)
|
|
731
|
+
end
|
|
732
|
+
false
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
private
|
|
736
|
+
def one_line_all_change?
|
|
737
|
+
return false if operations.size != 1
|
|
738
|
+
|
|
739
|
+
tag, from_start, from_end, to_start, to_end = operations.first
|
|
740
|
+
return false if tag != :replace
|
|
741
|
+
return false if [from_start, from_end] != [0, 1]
|
|
742
|
+
return false if [from_start, from_end] != [to_start, to_end]
|
|
743
|
+
|
|
744
|
+
_, _, _line_operations = line_operations(@from.first, @to.first)
|
|
745
|
+
_line_operations.size == 1
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
def output_single(something, color=nil)
|
|
749
|
+
@runner.__send__(:output_single, something, color)
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
def output(something, color=nil)
|
|
753
|
+
@runner.__send__(:output, something, color)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def color(name)
|
|
757
|
+
@runner.__send__(:color, name)
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
def cut_off_ratio
|
|
761
|
+
0
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
def default_ratio
|
|
765
|
+
0
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
def tag(mark, color_name, contents)
|
|
769
|
+
_color = color(color_name)
|
|
770
|
+
contents.each do |content|
|
|
771
|
+
output_single(mark, _color)
|
|
772
|
+
output_single(" ")
|
|
773
|
+
output(content)
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def tag_deleted(contents)
|
|
778
|
+
tag("-", "diff-deleted-tag", contents)
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def tag_inserted(contents)
|
|
782
|
+
tag("+", "diff-inserted-tag", contents)
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
def tag_equal(contents)
|
|
786
|
+
tag(" ", "normal", contents)
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
def tag_difference(contents)
|
|
790
|
+
tag("?", "diff-difference-tag", contents)
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
def diff_line(from_line, to_line)
|
|
794
|
+
to_operations = []
|
|
795
|
+
mark_operations = []
|
|
796
|
+
from_line, to_line, _operations = line_operations(from_line, to_line)
|
|
797
|
+
|
|
798
|
+
no_replace = true
|
|
799
|
+
_operations.each do |tag,|
|
|
800
|
+
if tag == :replace
|
|
801
|
+
no_replace = false
|
|
802
|
+
break
|
|
803
|
+
end
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
output_single("?", color("diff-difference-tag"))
|
|
807
|
+
output_single(" ")
|
|
808
|
+
_operations.each do |tag, from_start, from_end, to_start, to_end|
|
|
809
|
+
from_width = compute_width(from_line, from_start, from_end)
|
|
810
|
+
to_width = compute_width(to_line, to_start, to_end)
|
|
811
|
+
case tag
|
|
812
|
+
when :replace
|
|
813
|
+
output_single(from_line[from_start...from_end],
|
|
814
|
+
color("diff-deleted"))
|
|
815
|
+
if (from_width < to_width)
|
|
816
|
+
output_single(" " * (to_width - from_width))
|
|
817
|
+
end
|
|
818
|
+
to_operations << Proc.new do
|
|
819
|
+
output_single(to_line[to_start...to_end],
|
|
820
|
+
color("diff-inserted"))
|
|
821
|
+
if (to_width < from_width)
|
|
822
|
+
output_single(" " * (from_width - to_width))
|
|
823
|
+
end
|
|
824
|
+
end
|
|
825
|
+
mark_operations << Proc.new do
|
|
826
|
+
output_single("?" * from_width,
|
|
827
|
+
color("diff-difference-tag"))
|
|
828
|
+
if (to_width < from_width)
|
|
829
|
+
output_single(" " * (from_width - to_width))
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
when :delete
|
|
833
|
+
output_single(from_line[from_start...from_end],
|
|
834
|
+
color("diff-deleted"))
|
|
835
|
+
unless no_replace
|
|
836
|
+
to_operations << Proc.new {output_single(" " * from_width)}
|
|
837
|
+
mark_operations << Proc.new do
|
|
838
|
+
output_single("-" * from_width,
|
|
839
|
+
color("diff-deleted"))
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
when :insert
|
|
843
|
+
if no_replace
|
|
844
|
+
output_single(to_line[to_start...to_end],
|
|
845
|
+
color("diff-inserted"))
|
|
846
|
+
else
|
|
847
|
+
output_single(" " * to_width)
|
|
848
|
+
to_operations << Proc.new do
|
|
849
|
+
output_single(to_line[to_start...to_end],
|
|
850
|
+
color("diff-inserted"))
|
|
851
|
+
end
|
|
852
|
+
mark_operations << Proc.new do
|
|
853
|
+
output_single("+" * to_width,
|
|
854
|
+
color("diff-inserted"))
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
when :equal
|
|
858
|
+
output_single(from_line[from_start...from_end])
|
|
859
|
+
unless no_replace
|
|
860
|
+
to_operations << Proc.new {output_single(" " * to_width)}
|
|
861
|
+
mark_operations << Proc.new {output_single(" " * to_width)}
|
|
862
|
+
end
|
|
863
|
+
else
|
|
864
|
+
raise "unknown tag: #{tag}"
|
|
865
|
+
end
|
|
866
|
+
end
|
|
867
|
+
output("")
|
|
868
|
+
|
|
869
|
+
unless to_operations.empty?
|
|
870
|
+
output_single("?", color("diff-difference-tag"))
|
|
871
|
+
output_single(" ")
|
|
872
|
+
to_operations.each do |operation|
|
|
873
|
+
operation.call
|
|
874
|
+
end
|
|
875
|
+
output("")
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
unless mark_operations.empty?
|
|
879
|
+
output_single("?", color("diff-difference-tag"))
|
|
880
|
+
output_single(" ")
|
|
881
|
+
mark_operations.each do |operation|
|
|
882
|
+
operation.call
|
|
883
|
+
end
|
|
884
|
+
output("")
|
|
885
|
+
end
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
end
|
|
889
|
+
end
|
|
890
|
+
end
|
|
891
|
+
end
|