rubysl-test-unit 1.0.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d512a7e73f3ae104def9d3dcaf1b02194c87e6af
4
- data.tar.gz: 66d531e458ba79088e5cffa97784a4e807a3667b
3
+ metadata.gz: 6f409bfb09799e670824d4277954bc9980ef6103
4
+ data.tar.gz: 9d2da62b2d1e3a513cdb347c7dcfb7c771ba979c
5
5
  SHA512:
6
- metadata.gz: 03cb459964ed1dc7498ceb5f0ec8ecfc5049d1a66d0fed9e6739c077375ea06704452afe701be01887d7f3dbf8ccbf2cd2eb6d5754fab3dcb87d2955df185628
7
- data.tar.gz: 13428d099e385a0a5a7a806e0da9910c4f7a7ae57b6a783011ef04e1a2fe5185a4526cb68811e08e2d293fd33796847afb7361208206b0e87711b80fe0f374ce
6
+ metadata.gz: c93bf2d11e1f39b3ba7bfa923d7ba9ead6ef780e7baf5fe50ef624df335b389c7f10e8572b010158aa43289afd280ee1f8377db609237ce75c1fc80c461d1796
7
+ data.tar.gz: 64075cb13ea6f9914dc6065aa93320c72e49d69857195a715f9b38090a408d6e82eb774455e9037a3ae4ab6f136a97e7fb990f98ef4463f0c1068a6a6a51386c
data/.travis.yml CHANGED
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  before_install:
3
+ - rvm use $RVM --install --binary --fuzzy
3
4
  - gem update --system
4
5
  - gem --version
5
6
  - gem install rubysl-bundler
7
+ env:
8
+ - RVM=rbx-nightly-d21 RUBYLIB=lib
6
9
  script: bundle exec mspec spec
7
- rvm:
8
- - rbx-nightly-18mode
@@ -1,280 +1,876 @@
1
+ require 'minitest/unit'
2
+ require 'test/unit/assertions'
1
3
  require 'test/unit/testcase'
2
- require 'test/unit/autorunner'
4
+ require 'optparse'
3
5
 
4
- module Test # :nodoc:
6
+ # See Test::Unit
7
+ module Test
8
+ ##
9
+ # Test::Unit is an implementation of the xUnit testing framework for Ruby.
5
10
  #
6
- # = Test::Unit - Ruby Unit Testing Framework
7
- #
8
- # == Introduction
9
- #
10
- # Unit testing is making waves all over the place, largely due to the
11
- # fact that it is a core practice of XP. While XP is great, unit testing
12
- # has been around for a long time and has always been a good idea. One
13
- # of the keys to good unit testing, though, is not just writing tests,
14
- # but having tests. What's the difference? Well, if you just _write_ a
15
- # test and throw it away, you have no guarantee that something won't
16
- # change later which breaks your code. If, on the other hand, you _have_
17
- # tests (obviously you have to write them first), and run them as often
18
- # as possible, you slowly build up a wall of things that cannot break
19
- # without you immediately knowing about it. This is when unit testing
20
- # hits its peak usefulness.
21
- #
22
- # Enter Test::Unit, a framework for unit testing in Ruby, helping you to
23
- # design, debug and evaluate your code by making it easy to write and
24
- # have tests for it.
25
- #
26
- #
27
- # == Notes
28
- #
29
- # Test::Unit has grown out of and superceded Lapidary.
30
- #
31
- #
32
- # == Feedback
33
- #
34
- # I like (and do my best to practice) XP, so I value early releases,
35
- # user feedback, and clean, simple, expressive code. There is always
36
- # room for improvement in everything I do, and Test::Unit is no
37
- # exception. Please, let me know what you think of Test::Unit as it
38
- # stands, and what you'd like to see expanded/changed/improved/etc. If
39
- # you find a bug, let me know ASAP; one good way to let me know what the
40
- # bug is is to submit a new test that catches it :-) Also, I'd love to
41
- # hear about any successes you have with Test::Unit, and any
42
- # documentation you might add will be greatly appreciated. My contact
43
- # info is below.
44
- #
45
- #
46
- # == Contact Information
47
- #
48
- # A lot of discussion happens about Ruby in general on the ruby-talk
49
- # mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask
50
- # any questions you might have there. I monitor the list, as do many
51
- # other helpful Rubyists, and you're sure to get a quick answer. Of
52
- # course, you're also welcome to email me (Nathaniel Talbott) directly
53
- # at mailto:testunit@talbott.ws, and I'll do my best to help you out.
54
- #
55
- #
56
- # == Credits
57
- #
58
- # I'd like to thank...
59
- #
60
- # Matz, for a great language!
61
- #
62
- # Masaki Suketa, for his work on RubyUnit, which filled a vital need in
63
- # the Ruby world for a very long time. I'm also grateful for his help in
64
- # polishing Test::Unit and getting the RubyUnit compatibility layer
65
- # right. His graciousness in allowing Test::Unit to supercede RubyUnit
66
- # continues to be a challenge to me to be more willing to defer my own
67
- # rights.
68
- #
69
- # Ken McKinlay, for his interest and work on unit testing, and for his
70
- # willingness to dialog about it. He was also a great help in pointing
71
- # out some of the holes in the RubyUnit compatibility layer.
72
- #
73
- # Dave Thomas, for the original idea that led to the extremely simple
74
- # "require 'test/unit'", plus his code to improve it even more by
75
- # allowing the selection of tests from the command-line. Also, without
76
- # RDoc, the documentation for Test::Unit would stink a lot more than it
77
- # does now.
78
- #
79
- # Everyone who's helped out with bug reports, feature ideas,
80
- # encouragement to continue, etc. It's a real privilege to be a part of
81
- # the Ruby community.
82
- #
83
- # The guys at RoleModel Software, for putting up with me repeating, "But
84
- # this would be so much easier in Ruby!" whenever we're coding in Java.
85
- #
86
- # My Creator, for giving me life, and giving it more abundantly.
87
- #
88
- #
89
- # == License
90
- #
91
- # Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free
92
- # software, and is distributed under the Ruby license. See the COPYING
93
- # file in the standard Ruby distribution for details.
94
- #
95
- #
96
- # == Warranty
97
- #
98
- # This software is provided "as is" and without any express or
99
- # implied warranties, including, without limitation, the implied
100
- # warranties of merchantibility and fitness for a particular
101
- # purpose.
102
- #
103
- #
104
- # == Author
105
- #
106
- # Nathaniel Talbott.
107
- # Copyright (c) 2000-2003, Nathaniel Talbott
11
+ # If you are writing new test code, please use MiniTest instead of Test::Unit.
108
12
  #
