class Test::Runner

The Test::Runner class handles the execution of tests.

Constants

KNOWN_FORMATS

List of known report formats.

TODO: Could use finder gem to look these up, but that’s yet another dependency.

OPEN_ERRORS

Exceptions that are not caught by test runner.

Attributes

config[R]

Handle all configuration via the config instance.

observers[R]

Array of observers, typically this just contains the recorder and reporter instances.

recorder[R]

Record pass, fail, error and pending tests.

reporter[R]

The reporter to use for ouput.

Public Class Methods

new(config) { || ... } click to toggle source

New Runner.

@param [Config] config

Config instance.
# File lib/rubytest/runner.rb, line 51
def initialize(config) #:yield:
  @config = case config
    when Config then config
    when Hash   then Config.new(config)
    else Test.configuration(config)
  end

  @config.apply!  # apply lazy config block

  yield(@config) if block_given?

  @advice = Advice.new
end
run(config=nil) { || ... } click to toggle source

Run tests.

@param [Config,Hash,String,Symbol] config

Either a Config instance, a hash to construct a Config
instance with, or a name of a configuration profile.

@return [Boolean] Success of test run.

# File lib/rubytest/runner.rb, line 38
def self.run(config=nil, &config_proc) #:yield:
  runner = Runner.new(config, &config_proc)
  runner.run
end

Public Instance Methods

advice() click to toggle source

Instance of Advice is a special customizable observer.

# File lib/rubytest/runner.rb, line 92
def advice
  @advice
end
after(type, &block) click to toggle source

Define universal after advice. Can be used by mock libraries, for example to run mock verification.

# File lib/rubytest/runner.rb, line 103
def after(type, &block)
  advice.join_after(type, &block)
end
before(type, &block) click to toggle source

Define universal before advice.

# File lib/rubytest/runner.rb, line 97
def before(type, &block)
  advice.join_before(type, &block)
end
format() click to toggle source

Reporter format name, or name fragment, used to look up reporter class.

# File lib/rubytest/runner.rb, line 82
def format
  config.format
end
run() click to toggle source

Run test suite.

@return [Boolean]

That the tests ran without error or failure.
# File lib/rubytest/runner.rb, line 129
def run
  cd_chdir do
    Test::Config.load_path_setup if config.autopath?

    ignore_callers

    config.loadpath.flatten.each{ |path| $LOAD_PATH.unshift(path) }
    config.requires.flatten.each{ |file| require file }

    # Config before advice occurs after loadpath and require are
    # applied and before test files are required.
    config.before.call if config.before

    test_files.each do |test_file|
      require test_file
    end

    @reporter  = reporter_load(format)
    @recorder  = Recorder.new

    @observers = [advice, @recorder, @reporter]

    observers.each{ |o| o.begin_suite(suite) }
    run_thru(suite)
    observers.each{ |o| o.end_suite(suite) }

    config.after.call if config.after
  end

  recorder.success?
end
suite() click to toggle source

Test suite to run. This is a list of compliant test units and test cases.

# File lib/rubytest/runner.rb, line 69
def suite
  config.suite
end
test_files() click to toggle source

TODO: Cache or not?

# File lib/rubytest/runner.rb, line 76
def test_files
  #@test_files ||= resolve_test_files
  resolve_test_files
end
upon(type, &block) click to toggle source

Define universal upon advice.

See {Advice} for valid join-points.

# File lib/rubytest/runner.rb, line 110
def upon(type, &block)
  advice.join(type, &block)
end
verbose?() click to toggle source

Show extra details in reports.

# File lib/rubytest/runner.rb, line 87
def verbose?
  config.verbose?
end

Private Instance Methods

cd_chdir(&block) click to toggle source

Change to directory and run block.

@raise [Errno::ENOENT] If directory does not exist.

# File lib/rubytest/runner.rb, line 334
def cd_chdir(&block)
  if dir = config.chdir
    unless File.directory?(dir)
      raise Errno::ENOENT, "change directory doesn't exist -- `#{dir}'"
    end
    Dir.chdir(dir, &block)
  else
    block.call
  end
end
ignore_callers() click to toggle source

Add to $RUBY_IGNORE_CALLERS.

@todo Improve on this!

# File lib/rubytest/runner.rb, line 166
def ignore_callers
  ignore_path   = File.expand_path(File.join(__FILE__, '../../..'))
  ignore_regexp = Regexp.new(Regexp.escape(ignore_path))

  $RUBY_IGNORE_CALLERS ||= {}
  $RUBY_IGNORE_CALLERS << ignore_regexp
  $RUBY_IGNORE_CALLERS << /bin\/rubytest/
end
reporter_list() click to toggle source

Returns a list of available report types.

@return [Array<String>]

The names of available reporters.
# File lib/rubytest/runner.rb, line 310
def reporter_list
  return KNOWN_FORMATS.sort
  #list = Dir[File.dirname(__FILE__) + '/reporters/*.rb']
  #list = list.map{ |r| File.basename(r).chomp('.rb') }
  #list = list.reject{ |r| /^abstract/ =~ r }
  #list.sort
end
reporter_load(format) click to toggle source

Get a reporter instance be name fragment.

@return [Reporter::Abstract]

The test reporter instance.
# File lib/rubytest/runner.rb, line 283
def reporter_load(format)
  format = DEFAULT_REPORT_FORMAT unless format
  format = format.to_s.downcase
  name   = reporter_list.find{ |r| /^#{format}/ =~ r } || format

  begin
    require "rubytest/format/#{name}"
  rescue LoadError
    raise "mistyped or uninstalled report format" unless format
  end

  reporter = Test::Reporters.const_get(name.capitalize)
  reporter.new(self)
end
resolve_test_files() click to toggle source

Files can be globs and directories which need to be resolved to a list of files.

@return [Array<String>]

# File lib/rubytest/runner.rb, line 322
def resolve_test_files
  list = config.files.flatten
  list = list.map{ |f| Dir[f] }.flatten
  list = list.map{ |f| File.directory?(f) ? Dir[File.join(f, '**/*.rb')] : f }
  list = list.flatten.uniq
  list = list.map{ |f| File.expand_path(f) } 
  list
end
run_case(tcase) click to toggle source

Run a test case.

# File lib/rubytest/runner.rb, line 190
def run_case(tcase)
  if tcase.respond_to?(:skip?) && (reason = tcase.skip?)
    return observers.each{ |o| o.skip_case(tcase, reason) }
  end

  observers.each{ |o| o.begin_case(tcase) }

  if tcase.respond_to?(:call)
    tcase.call do
      run_thru( select(tcase) )
    end
  else
    run_thru( select(tcase) )
  end

  observers.each{ |o| o.end_case(tcase) }
end
run_test(test) click to toggle source

Run a test.

@param [Object] test

The test to run, must repsond to #call.
# File lib/rubytest/runner.rb, line 213
def run_test(test)
  if test.respond_to?(:skip?) && (reason = test.skip?)
    return observers.each{ |o| o.skip_test(test, reason) }
  end

  observers.each{ |o| o.begin_test(test) }
  begin
    success = test.call
    if config.hard? && !success  # TODO: separate run_test method to speed things up?
      raise Assertion, "failure of #{test}"
    else
      observers.each{ |o| o.pass(test) }
    end
  rescue *OPEN_ERRORS => exception
    raise exception
  rescue NotImplementedError => exception
    #if exception.assertion?  # TODO: May require assertion? for todo in future
      observers.each{ |o| o.todo(test, exception) }
    #else
    #  observers.each{ |o| o.error(test, exception) }
    #end
  rescue Exception => exception
    if exception.assertion?
      observers.each{ |o| o.fail(test, exception) }
    else
      observers.each{ |o| o.error(test, exception) }
    end
  end
  observers.each{ |o| o.end_test(test) }
end
run_thru(list) click to toggle source
# File lib/rubytest/runner.rb, line 176
def run_thru(list)
  list.each do |t|
    if t.respond_to?(:each)
      run_case(t)
    elsif t.respond_to?(:call)
      run_test(t)
    else
      #run_note(t) ?
    end
  end
end
select(cases) click to toggle source

Filter cases based on selection criteria.

@return [Array] selected test cases

# File lib/rubytest/runner.rb, line 251
def select(cases)
  selected = []
  if cases.respond_to?(:ordered?) && cases.ordered?
    cases.each do |tc|
      selected << tc
    end
  else
    cases.each do |tc|
      next if tc.respond_to?(:skip?) && tc.skip?
      next if !config.match.empty? && !config.match.any?{ |m| m =~ tc.to_s }

      if !config.units.empty?
        next unless tc.respond_to?(:unit)
        next unless config.units.find{ |u| tc.unit.start_with?(u) }
      end

      if !config.tags.empty?
        next unless tc.respond_to?(:tags)
        tc_tags = [tc.tags].flatten.map{ |t| t.to_s }
        next if (config.tags & tc_tags).empty?
      end

      selected << tc
    end
  end
  selected
end