[cairo-commit] rcairo/test-unit/lib/test/unit assertionfailederror.rb, NONE, 1.1 assertions.rb, NONE, 1.1 attribute.rb, NONE, 1.1 autorunner.rb, NONE, 1.1 collector.rb, NONE, 1.1 color.rb, NONE, 1.1 diff.rb, NONE, 1.1 error.rb, NONE, 1.1 exceptionhandler.rb, NONE, 1.1 failure.rb, NONE, 1.1 fixture.rb, NONE, 1.1 notification.rb, NONE, 1.1 omission.rb, NONE, 1.1 pending.rb, NONE, 1.1 priority.rb, NONE, 1.1 testcase.rb, NONE, 1.1 testresult.rb, NONE, 1.1 testsuite.rb, NONE, 1.1 version.rb, NONE, 1.1

Kouhei Sutou commit at pdx.freedesktop.org
Wed Aug 13 01:20:56 PDT 2008


Committed by: kou

Update of /cvs/cairo/rcairo/test-unit/lib/test/unit
In directory kemper:/tmp/cvs-serv10309/test-unit/lib/test/unit

Added Files:
	assertionfailederror.rb assertions.rb attribute.rb 
	autorunner.rb collector.rb color.rb diff.rb error.rb 
	exceptionhandler.rb failure.rb fixture.rb notification.rb 
	omission.rb pending.rb priority.rb testcase.rb testresult.rb 
	testsuite.rb version.rb 
Log Message:
* test-unit: imported Test::Unit 2.x.


--- NEW FILE: assertionfailederror.rb ---
#--
#
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

module Test
  module Unit

    # Thrown by Test::Unit::Assertions when an assertion fails.
    class AssertionFailedError < StandardError
    end
  end
end

--- NEW FILE: assertions.rb ---
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

require 'test/unit/assertionfailederror'
require 'test/unit/util/backtracefilter'
require 'test/unit/diff'

module Test
  module Unit

    ##
    # Test::Unit::Assertions contains the standard Test::Unit assertions.
    # Assertions is included in Test::Unit::TestCase.
    #
    # To include it in your own code and use its functionality, you simply
    # need to rescue Test::Unit::AssertionFailedError. Additionally you may
    # override add_assertion to get notified whenever an assertion is made.
    #
    # Notes:
    # * The message to each assertion, if given, will be propagated with the
    #   failure.
    # * It is easy to add your own assertions based on assert_block().
    #
    # = Example Custom Assertion
    #
    #   def deny(boolean, message = nil)
    #     message = build_message message, '<?> is not false or nil.', boolean
    #     assert_block message do
    #       not boolean
    #     end
    #   end

    module Assertions

      ##
      # The assertion upon which all other assertions are based. Passes if the
      # block yields true.
      #
      # Example:
      #   assert_block "Couldn't do the thing" do
      #     do_the_thing
      #   end

      public
      def assert_block(message="assert_block failed.") # :yields: 
        _wrap_assertion do
          if (! yield)
            raise AssertionFailedError.new(message.to_s)
          end
        end
      end

      ##
      # Asserts that +boolean+ is not false or nil.
      #
      # Example:
      #   assert [1, 2].include?(5)

      public
      def assert(boolean, message=nil)
        _wrap_assertion do
          assert_block("assert should not be called with a block.") { !block_given? }
          assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
        end
      end

      ##
      # Passes if +expected+ == +actual.
      #
      # Note that the ordering of arguments is important, since a helpful
      # error message is generated when this one fails that tells you the
      # values of expected and actual.
      #
      # Example:
      #   assert_equal 'MY STRING', 'my string'.upcase

      public
      def assert_equal(expected, actual, message=nil)
        diff = AssertionMessage.delayed_literal do
          if !expected.is_a?(String) or !actual.is_a?(String)
            expected = AssertionMessage.convert(expected)
            actual = AssertionMessage.convert(actual)
          end
          diff = Diff.readable(expected, actual)
          if /^[-+]/ !~ diff
            diff = ""
          elsif /^[ ?]/ =~ diff or /(?:.*\n){2,}/ =~ diff
            diff = "\n\ndiff:\n#{diff}"
          else
            diff = ""
          end
          diff
        end
        full_message = build_message(message, <<EOT, expected, actual, diff)
<?> expected but was
<?>.?
EOT
        assert_block(full_message) { expected == actual }
      end

      private
      def _check_exception_class(args) # :nodoc:
        args.partition do |klass|
          next if klass.instance_of?(Module)
          assert(Exception >= klass, "Should expect a class of exception, #{klass}")
          true
        end
      end

      private
      def _expected_exception?(actual_exception, exceptions, modules) # :nodoc:
        exceptions.include?(actual_exception.class) or
          modules.any? {|mod| actual_exception.is_a?(mod)}
      end

      ##
      # Passes if the block raises one of the given exceptions.
      #
      # Example:
      #   assert_raise RuntimeError, LoadError do
      #     raise 'Boom!!!'
      #   end

      public
      def assert_raise(*args)
        _wrap_assertion do
          if Module === args.last
            message = ""
          else
            message = args.pop
          end
          exceptions, modules = _check_exception_class(args)
          expected = args.size == 1 ? args.first : args
          actual_exception = nil
          full_message = build_message(message, "<?> exception expected but none was thrown.", expected)
          assert_block(full_message) do
            begin
              yield
            rescue Exception => actual_exception
              break
            end
            false
          end
          full_message = build_message(message, "<?> exception expected but was\n?", expected, actual_exception)
          assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)}
          actual_exception
        end
      end

      ##
      # Alias of assert_raise.
      #
      # Will be deprecated in 1.9, and removed in 2.0.

      public
      def assert_raises(*args, &block)
        assert_raise(*args, &block)
      end

      ##
      # Passes if +object+ .instance_of? +klass+
      #
      # Example:
      #   assert_instance_of String, 'foo'

      public
      def assert_instance_of(klass, object, message="")
        _wrap_assertion do
          assert_equal(Class, klass.class, "assert_instance_of takes a Class as its first argument")
          full_message = build_message(message, <<EOT, object, klass, object.class)
<?> expected to be an instance of
<?> but was
<?>.
EOT
          assert_block(full_message){object.instance_of?(klass)}
        end
      end

      ##
      # Passes if +object+ is nil.
      #
      # Example:
      #   assert_nil [1, 2].uniq!

      public
      def assert_nil(object, message="")
        full_message = build_message(message, <<EOT, object)
<?> expected to be nil.
EOT
        assert_block(full_message) { object.nil? }
      end

      ##
      # Passes if +object+ .kind_of? +klass+
      #
      # Example:
      #   assert_kind_of Object, 'foo'

      public
      def assert_kind_of(klass, object, message="")
        _wrap_assertion do
          assert(klass.kind_of?(Module), "The first parameter to assert_kind_of should be a kind_of Module.")
          full_message = build_message(message, "<?>\nexpected to be kind_of\\?\n<?> but was\n<?>.", object, klass, object.class)
          assert_block(full_message){object.kind_of?(klass)}
        end
      end

      ##
      # Passes if +object+ .respond_to? +method+
      #
      # Example:
      #   assert_respond_to 'bugbear', :slice

      public
      def assert_respond_to(object, method, message="")
        _wrap_assertion do
          full_message = build_message(nil, "<?>\ngiven as the method name argument to #assert_respond_to must be a Symbol or #respond_to\\?(:to_str).", method)

          assert_block(full_message) do
            method.kind_of?(Symbol) || method.respond_to?(:to_str)
          end
          full_message = build_message(message, <<EOT, object, object.class, method)
<?>
of type <?>
expected to respond_to\\?<?>.
EOT
          assert_block(full_message) { object.respond_to?(method) }
        end
      end

      ##
      # Passes if +string+ =~ +pattern+.
      #
      # Example:
      #   assert_match(/\d+/, 'five, 6, seven')

      public
      def assert_match(pattern, string, message="")
        _wrap_assertion do
          pattern = case(pattern)
            when String
              Regexp.new(Regexp.escape(pattern))
            else
              pattern
          end
          full_message = build_message(message, "<?> expected to be =~\n<?>.", string, pattern)
          assert_block(full_message) { string =~ pattern }
        end
      end

      ##
      # Passes if +actual+ .equal? +expected+ (i.e. they are the same
      # instance).
      #
      # Example:
      #   o = Object.new
      #   assert_same o, o

      public
      def assert_same(expected, actual, message="")
        full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
<?>
with id <?> expected to be equal\\? to
<?>
with id <?>.
EOT
        assert_block(full_message) { actual.equal?(expected) }
      end

      ##
      # Compares the +object1+ with +object2+ using +operator+.
      #
      # Passes if object1.__send__(operator, object2) is true.
      #
      # Example:
      #   assert_operator 5, :>=, 4

      public
      def assert_operator(object1, operator, object2, message="")
        _wrap_assertion do
          full_message = build_message(nil, "<?>\ngiven as the operator for #assert_operator must be a Symbol or #respond_to\\?(:to_str).", operator)
          assert_block(full_message){operator.kind_of?(Symbol) || operator.respond_to?(:to_str)}
          full_message = build_message(message, <<EOT, object1, AssertionMessage.literal(operator), object2)
