test-unit-minitest 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6650ecbb2b27cc3923b098b2696c09b5ff8c354b
4
+ data.tar.gz: dbd05d852ce49a65deeecc3a13afe2e01874428c
5
+ SHA512:
6
+ metadata.gz: 12dc3d8a7789fa414da3050974857b53b2969663901d8ece1c50472c25ca51ba9bceb0d206b734415499aaff06d1983a88492765d8230a3791110fd50b7531b3
7
+ data.tar.gz: 98b9b4c9fe1da1bb1e736d7c38313ae8890ed40b4403ec8f7066f6b87ccf1821e7285f08fe776293063bac64c80bf497a339c3816589e77c6330ee95c51bc0e4
data/LICENSE 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.
@@ -0,0 +1,29 @@
1
+ [![Build Status](http://img.shields.io/travis/iGEL/test-unit-minitest/master.svg?style=flat)](https://travis-ci.org/iGEL/test-unit-minitest)
2
+ # minitest wrapper for Test::Unit
3
+
4
+ Between Ruby 1.9 and 2.1, Ruby came with a wrapper around minitest to
5
+ provide the same interface of Test::Unit that was available with Ruby
6
+ 1.8. Ruby 2.2 comes with bundled gems of Test::Unit, but this is not
7
+ the wrapper. Some tests for the wrapped Test::Unit actually used
8
+ minitest features which are not available in the real thing, for
9
+ example some assertions of Rails 4.0.
10
+
11
+ This is a copy of the Wrapper from Ruby 2.1 as written by Shota
12
+ Fukumori. The License is same as Ruby's.
13
+
14
+ **I don't plan to maintain this for a long time. If possible, use
15
+ minitest directly.**
16
+
17
+ ## Usage
18
+
19
+ If the tests require the correct files from Test::Unit, just drop this
20
+ in your `Gemfile`:
21
+
22
+ ```ruby
23
+ gem 'test-unit-minitest', require: nil
24
+ ```
25
+
26
+ Otherwise this:
27
+ ```ruby
28
+ gem 'test-unit-minitest', require: 'test/unit'
29
+ ```
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/test/unit'
3
+ exit Test::Unit::AutoRunner.run(true)
@@ -0,0 +1,876 @@
1
+ require 'minitest/unit'
2
+ require 'test/unit/assertions'
3
+ require 'test/unit/testcase'
4
+ require 'optparse'
5
+
6
+ # See Test::Unit
7
+ module Test
8
+ ##
9
+ # Test::Unit is an implementation of the xUnit testing framework for Ruby.
10
+ #
11
+ # If you are writing new test code, please use MiniTest instead of Test::Unit.
12
+ #
13
+ # Test::Unit has been left in the standard library to support legacy test
14
+ # suites.
15
+ module Unit
16
+ TEST_UNIT_IMPLEMENTATION = 'test/unit compatibility layer using minitest' # :nodoc:
17
+
18
+ module RunCount # :nodoc: all
19
+ @@run_count = 0
20
+
21
+ def self.have_run?
22
+ @@run_count.nonzero?
23
+ end
24
+
25
+ def run(*)
26
+ @@run_count += 1
27
+ super
28
+ end
29
+
30
+ def run_once
31
+ return if have_run?
32
+ return if $! # don't run if there was an exception
33
+ yield
34
+ end
35
+ module_function :run_once
36
+ end
37
+
38
+ module Options # :nodoc: all
39
+ def initialize(*, &block)
40
+ @init_hook = block
41
+ @options = nil
42
+ super(&nil)
43
+ end
44
+
45
+ def option_parser
46
+ @option_parser ||= OptionParser.new
47
+ end
48
+
49
+ def process_args(args = [])
50
+ return @options if @options
51
+ orig_args = args.dup
52
+ options = {}
53
+ opts = option_parser
54
+ setup_options(opts, options)
55
+ opts.parse!(args)
56
+ orig_args -= args
57
+ args = @init_hook.call(args, options) if @init_hook
58
+ non_options(args, options)
59
+ @help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
60
+ @options = options
61
+ if @options[:parallel]
62
+ @files = args
63
+ @args = orig_args
64
+ end
65
+ options
66
+ end
67
+
68
+ private
69
+ def setup_options(opts, options)
70
+ opts.separator 'minitest options:'
71
+ opts.version = MiniTest::Unit::VERSION
72
+
73
+ options[:retry] = true
74
+ options[:job_status] = nil
75
+
76
+ opts.on '-h', '--help', 'Display this help.' do
77
+ puts opts
78
+ exit
79
+ end
80
+
81
+ opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
82
+ options[:seed] = m
83
+ end
84
+
85
+ opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
86
+ options[:verbose] = true
87
+ self.verbose = options[:verbose]
88
+ end
89
+
90
+ opts.on '-n', '--name PATTERN', "Filter test names on pattern." do |a|
91
+ options[:filter] = a
92
+ end
93
+
94
+ opts.on '--jobs-status [TYPE]', [:normal, :replace],
95
+ "Show status of jobs every file; Disabled when --jobs isn't specified." do |type|
96
+ options[:job_status] = type || :normal
97
+ end
98
+
99
+ opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
100
+ if /^t/ =~ a
101
+ options[:testing] = true # For testing
102
+ options[:parallel] = a[1..-1].to_i
103
+ else
104
+ options[:parallel] = a.to_i
105
+ end
106
+ end
107
+
108
+ opts.on '--separate', "Restart job process after one testcase has done" do
109
+ options[:parallel] ||= 1
110
+ options[:separate] = true
111
+ end
112
+
113
+ opts.on '--retry', "Retry running testcase when --jobs specified" do
114
+ options[:retry] = true
115
+ end
116
+
117
+ opts.on '--no-retry', "Disable --retry" do
118
+ options[:retry] = false
119
+ end
120
+
121
+ opts.on '--ruby VAL', "Path to ruby; It'll have used at -j option" do |a|
122
+ options[:ruby] = a.split(/ /).reject(&:empty?)
123
+ end
124
+
125
+ opts.on '-q', '--hide-skip', 'Hide skipped tests' do
126
+ options[:hide_skip] = true
127
+ end
128
+
129
+ opts.on '--show-skip', 'Show skipped tests' do
130
+ options[:hide_skip] = false
131
+ end
132
+
133
+ opts.on '--color[=WHEN]',
134
+ [:always, :never, :auto],
135
+ "colorize the output. WHEN defaults to 'always'", "or can be 'never' or 'auto'." do |c|
136
+ options[:color] = c || :always
137
+ end
138
+
139
+ opts.on '--tty[=WHEN]',
140
+ [:yes, :no],
141
+ "force to output tty control. WHEN defaults to 'yes'", "or can be 'no'." do |c|
142
+ @tty = c != :no
143
+ end
144
+ end
145
+
146
+ def non_options(files, options)
147
+ begin
148
+ require "rbconfig"
149
+ rescue LoadError
150
+ warn "#{caller(1)[0]}: warning: Parallel running disabled because can't get path to ruby; run specify with --ruby argument"
151
+ options[:parallel] = nil
152
+ else
153
+ options[:ruby] ||= [RbConfig.ruby]
154
+ end
155
+
156
+ true
157
+ end
158
+ end
159
+
160
+ module GlobOption # :nodoc: all
161
+ @@testfile_prefix = "test"
162
+
163
+ def setup_options(parser, options)
164
+ super
165
+ parser.on '-b', '--basedir=DIR', 'Base directory of test suites.' do |dir|
166
+ options[:base_directory] = dir
167
+ end
168
+ parser.on '-x', '--exclude PATTERN', 'Exclude test files on pattern.' do |pattern|
169
+ (options[:reject] ||= []) << pattern
170
+ end
171
+ end
172
+
173
+ def non_options(files, options)
174
+ paths = [options.delete(:base_directory), nil].uniq
175
+ if reject = options.delete(:reject)
176
+ reject_pat = Regexp.union(reject.map {|r| /#{r}/ })
177
+ end
178
+ files.map! {|f|
179
+ f = f.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
180
+ ((paths if /\A\.\.?(?:\z|\/)/ !~ f) || [nil]).any? do |prefix|
181
+ if prefix
182
+ path = f.empty? ? prefix : "#{prefix}/#{f}"
183
+ else
184
+ next if f.empty?
185
+ path = f
186
+ end
187
+ if !(match = Dir["#{path}/**/#{@@testfile_prefix}_*.rb"]).empty?
188
+ if reject
189
+ match.reject! {|n|
190
+ n[(prefix.length+1)..-1] if prefix
191
+ reject_pat =~ n
192
+ }
193
+ end
194
+ break match
195
+ elsif !reject or reject_pat !~ f and File.exist? path
196
+ break path
197
+ end
198
+ end or
199
+ raise ArgumentError, "file not found: #{f}"
200
+ }
201
+ files.flatten!
202
+ super(files, options)
203
+ end
204
+ end
205
+
206
+ module LoadPathOption # :nodoc: all
207
+ def setup_options(parser, options)
208
+ super
209
+ parser.on '-Idirectory', 'Add library load path' do |dirs|
210
+ dirs.split(':').each { |d| $LOAD_PATH.unshift d }
211
+ end
212
+ end
213
+ end
214
+
215
+ module GCStressOption # :nodoc: all
216
+ def setup_options(parser, options)
217
+ super
218
+ parser.on '--[no-]gc-stress', 'Set GC.stress as true' do |flag|
219
+ options[:gc_stress] = flag
220
+ end
221
+ end
222
+
223
+ def non_options(files, options)
224
+ if options.delete(:gc_stress)
225
+ MiniTest::Unit::TestCase.class_eval do
226
+ oldrun = instance_method(:run)
227
+ define_method(:run) do |runner|
228
+ begin
229
+ gc_stress, GC.stress = GC.stress, true
230
+ oldrun.bind(self).call(runner)
231
+ ensure
232
+ GC.stress = gc_stress
233
+ end
234
+ end
235
+ end
236
+ end
237
+ super
238
+ end
239
+ end
240
+
241
+ module RequireFiles # :nodoc: all
242
+ def non_options(files, options)
243
+ return false if !super
244
+ result = false
245
+ files.each {|f|
246
+ d = File.dirname(path = File.realpath(f))
247
+ unless $:.include? d
248
+ $: << d
249
+ end
250
+ begin
251
+ require path unless options[:parallel]
252
+ result = true
253
+ rescue LoadError
254
+ puts "#{f}: #{$!}"
255
+ end
256
+ }
257
+ result
258
+ end
259
+ end
260
+
261
+ class Runner < MiniTest::Unit # :nodoc: all
262
+ include Test::Unit::Options
263
+ include Test::Unit::GlobOption
264
+ include Test::Unit::LoadPathOption
265
+ include Test::Unit::GCStressOption
266
+ include Test::Unit::RunCount
267
+
268
+ class Worker
269
+ def self.launch(ruby,args=[])
270
+ io = IO.popen([*ruby,
271
+ "#{File.dirname(__FILE__)}/unit/parallel.rb",
272
+ *args], "rb+")
273
+ new(io, io.pid, :waiting)
274
+ end
275
+
276
+ attr_reader :quit_called
277
+
278
+ def initialize(io, pid, status)
279
+ @io = io
280
+ @pid = pid
281
+ @status = status
282
+ @file = nil
283
+ @real_file = nil
284
+ @loadpath = []
285
+ @hooks = {}
286
+ @quit_called = false
287
+ end
288
+
289
+ def puts(*args)
290
+ @io.puts(*args)
291
+ end
292
+
293
+ def run(task,type)
294
+ @file = File.basename(task, ".rb")
295
+ @real_file = task
296
+ begin
297
+ puts "loadpath #{[Marshal.dump($:-@loadpath)].pack("m0")}"
298
+ @loadpath = $:.dup
299
+ puts "run #{task} #{type}"
300
+ @status = :prepare
301
+ rescue Errno::EPIPE
302
+ died
303
+ rescue IOError
304
+ raise unless ["stream closed","closed stream"].include? $!.message
305
+ died
306
+ end
307
+ end
308
+
309
+ def hook(id,&block)
310
+ @hooks[id] ||= []
311
+ @hooks[id] << block
312
+ self
313
+ end
314
+
315
+ def read
316
+ res = (@status == :quit) ? @io.read : @io.gets
317
+ res && res.chomp
318
+ end
319
+
320
+ def close
321
+ @io.close unless @io.closed?
322
+ self
323
+ rescue IOError
324
+ end
325
+
326
+ def quit
327
+ return if @io.closed?
328
+ @quit_called = true
329
+ @io.puts "quit"
330
+ @io.close
331
+ end
332
+
333
+ def kill
334
+ Process.kill(:KILL, @pid)
335
+ rescue Errno::ESRCH
336
+ end
337
+
338
+ def died(*additional)
339
+ @status = :quit
340
+ @io.close
341
+
342
+ call_hook(:dead,*additional)
343
+ end
344
+
345
+ def to_s
346
+ if @file
347
+ "#{@pid}=#{@file}"
348
+ else
349
+ "#{@pid}:#{@status.to_s.ljust(7)}"
350
+ end
351
+ end
352
+
353
+ attr_reader :io, :pid
354
+ attr_accessor :status, :file, :real_file, :loadpath
355
+
356
+ private
357
+
358
+ def call_hook(id,*additional)
359
+ @hooks[id] ||= []
360
+ @hooks[id].each{|hook| hook[self,additional] }
361
+ self
362
+ end
363
+
364
+ end
365
+
366
+ class << self; undef autorun; end
367
+
368
+ @@stop_auto_run = false
369
+ def self.autorun
370
+ at_exit {
371
+ Test::Unit::RunCount.run_once {
372
+ exit(Test::Unit::Runner.new.run(ARGV) || true)
373
+ } unless @@stop_auto_run
374
+ } unless @@installed_at_exit
375
+ @@installed_at_exit = true
376
+ end
377
+
378
+ def after_worker_down(worker, e=nil, c=false)
379
+ return unless @options[:parallel]
380
+ return if @interrupt
381
+ warn e if e
382
+ @need_quit = true
383
+ warn ""
384
+ warn "Some worker was crashed. It seems ruby interpreter's bug"
385
+ warn "or, a bug of test/unit/parallel.rb. try again without -j"
386
+ warn "option."
387
+ warn ""
388
+ STDERR.flush
389
+ exit c
390
+ end
391
+
392
+ def terminal_width
393
+ unless @terminal_width ||= nil
394
+ begin
395
+ require 'io/console'
396
+ width = $stdout.winsize[1]
397
+ rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF
398
+ width = ENV["COLUMNS"].to_i.nonzero? || 80
399
+ end
400
+ width -= 1 if /mswin|mingw/ =~ RUBY_PLATFORM
401
+ @terminal_width = width
402
+ end
403
+ @terminal_width
404
+ end
405
+
406
+ def del_status_line
407
+ @status_line_size ||= 0
408
+ unless @options[:job_status] == :replace
409
+ $stdout.puts
410
+ return
411
+ end
412
+ print "\r"+" "*@status_line_size+"\r"
413
+ $stdout.flush
414
+ @status_line_size = 0
415
+ end
416
+
417
+ def put_status(line)
418
+ unless @options[:job_status] == :replace
419
+ print(line)
420
+ return
421
+ end
422
+ @status_line_size ||= 0
423
+ del_status_line
424
+ $stdout.flush
425
+ line = line[0...terminal_width]
426
+ print line
427
+ $stdout.flush
428
+ @status_line_size = line.size
429
+ end
430
+
431
+ def add_status(line)
432
+ unless @options[:job_status] == :replace
433
+ print(line)
434
+ return
435
+ end
436
+ @status_line_size ||= 0
437
+ line = line[0...(terminal_width-@status_line_size)]
438
+ print line
439
+ $stdout.flush
440
+ @status_line_size += line.size
441
+ end
442
+
443
+ def jobs_status
444
+ return unless @options[:job_status]
445
+ puts "" unless @options[:verbose] or @options[:job_status] == :replace
446
+ status_line = @workers.map(&:to_s).join(" ")
447
+ update_status(status_line) or (puts; nil)
448
+ end
449
+
450
+ def del_jobs_status
451
+ return unless @options[:job_status] == :replace && @status_line_size.nonzero?
452
+ del_status_line
453
+ end
454
+
455
+ def after_worker_quit(worker)
456
+ return unless @options[:parallel]
457
+ return if @interrupt
458
+ @workers.delete(worker)
459
+ @dead_workers << worker
460
+ @ios = @workers.map(&:io)
461
+ end
462
+
463
+ def launch_worker
464
+ begin
465
+ worker = Worker.launch(@options[:ruby],@args)
466
+ rescue => e
467
+ abort "ERROR: Failed to launch job process - #{e.class}: #{e.message}"
468
+ end
469
+ worker.hook(:dead) do |w,info|
470
+ after_worker_quit w
471
+ after_worker_down w, *info if !info.empty? && !worker.quit_called
472
+ end
473
+ @workers << worker
474
+ @ios << worker.io
475
+ @workers_hash[worker.io] = worker
476
+ worker
477
+ end
478
+
479
+ def delete_worker(worker)
480
+ @workers_hash.delete worker.io
481
+ @workers.delete worker
482
+ @ios.delete worker.io
483
+ end
484
+
485
+ def quit_workers
486
+ return if @workers.empty?
487
+ @workers.reject! do |worker|
488
+ begin
489
+ timeout(1) do
490
+ worker.quit
491
+ end
492
+ rescue Errno::EPIPE
493
+ rescue Timeout::Error
494
+ end
495
+ worker.close
496
+ end
497
+
498
+ return if @workers.empty?
499
+ begin
500
+ timeout(0.2 * @workers.size) do
501
+ Process.waitall
502
+ end
503
+ rescue Timeout::Error
504
+ @workers.each do |worker|
505
+ worker.kill
506
+ end
507
+ @worker.clear
508
+ end
509
+ end
510
+
511
+ def start_watchdog
512
+ Thread.new do
513
+ while stat = Process.wait2
514
+ break if @interrupt # Break when interrupt
515
+ pid, stat = stat
516
+ w = (@workers + @dead_workers).find{|x| pid == x.pid }
517
+ next unless w
518
+ w = w.dup
519
+ if w.status != :quit && !w.quit_called?
520
+ # Worker down
521
+ w.died(nil, !stat.signaled? && stat.exitstatus)
522
+ end
523
+ end
524
+ end
525
+ end
526
+
527
+ def deal(io, type, result, rep, shutting_down = false)
528
+ worker = @workers_hash[io]
529
+ case worker.read
530
+ when /^okay$/
531
+ worker.status = :running
532
+ jobs_status
533
+ when /^ready(!)?$/
534
+ bang = $1
535
+ worker.status = :ready
536
+
537
+ return nil unless task = @tasks.shift
538
+ if @options[:separate] and not bang
539
+ worker.quit
540
+ worker = add_worker
541
+ end
542
+ worker.run(task, type)
543
+ @test_count += 1
544
+
545
+ jobs_status
546
+ when /^done (.+?)$/
547
+ r = Marshal.load($1.unpack("m")[0])
548
+ result << r[0..1] unless r[0..1] == [nil,nil]
549
+ rep << {file: worker.real_file, report: r[2], result: r[3], testcase: r[5]}
550
+ $:.push(*r[4]).uniq!
551
+ return true
552
+ when /^p (.+?)$/
553
+ del_jobs_status
554
+ print $1.unpack("m")[0]
555
+ jobs_status if @options[:job_status] == :replace
556
+ when /^after (.+?)$/
557
+ @warnings << Marshal.load($1.unpack("m")[0])
558
+ when /^bye (.+?)$/
559
+ after_worker_down worker, Marshal.load($1.unpack("m")[0])
560
+ when /^bye$/, nil
561
+ if shutting_down || worker.quit_called
562
+ after_worker_quit worker
563
+ else
564
+ after_worker_down worker
565
+ end
566
+ end
567
+ return false
568
+ end
569
+
570
+ def _run_parallel suites, type, result
571
+ if @options[:parallel] < 1
572
+ warn "Error: parameter of -j option should be greater than 0."
573
+ return
574
+ end
575
+
576
+ # Require needed things for parallel running
577
+ require 'thread'
578
+ require 'timeout'
579
+ @tasks = @files.dup # Array of filenames.
580
+ @need_quit = false
581
+ @dead_workers = [] # Array of dead workers.
582
+ @warnings = []
583
+ @total_tests = @tasks.size.to_s(10)
584
+ rep = [] # FIXME: more good naming
585
+
586
+ @workers = [] # Array of workers.
587
+ @workers_hash = {} # out-IO => worker
588
+ @ios = [] # Array of worker IOs
589
+ begin
590
+ # Thread: watchdog
591
+ watchdog = start_watchdog
592
+
593
+ @options[:parallel].times {launch_worker}
594
+
595
+ while _io = IO.select(@ios)[0]
596
+ break if _io.any? do |io|
597
+ @need_quit or
598
+ (deal(io, type, result, rep).nil? and
599
+ !@workers.any? {|x| [:running, :prepare].include? x.status})
600
+ end
601
+ end
602
+ rescue Interrupt => ex
603
+ @interrupt = ex
604
+ return result
605
+ ensure
606
+ watchdog.kill if watchdog
607
+ if @interrupt
608
+ @ios.select!{|x| @workers_hash[x].status == :running }
609
+ while !@ios.empty? && (__io = IO.select(@ios,[],[],10))
610
+ __io[0].reject! {|io| deal(io, type, result, rep, true)}
611
+ end
612
+ end
613
+
614
+ quit_workers
615
+
616
+ unless @interrupt || !@options[:retry] || @need_quit
617
+ @options[:parallel] = false
618
+ suites, rep = rep.partition {|r| r[:testcase] && r[:file] && r[:report].any? {|e| !e[2].is_a?(MiniTest::Skip)}}
619
+ suites.map {|r| r[:file]}.uniq.each {|file| require file}
620
+ suites.map! {|r| eval("::"+r[:testcase])}
621
+ del_status_line or puts
622
+ unless suites.empty?
623
+ puts "Retrying..."
624
+ _run_suites(suites, type)
625
+ end
626
+ end
627
+ unless @options[:retry]
628
+ del_status_line or puts
629
+ end
630
+ unless rep.empty?
631
+ rep.each do |r|
632
+ r[:report].each do |f|
633
+ puke(*f) if f
634
+ end
635
+ end
636
+ if @options[:retry]
637
+ @errors += rep.map{|x| x[:result][0] }.inject(:+)
638
+ @failures += rep.map{|x| x[:result][1] }.inject(:+)
639
+ @skips += rep.map{|x| x[:result][2] }.inject(:+)
640
+ end
641
+ end
642
+ unless @warnings.empty?
643
+ warn ""
644
+ @warnings.uniq! {|w| w[1].message}
645
+ @warnings.each do |w|
646
+ warn "#{w[0]}: #{w[1].message} (#{w[1].class})"
647
+ end
648
+ warn ""
649
+ end
650
+ end
651
+ end
652
+
653
+ def _run_suites suites, type
654
+ _prepare_run(suites, type)
655
+ @interrupt = nil
656
+ result = []
657
+ GC.start
658
+ if @options[:parallel]
659
+ _run_parallel suites, type, result
660
+ else
661
+ suites.each {|suite|
662
+ begin
663
+ result << _run_suite(suite, type)
664
+ rescue Interrupt => e
665
+ @interrupt = e
666
+ break
667
+ end
668
+ }
669
+ end
670
+ report.reject!{|r| r.start_with? "Skipped:" } if @options[:hide_skip]
671
+ report.sort_by!{|r| r.start_with?("Skipped:") ? 0 : \
672
+ (r.start_with?("Failure:") ? 1 : 2) }
673
+ result
674
+ end
675
+
676
+ alias mini_run_suite _run_suite
677
+
678
+ def output
679
+ (@output ||= nil) || super
680
+ end
681
+
682
+ def _prepare_run(suites, type)
683
+ options[:job_status] ||= :replace if @tty && !@verbose
684
+ case options[:color]
685
+ when :always
686
+ color = true
687
+ when :auto, nil
688
+ color = @options[:job_status] == :replace && /dumb/ !~ ENV["TERM"]
689
+ else
690
+ color = false
691
+ end
692
+ if color
693
+ # dircolors-like style
694
+ colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:]*)/)] : {}
695
+ @passed_color = "\e[#{colors["pass"] || "32"}m"
696
+ @failed_color = "\e[#{colors["fail"] || "31"}m"
697
+ @skipped_color = "\e[#{colors["skip"] || "33"}m"
698
+ @reset_color = "\e[m"
699
+ else
700
+ @passed_color = @failed_color = @skipped_color = @reset_color = ""
701
+ end
702
+ if color or @options[:job_status] == :replace
703
+ @verbose = !options[:parallel]
704
+ @output = StatusLineOutput.new(self)
705
+ end
706
+ if /\A\/(.*)\/\z/ =~ (filter = options[:filter])
707
+ filter = Regexp.new($1)
708
+ end
709
+ type = "#{type}_methods"
710
+ total = if filter
711
+ suites.inject(0) {|n, suite| n + suite.send(type).grep(filter).size}
712
+ else
713
+ suites.inject(0) {|n, suite| n + suite.send(type).size}
714
+ end
715
+ @test_count = 0
716
+ @total_tests = total.to_s(10)
717
+ end
718
+
719
+ def new_test(s)
720
+ @test_count += 1
721
+ update_status(s)
722
+ end
723
+
724
+ def update_status(s)
725
+ count = @test_count.to_s(10).rjust(@total_tests.size)
726
+ put_status("#{@passed_color}[#{count}/#{@total_tests}]#{@reset_color} #{s}")
727
+ end
728
+
729
+ def _print(s); $stdout.print(s); end
730
+ def succeed; del_status_line; end
731
+
732
+ def failed(s)
733
+ sep = "\n"
734
+ @report_count ||= 0
735
+ report.each do |msg|
736
+ if msg.start_with? "Skipped:"
737
+ if @options[:hide_skip]
738
+ del_status_line
739
+ next
740
+ end
741
+ color = @skipped_color
742
+ else
743
+ color = @failed_color
744
+ end
745
+ msg = msg.split(/$/, 2)
746
+ $stdout.printf("%s%s%3d) %s%s%s\n",
747
+ sep, color, @report_count += 1,
748
+ msg[0], @reset_color, msg[1])
749
+ sep = nil
750
+ end
751
+ report.clear
752
+ end
753
+
754
+ # Overriding of MiniTest::Unit#puke
755
+ def puke klass, meth, e
756
+ # TODO:
757
+ # this overriding is for minitest feature that skip messages are
758
+ # hidden when not verbose (-v), note this is temporally.
759
+ n = report.size
760
+ rep = super
761
+ if MiniTest::Skip === e and /no message given\z/ =~ e.message
762
+ report.slice!(n..-1)
763
+ rep = "."
764
+ end
765
+ rep
766
+ end
767
+
768
+ def initialize
769
+ super
770
+ @tty = $stdout.tty?
771
+ end
772
+
773
+ def status(*args)
774
+ result = super
775
+ raise @interrupt if @interrupt
776
+ result
777
+ end
778
+
779
+ def run(*args)
780
+ result = super
781
+ puts "\nruby -v: #{RUBY_DESCRIPTION}"
782
+ result
783
+ end
784
+ end
785
+
786
+ class StatusLineOutput < Struct.new(:runner) # :nodoc: all
787
+ def puts(*a) $stdout.puts(*a) unless a.empty? end
788
+ def respond_to_missing?(*a) $stdout.respond_to?(*a) end
789
+ def method_missing(*a, &b) $stdout.__send__(*a, &b) end
790
+
791
+ def print(s)
792
+ case s
793
+ when /\A(.*\#.*) = \z/
794
+ runner.new_test($1)
795
+ when /\A(.* s) = \z/
796
+ runner.add_status(" = "+$1.chomp)
797
+ when /\A\.+\z/
798
+ runner.succeed
799
+ when /\A[EFS]\z/
800
+ runner.failed(s)
801
+ else
802
+ $stdout.print(s)
803
+ end
804
+ end
805
+ end
806
+
807
+ class AutoRunner # :nodoc: all
808
+ class Runner < Test::Unit::Runner
809
+ include Test::Unit::RequireFiles
810
+ end
811
+
812
+ attr_accessor :to_run, :options
813
+
814
+ def initialize(force_standalone = false, default_dir = nil, argv = ARGV)
815
+ @force_standalone = force_standalone
816
+ @runner = Runner.new do |files, options|
817
+ options[:base_directory] ||= default_dir
818
+ files << default_dir if files.empty? and default_dir
819
+ @to_run = files
820
+ yield self if block_given?
821
+ files
822
+ end
823
+ Runner.runner = @runner
824
+ @options = @runner.option_parser
825
+ if @force_standalone
826
+ @options.banner.sub!(/\[options\]/, '\& tests...')
827
+ end
828
+ @argv = argv
829
+ end
830
+
831
+ def process_args(*args)
832
+ @runner.process_args(*args)
833
+ !@to_run.empty?
834
+ end
835
+
836
+ def run
837
+ if @force_standalone and not process_args(@argv)
838
+ abort @options.banner
839
+ end
840
+ @runner.run(@argv) || true
841
+ end
842
+
843
+ def self.run(*args)
844
+ new(*args).run
845
+ end
846
+ end
847
+
848
+ class ProxyError < StandardError # :nodoc: all
849
+ def initialize(ex)
850
+ @message = ex.message
851
+ @backtrace = ex.backtrace
852
+ end
853
+
854
+ attr_accessor :message, :backtrace
855
+ end
856
+ end
857
+ end
858
+
859
+ module MiniTest # :nodoc: all
860
+ class Unit
861
+ end
862
+ end
863
+
864
+ class MiniTest::Unit::TestCase # :nodoc: all
865
+ undef run_test
866
+ RUN_TEST_TRACE = "#{__FILE__}:#{__LINE__+3}:in `run_test'".freeze
867
+ def run_test(name)
868
+ progname, $0 = $0, "#{$0}: #{self.class}##{name}"
869
+ self.__send__(name)
870
+ ensure
871
+ $@.delete(RUN_TEST_TRACE) if $@
872
+ $0 = progname
873
+ end
874
+ end
875
+
876
+ Test::Unit::Runner.autorun