test-unit-minitest 0.9.0

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.
@@ -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