<?> expected to be
?
<?>.
EOT
          assert_block(full_message) { object1.__send__(operator, object2) }
        end
      end

      ##
      # Passes if block does not raise an exception.
      #
      # Example:
      #   assert_nothing_raised do
      #     [1, 2].uniq
      #   end

      public
      def assert_nothing_raised(*args)
        _wrap_assertion do
          if Module === args.last
            message = ""
          else
            message = args.pop
          end
          exceptions, modules = _check_exception_class(args)
          begin
            yield
          rescue Exception => e
            if ((args.empty? && !e.instance_of?(AssertionFailedError)) ||
                _expected_exception?(e, exceptions, modules))
              assert_block(build_message(message, "Exception raised:\n?", e)){false}
            else
              raise
            end
          end
          nil
        end
      end

      ##
      # Flunk always fails.
      #
      # Example:
      #   flunk 'Not done testing yet.'

      public
      def flunk(message="Flunked")
        assert_block(build_message(message)){false}
      end

      ##
      # Passes if ! +actual+ .equal? +expected+
      #
      # Example:
      #   assert_not_same Object.new, Object.new

      public
      def assert_not_same(expected, actual, message="")
        full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
<?>
with id <?> expected to not be equal\\? to
<?>
with id <?>.
EOT
        assert_block(full_message) { !actual.equal?(expected) }
      end

      ##
      # Passes if +expected+ != +actual+
      #
      # Example:
      #   assert_not_equal 'some string', 5

      public
      def assert_not_equal(expected, actual, message="")
        full_message = build_message(message, "<?> expected to be != to\n<?>.", expected, actual)
        assert_block(full_message) { expected != actual }
      end

      ##
      # Passes if ! +object+ .nil?
      #
      # Example:
      #   assert_not_nil '1 two 3'.sub!(/two/, '2')

      public
      def assert_not_nil(object, message="")
        full_message = build_message(message, "<?> expected to not be nil.", object)
        assert_block(full_message){!object.nil?}
      end

      ##
      # Passes if +regexp+ !~ +string+ 
      #
      # Example:
      #   assert_no_match(/two/, 'one 2 three')

      public
      def assert_no_match(regexp, string, message="")
        _wrap_assertion do
          assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
          full_message = build_message(message, "<?> expected to not match\n<?>.", regexp, string)
          assert_block(full_message) { regexp !~ string }
        end
      end

      UncaughtThrow = {
        NameError => /^uncaught throw \`(.+)\'$/,
        ArgumentError => /^uncaught throw (.+)$/,
        ThreadError => /^uncaught throw \`(.+)\' in thread /
      } #`

      ##
      # Passes if the block throws +expected_object+
      #
      # Example:
      #   assert_throws :done do
      #     throw :done
      #   end

      public
      def assert_throws(expected_object, message="", &proc)
        _wrap_assertion do
          begin
            catch([]) {}
          rescue TypeError
            assert_instance_of(Symbol, expected_object,
                               "assert_throws expects the symbol that should be thrown for its first argument")
          end
          assert_block("Should have passed a block to assert_throws.") do
            block_given?
          end
          caught = true
          begin
            catch(expected_object) do
              proc.call
              caught = false
            end
            full_message = build_message(message,
                                         "<?> should have been thrown.",
                                         expected_object)
            assert_block(full_message) {caught}
          rescue NameError, ArgumentError, ThreadError => error
            raise unless UncaughtThrow[error.class] =~ error.message
            tag = $1
            tag = tag[1..-1].intern if tag[0, 1] == ":"
            full_message = build_message(message,
                                         "<?> expected to be thrown but\n" +
                                         "<?> was thrown.",
                                         expected_object, tag)
            flunk(full_message)
          end
        end
      end

      ##
      # Passes if block does not throw anything.
      #
      # Example:
      #  assert_nothing_thrown do
      #    [1, 2].uniq
      #  end

      public
      def assert_nothing_thrown(message="", &proc)
        _wrap_assertion do
          assert(block_given?, "Should have passed a block to assert_nothing_thrown")
          begin
            proc.call
          rescue NameError, ArgumentError, ThreadError => error
            raise unless UncaughtThrow[error.class] =~ error.message
            tag = $1
            tag = tag[1..-1].intern if tag[0, 1] == ":"
            full_message = build_message(message,
                                         "<?> was thrown when nothing was expected",
                                         tag)
            flunk(full_message)
          end
          assert(true, "Expected nothing to be thrown")
        end
      end

      ##
      # Passes if +expected_float+ and +actual_float+ are equal
      # within +delta+ tolerance.
      #
      # Example:
      #   assert_in_delta 0.05, (50000.0 / 10**6), 0.00001

      public
      def assert_in_delta(expected_float, actual_float, delta, message="")
        _wrap_assertion do
          {expected_float => "first float", actual_float => "second float", delta => "delta"}.each do |float, name|
            assert_respond_to(float, :to_f, "The arguments must respond to to_f; the #{name} did not")
          end
          assert_operator(delta, :>=, 0.0, "The delta should not be negative")
          full_message = build_message(message, <<EOT, expected_float, actual_float, delta)
<?> and
<?> expected to be within
<?> of each other.
EOT
          assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f }
        end
      end

      ##
      # Passes if the method send returns a true value.
      #
      # +send_array+ is composed of:
      # * A receiver
      # * A method
      # * Arguments to the method
      #
      # Example:
      #   assert_send [[1, 2], :include?, 4]

      public
      def assert_send(send_array, message="")
        _wrap_assertion do
          assert_instance_of(Array, send_array, "assert_send requires an array of send information")
          assert(send_array.size >= 2, "assert_send requires at least a receiver and a message name")
          full_message = build_message(message, <<EOT, send_array[0], AssertionMessage.literal(send_array[1].to_s), send_array[2..-1])
<?> expected to respond to
<?(?)> with a true value.
EOT
          assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) }
        end
      end

      ##
      # Passes if +actual+ is a boolean value.
      #
      # Example:
      #   assert_boolean(true) # -> pass
      #   assert_boolean(nil)  # -> fail
      def assert_boolean(actual, message=nil)
        _wrap_assertion do
          assert_block(build_message(message,
                                     "<true> or <false> expected but was\n<?>",
                                     actual)) do
            [true, false].include?(actual)
          end
        end
      end

      ##
      # Passes if +actual+ is true.
      #
      # Example:
      #   assert_true(true)  # -> pass
      #   assert_true(:true) # -> fail
      def assert_true(actual, message=nil)
        _wrap_assertion do
          assert_block(build_message(message,
                                     "<true> expected but was\n<?>",
                                     actual)) do
            actual == true
          end
        end
      end

      ##
      # Passes if +actual+ is false.
      #
      # Example:
      #   assert_false(false)  # -> pass
      #   assert_false(nil)    # -> fail
      def assert_false(actual, message=nil)
        _wrap_assertion do
          assert_block(build_message(message,
                                     "<false> expected but was\n<?>",
                                     actual)) do
            actual == false
          end
        end
      end

      ##
      # Builds a failure message.  +head+ is added before the +template+ and
      # +arguments+ replaces the '?'s positionally in the template.

      public
      def build_message(head, template=nil, *arguments)
        template &&= template.chomp
        return AssertionMessage.new(head, template, arguments)
      end

      private
      def _wrap_assertion
        @_assertion_wrapped ||= false
        unless (@_assertion_wrapped)
          @_assertion_wrapped = true
          begin
            add_assertion
            return yield
          ensure
            @_assertion_wrapped = false
          end
        else
          return yield
        end
      end
      
      ##
      # Called whenever an assertion is made.  Define this in classes that
      # include Test::Unit::Assertions to record assertion counts.

      private
      def add_assertion
      end

      ##
      # Select whether or not to use the pretty-printer. If this option is set
      # to false before any assertions are made, pp.rb will not be required.

      public
      def self.use_pp=(value)
        AssertionMessage.use_pp = value
      end
      
      # :stopdoc:

      class AssertionMessage
        @use_pp = true
        class << self
          attr_accessor :use_pp

          def literal(value)
            Literal.new(value)
          end

          def delayed_literal(&block)
            DelayedLiteral.new(block)
          end

          def convert(object)
            case object
            when Exception
              <<EOM.chop
Class: <#{convert(object.class)}>
Message: <#{convert(object.message)}>
---Backtrace---
#{Util::BacktraceFilter.filter_backtrace(object.backtrace).join("\n")}
---------------
EOM
            else
              if use_pp
                begin
                  require 'pp' unless defined?(PP)
                  return PP.pp(object, '').chomp
                rescue LoadError
                  self.use_pp = false
                end
              end
              object.inspect
            end
          end
        end

        class Literal
          def initialize(value)
            @value = value
          end
          
          def inspect
            @value.to_s
          end
        end

        class DelayedLiteral
          def initialize(value)
            @value = value
          end
          
          def inspect
            @value.call.to_s
          end
        end

        class Template
          def self.create(string)
            parts = (string ? string.scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/m) : [])
            self.new(parts)
          end

          attr_reader :count

          def initialize(parts)
            @parts = parts
            @count = parts.find_all{|e| e == '?'}.size
          end

          def result(parameters)
            raise "The number of parameters does not match the number of substitutions." if(parameters.size != count)
            params = parameters.dup
            @parts.collect{|e| e == '?' ? params.shift : e.gsub(/\\\?/m, '?')}.join('')
          end
        end

        include Util::BacktraceFilter

        def initialize(head, template_string, parameters)
          @head = head
          @template_string = template_string
          @parameters = parameters
        end

        def convert(object)
          self.class.convert(object)
        end

        def template
          @template ||= Template.create(@template_string)
        end

        def add_period(string)
          (string =~ /\.\Z/ ? string : string + '.')
        end

        def to_s
          message_parts = []
          if (@head)
            head = @head.to_s 
            unless(head.empty?)
              message_parts << add_period(head)
            end
          end
          tail = template.result(@parameters.collect{|e| convert(e)})
          message_parts << tail unless(tail.empty?)
          message_parts.join("\n")
        end
      end

      # :startdoc:

    end
  end
