mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Initial revision
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@3477 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
4a44a6d474
commit
04f2b8f7bf
23 changed files with 2337 additions and 0 deletions
8
lib/rubyunit.rb
Normal file
8
lib/rubyunit.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'runit/testcase'
|
||||
require 'test/unit'
|
||||
|
||||
TestCase = RUNIT::TestCase
|
71
lib/runit/assert.rb
Normal file
71
lib/runit/assert.rb
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/assertions'
|
||||
require 'runit/error'
|
||||
|
||||
module RUNIT
|
||||
module Assert
|
||||
include Test::Unit::Assertions
|
||||
|
||||
def setup_assert
|
||||
end
|
||||
|
||||
def assert_no_exception(*args, &block)
|
||||
assert_nothing_raised(*args, &block)
|
||||
end
|
||||
|
||||
# To deal with the fact that RubyUnit does not check that the regular expression
|
||||
# is, indeed, a regular expression, if it is not, we do our own assertion using
|
||||
# the same semantics as RubyUnit
|
||||
def assert_match(actual_string, expected_re, message="")
|
||||
_wrap_assertion {
|
||||
full_message = build_message(message, actual_string, expected_re) {
|
||||
| arg1, arg2 |
|
||||
"Expected <#{arg1}> to match <#{arg2}>"
|
||||
}
|
||||
assert_block(full_message) {
|
||||
expected_re =~ actual_string
|
||||
}
|
||||
Regexp.last_match
|
||||
}
|
||||
end
|
||||
|
||||
def assert_not_match(actual_string, expected_re, message="")
|
||||
assert_no_match(expected_re, actual_string, message)
|
||||
end
|
||||
|
||||
def assert_matches(*args)
|
||||
assert_match(*args)
|
||||
end
|
||||
|
||||
def assert_fail(message="")
|
||||
flunk(message)
|
||||
end
|
||||
|
||||
def assert_equal_float(expected, actual, delta, message="")
|
||||
assert_in_delta(expected, actual, delta, message)
|
||||
end
|
||||
|
||||
def assert_send(object, method, *args)
|
||||
super([object, method, *args])
|
||||
end
|
||||
|
||||
def assert_exception(exception, message="", &block)
|
||||
assert_raises(exception, message, &block)
|
||||
end
|
||||
|
||||
def assert_respond_to(method, object, message="")
|
||||
if (called_internally?)
|
||||
super
|
||||
else
|
||||
super(object, method, message)
|
||||
end
|
||||
end
|
||||
|
||||
def called_internally?
|
||||
/assertions\.rb/.match(caller[1])
|
||||
end
|
||||
end
|
||||
end
|
51
lib/runit/cui/testrunner.rb
Normal file
51
lib/runit/cui/testrunner.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/ui/console/testrunner'
|
||||
require 'runit/testresult'
|
||||
|
||||
module RUNIT
|
||||
module CUI
|
||||
class TestRunner < Test::Unit::UI::Console::TestRunner
|
||||
@@quiet_mode = false
|
||||
|
||||
def self.run(suite)
|
||||
self.new().run(suite)
|
||||
end
|
||||
|
||||
def initialize
|
||||
super nil
|
||||
end
|
||||
|
||||
def run(suite, quiet_mode=@@quiet_mode)
|
||||
@suite = suite
|
||||
def @suite.suite
|
||||
self
|
||||
end
|
||||
@output_level = (quiet_mode ? PROGRESS_ONLY : NORMAL)
|
||||
start
|
||||
end
|
||||
|
||||
def create_mediator(suite)
|
||||
mediator = Test::Unit::UI::TestRunnerMediator.new(suite)
|
||||
class << mediator
|
||||
attr_writer :result_delegate
|
||||
def create_result
|
||||
return @result_delegate.create_result
|
||||
end
|
||||
end
|
||||
mediator.result_delegate = self
|
||||
return mediator
|
||||
end
|
||||
|
||||
def create_result
|
||||
return RUNIT::TestResult.new
|
||||
end
|
||||
|
||||
def self.quiet_mode=(boolean)
|
||||
@@quiet_mode = boolean
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
9
lib/runit/error.rb
Normal file
9
lib/runit/error.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/assertionfailederror.rb'
|
||||
|
||||
module RUNIT
|
||||
AssertionFailedError = Test::Unit::AssertionFailedError
|
||||
end
|
45
lib/runit/testcase.rb
Normal file
45
lib/runit/testcase.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'runit/testresult'
|
||||
require 'runit/testsuite'
|
||||
require 'runit/assert'
|
||||
require 'runit/error'
|
||||
require 'test/unit/testcase'
|
||||
|
||||
module RUNIT
|
||||
class TestCase < Test::Unit::TestCase
|
||||
include RUNIT::Assert
|
||||
|
||||
def self.suite
|
||||
method_names = instance_methods(true)
|
||||
tests = method_names.delete_if { |method_name| method_name !~ /^test/ }
|
||||
suite = TestSuite.new(name)
|
||||
tests.each {
|
||||
|test|
|
||||
catch(:invalid_test) {
|
||||
suite << new(test, name)
|
||||
}
|
||||
}
|
||||
return suite
|
||||
end
|
||||
|
||||
def initialize(test_name, suite_name=self.class.name)
|
||||
super(test_name)
|
||||
end
|
||||
|
||||
def assert_equals(*args)
|
||||
assert_equal(*args)
|
||||
end
|
||||
|
||||
def name
|
||||
super.sub(/^(.*?)\((.*)\)$/, '\2#\1')
|
||||
end
|
||||
|
||||
def run(result, &progress_block)
|
||||
progress_block = proc {} unless (block_given?)
|
||||
super(result, &progress_block)
|
||||
end
|
||||
end
|
||||
end
|
44
lib/runit/testresult.rb
Normal file
44
lib/runit/testresult.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/testresult'
|
||||
|
||||
module RUNIT
|
||||
class TestResult < Test::Unit::TestResult
|
||||
attr_reader(:errors, :failures)
|
||||
def succeed?
|
||||
return passed?
|
||||
end
|
||||
def failure_size
|
||||
return failure_count
|
||||
end
|
||||
def run_asserts
|
||||
return assertion_count
|
||||
end
|
||||
def error_size
|
||||
return error_count
|
||||
end
|
||||
def run_tests
|
||||
return run_count
|
||||
end
|
||||
def add_failure(failure)
|
||||
def failure.at
|
||||
return location
|
||||
end
|
||||
def failure.err
|
||||
return message
|
||||
end
|
||||
super(failure)
|
||||
end
|
||||
def add_error(error)
|
||||
def error.at
|
||||
return location
|
||||
end
|
||||
def error.err
|
||||
return exception
|
||||
end
|
||||
super(error)
|
||||
end
|
||||
end
|
||||
end
|
26
lib/runit/testsuite.rb
Normal file
26
lib/runit/testsuite.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/testsuite'
|
||||
|
||||
module RUNIT
|
||||
class TestSuite < Test::Unit::TestSuite
|
||||
def add_test(*args)
|
||||
add(*args)
|
||||
end
|
||||
|
||||
def add(*args)
|
||||
self.<<(*args)
|
||||
end
|
||||
|
||||
def count_test_cases
|
||||
return size
|
||||
end
|
||||
|
||||
def run(result, &progress_block)
|
||||
progress_block = proc {} unless (block_given?)
|
||||
super(result, &progress_block)
|
||||
end
|
||||
end
|
||||
end
|
8
lib/runit/topublic.rb
Normal file
8
lib/runit/topublic.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
module RUNIT
|
||||
module ToPublic
|
||||
end
|
||||
end
|
214
lib/test/unit.rb
Normal file
214
lib/test/unit.rb
Normal file
|
@ -0,0 +1,214 @@
|
|||
# :include: ../../../../README
|
||||
#
|
||||
# ----
|
||||
#
|
||||
# = Usage
|
||||
#
|
||||
# The general idea behind unit testing is that you write a _test_
|
||||
# _method_ that makes certain _assertions_ about your code, working
|
||||
# against a _test_ _fixture_. A bunch of these _test_ _methods_ are
|
||||
# bundled up into a _test_ _suite_ and can be run any time the
|
||||
# developer wants. The results of a run are gathered in a _test_
|
||||
# _result_ and displayed to the user through some UI. So, lets break
|
||||
# this down and see how Test::Unit provides each of these necessary
|
||||
# pieces.
|
||||
#
|
||||
#
|
||||
# == Assertions
|
||||
#
|
||||
# These are the heart of the framework. Think of an assertion as a
|
||||
# statement of expected outcome, i.e. "I assert that x should be equal
|
||||
# to y". If, when the assertion is executed, it turns out to be
|
||||
# correct, nothing happens, and life is good. If, on the other hand,
|
||||
# your assertion turns out to be false, an error is propagated with
|
||||
# pertinent information so that you can go back and make your
|
||||
# assertion succeed, and, once again, life is good. For an explanation
|
||||
# of the current assertions, see Test::Unit::Assertions.
|
||||
#
|
||||
#
|
||||
# == Test Method & Test Fixture
|
||||
#
|
||||
# Obviously, these assertions have to be called within a context that
|
||||
# knows about them and can do something meaningful with their
|
||||
# pass/fail value. Also, it's handy to collect a bunch of related
|
||||
# tests, each test represented by a method, into a common test class
|
||||
# that knows how to run them. The tests will be in a separate class
|
||||
# from the code they're testing for a couple of reasons. First of all,
|
||||
# it allows your code to stay uncluttered with test code, making it
|
||||
# easier to maintain. Second, it allows the tests to be stripped out
|
||||
# for deployment, since they're really there for you, the developer,
|
||||
# and your users don't need them. Third, and most importantly, it
|
||||
# allows you to set up a common test fixture for your tests to run
|
||||
# against.
|
||||
#
|
||||
# What's a test fixture? Well, tests do not live in a vacuum; rather,
|
||||
# they're run against the code they are testing. Often, a collection
|
||||
# of tests will run against a common set of data, also called a
|
||||
# fixture. If they're all bundled into the same test class, they can
|
||||
# all share the setting up and tearing down of that data, eliminating
|
||||
# unnecessary duplication and making it much easier to add related
|
||||
# tests.
|
||||
#
|
||||
# Test::Unit::TestCase wraps up a collection of test methods together
|
||||
# and allows you to easily set up and tear down the same test fixture
|
||||
# for each test. This is done by overriding #setup and/or #teardown,
|
||||
# which will be called before and after each test method that is
|
||||
# run. The TestCase also knows how to collect the results of your
|
||||
# assertions into a Test::Unit::TestResult, which can then be reported
|
||||
# back to you... but I'm getting ahead of myself. To write a test,
|
||||
# follow these steps:
|
||||
#
|
||||
# * Make sure Test::Unit is in your library path.
|
||||
# * require 'test/unit' in your test script.
|
||||
# * Create a class that subclasses Test::Unit::TestCase.
|
||||
# * Add a method that begins with "test" to your class.
|
||||
# * Make assertions in your test method.
|
||||
# * Optionally define #setup and/or #teardown to set up and/or tear
|
||||
# down your common test fixture.
|
||||
# * You can now run your test as you would any other Ruby
|
||||
# script... try it and see!
|
||||
#
|
||||
# A really simple test might look like this (#setup and #teardown are
|
||||
# commented out to indicate that they are completely optional):
|
||||
#
|
||||
# require 'test/unit'
|
||||
#
|
||||
# class TC_MyTest < Test::Unit::TestCase
|
||||
# # def setup
|
||||
# # end
|
||||
#
|
||||
# # def teardown
|
||||
# # end
|
||||
#
|
||||
# def test_fail
|
||||
# assert(false, 'Assertion was false.')
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# == Test Runners
|
||||
#
|
||||
# So, now you have this great test class, but you still need a way to
|
||||
# run it and view any failures that occur during the run. This is
|
||||
# where Test::Unit::UI::Console::TestRunner (and others, such as
|
||||
# Test::Unit::UI::GTK::TestRunner) comes into play. The console test
|
||||
# runner is automatically invoked for you if you require 'test/unit'
|
||||
# and simply run the file. To use another runner, or to manually
|
||||
# invoke a runner, simply call its run class method and pass in an
|
||||
# object that responds to the suite message with a
|
||||
# Test::Unit::TestSuite. This can be as simple as passing in your
|
||||
# TestCase class (which has a class suite method). It might look
|
||||
# something like this:
|
||||
#
|
||||
# require 'test/unit/ui/console/testrunner'
|
||||
# Test::Unit::UI::Console::TestRunner.run(TC_MyTest)
|
||||
#
|
||||
#
|
||||
# == Test Suite
|
||||
#
|
||||
# As more and more unit tests accumulate for a given project, it
|
||||
# becomes a real drag running them one at a time, and it also
|
||||
# introduces the potential to overlook a failing test because you
|
||||
# forget to run it. Suddenly it becomes very handy that the
|
||||
# TestRunners can take any object that returns a Test::Unit::TestSuite
|
||||
# in response to a suite method. The TestSuite can, in turn, contain
|
||||
# other TestSuites or individual tests (typically created by a
|
||||
# TestCase). In other words, you can easily wrap up a group of
|
||||
# TestCases and TestSuites like this:
|
||||
#
|
||||
# require 'test/unit/testsuite'
|
||||
# require 'tc_myfirsttests'
|
||||
# require 'tc_moretestsbyme'
|
||||
# require 'ts_anothersetoftests'
|
||||
#
|
||||
# class TS_MyTests
|
||||
# def self.suite
|
||||
# suite = Test::Unit::TestSuite.new
|
||||
# suite << TC_MyFirstTests.suite
|
||||
# suite << TC_MoreTestsByMe.suite
|
||||
# suite << TS_AnotherSetOfTests.suite
|
||||
# return suite
|
||||
# end
|
||||
# end
|
||||
# Test::Unit::UI::Console::TestRunner.run(TS_MyTests)
|
||||
#
|
||||
# Now, this is a bit cumbersome, so Test::Unit does a little bit more
|
||||
# for you, by wrapping these up automatically when you require
|
||||
# 'test/unit'. What does this mean? It means you could write the above
|
||||
# test case like this instead:
|
||||
#
|
||||
# require 'test/unit'
|
||||
# require 'tc_myfirsttests'
|
||||
# require 'tc_moretestsbyme'
|
||||
# require 'ts_anothersetoftests'
|
||||
#
|
||||
# Test::Unit is smart enough to find all the test cases existing in
|
||||
# the ObjectSpace and wrap them up into a suite for you. It then runs
|
||||
# the dynamic suite using the console TestRunner.
|
||||
#
|
||||
#
|
||||
# == Questions?
|
||||
#
|
||||
# I'd really like to get feedback from all levels of Ruby
|
||||
# practitioners about typos, grammatical errors, unclear statements,
|
||||
# missing points, etc., in this document (or any other).
|
||||
|
||||
|
||||
|
||||
|
||||
require 'test/unit/testcase'
|
||||
require 'test/unit/ui/testrunnermediator'
|
||||
|
||||
at_exit {
|
||||
# We can't debug tests run with at_exit unless we add the following:
|
||||
set_trace_func DEBUGGER__.context.method(:trace_func).to_proc if (defined? DEBUGGER__)
|
||||
|
||||
if (!Test::Unit::UI::TestRunnerMediator.run?)
|
||||
suite_name = $0.sub(/\.rb$/, '')
|
||||
suite = Test::Unit::TestSuite.new(suite_name)
|
||||
test_classes = []
|
||||
ObjectSpace.each_object(Class) {
|
||||
| klass |
|
||||
test_classes << klass if (Test::Unit::TestCase > klass)
|
||||
}
|
||||
|
||||
runners = {
|
||||
'--console' => proc do |suite|
|
||||
require 'test/unit/ui/console/testrunner'
|
||||
Test::Unit::UI::Console::TestRunner.run(suite)
|
||||
end,
|
||||
'--gtk' => proc do |suite|
|
||||
require 'test/unit/ui/gtk/testrunner'
|
||||
Test::Unit::UI::GTK::TestRunner.run(suite)
|
||||
end,
|
||||
'--fox' => proc do |suite|
|
||||
require 'test/unit/ui/fox/testrunner'
|
||||
Test::Unit::UI::Fox::TestRunner.run(suite)
|
||||
end,
|
||||
}
|
||||
|
||||
unless (ARGV.empty?)
|
||||
runner = runners[ARGV[0]]
|
||||
ARGV.shift unless (runner.nil?)
|
||||
end
|
||||
runner = runners['--console'] if (runner.nil?)
|
||||
|
||||
if ARGV.empty?
|
||||
test_classes.each { |klass| suite << klass.suite }
|
||||
else
|
||||
tests = test_classes.map { |klass| klass.suite.tests }.flatten
|
||||
criteria = ARGV.map { |arg| (arg =~ %r{^/(.*)/$}) ? Regexp.new($1) : arg }
|
||||
criteria.each {
|
||||
| criterion |
|
||||
if (criterion.instance_of?(Regexp))
|
||||
tests.each { |test| suite << test if (criterion =~ test.name) }
|
||||
elsif (/^A-Z/ =~ criterion)
|
||||
tests.each { |test| suite << test if (criterion == test.class.name) }
|
||||
else
|
||||
tests.each { |test| suite << test if (criterion == test.method_name) }
|
||||
end
|
||||
}
|
||||
end
|
||||
runner.call(suite)
|
||||
end
|
||||
}
|
14
lib/test/unit/assertionfailederror.rb
Normal file
14
lib/test/unit/assertionfailederror.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# 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 < Exception
|
||||
end
|
||||
end
|
||||
end
|
394
lib/test/unit/assertions.rb
Normal file
394
lib/test/unit/assertions.rb
Normal file
|
@ -0,0 +1,394 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/assertionfailederror'
|
||||
|
||||
module Test # :nodoc:
|
||||
module Unit # :nodoc:
|
||||
|
||||
# Contains all of the standard Test::Unit assertions. Mixed in
|
||||
# to Test::Unit::TestCase. To mix it in and use its
|
||||
# functionality, you simply need to rescue
|
||||
# Test::Unit::AssertionFailedError, and you can additionally
|
||||
# override add_assertion to be notified whenever an assertion
|
||||
# is made.
|
||||
#
|
||||
# Notes:
|
||||
# * The message to each assertion, if given, will be
|
||||
# propagated with the failure.
|
||||
# * It's easy to add your own assertions based on assert_block().
|
||||
module Assertions
|
||||
|
||||
# The assertion upon which all other assertions are
|
||||
# based. Passes if the block yields true.
|
||||
public
|
||||
def assert_block(message="") # :yields:
|
||||
_wrap_assertion do
|
||||
if (! yield)
|
||||
raise AssertionFailedError.new(message.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if boolean is true.
|
||||
public
|
||||
def assert(boolean, message="")
|
||||
_wrap_assertion do
|
||||
assert_block("assert should not be called with a block.") { !block_given? }
|
||||
assert_block(message) { 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.
|
||||
public
|
||||
def assert_equal(expected, actual, message=nil)
|
||||
full_message = build_message(message, expected, actual) do |arg1, arg2|
|
||||
"<#{arg1}> expected but was\n" +
|
||||
"<#{arg2}>"
|
||||
end
|
||||
assert_block(full_message) { expected == actual }
|
||||
end
|
||||
|
||||
# Passes if block raises exception.
|
||||
public
|
||||
def assert_raises(expected_exception_klass, message="")
|
||||
_wrap_assertion do
|
||||
assert_instance_of(Class, expected_exception_klass, "Should expect a class of exception")
|
||||
actual_exception = nil
|
||||
full_message = build_message(message, expected_exception_klass) do |arg|
|
||||
"<#{arg}> exception expected but none was thrown"
|
||||
end
|
||||
assert_block(full_message) do
|
||||
thrown = false
|
||||
begin
|
||||
yield
|
||||
rescue Exception => thrown_exception
|
||||
actual_exception = thrown_exception
|
||||
thrown = true
|
||||
end
|
||||
thrown
|
||||
end
|
||||
full_message = build_message(message, expected_exception_klass, actual_exception) do |arg1, arg2|
|
||||
"<#{arg1}> exception expected but was\n" +
|
||||
arg2
|
||||
end
|
||||
assert_block(full_message) { expected_exception_klass == actual_exception.class }
|
||||
actual_exception
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if object.class == klass.
|
||||
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, object, klass, object.class) do |arg1, arg2, arg3|
|
||||
"<#{arg1}> expected to be an instance of\n" +
|
||||
"<#{arg2}> but was\n" +
|
||||
"<#{arg3}>"
|
||||
end
|
||||
assert_block(full_message) { klass == object.class }
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if object.nil?.
|
||||
public
|
||||
def assert_nil(object, message="")
|
||||
assert_equal(nil, object, message)
|
||||
end
|
||||
|
||||
# Passes if object.kind_of?(klass).
|
||||
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, object, klass) do |arg1, arg2|
|
||||
"<#{arg1}>\n" +
|
||||
"expected to be kind_of?<#{arg2}>"
|
||||
end
|
||||
assert_block(full_message) { object.kind_of?(klass) }
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if object.respond_to?(method) is true.
|
||||
public
|
||||
def assert_respond_to(object, method, message="")
|
||||
_wrap_assertion do
|
||||
assert(method.kind_of?(Symbol) || method.kind_of?(String), "The method argument to #assert_respond_to should be specified as a Symbol or a String.")
|
||||
full_message = build_message(message, object, object.class, method) do |arg1, arg2, arg3|
|
||||
"<#{arg1}>\n" +
|
||||
"of type <#{arg2}>\n" +
|
||||
"expected to respond_to?<#{arg3}>"
|
||||
end
|
||||
assert_block(full_message) { object.respond_to?(method) }
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if string =~ pattern.
|
||||
public
|
||||
def assert_match(pattern, string, message="")
|
||||
_wrap_assertion do
|
||||
full_message = build_message(message, string, pattern) do |arg1, arg2|
|
||||
"<#{arg1}> expected to be =~\n" +
|
||||
"<#{arg2}>"
|
||||
end
|
||||
assert_block(full_message) { string =~ pattern }
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if actual.equal?(expected) (i.e. they are the
|
||||
# same instance).
|
||||
public
|
||||
def assert_same(expected, actual, message="")
|
||||
full_message = build_message(message, expected, expected.__id__, actual, actual.__id__) do |arg1, arg2, arg3, arg4|
|
||||
"<#{arg1}:#{arg2}> expected to be equal? to\n" +
|
||||
"<#{arg3}:#{arg4}>"
|
||||
end
|
||||
assert_block(full_message) { actual.equal?(expected) }
|
||||
end
|
||||
|
||||
# Compares the two objects based on the passed
|
||||
# operator. Passes if object1.send(operator, object2) is
|
||||
# true.
|
||||
public
|
||||
def assert_operator(object1, operator, object2, message="")
|
||||
full_message = build_message(message, object1, operator, object2) do |arg1, arg2, arg3|
|
||||
"<#{arg1}> expected to be\n" +
|
||||
"#{arg2}\n" +
|
||||
"<#{arg3}>"
|
||||
end
|
||||
assert_block(full_message) { object1.send(operator, object2) }
|
||||
end
|
||||
|
||||
# Passes if block does not raise an exception.
|
||||
public
|
||||
def assert_nothing_raised(*args)
|
||||
_wrap_assertion do
|
||||
message = ""
|
||||
if (!args[-1].instance_of?(Class))
|
||||
message = args.pop
|
||||
end
|
||||
begin
|
||||
yield
|
||||
rescue Exception => thrown_exception
|
||||
if (args.empty? || args.include?(thrown_exception.class))
|
||||
full_message = build_message(message, thrown_exception) do |arg1|
|
||||
"Exception raised:\n" +
|
||||
arg1
|
||||
end
|
||||
flunk(full_message)
|
||||
else
|
||||
raise thrown_exception.class, thrown_exception.message, thrown_exception.backtrace
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Always fails.
|
||||
public
|
||||
def flunk(message="")
|
||||
assert(false, message)
|
||||
end
|
||||
|
||||
# Passes if !actual.equal?(expected).
|
||||
public
|
||||
def assert_not_same(expected, actual, message="")
|
||||
full_message = build_message(message, expected, expected.__id__, actual, actual.__id__) do |arg1, arg2, arg3, arg4|
|
||||
"<#{arg1}:#{arg2}> expected to not be equal? to\n" +
|
||||
"<#{arg3}:#{arg4}>"
|
||||
end
|
||||
assert_block(full_message) { !actual.equal?(expected) }
|
||||
end
|
||||
|
||||
# Passes if expected != actual.
|
||||
public
|
||||
def assert_not_equal(expected, actual, message="")
|
||||
full_message = build_message(message, expected, actual) do |arg1, arg2|
|
||||
"<#{arg1}> expected to be != to\n" +
|
||||
"<#{arg2}>"
|
||||
end
|
||||
assert_block(full_message) { expected != actual }
|
||||
end
|
||||
|
||||
# Passes if !object.nil?.
|
||||
public
|
||||
def assert_not_nil(object, message="")
|
||||
full_message = build_message(message, object) do |arg|
|
||||
"<#{arg}> expected to not be nil"
|
||||
end
|
||||
assert_block(full_message) { !object.nil? }
|
||||
end
|
||||
|
||||
# Passes if string !~ regularExpression.
|
||||
public
|
||||
def assert_no_match(regexp, string, message="")
|
||||
_wrap_assertion do
|
||||
assert_instance_of(Regexp, regexp, "The first argument to assert_does_not_match should be a Regexp.")
|
||||
full_message = build_message(message, regexp.source, string) do |arg1, arg2|
|
||||
"</#{arg1}/> expected to not match\n" +
|
||||
" <#{arg2}>"
|
||||
end
|
||||
assert_block(full_message) { regexp !~ string }
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if block throws symbol.
|
||||
public
|
||||
def assert_throws(expected_symbol, message="", &proc)
|
||||
_wrap_assertion do
|
||||
assert_instance_of(Symbol, expected_symbol, "assert_throws expects the symbol that should be thrown for its first argument")
|
||||
assert(block_given?, "Should have passed a block to assert_throws")
|
||||
caught = true
|
||||
begin
|
||||
catch(expected_symbol) do
|
||||
proc.call
|
||||
caught = false
|
||||
end
|
||||
full_message = build_message(message, expected_symbol) do |arg|
|
||||
"<:#{arg}> should have been thrown"
|
||||
end
|
||||
assert(caught, full_message)
|
||||
rescue NameError => name_error
|
||||
if ( name_error.message !~ /^uncaught throw `(.+)'$/ ) #`
|
||||
raise name_error
|
||||
end
|
||||
full_message = build_message(message, expected_symbol, $1) do |arg1, arg2|
|
||||
"<:#{arg1}> expected to be thrown but\n" +
|
||||
"<:#{arg2}> was thrown"
|
||||
end
|
||||
flunk(full_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if block does not throw anything.
|
||||
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 => name_error
|
||||
if (name_error.message !~ /^uncaught throw `(.+)'$/ ) #`
|
||||
raise name_error
|
||||
end
|
||||
full_message = build_message(message, $1) do |arg|
|
||||
"<:#{arg}> was thrown when nothing was expected"
|
||||
end
|
||||
flunk(full_message)
|
||||
end
|
||||
full_message = build_message(message) { || "Expected nothing to be thrown" }
|
||||
assert(true, full_message)
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if expected_float and actual_float are equal
|
||||
# within delta tolerance.
|
||||
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, expected_float, actual_float, delta) do |arg1, arg2, arg3|
|
||||
"<#{arg1}> and\n" +
|
||||
"<#{arg2}> expected to be within\n" +
|
||||
"<#{arg3}> of each other"
|
||||
end
|
||||
assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f }
|
||||
end
|
||||
end
|
||||
|
||||
# Passes if the method sent returns a true value.
|
||||
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, send_array[0], send_array[1], send_array[2..-1]) do |arg1, arg2, arg3|
|
||||
"<#{arg1}> expected to respond to\n" +
|
||||
"<#{arg2}(#{arg3})> with true"
|
||||
end
|
||||
assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) }
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
def build_message(message, *arguments, &block) # :nodoc:
|
||||
return AssertionMessage.new(message.to_s, arguments, block)
|
||||
end
|
||||
|
||||
private
|
||||
def _wrap_assertion # :nodoc:
|
||||
@_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.
|
||||
private
|
||||
def add_assertion
|
||||
end
|
||||
|
||||
class AssertionMessage # :nodoc: all
|
||||
def self.convert(object)
|
||||
case object
|
||||
when String
|
||||
return object
|
||||
when Symbol
|
||||
return object.to_s
|
||||
when Regexp
|
||||
return "/#{object.source}/"
|
||||
when Exception
|
||||
return "Class: <#{object.class}>\n" +
|
||||
"Message: <#{object.message}>\n" +
|
||||
"---Backtrace---\n" +
|
||||
object.backtrace.join("\n") + "\n" +
|
||||
"---------------"
|
||||
else
|
||||
return object.inspect
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(message, parameters, block)
|
||||
@message = message
|
||||
@parameters = parameters
|
||||
@block = block
|
||||
end
|
||||
|
||||
def to_s
|
||||
message_parts = []
|
||||
if (@message != nil && @message != "")
|
||||
if (@message !~ /\.$/)
|
||||
@message << "."
|
||||
end
|
||||
message_parts << @message
|
||||
end
|
||||
@parameters = @parameters.collect {
|
||||
| parameter |
|
||||
self.class.convert(parameter)
|
||||
}
|
||||
message_parts << @block.call(*@parameters)
|
||||
return message_parts.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
68
lib/test/unit/error.rb
Normal file
68
lib/test/unit/error.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
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
|
||||
attr_reader(:location, :exception)
|
||||
|
||||
SINGLE_CHARACTER = 'E'
|
||||
|
||||
# Creates a new Error with the given location and
|
||||
# exception.
|
||||
def initialize(location, exception)
|
||||
@location = location
|
||||
@exception = exception
|
||||
end
|
||||
|
||||
# Returns a single character representation of an error.
|
||||
def single_character_display
|
||||
SINGLE_CHARACTER
|
||||
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
|
||||
"#{@location}:\n#{message}"
|
||||
end
|
||||
|
||||
# Returns a verbose version of the error description.
|
||||
def long_display
|
||||
backtrace = self.class.filter(@exception.backtrace).join("\n ")
|
||||
"Error!!!\n#{short_display}\n #{backtrace}"
|
||||
end
|
||||
|
||||
# Overridden to return long_display.
|
||||
def to_s
|
||||
long_display
|
||||
end
|
||||
|
||||
SEPARATOR_PATTERN = '[\\\/:]'
|
||||
def self.filter(backtrace) # :nodoc:
|
||||
@test_unit_patterns ||= $:.collect {
|
||||
| path |
|
||||
/^#{Regexp.escape(path)}#{SEPARATOR_PATTERN}test#{SEPARATOR_PATTERN}unit#{SEPARATOR_PATTERN}/
|
||||
}.push(/#{SEPARATOR_PATTERN}test#{SEPARATOR_PATTERN}unit\.rb/)
|
||||
|
||||
return backtrace.delete_if {
|
||||
| line |
|
||||
@test_unit_patterns.detect {
|
||||
| pattern |
|
||||
line =~ pattern
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
45
lib/test/unit/failure.rb
Normal file
45
lib/test/unit/failure.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# 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(:location, :message)
|
||||
|
||||
SINGLE_CHARACTER = 'F'
|
||||
|
||||
# Creates a new Failure with the given location and
|
||||
# message.
|
||||
def initialize(location, message)
|
||||
@location = location
|
||||
@message = message
|
||||
end
|
||||
|
||||
# Returns a single character representation of a failure.
|
||||
def single_character_display
|
||||
SINGLE_CHARACTER
|
||||
end
|
||||
|
||||
# Returns a brief version of the error description.
|
||||
def short_display
|
||||
"#{@location}:\n#{@message}"
|
||||
end
|
||||
|
||||
# Returns a verbose version of the error description.
|
||||
def long_display
|
||||
"Failure!!!\n#{short_display}"
|
||||
end
|
||||
|
||||
# Overridden to return long_display.
|
||||
def to_s
|
||||
long_display
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
152
lib/test/unit/testcase.rb
Normal file
152
lib/test/unit/testcase.rb
Normal file
|
@ -0,0 +1,152 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/assertions'
|
||||
require 'test/unit/failure'
|
||||
require 'test/unit/error'
|
||||
require 'test/unit/testsuite'
|
||||
require 'test/unit/assertionfailederror'
|
||||
|
||||
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.
|
||||
class TestCase
|
||||
include Assertions
|
||||
|
||||
attr_reader :method_name
|
||||
|
||||
STARTED = name + "::STARTED"
|
||||
FINISHED = name + "::FINISHED"
|
||||
|
||||
# Creates a new instance of the fixture for running the
|
||||
# test represented by test_method_name.
|
||||
def initialize(test_method_name)
|
||||
if ((!respond_to?(test_method_name)) || (method(test_method_name).arity != 0))
|
||||
throw :invalid_test
|
||||
end
|
||||
@method_name = test_method_name
|
||||
@test_passed = true
|
||||
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 self.suite
|
||||
method_names = public_instance_methods(true)
|
||||
tests = method_names.delete_if { |method_name| method_name !~ /^test.+/ }
|
||||
suite = TestSuite.new(name)
|
||||
tests.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
|
||||
return suite
|
||||
end
|
||||
|
||||
# Runs the individual test method represented by this
|
||||
# instance of the fixture, collecting statistics, failures
|
||||
# and errors in result.
|
||||
def run(result)
|
||||
yield(STARTED, name)
|
||||
@_result = result
|
||||
begin
|
||||
setup
|
||||
send(@method_name)
|
||||
rescue AssertionFailedError => e
|
||||
add_failure(e.message, e.backtrace)
|
||||
rescue StandardError, ScriptError
|
||||
add_error($!)
|
||||
ensure
|
||||
begin
|
||||
teardown
|
||||
rescue AssertionFailedError => e
|
||||
add_failure(e.message, e.backtrace)
|
||||
rescue StandardError, ScriptError
|
||||
add_error($!)
|
||||
end
|
||||
end
|
||||
result.add_run
|
||||
yield(FINISHED, name)
|
||||
end
|
||||
|
||||
# Called before every test method runs. Can be used
|
||||
# to set up fixture information.
|
||||
def setup
|
||||
end
|
||||
|
||||
# Called after every test method runs. Can be used to tear
|
||||
# down fixture information.
|
||||
def teardown
|
||||
end
|
||||
|
||||
def default_test
|
||||
flunk("No tests were specified")
|
||||
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?
|
||||
return @test_passed
|
||||
end
|
||||
private :passed?
|
||||
|
||||
def size # :nodoc:
|
||||
1
|
||||
end
|
||||
|
||||
def add_assertion # :nodoc:
|
||||
@_result.add_assertion
|
||||
end
|
||||
private :add_assertion
|
||||
|
||||
def add_failure(message, all_locations=caller()) # :nodoc:
|
||||
@test_passed = false
|
||||
assertions_pattern = /[^A-Za-z_]assertions\.rb:/
|
||||
if (all_locations.detect { |entry| entry =~ assertions_pattern })
|
||||
all_locations.shift
|
||||
until (all_locations[0] =~ assertions_pattern || all_locations.empty?)
|
||||
all_locations.shift
|
||||
end
|
||||
location = all_locations.detect { |entry| entry !~ assertions_pattern }
|
||||
else
|
||||
location = all_locations[0]
|
||||
end
|
||||
location = location[/^.+:\d+/]
|
||||
@_result.add_failure(Failure.new("#{name} [#{location}]", message))
|
||||
end
|
||||
private :add_failure
|
||||
|
||||
def add_error(exception) # :nodoc:
|
||||
@test_passed = false
|
||||
@_result.add_error(Error.new(name, exception))
|
||||
end
|
||||
private :add_error
|
||||
|
||||
# Returns a human-readable name for the specific test that
|
||||
# this instance of TestCase represents.
|
||||
def name
|
||||
"#{@method_name}(#{self.class.name})"
|
||||
end
|
||||
|
||||
# Overriden to return #name.
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
81
lib/test/unit/testresult.rb
Normal file
81
lib/test/unit/testresult.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/util/observable'
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
|
||||
# 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
|
||||
|
||||
CHANGED = "CHANGED"
|
||||
FAULT = "FAULT"
|
||||
|
||||
attr_reader(:run_count, :assertion_count)
|
||||
|
||||
# Constructs a new, empty TestResult.
|
||||
def initialize
|
||||
@run_count, @assertion_count = 0, 0
|
||||
@failures, @errors = Array.new, Array.new
|
||||
end
|
||||
|
||||
# Records a test run.
|
||||
def add_run
|
||||
@run_count += 1
|
||||
notify_listeners(CHANGED, self)
|
||||
end
|
||||
|
||||
# Records a Test::Unit::Failure.
|
||||
def add_failure(failure)
|
||||
@failures << failure
|
||||
notify_listeners(FAULT, failure)
|
||||
notify_listeners(CHANGED, self)
|
||||
end
|
||||
|
||||
# Records a Test::Unit::Error.
|
||||
def add_error(error)
|
||||
@errors << error
|
||||
notify_listeners(FAULT, error)
|
||||
notify_listeners(CHANGED, self)
|
||||
end
|
||||
|
||||
# Records an individual assertion.
|
||||
def add_assertion
|
||||
@assertion_count += 1
|
||||
notify_listeners(CHANGED, self)
|
||||
end
|
||||
|
||||
# Returns a string contain the recorded runs, assertions,
|
||||
# failures and errors in this TestResult.
|
||||
def to_s
|
||||
"#{run_count} tests, #{assertion_count} assertions, #{failure_count} failures, #{error_count} errors"
|
||||
end
|
||||
|
||||
# Returns whether or not this TestResult represents
|
||||
# successful completion.
|
||||
def passed?
|
||||
return @failures.empty? && @errors.empty?
|
||||
end
|
||||
|
||||
# Returns the number of failures this TestResult has
|
||||
# recorded.
|
||||
def failure_count
|
||||
return @failures.size
|
||||
end
|
||||
|
||||
# Returns the number of errors this TestResult has
|
||||
# recorded.
|
||||
def error_count
|
||||
return @errors.size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
64
lib/test/unit/testsuite.rb
Normal file
64
lib/test/unit/testsuite.rb
Normal file
|
@ -0,0 +1,64 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
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")
|
||||
@name = name
|
||||
@tests = []
|
||||
end
|
||||
|
||||
# Runs the tests and/or suites contained in this
|
||||
# TestSuite.
|
||||
def run(result, &progress_block)
|
||||
yield(STARTED, name)
|
||||
@tests.sort { |test1, test2| test1.name <=> test2.name }.each do |test|
|
||||
test.run(result, &progress_block)
|
||||
end
|
||||
yield(FINISHED, name)
|
||||
end
|
||||
|
||||
# Adds the test to the suite.
|
||||
def <<(test)
|
||||
@tests << 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
|
||||
|
||||
# Overriden to return the name given the suite at
|
||||
# creation.
|
||||
def to_s
|
||||
@name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
135
lib/test/unit/ui/console/testrunner.rb
Normal file
135
lib/test/unit/ui/console/testrunner.rb
Normal file
|
@ -0,0 +1,135 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/ui/testrunnermediator'
|
||||
require 'test/unit/ui/testrunnerutilities'
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module UI
|
||||
module Console # :nodoc:
|
||||
|
||||
# Runs a Test::Unit::TestSuite on the console.
|
||||
class TestRunner
|
||||
extend TestRunnerUtilities
|
||||
|
||||
SILENT = 0
|
||||
PROGRESS_ONLY = 1
|
||||
NORMAL = 2
|
||||
VERBOSE = 3
|
||||
|
||||
# Creates a new TestRunner and runs the suite.
|
||||
def self.run(suite, output_level=NORMAL)
|
||||
return new(suite, output_level).start
|
||||
end
|
||||
|
||||
# Creates a new TestRunner for running the passed
|
||||
# suite. If quiet_mode is true, the output while
|
||||
# running is limited to progress dots, errors and
|
||||
# failures, and the final result. io specifies
|
||||
# where runner output should go to; defaults to
|
||||
# STDERR.
|
||||
def initialize(suite, output_level=NORMAL, io=STDOUT)
|
||||
if (suite.respond_to?(:suite))
|
||||
@suite = suite.suite
|
||||
else
|
||||
@suite = suite
|
||||
end
|
||||
@output_level = output_level
|
||||
@io = io
|
||||
@already_outputted = false
|
||||
@faults = []
|
||||
end
|
||||
|
||||
# Begins the test run.
|
||||
def start
|
||||
setup_mediator
|
||||
attach_to_mediator
|
||||
return start_mediator
|
||||
end
|
||||
|
||||
private
|
||||
def setup_mediator # :nodoc:
|
||||
@mediator = create_mediator(@suite)
|
||||
suite_name = @suite.to_s
|
||||
if ( @suite.kind_of?(Module) )
|
||||
suite_name = @suite.name
|
||||
end
|
||||
output("Loaded suite #{suite_name}")
|
||||
end
|
||||
|
||||
def create_mediator(suite) # :nodoc:
|
||||
return TestRunnerMediator.new(suite)
|
||||
end
|
||||
|
||||
def attach_to_mediator # :nodoc:
|
||||
@mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
||||
@mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
||||
@mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
||||
@mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
||||
@mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
|
||||
end
|
||||
|
||||
def start_mediator # :nodoc:
|
||||
return @mediator.run_suite
|
||||
end
|
||||
|
||||
def add_fault(fault) # :nodoc:
|
||||
@faults << fault
|
||||
output_single(fault.single_character_display, PROGRESS_ONLY)
|
||||
@already_outputted = true
|
||||
end
|
||||
|
||||
def started(result)
|
||||
@result = result
|
||||
output("Started")
|
||||
end
|
||||
|
||||
def finished(elapsed_time)
|
||||
nl
|
||||
output("Finished in #{elapsed_time} seconds.")
|
||||
@faults.each_with_index do |fault, index|
|
||||
nl
|
||||
output("%3d) %s" % [index + 1, fault.long_display])
|
||||
end
|
||||
nl
|
||||
output(@result)
|
||||
end
|
||||
|
||||
def test_started(name)
|
||||
output_single(name + ": ", VERBOSE)
|
||||
end
|
||||
|
||||
def test_finished(name)
|
||||
output_single(".", PROGRESS_ONLY) unless (@already_outputted)
|
||||
nl(VERBOSE)
|
||||
@already_outputted = false
|
||||
end
|
||||
|
||||
def nl(level=NORMAL)
|
||||
output("", level)
|
||||
end
|
||||
|
||||
def output(something, level=NORMAL)
|
||||
@io.puts(something) if (output?(level))
|
||||
end
|
||||
|
||||
def output_single(something, level=NORMAL)
|
||||
@io.write(something) if (output?(level))
|
||||
end
|
||||
|
||||
def output?(level)
|
||||
level <= @output_level
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
Test::Unit::UI::Console::TestRunner.start_command_line_test
|
||||
end
|
270
lib/test/unit/ui/fox/testrunner.rb
Normal file
270
lib/test/unit/ui/fox/testrunner.rb
Normal file
|
@ -0,0 +1,270 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'fox'
|
||||
require 'test/unit/ui/testrunnermediator'
|
||||
require 'test/unit/ui/testrunnerutilities'
|
||||
|
||||
include Fox
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module UI
|
||||
module Fox # :nodoc:
|
||||
|
||||
# Runs a Test::Unit::TestSuite in a Fox UI. Obviously,
|
||||
# this one requires you to have Fox
|
||||
# (http://www.fox-toolkit.org/fox.html) and the Ruby
|
||||
# Fox extension (http://fxruby.sourceforge.net/)
|
||||
# installed.
|
||||
class TestRunner
|
||||
|
||||
extend TestRunnerUtilities
|
||||
|
||||
RED_STYLE = FXRGBA(0xFF,0,0,0xFF) #0xFF000000
|
||||
GREEN_STYLE = FXRGBA(0,0xFF,0,0xFF) #0x00FF0000
|
||||
|
||||
# Creates a new TestRunner and runs the suite.
|
||||
def self.run(suite)
|
||||
new(suite).start
|
||||
end
|
||||
|
||||
# Creates a new TestRunner for running the passed
|
||||
# suite.
|
||||
def initialize(suite)
|
||||
if (suite.respond_to?(:suite))
|
||||
@suite = suite.suite
|
||||
else
|
||||
@suite = suite
|
||||
end
|
||||
|
||||
@red = false
|
||||
end
|
||||
|
||||
# Begins the test run.
|
||||
def start
|
||||
setup_ui
|
||||
setup_mediator
|
||||
attach_to_mediator
|
||||
start_ui
|
||||
end
|
||||
|
||||
def setup_mediator # :nodoc:
|
||||
@mediator = TestRunnerMediator.new(@suite)
|
||||
suite_name = @suite.to_s
|
||||
if ( @suite.kind_of?(Module) )
|
||||
suite_name = @suite.name
|
||||
end
|
||||
@suite_name_entry.text = suite_name
|
||||
end
|
||||
|
||||
def attach_to_mediator # :nodoc:
|
||||
@mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
|
||||
@mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
||||
@mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
|
||||
@mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
||||
@mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
||||
@mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
||||
end
|
||||
|
||||
def start_ui # :nodoc:
|
||||
@application.create
|
||||
@window.show(PLACEMENT_SCREEN)
|
||||
@application.addTimeout(1) do
|
||||
@mediator.run_suite
|
||||
end
|
||||
@application.run
|
||||
end
|
||||
|
||||
def stop # :nodoc:
|
||||
@application.exit(0)
|
||||
end
|
||||
|
||||
def reset_ui(count) # :nodoc:
|
||||
@test_progress_bar.barColor = GREEN_STYLE
|
||||
@test_progress_bar.total = count
|
||||
@test_progress_bar.progress = 0
|
||||
@red = false
|
||||
|
||||
@test_count_label.text = "0"
|
||||
@assertion_count_label.text = "0"
|
||||
@failure_count_label.text = "0"
|
||||
@error_count_label.text = "0"
|
||||
|
||||
@fault_list.clearItems
|
||||
end
|
||||
|
||||
def add_fault(fault) # :nodoc:
|
||||
if ( ! @red )
|
||||
@test_progress_bar.barColor = RED_STYLE
|
||||
@red = true
|
||||
end
|
||||
item = FaultListItem.new(fault)
|
||||
@fault_list.appendItem(item)
|
||||
end
|
||||
|
||||
def show_fault(fault) # :nodoc:
|
||||
raw_show_fault(fault.long_display)
|
||||
end
|
||||
|
||||
def raw_show_fault(string) # :nodoc:
|
||||
@detail_text.setText(string)
|
||||
end
|
||||
|
||||
def clear_fault # :nodoc:
|
||||
raw_show_fault("")
|
||||
end
|
||||
|
||||
def result_changed(result) # :nodoc:
|
||||
@test_progress_bar.progress = result.run_count
|
||||
|
||||
@test_count_label.text = result.run_count.to_s
|
||||
@assertion_count_label.text = result.assertion_count.to_s
|
||||
@failure_count_label.text = result.failure_count.to_s
|
||||
@error_count_label.text = result.error_count.to_s
|
||||
|
||||
# repaint now!
|
||||
@info_panel.repaint
|
||||
@application.flush
|
||||
end
|
||||
|
||||
def started(result) # :nodoc:
|
||||
output_status("Started...")
|
||||
end
|
||||
|
||||
def test_started(test_name)
|
||||
output_status("Running #{test_name}...")
|
||||
end
|
||||
|
||||
def finished(elapsed_time)
|
||||
output_status("Finished in #{elapsed_time} seconds")
|
||||
end
|
||||
|
||||
def output_status(string)
|
||||
@status_entry.text = string
|
||||
@status_entry.repaint
|
||||
end
|
||||
|
||||
def setup_ui # :nodoc:
|
||||
@application = create_application
|
||||
create_tooltip(@application)
|
||||
|
||||
@window = create_window(@application)
|
||||
|
||||
@status_entry = create_entry(@window)
|
||||
|
||||
main_panel = create_main_panel(@window)
|
||||
|
||||
suite_panel = create_suite_panel(main_panel)
|
||||
create_label(suite_panel, "Suite:")
|
||||
@suite_name_entry = create_entry(suite_panel)
|
||||
create_button(suite_panel, "&Run\tRun the current suite", proc { @mediator.run_suite })
|
||||
|
||||
@test_progress_bar = create_progress_bar(main_panel)
|
||||
|
||||
@info_panel = create_info_panel(main_panel)
|
||||
create_label(@info_panel, "Tests:")
|
||||
@test_count_label = create_label(@info_panel, "0")
|
||||
create_label(@info_panel, "Assertions:")
|
||||
@assertion_count_label = create_label(@info_panel, "0")
|
||||
create_label(@info_panel, "Failures:")
|
||||
@failure_count_label = create_label(@info_panel, "0")
|
||||
create_label(@info_panel, "Errors:")
|
||||
@error_count_label = create_label(@info_panel, "0")
|
||||
|
||||
list_panel = create_list_panel(main_panel)
|
||||
@fault_list = create_fault_list(list_panel)
|
||||
|
||||
detail_panel = create_detail_panel(main_panel)
|
||||
@detail_text = create_text(detail_panel)
|
||||
end
|
||||
|
||||
def create_application # :nodoc:
|
||||
app = FXApp.new("TestRunner", "Test::Unit")
|
||||
app.init([])
|
||||
app
|
||||
end
|
||||
|
||||
def create_window(app)
|
||||
FXMainWindow.new(app, "Test::Unit TestRunner", nil, nil, DECOR_ALL, 0, 0, 450)
|
||||
end
|
||||
|
||||
def create_tooltip(app)
|
||||
FXTooltip.new(app)
|
||||
end
|
||||
|
||||
def create_main_panel(parent) # :nodoc:
|
||||
panel = FXVerticalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y)
|
||||
panel.vSpacing = 10
|
||||
panel
|
||||
end
|
||||
|
||||
def create_suite_panel(parent) # :nodoc:
|
||||
FXHorizontalFrame.new(parent, LAYOUT_SIDE_LEFT | LAYOUT_FILL_X)
|
||||
end
|
||||
|
||||
def create_button(parent, text, action) # :nodoc:
|
||||
FXButton.new(parent, text).connect(SEL_COMMAND, &action)
|
||||
end
|
||||
|
||||
def create_progress_bar(parent) # :nodoc:
|
||||
FXProgressBar.new(parent, nil, 0, PROGRESSBAR_NORMAL | LAYOUT_FILL_X)
|
||||
end
|
||||
|
||||
def create_info_panel(parent) # :nodoc:
|
||||
FXMatrix.new(parent, 1, MATRIX_BY_ROWS | LAYOUT_FILL_X)
|
||||
end
|
||||
|
||||
def create_label(parent, text)
|
||||
FXLabel.new(parent, text, nil, JUSTIFY_CENTER_X | LAYOUT_FILL_COLUMN)
|
||||
end
|
||||
|
||||
def create_list_panel(parent) # :nodoc:
|
||||
FXHorizontalFrame.new(parent, LAYOUT_FILL_X | FRAME_SUNKEN | FRAME_THICK)
|
||||
end
|
||||
|
||||
def create_fault_list(parent) # :nodoc:
|
||||
list = FXList.new(parent, 10, nil, 0, LIST_SINGLESELECT | LAYOUT_FILL_X) #, 0, 0, 0, 150)
|
||||
list.connect(SEL_COMMAND) do |sender, sel, ptr|
|
||||
if sender.retrieveItem(sender.currentItem).selected?
|
||||
show_fault(sender.retrieveItem(sender.currentItem).fault)
|
||||
else
|
||||
clear_fault
|
||||
end
|
||||
end
|
||||
list
|
||||
end
|
||||
|
||||
def create_detail_panel(parent) # :nodoc:
|
||||
FXHorizontalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y | FRAME_SUNKEN | FRAME_THICK)
|
||||
end
|
||||
|
||||
def create_text(parent) # :nodoc:
|
||||
FXText.new(parent, nil, 0, TEXT_READONLY | LAYOUT_FILL_X | LAYOUT_FILL_Y)
|
||||
end
|
||||
|
||||
def create_entry(parent) # :nodoc:
|
||||
entry = FXTextField.new(parent, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X)
|
||||
entry.disable
|
||||
entry
|
||||
end
|
||||
end
|
||||
|
||||
class FaultListItem < FXListItem # :nodoc: all
|
||||
attr_reader(:fault)
|
||||
def initialize(fault)
|
||||
super(fault.short_display)
|
||||
@fault = fault
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
Test::Unit::UI::Fox::TestRunner.start_command_line_test
|
||||
end
|
389
lib/test/unit/ui/gtk/testrunner.rb
Normal file
389
lib/test/unit/ui/gtk/testrunner.rb
Normal file
|
@ -0,0 +1,389 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'gtk'
|
||||
require 'test/unit/ui/testrunnermediator'
|
||||
require 'test/unit/ui/testrunnerutilities'
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module UI
|
||||
module GTK # :nodoc:
|
||||
|
||||
# Runs a Test::Unit::TestSuite in a Gtk UI. Obviously,
|
||||
# this one requires you to have Gtk
|
||||
# (http://www.gtk.org/) and the Ruby Gtk extension
|
||||
# (http://ruby-gnome.sourceforge.net/) installed.
|
||||
class TestRunner
|
||||
extend TestRunnerUtilities
|
||||
|
||||
# Creates a new TestRunner and runs the suite.
|
||||
def self.run(suite)
|
||||
new(suite).start
|
||||
|
||||
end
|
||||
|
||||
# Creates a new TestRunner for running the passed
|
||||
# suite.
|
||||
def initialize(suite)
|
||||
if (suite.respond_to?(:suite))
|
||||
@suite = suite.suite
|
||||
else
|
||||
@suite = suite
|
||||
end
|
||||
end
|
||||
|
||||
# Begins the test run.
|
||||
def start
|
||||
setup_mediator
|
||||
setup_ui
|
||||
attach_to_mediator
|
||||
start_ui
|
||||
end
|
||||
|
||||
private
|
||||
def setup_mediator # :nodoc:
|
||||
@mediator = TestRunnerMediator.new(@suite)
|
||||
suite_name = @suite.to_s
|
||||
if ( @suite.kind_of?(Module) )
|
||||
suite_name = @suite.name
|
||||
end
|
||||
suite_name_entry.set_text(suite_name)
|
||||
end
|
||||
|
||||
def attach_to_mediator # :nodoc:
|
||||
run_button.signal_connect("clicked", nil) { @mediator.run_suite }
|
||||
@mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
|
||||
@mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
||||
@mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
|
||||
@mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
||||
@mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
||||
@mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
||||
end
|
||||
|
||||
def start_ui # :nodoc:
|
||||
timer = Gtk::timeout_add(0) {
|
||||
Gtk::timeout_remove(timer)
|
||||
@mediator.run_suite
|
||||
}
|
||||
Gtk.main
|
||||
end
|
||||
|
||||
def stop # :nodoc:
|
||||
Gtk.main_quit
|
||||
end
|
||||
|
||||
def reset_ui(count) # :nodoc:
|
||||
test_progress_bar.set_style(green_style)
|
||||
test_progress_bar.configure(0, 0, count)
|
||||
@red = false
|
||||
|
||||
run_count_label.set_text("0")
|
||||
assertion_count_label.set_text("0")
|
||||
failure_count_label.set_text("0")
|
||||
error_count_label.set_text("0")
|
||||
|
||||
fault_list.remove_items(fault_list.children)
|
||||
end
|
||||
|
||||
def add_fault(fault) # :nodoc:
|
||||
if ( ! @red )
|
||||
test_progress_bar.set_style(red_style)
|
||||
@red = true
|
||||
end
|
||||
item = FaultListItem.new(fault)
|
||||
item.show
|
||||
fault_list.append_items([item])
|
||||
end
|
||||
|
||||
def show_fault(fault) # :nodoc:
|
||||
raw_show_fault(fault.longDisplay)
|
||||
end
|
||||
|
||||
def raw_show_fault(string) # :nodoc:
|
||||
faultDetailLabel.set_text(string)
|
||||
outerDetailSubPanel.queue_resize
|
||||
end
|
||||
|
||||
def clear_fault # :nodoc:
|
||||
raw_show_fault("")
|
||||
end
|
||||
|
||||
def result_changed(result) # :nodoc:
|
||||
test_progress_bar.set_value(test_progress_bar.get_value + 1)
|
||||
|
||||
run_count_label.set_text(result.run_count.to_s)
|
||||
assertion_count_label.set_text(result.assertion_count.to_s)
|
||||
failure_count_label.set_text(result.failure_count.to_s)
|
||||
error_count_label.set_text(result.error_count.to_s)
|
||||
end
|
||||
|
||||
def started(result) # :nodoc:
|
||||
output_status("Started...")
|
||||
end
|
||||
|
||||
def test_started(test_name)
|
||||
output_status("Running #{test_name}...")
|
||||
end
|
||||
|
||||
def finished(elapsed_time)
|
||||
output_status("Finished in #{elapsed_time} seconds")
|
||||
end
|
||||
|
||||
def output_status(string) # :nodoc:
|
||||
status_entry.set_text(string)
|
||||
end
|
||||
|
||||
def setup_ui # :nodoc:
|
||||
main_window.signal_connect("destroy", nil) { stop }
|
||||
main_window.show_all
|
||||
fault_list.signal_connect("select-child", nil) {
|
||||
| list, item, data |
|
||||
show_fault(item.fault)
|
||||
}
|
||||
fault_list.signal_connect("unselect-child", nil) {
|
||||
clear_fault
|
||||
}
|
||||
@red = false
|
||||
end
|
||||
|
||||
def main_window # :nodoc:
|
||||
lazy_initialize(:main_window) {
|
||||
@main_window = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL)
|
||||
@main_window.set_title("Test::Unit TestRunner")
|
||||
@main_window.set_usize(800, 600)
|
||||
@main_window.set_uposition(20, 20)
|
||||
@main_window.set_policy(true, true, false)
|
||||
@main_window.add(main_panel)
|
||||
}
|
||||
end
|
||||
|
||||
def main_panel # :nodoc:
|
||||
lazy_initialize(:main_panel) {
|
||||
@main_panel = Gtk::VBox.new(false, 0)
|
||||
@main_panel.pack_start(suite_panel, false, false, 0)
|
||||
@main_panel.pack_start(progress_panel, false, false, 0)
|
||||
@main_panel.pack_start(info_panel, false, false, 0)
|
||||
@main_panel.pack_start(list_panel, false, false, 0)
|
||||
@main_panel.pack_start(detail_panel, true, true, 0)
|
||||
@main_panel.pack_start(status_panel, false, false, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def suite_panel # :nodoc:
|
||||
lazy_initialize(:suite_panel) {
|
||||
@suite_panel = Gtk::HBox.new(false, 10)
|
||||
@suite_panel.border_width(10)
|
||||
@suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0)
|
||||
@suite_panel.pack_start(suite_name_entry, true, true, 0)
|
||||
@suite_panel.pack_start(run_button, false, false, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def suite_name_entry # :nodoc:
|
||||
lazy_initialize(:suite_name_entry) {
|
||||
@suite_name_entry = Gtk::Entry.new
|
||||
@suite_name_entry.set_editable(false)
|
||||
}
|
||||
end
|
||||
|
||||
def run_button # :nodoc:
|
||||
lazy_initialize(:run_button) {
|
||||
@run_button = Gtk::Button.new("Run")
|
||||
}
|
||||
end
|
||||
|
||||
def progress_panel # :nodoc:
|
||||
lazy_initialize(:progress_panel) {
|
||||
@progress_panel = Gtk::HBox.new(false, 10)
|
||||
@progress_panel.border_width(10)
|
||||
@progress_panel.pack_start(test_progress_bar, true, true, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def test_progress_bar # :nodoc:
|
||||
lazy_initialize(:test_progress_bar) {
|
||||
@test_progress_bar = EnhancedProgressBar.new
|
||||
@test_progress_bar.set_usize(@test_progress_bar.allocation.width, 50)
|
||||
@test_progress_bar.set_style(green_style)
|
||||
}
|
||||
end
|
||||
|
||||
def green_style # :nodoc:
|
||||
lazy_initialize(:green_style) {
|
||||
@green_style = Gtk::Style.new
|
||||
@green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000)
|
||||
}
|
||||
end
|
||||
|
||||
def red_style # :nodoc:
|
||||
lazy_initialize(:red_style) {
|
||||
@red_style = Gtk::Style.new
|
||||
@red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000)
|
||||
}
|
||||
end
|
||||
|
||||
def info_panel # :nodoc:
|
||||
lazy_initialize(:info_panel) {
|
||||
@info_panel = Gtk::HBox.new(false, 0)
|
||||
@info_panel.border_width(10)
|
||||
@info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0)
|
||||
@info_panel.pack_start(run_count_label, true, false, 0)
|
||||
@info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0)
|
||||
@info_panel.pack_start(assertion_count_label, true, false, 0)
|
||||
@info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0)
|
||||
@info_panel.pack_start(failure_count_label, true, false, 0)
|
||||
@info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0)
|
||||
@info_panel.pack_start(error_count_label, true, false, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def run_count_label # :nodoc:
|
||||
lazy_initialize(:run_count_label) {
|
||||
@run_count_label = Gtk::Label.new("0")
|
||||
@run_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
||||
}
|
||||
end
|
||||
|
||||
def assertion_count_label # :nodoc:
|
||||
lazy_initialize(:assertion_count_label) {
|
||||
@assertion_count_label = Gtk::Label.new("0")
|
||||
@assertion_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
||||
}
|
||||
end
|
||||
|
||||
def failure_count_label # :nodoc:
|
||||
lazy_initialize(:failure_count_label) {
|
||||
@failure_count_label = Gtk::Label.new("0")
|
||||
@failure_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
||||
}
|
||||
end
|
||||
|
||||
def error_count_label # :nodoc:
|
||||
lazy_initialize(:error_count_label) {
|
||||
@error_count_label = Gtk::Label.new("0")
|
||||
@error_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
||||
}
|
||||
end
|
||||
|
||||
def list_panel # :nodoc:
|
||||
lazy_initialize(:list_panel) {
|
||||
@list_panel = Gtk::HBox.new
|
||||
@list_panel.border_width(10)
|
||||
@list_panel.pack_start(list_scrolled_window, true, true, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def list_scrolled_window # :nodoc:
|
||||
lazy_initialize(:list_scrolled_window) {
|
||||
@list_scrolled_window = Gtk::ScrolledWindow.new
|
||||
@list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
||||
@list_scrolled_window.set_usize(@list_scrolled_window.allocation.width, 150)
|
||||
@list_scrolled_window.add_with_viewport(fault_list)
|
||||
}
|
||||
end
|
||||
|
||||
def fault_list # :nodoc:
|
||||
lazy_initialize(:fault_list) {
|
||||
@fault_list = Gtk::List.new
|
||||
}
|
||||
end
|
||||
|
||||
def detail_panel # :nodoc:
|
||||
lazy_initialize(:detail_panel) {
|
||||
@detail_panel = Gtk::HBox.new
|
||||
@detail_panel.border_width(10)
|
||||
@detail_panel.pack_start(detail_scrolled_window, true, true, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def detail_scrolled_window # :nodoc:
|
||||
lazy_initialize(:detail_scrolled_window) {
|
||||
@detail_scrolled_window = Gtk::ScrolledWindow.new
|
||||
@detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
||||
@detail_scrolled_window.set_usize(400, @detail_scrolled_window.allocation.height)
|
||||
@detail_scrolled_window.add_with_viewport(outer_detail_sub_panel)
|
||||
}
|
||||
end
|
||||
|
||||
def outer_detail_sub_panel # :nodoc:
|
||||
lazy_initialize(:outer_detail_sub_panel) {
|
||||
@outer_detail_sub_panel = Gtk::VBox.new
|
||||
@outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def inner_detail_sub_panel # :nodoc:
|
||||
lazy_initialize(:inner_detail_sub_panel) {
|
||||
@inner_detail_sub_panel = Gtk::HBox.new
|
||||
@inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def fault_detail_label # :nodoc:
|
||||
lazy_initialize(:fault_detail_label) {
|
||||
@fault_detail_label = EnhancedLabel.new("")
|
||||
style = Gtk::Style.new
|
||||
font = Gdk::Font.font_load("-*-Courier New-medium-r-normal--*-120-*-*-*-*-*-*")
|
||||
style.set_font(font)
|
||||
@fault_detail_label.set_style(style)
|
||||
@fault_detail_label.set_justify(Gtk::JUSTIFY_LEFT)
|
||||
@fault_detail_label.set_line_wrap(false)
|
||||
}
|
||||
end
|
||||
|
||||
def status_panel # :nodoc:
|
||||
lazy_initialize(:status_panel) {
|
||||
@status_panel = Gtk::HBox.new
|
||||
@status_panel.border_width(10)
|
||||
@status_panel.pack_start(status_entry, true, true, 0)
|
||||
}
|
||||
end
|
||||
|
||||
def status_entry # :nodoc:
|
||||
lazy_initialize(:status_entry) {
|
||||
@status_entry = Gtk::Entry.new
|
||||
@status_entry.set_editable(false)
|
||||
}
|
||||
end
|
||||
|
||||
def lazy_initialize(symbol) # :nodoc:
|
||||
if (!instance_eval("defined?(@#{symbol.to_s})"))
|
||||
yield
|
||||
end
|
||||
return instance_eval("@" + symbol.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
class EnhancedProgressBar < Gtk::ProgressBar # :nodoc: all
|
||||
def set_style(style)
|
||||
super
|
||||
hide
|
||||
show
|
||||
end
|
||||
end
|
||||
|
||||
class EnhancedLabel < Gtk::Label # :nodoc: all
|
||||
def set_text(text)
|
||||
super(text.gsub(/\n\t/, "\n" + (" " * 4)))
|
||||
end
|
||||
end
|
||||
|
||||
class FaultListItem < Gtk::ListItem # :nodoc: all
|
||||
attr_reader(:fault)
|
||||
def initialize(fault)
|
||||
super(fault.short_display)
|
||||
@fault = fault
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
Test::Unit::UI::GTK::TestRunner.start_command_line_test
|
||||
end
|
75
lib/test/unit/ui/testrunnermediator.rb
Normal file
75
lib/test/unit/ui/testrunnermediator.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/util/observable'
|
||||
require 'test/unit/testresult'
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module UI # :nodoc:
|
||||
|
||||
# Provides an interface to write any given UI against,
|
||||
# hopefully making it easy to write new UIs.
|
||||
class TestRunnerMediator
|
||||
RESET = name + "::RESET"
|
||||
STARTED = name + "::STARTED"
|
||||
FINISHED = name + "::FINISHED"
|
||||
|
||||
include Util::Observable
|
||||
|
||||
@@run = false
|
||||
|
||||
# Returns true if any TestRunnerMediator instances
|
||||
# have been run.
|
||||
def self.run?
|
||||
return @@run
|
||||
end
|
||||
|
||||
# Creates a new TestRunnerMediator initialized to run
|
||||
# the passed suite.
|
||||
def initialize(suite)
|
||||
@suite = suite
|
||||
end
|
||||
|
||||
# Runs the suite the TestRunnerMediator was created
|
||||
# with.
|
||||
def run_suite
|
||||
@@run = true
|
||||
begin_time = Time.now
|
||||
notify_listeners(RESET, @suite.size)
|
||||
result = create_result
|
||||
notify_listeners(STARTED, result)
|
||||
result_listener = result.add_listener(TestResult::CHANGED) do |updated_result|
|
||||
notify_listeners(TestResult::CHANGED, updated_result)
|
||||
end
|
||||
|
||||
fault_listener = result.add_listener(TestResult::FAULT) do |fault|
|
||||
notify_listeners(TestResult::FAULT, fault)
|
||||
end
|
||||
|
||||
@suite.run(result) do |channel, value|
|
||||
notify_listeners(channel, value)
|
||||
end
|
||||
|
||||
result.remove_listener(TestResult::FAULT, fault_listener)
|
||||
result.remove_listener(TestResult::CHANGED, result_listener)
|
||||
end_time = Time.now
|
||||
elapsed_time = end_time - begin_time
|
||||
notify_listeners(FINISHED, elapsed_time) #"Finished in #{elapsed_time} seconds.")
|
||||
return result
|
||||
end
|
||||
|
||||
private
|
||||
# A factory method to create the result the mediator
|
||||
# should run with. Can be overridden by subclasses if
|
||||
# one wants to use a different result.
|
||||
def create_result
|
||||
return TestResult.new
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
lib/test/unit/ui/testrunnerutilities.rb
Normal file
36
lib/test/unit/ui/testrunnerutilities.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module UI
|
||||
|
||||
# Provides some utilities common to most, if not all,
|
||||
# TestRunners.
|
||||
#
|
||||
#--
|
||||
#
|
||||
# Perhaps there ought to be a TestRunner superclass? There
|
||||
# seems to be a decent amount of shared code between test
|
||||
# runners.
|
||||
|
||||
module TestRunnerUtilities
|
||||
|
||||
# Takes care of the ARGV parsing and suite
|
||||
# determination necessary for running one of the
|
||||
# TestRunners from the command line.
|
||||
def start_command_line_test
|
||||
if ARGV.empty?
|
||||
puts "You should supply the name of a test suite file to the runner"
|
||||
exit
|
||||
end
|
||||
require ARGV[0].gsub(/.+::/, '')
|
||||
new(eval(ARGV[0])).start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
90
lib/test/unit/util/observable.rb
Normal file
90
lib/test/unit/util/observable.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
require 'test/unit/util/procwrapper'
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module Util # :nodoc:
|
||||
|
||||
# This is a utility class that allows anything mixing
|
||||
# it in to notify a set of listeners about interesting
|
||||
# events.
|
||||
module Observable
|
||||
# We use this for defaults since nil might mean something
|
||||
NOTHING = "NOTHING/#{__id__}"
|
||||
|
||||
# Adds the passed proc as a listener on the
|
||||
# channel indicated by channel_name. listener_key
|
||||
# is used to remove the listener later; if none is
|
||||
# specified, the proc itself is used.
|
||||
#
|
||||
# Whatever is used as the listener_key is
|
||||
# returned, making it very easy to use the proc
|
||||
# itself as the listener_key:
|
||||
#
|
||||
# listener = add_listener("Channel") { ... }
|
||||
# remove_listener("Channel", listener)
|
||||
def add_listener(channel_name, listener_key=NOTHING, &listener) # :yields: value
|
||||
unless(block_given?)
|
||||
raise ArgumentError.new("No callback was passed as a listener")
|
||||
end
|
||||
|
||||
key = listener_key
|
||||
if (listener_key == NOTHING)
|
||||
listener_key = listener
|
||||
key = ProcWrapper.new(listener)
|
||||
end
|
||||
|
||||
channels[channel_name] ||= {}
|
||||
channels[channel_name][key] = listener
|
||||
return listener_key
|
||||
end
|
||||
|
||||
# Removes the listener indicated by listener_key
|
||||
# from the channel indicated by
|
||||
# channel_name. Returns the registered proc, or
|
||||
# nil if none was found.
|
||||
def remove_listener(channel_name, listener_key)
|
||||
channel = channels[channel_name]
|
||||
return nil unless (channel)
|
||||
key = listener_key
|
||||
if (listener_key.instance_of?(Proc))
|
||||
key = ProcWrapper.new(listener_key)
|
||||
end
|
||||
if (channel.has_key?(key))
|
||||
return channel.delete(key)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# Calls all the procs registered on the channel
|
||||
# indicated by channel_name. If value is
|
||||
# specified, it is passed in to the procs,
|
||||
# otherwise they are called with no arguments.
|
||||
#
|
||||
#--
|
||||
#
|
||||
# Perhaps this should be private? Would it ever
|
||||
# make sense for an external class to call this
|
||||
# method directly?
|
||||
def notify_listeners(channel_name, *arguments)
|
||||
channel = channels[channel_name]
|
||||
return 0 unless (channel)
|
||||
listeners = channel.values
|
||||
listeners.each { |listener| listener.call(*arguments) }
|
||||
return listeners.size
|
||||
end
|
||||
|
||||
private
|
||||
def channels # :nodoc:
|
||||
@channels ||= {}
|
||||
return @channels
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
48
lib/test/unit/util/procwrapper.rb
Normal file
48
lib/test/unit/util/procwrapper.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
# :nodoc:
|
||||
#
|
||||
# Author:: Nathaniel Talbott.
|
||||
# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
||||
# License:: Ruby license.
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
module Util
|
||||
|
||||
# Allows the storage of a Proc passed through '&' in a
|
||||
# hash.
|
||||
#
|
||||
# Note: this may be inefficient, since the hash being
|
||||
# used is not necessarily very good. In Observable,
|
||||
# efficiency is not too important, since the hash is
|
||||
# only accessed when adding and removing listeners,
|
||||
# not when notifying.
|
||||
|
||||
class ProcWrapper
|
||||
|
||||
# Creates a new wrapper for a_proc.
|
||||
def initialize(a_proc)
|
||||
@a_proc = a_proc
|
||||
@hash = a_proc.inspect.sub(/^(#<#{a_proc.class}:)/){''}.sub(/(>)$/){''}.hex
|
||||
end
|
||||
|
||||
def hash # :nodoc:
|
||||
return @hash
|
||||
end
|
||||
|
||||
def ==(other) # :nodoc:
|
||||
case(other)
|
||||
when ProcWrapper
|
||||
return @a_proc == other.to_proc
|
||||
else
|
||||
return super
|
||||
end
|
||||
end
|
||||
alias :eql? :==
|
||||
|
||||
def to_proc # :nodoc:
|
||||
return @a_proc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue