mirror of https://github.com/KLayout/klayout.git
813 lines
22 KiB
Ruby
813 lines
22 KiB
Ruby
|
|
#--
|
||
|
|
#
|
||
|
|
# Author:: Nathaniel Talbott.
|
||
|
|
# Copyright::
|
||
|
|
# * Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
|
||
|
|
# * Copyright (c) 2008-2012 Kouhei Sutou <tt><kou@clear-code.com></tt>
|
||
|
|
# License:: Ruby license.
|
||
|
|
|
||
|
|
require 'test/unit/attribute'
|
||
|
|
require 'test/unit/fixture'
|
||
|
|
require 'test/unit/exception-handler'
|
||
|
|
require 'test/unit/assertions'
|
||
|
|
require 'test/unit/failure'
|
||
|
|
require 'test/unit/error'
|
||
|
|
require 'test/unit/pending'
|
||
|
|
require 'test/unit/omission'
|
||
|
|
require 'test/unit/notification'
|
||
|
|
require 'test/unit/priority'
|
||
|
|
require 'test/unit/data'
|
||
|
|
require 'test/unit/testsuite'
|
||
|
|
require 'test/unit/test-suite-creator'
|
||
|
|
require 'test/unit/assertion-failed-error'
|
||
|
|
require 'test/unit/auto-runner-loader'
|
||
|
|
require 'test/unit/util/backtracefilter'
|
||
|
|
require 'test/unit/util/output'
|
||
|
|
require 'test/unit/util/method-owner-finder'
|
||
|
|
|
||
|
|
module Test
|
||
|
|
module Unit
|
||
|
|
|
||
|
|
# Ties everything together. If you subclass and add your own
|
||
|
|
# test methods, it takes care of making them into tests and
|
||
|
|
# wrapping those tests into a suite. It also does the
|
||
|
|
# nitty-gritty of actually running an individual test and
|
||
|
|
# collecting its results into a Test::Unit::TestResult object.
|
||
|
|
#
|
||
|
|
# You can run two hooks before/after a TestCase run.
|
||
|
|
#
|
||
|
|
# Example:
|
||
|
|
# class TestMyClass < Test::Unit::TestCase
|
||
|
|
# class << self
|
||
|
|
# def startup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def shutdown
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def setup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def cleanup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def teardown
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_method1
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_method2
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Here is a call order:
|
||
|
|
# * startup
|
||
|
|
# * setup
|
||
|
|
# * test_my_method1
|
||
|
|
# * cleanup
|
||
|
|
# * teardown
|
||
|
|
# * setup
|
||
|
|
# * test_my_method2
|
||
|
|
# * cleanup
|
||
|
|
# * teardown
|
||
|
|
# * shutdown
|
||
|
|
class TestCase
|
||
|
|
include Attribute
|
||
|
|
include Fixture
|
||
|
|
include ExceptionHandler
|
||
|
|
include ErrorHandler
|
||
|
|
include FailureHandler
|
||
|
|
include TestCasePendingSupport
|
||
|
|
include TestCaseOmissionSupport
|
||
|
|
include TestCaseNotificationSupport
|
||
|
|
include Priority
|
||
|
|
include Data
|
||
|
|
include Assertions
|
||
|
|
include Util::BacktraceFilter
|
||
|
|
include Util::Output
|
||
|
|
|
||
|
|
STARTED = name + "::STARTED" # :nodoc:
|
||
|
|
FINISHED = name + "::FINISHED" # :nodoc:
|
||
|
|
STARTED_OBJECT = name + "::STARTED::OBJECT" # :nodoc:
|
||
|
|
FINISHED_OBJECT = name + "::FINISHED::OBJECT" # :nodoc:
|
||
|
|
|
||
|
|
DESCENDANTS = [] # :nodoc:
|
||
|
|
AVAILABLE_ORDERS = [:alphabetic, :random, :defined] # :nodoc:
|
||
|
|
|
||
|
|
class << self
|
||
|
|
def inherited(sub_class) # :nodoc:
|
||
|
|
DESCENDANTS << sub_class
|
||
|
|
super
|
||
|
|
end
|
||
|
|
|
||
|
|
@@added_method_names = {}
|
||
|
|
def method_added(name) # :nodoc:
|
||
|
|
super
|
||
|
|
added_method_names = (@@added_method_names[self] ||= {})
|
||
|
|
stringified_name = name.to_s
|
||
|
|
if added_method_names.key?(stringified_name)
|
||
|
|
attribute(:redefined, {:backtrace => caller}, {}, stringified_name)
|
||
|
|
end
|
||
|
|
source_location = find_attribute(stringified_name, :source_location)
|
||
|
|
if source_location
|
||
|
|
path, line = source_location
|
||
|
|
elsif respond_to?(:caller_locations, true)
|
||
|
|
location = caller_locations(1, 1)[0]
|
||
|
|
path = location.absolute_path || location.path
|
||
|
|
line = location.lineno
|
||
|
|
else
|
||
|
|
# TODO: Remove me when Ruby 1.9 support is dropped
|
||
|
|
path, line, = caller[0].split(/:(\d+)/, 2)
|
||
|
|
line = line.to_i if line
|
||
|
|
end
|
||
|
|
method_locations << {
|
||
|
|
:method_name => stringified_name,
|
||
|
|
:path => File.expand_path(path),
|
||
|
|
:line => line,
|
||
|
|
}
|
||
|
|
added_method_names[stringified_name] = true
|
||
|
|
AutoRunnerLoader.check(self, stringified_name)
|
||
|
|
end
|
||
|
|
|
||
|
|
def added_method_names # :nodoc:
|
||
|
|
(@@added_method_names[self] ||= {}).keys
|
||
|
|
end
|
||
|
|
|
||
|
|
# Rolls up all of the test* methods in the fixture into
|
||
|
|
# one suite, creating a new instance of the fixture for
|
||
|
|
# each method.
|
||
|
|
def suite
|
||
|
|
suite_creator = TestSuiteCreator.new(self)
|
||
|
|
suite_creator.create
|
||
|
|
end
|
||
|
|
|
||
|
|
# Called before every test case runs. Can be used
|
||
|
|
# to set up fixture information used in test case
|
||
|
|
# scope.
|
||
|
|
#
|
||
|
|
# Here is an example test case:
|
||
|
|
# class TestMyClass < Test::Unit::TestCase
|
||
|
|
# class << self
|
||
|
|
# def startup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def setup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class1
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class2
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Here is a call order:
|
||
|
|
# * startup
|
||
|
|
# * setup
|
||
|
|
# * test_my_class1 (or test_my_class2)
|
||
|
|
# * setup
|
||
|
|
# * test_my_class2 (or test_my_class1)
|
||
|
|
#
|
||
|
|
# Note that you should not assume test order. Tests
|
||
|
|
# should be worked in any order.
|
||
|
|
def startup
|
||
|
|
end
|
||
|
|
|
||
|
|
# Called after every test case runs. Can be used to tear
|
||
|
|
# down fixture information used in test case scope.
|
||
|
|
#
|
||
|
|
# Here is an example test case:
|
||
|
|
# class TestMyClass < Test::Unit::TestCase
|
||
|
|
# class << self
|
||
|
|
# def shutdown
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def teardown
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class1
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class2
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Here is a call order:
|
||
|
|
# * test_my_class1 (or test_my_class2)
|
||
|
|
# * teardown
|
||
|
|
# * test_my_class2 (or test_my_class1)
|
||
|
|
# * teardown
|
||
|
|
# * shutdown
|
||
|
|
#
|
||
|
|
# Note that you should not assume test order. Tests
|
||
|
|
# should be worked in any order.
|
||
|
|
def shutdown
|
||
|
|
end
|
||
|
|
|
||
|
|
@@test_orders = {}
|
||
|
|
|
||
|
|
# Returns the current test order. This returns
|
||
|
|
# +:alphabetic+ by default.
|
||
|
|
def test_order
|
||
|
|
@@test_orders[self] || AVAILABLE_ORDERS.first
|
||
|
|
end
|
||
|
|
|
||
|
|
# Sets the current test order.
|
||
|
|
#
|
||
|
|
# Here are the available _order_:
|
||
|
|
# [:alphabetic]
|
||
|
|
# Default. Tests are sorted in alphabetic order.
|
||
|
|
# [:random]
|
||
|
|
# Tests are sorted in random order.
|
||
|
|
# [:defined]
|
||
|
|
# Tests are sorted in defined order.
|
||
|
|
def test_order=(order)
|
||
|
|
@@test_orders[self] = order
|
||
|
|
end
|
||
|
|
|
||
|
|
# Defines a test in declarative syntax or marks
|
||
|
|
# following method as a test method.
|
||
|
|
#
|
||
|
|
# In declarative syntax usage, the following two
|
||
|
|
# test definitions are the almost same:
|
||
|
|
#
|
||
|
|
# description "register user"
|
||
|
|
# def test_register_user
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# test "register user" do
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# In test method mark usage, the "my_test_method" is
|
||
|
|
# treated as a test method:
|
||
|
|
#
|
||
|
|
# test
|
||
|
|
# def my_test_method
|
||
|
|
# assert_equal("call me", ...)
|
||
|
|
# end
|
||
|
|
def test(*test_description_or_targets, &block)
|
||
|
|
if block_given?
|
||
|
|
test_description = test_description_or_targets.first
|
||
|
|
if test_description.nil?
|
||
|
|
raise ArgumentError, "test description is missing"
|
||
|
|
end
|
||
|
|
n_arguments = test_description_or_targets.size
|
||
|
|
if n_arguments > 1
|
||
|
|
message = "wrong number of arguments (#{n_arguments} for 1)"
|
||
|
|
raise ArgumentError, message
|
||
|
|
end
|
||
|
|
method_name = "test: #{test_description}"
|
||
|
|
description(test_description, method_name)
|
||
|
|
attribute(:test, true, {}, method_name)
|
||
|
|
if block.respond_to?(:source_location)
|
||
|
|
attribute(:source_location, block.source_location, {}, method_name)
|
||
|
|
end
|
||
|
|
define_method(method_name, &block)
|
||
|
|
else
|
||
|
|
targets = test_description_or_targets
|
||
|
|
attribute(:test, true, {}, *targets)
|
||
|
|
targets.each do |target|
|
||
|
|
AutoRunnerLoader.check(self, target)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
# Describes a test.
|
||
|
|
#
|
||
|
|
# The following example associates "register a
|
||
|
|
# normal user" description with "test_register"
|
||
|
|
# test.
|
||
|
|
#
|
||
|
|
# description "register a normal user"
|
||
|
|
# def test_register
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
def description(value, target=nil)
|
||
|
|
targets = [target].compact
|
||
|
|
attribute(:description, value, {}, *targets)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Defines a sub test case.
|
||
|
|
#
|
||
|
|
# This is a syntax sugar. The both of the following codes are
|
||
|
|
# the same in meaning:
|
||
|
|
#
|
||
|
|
# Standard:
|
||
|
|
# class TestParent < Test::UnitTestCase
|
||
|
|
# class TestChild < self
|
||
|
|
# def test_in_child
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Syntax sugar:
|
||
|
|
# class TestParent < Test::UnitTestCase
|
||
|
|
# sub_test_case("TestChild") do
|
||
|
|
# def test_in_child
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# The difference of them are the following:
|
||
|
|
#
|
||
|
|
# * Test case created by {sub_test_case} is an anonymous class.
|
||
|
|
# So you can't refer the test case by name.
|
||
|
|
# * The class name of class style must follow
|
||
|
|
# constant naming rule in Ruby. But the name of test case
|
||
|
|
# created by {sub_test_case} doesn't need to follow the rule.
|
||
|
|
# For example, you can use a space in name such as "child test".
|
||
|
|
#
|
||
|
|
# @param name [String] The name of newly created sub test case.
|
||
|
|
# @yield
|
||
|
|
# The block is evaluated under the newly created sub test
|
||
|
|
# case class context.
|
||
|
|
# @return [Test::Unit::TestCase] Created sub test case class.
|
||
|
|
def sub_test_case(name, &block)
|
||
|
|
parent_test_case = self
|
||
|
|
sub_test_case = Class.new(self) do
|
||
|
|
singleton_class = class << self; self; end
|
||
|
|
singleton_class.__send__(:define_method, :name) do
|
||
|
|
[parent_test_case.name, name].compact.join("::")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
sub_test_case.class_eval(&block)
|
||
|
|
sub_test_case
|
||
|
|
end
|
||
|
|
|
||
|
|
# Checks whether a test that is matched the query is
|
||
|
|
# defined.
|
||
|
|
#
|
||
|
|
# @option query [String] :path (nil)
|
||
|
|
# the path where a test is defined in.
|
||
|
|
# @option query [Numeric] :line (nil)
|
||
|
|
# the line number where a test is defined at.
|
||
|
|
# @option query [String] :method_name (nil)
|
||
|
|
# the method name for a test.
|
||
|
|
def test_defined?(query)
|
||
|
|
query_path = query[:path]
|
||
|
|
query_line = query[:line]
|
||
|
|
query_method_name = query[:method_name]
|
||
|
|
|
||
|
|
available_locations = target_method_locations(query_path)
|
||
|
|
if query_line
|
||
|
|
available_locations = available_locations.sort_by do |location|
|
||
|
|
-location[:line]
|
||
|
|
end
|
||
|
|
available_location = available_locations.find do |location|
|
||
|
|
query_line >= location[:line]
|
||
|
|
end
|
||
|
|
return false if available_location.nil?
|
||
|
|
return false if available_location[:test_case] != self
|
||
|
|
available_locations = [available_location]
|
||
|
|
end
|
||
|
|
if query_method_name
|
||
|
|
available_location = available_locations.find do |location|
|
||
|
|
query_method_name == location[:method_name]
|
||
|
|
end
|
||
|
|
return false if available_location.nil?
|
||
|
|
available_locations = [available_location]
|
||
|
|
end
|
||
|
|
|
||
|
|
not available_locations.empty?
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
# @private
|
||
|
|
@@method_locations = {}
|
||
|
|
# @private
|
||
|
|
def method_locations
|
||
|
|
@@method_locations[self] ||= []
|
||
|
|
end
|
||
|
|
|
||
|
|
# @private
|
||
|
|
def target_method_locations(path)
|
||
|
|
if path.nil?
|
||
|
|
self_location = method_locations.first
|
||
|
|
path = self_location[:path] if self_location
|
||
|
|
end
|
||
|
|
return [] if path.nil?
|
||
|
|
|
||
|
|
target_locations = []
|
||
|
|
@@method_locations.each do |test_case, locations|
|
||
|
|
locations.each do |location|
|
||
|
|
absolete_path = File.expand_path(path)
|
||
|
|
location_path = location[:path]
|
||
|
|
location_basename = File.basename(location_path)
|
||
|
|
if location_path == absolete_path or location_basename == path
|
||
|
|
target_locations << location.merge(:test_case => test_case)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
target_locations
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
attr_reader :method_name
|
||
|
|
|
||
|
|
# Creates a new instance of the fixture for running the
|
||
|
|
# test represented by test_method_name.
|
||
|
|
def initialize(test_method_name)
|
||
|
|
@method_name = test_method_name
|
||
|
|
@internal_data = InternalData.new
|
||
|
|
end
|
||
|
|
|
||
|
|
# Assigns test data to the test. It is used in internal.
|
||
|
|
def assign_test_data(label, data) # :nodoc:
|
||
|
|
@internal_data.assign_test_data(label, data)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns the test is valid test. It is used in internal.
|
||
|
|
def valid? # :nodoc:
|
||
|
|
return false unless respond_to?(@method_name)
|
||
|
|
test_method = method(@method_name)
|
||
|
|
if @internal_data.have_test_data?
|
||
|
|
return false unless test_method.arity == 1
|
||
|
|
else
|
||
|
|
return false unless test_method.arity <= 0
|
||
|
|
end
|
||
|
|
owner = Util::MethodOwnerFinder.find(self, @method_name)
|
||
|
|
if owner.class != Module and self.class != owner
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
true
|
||
|
|
end
|
||
|
|
|
||
|
|
# Runs the individual test method represented by this
|
||
|
|
# instance of the fixture, collecting statistics, failures
|
||
|
|
# and errors in result.
|
||
|
|
def run(result)
|
||
|
|
begin
|
||
|
|
@_result = result
|
||
|
|
@internal_data.test_started
|
||
|
|
yield(STARTED, name)
|
||
|
|
yield(STARTED_OBJECT, self)
|
||
|
|
processed_exception_in_setup = false
|
||
|
|
begin
|
||
|
|
tag = eval(":tag_#{self.object_id}")
|
||
|
|
catch(tag) do
|
||
|
|
run_setup do
|
||
|
|
begin
|
||
|
|
run_test
|
||
|
|
run_cleanup
|
||
|
|
add_pass
|
||
|
|
rescue Exception
|
||
|
|
@internal_data.interrupted
|
||
|
|
unless handle_exception($!)
|
||
|
|
processed_exception_in_setup = true
|
||
|
|
raise
|
||
|
|
end
|
||
|
|
throw(tag)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
rescue Exception
|
||
|
|
if processed_exception_in_setup
|
||
|
|
raise
|
||
|
|
else
|
||
|
|
@internal_data.interrupted
|
||
|
|
raise unless handle_exception($!)
|
||
|
|
end
|
||
|
|
ensure
|
||
|
|
begin
|
||
|
|
run_teardown
|
||
|
|
rescue Exception
|
||
|
|
raise unless handle_exception($!)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
@internal_data.test_finished
|
||
|
|
result.add_run
|
||
|
|
yield(FINISHED, name)
|
||
|
|
yield(FINISHED_OBJECT, self)
|
||
|
|
ensure
|
||
|
|
# @_result = nil # For test-spec's after_all :<
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
# Called before every test method runs. Can be used
|
||
|
|
# to set up fixture information.
|
||
|
|
#
|
||
|
|
# You can add additional setup tasks by the following
|
||
|
|
# code:
|
||
|
|
# class TestMyClass < Test::Unit::TestCase
|
||
|
|
# def setup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# setup
|
||
|
|
# def my_setup1
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# setup do
|
||
|
|
# ... # setup callback1
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# setup
|
||
|
|
# def my_setup2
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# setup do
|
||
|
|
# ... # setup callback2
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Here is a call order:
|
||
|
|
# * setup
|
||
|
|
# * my_setup1
|
||
|
|
# * setup callback1
|
||
|
|
# * my_setup2
|
||
|
|
# * setup callback2
|
||
|
|
# * test_my_class
|
||
|
|
def setup
|
||
|
|
end
|
||
|
|
|
||
|
|
# Called after every test method runs but the test
|
||
|
|
# method isn't marked as 'passed'. Can be used to
|
||
|
|
# clean up and/or verify tested condition.
|
||
|
|
# e.g. Can be used to verify mock.
|
||
|
|
#
|
||
|
|
# You can add additional cleanup tasks by the following
|
||
|
|
# code:
|
||
|
|
# class TestMyClass < Test::Unit::TestCase
|
||
|
|
# def cleanup
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# cleanup
|
||
|
|
# def my_cleanup1
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# cleanup do
|
||
|
|
# ... # cleanup callback1
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# cleanup
|
||
|
|
# def my_cleanup2
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# cleanup do
|
||
|
|
# ... # cleanup callback2
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Here is a call order:
|
||
|
|
# * test_my_class
|
||
|
|
# * cleanup callback2
|
||
|
|
# * my_cleanup2
|
||
|
|
# * cleanup callback1
|
||
|
|
# * my_cleanup1
|
||
|
|
# * cleanup
|
||
|
|
def cleanup
|
||
|
|
end
|
||
|
|
|
||
|
|
# Called after every test method runs. Can be used to tear
|
||
|
|
# down fixture information.
|
||
|
|
#
|
||
|
|
# You can add additional teardown tasks by the following
|
||
|
|
# code:
|
||
|
|
# class TestMyClass < Test::Unit::TestCase
|
||
|
|
# def teardown
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# teardown
|
||
|
|
# def my_teardown1
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# teardown do
|
||
|
|
# ... # teardown callback1
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# teardown
|
||
|
|
# def my_teardown2
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# teardown do
|
||
|
|
# ... # teardown callback2
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# def test_my_class
|
||
|
|
# ...
|
||
|
|
# end
|
||
|
|
# end
|
||
|
|
#
|
||
|
|
# Here is a call order:
|
||
|
|
# * test_my_class
|
||
|
|
# * teardown callback2
|
||
|
|
# * my_teardown2
|
||
|
|
# * teardown callback1
|
||
|
|
# * my_teardown1
|
||
|
|
# * teardown
|
||
|
|
def teardown
|
||
|
|
end
|
||
|
|
|
||
|
|
def default_test
|
||
|
|
flunk("No tests were specified")
|
||
|
|
end
|
||
|
|
|
||
|
|
def size
|
||
|
|
1
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns a label of test data for the test. If the
|
||
|
|
# test isn't associated with any test data, it returns
|
||
|
|
# +nil+.
|
||
|
|
def data_label
|
||
|
|
@internal_data.test_data_label
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns a human-readable name for the specific test that
|
||
|
|
# this instance of TestCase represents.
|
||
|
|
def name
|
||
|
|
if @internal_data.have_test_data?
|
||
|
|
"#{@method_name}[#{data_label}](#{self.class.name})"
|
||
|
|
else
|
||
|
|
"#{@method_name}(#{self.class.name})"
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns a description for the test. A description
|
||
|
|
# will be associated by Test::Unit::TestCase.test or
|
||
|
|
# Test::Unit::TestCase.description.
|
||
|
|
#
|
||
|
|
# Returns a name for the test for no description test.
|
||
|
|
def description
|
||
|
|
self[:description] || name
|
||
|
|
end
|
||
|
|
|
||
|
|
# Overridden to return #name.
|
||
|
|
def to_s
|
||
|
|
name
|
||
|
|
end
|
||
|
|
|
||
|
|
# It's handy to be able to compare TestCase instances.
|
||
|
|
def ==(other)
|
||
|
|
return false unless other.kind_of?(self.class)
|
||
|
|
return false unless @method_name == other.method_name
|
||
|
|
return false unless data_label == other.data_label
|
||
|
|
self.class == other.class
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns a Time at the test was started.
|
||
|
|
def start_time
|
||
|
|
@internal_data.start_time
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns elapsed time for the test was ran.
|
||
|
|
def elapsed_time
|
||
|
|
@internal_data.elapsed_time
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns whether the test is interrupted.
|
||
|
|
def interrupted?
|
||
|
|
@internal_data.interrupted?
|
||
|
|
end
|
||
|
|
|
||
|
|
# Returns whether this individual test passed or
|
||
|
|
# not. Primarily for use in teardown so that artifacts
|
||
|
|
# can be left behind if the test fails.
|
||
|
|
def passed?
|
||
|
|
@internal_data.passed?
|
||
|
|
end
|
||
|
|
|
||
|
|
# Notify that a problem is occurred in the test. It means that
|
||
|
|
# the test is a failed test. If any failed tests exist in test
|
||
|
|
# suites, the test process exits with failure exit status.
|
||
|
|
#
|
||
|
|
# This is a public API for developers who extend test-unit.
|
||
|
|
#
|
||
|
|
# @return [void]
|
||
|
|
def problem_occurred
|
||
|
|
@internal_data.problem_occurred
|
||
|
|
end
|
||
|
|
|
||
|
|
# Notify that the test is passed. Normally, it is not needed
|
||
|
|
# because #run calls it automatically. If you want to override
|
||
|
|
# #run, it is not a good idea. Please contact test-unit
|
||
|
|
# developers. We will help you without your custom #run. For
|
||
|
|
# example, we may add a new hook in #run.
|
||
|
|
#
|
||
|
|
# This is a public API for developers who extend test-unit.
|
||
|
|
#
|
||
|
|
# @return [void]
|
||
|
|
def add_pass
|
||
|
|
current_result.add_pass
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
def current_result
|
||
|
|
@_result
|
||
|
|
end
|
||
|
|
|
||
|
|
def run_test
|
||
|
|
redefined_info = self[:redefined]
|
||
|
|
if redefined_info
|
||
|
|
notify("#{self.class}\##{@method_name} was redefined",
|
||
|
|
:backtrace => redefined_info[:backtrace])
|
||
|
|
end
|
||
|
|
if @internal_data.have_test_data?
|
||
|
|
__send__(@method_name, @internal_data.test_data)
|
||
|
|
else
|
||
|
|
__send__(@method_name)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def handle_exception(exception)
|
||
|
|
self.class.exception_handlers.each do |handler|
|
||
|
|
if handler.respond_to?(:call)
|
||
|
|
handled = handler.call(self, exception)
|
||
|
|
else
|
||
|
|
handled = __send__(handler, exception)
|
||
|
|
end
|
||
|
|
return true if handled
|
||
|
|
end
|
||
|
|
false
|
||
|
|
end
|
||
|
|
|
||
|
|
def add_assertion
|
||
|
|
current_result.add_assertion
|
||
|
|
end
|
||
|
|
|
||
|
|
class InternalData
|
||
|
|
attr_reader :start_time, :elapsed_time
|
||
|
|
attr_reader :test_data_label, :test_data
|
||
|
|
def initialize
|
||
|
|
@start_time = nil
|
||
|
|
@elapsed_time = nil
|
||
|
|
@passed = true
|
||
|
|
@interrupted = false
|
||
|
|
@test_data_label = nil
|
||
|
|
@test_data = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
def passed?
|
||
|
|
@passed
|
||
|
|
end
|
||
|
|
|
||
|
|
def interrupted?
|
||
|
|
@interrupted
|
||
|
|
end
|
||
|
|
|
||
|
|
def assign_test_data(label, data)
|
||
|
|
@test_data_label = label
|
||
|
|
@test_data = data
|
||
|
|
end
|
||
|
|
|
||
|
|
def have_test_data?
|
||
|
|
not @test_data_label.nil?
|
||
|
|
end
|
||
|
|
|
||
|
|
def test_started
|
||
|
|
@start_time = Time.now
|
||
|
|
end
|
||
|
|
|
||
|
|
def test_finished
|
||
|
|
@elapsed_time = Time.now - @start_time
|
||
|
|
end
|
||
|
|
|
||
|
|
def problem_occurred
|
||
|
|
@passed = false
|
||
|
|
end
|
||
|
|
|
||
|
|
def interrupted
|
||
|
|
@interrupted = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|