minitest 5.16.2 → 6.0.5
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
- checksums.yaml.gz.sig +0 -0
- data/History.rdoc +373 -1
- data/Manifest.txt +16 -4
- data/README.rdoc +48 -118
- data/Rakefile +17 -2
- data/bin/minitest +5 -0
- data/design_rationale.rb +21 -19
- data/lib/hoe/minitest.rb +4 -2
- data/lib/minitest/assertions.rb +142 -124
- data/lib/minitest/autorun.rb +3 -11
- data/lib/minitest/benchmark.rb +9 -12
- data/lib/minitest/bisect.rb +304 -0
- data/lib/minitest/complete.rb +56 -0
- data/lib/minitest/compress.rb +94 -0
- data/lib/minitest/error_on_warning.rb +11 -0
- data/lib/minitest/expectations.rb +18 -0
- data/lib/minitest/find_minimal_combination.rb +127 -0
- data/lib/minitest/hell.rb +1 -1
- data/lib/minitest/manual_plugins.rb +4 -0
- data/lib/minitest/parallel.rb +10 -8
- data/lib/minitest/path_expander.rb +432 -0
- data/lib/minitest/pride.rb +2 -2
- data/lib/minitest/pride_plugin.rb +17 -24
- data/lib/minitest/server.rb +49 -0
- data/lib/minitest/server_plugin.rb +88 -0
- data/lib/minitest/spec.rb +27 -46
- data/lib/minitest/sprint.rb +105 -0
- data/lib/minitest/sprint_plugin.rb +39 -0
- data/lib/minitest/test.rb +32 -52
- data/lib/minitest/test_task.rb +68 -42
- data/lib/minitest.rb +361 -215
- data/test/minitest/metametameta.rb +33 -19
- data/test/minitest/test_bisect.rb +249 -0
- data/test/minitest/test_find_minimal_combination.rb +138 -0
- data/test/minitest/test_minitest_assertions.rb +311 -173
- data/test/minitest/test_minitest_benchmark.rb +15 -1
- data/test/minitest/test_minitest_reporter.rb +148 -23
- data/test/minitest/test_minitest_spec.rb +157 -132
- data/test/minitest/test_minitest_test.rb +270 -204
- data/test/minitest/test_minitest_test_task.rb +18 -7
- data/test/minitest/test_path_expander.rb +229 -0
- data/test/minitest/test_server.rb +146 -0
- data.tar.gz.sig +2 -2
- metadata +97 -37
- metadata.gz.sig +0 -0
- data/.autotest +0 -34
- data/lib/minitest/mock.rb +0 -323
- data/lib/minitest/unit.rb +0 -42
- data/test/minitest/test_minitest_mock.rb +0 -1139
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
require "prism"
|
|
2
|
+
require "pathname" # for ruby 3
|
|
3
|
+
|
|
4
|
+
module Minitest; end # :nodoc:
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# PathExpander helps pre-process command-line arguments expanding
|
|
8
|
+
# directories into their constituent files. It further helps by
|
|
9
|
+
# providing additional mechanisms to make specifying subsets easier
|
|
10
|
+
# with path subtraction and allowing for command-line arguments to be
|
|
11
|
+
# saved in a file.
|
|
12
|
+
#
|
|
13
|
+
# NOTE: this is NOT an options processor. It is a path processor
|
|
14
|
+
# (basically everything else besides options). It does provide a
|
|
15
|
+
# mechanism for pre-filtering cmdline options, but not with the intent
|
|
16
|
+
# of actually processing them in PathExpander. Use OptionParser to
|
|
17
|
+
# deal with options either before or after passing ARGV through
|
|
18
|
+
# PathExpander.
|
|
19
|
+
|
|
20
|
+
class Minitest::VendoredPathExpander
|
|
21
|
+
# extracted version = "2.0.0"
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# The args array to process.
|
|
25
|
+
|
|
26
|
+
attr_accessor :args
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# The glob used to expand dirs to files.
|
|
30
|
+
|
|
31
|
+
attr_accessor :glob
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# The path to scan if no paths are found in the initial scan.
|
|
35
|
+
|
|
36
|
+
attr_accessor :path
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Create a new path expander that operates on args and expands via
|
|
40
|
+
# glob as necessary. Takes an optional +path+ arg to fall back on if
|
|
41
|
+
# no paths are found on the initial scan (see #process_args).
|
|
42
|
+
|
|
43
|
+
def initialize args, glob, path = "."
|
|
44
|
+
self.args = args
|
|
45
|
+
self.glob = glob
|
|
46
|
+
self.path = path
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Takes an array of paths and returns an array of paths where all
|
|
51
|
+
# directories are expanded to all files found via the glob provided
|
|
52
|
+
# to PathExpander.
|
|
53
|
+
#
|
|
54
|
+
# Paths are normalized to not have a leading "./".
|
|
55
|
+
|
|
56
|
+
def expand_dirs_to_files *dirs
|
|
57
|
+
dirs.flatten.map { |p|
|
|
58
|
+
if File.directory? p then
|
|
59
|
+
Dir[File.join(p, glob)].find_all { |f| File.file? f }
|
|
60
|
+
else
|
|
61
|
+
p
|
|
62
|
+
end
|
|
63
|
+
}.flatten.sort.map { |s| _normalize s }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def _normalize(f) = Pathname.new(f).cleanpath.to_s # :nodoc:
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# Process a file into more arguments. Override this to add
|
|
70
|
+
# additional capabilities.
|
|
71
|
+
|
|
72
|
+
def process_file path
|
|
73
|
+
File.readlines(path).map(&:chomp)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Enumerate over args passed to PathExpander and return a list of
|
|
78
|
+
# files and flags to process. Arguments are processed as:
|
|
79
|
+
#
|
|
80
|
+
# @file_of_args :: Read the file and append to args.
|
|
81
|
+
# -file_path :: Subtract path from file to be processed.
|
|
82
|
+
# -dir_path :: Expand and subtract paths from files to be processed.
|
|
83
|
+
# -not_a_path :: Add to flags to be processed.
|
|
84
|
+
# dir_path :: Expand and add to files to be processed.
|
|
85
|
+
# file_path :: Add to files to be processed.
|
|
86
|
+
# - :: Add "-" (stdin) to files to be processed.
|
|
87
|
+
#
|
|
88
|
+
# See expand_dirs_to_files for details on how expansion occurs.
|
|
89
|
+
#
|
|
90
|
+
# Subtraction happens last, regardless of argument ordering.
|
|
91
|
+
#
|
|
92
|
+
# If no files are found (which is not the same as having an empty
|
|
93
|
+
# file list after subtraction), then fall back to expanding on the
|
|
94
|
+
# default #path given to initialize.
|
|
95
|
+
|
|
96
|
+
def process_args
|
|
97
|
+
pos_files = []
|
|
98
|
+
neg_files = []
|
|
99
|
+
flags = []
|
|
100
|
+
clean = true
|
|
101
|
+
|
|
102
|
+
root_dir = File.expand_path "/" # needed for windows paths
|
|
103
|
+
|
|
104
|
+
args.each do |arg|
|
|
105
|
+
case arg
|
|
106
|
+
when /^@(.*)/ then # push back on, so they can have dirs/-/@ as well
|
|
107
|
+
clean = false
|
|
108
|
+
args.concat process_file $1
|
|
109
|
+
when "-" then
|
|
110
|
+
pos_files << arg
|
|
111
|
+
when /^-(.*)/ then
|
|
112
|
+
if File.exist? $1 then
|
|
113
|
+
clean = false
|
|
114
|
+
neg_files += expand_dirs_to_files($1)
|
|
115
|
+
else
|
|
116
|
+
flags << arg
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
root_path = File.expand_path(arg) == root_dir # eg: -n /./
|
|
120
|
+
if File.exist? arg and not root_path then
|
|
121
|
+
clean = false
|
|
122
|
+
pos_files += expand_dirs_to_files(arg)
|
|
123
|
+
else
|
|
124
|
+
flags << arg
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
files = pos_files - neg_files
|
|
130
|
+
files += expand_dirs_to_files(self.path) if files.empty? && clean
|
|
131
|
+
|
|
132
|
+
[files, flags]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
##
|
|
136
|
+
# Process over flags and treat any special ones here. Returns an
|
|
137
|
+
# array of the flags you haven't processed.
|
|
138
|
+
#
|
|
139
|
+
# This version does nothing. Subclass and override for
|
|
140
|
+
# customization.
|
|
141
|
+
|
|
142
|
+
def process_flags flags
|
|
143
|
+
flags
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
##
|
|
147
|
+
# Top-level method processes args. If no block is given, immediately
|
|
148
|
+
# returns with an Enumerator for further chaining.
|
|
149
|
+
#
|
|
150
|
+
# Otherwise, it calls +pre_process+, +process_args+ and
|
|
151
|
+
# +process_flags+, enumerates over the files, and then calls
|
|
152
|
+
# +post_process+, returning self for any further chaining.
|
|
153
|
+
#
|
|
154
|
+
# Most of the time, you're going to provide a block to process files
|
|
155
|
+
# and do nothing more with the result. Eg:
|
|
156
|
+
#
|
|
157
|
+
# PathExpander.new(ARGV).process do |f|
|
|
158
|
+
# puts "./#{f}"
|
|
159
|
+
# end
|
|
160
|
+
#
|
|
161
|
+
# or:
|
|
162
|
+
#
|
|
163
|
+
# PathExpander.new(ARGV).process # => Enumerator
|
|
164
|
+
|
|
165
|
+
def process(&b)
|
|
166
|
+
return enum_for(:process) unless block_given?
|
|
167
|
+
|
|
168
|
+
pre_process
|
|
169
|
+
|
|
170
|
+
files, flags = process_args
|
|
171
|
+
|
|
172
|
+
args.replace process_flags flags
|
|
173
|
+
|
|
174
|
+
files.uniq.each(&b)
|
|
175
|
+
|
|
176
|
+
post_process
|
|
177
|
+
|
|
178
|
+
self
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
##
|
|
182
|
+
# Hook to run before process
|
|
183
|
+
|
|
184
|
+
def pre_process = nil
|
|
185
|
+
|
|
186
|
+
##
|
|
187
|
+
# Hook to run after process
|
|
188
|
+
|
|
189
|
+
def post_process = nil
|
|
190
|
+
|
|
191
|
+
##
|
|
192
|
+
# A file filter mechanism similar to, but not as extensive as,
|
|
193
|
+
# .gitignore files:
|
|
194
|
+
#
|
|
195
|
+
# + If a pattern does not contain a slash, it is treated as a shell glob.
|
|
196
|
+
# + If a pattern ends in a slash, it matches on directories (and contents).
|
|
197
|
+
# + Otherwise, it matches on relative paths.
|
|
198
|
+
#
|
|
199
|
+
# File.fnmatch is used throughout, so glob patterns work for all 3 types.
|
|
200
|
+
#
|
|
201
|
+
# Takes a list of +files+ and either an io or path of +ignore+ data
|
|
202
|
+
# and returns a list of files left after filtering.
|
|
203
|
+
|
|
204
|
+
def filter_files files, ignore
|
|
205
|
+
ignore_paths = if ignore.respond_to? :read then
|
|
206
|
+
ignore.read
|
|
207
|
+
elsif File.exist? ignore then
|
|
208
|
+
File.read ignore
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
if ignore_paths then
|
|
212
|
+
nonglobs, globs = ignore_paths.split("\n").partition { |p| p.include? "/" }
|
|
213
|
+
dirs, ifiles = nonglobs.partition { |p| p.end_with? "/" }
|
|
214
|
+
dirs = dirs.map { |s| s.chomp "/" }
|
|
215
|
+
|
|
216
|
+
dirs.map! { |i| File.expand_path i }
|
|
217
|
+
globs.map! { |i| File.expand_path i }
|
|
218
|
+
ifiles.map! { |i| File.expand_path i }
|
|
219
|
+
|
|
220
|
+
only_paths = File::FNM_PATHNAME
|
|
221
|
+
files = files.reject { |f|
|
|
222
|
+
f = File.expand_path(f)
|
|
223
|
+
dirs.any? { |i| File.fnmatch?(i, File.dirname(f), only_paths) } ||
|
|
224
|
+
globs.any? { |i| File.fnmatch?(i, f) } ||
|
|
225
|
+
ifiles.any? { |i| File.fnmatch?(i, f, only_paths) }
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
files
|
|
230
|
+
end
|
|
231
|
+
end # VendoredPathExpander
|
|
232
|
+
|
|
233
|
+
##
|
|
234
|
+
# Minitest's PathExpander to find and filter tests.
|
|
235
|
+
|
|
236
|
+
class Minitest::PathExpander < Minitest::VendoredPathExpander
|
|
237
|
+
attr_accessor :by_line # :nodoc:
|
|
238
|
+
|
|
239
|
+
TEST_GLOB = "**/{test_*,*_test,spec_*,*_spec}.rb" # :nodoc:
|
|
240
|
+
|
|
241
|
+
def initialize args = ARGV # :nodoc:
|
|
242
|
+
super args, TEST_GLOB, "test"
|
|
243
|
+
self.by_line = {}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def process_args # :nodoc:
|
|
247
|
+
args.reject! { |arg| # this is a good use of overriding
|
|
248
|
+
case arg
|
|
249
|
+
when /^(.*):([\d,-]+)$/ then
|
|
250
|
+
f, ls = $1, $2
|
|
251
|
+
ls = ls
|
|
252
|
+
.split(/,/)
|
|
253
|
+
.map { |l|
|
|
254
|
+
case l
|
|
255
|
+
when /^\d+$/ then
|
|
256
|
+
l.to_i
|
|
257
|
+
when /^(\d+)-(\d+)$/ then
|
|
258
|
+
$1.to_i..$2.to_i
|
|
259
|
+
else
|
|
260
|
+
raise "unhandled argument format: %p" % [l]
|
|
261
|
+
end
|
|
262
|
+
}
|
|
263
|
+
next unless File.exist? f
|
|
264
|
+
f = _normalize f
|
|
265
|
+
args << f # push path on lest it run whole dir
|
|
266
|
+
by_line[f] = ls # implies rejection
|
|
267
|
+
end
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
super
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
##
|
|
274
|
+
# Overrides PathExpander#process_flags to filter out ruby flags
|
|
275
|
+
# from minitest flags. Only supports -I<paths>, -d, and -w for
|
|
276
|
+
# ruby.
|
|
277
|
+
|
|
278
|
+
def process_flags flags
|
|
279
|
+
flags.reject { |flag| # all hits are truthy, so this works out well
|
|
280
|
+
case flag
|
|
281
|
+
when /^-I(.*)/ then
|
|
282
|
+
$LOAD_PATH.prepend(*$1.split(/:/))
|
|
283
|
+
when /^-d/ then
|
|
284
|
+
$DEBUG = true
|
|
285
|
+
when /^-w/ then
|
|
286
|
+
$VERBOSE = true
|
|
287
|
+
else
|
|
288
|
+
false
|
|
289
|
+
end
|
|
290
|
+
}
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
##
|
|
294
|
+
# Add additional arguments to args to handle path:line argument filtering
|
|
295
|
+
|
|
296
|
+
def post_process
|
|
297
|
+
return if by_line.empty?
|
|
298
|
+
|
|
299
|
+
tests = tests_by_class
|
|
300
|
+
|
|
301
|
+
exit! 1 if handle_missing_tests? tests
|
|
302
|
+
|
|
303
|
+
test_res = tests_to_regexp tests
|
|
304
|
+
self.args << "-n" << "/#{test_res.join "|"}/"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
##
|
|
308
|
+
# Find and return all known tests as a hash of klass => [TM...]
|
|
309
|
+
# pairs.
|
|
310
|
+
|
|
311
|
+
def all_tests
|
|
312
|
+
Minitest.seed = 42 # minor hack to deal with runnable_methods shuffling
|
|
313
|
+
Minitest::Runnable.runnables
|
|
314
|
+
.to_h { |k|
|
|
315
|
+
ms = k.runnable_methods
|
|
316
|
+
.sort
|
|
317
|
+
.map { |m| TM.new k, m.to_sym }
|
|
318
|
+
.sort_by { |t| [t.path, t.line_s] }
|
|
319
|
+
[k, ms]
|
|
320
|
+
}
|
|
321
|
+
.reject { |k, v| v.empty? }
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
##
|
|
325
|
+
# Returns a hash mapping Minitest runnable classes to TMs
|
|
326
|
+
|
|
327
|
+
def tests_by_class
|
|
328
|
+
all_tests
|
|
329
|
+
.transform_values { |ms|
|
|
330
|
+
ms.select { |m|
|
|
331
|
+
bl = by_line[m.path]
|
|
332
|
+
not bl or bl.any? { |l| m.include? l }
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
.reject { |k, v| v.empty? }
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
##
|
|
339
|
+
# Converts +tests+ to an array of "klass#(methods+)" regexps to be
|
|
340
|
+
# used for test selection.
|
|
341
|
+
|
|
342
|
+
def tests_to_regexp tests
|
|
343
|
+
tests # { k1 => [Test(a), ...}
|
|
344
|
+
.transform_values { |tms| tms.map(&:name) } # { k1 => %w[a, b], ...}
|
|
345
|
+
.map { |k, ns| # [ "k1#(?:a|b)", "k2#c", ...]
|
|
346
|
+
if ns.size > 1 then
|
|
347
|
+
ns.map! { |n| Regexp.escape n }
|
|
348
|
+
"%s#\(?:%s\)" % [Regexp.escape(k.name), ns.join("|")]
|
|
349
|
+
else
|
|
350
|
+
"%s#%s" % [Regexp.escape(k.name), ns.first]
|
|
351
|
+
end
|
|
352
|
+
}
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
##
|
|
356
|
+
# Handle the case where a line number doesn't match any known tests.
|
|
357
|
+
# Returns true to signal that running should stop.
|
|
358
|
+
|
|
359
|
+
def handle_missing_tests? tests
|
|
360
|
+
_tests = tests.values.flatten
|
|
361
|
+
not_found = by_line
|
|
362
|
+
.flat_map { |f, ls| ls.map { |l| [f, l] } }
|
|
363
|
+
.reject { |f, l|
|
|
364
|
+
_tests.any? { |t| t.path == f and t.include? l }
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
unless not_found.empty? then
|
|
368
|
+
by_path = all_tests.values.flatten.group_by(&:path)
|
|
369
|
+
|
|
370
|
+
puts
|
|
371
|
+
puts "ERROR: test(s) not found at:"
|
|
372
|
+
not_found.each do |f, l|
|
|
373
|
+
puts " %s:%s" % [f, l]
|
|
374
|
+
puts
|
|
375
|
+
puts "Did you mean?"
|
|
376
|
+
puts
|
|
377
|
+
l = l.begin if l.is_a? Range
|
|
378
|
+
by_path[f] and
|
|
379
|
+
by_path[f]
|
|
380
|
+
.sort_by { |m| (m.line_s - l).abs }
|
|
381
|
+
.first(2)
|
|
382
|
+
.each do |m|
|
|
383
|
+
puts " %-30s (dist=%+d) (%s)" % [m, m.line_s - l, m.name]
|
|
384
|
+
end
|
|
385
|
+
puts
|
|
386
|
+
end
|
|
387
|
+
$stdout.flush
|
|
388
|
+
$stderr.flush
|
|
389
|
+
true
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
##
|
|
394
|
+
# Simple TestMethod (abbr TM) Data object.
|
|
395
|
+
|
|
396
|
+
TM = Data.define :klass, :name, :path, :lines do
|
|
397
|
+
def initialize klass:, name:
|
|
398
|
+
method = klass.instance_method name
|
|
399
|
+
path, line_s = method.source_location
|
|
400
|
+
|
|
401
|
+
path = path.delete_prefix "#{Dir.pwd}/"
|
|
402
|
+
|
|
403
|
+
line_e = line_s + TM.source_for(method).lines.size - 1
|
|
404
|
+
|
|
405
|
+
lines = line_s..line_e
|
|
406
|
+
|
|
407
|
+
super klass:, name:, path:, lines:
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def self.source_for method
|
|
411
|
+
path, line = method.source_location
|
|
412
|
+
file = cache[path] ||= File.readlines(path)
|
|
413
|
+
|
|
414
|
+
ruby = +""
|
|
415
|
+
|
|
416
|
+
file[line-1..].each do |l|
|
|
417
|
+
ruby << l
|
|
418
|
+
return ruby if Prism.parse_success? ruby
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
nil
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def self.cache = @cache ||= {}
|
|
425
|
+
|
|
426
|
+
def include?(o) = o.is_a?(Integer) ? lines.include?(o) : lines.overlap?(o)
|
|
427
|
+
|
|
428
|
+
def to_s = "%s:%d-%d" % [path, lines.begin, lines.end]
|
|
429
|
+
|
|
430
|
+
def line_s = lines.begin
|
|
431
|
+
end
|
|
432
|
+
end
|
data/lib/minitest/pride.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative "../minitest"
|
|
2
2
|
|
|
3
3
|
module Minitest
|
|
4
4
|
def self.plugin_pride_options opts, _options # :nodoc:
|
|
@@ -8,13 +8,13 @@ module Minitest
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def self.plugin_pride_init options # :nodoc:
|
|
11
|
-
|
|
12
|
-
klass = ENV["TERM"] =~ /^xterm|-256color$/ ? PrideLOL : PrideIO
|
|
13
|
-
io = klass.new options[:io]
|
|
11
|
+
return unless PrideIO.pride?
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
klass = ENV["TERM"] =~ /^xterm|-(?:256color|direct)$/ ? PrideLOL : PrideIO
|
|
14
|
+
io = klass.new options[:io]
|
|
15
|
+
|
|
16
|
+
self.reporter.reporters.grep(Minitest::Reporter).each do |rep|
|
|
17
|
+
rep.io = io if rep.io.tty?
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -59,12 +59,10 @@ module Minitest
|
|
|
59
59
|
|
|
60
60
|
def print o
|
|
61
61
|
case o
|
|
62
|
-
when "." then
|
|
62
|
+
when ".", "S" then
|
|
63
63
|
io.print pride o
|
|
64
64
|
when "E", "F" then
|
|
65
65
|
io.print "#{ESC}41m#{ESC}37m#{o}#{NND}"
|
|
66
|
-
when "S" then
|
|
67
|
-
io.print pride o
|
|
68
66
|
else
|
|
69
67
|
io.print o
|
|
70
68
|
end
|
|
@@ -72,11 +70,9 @@ module Minitest
|
|
|
72
70
|
|
|
73
71
|
def puts *o # :nodoc:
|
|
74
72
|
o.map! { |s|
|
|
75
|
-
s.to_s.sub(
|
|
73
|
+
s.to_s.sub("Finished") {
|
|
76
74
|
@index = 0
|
|
77
|
-
"Fabulous run".
|
|
78
|
-
pride(c)
|
|
79
|
-
}.join
|
|
75
|
+
"Fabulous run".chars.map { |c| pride(c) }.join
|
|
80
76
|
}
|
|
81
77
|
}
|
|
82
78
|
|
|
@@ -113,19 +109,16 @@ module Minitest
|
|
|
113
109
|
#
|
|
114
110
|
# plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3)
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
n *= 1.0 / 6
|
|
112
|
+
@colors = Array.new(6 * 7) { |n|
|
|
113
|
+
n *= 1.0 / 3
|
|
119
114
|
r = (3 * Math.sin(n ) + 3).to_i
|
|
120
|
-
g = (3 * Math.sin(n +
|
|
121
|
-
b = (3 * Math.sin(n +
|
|
122
|
-
|
|
123
|
-
# Then we take rgb and encode them in a single number using base 6.
|
|
124
|
-
# For some mysterious reason, we add 16... to clear the bottom 4 bits?
|
|
125
|
-
# Yes... they're ugly.
|
|
115
|
+
g = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
|
|
116
|
+
b = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
|
|
126
117
|
|
|
118
|
+
# Then we take rgb and encode them in a single number using
|
|
119
|
+
# base 6, shifted by 16 for the base 16 ansi colors.
|
|
127
120
|
36 * r + 6 * g + b + 16
|
|
128
|
-
}
|
|
121
|
+
}.rotate(4) # puts "red" first
|
|
129
122
|
|
|
130
123
|
super
|
|
131
124
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "drb"
|
|
2
|
+
require "tmpdir"
|
|
3
|
+
require_relative "../minitest"
|
|
4
|
+
|
|
5
|
+
# :stopdoc:
|
|
6
|
+
|
|
7
|
+
class Minitest::Server
|
|
8
|
+
# extracted version = "1.0.10"
|
|
9
|
+
|
|
10
|
+
TOPDIR = Dir.pwd + "/"
|
|
11
|
+
|
|
12
|
+
def self.path pid = $$
|
|
13
|
+
"drbunix:#{Dir.tmpdir}/minitest.#{pid}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.run client
|
|
17
|
+
DRb.start_service path, new(client)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.stop
|
|
21
|
+
DRb.stop_service
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_accessor :client
|
|
25
|
+
|
|
26
|
+
def initialize client
|
|
27
|
+
self.client = client
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def quit
|
|
31
|
+
self.class.stop
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def start
|
|
35
|
+
client.minitest_start
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def result file, klass, method, fails, assertions, time
|
|
39
|
+
file = file.sub(/^#{TOPDIR}/, "")
|
|
40
|
+
|
|
41
|
+
client.minitest_result file, klass, method, fails, assertions, time
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def report
|
|
45
|
+
# do nothing
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# :startdoc:
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require_relative "../minitest" unless defined? Minitest
|
|
2
|
+
|
|
3
|
+
# :stopdoc:
|
|
4
|
+
|
|
5
|
+
module Minitest
|
|
6
|
+
@server = false
|
|
7
|
+
|
|
8
|
+
def self.plugin_server_options opts, options # :nodoc:
|
|
9
|
+
opts.on "--server=pid", Integer, "Connect to minitest server w/ pid." do |s|
|
|
10
|
+
@server = s
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.plugin_server_init options
|
|
15
|
+
if @server then
|
|
16
|
+
require_relative "server"
|
|
17
|
+
self.reporter << Minitest::ServerReporter.new(@server)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Minitest::ServerReporter < Minitest::AbstractReporter
|
|
23
|
+
def initialize pid
|
|
24
|
+
uri = Minitest::Server.path(pid)
|
|
25
|
+
@mt_server = DRbObject.new_with_uri uri
|
|
26
|
+
super()
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def start
|
|
30
|
+
@mt_server.start
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def record result
|
|
34
|
+
r = result
|
|
35
|
+
c = r.class
|
|
36
|
+
|
|
37
|
+
case r
|
|
38
|
+
when Minitest::Result then
|
|
39
|
+
file, = r.source_location
|
|
40
|
+
cn = r.klass
|
|
41
|
+
else
|
|
42
|
+
# TODO: remove? when is this used?
|
|
43
|
+
file, = r.method(r.name).source_location
|
|
44
|
+
cn = c.name
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
sanitize r.failures
|
|
48
|
+
|
|
49
|
+
@mt_server.result file, cn, r.name, r.failures, r.assertions, r.time
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def sanitize failures
|
|
53
|
+
failures.map! { |e|
|
|
54
|
+
case e
|
|
55
|
+
when Minitest::UnexpectedError then
|
|
56
|
+
# embedded exception might not be able to be marshaled.
|
|
57
|
+
bt = e.error.backtrace
|
|
58
|
+
|
|
59
|
+
ex = RuntimeError.new(e.error.message)
|
|
60
|
+
e.error = ex
|
|
61
|
+
ex.set_backtrace bt
|
|
62
|
+
|
|
63
|
+
e = Minitest::UnexpectedError.new ex # ugh. some rails plugin. ugh.
|
|
64
|
+
|
|
65
|
+
if ex.instance_variables.include? :@bindings then # web-console is Evil
|
|
66
|
+
ex.instance_variable_set :@bindings, nil
|
|
67
|
+
e.instance_variable_set :@bindings, nil
|
|
68
|
+
end
|
|
69
|
+
when Minitest::Skip then
|
|
70
|
+
# do nothing
|
|
71
|
+
when Minitest::Assertion then
|
|
72
|
+
bt = e.backtrace
|
|
73
|
+
e = e.class.new(e.message)
|
|
74
|
+
e.set_backtrace bt
|
|
75
|
+
else
|
|
76
|
+
warn "Unhandled exception type: #{e.class}\n\n#{e.inspect}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
e
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def report
|
|
84
|
+
@mt_server.report
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# :startdoc:
|