end

--- NEW FILE: attribute.rb ---
module Test
  module Unit
    module Attribute
      class << self
        def included(base)
          base.extend(BaseClassMethods)
          base.extend(ClassMethods)
        end
      end

      module BaseClassMethods
        def attributes_table
          {}
        end
      end

      module ClassMethods
        def method_added(name)
          super
          return unless defined?(@current_attributes)

          attributes = {}
          kept_attributes = {}
          @current_attributes.each do |attribute_name, attribute|
            attributes[attribute_name] = attribute[:value]
            kept_attributes[attribute_name] = attribute if attribute[:keep]
          end
          set_attributes(name, attributes)
          @current_attributes = kept_attributes
        end

        def attribute(name, value, options={}, *method_names)
          unless options.is_a?(Hash)
            method_names << options
            options = {}
          end
          @current_attributes ||= {}
          if method_names.empty?
            @current_attributes[name] = options.merge(:value => value)
          else
            method_names.each do |method_name|
              set_attributes(method_name, {name => value})
            end
          end
        end

        def attributes_table
          @attributes_table ||= {}
          super.merge(@attributes_table)
        end

        def set_attributes(method_name, new_attributes)
          return if new_attributes.empty?
          method_name = normalize_method_name(method_name)
          @attributes_table ||= {}
          @attributes_table[method_name] ||= {}
          current_attributes = @attributes_table[method_name]
          new_attributes.each do |key, value|
            key = normalize_attribute_name(key)
            observers = attribute_observers(key) || []
            observers.each do |observer|
              observer.call(self,
                            key,
                            (attributes(method_name) || {})[key],
                            value,
                            method_name)
            end
            current_attributes[key] = value
          end
        end

        def attributes(method_name)
          method_name = normalize_method_name(method_name)
          attributes = attributes_table[method_name]
          ancestors[1..-1].each do |ancestor|
            if ancestor.is_a?(Class) and ancestor < Test::Unit::Attribute
              parent_attributes = ancestor.attributes(method_name)
              if attributes
                attributes = (parent_attributes || {}).merge(attributes)
              else
                attributes = parent_attributes
              end
              break
            end
          end
          attributes
        end

        def get_attribute(method_name, attribute_name)
          attribute_name = normalize_attribute_name(attribute_name)
          (attributes(method_name) || {})[attribute_name]
        end

        @@attribute_observers = {}
        def register_attribute_observer(attribute_name, observer=Proc.new)
          attribute_name = normalize_attribute_name(attribute_name)
          @@attribute_observers[attribute_name] ||= []
          @@attribute_observers[attribute_name] << observer
        end

        def attribute_observers(attribute_name)
          attribute_name = normalize_attribute_name(attribute_name)
          @@attribute_observers[attribute_name]
        end

        private
        def normalize_attribute_name(name)
          name.to_s
        end

        def normalize_method_name(name)
          name.to_s
        end
      end

      def attributes
        self.class.attributes(@method_name) || {}
      end

      def [](name)
        self.class.get_attribute(@method_name, name)
      end
    end
  end
end

--- NEW FILE: autorunner.rb ---
require 'test/unit'
require 'optparse'

module Test
  module Unit
    class AutoRunner
      RUNNERS = {}
      COLLECTORS = {}
      ADDITIONAL_OPTIONS = []

      class << self
        def register_runner(id, runner_builder=Proc.new)
          RUNNERS[id] = runner_builder
        end

        def register_collector(id, collector_builder=Proc.new)
          COLLECTORS[id] = collector_builder
        end

        def setup_option(option_builder=Proc.new)
          ADDITIONAL_OPTIONS << option_builder
        end
      end

      def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block)
        r = new(force_standalone || standalone?, &block)
        r.base = default_dir
        r.process_args(argv)
        r.run
      end
      
      def self.standalone?
        return false unless("-e" == $0)
        ObjectSpace.each_object(Class) do |klass|
          return false if(klass < TestCase)
        end
        true
      end

      register_collector(:descendant) do |auto_runner|
        require 'test/unit/collector/descendant'
        collector = Collector::Descendant.new
        collector.filter = auto_runner.filters
        collector.collect($0.sub(/\.rb\Z/, ''))
      end

      register_collector(:load) do |auto_runner|
        require 'test/unit/collector/load'
        collector = Collector::Load.new
        collector.patterns.concat(auto_runner.pattern) if auto_runner.pattern
        collector.excludes.concat(auto_runner.exclude) if auto_runner.exclude
        collector.base = auto_runner.base
        collector.filter = auto_runner.filters
        collector.collect(*auto_runner.to_run)
      end

      # deprecated
      register_collector(:object_space) do |auto_runner|
        require 'test/unit/collector/objectspace'
        c = Collector::ObjectSpace.new
        c.filter = auto_runner.filters
        c.collect($0.sub(/\.rb\Z/, ''))
      end

      # deprecated
      register_collector(:dir) do |auto_runner|
        require 'test/unit/collector/dir'
        c = Collector::Dir.new
        c.filter = auto_runner.filters
        c.pattern.concat(auto_runner.pattern) if auto_runner.pattern
        c.exclude.concat(auto_runner.exclude) if auto_runner.exclude
        c.base = auto_runner.base
        $:.push(auto_runner.base) if auto_runner.base
        c.collect(*(auto_runner.to_run.empty? ? ['.'] : auto_runner.to_run))
      end

      attr_reader :suite, :runner_options
      attr_accessor :filters, :to_run, :pattern, :exclude, :base, :workdir
      attr_writer :runner, :collector

      def initialize(standalone)
        Unit.run = true
        @standalone = standalone
        @runner = default_runner
        @collector = default_collector
        @filters = []
        @to_run = []
        @runner_options = {}
        @workdir = nil
        yield(self) if block_given?
      end

      def process_args(args = ARGV)
        begin
          options.order!(args) {|arg| @to_run << arg}
        rescue OptionParser::ParseError => e
          puts e
          puts options
          $! = nil
          abort
        else
          @filters << proc{false} unless(@filters.empty?)
        end
        not @to_run.empty?
      end

      def options
        @options ||= OptionParser.new do |o|
          o.banner = "Test::Unit automatic runner."
          o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"

          o.on
          o.on('-r', '--runner=RUNNER', RUNNERS,
               "Use the given RUNNER.",
               "(" + keyword_display(RUNNERS) + ")") do |r|
            @runner = r
          end

          if(@standalone)
            o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b|
              @base = b
            end

            o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w|
              @workdir = w
            end

            o.on('-a', '--add=TORUN', Array,
                 "Add TORUN to the list of things to run;",
                 "can be a file or a directory.") do |a|
              @to_run.concat(a)
            end

            @pattern = []
            o.on('-p', '--pattern=PATTERN', Regexp,
                 "Match files to collect against PATTERN.") do |e|
              @pattern << e
            end

            @exclude = []
            o.on('-x', '--exclude=PATTERN', Regexp,
                 "Ignore files to collect against PATTERN.") do |e|
              @exclude << e
            end
          end

          o.on('-n', '--name=NAME', String,
               "Runs tests matching NAME.",
               "(patterns may be used).") do |n|
            n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
            case n
            when Regexp
              @filters << proc{|t| n =~ t.method_name ? true : nil}
            else
              @filters << proc{|t| n == t.method_name ? true : nil}
            end
          end

          o.on('-t', '--testcase=TESTCASE', String,
               "Runs tests in TestCases matching TESTCASE.",
               "(patterns may be used).") do |n|
            n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
            case n
            when Regexp
              @filters << proc{|t| n =~ t.class.name ? true : nil}
            else
              @filters << proc{|t| n == t.class.name ? true : nil}
            end
          end

          priority_filter = Proc.new do |test|
            if @filters.size > 2
              nil
            else
              Priority::Checker.new(test).need_to_run? or nil
            end
          end
          o.on("--[no-]priority-mode",
               "Runs some tests based on their priority.") do |priority_mode|
            if priority_mode
              @filters |= [priority_filter]
            else
              @filters -= [priority_filter]
            end
          end

          o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]",
               "Appends directory list to $LOAD_PATH.") do |dirs|
            $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR))
          end

          ADDITIONAL_OPTIONS.each do |option_builder|
            option_builder.call(self, o)
          end

          o.on('--',
               "Stop processing options so that the",
               "remaining options will be passed to the",
               "test."){o.terminate}

          o.on('-h', '--help', 'Display this help.'){puts o; exit}

          o.on_tail
          o.on_tail('Deprecated options:')

          o.on_tail('--console', 'Console runner (use --runner).') do
            warn("Deprecated option (--console).")
            @runner = RUNNERS[:console]
          end

          if RUNNERS[:fox]
            o.on_tail('--fox', 'Fox runner (use --runner).') do
              warn("Deprecated option (--fox).")
              @runner = RUNNERS[:fox]
            end
          end

          o.on_tail
        end
      end

      def keyword_display(array)
        list = array.collect {|e, *| e.to_s}
        Array === array or list.sort!
        list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ")
      end

      def run
        suite = @collector[self]
        return false if suite.nil?
        runner = @runner[self]
        return false if runner.nil?
        Dir.chdir(@workdir) if @workdir
        runner.run(suite, @runner_options).passed?
      end

      private
      def default_runner
        if ENV["EMACS"] == "t"
          RUNNERS[:emacs]
        else
          RUNNERS[:console]
        end
      end

      def default_collector
        COLLECTORS[@standalone ? :load : :descendant]
      end
    end
  end
