minitest-spec-rails-tu-shim 1.9.3
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.
- data/.gitignore +17 -0
- data/COPYING.txt +56 -0
- data/LICENSE.txt +3 -0
- data/README.md +11 -0
- data/Rakefile +1 -0
- data/lib/test/unit.rb +640 -0
- data/lib/test/unit/assertions.rb +324 -0
- data/lib/test/unit/parallel.rb +161 -0
- data/lib/test/unit/testcase.rb +25 -0
- data/minitest-spec-rails-tu-shim.gemspec +15 -0
- metadata +89 -0
data/.gitignore
ADDED
data/COPYING.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the
|
3
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/LICENSE.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# minitest-spec-rails-tu-shim
|
2
|
+
|
3
|
+
Test::Unit compatability for MiniTest For Ruby 1.8 for use with [minitest-spec-rails](https://github.com/metaskills/minitest-spec-rails).
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile if you
|
9
|
+
|
10
|
+
gem 'minitest-spec-rails-tu-shim'
|
11
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/test/unit.rb
ADDED
@@ -0,0 +1,640 @@
|
|
1
|
+
# test/unit compatibility layer using minitest.
|
2
|
+
|
3
|
+
require 'minitest/unit'
|
4
|
+
require 'test/unit/assertions'
|
5
|
+
require 'test/unit/testcase'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
module Test
|
9
|
+
module Unit
|
10
|
+
TEST_UNIT_IMPLEMENTATION = 'test/unit compatibility layer using minitest'
|
11
|
+
|
12
|
+
module RunCount
|
13
|
+
@@run_count = 0
|
14
|
+
|
15
|
+
def self.have_run?
|
16
|
+
@@run_count.nonzero?
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(*)
|
20
|
+
@@run_count += 1
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_once
|
25
|
+
return if have_run?
|
26
|
+
return if $! # don't run if there was an exception
|
27
|
+
yield
|
28
|
+
end
|
29
|
+
module_function :run_once
|
30
|
+
end
|
31
|
+
|
32
|
+
module Options
|
33
|
+
def initialize(*, &block)
|
34
|
+
@init_hook = block
|
35
|
+
@options = nil
|
36
|
+
super(&nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def option_parser
|
40
|
+
@option_parser ||= OptionParser.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_args(args = [])
|
44
|
+
return @options if @options
|
45
|
+
orig_args = args.dup
|
46
|
+
options = {}
|
47
|
+
opts = option_parser
|
48
|
+
setup_options(opts, options)
|
49
|
+
opts.parse!(args)
|
50
|
+
orig_args -= args
|
51
|
+
args = @init_hook.call(args, options) if @init_hook
|
52
|
+
non_options(args, options)
|
53
|
+
@help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
|
54
|
+
@options = options
|
55
|
+
if @options[:parallel]
|
56
|
+
@files = args
|
57
|
+
@args = orig_args
|
58
|
+
end
|
59
|
+
options
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def setup_options(opts, options)
|
64
|
+
opts.separator 'minitest options:'
|
65
|
+
opts.version = MiniTest::Unit::VERSION
|
66
|
+
|
67
|
+
opts.on '-h', '--help', 'Display this help.' do
|
68
|
+
puts opts
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
|
73
|
+
options[:seed] = m
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
|
77
|
+
options[:verbose] = true
|
78
|
+
self.verbose = options[:verbose]
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on '-n', '--name PATTERN', "Filter test names on pattern." do |a|
|
82
|
+
options[:filter] = a
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on '--jobs-status [TYPE]', [:normal, :replace],
|
86
|
+
"Show status of jobs every file; Disabled when --jobs isn't specified." do |type|
|
87
|
+
options[:job_status] = type || :normal
|
88
|
+
end
|
89
|
+
|
90
|
+
opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
|
91
|
+
if /^t/ =~ a
|
92
|
+
options[:testing] = true # For testing
|
93
|
+
options[:parallel] = a[1..-1].to_i
|
94
|
+
else
|
95
|
+
options[:parallel] = a.to_i
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
opts.on '--no-retry', "Don't retry running testcase when --jobs specified" do
|
100
|
+
options[:no_retry] = true
|
101
|
+
end
|
102
|
+
|
103
|
+
opts.on '--ruby VAL', "Path to ruby; It'll have used at -j option" do |a|
|
104
|
+
options[:ruby] = a.split(/ /).reject(&:empty?)
|
105
|
+
end
|
106
|
+
|
107
|
+
opts.on '-q', '--hide-skip', 'Hide skipped tests' do
|
108
|
+
options[:hide_skip] = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def non_options(files, options)
|
113
|
+
begin
|
114
|
+
require "rbconfig"
|
115
|
+
rescue LoadError
|
116
|
+
warn "#{caller(1)[0]}: warning: Parallel running disabled because can't get path to ruby; run specify with --ruby argument"
|
117
|
+
options[:parallel] = nil
|
118
|
+
else
|
119
|
+
options[:ruby] ||= RbConfig.ruby
|
120
|
+
end
|
121
|
+
|
122
|
+
true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module GlobOption
|
127
|
+
include Options
|
128
|
+
|
129
|
+
@@testfile_prefix = "test"
|
130
|
+
|
131
|
+
def setup_options(parser, options)
|
132
|
+
super
|
133
|
+
parser.on '-b', '--basedir=DIR', 'Base directory of test suites.' do |dir|
|
134
|
+
options[:base_directory] = dir
|
135
|
+
end
|
136
|
+
parser.on '-x', '--exclude PATTERN', 'Exclude test files on pattern.' do |pattern|
|
137
|
+
(options[:reject] ||= []) << pattern
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def non_options(files, options)
|
142
|
+
paths = [options.delete(:base_directory), nil].uniq
|
143
|
+
if reject = options.delete(:reject)
|
144
|
+
reject_pat = Regexp.union(reject.map {|r| /#{r}/ })
|
145
|
+
end
|
146
|
+
files.map! {|f|
|
147
|
+
f = f.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
|
148
|
+
((paths if /\A\.\.?(?:\z|\/)/ !~ f) || [nil]).any? do |prefix|
|
149
|
+
if prefix
|
150
|
+
path = f.empty? ? prefix : "#{prefix}/#{f}"
|
151
|
+
else
|
152
|
+
next if f.empty?
|
153
|
+
path = f
|
154
|
+
end
|
155
|
+
if !(match = Dir["#{path}/**/#{@@testfile_prefix}_*.rb"]).empty?
|
156
|
+
if reject
|
157
|
+
match.reject! {|n|
|
158
|
+
n[(prefix.length+1)..-1] if prefix
|
159
|
+
reject_pat =~ n
|
160
|
+
}
|
161
|
+
end
|
162
|
+
break match
|
163
|
+
elsif !reject or reject_pat !~ f and File.exist? path
|
164
|
+
break path
|
165
|
+
end
|
166
|
+
end or
|
167
|
+
raise ArgumentError, "file not found: #{f}"
|
168
|
+
}
|
169
|
+
files.flatten!
|
170
|
+
super(files, options)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
module LoadPathOption
|
175
|
+
include Options
|
176
|
+
|
177
|
+
def setup_options(parser, options)
|
178
|
+
super
|
179
|
+
parser.on '-Idirectory', 'Add library load path' do |dirs|
|
180
|
+
dirs.split(':').each { |d| $LOAD_PATH.unshift d }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
module GCStressOption
|
186
|
+
def setup_options(parser, options)
|
187
|
+
super
|
188
|
+
parser.on '--[no-]gc-stress', 'Set GC.stress as true' do |flag|
|
189
|
+
options[:gc_stress] = flag
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def non_options(files, options)
|
194
|
+
if options.delete(:gc_stress)
|
195
|
+
MiniTest::Unit::TestCase.class_eval do
|
196
|
+
oldrun = instance_method(:run)
|
197
|
+
define_method(:run) do |runner|
|
198
|
+
begin
|
199
|
+
gc_stress, GC.stress = GC.stress, true
|
200
|
+
oldrun.bind(self).call(runner)
|
201
|
+
ensure
|
202
|
+
GC.stress = gc_stress
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
super
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
module RequireFiles
|
212
|
+
def non_options(files, options)
|
213
|
+
return false if !super
|
214
|
+
result = false
|
215
|
+
files.each {|f|
|
216
|
+
d = File.dirname(path = File.expand_path(f))
|
217
|
+
unless $:.include? d
|
218
|
+
$: << d
|
219
|
+
end
|
220
|
+
begin
|
221
|
+
require path unless options[:parallel]
|
222
|
+
result = true
|
223
|
+
rescue LoadError
|
224
|
+
puts "#{f}: #{$!}"
|
225
|
+
end
|
226
|
+
}
|
227
|
+
result
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class Runner < MiniTest::Unit
|
232
|
+
include Test::Unit::Options
|
233
|
+
include Test::Unit::GlobOption
|
234
|
+
include Test::Unit::LoadPathOption
|
235
|
+
include Test::Unit::GCStressOption
|
236
|
+
include Test::Unit::RunCount
|
237
|
+
|
238
|
+
class Worker
|
239
|
+
def self.launch(ruby,args=[])
|
240
|
+
io = IO.popen([*ruby,
|
241
|
+
"#{File.dirname(__FILE__)}/unit/parallel.rb",
|
242
|
+
*args], "rb+")
|
243
|
+
new(io, io.pid, :waiting)
|
244
|
+
end
|
245
|
+
|
246
|
+
def initialize(io, pid, status)
|
247
|
+
@io = io
|
248
|
+
@pid = pid
|
249
|
+
@status = status
|
250
|
+
@file = nil
|
251
|
+
@real_file = nil
|
252
|
+
@loadpath = []
|
253
|
+
@hooks = {}
|
254
|
+
end
|
255
|
+
|
256
|
+
def puts(*args)
|
257
|
+
@io.puts(*args)
|
258
|
+
end
|
259
|
+
|
260
|
+
def run(task,type)
|
261
|
+
@file = File.basename(task).gsub(/\.rb/,"")
|
262
|
+
@real_file = task
|
263
|
+
begin
|
264
|
+
puts "loadpath #{[Marshal.dump($:-@loadpath)].pack("m").gsub("\n","")}"
|
265
|
+
@loadpath = $:.dup
|
266
|
+
puts "run #{task} #{type}"
|
267
|
+
@status = :prepare
|
268
|
+
rescue Errno::EPIPE
|
269
|
+
died
|
270
|
+
rescue IOError
|
271
|
+
raise unless ["stream closed","closed stream"].include? $!.message
|
272
|
+
died
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def hook(id,&block)
|
277
|
+
@hooks[id] ||= []
|
278
|
+
@hooks[id] << block
|
279
|
+
self
|
280
|
+
end
|
281
|
+
|
282
|
+
def read
|
283
|
+
res = (@status == :quit) ? @io.read : @io.gets
|
284
|
+
res && res.chomp
|
285
|
+
end
|
286
|
+
|
287
|
+
def close
|
288
|
+
@io.close
|
289
|
+
self
|
290
|
+
end
|
291
|
+
|
292
|
+
def died(*additional)
|
293
|
+
@status = :quit
|
294
|
+
@io.close
|
295
|
+
|
296
|
+
call_hook(:dead,*additional)
|
297
|
+
end
|
298
|
+
|
299
|
+
def to_s
|
300
|
+
if @file
|
301
|
+
"#{@pid}=#{@file}"
|
302
|
+
else
|
303
|
+
"#{@pid}:#{@status.to_s.ljust(7)}"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
attr_reader :io, :pid
|
308
|
+
attr_accessor :status, :file, :real_file, :loadpath
|
309
|
+
|
310
|
+
private
|
311
|
+
|
312
|
+
def call_hook(id,*additional)
|
313
|
+
@hooks[id] ||= []
|
314
|
+
@hooks[id].each{|hook| hook[self,additional] }
|
315
|
+
self
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
class << self; undef autorun; end
|
321
|
+
|
322
|
+
@@stop_auto_run = false
|
323
|
+
def self.autorun
|
324
|
+
at_exit {
|
325
|
+
Test::Unit::RunCount.run_once {
|
326
|
+
exit(Test::Unit::Runner.new.run(ARGV) || true)
|
327
|
+
} unless @@stop_auto_run
|
328
|
+
} unless @@installed_at_exit
|
329
|
+
@@installed_at_exit = true
|
330
|
+
end
|
331
|
+
|
332
|
+
def after_worker_down(worker, e=nil, c=false)
|
333
|
+
return unless @options[:parallel]
|
334
|
+
return if @interrupt
|
335
|
+
if e
|
336
|
+
b = e.backtrace
|
337
|
+
warn "#{b.shift}: #{e.message} (#{e.class})"
|
338
|
+
STDERR.print b.map{|s| "\tfrom #{s}"}.join("\n")
|
339
|
+
end
|
340
|
+
@need_quit = true
|
341
|
+
warn ""
|
342
|
+
warn "Some worker was crashed. It seems ruby interpreter's bug"
|
343
|
+
warn "or, a bug of test/unit/parallel.rb. try again without -j"
|
344
|
+
warn "option."
|
345
|
+
warn ""
|
346
|
+
STDERR.flush
|
347
|
+
exit c
|
348
|
+
end
|
349
|
+
|
350
|
+
def jobs_status
|
351
|
+
return unless @options[:job_status]
|
352
|
+
puts "" unless @options[:verbose]
|
353
|
+
status_line = @workers.map(&:to_s).join(" ")
|
354
|
+
if @options[:job_status] == :replace and $stdout.tty?
|
355
|
+
@terminal_width ||=
|
356
|
+
begin
|
357
|
+
require 'io/console'
|
358
|
+
$stdout.winsize[1]
|
359
|
+
rescue LoadError, NoMethodError
|
360
|
+
ENV["COLUMNS"].to_i.nonzero? || 80
|
361
|
+
end
|
362
|
+
@jstr_size ||= 0
|
363
|
+
del_jobs_status
|
364
|
+
$stdout.flush
|
365
|
+
print status_line[0...@terminal_width]
|
366
|
+
$stdout.flush
|
367
|
+
@jstr_size = [status_line.size, @terminal_width].min
|
368
|
+
else
|
369
|
+
puts status_line
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def del_jobs_status
|
374
|
+
return unless @options[:job_status] == :replace && @jstr_size.nonzero?
|
375
|
+
print "\r"+" "*@jstr_size+"\r"
|
376
|
+
end
|
377
|
+
|
378
|
+
def after_worker_quit(worker)
|
379
|
+
return unless @options[:parallel]
|
380
|
+
return if @interrupt
|
381
|
+
@workers.delete(worker)
|
382
|
+
@dead_workers << worker
|
383
|
+
@ios = @workers.map(&:io)
|
384
|
+
end
|
385
|
+
|
386
|
+
def _run_parallel suites, type, result
|
387
|
+
if @options[:parallel] < 1
|
388
|
+
warn "Error: parameter of -j option should be greater than 0."
|
389
|
+
return
|
390
|
+
end
|
391
|
+
|
392
|
+
begin
|
393
|
+
# Require needed things for parallel running
|
394
|
+
require 'thread'
|
395
|
+
require 'timeout'
|
396
|
+
@tasks = @files.dup # Array of filenames.
|
397
|
+
@need_quit = false
|
398
|
+
@dead_workers = [] # Array of dead workers.
|
399
|
+
@warnings = []
|
400
|
+
shutting_down = false
|
401
|
+
rep = [] # FIXME: more good naming
|
402
|
+
|
403
|
+
# Array of workers.
|
404
|
+
@workers = @options[:parallel].times.map {
|
405
|
+
worker = Worker.launch(@options[:ruby],@args)
|
406
|
+
worker.hook(:dead) do |w,info|
|
407
|
+
after_worker_quit w
|
408
|
+
after_worker_down w, *info unless info.empty?
|
409
|
+
end
|
410
|
+
worker
|
411
|
+
}
|
412
|
+
|
413
|
+
# Thread: watchdog
|
414
|
+
watchdog = Thread.new do
|
415
|
+
while stat = Process.wait2
|
416
|
+
break if @interrupt # Break when interrupt
|
417
|
+
pid, stat = stat
|
418
|
+
w = (@workers + @dead_workers).find{|x| pid == x.pid }.dup
|
419
|
+
next unless w
|
420
|
+
unless w.status == :quit
|
421
|
+
# Worker down
|
422
|
+
w.died(nil, !stat.signaled? && stat.exitstatus)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
@workers_hash = Hash[@workers.map {|w| [w.io,w] }] # out-IO => worker
|
428
|
+
@ios = @workers.map{|w| w.io } # Array of worker IOs
|
429
|
+
|
430
|
+
while _io = IO.select(@ios)[0]
|
431
|
+
break unless _io.each do |io|
|
432
|
+
break if @need_quit
|
433
|
+
worker = @workers_hash[io]
|
434
|
+
case worker.read
|
435
|
+
when /^okay$/
|
436
|
+
worker.status = :running
|
437
|
+
jobs_status
|
438
|
+
when /^ready$/
|
439
|
+
worker.status = :ready
|
440
|
+
if @tasks.empty?
|
441
|
+
break unless @workers.find{|x| x.status == :running }
|
442
|
+
else
|
443
|
+
worker.run(@tasks.shift, type)
|
444
|
+
end
|
445
|
+
|
446
|
+
jobs_status
|
447
|
+
when /^done (.+?)$/
|
448
|
+
r = Marshal.load($1.unpack("m")[0])
|
449
|
+
result << r[0..1] unless r[0..1] == [nil,nil]
|
450
|
+
rep << {file: worker.real_file,
|
451
|
+
report: r[2], result: r[3], testcase: r[5]}
|
452
|
+
$:.push(*r[4]).uniq!
|
453
|
+
when /^p (.+?)$/
|
454
|
+
del_jobs_status
|
455
|
+
print $1.unpack("m")[0]
|
456
|
+
jobs_status if @options[:job_status] == :replace
|
457
|
+
when /^after (.+?)$/
|
458
|
+
@warnings << Marshal.load($1.unpack("m")[0])
|
459
|
+
when /^bye (.+?)$/
|
460
|
+
after_worker_down worker, Marshal.load($1.unpack("m")[0])
|
461
|
+
when /^bye$/
|
462
|
+
if shutting_down
|
463
|
+
after_worker_quit worker
|
464
|
+
else
|
465
|
+
after_worker_down worker
|
466
|
+
end
|
467
|
+
end
|
468
|
+
break if @need_quit
|
469
|
+
end
|
470
|
+
end
|
471
|
+
rescue Interrupt => e
|
472
|
+
@interrupt = e
|
473
|
+
return result
|
474
|
+
ensure
|
475
|
+
shutting_down = true
|
476
|
+
|
477
|
+
watchdog.kill if watchdog
|
478
|
+
if @interrupt
|
479
|
+
@ios.select!{|x| @workers_hash[x].status == :running }
|
480
|
+
while !@ios.empty? && (__io = IO.select(@ios,[],[],10))
|
481
|
+
_io = __io[0]
|
482
|
+
_io.each do |io|
|
483
|
+
worker = @workers_hash[io]
|
484
|
+
case worker.read
|
485
|
+
when /^done (.+?)$/
|
486
|
+
r = Marshal.load($1.unpack("m")[0])
|
487
|
+
result << r[0..1] unless r[0..1] == [nil,nil]
|
488
|
+
rep << {file: worker.real_file,
|
489
|
+
report: r[2], result: r[3], testcase: r[5]}
|
490
|
+
$:.push(*r[4]).uniq!
|
491
|
+
@ios.delete(io)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
@workers.each do |worker|
|
497
|
+
begin
|
498
|
+
timeout(1) do
|
499
|
+
worker.puts "quit"
|
500
|
+
end
|
501
|
+
rescue Errno::EPIPE
|
502
|
+
rescue Timeout::Error
|
503
|
+
end
|
504
|
+
worker.close
|
505
|
+
end
|
506
|
+
begin
|
507
|
+
timeout(0.2*@workers.size) do
|
508
|
+
Process.waitall
|
509
|
+
end
|
510
|
+
rescue Timeout::Error
|
511
|
+
@workers.each do |worker|
|
512
|
+
begin
|
513
|
+
Process.kill(:KILL,worker.pid)
|
514
|
+
rescue Errno::ESRCH; end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
if @interrupt || @options[:no_retry] || @need_quit
|
519
|
+
rep.each do |r|
|
520
|
+
report.push(*r[:report])
|
521
|
+
end
|
522
|
+
@errors += rep.map{|x| x[:result][0] }.inject(:+)
|
523
|
+
@failures += rep.map{|x| x[:result][1] }.inject(:+)
|
524
|
+
@skips += rep.map{|x| x[:result][2] }.inject(:+)
|
525
|
+
else
|
526
|
+
puts ""
|
527
|
+
puts "Retrying..."
|
528
|
+
puts ""
|
529
|
+
rep.each do |r|
|
530
|
+
if r[:testcase] && r[:file] && !r[:report].empty?
|
531
|
+
require r[:file]
|
532
|
+
_run_suite(eval(r[:testcase]),type)
|
533
|
+
else
|
534
|
+
report.push(*r[:report])
|
535
|
+
@errors += r[:result][0]
|
536
|
+
@failures += r[:result][1]
|
537
|
+
@skips += r[:result][2]
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
if @warnings
|
542
|
+
warn ""
|
543
|
+
ary = []
|
544
|
+
@warnings.reject! do |w|
|
545
|
+
r = ary.include?(w[1].message)
|
546
|
+
ary << w[1].message
|
547
|
+
r
|
548
|
+
end
|
549
|
+
@warnings.each do |w|
|
550
|
+
warn "#{w[0]}: #{w[1].message} (#{w[1].class})"
|
551
|
+
end
|
552
|
+
warn ""
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
def _run_suites suites, type
|
558
|
+
@interrupt = nil
|
559
|
+
result = []
|
560
|
+
if @options[:parallel]
|
561
|
+
_run_parallel suites, type, result
|
562
|
+
else
|
563
|
+
suites.each {|suite|
|
564
|
+
begin
|
565
|
+
result << _run_suite(suite, type)
|
566
|
+
rescue Interrupt => e
|
567
|
+
@interrupt = e
|
568
|
+
break
|
569
|
+
end
|
570
|
+
}
|
571
|
+
end
|
572
|
+
report.reject!{|r| r.start_with? "Skipped:" } if @options[:hide_skip]
|
573
|
+
result
|
574
|
+
end
|
575
|
+
|
576
|
+
# Overriding of MiniTest::Unit#puke
|
577
|
+
def puke klass, meth, e
|
578
|
+
# TODO:
|
579
|
+
# this overriding is for minitest feature that skip messages are
|
580
|
+
# hidden when not verbose (-v), note this is temporally.
|
581
|
+
e = case e
|
582
|
+
when MiniTest::Skip then
|
583
|
+
@skips += 1
|
584
|
+
"Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
|
585
|
+
when MiniTest::Assertion then
|
586
|
+
@failures += 1
|
587
|
+
"Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
|
588
|
+
else
|
589
|
+
@errors += 1
|
590
|
+
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
|
591
|
+
"Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
|
592
|
+
end
|
593
|
+
@report << e
|
594
|
+
e[0, 1]
|
595
|
+
end
|
596
|
+
|
597
|
+
def status(*args)
|
598
|
+
result = super
|
599
|
+
raise @interrupt if @interrupt
|
600
|
+
result
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
class AutoRunner
|
605
|
+
class Runner < Test::Unit::Runner
|
606
|
+
include Test::Unit::RequireFiles
|
607
|
+
end
|
608
|
+
|
609
|
+
attr_accessor :to_run, :options
|
610
|
+
|
611
|
+
def initialize(force_standalone = false, default_dir = nil, argv = ARGV)
|
612
|
+
@runner = Runner.new do |files, options|
|
613
|
+
options[:base_directory] ||= default_dir
|
614
|
+
files << default_dir if files.empty? and default_dir
|
615
|
+
@to_run = files
|
616
|
+
yield self if block_given?
|
617
|
+
files
|
618
|
+
end
|
619
|
+
Runner.runner = @runner
|
620
|
+
@options = @runner.option_parser
|
621
|
+
@argv = argv
|
622
|
+
end
|
623
|
+
|
624
|
+
def process_args(*args)
|
625
|
+
@runner.process_args(*args)
|
626
|
+
!@to_run.empty?
|
627
|
+
end
|
628
|
+
|
629
|
+
def run
|
630
|
+
@runner.run(@argv) || true
|
631
|
+
end
|
632
|
+
|
633
|
+
def self.run(*args)
|
634
|
+
new(*args).run
|
635
|
+
end
|
636
|
+
end
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
Test::Unit::Runner.autorun
|
@@ -0,0 +1,324 @@
|
|
1
|
+
require 'minitest/unit'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
module Test
|
5
|
+
module Unit
|
6
|
+
module Assertions
|
7
|
+
include MiniTest::Assertions
|
8
|
+
|
9
|
+
def mu_pp(obj) #:nodoc:
|
10
|
+
obj.pretty_inspect.chomp
|
11
|
+
end
|
12
|
+
|
13
|
+
MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc:
|
14
|
+
|
15
|
+
UNASSIGNED = Object.new # :nodoc:
|
16
|
+
|
17
|
+
# :call-seq:
|
18
|
+
# assert( test, failure_message = UNASSIGNED )
|
19
|
+
#
|
20
|
+
#Tests if +test+ is true.
|
21
|
+
#
|
22
|
+
#+msg+ may be a String or a Proc. If +msg+ is a String, it will be used
|
23
|
+
#as the failure message. Otherwise, the result of calling +msg+ will be
|
24
|
+
#used as the message if the assertion fails.
|
25
|
+
#
|
26
|
+
#If no +msg+ is given, a default message will be used.
|
27
|
+
#
|
28
|
+
# assert(false, "This was expected to be true")
|
29
|
+
def assert(test, msg = UNASSIGNED)
|
30
|
+
case msg
|
31
|
+
when UNASSIGNED
|
32
|
+
msg = nil
|
33
|
+
when String, Proc
|
34
|
+
else
|
35
|
+
bt = caller.reject { |s| s.rindex(MINI_DIR, 0) }
|
36
|
+
raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt
|
37
|
+
end
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
# :call-seq:
|
42
|
+
# assert_block( failure_message = nil )
|
43
|
+
#
|
44
|
+
#Tests the result of the given block. If the block does not return true,
|
45
|
+
#the assertion will fail. The optional +failure_message+ argument is the same as in
|
46
|
+
#Assertions#assert.
|
47
|
+
#
|
48
|
+
# assert_block do
|
49
|
+
# [1, 2, 3].any? { |num| num < 1 }
|
50
|
+
# end
|
51
|
+
def assert_block(*msgs)
|
52
|
+
assert yield, *msgs
|
53
|
+
end
|
54
|
+
|
55
|
+
# :call-seq:
|
56
|
+
# assert_raise( *args, &block )
|
57
|
+
#
|
58
|
+
#Tests if the given block raises an exception. Acceptable exception
|
59
|
+
#types maye be given as optional arguments. If the last argument is a
|
60
|
+
#String, it will be used as the error message.
|
61
|
+
#
|
62
|
+
# assert_raise do #Fails, no Exceptions are raised
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# assert_raise NameError do
|
66
|
+
# puts x #Raises NameError, so assertion succeeds
|
67
|
+
# end
|
68
|
+
def assert_raise(*args, &b)
|
69
|
+
assert_raises(*args, &b)
|
70
|
+
end
|
71
|
+
|
72
|
+
# :call-seq:
|
73
|
+
# assert_nothing_raised( *args, &block )
|
74
|
+
#
|
75
|
+
#If any exceptions are given as arguments, the assertion will
|
76
|
+
#fail if one of those exceptions are raised. Otherwise, the test fails
|
77
|
+
#if any exceptions are raised.
|
78
|
+
#
|
79
|
+
#The final argument may be a failure message.
|
80
|
+
#
|
81
|
+
# assert_nothing_raised RuntimeError do
|
82
|
+
# raise Exception #Assertion passes, Exception is not a RuntimeError
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# assert_nothing_raised do
|
86
|
+
# raise Exception #Assertion fails
|
87
|
+
# end
|
88
|
+
def assert_nothing_raised(*args)
|
89
|
+
self._assertions += 1
|
90
|
+
if Module === args.last
|
91
|
+
msg = nil
|
92
|
+
else
|
93
|
+
msg = args.pop
|
94
|
+
end
|
95
|
+
begin
|
96
|
+
line = __LINE__; yield
|
97
|
+
rescue MiniTest::Skip
|
98
|
+
raise
|
99
|
+
rescue Exception => e
|
100
|
+
bt = e.backtrace
|
101
|
+
as = e.instance_of?(MiniTest::Assertion)
|
102
|
+
if as
|
103
|
+
ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o
|
104
|
+
bt.reject! {|ln| ans =~ ln}
|
105
|
+
end
|
106
|
+
if ((args.empty? && !as) ||
|
107
|
+
args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a })
|
108
|
+
msg = message(msg) { "Exception raised:\n<#{mu_pp(e)}>" }
|
109
|
+
raise MiniTest::Assertion, msg.call, bt
|
110
|
+
else
|
111
|
+
raise
|
112
|
+
end
|
113
|
+
end
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
# :call-seq:
|
118
|
+
# assert_nothing_thrown( failure_message = nil, &block )
|
119
|
+
#
|
120
|
+
#Fails if the given block uses a call to Kernel#throw.
|
121
|
+
#
|
122
|
+
#An optional failure message may be provided as the final argument.
|
123
|
+
#
|
124
|
+
# assert_nothing_thrown "Something was thrown!" do
|
125
|
+
# throw :problem?
|
126
|
+
# end
|
127
|
+
def assert_nothing_thrown(msg=nil)
|
128
|
+
begin
|
129
|
+
yield
|
130
|
+
rescue ArgumentError => error
|
131
|
+
raise error if /\Auncaught throw (.+)\z/m !~ error.message
|
132
|
+
msg = message(msg) { "<#{$1}> was thrown when nothing was expected" }
|
133
|
+
flunk(msg)
|
134
|
+
end
|
135
|
+
assert(true, "Expected nothing to be thrown")
|
136
|
+
end
|
137
|
+
|
138
|
+
# :call-seq:
|
139
|
+
# assert_equal( expected, actual, failure_message = nil )
|
140
|
+
#
|
141
|
+
#Tests if +expected+ is equal to +actual+.
|
142
|
+
#
|
143
|
+
#An optional failure message may be provided as the final argument.
|
144
|
+
def assert_equal(exp, act, msg = nil)
|
145
|
+
msg = message(msg) {
|
146
|
+
exp_str = mu_pp(exp)
|
147
|
+
act_str = mu_pp(act)
|
148
|
+
exp_comment = ''
|
149
|
+
act_comment = ''
|
150
|
+
if exp_str == act_str
|
151
|
+
if (exp.is_a?(String) && act.is_a?(String)) ||
|
152
|
+
(exp.is_a?(Regexp) && act.is_a?(Regexp))
|
153
|
+
exp_comment = " (#{exp.encoding})"
|
154
|
+
act_comment = " (#{act.encoding})"
|
155
|
+
elsif exp.is_a?(Float) && act.is_a?(Float)
|
156
|
+
exp_str = "%\#.#{Float::DIG+2}g" % exp
|
157
|
+
act_str = "%\#.#{Float::DIG+2}g" % act
|
158
|
+
elsif exp.is_a?(Time) && act.is_a?(Time)
|
159
|
+
if exp.subsec * 1000_000_000 == exp.nsec
|
160
|
+
exp_comment = " (#{exp.nsec}[ns])"
|
161
|
+
else
|
162
|
+
exp_comment = " (subsec=#{exp.subsec})"
|
163
|
+
end
|
164
|
+
if act.subsec * 1000_000_000 == act.nsec
|
165
|
+
act_comment = " (#{act.nsec}[ns])"
|
166
|
+
else
|
167
|
+
act_comment = " (subsec=#{act.subsec})"
|
168
|
+
end
|
169
|
+
elsif exp.class != act.class
|
170
|
+
# a subclass of Range, for example.
|
171
|
+
exp_comment = " (#{exp.class})"
|
172
|
+
act_comment = " (#{act.class})"
|
173
|
+
end
|
174
|
+
elsif !Encoding.compatible?(exp_str, act_str)
|
175
|
+
if exp.is_a?(String) && act.is_a?(String)
|
176
|
+
exp_str = exp.dump
|
177
|
+
act_str = act.dump
|
178
|
+
exp_comment = " (#{exp.encoding})"
|
179
|
+
act_comment = " (#{act.encoding})"
|
180
|
+
else
|
181
|
+
exp_str = exp_str.dump
|
182
|
+
act_str = act_str.dump
|
183
|
+
end
|
184
|
+
end
|
185
|
+
"<#{exp_str}>#{exp_comment} expected but was\n<#{act_str}>#{act_comment}"
|
186
|
+
}
|
187
|
+
assert(exp == act, msg)
|
188
|
+
end
|
189
|
+
|
190
|
+
# :call-seq:
|
191
|
+
# assert_not_nil( expression, failure_message = nil )
|
192
|
+
#
|
193
|
+
#Tests if +expression+ is not nil.
|
194
|
+
#
|
195
|
+
#An optional failure message may be provided as the final argument.
|
196
|
+
def assert_not_nil(exp, msg=nil)
|
197
|
+
msg = message(msg) { "<#{mu_pp(exp)}> expected to not be nil" }
|
198
|
+
assert(!exp.nil?, msg)
|
199
|
+
end
|
200
|
+
|
201
|
+
# :call-seq:
|
202
|
+
# assert_not_equal( expected, actual, failure_message = nil )
|
203
|
+
#
|
204
|
+
#Tests if +expected+ is not equal to +actual+.
|
205
|
+
#
|
206
|
+
#An optional failure message may be provided as the final argument.
|
207
|
+
def assert_not_equal(exp, act, msg=nil)
|
208
|
+
msg = message(msg) { "<#{mu_pp(exp)}> expected to be != to\n<#{mu_pp(act)}>" }
|
209
|
+
assert(exp != act, msg)
|
210
|
+
end
|
211
|
+
|
212
|
+
# :call-seq:
|
213
|
+
# assert_no_match( regexp, string, failure_message = nil )
|
214
|
+
#
|
215
|
+
#Tests if the given Regexp does not match a given String.
|
216
|
+
#
|
217
|
+
#An optional failure message may be provided as the final argument.
|
218
|
+
def assert_no_match(regexp, string, msg=nil)
|
219
|
+
assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
|
220
|
+
self._assertions -= 1
|
221
|
+
msg = message(msg) { "<#{mu_pp(regexp)}> expected to not match\n<#{mu_pp(string)}>" }
|
222
|
+
assert(regexp !~ string, msg)
|
223
|
+
end
|
224
|
+
|
225
|
+
# :call-seq:
|
226
|
+
# assert_not_same( expected, actual, failure_message = nil )
|
227
|
+
#
|
228
|
+
#Tests if +expected+ is not the same object as +actual+.
|
229
|
+
#This test uses Object#equal? to test equality.
|
230
|
+
#
|
231
|
+
#An optional failure message may be provided as the final argument.
|
232
|
+
#
|
233
|
+
# assert_not_same("x", "x") #Succeeds
|
234
|
+
def assert_not_same(expected, actual, message="")
|
235
|
+
msg = message(msg) { build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__) }
|
236
|
+
<?>
|
237
|
+
with id <?> expected to not be equal\\? to
|
238
|
+
<?>
|
239
|
+
with id <?>.
|
240
|
+
EOT
|
241
|
+
assert(!actual.equal?(expected), msg)
|
242
|
+
end
|
243
|
+
|
244
|
+
# :call-seq:
|
245
|
+
# assert_respond_to( object, method, failure_message = nil )
|
246
|
+
#
|
247
|
+
#Tests if the given Object responds to +method+.
|
248
|
+
#
|
249
|
+
#An optional failure message may be provided as the final argument.
|
250
|
+
#
|
251
|
+
# assert_respond_to("hello", :reverse) #Succeeds
|
252
|
+
# assert_respond_to("hello", :does_not_exist) #Fails
|
253
|
+
def assert_respond_to obj, meth, msg = nil
|
254
|
+
#get rid of overcounting
|
255
|
+
super if !caller[0].rindex(MINI_DIR, 0) || !obj.respond_to?(meth)
|
256
|
+
end
|
257
|
+
|
258
|
+
# :call-seq:
|
259
|
+
# assert_send( +send_array+, failure_message = nil )
|
260
|
+
#
|
261
|
+
# Passes if the method send returns a true value.
|
262
|
+
#
|
263
|
+
# +send_array+ is composed of:
|
264
|
+
# * A receiver
|
265
|
+
# * A method
|
266
|
+
# * Arguments to the method
|
267
|
+
#
|
268
|
+
# Example:
|
269
|
+
# assert_send([[1, 2], :member?, 1]) # -> pass
|
270
|
+
# assert_send([[1, 2], :member?, 4]) # -> fail
|
271
|
+
def assert_send send_ary, m = nil
|
272
|
+
recv, msg, *args = send_ary
|
273
|
+
m = message(m) {
|
274
|
+
if args.empty?
|
275
|
+
argsstr = ""
|
276
|
+
else
|
277
|
+
(argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
|
278
|
+
end
|
279
|
+
"Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return true"
|
280
|
+
}
|
281
|
+
assert recv.__send__(msg, *args), m
|
282
|
+
end
|
283
|
+
|
284
|
+
# :call-seq:
|
285
|
+
# assert_not_send( +send_array+, failure_message = nil )
|
286
|
+
#
|
287
|
+
# Passes if the method send doesn't return a true value.
|
288
|
+
#
|
289
|
+
# +send_array+ is composed of:
|
290
|
+
# * A receiver
|
291
|
+
# * A method
|
292
|
+
# * Arguments to the method
|
293
|
+
#
|
294
|
+
# Example:
|
295
|
+
# assert_not_send([[1, 2], :member?, 1]) # -> fail
|
296
|
+
# assert_not_send([[1, 2], :member?, 4]) # -> pass
|
297
|
+
def assert_not_send send_ary, m = nil
|
298
|
+
recv, msg, *args = send_ary
|
299
|
+
m = message(m) {
|
300
|
+
if args.empty?
|
301
|
+
argsstr = ""
|
302
|
+
else
|
303
|
+
(argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
|
304
|
+
end
|
305
|
+
"Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return false"
|
306
|
+
}
|
307
|
+
assert !recv.__send__(msg, *args), m
|
308
|
+
end
|
309
|
+
|
310
|
+
ms = instance_methods(true).map {|sym| sym.to_s }
|
311
|
+
ms.grep(/\Arefute_/) do |m|
|
312
|
+
mname = ('assert_not_' << m.to_s[/.*?_(.*)/, 1])
|
313
|
+
alias_method(mname, m) unless ms.include? mname
|
314
|
+
end
|
315
|
+
alias assert_include assert_includes
|
316
|
+
alias assert_not_include assert_not_includes
|
317
|
+
|
318
|
+
def build_message(head, template=nil, *arguments) #:nodoc:
|
319
|
+
template &&= template.chomp
|
320
|
+
template.gsub(/\G((?:[^\\]|\\.)*?)(\\)?\?/) { $1 + ($2 ? "?" : mu_pp(arguments.shift)) }
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
module Test
|
4
|
+
module Unit
|
5
|
+
class Worker < Runner
|
6
|
+
class << self
|
7
|
+
undef autorun
|
8
|
+
end
|
9
|
+
|
10
|
+
alias orig_run_suite _run_suite
|
11
|
+
undef _run_suite
|
12
|
+
undef _run_suites
|
13
|
+
undef run
|
14
|
+
|
15
|
+
def increment_io(orig)
|
16
|
+
*rest, io = 32.times.inject([orig.dup]){|ios, | ios << ios.last.dup }
|
17
|
+
rest.each(&:close)
|
18
|
+
io
|
19
|
+
end
|
20
|
+
|
21
|
+
def _run_suites(suites, type)
|
22
|
+
suites.map do |suite|
|
23
|
+
_run_suite(suite, type)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def _run_suite(suite, type)
|
28
|
+
r = report.dup
|
29
|
+
orig_testout = MiniTest::Unit.output
|
30
|
+
i,o = IO.pipe
|
31
|
+
|
32
|
+
MiniTest::Unit.output = o
|
33
|
+
orig_stdin, orig_stdout = $stdin, $stdout
|
34
|
+
|
35
|
+
th = Thread.new do
|
36
|
+
begin
|
37
|
+
while buf = (self.verbose ? i.gets : i.read(5))
|
38
|
+
@stdout.puts "p #{[buf].pack("m").gsub("\n","")}"
|
39
|
+
end
|
40
|
+
rescue IOError
|
41
|
+
rescue Errno::EPIPE
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
e, f, s = @errors, @failures, @skips
|
46
|
+
|
47
|
+
begin
|
48
|
+
result = orig_run_suite(suite, type)
|
49
|
+
rescue Interrupt
|
50
|
+
@need_exit = true
|
51
|
+
result = [nil,nil]
|
52
|
+
end
|
53
|
+
|
54
|
+
MiniTest::Unit.output = orig_testout
|
55
|
+
$stdin = orig_stdin
|
56
|
+
$stdout = orig_stdout
|
57
|
+
|
58
|
+
o.close
|
59
|
+
begin
|
60
|
+
th.join
|
61
|
+
rescue IOError
|
62
|
+
raise unless ["stream closed","closed stream"].include? $!.message
|
63
|
+
end
|
64
|
+
i.close
|
65
|
+
|
66
|
+
result << (report - r)
|
67
|
+
result << [@errors-e,@failures-f,@skips-s]
|
68
|
+
result << ($: - @old_loadpath)
|
69
|
+
result << suite.name
|
70
|
+
|
71
|
+
begin
|
72
|
+
@stdout.puts "done #{[Marshal.dump(result)].pack("m").gsub("\n","")}"
|
73
|
+
rescue Errno::EPIPE; end
|
74
|
+
return result
|
75
|
+
ensure
|
76
|
+
MiniTest::Unit.output = orig_stdout
|
77
|
+
$stdin = orig_stdin
|
78
|
+
$stdout = orig_stdout
|
79
|
+
o.close if o && !o.closed?
|
80
|
+
i.close if i && !i.closed?
|
81
|
+
end
|
82
|
+
|
83
|
+
def run(args = [])
|
84
|
+
process_args args
|
85
|
+
@@stop_auto_run = true
|
86
|
+
@opts = @options.dup
|
87
|
+
@need_exit = false
|
88
|
+
|
89
|
+
@old_loadpath = []
|
90
|
+
begin
|
91
|
+
@stdout = increment_io(STDOUT)
|
92
|
+
@stdin = increment_io(STDIN)
|
93
|
+
@stdout.sync = true
|
94
|
+
@stdout.puts "ready"
|
95
|
+
while buf = @stdin.gets
|
96
|
+
case buf.chomp
|
97
|
+
when /^loadpath (.+?)$/
|
98
|
+
@old_loadpath = $:.dup
|
99
|
+
$:.push(*Marshal.load($1.unpack("m")[0].force_encoding("ASCII-8BIT"))).uniq!
|
100
|
+
when /^run (.+?) (.+?)$/
|
101
|
+
@stdout.puts "okay"
|
102
|
+
|
103
|
+
@options = @opts.dup
|
104
|
+
suites = MiniTest::Unit::TestCase.test_suites
|
105
|
+
|
106
|
+
begin
|
107
|
+
require $1
|
108
|
+
rescue LoadError
|
109
|
+
@stdout.puts "after #{[Marshal.dump([$1, $!])].pack("m").gsub("\n","")}"
|
110
|
+
@stdout.puts "ready"
|
111
|
+
next
|
112
|
+
end
|
113
|
+
_run_suites MiniTest::Unit::TestCase.test_suites-suites, $2.to_sym
|
114
|
+
|
115
|
+
if @need_exit
|
116
|
+
begin
|
117
|
+
@stdout.puts "bye"
|
118
|
+
rescue Errno::EPIPE; end
|
119
|
+
exit
|
120
|
+
else
|
121
|
+
@stdout.puts "ready"
|
122
|
+
end
|
123
|
+
when /^quit$/
|
124
|
+
begin
|
125
|
+
@stdout.puts "bye"
|
126
|
+
rescue Errno::EPIPE; end
|
127
|
+
exit
|
128
|
+
end
|
129
|
+
end
|
130
|
+
rescue Errno::EPIPE
|
131
|
+
rescue Exception => e
|
132
|
+
begin
|
133
|
+
@stdout.puts "bye #{[Marshal.dump(e)].pack("m").gsub("\n","")}"
|
134
|
+
rescue Errno::EPIPE;end
|
135
|
+
exit
|
136
|
+
ensure
|
137
|
+
@stdin.close
|
138
|
+
@stdout.close
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if $0 == __FILE__
|
146
|
+
module Test
|
147
|
+
module Unit
|
148
|
+
class TestCase < MiniTest::Unit::TestCase
|
149
|
+
def on_parallel_worker?
|
150
|
+
true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
require 'rubygems'
|
156
|
+
class Gem::TestCase < MiniTest::Unit::TestCase
|
157
|
+
@@project_dir = File.expand_path('../../../..', __FILE__)
|
158
|
+
end
|
159
|
+
|
160
|
+
Test::Unit::Worker.new.run(ARGV)
|
161
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test/unit/assertions'
|
2
|
+
|
3
|
+
module Test
|
4
|
+
module Unit
|
5
|
+
# remove silly TestCase class
|
6
|
+
remove_const(:TestCase) if defined?(self::TestCase)
|
7
|
+
|
8
|
+
class TestCase < MiniTest::Unit::TestCase
|
9
|
+
include Assertions
|
10
|
+
|
11
|
+
def on_parallel_worker?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def run runner
|
16
|
+
@options = runner.options
|
17
|
+
super runner
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.test_order
|
21
|
+
:sorted
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "minitest-spec-rails-tu-shim"
|
3
|
+
spec.version = '1.9.3'
|
4
|
+
spec.authors = ["Nobuyoshi Nakada", "Shota Fukumori", "akr", "unak", "Ryan Davis", "Aaron Patterson", "Yuki Sonoda", "NARUSE, Yui", "Yusuke Endoh", "Eric Hodel", "Kenta Murata"]
|
5
|
+
spec.email = ["nobu@ruby-lang.org", "sorah@tubusu.net", "ryand-ruby@zenspider.com", "aaron.patterson@gmail.com", "ugui@yugui.jp", "drbrain@segment7.net", "mrkn+github@mrkn.jp"]
|
6
|
+
spec.description = 'Redistribution of lib/test source directory from Ruby 1.9.3p392.'
|
7
|
+
spec.summary = spec.description
|
8
|
+
spec.homepage = "https://github.com/metaskills/minitest-spec-rails-tu-shim"
|
9
|
+
spec.license = "See LICENSE.txt and COPYING.txt"
|
10
|
+
spec.files = `git ls-files`.split($/)
|
11
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
12
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
13
|
+
spec.require_paths = ["lib"]
|
14
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: minitest-spec-rails-tu-shim
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.9.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nobuyoshi Nakada
|
9
|
+
- Shota Fukumori
|
10
|
+
- akr
|
11
|
+
- unak
|
12
|
+
- Ryan Davis
|
13
|
+
- Aaron Patterson
|
14
|
+
- Yuki Sonoda
|
15
|
+
- NARUSE, Yui
|
16
|
+
- Yusuke Endoh
|
17
|
+
- Eric Hodel
|
18
|
+
- Kenta Murata
|
19
|
+
autorequire:
|
20
|
+
bindir: bin
|
21
|
+
cert_chain: []
|
22
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
23
|
+
dependencies:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: bundler
|
26
|
+
requirement: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '1.3'
|
32
|
+
type: :development
|
33
|
+
prerelease: false
|
34
|
+
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.3'
|
40
|
+
description: Redistribution of lib/test source directory from Ruby 1.9.3p392.
|
41
|
+
email:
|
42
|
+
- nobu@ruby-lang.org
|
43
|
+
- sorah@tubusu.net
|
44
|
+
- ryand-ruby@zenspider.com
|
45
|
+
- aaron.patterson@gmail.com
|
46
|
+
- ugui@yugui.jp
|
47
|
+
- drbrain@segment7.net
|
48
|
+
- mrkn+github@mrkn.jp
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- COPYING.txt
|
55
|
+
- LICENSE.txt
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- lib/test/unit.rb
|
59
|
+
- lib/test/unit/assertions.rb
|
60
|
+
- lib/test/unit/parallel.rb
|
61
|
+
- lib/test/unit/testcase.rb
|
62
|
+
- minitest-spec-rails-tu-shim.gemspec
|
63
|
+
homepage: https://github.com/metaskills/minitest-spec-rails-tu-shim
|
64
|
+
licenses:
|
65
|
+
- See LICENSE.txt and COPYING.txt
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.8.25
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Redistribution of lib/test source directory from Ruby 1.9.3p392.
|
88
|
+
test_files: []
|
89
|
+
has_rdoc:
|