minitest-spec-rails-tu-shim 1.9.3
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|