end

require 'test/unit/runner/console'
require 'test/unit/runner/emacs'

--- NEW FILE: collector.rb ---
module Test
  module Unit
    module Collector
      def initialize
        @filters = []
      end

      def filter=(filters)
        @filters = case(filters)
          when Proc
            [filters]
          when Array
            filters
        end
      end

      def add_suite(destination, suite)
        to_delete = suite.tests.find_all{|t| !include?(t)}
        to_delete.each{|t| suite.delete(t)}
        destination << suite unless(suite.size == 0)
      end

      def include?(test)
        return true if(@filters.empty?)
        @filters.each do |filter|
          result = filter[test]
          if(result.nil?)
            next
          elsif(!result)
            return false
          else
            return true
          end
        end
        true
      end

      def sort(suites)
        suites.sort_by{|s| s.name}
      end
    end
  end
end

--- NEW FILE: color.rb ---
module Test
  module Unit
    class Color
      NAMES = ["black", "red", "green", "yellow",
               "blue", "magenta", "cyan", "white"]
      def initialize(name, options={})
        @name = name
        @foreground = options[:foreground]
        @foreground = true if @foreground.nil?
        @intensity = options[:intensity]
        @bold = options[:bold]
        @italic = options[:italic]
        @underline = options[:underline]
      end

      def sequence
        sequence = []
        if @name == "none"
        elsif @name == "reset"
          sequence << "0"
        else
          foreground_parameter = @foreground ? 3 : 4
          foreground_parameter += 6 if @intensity
          sequence << "#{foreground_parameter}#{NAMES.index(@name)}"
        end
        sequence << "1" if @bold
        sequence << "3" if @italic
        sequence << "4" if @underline
        sequence
      end

      def escape_sequence
        "\e[#{sequence.join(';')}m"
      end

      def +(other)
        MixColor.new([self, other])
      end
    end

    class MixColor
      def initialize(colors)
        @colors = colors
      end

      def sequence
        @colors.inject([]) do |result, color|
          result + color.sequence
        end
      end

      def escape_sequence
        "\e[#{sequence.join(';')}m"
      end

      def +(other)
        self.class.new([self, other])
      end
    end
  end
end

--- NEW FILE: diff.rb ---
# port of Python's difflib.