109
- # ----
110
- #
111
- # = Usage
112
- #
113
- # The general idea behind unit testing is that you write a _test_
114
- # _method_ that makes certain _assertions_ about your code, working
115
- # against a _test_ _fixture_. A bunch of these _test_ _methods_ are
116
- # bundled up into a _test_ _suite_ and can be run any time the
117
- # developer wants. The results of a run are gathered in a _test_
118
- # _result_ and displayed to the user through some UI. So, lets break
119
- # this down and see how Test::Unit provides each of these necessary
120
- # pieces.
121
- #
122
- #
123
- # == Assertions
124
- #
125
- # These are the heart of the framework. Think of an assertion as a
126
- # statement of expected outcome, i.e. "I assert that x should be equal
127
- # to y". If, when the assertion is executed, it turns out to be
128
- # correct, nothing happens, and life is good. If, on the other hand,
129
- # your assertion turns out to be false, an error is propagated with
130
- # pertinent information so that you can go back and make your
131
- # assertion succeed, and, once again, life is good. For an explanation
132
- # of the current assertions, see Test::Unit::Assertions.
133
- #
134
- #
135
- # == Test Method & Test Fixture
136
- #
137
- # Obviously, these assertions have to be called within a context that
138
- # knows about them and can do something meaningful with their
139
- # pass/fail value. Also, it's handy to collect a bunch of related
140
- # tests, each test represented by a method, into a common test class
141
- # that knows how to run them. The tests will be in a separate class
142
- # from the code they're testing for a couple of reasons. First of all,
143
- # it allows your code to stay uncluttered with test code, making it
144
- # easier to maintain. Second, it allows the tests to be stripped out
145
- # for deployment, since they're really there for you, the developer,
146
- # and your users don't need them. Third, and most importantly, it
147
- # allows you to set up a common test fixture for your tests to run
148
- # against.
149
- #
150
- # What's a test fixture? Well, tests do not live in a vacuum; rather,
151
- # they're run against the code they are testing. Often, a collection
152
- # of tests will run against a common set of data, also called a
153
- # fixture. If they're all bundled into the same test class, they can
154
- # all share the setting up and tearing down of that data, eliminating
155
- # unnecessary duplication and making it much easier to add related
156
- # tests.
157
- #
158
- # Test::Unit::TestCase wraps up a collection of test methods together
159
- # and allows you to easily set up and tear down the same test fixture
160
- # for each test. This is done by overriding #setup and/or #teardown,
161
- # which will be called before and after each test method that is
162
- # run. The TestCase also knows how to collect the results of your
163
- # assertions into a Test::Unit::TestResult, which can then be reported
164
- # back to you... but I'm getting ahead of myself. To write a test,
165
- # follow these steps:
166
- #
167
- # * Make sure Test::Unit is in your library path.
168
- # * require 'test/unit' in your test script.
169
- # * Create a class that subclasses Test::Unit::TestCase.
170
- # * Add a method that begins with "test" to your class.
171
- # * Make assertions in your test method.
172
- # * Optionally define #setup and/or #teardown to set up and/or tear
173
- # down your common test fixture.
174
- # * You can now run your test as you would any other Ruby
175
- # script... try it and see!
176
- #
177
- # A really simple test might look like this (#setup and #teardown are
178
- # commented out to indicate that they are completely optional):
179
- #
180
- # require 'test/unit'
181
- #
182
- # class TC_MyTest < Test::Unit::TestCase
183
- # # def setup
184
- # # end
185
- #
186
- # # def teardown
187
- # # end
188
- #
189
- # def test_fail
190
- # assert(false, 'Assertion was false.')
191
- # end
192
- # end
193
- #
194
- #
195
- # == Test Runners
196
- #
197
- # So, now you have this great test class, but you still need a way to
198
- # run it and view any failures that occur during the run. This is
199
- # where Test::Unit::UI::Console::TestRunner (and others, such as
200
- # Test::Unit::UI::GTK::TestRunner) comes into play. The console test
201
- # runner is automatically invoked for you if you require 'test/unit'
202
- # and simply run the file. To use another runner, or to manually
203
- # invoke a runner, simply call its run class method and pass in an
204
- # object that responds to the suite message with a
205
- # Test::Unit::TestSuite. This can be as simple as passing in your
206
- # TestCase class (which has a class suite method). It might look
207
- # something like this:
208
- #
209
- # require 'test/unit/ui/console/testrunner'
210
- # Test::Unit::UI::Console::TestRunner.run(TC_MyTest)
211
- #
212
- #
213
- # == Test Suite
214
- #
215
- # As more and more unit tests accumulate for a given project, it
216
- # becomes a real drag running them one at a time, and it also
217
- # introduces the potential to overlook a failing test because you
218
- # forget to run it. Suddenly it becomes very handy that the
219
- # TestRunners can take any object that returns a Test::Unit::TestSuite
220
- # in response to a suite method. The TestSuite can, in turn, contain
221
- # other TestSuites or individual tests (typically created by a
222
- # TestCase). In other words, you can easily wrap up a group of
223
- # TestCases and TestSuites like this:
224
- #
225
- # require 'test/unit/testsuite'
226
- # require 'tc_myfirsttests'
227
- # require 'tc_moretestsbyme'
228
- # require 'ts_anothersetoftests'
229
- #
230
- # class TS_MyTests
231
- # def self.suite
232
- # suite = Test::Unit::TestSuite.new
233
- # suite << TC_MyFirstTests.suite
234
- # suite << TC_MoreTestsByMe.suite
235
- # suite << TS_AnotherSetOfTests.suite
236
- # return suite
237
- # end
238
- # end
239
- # Test::Unit::UI::Console::TestRunner.run(TS_MyTests)
240
- #
241
- # Now, this is a bit cumbersome, so Test::Unit does a little bit more
242
- # for you, by wrapping these up automatically when you require
243
- # 'test/unit'. What does this mean? It means you could write the above
244
- # test case like this instead:
245
- #
246
- # require 'test/unit'
247
- # require 'tc_myfirsttests'
248
- # require 'tc_moretestsbyme'
249
- # require 'ts_anothersetoftests'
250
- #
251
- # Test::Unit is smart enough to find all the test cases existing in
252
- # the ObjectSpace and wrap them up into a suite for you. It then runs
253
- # the dynamic suite using the console TestRunner.
254
- #
255
- #
256
- # == Questions?
257
- #
258
- # I'd really like to get feedback from all levels of Ruby
259
- # practitioners about typos, grammatical errors, unclear statements,
260
- # missing points, etc., in this document (or any other).
261
- #
262
-
13
+ # Test::Unit has been left in the standard library to support legacy test
14
+ # suites.
263
15
  module Unit
264
- # If set to false Test::Unit will not automatically run at exit.
265
- def self.run=(flag)
266
- @run = flag
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
267
784
  end
268
785
 
269
- # Automatically run tests at exit?
270
- def self.run?
271
- @run ||= false
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
272
855
  end
273
856
  end
274
857
  end
275
858
 
276
- at_exit do
277
- unless $! || Test::Unit.run?
278
- Kernel.exit Test::Unit::AutoRunner.run
859
+ module MiniTest # :nodoc: all
860
+ class Unit
279
861
  end
280
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