lib/minitest.rb in spec-5.0.19 vs lib/minitest.rb in spec-5.3.3
- old
+ new
@@ -1,22 +1,30 @@
require "optparse"
-require 'chronic_duration'
+require "thread"
+require "mutex_m"
+require "minitest/parallel"
##
# :include: README.txt
module Minitest
- VERSION = '5.0.19' # :nodoc:
- DATE = '2013-08-19' # :nodoc:
+ VERSION = '5.3.3' # :nodoc:
+ DATE = '2014-04-21' # :nodoc:
@@installed_at_exit ||= false
@@after_run = []
@extensions = []
mc = (class << self; self; end)
##
+ # Parallel test executor
+
+ mc.send :attr_accessor, :parallel_executor
+ self.parallel_executor = Parallel::Executor.new((ENV['N'] || 2).to_i)
+
+ ##
# Filter object for backtraces.
mc.send :attr_accessor, :backtrace_filter
##
@@ -34,11 +42,11 @@
##
# Registers Minitest to run at process exit
def self.autorun
at_exit {
- next if $! and not $!.kind_of? SystemExit
+ next if $! and not ($!.kind_of? SystemExit and $!.success?)
exit_code = nil
at_exit {
@@after_run.reverse_each(&:call)
@@ -70,10 +78,12 @@
def self.load_plugins # :nodoc:
return unless self.extensions.empty?
seen = {}
+ require "rubygems" unless defined? Gem
+
Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
name = File.basename plugin_path, "_plugin.rb"
next if seen[name]
seen[name] = true
@@ -90,31 +100,34 @@
#
# The overall structure of a run looks like this:
#
# Minitest.autorun
# Minitest.run(args)
- # __run(reporter, options)
+ # Minitest.__run(reporter, options)
# Runnable.runnables.each
# runnable.run(reporter, options)
# self.runnable_methods.each
- # self.new(runnable_method).run
+ # self.run_one_method(self, runnable_method, reporter)
+ # Minitest.run_one_method(klass, runnable_method, reporter)
+ # klass.new(runnable_method).run
def self.run args = []
self.load_plugins
options = process_args args
reporter = CompositeReporter.new
- reporter << ProgressReporter.new(options[:io], options)
reporter << SummaryReporter.new(options[:io], options)
+ reporter << ProgressReporter.new(options[:io], options)
self.reporter = reporter # this makes it available to plugins
self.init_plugins options
self.reporter = nil # runnables shouldn't depend on the reporter, ever
reporter.start
__run reporter, options
+ self.parallel_executor.shutdown
reporter.report
reporter.passed?
end
@@ -124,92 +137,26 @@
#
# NOTE: this method is redefined in parallel_each.rb, which is
# loaded if a Runnable calls parallelize_me!.
def self.__run reporter, options
- Runnable.runnables.each do |runnable|
- runnable.run reporter, options
- end
- end
+ suites = Runnable.runnables
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
- ##
- # Trace file source to :io (default $stdout)
- #
- # spec_opts = {}
- #
- # @param :trace [Array<String>] the files to trace
- # @param :io [IO] io to print to
- def self.trace_specs spec_opts
- targets = []
- files = {}
- last_file = ''
- last_line = -1
-
- files_to_trace = spec_opts.fetch(:trace, []);
- io = spec_opts.fetch(:io, $stdout)
- color = spec_opts.fetch(:color, "\e[32m") # ANSI.green default
- # target only existing readable files
- files_to_trace.each do |f|
- targets.push(File.expand_path(f)) if File.exists?(f) && File.readable?(f)
- end
- return if targets.empty?
-
- set_trace_func(lambda do |event, file, line, id, binding, classname|
- return unless targets.include? file
-
- # never repeat a line
- return if file == last_file && line == last_line
-
- file_sym = file.intern
- files[file_sym] = IO.readlines(file) if files[file_sym].nil?
- lines = files[file_sym]
-
- # arrays are 0 indexed and line numbers start at one.
- io.print color if color # ANSI code
- io.puts lines[ line - 1]
- io.print "\e[0m" if color # ANSI.clear
-
- last_file = file
- last_line = line
-
- end)
+ # If we run the parallel tests before the serial tests, the parallel tests
+ # could run in parallel with the serial tests. This would be bad because
+ # the serial tests won't lock around Reporter#record. Run the serial tests
+ # first, so that after they complete, the parallel tests will lock when
+ # recording results.
+ serial.map { |suite| suite.run reporter, options } +
+ parallel.map { |suite| suite.run reporter, options }
end
- def self.on_exit exit_code
- @@after_run.reverse_each(&:call)
- exit exit_code || false
- end
-
- ##
- # Run specs. Does not print dots (ProgressReporter)
- #
- # spec_opts
- # @param :io [Array<String>] defaults to $stdout
- # @param :trace [Array<String>] files to trace
-
- def self.run_specs spec_opts={}
- options = { :io => spec_opts.fetch(:io, $stdout) }
- reporter = Minitest::CompositeReporter.new
- reporter << Minitest::SummaryReporter.new(options[:io], options)
- reporter.start
-
- at_exit { on_exit reporter.passed? }
- # exit on ctrl+c to trigger at_exit
- trap('SIGINT') { exit }
-
- trace_specs spec_opts
-
- begin
- Minitest.__run reporter, options
- reporter.reporters.each { |r| r.report }
- rescue Minitest::Runnable::ExitAfterFirstFail
- # Minitest calls .report on exception
- end
- end
-
def self.process_args args = [] # :nodoc:
- options = { :io => $stdout }
+ options = {
+ :io => $stdout,
+ }
orig_args = args.dup
OptionParser.new do |opts|
opts.banner = "minitest options:"
opts.version = Minitest::VERSION
@@ -321,96 +268,33 @@
@@runnables = []
end
reset
- class ExitAfterFirstFail < RuntimeError; end
-
- def self.check_failures result, reporter
- # skip is not a failure.
- true_fails = result.failures.reject { |obj| obj.class == Minitest::Skip }
- if !true_fails.empty?
- begin
- reporter.reporters.each { |r| r.report }
- ensure
- raise ExitAfterFirstFail
- end
- end
- end
-
##
# Responsible for running all runnable methods in a given class,
# each in its own instance. Each instance is passed to the
# reporter to record.
def self.run reporter, options = {}
- io = options.fetch :io, $stdout
filter = options[:filter] || '/./'
filter = Regexp.new $1 if filter =~ /\/(.*)\//
filtered_methods = self.runnable_methods.find_all { |m|
filter === m || filter === "#{self}##{m}"
}
- begin
- # before_first
- method1 = self.new(filtered_methods.first)
- # run method and capture exceptions.
- method1.capture_exceptions do
- method1.before_first_method
+ with_info_handler reporter do
+ filtered_methods.each do |method_name|
+ run_one_method self, method_name, reporter
end
- # save exceptions to reporter and check for failures
- with_info_handler reporter do
- # only record if failures not empty, otherwise
- # the count (runs) are messed up. each call to .record
- # increases count by one.
- if !method1.failures.empty?
- reporter.record method1
- check_failures method1, reporter
- end
-
- # run the other methods
- filtered_methods.each do |method_name|
- method = self.new(method_name)
- matched_name = method_name.match /test_(\d+)_/
- if matched_name
- test_number = matched_name[1].to_i
- test_name = method_name.split(/_\d+_/).last
- file_path, line_number = method.method(method_name).source_location
- # /5/4/3/2/1/test.rb => 2/1/test.rb
- file_path = file_path.split(File::SEPARATOR).reject(&:empty?)
- file_path = (file_path.length >= 3 ? file_path[-3..-1] :
- file_path).join(File::SEPARATOR)
- # 36 = cyan, 0 = clear
- test_output_title = "\e[36m#{test_name} | #{test_number} |" +
- "#{file_path}:#{line_number}\e[0m"
- io.puts test_output_title
- end
- result = method.run
- raise "#{self}#run _must_ return self" unless self === result
- reporter.record result
- check_failures result, reporter
- end
- end
- ensure # ensure after last runs
- # after_last
- # init method1 again
- method1 = self.new(filtered_methods.first)
- method1.capture_exceptions do
- method1.after_last_method
- end
- with_info_handler reporter do
- if !method1.failures.empty?
- reporter.record method1
- check_failures method1, reporter
- end
- end
end
end
- def before_first_method; end
- def after_last_method; end
+ def self.run_one_method klass, method_name, reporter
+ reporter.record Minitest.run_one_method(klass, method_name)
+ end
def self.with_info_handler reporter, &block # :nodoc:
handler = lambda do
unless reporter.passed? then
warn "Current results:"
@@ -505,10 +389,12 @@
##
# Defines the API for Reporters. Subclass this and override whatever
# you want. Go nuts.
class AbstractReporter
+ include Mutex_m
+
##
# Starts reporting on the run.
def start
end
@@ -544,10 +430,11 @@
# Command-line options for this run.
attr_accessor :options
def initialize io = $stdout, options = {} # :nodoc:
+ super()
self.io = io
self.options = options
end
end
@@ -643,10 +530,15 @@
# :startdoc:
def start # :nodoc:
super
+ io.puts "Run options: #{options[:args]}"
+ io.puts
+ io.puts "# Running:"
+ io.puts
+
self.sync = io.respond_to? :"sync=" # stupid emacs
self.old_sync, io.sync = io.sync, true if self.sync
end
def report # :nodoc:
@@ -660,11 +552,12 @@
io.puts aggregated_results
io.puts summary
end
def statistics # :nodoc:
- "Finished in #{ChronicDuration.output(total_time.round) || '0s'}"
+ "Finished in %.6fs, %.4f runs/s, %.4f assertions/s." %
+ [total_time, count / total_time, assertions / total_time]
end
def aggregated_results # :nodoc:
filtered_results = results.dup
filtered_results.reject!(&:skipped?) unless options[:verbose]
@@ -672,10 +565,12 @@
filtered_results.each_with_index.map do |result, i|
"\n%3d) %s" % [i+1, result]
end.join("\n") + "\n"
end
+ alias to_s aggregated_results
+
def summary # :nodoc:
extra = ""
extra = "\n\nYou have skipped tests. Run with --verbose for details." if
results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
@@ -693,10 +588,11 @@
# The list of reporters to dispatch to.
attr_accessor :reporters
def initialize *reporters # :nodoc:
+ super()
self.reporters = reporters
end
##
# Add another reporter to the mix.
@@ -857,9 +753,15 @@
new_bt
end
end
self.backtrace_filter = BacktraceFilter.new
+
+ def self.run_one_method klass, method_name # :nodoc:
+ result = klass.new(method_name).run
+ raise "#{klass}#run _must_ return self" unless klass === result
+ result
+ end
end
require "minitest/test"
require "minitest/unit" unless defined?(MiniTest) # compatibility layer only