module Test
  module Unit
    module Diff
      class SequenceMatcher
        def initialize(from, to, &junk_predicate)
          @from = from
          @to = to
          @junk_predicate = junk_predicate
          update_to_indexes
        end

        def longest_match(from_start, from_end, to_start, to_end)
          best_info = find_best_match_position(from_start, from_end,
                                               to_start, to_end)
          unless @junks.empty?
            args = [from_start, from_end, to_start, to_end]
            best_info = adjust_best_info_with_junk_predicate(false, best_info,
                                                             *args)
            best_info = adjust_best_info_with_junk_predicate(true, best_info,
                                                             *args)
          end

          best_info
        end

        def blocks
          @blocks ||= compute_blocks
        end

        def operations
          @operations ||= compute_operations
        end

        def grouped_operations(context_size=nil)
          context_size ||= 3
          _operations = operations
          _operations = [[:equal, 0, 0, 0, 0]] if _operations.empty?
          expand_edge_equal_operations!(_operations, context_size)

          group_window = context_size * 2
          groups = []
          group = []
          _operations.each do |tag, from_start, from_end, to_start, to_end|
            if tag == :equal and from_end - from_start > group_window
              group << [tag,
                        from_start,
                        [from_end, from_start + context_size].min,
                        to_start,
                        [to_end, to_start + context_size].min]
              groups << group
              group = []
              from_start = [from_start, from_end - context_size].max
              to_start = [to_start, to_end - context_size].max
            end
            group << [tag, from_start, from_end, to_start, to_end]
          end
          groups << group unless group.empty?
          groups
        end

        def ratio
          @ratio ||= compute_ratio
        end

        private
        def update_to_indexes
          @to_indexes = {}
          @junks = {}
          if @to.is_a?(String)
            each = " "[0].is_a?(Integer) ? :each_byte : :each_char
          else
            each = :each
          end
          i = 0
          @to.send(each) do |item|
            @to_indexes[item] ||= []
            @to_indexes[item] << i
            i += 1
          end

          return if @junk_predicate.nil?
          @to_indexes = @to_indexes.reject do |key, value|
            junk = @junk_predicate.call(key)
            @junks[key] = true if junk
            junk
          end
        end

        def find_best_match_position(from_start, from_end, to_start, to_end)
          best_from, best_to, best_size = from_start, to_start, 0
          sizes = {}
          from_start.upto(from_end) do |from_index|
            _sizes = {}
            (@to_indexes[@from[from_index]] || []).each do |to_index|
              next if to_index < to_start
              break if to_index > to_end
              size = _sizes[to_index] = (sizes[to_index - 1] || 0) + 1
              if size > best_size
                best_from = from_index - size + 1
                best_to = to_index - size + 1
                best_size = size
              end
            end
            sizes = _sizes
          end
          [best_from, best_to, best_size]
        end

        def adjust_best_info_with_junk_predicate(should_junk, best_info,
                                                 from_start, from_end,
                                                 to_start, to_end)
          best_from, best_to, best_size = best_info
          while best_from > from_start and best_to > to_start and
              (should_junk ?
               @junks.has_key?(@to[best_to - 1]) :
               !@junks.has_key?(@to[best_to - 1])) and
              @from[best_from - 1] == @to[best_to - 1]
            best_from -= 1
            best_to -= 1
            best_size += 1
          end

          while best_from + best_size < from_end and
              best_to + best_size < to_end and
              (should_junk ?
               @junks.has_key?(@to[best_to + best_size]) :
               !@junks.has_key?(@to[best_to + best_size])) and
              @from[best_from + best_size] == @to[best_to + best_size]
            best_size += 1
          end

          [best_from, best_to, best_size]
        end

        def matches
          @matches ||= compute_matches
        end

        def compute_matches
          matches = []
          queue = [[0, @from.size, 0, @to.size]]
          until queue.empty?
            from_start, from_end, to_start, to_end = queue.pop
            match = longest_match(from_start, from_end - 1, to_start, to_end - 1)
            match_from_index, match_to_index, size = match
            unless size.zero?
              if from_start < match_from_index and
                  to_start < match_to_index
                queue.push([from_start, match_from_index,
                            to_start, match_to_index])
              end
              matches << match
              if match_from_index + size < from_end and
                  match_to_index + size < to_end
                queue.push([match_from_index + size, from_end,
                            match_to_index + size, to_end])
              end
            end
          end
          matches.sort_by do |(from_index, to_index, match_size)|
            from_index
          end
        end

        def compute_blocks
          blocks = []
          current_from_index = current_to_index = current_size = 0
          matches.each do |from_index, to_index, size|
            if current_from_index + current_size == from_index and
                current_to_index + current_size == to_index
              current_size += size
            else
              unless current_size.zero?
                blocks << [current_from_index, current_to_index, current_size]
              end
              current_from_index = from_index
              current_to_index = to_index
              current_size = size
            end
          end
          unless current_size.zero?
            blocks << [current_from_index, current_to_index, current_size]
          end

          blocks << [@from.size, @to.size, 0]
          blocks
        end

        def compute_operations
          from_index = to_index = 0
          operations = []
          blocks.each do |match_from_index, match_to_index, size|
            tag = determine_tag(from_index, to_index,
                                match_from_index, match_to_index)
            if tag != :equal
              operations << [tag,
                             from_index, match_from_index,
                             to_index, match_to_index]
            end

            from_index, to_index = match_from_index + size, match_to_index + size
            if size > 0
              operations << [:equal,
                             match_from_index, from_index,
                             match_to_index, to_index]
            end
          end
          operations
        end

        def compute_ratio
          matches = blocks.inject(0) {|result, block| result + block[-1]}
          length = @from.length + @to.length
          if length.zero?
            1.0
          else
            2.0 * matches / length
          end
        end

        def determine_tag(from_index, to_index,
                          match_from_index, match_to_index)
          if from_index < match_from_index and to_index < match_to_index
            :replace
          elsif from_index < match_from_index
            :delete
          elsif to_index < match_to_index
            :insert
          else
            :equal
          end
        end

        def expand_edge_equal_operations!(_operations, context_size)
          tag, from_start, from_end, to_start, to_end = _operations[0]
          if tag == :equal
            _operations[0] = [tag,
                              [from_start, from_end - context_size].max,
                              from_end,
                              [to_start, to_end - context_size].max,
                              to_end]
          end

          tag, from_start, from_end, to_start, to_end = _operations[-1]
          if tag == :equal
            _operations[-1] = [tag,
                               from_start,
                               [from_end, from_start + context_size].min,
                               to_start,
                               [to_end, to_start + context_size].min]
          end
        end
      end

      class Differ
        def initialize(from, to)
          @from = from
          @to = to
        end

        private
        def tag(mark, contents)
          contents.collect {|content| "#{mark}#{content}"}
        end
      end

      class ReadableDiffer < Differ
        def diff(options={})
          result = []
          matcher = SequenceMatcher.new(@from, @to)
          matcher.operations.each do |args|
            tag, from_start, from_end, to_start, to_end = args
            case tag
            when :replace
              result.concat(diff_lines(from_start, from_end, to_start, to_end))
            when :delete
              result.concat(tag_deleted(@from[from_start...from_end]))
            when :insert
              result.concat(tag_inserted(@to[to_start...to_end]))
            when :equal
              result.concat(tag_equal(@from[from_start...from_end]))
            else
              raise "unknown tag: #{tag}"
            end
          end
          result
        end

        private
        def tag_deleted(contents)
          tag("- ", contents)
        end

        def tag_inserted(contents)
          tag("+ ", contents)
        end

        def tag_equal(contents)
          tag("  ", contents)
        end

        def tag_difference(contents)
          tag("? ", contents)
        end

        def find_diff_line_info(from_start, from_end, to_start, to_end)
          best_ratio = 0.74
          from_equal_index = to_equal_index = nil
          from_best_index = to_best_index = nil

          to_start.upto(to_end - 1) do |to_index|
            from_start.upto(from_end - 1) do |from_index|
              if @from[from_index] == @to[to_index]
                from_equal_index ||= from_index
                to_equal_index ||= to_index
                next
              end

              matcher = SequenceMatcher.new(@from[from_index], @to[to_index],
                                            &method(:space_character?))
              if matcher.ratio > best_ratio
                best_ratio = matcher.ratio
                from_best_index = from_index
                to_best_index = to_index
              end
            end
          end

          [best_ratio,
           from_equal_index, to_equal_index,
           from_best_index,  to_best_index]
        end

        def diff_lines(from_start, from_end, to_start, to_end)
          cut_off = 0.75

          info = find_diff_line_info(from_start, from_end, to_start, to_end)
          best_ratio, from_equal_index, to_equal_index, *info = info
          from_best_index, to_best_index = info

          if best_ratio < cut_off
            if from_equal_index.nil?
              tagged_from = tag_deleted(@from[from_start...from_end])
              tagged_to = tag_inserted(@to[to_start...to_end])
              if to_end - to_start < from_end - from_start
                return tagged_to + tagged_from
              else
                return tagged_from + tagged_to
              end
            end
            from_best_index = from_equal_index
            to_best_index = to_equal_index
            best_ratio = 1.0
          end

          _diff_lines(from_start, from_best_index, to_start, to_best_index) +
            diff_line(@from[from_best_index], @to[to_best_index]) +
            _diff_lines(from_best_index + 1, from_end, to_best_index + 1, to_end)
        end

        def _diff_lines(from_start, from_end, to_start, to_end)
          if from_start < from_end
            if to_start < to_end
              diff_lines(from_start, from_end, to_start, to_end)
            else
              tag_deleted(@from[from_start...from_end])
            end
          else
            tag_inserted(@to[to_start...to_end])
          end
        end

        def diff_line(from_line, to_line)
          from_tags = ""
          to_tags = ""
          matcher = SequenceMatcher.new(from_line, to_line,
                                        &method(:space_character?))
          operations = matcher.operations
          operations.each do |tag, from_start, from_end, to_start, to_end|
            from_length = from_end - from_start
            to_length = to_end - to_start
            case tag
            when :replace
              from_tags << "^" * from_length
              to_tags << "^" * to_length
            when :delete
              from_tags << "-" * from_length
            when :insert
              to_tags << "+" * to_length
            when :equal
              from_tags << " " * from_length
              to_tags << " " * to_length
            else
              raise "unknown tag: #{tag}"
            end
          end
          format_diff_point(from_line, to_line, from_tags, to_tags)
        end

        def format_diff_point(from_line, to_line, from_tags, to_tags)
          common = [n_leading_characters(from_line, ?\t),
                    n_leading_characters(to_line, ?\t)].min
          common = [common,
                    n_leading_characters(from_tags[0, common], " "[0])].min
          from_tags = from_tags[common..-1].rstrip
          to_tags = to_tags[common..-1].rstrip

          result = tag_deleted([from_line])
          unless from_tags.empty?
            result.concat(tag_difference(["#{"\t" * common}#{from_tags}"]))
          end
          result.concat(tag_inserted([to_line]))
          unless to_tags.empty?
            result.concat(tag_difference(["#{"\t" * common}#{to_tags}"]))
          end
          result
        end

        def n_leading_characters(string, character)
          n = 0
          while string[n] == character
            n += 1
          end
          n
        end

        def space_character?(character)
          [" "[0], "\t"[0]].include?(character)
        end
      end

      class UnifiedDiffer < Differ
        def diff(options={})
          groups = SequenceMatcher.new(@from, @to).grouped_operations
          return [] if groups.empty?
          return [] if same_content?(groups)

          show_context = options[:show_context]
          show_context = true if show_context.nil?
          result = ["--- #{options[:from_label]}".rstrip,
                    "+++ #{options[:to_label]}".rstrip]
          groups.each do |operations|
            result << format_summary(operations, show_context)
            operations.each do |args|
              operation_tag, from_start, from_end, to_start, to_end = args
              case operation_tag
              when :replace
                result.concat(tag("-", @from[from_start...from_end]))
                result.concat(tag("+", @to[to_start...to_end]))
              when :delete
                result.concat(tag("-", @from[from_start...from_end]))
              when :insert
                result.concat(tag("+", @to[to_start...to_end]))
              when :equal
                result.concat(tag(" ", @from[from_start...from_end]))
              end
            end
          end
          result
        end

        private
        def same_content?(groups)
          return false if groups.size != 1
          group = groups[0]
          return false if group.size != 1
          tag, from_start, from_end, to_start, to_end = group[0]

          tag == :equal and [from_start, from_end] == [to_start, to_end]
        end

        def format_summary(operations, show_context)
          _, first_from_start, _, first_to_start, _ = operations[0]
          _, _, last_from_end, _, last_to_end = operations[-1]
          summary = "@@ -%d,%d +%d,%d @@" % [first_from_start + 1,
                                             last_from_end - first_from_start,
                                             first_to_start + 1,
                                             last_to_end - first_to_start,]
          if show_context
            interesting_line = find_interesting_line(first_from_start,
                                                     first_to_start,
                                                     :define_line?)
            summary << " #{interesting_line}" if interesting_line
          end
          summary
        end

        def find_interesting_line(from_start, to_start, predicate)
          from_index = from_start
          to_index = to_start
          while from_index >= 0 or to_index >= 0
            [@from[from_index], @to[to_index]].each do |line|
              return line if line and send(predicate, line)
            end

            from_index -= 1
            to_index -= 1
          end
          nil
        end

        def define_line?(line)
          /\A(?:[_a-zA-Z$]|\s*(?:class|module|def)\b)/ =~ line
        end
      end

      module_function
      def readable(from, to, options={})
        diff(ReadableDiffer, from, to, options)
      end

      def unified(from, to, options={})
        diff(UnifiedDiffer, from, to, options)
      end

      def diff(differ_class, from, to, options={})
        differ = differ_class.new(from.split(/\r?\n/), to.split(/\r?\n/))
        differ.diff(options).join("\n")
      end
    end
  end
end

--- NEW FILE: error.rb ---
#--
#
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

require 'test/unit/util/backtracefilter'

