rubysl-test-unit 1.0.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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