minitest 4.7.5 → 5.0.7

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.
data/lib/minitest.rb ADDED
@@ -0,0 +1,733 @@
1
+ require "optparse"
2
+
3
+ ##
4
+ # :include: README.txt
5
+
6
+ module Minitest
7
+ VERSION = "5.0.7" # :nodoc:
8
+
9
+ @@installed_at_exit ||= false
10
+ @@after_run = []
11
+ @extensions = []
12
+
13
+ mc = (class << self; self; end)
14
+
15
+ ##
16
+ # Filter object for backtraces.
17
+
18
+ mc.send :attr_accessor, :backtrace_filter
19
+
20
+ ##
21
+ # Reporter object to be used for all runs.
22
+ #
23
+ # NOTE: This accessor is only available during setup, not during runs.
24
+
25
+ mc.send :attr_accessor, :reporter
26
+
27
+ ##
28
+ # Names of known extension plugins.
29
+
30
+ mc.send :attr_accessor, :extensions
31
+
32
+ ##
33
+ # Registers Minitest to run at process exit
34
+
35
+ def self.autorun
36
+ at_exit {
37
+ next if $! and not $!.kind_of? SystemExit
38
+
39
+ exit_code = nil
40
+
41
+ at_exit {
42
+ @@after_run.reverse_each(&:call)
43
+ exit exit_code || false
44
+ }
45
+
46
+ exit_code = Minitest.run ARGV
47
+ } unless @@installed_at_exit
48
+ @@installed_at_exit = true
49
+ end
50
+
51
+ ##
52
+ # A simple hook allowing you to run a block of code after everything
53
+ # is done running. Eg:
54
+ #
55
+ # Minitest.after_run { p $debugging_info }
56
+
57
+ def self.after_run &block
58
+ @@after_run << block
59
+ end
60
+
61
+ def self.init_plugins options # :nodoc:
62
+ self.extensions.each do |name|
63
+ msg = "plugin_#{name}_init"
64
+ send msg, options if self.respond_to? msg
65
+ end
66
+ end
67
+
68
+ def self.load_plugins # :nodoc:
69
+ return unless self.extensions.empty?
70
+
71
+ seen = {}
72
+
73
+ require "rubygems" unless defined? Gem
74
+
75
+ Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
76
+ name = File.basename plugin_path, "_plugin.rb"
77
+
78
+ next if seen[name]
79
+ seen[name] = true
80
+
81
+ require plugin_path
82
+ self.extensions << name
83
+ end
84
+ end
85
+
86
+ ##
87
+ # This is the top-level run method. Everything starts from here. It
88
+ # tells each Runnable sub-class to run, and each of those are
89
+ # responsible for doing whatever they do.
90
+ #
91
+ # The overall structure of a run looks like this:
92
+ #
93
+ # Minitest.autorun
94
+ # Minitest.run(args)
95
+ # __run(reporter, options)
96
+ # Runnable.runnables.each
97
+ # runnable.run(reporter, options)
98
+ # self.runnable_methods.each
99
+ # self.new(runnable_method).run
100
+
101
+ def self.run args = []
102
+ self.load_plugins
103
+
104
+ options = process_args args
105
+
106
+ reporter = CompositeReporter.new
107
+ reporter << ProgressReporter.new(options[:io], options)
108
+ reporter << SummaryReporter.new(options[:io], options)
109
+
110
+ self.reporter = reporter # this makes it available to plugins
111
+ self.init_plugins options
112
+ self.reporter = nil # runnables shouldn't depend on the reporter, ever
113
+
114
+ reporter.start
115
+ __run reporter, options
116
+ reporter.report
117
+
118
+ reporter.passed?
119
+ end
120
+
121
+ ##
122
+ # Internal run method. Responsible for telling all Runnable
123
+ # sub-classes to run.
124
+ #
125
+ # NOTE: this method is redefined in parallel_each.rb, which is
126
+ # loaded if a Runnable calls parallelize_me!.
127
+
128
+ def self.__run reporter, options
129
+ Runnable.runnables.each do |runnable|
130
+ runnable.run reporter, options
131
+ end
132
+ end
133
+
134
+ def self.process_args args = [] # :nodoc:
135
+ options = {
136
+ :io => $stdout,
137
+ }
138
+ orig_args = args.dup
139
+
140
+ OptionParser.new do |opts|
141
+ opts.banner = "minitest options:"
142
+ opts.version = Minitest::VERSION
143
+
144
+ opts.on "-h", "--help", "Display this help." do
145
+ puts opts
146
+ exit
147
+ end
148
+
149
+ opts.on "-s", "--seed SEED", Integer, "Sets random seed" do |m|
150
+ options[:seed] = m.to_i
151
+ end
152
+
153
+ opts.on "-v", "--verbose", "Verbose. Show progress processing files." do
154
+ options[:verbose] = true
155
+ end
156
+
157
+ opts.on "-n", "--name PATTERN","Filter run on /pattern/ or string." do |a|
158
+ options[:filter] = a
159
+ end
160
+
161
+ unless extensions.empty?
162
+ opts.separator ""
163
+ opts.separator "Known extensions: #{extensions.join(', ')}"
164
+
165
+ extensions.each do |meth|
166
+ msg = "plugin_#{meth}_options"
167
+ send msg, opts, options if self.respond_to?(msg)
168
+ end
169
+ end
170
+
171
+ begin
172
+ opts.parse! args
173
+ rescue OptionParser::InvalidOption => e
174
+ puts
175
+ puts e
176
+ puts
177
+ puts opts
178
+ exit 1
179
+ end
180
+
181
+ orig_args -= args
182
+ end
183
+
184
+ unless options[:seed] then
185
+ srand
186
+ options[:seed] = srand % 0xFFFF
187
+ orig_args << "--seed" << options[:seed].to_s
188
+ end
189
+
190
+ srand options[:seed]
191
+
192
+ options[:args] = orig_args.map { |s|
193
+ s =~ /[\s|&<>$()]/ ? s.inspect : s
194
+ }.join " "
195
+
196
+ options
197
+ end
198
+
199
+ def self.filter_backtrace bt # :nodoc:
200
+ backtrace_filter.filter bt
201
+ end
202
+
203
+ ##
204
+ # Represents anything "runnable", like Test, Spec, Benchmark, or
205
+ # whatever you can dream up.
206
+ #
207
+ # Subclasses of this are automatically registered and available in
208
+ # Runnable.runnables.
209
+
210
+ class Runnable
211
+ ##
212
+ # Number of assertions executed in this run.
213
+
214
+ attr_accessor :assertions
215
+
216
+ ##
217
+ # An assertion raised during the run, if any.
218
+
219
+ attr_accessor :failures
220
+
221
+ ##
222
+ # Name of the run.
223
+
224
+ def name
225
+ @NAME
226
+ end
227
+
228
+ ##
229
+ # Set the name of the run.
230
+
231
+ def name= o
232
+ @NAME = o
233
+ end
234
+
235
+ def self.inherited klass # :nodoc:
236
+ self.runnables << klass
237
+ super
238
+ end
239
+
240
+ ##
241
+ # Returns all instance methods matching the pattern +re+.
242
+
243
+ def self.methods_matching re
244
+ public_instance_methods(true).grep(re).map(&:to_s)
245
+ end
246
+
247
+ def self.reset # :nodoc:
248
+ @@runnables = []
249
+ end
250
+
251
+ reset
252
+
253
+ ##
254
+ # Responsible for running all runnable methods in a given class,
255
+ # each in its own instance. Each instance is passed to the
256
+ # reporter to record.
257
+
258
+ def self.run reporter, options = {}
259
+ filter = options[:filter] || '/./'
260
+ filter = Regexp.new $1 if filter =~ /\/(.*)\//
261
+
262
+ filtered_methods = self.runnable_methods.find_all { |m|
263
+ filter === m || filter === "#{self}##{m}"
264
+ }
265
+
266
+ with_info_handler reporter do
267
+ filtered_methods.each do |method_name|
268
+ result = self.new(method_name).run
269
+ raise "#{self}#run _must_ return self" unless self === result
270
+ reporter.record result
271
+ end
272
+ end
273
+ end
274
+
275
+ def self.with_info_handler reporter, &block # :nodoc:
276
+ handler = lambda do
277
+ unless reporter.passed? then
278
+ warn "Current results:"
279
+ warn ""
280
+ warn reporter.reporters.first
281
+ warn ""
282
+ end
283
+ end
284
+
285
+ on_signal "INFO", handler, &block
286
+ end
287
+
288
+ def self.on_signal name, action # :nodoc:
289
+ supported = Signal.list[name]
290
+
291
+ old_trap = trap name do
292
+ old_trap.call if old_trap.respond_to? :call
293
+ action.call
294
+ end if supported
295
+
296
+ yield
297
+ ensure
298
+ trap name, old_trap if supported
299
+ end
300
+
301
+ ##
302
+ # Each subclass of Runnable is responsible for overriding this
303
+ # method to return all runnable methods. See #methods_matching.
304
+
305
+ def self.runnable_methods
306
+ raise NotImplementedError, "subclass responsibility"
307
+ end
308
+
309
+ ##
310
+ # Returns all subclasses of Runnable.
311
+
312
+ def self.runnables
313
+ @@runnables
314
+ end
315
+
316
+ def marshal_dump # :nodoc:
317
+ [self.name, self.failures, self.assertions]
318
+ end
319
+
320
+ def marshal_load ary # :nodoc:
321
+ self.name, self.failures, self.assertions = ary
322
+ end
323
+
324
+ def failure # :nodoc:
325
+ self.failures.first
326
+ end
327
+
328
+ def initialize name # :nodoc:
329
+ self.name = name
330
+ self.failures = []
331
+ self.assertions = 0
332
+ end
333
+
334
+ ##
335
+ # Runs a single method. Needs to return self.
336
+
337
+ def run
338
+ raise NotImplementedError, "subclass responsibility"
339
+ end
340
+
341
+ ##
342
+ # Did this run pass?
343
+ #
344
+ # Note: skipped runs are not considered passing, but they don't
345
+ # cause the process to exit non-zero.
346
+
347
+ def passed?
348
+ raise NotImplementedError, "subclass responsibility"
349
+ end
350
+
351
+ ##
352
+ # Returns a single character string to print based on the result
353
+ # of the run. Eg ".", "F", or "E".
354
+
355
+ def result_code
356
+ raise NotImplementedError, "subclass responsibility"
357
+ end
358
+
359
+ ##
360
+ # Was this run skipped? See #passed? for more information.
361
+
362
+ def skipped?
363
+ raise NotImplementedError, "subclass responsibility"
364
+ end
365
+ end
366
+
367
+ ##
368
+ # Defines the API for Reporters. Subclass this and override whatever
369
+ # you want. Go nuts.
370
+
371
+ class AbstractReporter
372
+ ##
373
+ # Starts reporting on the run.
374
+
375
+ def start
376
+ end
377
+
378
+ ##
379
+ # Record a result and output the Runnable#result_code. Stores the
380
+ # result of the run if the run did not pass.
381
+
382
+ def record result
383
+ end
384
+
385
+ ##
386
+ # Outputs the summary of the run.
387
+
388
+ def report
389
+ end
390
+
391
+ ##
392
+ # Did this run pass?
393
+
394
+ def passed?
395
+ true
396
+ end
397
+ end
398
+
399
+ class Reporter < AbstractReporter # :nodoc:
400
+ ##
401
+ # The IO used to report.
402
+
403
+ attr_accessor :io
404
+
405
+ ##
406
+ # Command-line options for this run.
407
+
408
+ attr_accessor :options
409
+
410
+ def initialize io = $stdout, options = {} # :nodoc:
411
+ self.io = io
412
+ self.options = options
413
+ end
414
+ end
415
+
416
+ ##
417
+ # A very simple reporter that prints the "dots" during the run.
418
+ #
419
+ # This is added to the top-level CompositeReporter at the start of
420
+ # the run. If you want to change the output of minitest via a
421
+ # plugin, pull this out of the composite and replace it with your
422
+ # own.
423
+
424
+ class ProgressReporter < Reporter
425
+ def record result # :nodoc:
426
+ io.print "%s#%s = %.2f s = " % [result.class, result.name, result.time] if
427
+ options[:verbose]
428
+ io.print result.result_code
429
+ io.puts if options[:verbose]
430
+ end
431
+ end
432
+
433
+ ##
434
+ # A reporter that gathers statistics about a test run. Does not do
435
+ # any IO because meant to be used as a parent class for a reporter
436
+ # that does.
437
+ #
438
+ # If you want to create an entirely different type of output (eg,
439
+ # CI, HTML, etc), this is the place to start.
440
+
441
+ class StatisticsReporter < Reporter
442
+ # :stopdoc:
443
+ attr_accessor :assertions
444
+ attr_accessor :count
445
+ attr_accessor :results
446
+ attr_accessor :start_time
447
+ attr_accessor :total_time
448
+ attr_accessor :failures
449
+ attr_accessor :errors
450
+ attr_accessor :skips
451
+ # :startdoc:
452
+
453
+ def initialize io = $stdout, options = {} # :nodoc:
454
+ super
455
+
456
+ self.assertions = 0
457
+ self.count = 0
458
+ self.results = []
459
+ self.start_time = nil
460
+ self.total_time = nil
461
+ self.failures = nil
462
+ self.errors = nil
463
+ self.skips = nil
464
+ end
465
+
466
+ def passed? # :nodoc:
467
+ results.all?(&:skipped?)
468
+ end
469
+
470
+ def start # :nodoc:
471
+ self.start_time = Time.now
472
+ end
473
+
474
+ def record result # :nodoc:
475
+ self.count += 1
476
+ self.assertions += result.assertions
477
+
478
+ results << result if not result.passed? or result.skipped?
479
+ end
480
+
481
+ def report # :nodoc:
482
+ aggregate = results.group_by { |r| r.failure.class }
483
+ aggregate.default = [] # dumb. group_by should provide this
484
+
485
+ self.total_time = Time.now - start_time
486
+ self.failures = aggregate[Assertion].size
487
+ self.errors = aggregate[UnexpectedError].size
488
+ self.skips = aggregate[Skip].size
489
+ end
490
+ end
491
+
492
+ ##
493
+ # A reporter that prints the header, summary, and failure details at
494
+ # the end of the run.
495
+ #
496
+ # This is added to the top-level CompositeReporter at the start of
497
+ # the run. If you want to change the output of minitest via a
498
+ # plugin, pull this out of the composite and replace it with your
499
+ # own.
500
+
501
+ class SummaryReporter < StatisticsReporter
502
+ # :stopdoc:
503
+ attr_accessor :sync
504
+ attr_accessor :old_sync
505
+ # :startdoc:
506
+
507
+ def start # :nodoc:
508
+ super
509
+
510
+ io.puts "Run options: #{options[:args]}"
511
+ io.puts
512
+ io.puts "# Running:"
513
+ io.puts
514
+
515
+ self.sync = io.respond_to? :"sync=" # stupid emacs
516
+ self.old_sync, io.sync = io.sync, true if self.sync
517
+ end
518
+
519
+ def report # :nodoc:
520
+ super
521
+
522
+ io.sync = self.old_sync
523
+
524
+ io.puts unless options[:verbose] # finish the dots
525
+ io.puts
526
+ io.puts statistics
527
+ io.puts aggregated_results
528
+ io.puts summary
529
+ end
530
+
531
+ def statistics # :nodoc:
532
+ "Finished in %.6fs, %.4f runs/s, %.4f assertions/s." %
533
+ [total_time, count / total_time, assertions / total_time]
534
+ end
535
+
536
+ def aggregated_results # :nodoc:
537
+ filtered_results = results.dup
538
+ filtered_results.reject!(&:skipped?) unless options[:verbose]
539
+
540
+ filtered_results.each_with_index.map do |result, i|
541
+ "\n%3d) %s" % [i+1, result]
542
+ end.join("\n") + "\n"
543
+ end
544
+
545
+ def summary # :nodoc:
546
+ extra = ""
547
+
548
+ extra = "\n\nYou have skipped tests. Run with --verbose for details." if
549
+ results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
550
+
551
+ "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
552
+ [count, assertions, failures, errors, skips, extra]
553
+ end
554
+ end
555
+
556
+ ##
557
+ # Dispatch to multiple reporters as one.
558
+
559
+ class CompositeReporter < AbstractReporter
560
+ ##
561
+ # The list of reporters to dispatch to.
562
+
563
+ attr_accessor :reporters
564
+
565
+ def initialize *reporters # :nodoc:
566
+ self.reporters = reporters
567
+ end
568
+
569
+ ##
570
+ # Add another reporter to the mix.
571
+
572
+ def << reporter
573
+ self.reporters << reporter
574
+ end
575
+
576
+ def passed? # :nodoc:
577
+ self.reporters.all?(&:passed?)
578
+ end
579
+
580
+ def start # :nodoc:
581
+ self.reporters.each(&:start)
582
+ end
583
+
584
+ def record result # :nodoc:
585
+ self.reporters.each do |reporter|
586
+ reporter.record result
587
+ end
588
+ end
589
+
590
+ def report # :nodoc:
591
+ self.reporters.each(&:report)
592
+ end
593
+ end
594
+
595
+ ##
596
+ # Represents run failures.
597
+
598
+ class Assertion < Exception
599
+ def error # :nodoc:
600
+ self
601
+ end
602
+
603
+ ##
604
+ # Where was this run before an assertion was raised?
605
+
606
+ def location
607
+ last_before_assertion = ""
608
+ self.backtrace.reverse_each do |s|
609
+ break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
610
+ last_before_assertion = s
611
+ end
612
+ last_before_assertion.sub(/:in .*$/, "")
613
+ end
614
+
615
+ def result_code # :nodoc:
616
+ result_label[0, 1]
617
+ end
618
+
619
+ def result_label # :nodoc:
620
+ "Failure"
621
+ end
622
+ end
623
+
624
+ ##
625
+ # Assertion raised when skipping a run.
626
+
627
+ class Skip < Assertion
628
+ def result_label # :nodoc:
629
+ "Skipped"
630
+ end
631
+ end
632
+
633
+ ##
634
+ # Assertion wrapping an unexpected error that was raised during a run.
635
+
636
+ class UnexpectedError < Assertion
637
+ attr_accessor :exception # :nodoc:
638
+
639
+ def initialize exception # :nodoc:
640
+ super
641
+ self.exception = exception
642
+ end
643
+
644
+ def backtrace # :nodoc:
645
+ self.exception.backtrace
646
+ end
647
+
648
+ def error # :nodoc:
649
+ self.exception
650
+ end
651
+
652
+ def message # :nodoc:
653
+ bt = Minitest::filter_backtrace(self.backtrace).join "\n "
654
+ "#{self.exception.class}: #{self.exception.message}\n #{bt}"
655
+ end
656
+
657
+ def result_label # :nodoc:
658
+ "Error"
659
+ end
660
+ end
661
+
662
+ ##
663
+ # Provides a simple set of guards that you can use in your tests
664
+ # to skip execution if it is not applicable. These methods are
665
+ # mixed into Test as both instance and class methods so you
666
+ # can use them inside or outside of the test methods.
667
+ #
668
+ # def test_something_for_mri
669
+ # skip "bug 1234" if jruby?
670
+ # # ...
671
+ # end
672
+ #
673
+ # if windows? then
674
+ # # ... lots of test methods ...
675
+ # end
676
+
677
+ module Guard
678
+
679
+ ##
680
+ # Is this running on jruby?
681
+
682
+ def jruby? platform = RUBY_PLATFORM
683
+ "java" == platform
684
+ end
685
+
686
+ ##
687
+ # Is this running on maglev?
688
+
689
+ def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
690
+ "maglev" == platform
691
+ end
692
+
693
+ ##
694
+ # Is this running on mri?
695
+
696
+ def mri? platform = RUBY_DESCRIPTION
697
+ /^ruby/ =~ platform
698
+ end
699
+
700
+ ##
701
+ # Is this running on rubinius?
702
+
703
+ def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
704
+ "rbx" == platform
705
+ end
706
+
707
+ ##
708
+ # Is this running on windows?
709
+
710
+ def windows? platform = RUBY_PLATFORM
711
+ /mswin|mingw/ =~ platform
712
+ end
713
+ end
714
+
715
+ class BacktraceFilter # :nodoc:
716
+ def filter bt
717
+ return ["No backtrace"] unless bt
718
+
719
+ return bt.dup if $DEBUG
720
+
721
+ new_bt = bt.take_while { |line| line !~ /lib\/minitest/ }
722
+ new_bt = bt.select { |line| line !~ /lib\/minitest/ } if new_bt.empty?
723
+ new_bt = bt.dup if new_bt.empty?
724
+
725
+ new_bt
726
+ end
727
+ end
728
+
729
+ self.backtrace_filter = BacktraceFilter.new
730
+ end
731
+
732
+ require "minitest/test"
733
+ require "minitest/unit" unless defined?(MiniTest) # compatibility layer only