module Test
  module Unit

    # Encapsulates an error in a test. Created by
    # Test::Unit::TestCase when it rescues an exception thrown
    # during the processing of a test.
    class Error
      include Util::BacktraceFilter

      attr_reader(:test_name, :exception)

      SINGLE_CHARACTER = 'E'
      LABEL = "Error"

      # Creates a new Error with the given test_name and
      # exception.
      def initialize(test_name, exception)
        @test_name = test_name
        @exception = exception
      end

      # Returns a single character representation of an error.
      def single_character_display
        SINGLE_CHARACTER
      end

      def label
        LABEL
      end

      # Returns the message associated with the error.
      def message
        "#{@exception.class.name}: #{@exception.message}"
      end

      # Returns a brief version of the error description.
      def short_display
        "#@test_name: #{message.split("\n")[0]}"
      end

      # Returns a verbose version of the error description.
      def long_display
        backtrace_display = backtrace.join("\n    ")
        "#{label}:\n#@test_name:\n#{message}\n    #{backtrace_display}"
      end

      def backtrace
        filter_backtrace(@exception.backtrace)
      end

      # Overridden to return long_display.
      def to_s
        long_display
      end
    end

    module ErrorHandler
      class << self
        def included(base)
          base.exception_handler(:handle_all_exception)
        end
      end

      PASS_THROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt,
                                 SystemExit]
      private
      def handle_all_exception(exception)
        case exception
        when *PASS_THROUGH_EXCEPTIONS
          false
        else
          problem_occurred
          add_error(exception)
          true
        end
      end

      def add_error(exception)
        current_result.add_error(Error.new(name, exception))
      end
    end

    module TestResultErrorSupport
      attr_reader :errors

      # Records a Test::Unit::Error.
      def add_error(error)
        @errors << error
        notify_fault(error)
        notify_changed
      end

      # Returns the number of errors this TestResult has
      # recorded.
      def error_count
        @errors.size
      end

      def error_occurred?
        not @errors.empty?
      end

      private
      def initialize_containers
        super
        @errors = []
        @summary_generators << :error_summary
        @problem_checkers << :error_occurred?
      end

      def error_summary
        "#{error_count} errors"
      end
    end
  end
end

--- NEW FILE: exceptionhandler.rb ---
module Test
  module Unit
    module ExceptionHandler
      @@exception_handlers = []
      class << self
        def exception_handlers
          @@exception_handlers
	end

        def included(base)
          base.extend(ClassMethods)

          observer = Proc.new do |test_case, _, _, value, method_name|
            if value
              @@exception_handlers.unshift(method_name)
            else
              @@exception_handlers -= [method_name]
            end
          end
          base.register_attribute_observer(:exception_handler, &observer)
        end
      end

      module ClassMethods
        def exception_handlers
          ExceptionHandler.exception_handlers
        end

        def exception_handler(*method_names)
          attribute(:exception_handler, true, *method_names)
        end

        def unregister_exception_handler(*method_names)
          attribute(:exception_handler, false, *method_names)
        end
      end
    end
  end
end

--- NEW FILE: failure.rb ---
#--
#
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

module Test
  module Unit

    # Encapsulates a test failure. Created by Test::Unit::TestCase
    # when an assertion fails.
    class Failure
      attr_reader :test_name, :location, :message
      
      SINGLE_CHARACTER = 'F'
      LABEL = "Failure"

      # Creates a new Failure with the given location and
      # message.
      def initialize(test_name, location, message)
        @test_name = test_name
        @location = location
        @message = message
      end
      
      # Returns a single character representation of a failure.
      def single_character_display
        SINGLE_CHARACTER
      end

      def label
        LABEL
      end

      # Returns a brief version of the error description.
      def short_display
        "#@test_name: #{@message.split("\n")[0]}"
      end

      # Returns a verbose version of the error description.
      def long_display
        if location.size == 1
          location_display = location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
        else
          location_display = "\n    [#{location.join("\n     ")}]"
        end
        "#{label}:\n#@test_name#{location_display}:\n#@message"
      end

      # Overridden to return long_display.
      def to_s
        long_display
      end
    end

    module FailureHandler
      class << self
        def included(base)
          base.exception_handler(:handle_assertion_failed_error)
        end
      end

      private
      def handle_assertion_failed_error(exception)
        return false unless exception.is_a?(AssertionFailedError)
        problem_occurred
        add_failure(exception.message, exception.backtrace)
        true
      end

      def add_failure(message, backtrace)
        failure = Failure.new(name, filter_backtrace(backtrace), message)
        current_result.add_failure(failure)
      end
    end

    module TestResultFailureSupport
      attr_reader :failures

      # Records a Test::Unit::Failure.
      def add_failure(failure)
        @failures << failure
        notify_fault(failure)
        notify_changed
      end

      # Returns the number of failures this TestResult has
      # recorded.
      def failure_count
        @failures.size
      end

      def failure_occurred?
        not @failures.empty?
      end

      private
      def initialize_containers
        super
        @failures = []
        @summary_generators << :failure_summary
        @problem_checkers << :failure_occurred?
      end

      def failure_summary
        "#{failure_count} failures"
      end
    end
  end
end

--- NEW FILE: fixture.rb ---
module Test
  module Unit
    module Fixture
      class << self
        def included(base)
          base.extend(ClassMethods)

          [:setup, :teardown].each do |fixture|
            observer = Proc.new do |test_case, _, _, value, method_name|
              if value.nil?
                test_case.send("unregister_#{fixture}_method", method_name)
              else
                test_case.send("register_#{fixture}_method", method_name,
                               value)
              end
            end
            base.register_attribute_observer(fixture, &observer)
          end
        end
      end

      module ClassMethods
        def setup(*method_names)
          register_fixture(:setup, *method_names)
        end

        def unregister_setup(*method_names)
          unregister_fixture(:setup, *method_names)
        end

        def teardown(*method_names)
          register_fixture(:teardown, *method_names)
        end

        def unregister_teardown(*method_names)
          unregister_fixture(:teardown, *method_names)
        end

        def register_setup_method(method_name, options)
          register_fixture_method(:setup, method_name, options, :after, :append)
        end

        def unregister_setup_method(method_name)
          unregister_fixture_method(:setup, method_name)
        end

        def register_teardown_method(method_name, options)
          register_fixture_method(:teardown, method_name, options,
                                  :before, :prepend)
        end

        def unregister_teardown_method(method_name)
          unregister_fixture_method(:teardown, method_name)
        end

        def before_setup_methods
          collect_fixture_methods(:setup, :before)
        end

        def after_setup_methods
          collect_fixture_methods(:setup, :after)
        end

        def before_teardown_methods
          collect_fixture_methods(:teardown, :before)
        end

        def after_teardown_methods
          collect_fixture_methods(:teardown, :after)
        end

        private
        def register_fixture(fixture, *method_names)
          options = {}
          options = method_names.pop if method_names.last.is_a?(Hash)
          attribute(fixture, options, *method_names)
        end

        def unregister_fixture(fixture, *method_names)
          attribute(fixture, nil, *method_names)
        end

        def valid_register_fixture_options?(options)
          return true if options.empty?
          return false if options.size > 1

          key = options.keys.first
          [:before, :after].include?(key) and
            [:prepend, :append].include?(options[key])
        end

        def add_fixture_method_name(how, variable_name, method_name)
          unless self.instance_variable_defined?(variable_name)
            self.instance_variable_set(variable_name, [])
          end
          methods = self.instance_variable_get(variable_name)

          if how == :prepend
            methods = [method_name] | methods
          else
            methods = methods | [method_name]
          end
          self.instance_variable_set(variable_name, methods)
        end

        def registered_methods_variable_name(fixture, order)
          "@#{order}_#{fixture}_methods"
        end

        def unregistered_methods_variable_name(fixture)
          "@unregistered_#{fixture}_methods"
        end

        def register_fixture_method(fixture, method_name, options,
                                    default_order, default_how)
          unless valid_register_fixture_options?(options)
            message = "must be {:before => :prepend}, " +
              "{:before => :append}, {:after => :prepend} or " +
              "{:after => :append}: #{options.inspect}"
            raise ArgumentError, message
          end

          if options.empty?
            order, how = default_order, default_how
          else
            order, how = options.to_a.first
          end
          variable_name = registered_methods_variable_name(fixture, order)
          add_fixture_method_name(how, variable_name, method_name)
        end

        def unregister_fixture_method(fixture, method_name)
          variable_name = unregistered_methods_variable_name(fixture)
          add_fixture_method_name(:append, variable_name, method_name)
        end

        def collect_fixture_methods(fixture, order)
          methods_variable = registered_methods_variable_name(fixture, order)
          unregistered_methods_variable =
            unregistered_methods_variable_name(fixture)

          base_index = ancestors.index(Fixture)
          interested_ancestors = ancestors[0, base_index].reverse
          interested_ancestors.inject([]) do |result, ancestor|
            if ancestor.is_a?(Class)
              ancestor.class_eval do
                methods = []
                unregistered_methods = []
                if instance_variable_defined?(methods_variable)
                  methods = instance_variable_get(methods_variable)
                end
                if instance_variable_defined?(unregistered_methods_variable)
                  unregistered_methods =
                    instance_variable_get(unregistered_methods_variable)
                end
                (result | methods) - unregistered_methods
              end
            else
              result
            end
          end
        end
      end

      private
      def run_fixture(fixture)
        [
         self.class.send("before_#{fixture}_methods"),
         fixture,
         self.class.send("after_#{fixture}_methods")
        ].flatten.each do |method_name|
          send(method_name) if respond_to?(method_name, true)
        end
      end

      def run_setup
        run_fixture(:setup)
      end

      def run_teardown
        run_fixture(:teardown)
      end
    end
  end
end

--- NEW FILE: notification.rb ---
require 'test/unit/util/backtracefilter'

