betatest 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 757d652e84bb4276928a3dd5c7741bb0a8314e8c
4
+ data.tar.gz: e7553121aee3b19a146bc905d92211b875e087cb
5
+ SHA512:
6
+ metadata.gz: daa0d1c3386ee49e5f722fca46408b731e7f7d9983e375f6a7170979ebac1e2d85b52599b20bbc78ddb487afe5985bebd7956f7b46fe1f48b061912486053905
7
+ data.tar.gz: cd02e9cc115abbf5f6fd0eba7d2e5219218a80b9ad1dc911cdef162a53fe31180f02e770ba87f5793960d1156699db53aa89e0b51bc7c81aa2d7588577419535
@@ -0,0 +1,34 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+ require 'autotest/rcov' if ENV['RCOV']
5
+
6
+ Autotest.add_hook :initialize do |at|
7
+ at.testlib = 'minitest/autorun'
8
+
9
+ bench_tests = %w(TestMinitestBenchmark)
10
+ mock_tests = %w(TestMinitestMock TestMinitestStub)
11
+ spec_tests = %w(TestMinitestReporter TestMetaStatic TestMeta
12
+ TestSpecInTestCase)
13
+ unit_tests = %w(TestMinitestGuard TestMinitestRunnable
14
+ TestMinitestRunner TestMinitestTest TestMinitestUnit
15
+ TestMinitestUnitInherited TestMinitestUnitOrder
16
+ TestMinitestUnitRecording TestMinitestUnitTestCase)
17
+
18
+ {
19
+ bench_tests => "test/minitest/test_minitest_benchmark.rb",
20
+ mock_tests => "test/minitest/test_minitest_mock.rb",
21
+ spec_tests => "test/minitest/test_minitest_reporter.rb",
22
+ unit_tests => "test/minitest/test_minitest_unit.rb",
23
+ }.each do |klasses, file|
24
+ klasses.each do |klass|
25
+ at.extra_class_map[klass] = file
26
+ end
27
+ end
28
+
29
+ at.add_exception 'coverage.info'
30
+ at.add_exception 'coverage'
31
+ end
32
+
33
+ # require 'autotest/rcov'
34
+ # Autotest::RCov.command = 'rcov_info'
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in betatest.gemspec
4
+ gemspec
@@ -0,0 +1,54 @@
1
+ # Betatest
2
+
3
+ A backwards compatible fork of `betatest` but with less bugs.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'betatest'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install betatest
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/betatest/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
30
+
31
+ ## License
32
+
33
+ Released under the MIT license.
34
+
35
+ Copyright, 2014, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
36
+ Copyright, 2014, by Ryan Davis, seattle.rb.
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining a copy
39
+ of this software and associated documentation files (the "Software"), to deal
40
+ in the Software without restriction, including without limitation the rights
41
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42
+ copies of the Software, and to permit persons to whom the Software is
43
+ furnished to do so, subject to the following conditions:
44
+
45
+ The above copyright notice and this permission notice shall be included in
46
+ all copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
54
+ THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'betatest/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "betatest"
8
+ spec.version = Betatest::VERSION
9
+ spec.authors = ["Ryan Davis", "Samuel Williams"]
10
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
11
+ spec.summary = %q{A fork of minitest with less bugs.}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+ end
@@ -0,0 +1,773 @@
1
+
2
+ require "betatest/version"
3
+
4
+ require "optparse"
5
+ require "thread"
6
+ require "mutex_m"
7
+ require "betatest/parallel"
8
+
9
+ ##
10
+ # :include: README.txt
11
+
12
+ module Betatest
13
+ @@installed_at_exit ||= false
14
+ @@after_run = []
15
+ @extensions = []
16
+
17
+ mc = (class << self; self; end)
18
+
19
+ ##
20
+ # Parallel test executor
21
+
22
+ mc.send :attr_accessor, :parallel_executor
23
+ self.parallel_executor = Parallel::Executor.new((ENV['N'] || 2).to_i)
24
+
25
+ ##
26
+ # Filter object for backtraces.
27
+
28
+ mc.send :attr_accessor, :backtrace_filter
29
+
30
+ ##
31
+ # Reporter object to be used for all runs.
32
+ #
33
+ # NOTE: This accessor is only available during setup, not during runs.
34
+
35
+ mc.send :attr_accessor, :reporter
36
+
37
+ ##
38
+ # Names of known extension plugins.
39
+
40
+ mc.send :attr_accessor, :extensions
41
+
42
+ ##
43
+ # Registers Betatest to run at process exit
44
+
45
+ def self.autorun
46
+ at_exit {
47
+ next if $! and not ($!.kind_of? SystemExit and $!.success?)
48
+
49
+ # Keep track of the testing process pid, so that we don't accidentally run the @@after_run handlers in children processes (e.g. Process.fork).
50
+ @@testing_process_pid = Process.pid
51
+
52
+ exit_code = nil
53
+
54
+ at_exit {
55
+ # Don't do anything if we aren't the testing process.
56
+ next if Process.pid != @@testing_process_pid
57
+
58
+ @@after_run.reverse_each(&:call)
59
+ exit exit_code || false
60
+ }
61
+
62
+ exit_code = Betatest.run ARGV
63
+ } unless @@installed_at_exit
64
+ @@installed_at_exit = true
65
+ end
66
+
67
+ ##
68
+ # A simple hook allowing you to run a block of code after everything
69
+ # is done running. Eg:
70
+ #
71
+ # Betatest.after_run { p $debugging_info }
72
+
73
+ def self.after_run &block
74
+ @@after_run << block
75
+ end
76
+
77
+ def self.init_plugins options # :nodoc:
78
+ self.extensions.each do |name|
79
+ msg = "plugin_#{name}_init"
80
+ send msg, options if self.respond_to? msg
81
+ end
82
+ end
83
+
84
+ def self.load_plugins # :nodoc:
85
+ return unless self.extensions.empty?
86
+
87
+ seen = {}
88
+
89
+ require "rubygems" unless defined? Gem
90
+
91
+ Gem.find_files("betatest/*_plugin.rb").each do |plugin_path|
92
+ name = File.basename plugin_path, "_plugin.rb"
93
+
94
+ next if seen[name]
95
+ seen[name] = true
96
+
97
+ require plugin_path
98
+ self.extensions << name
99
+ end
100
+ end
101
+
102
+ ##
103
+ # This is the top-level run method. Everything starts from here. It
104
+ # tells each Runnable sub-class to run, and each of those are
105
+ # responsible for doing whatever they do.
106
+ #
107
+ # The overall structure of a run looks like this:
108
+ #
109
+ # Betatest.autorun
110
+ # Betatest.run(args)
111
+ # Betatest.__run(reporter, options)
112
+ # Runnable.runnables.each
113
+ # runnable.run(reporter, options)
114
+ # self.runnable_methods.each
115
+ # self.run_one_method(self, runnable_method, reporter)
116
+ # Betatest.run_one_method(klass, runnable_method, reporter)
117
+ # klass.new(runnable_method).run
118
+
119
+ def self.run args = []
120
+ self.load_plugins
121
+
122
+ options = process_args args
123
+
124
+ reporter = CompositeReporter.new
125
+ reporter << SummaryReporter.new(options[:io], options)
126
+ reporter << ProgressReporter.new(options[:io], options)
127
+
128
+ self.reporter = reporter # this makes it available to plugins
129
+ self.init_plugins options
130
+ self.reporter = nil # runnables shouldn't depend on the reporter, ever
131
+
132
+ reporter.start
133
+ __run reporter, options
134
+ self.parallel_executor.shutdown
135
+ reporter.report
136
+
137
+ reporter.passed?
138
+ end
139
+
140
+ ##
141
+ # Internal run method. Responsible for telling all Runnable
142
+ # sub-classes to run.
143
+ #
144
+ # NOTE: this method is redefined in parallel_each.rb, which is
145
+ # loaded if a Runnable calls parallelize_me!.
146
+
147
+ def self.__run reporter, options
148
+ suites = Runnable.runnables.shuffle
149
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
150
+
151
+ # If we run the parallel tests before the serial tests, the parallel tests
152
+ # could run in parallel with the serial tests. This would be bad because
153
+ # the serial tests won't lock around Reporter#record. Run the serial tests
154
+ # first, so that after they complete, the parallel tests will lock when
155
+ # recording results.
156
+ serial.map { |suite| suite.run reporter, options } +
157
+ parallel.map { |suite| suite.run reporter, options }
158
+ end
159
+
160
+ def self.process_args args = [] # :nodoc:
161
+ options = {
162
+ :io => $stdout,
163
+ }
164
+ orig_args = args.dup
165
+
166
+ OptionParser.new do |opts|
167
+ opts.banner = "betatest options:"
168
+ opts.version = Betatest::VERSION
169
+
170
+ opts.on "-h", "--help", "Display this help." do
171
+ puts opts
172
+ exit
173
+ end
174
+
175
+ opts.on "-s", "--seed SEED", Integer, "Sets random seed" do |m|
176
+ options[:seed] = m.to_i
177
+ end
178
+
179
+ opts.on "-v", "--verbose", "Verbose. Show progress processing files." do
180
+ options[:verbose] = true
181
+ end
182
+
183
+ opts.on "-n", "--name PATTERN","Filter run on /pattern/ or string." do |a|
184
+ options[:filter] = a
185
+ end
186
+
187
+ unless extensions.empty?
188
+ opts.separator ""
189
+ opts.separator "Known extensions: #{extensions.join(', ')}"
190
+
191
+ extensions.each do |meth|
192
+ msg = "plugin_#{meth}_options"
193
+ send msg, opts, options if self.respond_to?(msg)
194
+ end
195
+ end
196
+
197
+ begin
198
+ opts.parse! args
199
+ rescue OptionParser::InvalidOption => e
200
+ puts
201
+ puts e
202
+ puts
203
+ puts opts
204
+ exit 1
205
+ end
206
+
207
+ orig_args -= args
208
+ end
209
+
210
+ unless options[:seed] then
211
+ srand
212
+ options[:seed] = srand % 0xFFFF
213
+ orig_args << "--seed" << options[:seed].to_s
214
+ end
215
+
216
+ srand options[:seed]
217
+
218
+ options[:args] = orig_args.map { |s|
219
+ s =~ /[\s|&<>$()]/ ? s.inspect : s
220
+ }.join " "
221
+
222
+ options
223
+ end
224
+
225
+ def self.filter_backtrace bt # :nodoc:
226
+ backtrace_filter.filter bt
227
+ end
228
+
229
+ ##
230
+ # Represents anything "runnable", like Test, Spec, Benchmark, or
231
+ # whatever you can dream up.
232
+ #
233
+ # Subclasses of this are automatically registered and available in
234
+ # Runnable.runnables.
235
+
236
+ class Runnable
237
+ ##
238
+ # Number of assertions executed in this run.
239
+
240
+ attr_accessor :assertions
241
+
242
+ ##
243
+ # An assertion raised during the run, if any.
244
+
245
+ attr_accessor :failures
246
+
247
+ ##
248
+ # Name of the run.
249
+
250
+ def name
251
+ @NAME
252
+ end
253
+
254
+ ##
255
+ # Set the name of the run.
256
+
257
+ def name= o
258
+ @NAME = o
259
+ end
260
+
261
+ def self.inherited klass # :nodoc:
262
+ self.runnables << klass
263
+ super
264
+ end
265
+
266
+ ##
267
+ # Returns all instance methods matching the pattern +re+.
268
+
269
+ def self.methods_matching re
270
+ public_instance_methods(true).grep(re).map(&:to_s)
271
+ end
272
+
273
+ def self.reset # :nodoc:
274
+ @@runnables = []
275
+ end
276
+
277
+ reset
278
+
279
+ ##
280
+ # Responsible for running all runnable methods in a given class,
281
+ # each in its own instance. Each instance is passed to the
282
+ # reporter to record.
283
+
284
+ def self.run reporter, options = {}
285
+ filter = options[:filter] || '/./'
286
+ filter = Regexp.new $1 if filter =~ /\/(.*)\//
287
+
288
+ filtered_methods = self.runnable_methods.find_all { |m|
289
+ filter === m || filter === "#{self}##{m}"
290
+ }
291
+
292
+ with_info_handler reporter do
293
+ filtered_methods.each do |method_name|
294
+ run_one_method self, method_name, reporter
295
+ end
296
+ end
297
+ end
298
+
299
+ def self.run_one_method klass, method_name, reporter
300
+ reporter.record Betatest.run_one_method(klass, method_name)
301
+ end
302
+
303
+ def self.with_info_handler reporter, &block # :nodoc:
304
+ handler = lambda do
305
+ unless reporter.passed? then
306
+ warn "Current results:"
307
+ warn ""
308
+ warn reporter.reporters.first
309
+ warn ""
310
+ end
311
+ end
312
+
313
+ on_signal "INFO", handler, &block
314
+ end
315
+
316
+ def self.on_signal name, action # :nodoc:
317
+ supported = Signal.list[name]
318
+
319
+ old_trap = trap name do
320
+ old_trap.call if old_trap.respond_to? :call
321
+ action.call
322
+ end if supported
323
+
324
+ yield
325
+ ensure
326
+ trap name, old_trap if supported
327
+ end
328
+
329
+ ##
330
+ # Each subclass of Runnable is responsible for overriding this
331
+ # method to return all runnable methods. See #methods_matching.
332
+
333
+ def self.runnable_methods
334
+ raise NotImplementedError, "subclass responsibility"
335
+ end
336
+
337
+ ##
338
+ # Returns all subclasses of Runnable.
339
+
340
+ def self.runnables
341
+ @@runnables
342
+ end
343
+
344
+ def marshal_dump # :nodoc:
345
+ [self.name, self.failures, self.assertions]
346
+ end
347
+
348
+ def marshal_load ary # :nodoc:
349
+ self.name, self.failures, self.assertions = ary
350
+ end
351
+
352
+ def failure # :nodoc:
353
+ self.failures.first
354
+ end
355
+
356
+ def initialize name # :nodoc:
357
+ self.name = name
358
+ self.failures = []
359
+ self.assertions = 0
360
+ end
361
+
362
+ ##
363
+ # Runs a single method. Needs to return self.
364
+
365
+ def run
366
+ raise NotImplementedError, "subclass responsibility"
367
+ end
368
+
369
+ ##
370
+ # Did this run pass?
371
+ #
372
+ # Note: skipped runs are not considered passing, but they don't
373
+ # cause the process to exit non-zero.
374
+
375
+ def passed?
376
+ raise NotImplementedError, "subclass responsibility"
377
+ end
378
+
379
+ ##
380
+ # Returns a single character string to print based on the result
381
+ # of the run. Eg ".", "F", or "E".
382
+
383
+ def result_code
384
+ raise NotImplementedError, "subclass responsibility"
385
+ end
386
+
387
+ ##
388
+ # Was this run skipped? See #passed? for more information.
389
+
390
+ def skipped?
391
+ raise NotImplementedError, "subclass responsibility"
392
+ end
393
+ end
394
+
395
+ ##
396
+ # Defines the API for Reporters. Subclass this and override whatever
397
+ # you want. Go nuts.
398
+
399
+ class AbstractReporter
400
+ include Mutex_m
401
+
402
+ ##
403
+ # Starts reporting on the run.
404
+
405
+ def start
406
+ end
407
+
408
+ ##
409
+ # Record a result and output the Runnable#result_code. Stores the
410
+ # result of the run if the run did not pass.
411
+
412
+ def record result
413
+ end
414
+
415
+ ##
416
+ # Outputs the summary of the run.
417
+
418
+ def report
419
+ end
420
+
421
+ ##
422
+ # Did this run pass?
423
+
424
+ def passed?
425
+ true
426
+ end
427
+ end
428
+
429
+ class Reporter < AbstractReporter # :nodoc:
430
+ ##
431
+ # The IO used to report.
432
+
433
+ attr_accessor :io
434
+
435
+ ##
436
+ # Command-line options for this run.
437
+
438
+ attr_accessor :options
439
+
440
+ def initialize io = $stdout, options = {} # :nodoc:
441
+ super()
442
+ self.io = io
443
+ self.options = options
444
+ end
445
+ end
446
+
447
+ ##
448
+ # A very simple reporter that prints the "dots" during the run.
449
+ #
450
+ # This is added to the top-level CompositeReporter at the start of
451
+ # the run. If you want to change the output of betatest via a
452
+ # plugin, pull this out of the composite and replace it with your
453
+ # own.
454
+
455
+ class ProgressReporter < Reporter
456
+ def record result # :nodoc:
457
+ io.print "%s#%s = %.2f s = " % [result.class, result.name, result.time] if
458
+ options[:verbose]
459
+ io.print result.result_code
460
+ io.puts if options[:verbose]
461
+ end
462
+ end
463
+
464
+ ##
465
+ # A reporter that gathers statistics about a test run. Does not do
466
+ # any IO because meant to be used as a parent class for a reporter
467
+ # that does.
468
+ #
469
+ # If you want to create an entirely different type of output (eg,
470
+ # CI, HTML, etc), this is the place to start.
471
+
472
+ class StatisticsReporter < Reporter
473
+ # :stopdoc:
474
+ attr_accessor :assertions
475
+ attr_accessor :count
476
+ attr_accessor :results
477
+ attr_accessor :start_time
478
+ attr_accessor :total_time
479
+ attr_accessor :failures
480
+ attr_accessor :errors
481
+ attr_accessor :skips
482
+ # :startdoc:
483
+
484
+ def initialize io = $stdout, options = {} # :nodoc:
485
+ super
486
+
487
+ self.assertions = 0
488
+ self.count = 0
489
+ self.results = []
490
+ self.start_time = nil
491
+ self.total_time = nil
492
+ self.failures = nil
493
+ self.errors = nil
494
+ self.skips = nil
495
+ end
496
+
497
+ def passed? # :nodoc:
498
+ results.all?(&:skipped?)
499
+ end
500
+
501
+ def start # :nodoc:
502
+ self.start_time = Time.now
503
+ end
504
+
505
+ def record result # :nodoc:
506
+ self.count += 1
507
+ self.assertions += result.assertions
508
+
509
+ results << result if not result.passed? or result.skipped?
510
+ end
511
+
512
+ def report # :nodoc:
513
+ aggregate = results.group_by { |r| r.failure.class }
514
+ aggregate.default = [] # dumb. group_by should provide this
515
+
516
+ self.total_time = Time.now - start_time
517
+ self.failures = aggregate[Assertion].size
518
+ self.errors = aggregate[UnexpectedError].size
519
+ self.skips = aggregate[Skip].size
520
+ end
521
+ end
522
+
523
+ ##
524
+ # A reporter that prints the header, summary, and failure details at
525
+ # the end of the run.
526
+ #
527
+ # This is added to the top-level CompositeReporter at the start of
528
+ # the run. If you want to change the output of betatest via a
529
+ # plugin, pull this out of the composite and replace it with your
530
+ # own.
531
+
532
+ class SummaryReporter < StatisticsReporter
533
+ # :stopdoc:
534
+ attr_accessor :sync
535
+ attr_accessor :old_sync
536
+ # :startdoc:
537
+
538
+ def start # :nodoc:
539
+ super
540
+
541
+ io.puts "Run options: #{options[:args]}"
542
+ io.puts
543
+ io.puts "# Running:"
544
+ io.puts
545
+
546
+ self.sync = io.respond_to? :"sync=" # stupid emacs
547
+ self.old_sync, io.sync = io.sync, true if self.sync
548
+ end
549
+
550
+ def report # :nodoc:
551
+ super
552
+
553
+ io.sync = self.old_sync
554
+
555
+ io.puts unless options[:verbose] # finish the dots
556
+ io.puts
557
+ io.puts statistics
558
+ io.puts aggregated_results
559
+ io.puts summary
560
+ end
561
+
562
+ def statistics # :nodoc:
563
+ "Finished in %.6fs, %.4f runs/s, %.4f assertions/s." %
564
+ [total_time, count / total_time, assertions / total_time]
565
+ end
566
+
567
+ def aggregated_results # :nodoc:
568
+ filtered_results = results.dup
569
+ filtered_results.reject!(&:skipped?) unless options[:verbose]
570
+
571
+ filtered_results.each_with_index.map do |result, i|
572
+ "\n%3d) %s" % [i+1, result]
573
+ end.join("\n") + "\n"
574
+ end
575
+
576
+ alias to_s aggregated_results
577
+
578
+ def summary # :nodoc:
579
+ extra = ""
580
+
581
+ extra = "\n\nYou have skipped tests. Run with --verbose for details." if
582
+ results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
583
+
584
+ "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
585
+ [count, assertions, failures, errors, skips, extra]
586
+ end
587
+ end
588
+
589
+ ##
590
+ # Dispatch to multiple reporters as one.
591
+
592
+ class CompositeReporter < AbstractReporter
593
+ ##
594
+ # The list of reporters to dispatch to.
595
+
596
+ attr_accessor :reporters
597
+
598
+ def initialize *reporters # :nodoc:
599
+ super()
600
+ self.reporters = reporters
601
+ end
602
+
603
+ ##
604
+ # Add another reporter to the mix.
605
+
606
+ def << reporter
607
+ self.reporters << reporter
608
+ end
609
+
610
+ def passed? # :nodoc:
611
+ self.reporters.all?(&:passed?)
612
+ end
613
+
614
+ def start # :nodoc:
615
+ self.reporters.each(&:start)
616
+ end
617
+
618
+ def record result # :nodoc:
619
+ self.reporters.each do |reporter|
620
+ reporter.record result
621
+ end
622
+ end
623
+
624
+ def report # :nodoc:
625
+ self.reporters.each(&:report)
626
+ end
627
+ end
628
+
629
+ ##
630
+ # Represents run failures.
631
+
632
+ class Assertion < Exception
633
+ def error # :nodoc:
634
+ self
635
+ end
636
+
637
+ ##
638
+ # Where was this run before an assertion was raised?
639
+
640
+ def location
641
+ last_before_assertion = ""
642
+ self.backtrace.reverse_each do |s|
643
+ break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
644
+ last_before_assertion = s
645
+ end
646
+ last_before_assertion.sub(/:in .*$/, "")
647
+ end
648
+
649
+ def result_code # :nodoc:
650
+ result_label[0, 1]
651
+ end
652
+
653
+ def result_label # :nodoc:
654
+ "Failure"
655
+ end
656
+ end
657
+
658
+ ##
659
+ # Assertion raised when skipping a run.
660
+
661
+ class Skip < Assertion
662
+ def result_label # :nodoc:
663
+ "Skipped"
664
+ end
665
+ end
666
+
667
+ ##
668
+ # Assertion wrapping an unexpected error that was raised during a run.
669
+
670
+ class UnexpectedError < Assertion
671
+ attr_accessor :exception # :nodoc:
672
+
673
+ def initialize exception # :nodoc:
674
+ super
675
+ self.exception = exception
676
+ end
677
+
678
+ def backtrace # :nodoc:
679
+ self.exception.backtrace
680
+ end
681
+
682
+ def error # :nodoc:
683
+ self.exception
684
+ end
685
+
686
+ def message # :nodoc:
687
+ bt = Betatest::filter_backtrace(self.backtrace).join "\n "
688
+ "#{self.exception.class}: #{self.exception.message}\n #{bt}"
689
+ end
690
+
691
+ def result_label # :nodoc:
692
+ "Error"
693
+ end
694
+ end
695
+
696
+ ##
697
+ # Provides a simple set of guards that you can use in your tests
698
+ # to skip execution if it is not applicable. These methods are
699
+ # mixed into Test as both instance and class methods so you
700
+ # can use them inside or outside of the test methods.
701
+ #
702
+ # def test_something_for_mri
703
+ # skip "bug 1234" if jruby?
704
+ # # ...
705
+ # end
706
+ #
707
+ # if windows? then
708
+ # # ... lots of test methods ...
709
+ # end
710
+
711
+ module Guard
712
+
713
+ ##
714
+ # Is this running on jruby?
715
+
716
+ def jruby? platform = RUBY_PLATFORM
717
+ "java" == platform
718
+ end
719
+
720
+ ##
721
+ # Is this running on maglev?
722
+
723
+ def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
724
+ "maglev" == platform
725
+ end
726
+
727
+ ##
728
+ # Is this running on mri?
729
+
730
+ def mri? platform = RUBY_DESCRIPTION
731
+ /^ruby/ =~ platform
732
+ end
733
+
734
+ ##
735
+ # Is this running on rubinius?
736
+
737
+ def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
738
+ "rbx" == platform
739
+ end
740
+
741
+ ##
742
+ # Is this running on windows?
743
+
744
+ def windows? platform = RUBY_PLATFORM
745
+ /mswin|mingw/ =~ platform
746
+ end
747
+ end
748
+
749
+ class BacktraceFilter # :nodoc:
750
+ def filter bt
751
+ return ["No backtrace"] unless bt
752
+
753
+ return bt.dup if $DEBUG
754
+
755
+ new_bt = bt.take_while { |line| line !~ /lib\/betatest/ }
756
+ new_bt = bt.select { |line| line !~ /lib\/betatest/ } if new_bt.empty?
757
+ new_bt = bt.dup if new_bt.empty?
758
+
759
+ new_bt
760
+ end
761
+ end
762
+
763
+ self.backtrace_filter = BacktraceFilter.new
764
+
765
+ def self.run_one_method klass, method_name # :nodoc:
766
+ result = klass.new(method_name).run
767
+ raise "#{klass}#run _must_ return self" unless klass === result
768
+ result
769
+ end
770
+ end
771
+
772
+ require "betatest/test"
773
+ require "betatest/unit" unless defined?(MiniTest) # compatibility layer only