module Test
  module Unit
    class Notification
      include Util::BacktraceFilter
      attr_reader :test_name, :location, :message

      SINGLE_CHARACTER = 'N'
      LABEL = "Notification"

      # Creates a new Notification with the given location and
      # message.
      def initialize(test_name, location, message)
        @test_name = test_name
        @location = location
        @message = message
      end

      # Returns a single character representation of a notification.
      def single_character_display
        SINGLE_CHARACTER
      end

      def label
        LABEL
      end

      # Returns a brief version of the error description.
      def short_display
        "#{@test_name}: #{@message.split("\n")[0]}"
      end

      # Returns a verbose version of the error description.
      def long_display
        backtrace = filter_backtrace(location).join("\n")
        "#{label}: #{@message}\n#{@test_name}\n#{backtrace}"
      end

      # Overridden to return long_display.
      def to_s
        long_display
      end
    end

    class NotifiedError < StandardError
    end


    module TestCaseNotificationSupport
      class << self
        def included(base)
          base.class_eval do
            include NotificationHandler
          end
        end
      end

      # Notify some information.
      #
      # Example:
      #   def test_notification
      #     notify("I'm here!")
      #     # Reached here
      #     notify("Special!") if special_case?
      #     # Reached here too
      #   end
      def notify(message, &block)
        notification = Notification.new(name, filter_backtrace(caller), message)
        add_notification(notification)
      end

      private
      def add_notification(notification)
        current_result.add_notification(notification)
      end
    end

    module NotificationHandler
      class << self
        def included(base)
          base.exception_handler(:handle_Notified_error)
        end
      end

      private
      def handle_Notified_error(exception)
        return false unless exception.is_a?(NotifiedError)
        notification = Notification.new(name,
                                filter_backtrace(exception.backtrace),
                                exception.message)
        add_notification(notification)
        true
      end
    end

    module TestResultNotificationSupport
      attr_reader :notifications

      # Records a Test::Unit::Notification.
      def add_notification(notification)
        @notifications << notification
        notify_fault(notification)
        notify_changed
      end

      # Returns the number of notifications this TestResult has
      # recorded.
      def notification_count
        @notifications.size
      end

      private
      def initialize_containers
        super
        @notifications = []
        @summary_generators << :notification_summary
      end

      def notification_summary
        "#{notification_count} notifications"
      end
    end
  end
end

--- NEW FILE: omission.rb ---
require 'test/unit/util/backtracefilter'

module Test
  module Unit
    class Omission
      include Util::BacktraceFilter
      attr_reader :test_name, :location, :message

      SINGLE_CHARACTER = 'O'
      LABEL = "Omission"

      # Creates a new Omission with the given location and
      # message.
      def initialize(test_name, location, message)
        @test_name = test_name
        @location = location
        @message = message
      end

      # Returns a single character representation of a omission.
      def single_character_display
        SINGLE_CHARACTER
      end

      def label
        LABEL
      end

      # Returns a brief version of the error description.
      def short_display
        "#{@test_name}: #{@message.split("\n")[0]}"
      end

      # Returns a verbose version of the error description.
      def long_display
        backtrace = filter_backtrace(location).join("\n")
        "#{label}: #{@message}\n#{@test_name}\n#{backtrace}"
      end

      # Overridden to return long_display.
      def to_s
        long_display
      end
    end

    class OmittedError < StandardError
    end


    module TestCaseOmissionSupport
      class << self
        def included(base)
          base.class_eval do
            include OmissionHandler
          end
        end
      end

      # Omit the test of part of the test.
      #
      # Example:
      #   def test_omission
      #     omit
      #     # Not reached here
      #   end
      #
      #   def test_omission_with_here
      #     omit do
      #       # Not ran here
      #     end
      #     # Reached here
      #   end
      def omit(message=nil, &block)
        message ||= "omitted."
        if block_given?
          omission = Omission.new(name, filter_backtrace(caller), message)
          add_omission(omission)
        else
          raise OmittedError.new(message)
        end
      end

      def omit_if(condition, *args, &block)
        omit(*args, &block) if condition
      end

      def omit_unless(condition, *args, &block)
        omit(*args, &block) unless condition
      end

      private
      def add_omission(omission)
        current_result.add_omission(omission)
      end
    end

    module OmissionHandler
      class << self
        def included(base)
          base.exception_handler(:handle_omitted_error)
        end
      end

      private
      def handle_omitted_error(exception)
        return false unless exception.is_a?(OmittedError)
        omission = Omission.new(name,
                                filter_backtrace(exception.backtrace),
                                exception.message)
        add_omission(omission)
        true
      end
    end

    module TestResultOmissionSupport
      attr_reader :omissions

      # Records a Test::Unit::Omission.
      def add_omission(omission)
        @omissions << omission
        notify_fault(omission)
        notify_changed
      end

      # Returns the number of omissions this TestResult has
      # recorded.
      def omission_count
        @omissions.size
      end

      private
      def initialize_containers
        super
        @omissions = []
        @summary_generators << :omission_summary
      end

      def omission_summary
        "#{omission_count} omissions"
      end
    end
  end
end

--- NEW FILE: pending.rb ---
require 'test/unit/util/backtracefilter'

module Test
  module Unit
    class Pending
      include Util::BacktraceFilter
      attr_reader :test_name, :location, :message

      SINGLE_CHARACTER = 'P'
      LABEL = "Pending"

      # Creates a new Pending with the given location and
      # message.
      def initialize(test_name, location, message)
        @test_name = test_name
        @location = location
        @message = message
      end

      # Returns a single character representation of a pending.
      def single_character_display
        SINGLE_CHARACTER
      end

      def label
        LABEL
      end

      # Returns a brief version of the error description.
      def short_display
        "#{@test_name}: #{@message.split("\n")[0]}"
      end

      # Returns a verbose version of the error description.
      def long_display
        backtrace = filter_backtrace(location).join("\n")
        "#{label}: #{@message}\n#{@test_name}\n#{backtrace}"
      end

      # Overridden to return long_display.
      def to_s
        long_display
      end
    end

    class PendedError < StandardError
    end


    module TestCasePendingSupport
      class << self
        def included(base)
          base.class_eval do
            include PendingHandler
          end
        end
      end

      # Marks the test or part of the test is pending.
      #
      # Example:
      #   def test_pending
      #     pend
      #     # Not reached here
      #   end
      #
      #   def test_pending_with_here
      #     pend do
      #       # Ran here
      #       # Fails if the block doesn't raise any error.
      #       # Because it means the block is passed unexpectedly.
      #     end
      #     # Reached here
      #   end
      def pend(message=nil, &block)
        message ||= "pended."
        if block_given?
          pending = nil
          begin
            yield
          rescue Exception
            pending = Pending.new(name, filter_backtrace(caller), message)
            add_pending(pending)
          end
          unless pending
            flunk("Pending block should not be passed: #{message}")
          end
        else
          raise PendedError.new(message)
        end
      end

      private
      def add_pending(pending)
        problem_occurred
        current_result.add_pending(pending)
      end
    end

    module PendingHandler
      class << self
        def included(base)
          base.exception_handler(:handle_pended_error)
        end
      end

      private
      def handle_pended_error(exception)
        return false unless exception.is_a?(PendedError)
        pending = Pending.new(name,
                                filter_backtrace(exception.backtrace),
                                exception.message)
        add_pending(pending)
        true
      end
    end

    module TestResultPendingSupport
      attr_reader :pendings

      # Records a Test::Unit::Pending.
      def add_pending(pending)
        @pendings << pending
        notify_fault(pending)
        notify_changed
      end

      # Returns the number of pendings this TestResult has
      # recorded.
      def pending_count
        @pendings.size
      end

      private
      def initialize_containers
        super
        @pendings = []
        @summary_generators << :pending_summary
      end

      def pending_summary
        "#{pending_count} pendings"
      end
    end
  end
end

--- NEW FILE: priority.rb ---
require "fileutils"
require "tmpdir"

module Test
  module Unit
    module Priority
      class << self
        def included(base)
          base.extend(ClassMethods)

          base.class_eval do
            setup :priority_setup, :before => :prepend
            teardown :priority_teardown, :after => :append
          end
        end
      end

      class Checker
        class << self
          def have_priority?(name)
            singleton_class = (class << self; self; end)
            singleton_class.method_defined?(priority_check_method_name(name))
          end

          def need_to_run?(test)
            priority = test[:priority] || :normal
            if have_priority?(priority)
              send(priority_check_method_name(priority), test)
            else
              true
            end
          end

          def run_priority_must?(test)
            true
          end

          def run_priority_important?(test)
            rand > 0.1
          end

          def run_priority_high?(test)
            rand > 0.3
          end

          def run_priority_normal?(test)
            rand > 0.5
          end

          def run_priority_low?(test)
            rand > 0.75
          end

          def run_priority_never?(test)
            false
          end

          private
          def priority_check_method_name(priority_name)
            "run_priority_#{priority_name}?"
          end
        end

        attr_reader :test
        def initialize(test)
          @test = test
        end

        def setup
          FileUtils.rm_f(passed_file)
        end

        def teardown
          if @test.send(:passed?)
            FileUtils.touch(passed_file)
          else
            FileUtils.rm_f(passed_file)
          end
        end

        def need_to_run?
          !previous_test_success? or self.class.need_to_run?(@test)
        end

        private
        def previous_test_success?
          File.exist?(passed_file)
        end

        def result_dir
          components = [".test-result",
                        @test.class.name || "AnonymousTestCase",
                        @test.method_name.to_s]
          parent_directories = [File.dirname($0), Dir.pwd]
          if Process.respond_to?(:uid)
            parent_directories << File.join(Dir.tmpdir, Process.uid.to_s)
          end
          parent_directories.each do |parent_directory|
            dir = File.expand_path(File.join(parent_directory, *components))
            begin
              FileUtils.mkdir_p(dir)
              return dir
            rescue Errno::EACCES
            end
          end

          raise Errno::EACCES, parent_directories.join(", ")
        end

        def passed_file
          File.join(result_dir, "passed")
        end

        def escaped_method_name
          @method_name.to_s.gsub(/[!?=]$/) do |matched|
            case matched
            when "!"
              ".destructive"
            when "?"
              ".predicate"
            when "="
              ".equal"
            end
          end
        end
      end

      module ClassMethods
        def priority(name, *tests)
          unless Checker.have_priority?(name)
            raise ArgumentError, "unknown priority: #{name}"
          end
          attribute(:priority, name, {:keep => true}, *tests)
        end
      end

      def priority_setup
        Checker.new(self).setup
      end

      def priority_teardown
        Checker.new(self).teardown
      end
    end
  end
end

--- NEW FILE: testcase.rb ---
#--
#
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

require 'test/unit/attribute'
require 'test/unit/fixture'
require 'test/unit/exceptionhandler'
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/testsuite'
require 'test/unit/assertionfailederror'
require 'test/unit/util/backtracefilter'

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 teardown
    #       ...
    #     end
    #
    #     def test_my_method1
    #       ...
    #     end
    #
    #     def test_my_method2
    #       ...
    #     end
    #   end
    #
    # Here is a call order:
    #  * startup
    #  * setup
    #  * test_my_method1
    #  * teardown
    #  * setup
    #  * test_my_method2
    #  * teardown
    #  * shutdown
    class TestCase
      include Attribute
      include Fixture
      include ExceptionHandler
      include ErrorHandler
      include FailureHandler
      include TestCasePendingSupport
      include TestCaseOmissionSupport
      include TestCaseNotificationSupport
      include Priority
      include Assertions
      include Util::BacktraceFilter

      STARTED = name + "::STARTED"
      FINISHED = name + "::FINISHED"

      DESCENDANTS = []

      class << self
        def inherited(sub_class)
          DESCENDANTS << sub_class
        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
          method_names = public_instance_methods(true).collect {|name| name.to_s}
          tests = method_names.delete_if {|method_name| method_name !~ /^test./}
          suite = TestSuite.new(name, self)
          tests.sort.each do |test|
            catch(:invalid_test) do
              suite << new(test)
            end
          end
          if suite.empty?
            catch(:invalid_test) do
              suite << new("default_test")
            end
          end
          suite
        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)
        throw :invalid_test unless respond_to?(test_method_name)
        throw :invalid_test if method(test_method_name).arity > 0
        @method_name = test_method_name
        @test_passed = true
        @interrupted = false
      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
          yield(STARTED, name)
          begin
            run_setup
            run_test
          rescue Exception
            @interrupted = true
            raise unless handle_exception($!)
          ensure
            begin
              run_teardown
            rescue Exception
              raise unless handle_exception($!)
            end
          end
          result.add_run
          yield(FINISHED, name)
        ensure
          @_result = nil
        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
      #     def my_setup2
      #       ...
      #     end
      #
      #     def test_my_class
      #       ...
      #     end
      #   end
      #
      # Here is a call order:
      #   * setup
      #   * my_setup1
      #   * my_setup2
      #   * test_my_class
      def setup
      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
      #     def my_teardown2
      #       ...
      #     end
      #
      #     def test_my_class
      #       ...
      #     end
      #   end
      #
      # Here is a call order:
      #   * test_my_class
      #   * my_teardown2
      #   * my_teardown1
      #   * teardown
      def teardown
      end
      
      def default_test
        flunk("No tests were specified")
      end

      def size
        1
      end

      # Returns a human-readable name for the specific test that
      # this instance of TestCase represents.
      def name
        "#{@method_name}(#{self.class.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)
        self.class == other.class
      end

      def interrupted?
        @interrupted
      end

      private
      def current_result
        @_result
      end

      def run_test
        __send__(@method_name)
      end

      def handle_exception(exception)
        self.class.exception_handlers.each do |handler|
          return true if send(handler, exception)
        end
        false
      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?
        @test_passed
      end

      def problem_occurred
        @test_passed = false
      end

      def add_assertion
        current_result.add_assertion
      end
    end
  end
end

--- NEW FILE: testresult.rb ---
#--
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

require 'test/unit/util/observable'
require 'test/unit/failure'
require 'test/unit/error'
require 'test/unit/omission'
require 'test/unit/pending'
require 'test/unit/notification'

module Test
  module Unit
    module NullResultContainerInitializer
      private
      def initialize_containers
      end
    end

    # Collects Test::Unit::Failure and Test::Unit::Error so that
    # they can be displayed to the user. To this end, observers
    # can be added to it, allowing the dynamic updating of, say, a
    # UI.
    class TestResult
      include Util::Observable
      include NullResultContainerInitializer
      include TestResultFailureSupport
      include TestResultErrorSupport
      include TestResultPendingSupport
      include TestResultOmissionSupport
      include TestResultNotificationSupport

      CHANGED = "CHANGED"
      FAULT = "FAULT"

      attr_reader :run_count, :assertion_count, :faults

      # Constructs a new, empty TestResult.
      def initialize
        @run_count, @assertion_count = 0, 0
        @summary_generators = []
        @problem_checkers = []
        @faults = []
        initialize_containers
      end

      # Records a test run.
      def add_run
        @run_count += 1
        notify_changed
      end

      # Records an individual assertion.
      def add_assertion
        @assertion_count += 1
        notify_changed
      end

      # Returns a string contain the recorded runs, assertions,
      # failures and errors in this TestResult.
      def summary
        ["#{run_count} tests",
         "#{assertion_count} assertions",
         *@summary_generators.collect {|generator| send(generator)}].join(", ")
      end

      def to_s
        summary
      end

      # Returns whether or not this TestResult represents
      # successful completion.
      def passed?
        @problem_checkers.all? {|checker| not send(checker)}
      end

      private
      def notify_changed
        notify_listeners(CHANGED, self)
      end

      def notify_fault(fault)
        @faults << fault
        notify_listeners(FAULT, fault)
      end
    end
  end
end

--- NEW FILE: testsuite.rb ---
#--
#
# Author:: Nathaniel Talbott.
# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
# License:: Ruby license.

require 'test/unit/error'

module Test
  module Unit

    # A collection of tests which can be #run.
    #
    # Note: It is easy to confuse a TestSuite instance with
    # something that has a static suite method; I know because _I_
    # have trouble keeping them straight. Think of something that
    # has a suite method as simply providing a way to get a
    # meaningful TestSuite instance.
    class TestSuite
      attr_reader :name, :tests
      
      STARTED = name + "::STARTED"
      FINISHED = name + "::FINISHED"

      # Creates a new TestSuite with the given name.
      def initialize(name="Unnamed TestSuite", test_case=nil)
        @name = name
        @tests = []
        @test_case = test_case
      end

      # Runs the tests and/or suites contained in this
      # TestSuite.
      def run(result, &progress_block)
        yield(STARTED, name)
        run_startup(result)
        @tests.each do |test|
          test.run(result, &progress_block)
        end
        run_shutdown(result)
        yield(FINISHED, name)
      end

      # Adds the test to the suite.
      def <<(test)
        @tests << test
        self
      end

      def delete(test)
        @tests.delete(test)
      end

      # Retuns the rolled up number of tests in this suite;
      # i.e. if the suite contains other suites, it counts the
      # tests within those suites, not the suites themselves.
      def size
        total_size = 0
        @tests.each { |test| total_size += test.size }
        total_size
      end
      
      def empty?
        tests.empty?
      end

      # Overridden to return the name given the suite at
      # creation.
      def to_s
        @name
      end
      
      # It's handy to be able to compare TestSuite instances.
      def ==(other)
        return false unless(other.kind_of?(self.class))
        return false unless(@name == other.name)
        @tests == other.tests
      end

      private
      def run_startup(result)
        return if @test_case.nil? or !@test_case.respond_to?(:startup)
        begin
          @test_case.startup
        rescue Exception
          raise unless handle_exception($!, result)
        end
      end

      def run_shutdown(result)
        return if @test_case.nil? or !@test_case.respond_to?(:shutdown)
        begin
          @test_case.shutdown
        rescue Exception
          raise unless handle_exception($!, result)
        end
      end

      def handle_exception(exception, result)
        case exception
        when *ErrorHandler::PASS_THROUGH_EXCEPTIONS
          false
        else
          result.add_error(Error.new(@test_case.name, exception))
          true
        end
      end
    end
  end
end

--- NEW FILE: version.rb ---

# HACK: quick and dirty to get integrated into the new project - ryan
module Test
  module Unit
    VERSION = '2.0.1'
  end
end



More information about the cairo-commit mailing list