diff options
author | ntalbott <ntalbott@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2003-09-19 02:48:46 +0000 |
---|---|---|
committer | ntalbott <ntalbott@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2003-09-19 02:48:46 +0000 |
commit | ba51a267716363f1a69632f040ab01da0bbde78e (patch) | |
tree | 715b07f7eccd87ad19acb59d51494d85b759fb66 | |
parent | 9fb55fa783bf276e3eebaf04d99ef77902d50e0e (diff) | |
download | ruby-ba51a267716363f1a69632f040ab01da0bbde78e.tar.gz ruby-ba51a267716363f1a69632f040ab01da0bbde78e.tar.xz ruby-ba51a267716363f1a69632f040ab01da0bbde78e.zip |
* test/testunit/*: Added.
* lib/test/unit.rb: Documentation update.
* lib/test/unit/ui/console/testrunner.rb (TestRunner#initialize):
Ditto.
* lib/test/unit.rb: Factored out an ObjectSpace collector.
* lib/test/unit/collector/objectspace.rb: Ditto.
* sample/testunit/*: Added.
git-svn-id: http://svn.ruby-lang.org/repos/ruby/trunk@4576 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | lib/test/unit.rb | 156 | ||||
-rw-r--r-- | lib/test/unit/collector/objectspace.rb | 44 | ||||
-rw-r--r-- | lib/test/unit/ui/console/testrunner.rb | 4 | ||||
-rw-r--r-- | sample/testunit/adder.rb | 13 | ||||
-rw-r--r-- | sample/testunit/subtracter.rb | 12 | ||||
-rw-r--r-- | sample/testunit/tc_adder.rb | 18 | ||||
-rw-r--r-- | sample/testunit/tc_subtracter.rb | 18 | ||||
-rw-r--r-- | sample/testunit/ts_examples.rb | 7 | ||||
-rw-r--r-- | test/testunit/collector/test_objectspace.rb | 74 | ||||
-rw-r--r-- | test/testunit/test_assertions.rb | 484 | ||||
-rw-r--r-- | test/testunit/test_error.rb | 30 | ||||
-rw-r--r-- | test/testunit/test_testcase.rb | 239 | ||||
-rw-r--r-- | test/testunit/test_testresult.rb | 104 | ||||
-rw-r--r-- | test/testunit/test_testsuite.rb | 116 | ||||
-rw-r--r-- | test/testunit/util/test_observable.rb | 102 | ||||
-rw-r--r-- | test/testunit/util/test_procwrapper.rb | 36 |
17 files changed, 1448 insertions, 24 deletions
@@ -1,3 +1,18 @@ +Fri Sep 19 11:39:00 2003 Nathaniel Talbott <ntalbott@ruby-lang.org> + + * test/testunit/*: Added. + + * lib/test/unit.rb: Documentation update. + + * lib/test/unit/ui/console/testrunner.rb (TestRunner#initialize): + Ditto. + + * lib/test/unit.rb: Factored out an ObjectSpace collector. + + * lib/test/unit/collector/objectspace.rb: Ditto. + + * sample/testunit/*: Added. + Fri Sep 19 01:00:48 2003 GOTOU Yuuzou <gotoyuzo@notwork.org> * lib/webrick/log.rb (BasicLog#log): get rid of as ineffectual diff --git a/lib/test/unit.rb b/lib/test/unit.rb index 6c12f85c6..b891883c3 100644 --- a/lib/test/unit.rb +++ b/lib/test/unit.rb @@ -1,4 +1,119 @@ -# :include: ../../../../README +# = Test::Unit - Ruby Unit Testing Framework +# +# == Introduction +# +# Unit testing is making waves all over the place, largely due to the +# fact that it is a core practice of XP. While XP is great, unit testing +# has been around for a long time and has always been a good idea. One +# of the keys to good unit testing, though, is not just writing tests, +# but having tests. What's the difference? Well, if you just _write_ a +# test and throw it away, you have no guarantee that something won't +# change later which breaks your code. If, on the other hand, you _have_ +# tests (obviously you have to write them first), and run them as often +# as possible, you slowly build up a wall of things that cannot break +# without you immediately knowing about it. This is when unit testing +# hits its peak usefulness. +# +# Enter Test::Unit, a framework for unit testing in Ruby, helping you to +# design, debug and evaluate your code by making it easy to write and +# have tests for it. +# +# +# == Installation +# +# Run: +# * ruby setup.rb config +# * ruby setup.rb setup +# * ruby setup.rb install +# +# Note that the runit compatibility layer will *not* be installed if you +# already have RubyUnit installed. +# +# Mac OS X users should also note that setup.rb will fail unless they +# execute 'unlimit stacksize' before running it. +# +# +# == Notes +# +# Test::Unit has grown out of and superceded Lapidary. +# +# +# == Feedback +# +# I like (and do my best to practice) XP, so I value early releases, +# user feedback, and clean, simple, expressive code. There is always +# room for improvement in everything I do, and Test::Unit is no +# exception. Please, let me know what you think of Test::Unit as it +# stands, and what you'd like to see expanded/changed/improved/etc. If +# you find a bug, let me know ASAP; one good way to let me know what the +# bug is is to submit a new test that catches it :-) Also, I'd love to +# hear about any successes you have with Test::Unit, and any +# documentation you might add will be greatly appreciated. My contact +# info is below. +# +# +# == Contact Information +# +# A lot of discussion happens about Ruby in general on the ruby-talk +# mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask +# any questions you might have there. I monitor the list, as do many +# other helpful Rubyists, and you're sure to get a quick answer. Of +# course, you're also welcome to email me (Nathaniel Talbott) directly +# at mailto:testunit@talbott.ws, and I'll do my best to help you out. +# +# +# == Credits +# +# I'd like to thank... +# +# Matz, for a great language! +# +# Masaki Suketa, for his work on RubyUnit, which filled a vital need in +# the Ruby world for a very long time. I'm also grateful for his help in +# polishing Test::Unit and getting the RubyUnit compatibility layer +# right. His graciousness in allowing Test::Unit to supercede RubyUnit +# continues to be a challenge to me to be more willing to defer my own +# rights. +# +# Ken McKinlay, for his interest and work on unit testing, and for his +# willingness to dialog about it. He was also a great help in pointing +# out some of the holes in the RubyUnit compatibility layer. +# +# Dave Thomas, for the original idea that led to the extremely simple +# "require 'test/unit'", plus his code to improve it even more by +# allowing the selection of tests from the command-line. Also, without +# RDoc, the documentation for Test::Unit would stink a lot more than it +# does now. +# +# Everyone who's helped out with bug reports, feature ideas, +# encouragement to continue, etc. It's a real privilege to be a part of +# the Ruby community. +# +# The guys at RoleModel Software, for putting up with me repeating, "But +# this would be so much easier in Ruby!" whenever we're coding in Java. +# +# My Creator, for giving me life, and giving it more abundantly. +# +# +# == License +# +# Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free +# software, and is distributed under the Ruby license. See the COPYING +# file in the standard Ruby distribution for details. +# +# +# == Warranty +# +# This software is provided "as is" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantibility and fitness for a particular +# purpose. +# +# +# == Author +# +# Nathaniel Talbott. +# Copyright (c) 2000-2003, Nathaniel Talbott # # ---- # @@ -158,20 +273,13 @@ require 'test/unit/testcase' require 'test/unit/ui/testrunnermediator' +require 'test/unit/collector/objectspace' 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' @@ -194,22 +302,26 @@ at_exit { 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 + collector = Test::Unit::Collector::ObjectSpace::new + + unless ARGV.empty? criteria = ARGV.map { |arg| (arg =~ %r{^/(.*)/$}) ? Regexp.new($1) : arg } - criteria.each { + filters = [] + criteria.each do | 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) } + case criterion + when Regexp + filters << proc{|test| criterion =~ test.name} + when /^A-Z/ + filters << proc{|test| criterion == test.class.name} + else + filters << proc{|test| criterion == test.method_name} end - } + end + collector.filter = filters end - runner.call(suite) + + suite_name = $0.sub(/\.rb$/, '') + runner.call(collector.collect(suite_name)) end } diff --git a/lib/test/unit/collector/objectspace.rb b/lib/test/unit/collector/objectspace.rb new file mode 100644 index 000000000..32b633268 --- /dev/null +++ b/lib/test/unit/collector/objectspace.rb @@ -0,0 +1,44 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module Test + module Unit + module Collector + class ObjectSpace + NAME = 'collected from the ObjectSpace' + + def initialize(source=::ObjectSpace) + @source = source + end + + def collect(name=NAME) + suite = TestSuite.new(name) + tests = [] + @source.each_object(Class) do |klass| + tests.concat(klass.suite.tests) if(Test::Unit::TestCase > klass) + end + tests.each{|test| suite << test if(include(test))} + suite + end + + def include(test) + return true unless(@filters) + @filters.each do |filter| + return true if(filter.call(test)) + end + false + end + + def filter=(filters) + @filters = case(filters) + when Proc + [filters] + when Array + filters + end + end + end + end + end +end diff --git a/lib/test/unit/ui/console/testrunner.rb b/lib/test/unit/ui/console/testrunner.rb index 5b384bc33..dab07ddbc 100644 --- a/lib/test/unit/ui/console/testrunner.rb +++ b/lib/test/unit/ui/console/testrunner.rb @@ -1,7 +1,7 @@ # :nodoc: # # Author:: Nathaniel Talbott. -# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/ui/testrunnermediator' @@ -31,7 +31,7 @@ module Test # running is limited to progress dots, errors and # failures, and the final result. io specifies # where runner output should go to; defaults to - # STDERR. + # STDOUT. def initialize(suite, output_level=NORMAL, io=STDOUT) if (suite.respond_to?(:suite)) @suite = suite.suite diff --git a/sample/testunit/adder.rb b/sample/testunit/adder.rb new file mode 100644 index 000000000..aa5c88cc7 --- /dev/null +++ b/sample/testunit/adder.rb @@ -0,0 +1,13 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +class Adder + def initialize(number) + @number = number + end + def add(number) + return @number + number + end +end + diff --git a/sample/testunit/subtracter.rb b/sample/testunit/subtracter.rb new file mode 100644 index 000000000..2c0824780 --- /dev/null +++ b/sample/testunit/subtracter.rb @@ -0,0 +1,12 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +class Subtracter + def initialize(number) + @number = number + end + def subtract(number) + return @number - number + end +end diff --git a/sample/testunit/tc_adder.rb b/sample/testunit/tc_adder.rb new file mode 100644 index 000000000..8453beb20 --- /dev/null +++ b/sample/testunit/tc_adder.rb @@ -0,0 +1,18 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit' +require 'adder' + +class TC_Adder < Test::Unit::TestCase + def setup + @adder = Adder.new(5) + end + def test_add + assert_equal(7, @adder.add(2), "Should have added correctly") + end + def teardown + @adder = nil + end +end diff --git a/sample/testunit/tc_subtracter.rb b/sample/testunit/tc_subtracter.rb new file mode 100644 index 000000000..d2c831335 --- /dev/null +++ b/sample/testunit/tc_subtracter.rb @@ -0,0 +1,18 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit' +require 'subtracter' + +class TC_Subtracter < Test::Unit::TestCase + def setup + @subtracter = Subtracter.new(5) + end + def test_subtract + assert_equal(3, @subtracter.subtract(2), "Should have subtracted correctly") + end + def teardown + @subtracter = nil + end +end diff --git a/sample/testunit/ts_examples.rb b/sample/testunit/ts_examples.rb new file mode 100644 index 000000000..3d24dd652 --- /dev/null +++ b/sample/testunit/ts_examples.rb @@ -0,0 +1,7 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit' +require 'tc_adder' +require 'tc_subtracter' diff --git a/test/testunit/collector/test_objectspace.rb b/test/testunit/collector/test_objectspace.rb new file mode 100644 index 000000000..d596e1d31 --- /dev/null +++ b/test/testunit/collector/test_objectspace.rb @@ -0,0 +1,74 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/collector/objectspace' + +module Test + module Unit + module Collector + class TC_ObjectSpace < TestCase + def setup + @tc1 = Class.new(TestCase) do + def test_1 + end + def test_2 + end + end + + @tc2 = Class.new(TestCase) do + def test_3 + end + end + + @no_tc = Class.new do + def test_4 + end + end + + @object_space = {Class => [@tc1, @tc2, @no_tc], String => ['']} + def @object_space.each_object(type) + self[type].each{|item| yield(item) } + end + end + + def test_basic_collection + expected = TestSuite.new("name") + expected << @tc1.new('test_1') + expected << @tc1.new('test_2') + expected << @tc2.new('test_3') + assert_equal(expected, ObjectSpace.new(@object_space).collect("name")) + end + + def test_filtered_collection + expected = TestSuite.new(ObjectSpace::NAME) + collector = ObjectSpace.new(@object_space) + collector.filter = proc{|test| false} + assert_equal(expected, collector.collect) + + expected = TestSuite.new(ObjectSpace::NAME) + expected << @tc1.new('test_1') + expected << @tc1.new('test_2') + expected << @tc2.new('test_3') + collector = ObjectSpace.new(@object_space) + collector.filter = proc{|test| true} + assert_equal(expected, collector.collect) + + expected = TestSuite.new(ObjectSpace::NAME) + expected << @tc1.new('test_1') + expected << @tc2.new('test_3') + collector = ObjectSpace.new(@object_space) + collector.filter = proc{|test| ['test_1', 'test_3'].include?(test.method_name)} + assert_equal(expected, collector.collect) + + expected = TestSuite.new(ObjectSpace::NAME) + expected << @tc1.new('test_1') + expected << @tc2.new('test_3') + collector = ObjectSpace.new(@object_space) + collector.filter = [proc{|test| test.method_name == 'test_1'}, proc{|test| test.method_name == 'test_3'}] + assert_equal(expected, collector.collect) + end + end + end + end +end diff --git a/test/testunit/test_assertions.rb b/test/testunit/test_assertions.rb new file mode 100644 index 000000000..ee52a503a --- /dev/null +++ b/test/testunit/test_assertions.rb @@ -0,0 +1,484 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/testcase' + +module Test + module Unit + class TC_Assertions < TestCase + def check(value, message="") + add_assertion + if (!value) + raise AssertionFailedError.new(message) + end + end + + def check_assertions(expect_fail, expected_message="", return_value_expected=false) + @actual_assertion_count = 0 + failed = true + actual_message = nil + @catch_assertions = true + return_value = nil + begin + return_value = yield + failed = false + rescue AssertionFailedError => error + actual_message = error.message + end + @catch_assertions = false + check(expect_fail == failed, (expect_fail ? "Should have failed, but didn't" : "Should not have failed, but did with message\n<#{actual_message}>")) + check(1 == @actual_assertion_count, "Should have made one assertion but made <#{@actual_assertion_count}>") + if (expect_fail) + case expected_message + when String + check(actual_message == expected_message, "Should have the correct message.\n<#{expected_message.inspect}> expected but was\n<#{actual_message.inspect}>") + when Regexp + check(actual_message =~ expected_message, "The message should match correctly.\n</#{expected_message.source}/> expected to match\n<#{actual_message.inspect}>") + else + check(false, "Incorrect expected message type in assert_nothing_failed") + end + else + if (!return_value_expected) + check(return_value.nil?, "Should not return a value but returned <#{return_value}>") + else + check(!return_value.nil?, "Should return a value") + end + end + return return_value + end + + def check_nothing_fails(return_value_expected=false, &proc) + check_assertions(false, "", return_value_expected, &proc) + end + + def check_fails(expected_message="", &proc) + check_assertions(true, expected_message, &proc) + end + + def test_assert_block + check_nothing_fails { + assert_block {true} + } + check_nothing_fails { + assert_block("successful assert_block") {true} + } + check_nothing_fails { + assert_block("successful assert_block") {true} + } + check_fails { + assert_block {false} + } + check_fails("failed assert_block") { + assert_block("failed assert_block") {false} + } + end + + def test_assert + check_nothing_fails { + assert(true) + } + check_nothing_fails { + assert(true, "successful assert") + } + check_fails { + assert(false) + } + check_fails("failed assert") { + assert(false, "failed assert") + } + end + + def test_assert_equal + check_nothing_fails { + assert_equal("string1", "string1") + } + check_nothing_fails { + assert_equal( "string1", "string1", "successful assert_equal") + } + check_nothing_fails { + assert_equal("string1", "string1", "successful assert_equal") + } + check_fails("<string1> expected but was\n<string2>") { + assert_equal("string1", "string2") + } + check_fails("failed assert_equal.\n<string1> expected but was\n<string2>") { + assert_equal("string1", "string2", "failed assert_equal") + } + end + + def test_assert_raises + return_value = nil + check_nothing_fails(true) { + return_value = assert_raises(RuntimeError) { + raise "Error" + } + } + check(return_value.kind_of?(Exception), "Should have returned the exception from a successful assert_raises") + check(return_value.message == "Error", "Should have returned the correct exception from a successful assert_raises") + check_nothing_fails(true) { + assert_raises(ArgumentError, "successful assert_raises") { + raise ArgumentError.new("Error") + } + } + check_nothing_fails(true) { + assert_raises(RuntimeError) { + raise "Error" + } + } + check_nothing_fails(true) { + assert_raises(RuntimeError, "successful assert_raises") { + raise "Error" + } + } + check_fails("<RuntimeError> exception expected but none was thrown") { + assert_raises(RuntimeError) { + 1 + 1 + } + } + check_fails(%r{^failed assert_raises.\n<ArgumentError> exception expected but was\nClass: <RuntimeError>\nMessage: <Error>\n---Backtrace---\n.+\n---------------$}m) { + assert_raises(ArgumentError, "failed assert_raises") { + raise "Error" + } + } + end + + def test_assert_instance_of + check_nothing_fails { + assert_instance_of(String, "string") + } + check_nothing_fails { + assert_instance_of(String, "string", "successful assert_instance_of") + } + check_nothing_fails { + assert_instance_of(String, "string", "successful assert_instance_of") + } + check_fails("<string> expected to be an instance of\n<Hash> but was\n<String>") { + assert_instance_of(Hash, "string") + } + check_fails("failed assert_instance_of.\n<string> expected to be an instance of\n<Hash> but was\n<String>") { + assert_instance_of(Hash, "string", "failed assert_instance_of") + } + end + + def test_assert_nil + check_nothing_fails { + assert_nil(nil) + } + check_nothing_fails { + assert_nil(nil, "successful assert_nil") + } + check_nothing_fails { + assert_nil(nil, "successful assert_nil") + } + check_fails("<nil> expected but was\n<string>") { + assert_nil("string") + } + check_fails("failed assert_nil.\n<nil> expected but was\n<string>") { + assert_nil("string", "failed assert_nil") + } + end + + def test_assert_kind_of + check_nothing_fails { + assert_kind_of(Module, Array) + } + check_nothing_fails { + assert_kind_of(Object, "string", "successful assert_kind_of") + } + check_nothing_fails { + assert_kind_of(Object, "string", "successful assert_kind_of") + } + check_nothing_fails { + assert_kind_of(Comparable, 1) + } + check_fails("<string>\nexpected to be kind_of?<Class>") { + assert_kind_of(Class, "string") + } + check_fails("failed assert_kind_of.\n<string>\nexpected to be kind_of?<Class>") { + assert_kind_of(Class, "string", "failed assert_kind_of") + } + end + + def test_assert_match + check_nothing_fails { + assert_match(/strin./, "string") + } + check_nothing_fails { + assert_match("strin", "string") + } + check_nothing_fails { + assert_match(/strin./, "string", "successful assert_match") + } + check_nothing_fails { + assert_match(/strin./, "string", "successful assert_match") + } + check_fails("<string> expected to be =~\n</slin./>") { + assert_match(/slin./, "string") + } + check_fails("<string> expected to be =~\n<slin>") { + assert_match("slin", "string") + } + check_fails("failed assert_match.\n<string> expected to be =~\n</slin./>") { + assert_match(/slin./, "string", "failed assert_match") + } + end + + def test_assert_same + thing = "thing" + check_nothing_fails { + assert_same(thing, thing) + } + check_nothing_fails { + assert_same(thing, thing, "successful assert_same") + } + check_nothing_fails { + assert_same(thing, thing, "successful assert_same") + } + thing2 = "thing" + check_fails("<#{thing}:#{thing.__id__}> expected to be equal? to\n<#{thing2}:#{thing2.__id__}>") { + assert_same(thing, thing2) + } + check_fails("failed assert_same.\n<#{thing}:#{thing.__id__}> expected to be equal? to\n<#{thing2}:#{thing2.__id__}>") { + assert_same(thing, thing2, "failed assert_same") + } + end + + def test_assert_nothing_raised + check_nothing_fails { + assert_nothing_raised { + 1 + 1 + } + } + check_nothing_fails { + assert_nothing_raised("successful assert_nothing_raised") { + 1 + 1 + } + } + check_nothing_fails { + assert_nothing_raised("successful assert_nothing_raised") { + 1 + 1 + } + } + check_nothing_fails { + begin + assert_nothing_raised(RuntimeError, StandardError, "successful assert_nothing_raised") { + raise ZeroDivisionError.new("ArgumentError") + } + rescue ZeroDivisionError + end + } + check_fails(%r{^Exception raised:\nClass: <RuntimeError>\nMessage: <Error>\n---Backtrace---\n.+\n---------------$}m) { + assert_nothing_raised { + raise "Error" + } + } + check_fails(%r{^failed assert_nothing_raised\.\nException raised:\nClass: <RuntimeError>\nMessage: <Error>\n---Backtrace---\n.+\n---------------$}m) { + assert_nothing_raised("failed assert_nothing_raised") { + raise "Error" + } + } + check_fails(%r{^Exception raised:\nClass: <RuntimeError>\nMessage: <Error>\n---Backtrace---\n.+\n---------------$}m) { + assert_nothing_raised(StandardError, RuntimeError) { + raise "Error" + } + } + end + + def test_flunk + check_fails { + flunk + } + check_fails("flunk message") { + flunk("flunk message") + } + end + + def test_assert_not_same + thing = "thing" + thing2 = "thing" + check_nothing_fails { + assert_not_same(thing, thing2) + } + check_nothing_fails { + assert_not_same(thing, thing2, "message") + } + check_fails("<#{thing}:#{thing.__id__}> expected to not be equal? to\n<#{thing}:#{thing.__id__}>") { + assert_not_same(thing, thing) + } + check_fails("message.\n<#{thing}:#{thing.__id__}> expected to not be equal? to\n<#{thing}:#{thing.__id__}>") { + assert_not_same(thing, thing, "message") + } + end + + def test_assert_not_equal + check_nothing_fails { + assert_not_equal("string1", "string2") + } + check_nothing_fails { + assert_not_equal("string1", "string2", "message") + } + check_fails("<string> expected to be != to\n<string>") { + assert_not_equal("string", "string") + } + check_fails("message.\n<string> expected to be != to\n<string>") { + assert_not_equal("string", "string", "message") + } + end + + def test_assert_not_nil + check_nothing_fails { + assert_not_nil("string") + } + check_nothing_fails { + assert_not_nil("string", "message") + } + check_fails("<nil> expected to not be nil") { + assert_not_nil(nil) + } + check_fails("message.\n<nil> expected to not be nil") { + assert_not_nil(nil, "message") + } + end + + def test_assert_no_match + check_nothing_fails { + assert_no_match(/sling/, "string") + } + check_nothing_fails { + assert_no_match(/sling/, "string", "message") + } + check_fails("</string/> expected to not match\n <string>") { + assert_no_match(/string/, "string") + } + check_fails("message.\n</string/> expected to not match\n <string>") { + assert_no_match(/string/, "string", "message") + } + end + + def test_assert_throws + check_nothing_fails { + assert_throws(:thing, "message") { + throw :thing + } + } + check_fails("message.\n<:thing> expected to be thrown but\n<:thing2> was thrown") { + assert_throws(:thing, "message") { + throw :thing2 + } + } + check_fails("message.\n<:thing> should have been thrown") { + assert_throws(:thing, "message") { + 1 + 1 + } + } + end + + def test_assert_nothing_thrown + check_nothing_fails { + assert_nothing_thrown("message") { + 1 + 1 + } + } + check_fails("message.\n<:thing> was thrown when nothing was expected") { + assert_nothing_thrown("message") { + throw :thing + } + } + end + + def test_assert_operator + check_nothing_fails { + assert_operator("thing", :==, "thing", "message") + } + check_fails("message.\n<thing1> expected to be\n==\n<thing2>") { + assert_operator("thing1", :==, "thing2", "message") + } + end + + def test_assert_respond_to + check_nothing_fails { + assert_respond_to("thing", :to_s, "message") + } + check_nothing_fails { + assert_respond_to("thing", "to_s", "message") + } + check_fails("The method argument to #assert_respond_to should be specified as a Symbol or a String.") { + assert_respond_to("thing", 0.15) + } + check_fails("message.\n<symbol>\nof type <Symbol>\nexpected to respond_to?<non_existent>") { + assert_respond_to(:symbol, :non_existent, "message") + } + end + + def test_assert_in_delta + check_nothing_fails { + assert_in_delta(1.4, 1.4, 0) + } + check_nothing_fails { + assert_in_delta(0.5, 0.4, 0.1, "message") + } + check_nothing_fails { + float_thing = Object.new + def float_thing.to_f + 0.2 + end + assert_in_delta(0.1, float_thing, 0.1) + } + check_fails("message.\n<0.5> and\n<0.4> expected to be within\n<0.05> of each other") { + assert_in_delta(0.5, 0.4, 0.05, "message") + } + check_fails(%r{The arguments must respond to to_f; the first float did not\.\n<.+>\nof type <Object>\nexpected to respond_to\?<to_f>}) { + assert_in_delta(Object.new, 0.4, 0.1) + } + check_fails("The delta should not be negative.\n<-0.1> expected to be\n>=\n<0.0>") { + assert_in_delta(0.5, 0.4, -0.1, "message") + } + end + + def test_assert_send + object = Object.new + class << object + private + def return_argument(argument, bogus) + return argument + end + end + check_nothing_fails { + assert_send([object, :return_argument, true, "bogus"], "message") + } + check_fails(%r{message\.\n<.+> expected to respond to\n<return_argument\(\[false, "bogus"\]\)> with true}) { + assert_send([object, :return_argument, false, "bogus"], "message") + } + end + + def test_condition_invariant + object = Object.new + def object.inspect + @changed = true + end + def object.==(other) + @changed ||= false + return (!@changed) + end + check_nothing_fails { + assert_equal(object, object, "message") + } + end + + def add_failure(message, location=caller) + if (!@catch_assertions) + super + end + end + + def add_assertion + if (!@catch_assertions) + super + else + @actual_assertion_count += 1 + end + end + end + end +end diff --git a/test/testunit/test_error.rb b/test/testunit/test_error.rb new file mode 100644 index 000000000..c95bef38c --- /dev/null +++ b/test/testunit/test_error.rb @@ -0,0 +1,30 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/error' + +module Test + module Unit + class TC_Error < TestCase + def setup + @old_load_path = $:.dup + $:.replace(['C:\some\old\path']) + end + + def test_backtrace_filtering + backtrace = [%q{tc_thing.rb:4:in '/'}] + + backtrace.concat([%q{tc_thing.rb:4:in 'test_stuff'}, + %q{C:\some\old\path/test/unit/testcase.rb:44:in 'send'}, + %q{C:\some\old\path\test\unit\testcase.rb:44:in 'run'}, + %q{tc_thing.rb:3}]) + assert_equal([backtrace[0..1], backtrace[-1]].flatten, Error.filter(backtrace), "Should filter out all TestUnit-specific lines") + end + + def teardown + $:.replace(@old_load_path) + end + end + end +end diff --git a/test/testunit/test_testcase.rb b/test/testunit/test_testcase.rb new file mode 100644 index 000000000..c08771200 --- /dev/null +++ b/test/testunit/test_testcase.rb @@ -0,0 +1,239 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/testcase' + +module Test + module Unit + class TC_TestCase < TestCase + def test_creation + tc = Class.new(TestCase) do + def test_with_arguments(arg1, arg2) + end + end + + caught = true + catch(:invalid_test) do + tc.new(:test_with_arguments) + caught = false + end + check("Should have caught an invalid test when there are arguments", caught) + + caught = true + catch(:invalid_test) do + tc.new(:non_existent_test) + caught = false + end + check("Should have caught an invalid test when the method does not exist", caught) + end + + def setup + @tc_failure_error = Class.new(TestCase) do + def test_failure + assert_block("failure") { false } + end + def test_error + 1 / 0 + end + def return_passed? + return passed? + end + end + + def @tc_failure_error.name + "TC_FailureError" + end + end + + def test_add_failed_assertion + test_case = @tc_failure_error.new(:test_failure) + check("passed? should start out true", test_case.return_passed?) + result = TestResult.new + called = false + result.add_listener(TestResult::FAULT) { + | fault | + check("Should have a Failure", fault.instance_of?(Failure)) + check("The Failure should have the correct message", "failure" == fault.message) + check("The Failure should have the correct location (was <#{fault.location}>)", fault.location =~ /test_failure\(TC_FailureError\) \[.*#{File.basename(__FILE__)}:\d+\]/) + called = true + } + progress = [] + test_case.run(result) { |*arguments| progress << arguments } + check("The failure should have triggered the listener", called) + check("The failure should have set passed?", !test_case.return_passed?) + check("The progress block should have been updated correctly", [[TestCase::STARTED, test_case.name], [TestCase::FINISHED, test_case.name]] == progress) + end + + def test_add_error + test_case = @tc_failure_error.new(:test_error) + check("passed? should start out true", test_case.return_passed?) + result = TestResult.new + called = false + result.add_listener(TestResult::FAULT) { + | fault | + check("Should have a TestError", fault.instance_of?(Error)) + check("The Error should have the correct message", "ZeroDivisionError: divided by 0" == fault.message) + check("The Error should have the correct location", "test_error(TC_FailureError)" == fault.location) + check("The Error should have the correct exception", fault.exception.instance_of?(ZeroDivisionError)) + called = true + } + test_case.run(result) {} + check("The error should have triggered the listener", called) + check("The error should have set passed?", !test_case.return_passed?) + end + + def test_no_tests + suite = TestCase.suite + check("Should have a test suite", suite.instance_of?(TestSuite)) + check("Should have one test", suite.size == 1) + check("Should have the default test", suite.tests.first.name == "default_test(Test::Unit::TestCase)") + + result = TestResult.new + suite.run(result) {} + check("Should have had one test run", result.run_count == 1) + check("Should have had one test failure", result.failure_count == 1) + check("Should have had no errors", result.error_count == 0) + end + + def test_suite + tc = Class.new(TestCase) do + def test_succeed + assert_block {true} + end + def test_fail + assert_block {false} + end + def test_error + 1/0 + end + def dont_run + assert_block {true} + end + def test_dont_run(argument) + assert_block {true} + end + def test + assert_block {true} + end + end + + suite = tc.suite + check("Should have a test suite", suite.instance_of?(TestSuite)) + check("Should have three tests", suite.size == 3) + + result = TestResult.new + suite.run(result) {} + check("Should have had three test runs", result.run_count == 3) + check("Should have had one test failure", result.failure_count == 1) + check("Should have had one test error", result.error_count == 1) + end + + + def test_setup_teardown + tc = Class.new(TestCase) do + attr_reader(:setup_called, :teardown_called) + def initialize(test) + super(test) + @setup_called = false + @teardown_called = false + end + def setup + @setup_called = true + end + def teardown + @teardown_called = true + end + def test_succeed + assert_block {true} + end + def test_fail + assert_block {false} + end + def test_error + raise "Error!" + end + end + result = TestResult.new + + test = tc.new(:test_succeed) + test.run(result) {} + check("Should have called setup the correct number of times", test.setup_called) + check("Should have called teardown the correct number of times", test.teardown_called) + + test = tc.new(:test_fail) + test.run(result) {} + check("Should have called setup the correct number of times", test.setup_called) + check("Should have called teardown the correct number of times", test.teardown_called) + + test = tc.new(:test_error) + test.run(result) {} + check("Should have called setup the correct number of times", test.setup_called) + check("Should have called teardown the correct number of times", test.teardown_called) + + check("Should have had two test runs", result.run_count == 3) + check("Should have had a test failure", result.failure_count == 1) + check("Should have had a test error", result.error_count == 1) + end + + def test_assertion_failed_not_called + tc = Class.new(TestCase) do + def test_thing + raise AssertionFailedError.new + end + end + + suite = tc.suite + check("Should have one test", suite.size == 1) + result = TestResult.new + suite.run(result) {} + check("Should have had one test run", result.run_count == 1) + check("Should have had one assertion failure", result.failure_count == 1) + check("Should not have any assertion errors but had #{result.error_count}", result.error_count == 0) + end + + def test_equality + tc1 = Class.new(TestCase) do + def test_1 + end + def test_2 + end + end + + tc2 = Class.new(TestCase) do + def test_1 + end + end + + test1 = tc1.new('test_1') + test2 = tc1.new('test_1') + check("Should be equal", test1 == test2) + check("Should be equal", test2 == test1) + + test1 = tc1.new('test_2') + check("Should not be equal", test1 != test2) + check("Should not be equal", test2 != test1) + + test2 = tc1.new('test_2') + check("Should be equal", test1 == test2) + check("Should be equal", test2 == test1) + + test1 = tc1.new('test_1') + test2 = tc2.new('test_1') + check("Should not be equal", test1 != test2) + check("Should not be equal", test2 != test1) + + + check("Should not be equal", test1 != Object.new) + check("Should not be equal", Object.new != test1) + end + + def check(message, passed) + @_result.add_assertion + if ! passed + raise AssertionFailedError.new(message) + end + end + end + end +end diff --git a/test/testunit/test_testresult.rb b/test/testunit/test_testresult.rb new file mode 100644 index 000000000..95d631a08 --- /dev/null +++ b/test/testunit/test_testresult.rb @@ -0,0 +1,104 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/testcase' +require 'test/unit/testresult' + +module Test + module Unit + class TC_TestResult < TestCase + def setup + @my_result = TestResult.new + @my_result.add_assertion() + @my_result.add_failure("") + @my_result.add_error("") + end + def test_result_changed_notification + called1 = false + @my_result.add_listener( TestResult::CHANGED) { + |result| + assert_block("The result should be correct") { result == @my_result } + called1 = true + } + @my_result.add_assertion + assert_block("Should have been notified when the assertion happened") { called1 } + + called1, called2 = false, false + @my_result.add_listener( TestResult::CHANGED) { + |result| + assert_block("The result should be correct") { result == @my_result } + called2 = true + } + @my_result.add_assertion + assert_block("Both listeners should have been notified for a success") { called1 && called2 } + + called1, called2 = false, false + @my_result.add_failure("") + assert_block("Both listeners should have been notified for a failure") { called1 && called2 } + + called1, called2 = false, false + @my_result.add_error("") + assert_block("Both listeners should have been notified for an error") { called1 && called2 } + + called1, called2 = false, false + @my_result.add_run + assert_block("Both listeners should have been notified for a run") { called1 && called2 } + end + def test_fault_notification + called1 = false + fault = "fault" + @my_result.add_listener(TestResult::FAULT) { + | passed_fault | + assert_block("The fault should be correct") { passed_fault == fault } + called1 = true + } + + @my_result.add_assertion + assert_block("Should not have been notified when the assertion happened") { !called1 } + + @my_result.add_failure(fault) + assert_block("Should have been notified when the failure happened") { called1 } + + called1, called2 = false, false + @my_result.add_listener(TestResult::FAULT) { + | passed_fault | + assert_block("The fault should be correct") { passed_fault == fault } + called2 = true + } + + @my_result.add_assertion + assert_block("Neither listener should have been notified for a success") { !(called1 || called2) } + + called1, called2 = false, false + @my_result.add_failure(fault) + assert_block("Both listeners should have been notified for a failure") { called1 && called2 } + + called1, called2 = false, false + @my_result.add_error(fault) + assert_block("Both listeners should have been notified for an error") { called1 && called2 } + + called1, called2 = false, false + @my_result.add_run + assert_block("Neither listener should have been notified for a run") { !(called1 || called2) } + end + def test_passed? + result = TestResult.new + assert(result.passed?, "An empty result should have passed") + + result.add_assertion + assert(result.passed?, "Adding an assertion should not cause the result to not pass") + + result.add_run + assert(result.passed?, "Adding a run should not cause the result to not pass") + + result.add_failure("") + assert(!result.passed?, "Adding a failed assertion should cause the result to not pass") + + result = TestResult.new + result.add_error("") + assert(!result.passed?, "Adding an error should cause the result to not pass") + end + end + end +end diff --git a/test/testunit/test_testsuite.rb b/test/testunit/test_testsuite.rb new file mode 100644 index 000000000..97f639da8 --- /dev/null +++ b/test/testunit/test_testsuite.rb @@ -0,0 +1,116 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/testcase' +require 'test/unit/testsuite' + +module Test + module Unit + class TC_TestSuite < TestCase + def setup + @testcase1 = Class.new(TestCase) do + def test_succeed1 + assert_block { true } + end + def test_fail + assert_block { false } + end + end + + @testcase2 = Class.new(TestCase) do + def test_succeed2 + assert_block { true } + end + def test_error + raise + end + end + end + + def test_size + assert_block("The count should be correct") do + suite = TestSuite.new + suite2 = TestSuite.new + suite2 << self.class.new("test_size") + suite << suite2 + suite << self.class.new("test_size") + suite.size == 2 + end + end + + def test_run + progress = [] + suite = @testcase1.suite + result = TestResult.new + suite.run(result) { |*values| progress << values } + + assert_equal(2, result.run_count, "Should have had four test runs") + assert_equal(1, result.failure_count, "Should have had one test failure") + assert_equal(0, result.error_count, "Should have had one test error") + assert_equal([[TestSuite::STARTED, suite.name], + [TestCase::STARTED, "test_fail(#{suite.name})"], + [TestCase::FINISHED, "test_fail(#{suite.name})"], + [TestCase::STARTED, "test_succeed1(#{suite.name})"], + [TestCase::FINISHED, "test_succeed1(#{suite.name})"], + [TestSuite::FINISHED, suite.name]], + progress, "Should have had the correct progress") + + suite = TestSuite.new + suite << @testcase1.suite + suite << @testcase2.suite + result = TestResult.new + progress = [] + suite.run(result) { |*values| progress << values } + + assert_equal(4, result.run_count, "Should have had four test runs") + assert_equal(1, result.failure_count, "Should have had one test failure") + assert_equal(1, result.error_count, "Should have had one test error") + assert_equal(14, progress.size, "Should have had the correct number of progress calls") + end + + def test_empty? + assert(TestSuite.new.empty?, "A new test suite should be empty?") + assert(!@testcase2.suite.empty?, "A test suite with tests should not be empty") + end + + def test_equality + suite1 = TestSuite.new + suite2 = TestSuite.new + assert_equal(suite1, suite2) + assert_equal(suite2, suite1) + + suite1 = TestSuite.new('name') + assert_not_equal(suite1, suite2) + assert_not_equal(suite2, suite1) + + suite2 = TestSuite.new('name') + assert_equal(suite1, suite2) + assert_equal(suite2, suite1) + + suite1 << 'test' + assert_not_equal(suite1, suite2) + assert_not_equal(suite2, suite1) + + suite2 << 'test' + assert_equal(suite1, suite2) + assert_equal(suite2, suite1) + + suite2 = Object.new + class << suite2 + def name + 'name' + end + def tests + ['test'] + end + end + assert_not_equal(suite1, suite2) + assert_not_equal(suite2, suite1) + + assert_not_equal(suite1, Object.new) + assert_not_equal(Object.new, suite1) + end + end + end +end diff --git a/test/testunit/util/test_observable.rb b/test/testunit/util/test_observable.rb new file mode 100644 index 000000000..6cd10184f --- /dev/null +++ b/test/testunit/util/test_observable.rb @@ -0,0 +1,102 @@ +# 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 + module Util + class TC_Observable < TestCase + + class TF_Observable + include Observable + end + + def setup + @observable = TF_Observable.new + end + + def test_simple_observation + assert_raises(ArgumentError, "add_listener should throw an exception if no callback is supplied") do + @observable.add_listener(:property, "a") + end + + heard = false + callback = proc { heard = true } + assert_equal("a", @observable.add_listener(:property, "a", &callback), "add_listener should return the listener that was added") + + count = 0 + @observable.instance_eval do + count = notify_listeners(:property) + end + assert_equal(1, count, "notify_listeners should have returned the number of listeners that were notified") + assert(heard, "Should have heard the property changed") + + heard = false + assert_equal(callback, @observable.remove_listener(:property, "a"), "remove_listener should return the callback") + + count = 1 + @observable.instance_eval do + count = notify_listeners(:property) + end + assert_equal(0, count, "notify_listeners should have returned the number of listeners that were notified") + assert(!heard, "Should not have heard the property change") + end + + def test_value_observation + value = nil + @observable.add_listener(:property, "a") do |passed_value| + value = passed_value + end + count = 0 + @observable.instance_eval do + count = notify_listeners(:property, "stuff") + end + assert_equal(1, count, "Should have update the correct number of listeners") + assert_equal("stuff", value, "Should have received the value as an argument to the listener") + end + + def test_multiple_value_observation + values = [] + @observable.add_listener(:property, "a") do |first_value, second_value| + values = [first_value, second_value] + end + count = 0 + @observable.instance_eval do + count = notify_listeners(:property, "stuff", "more stuff") + end + assert_equal(1, count, "Should have update the correct number of listeners") + assert_equal(["stuff", "more stuff"], values, "Should have received the value as an argument to the listener") + end + + def test_add_remove_with_default_listener + assert_raises(ArgumentError, "add_listener should throw an exception if no callback is supplied") do + @observable.add_listener(:property) + end + + heard = false + callback = proc { heard = true } + assert_equal(callback, @observable.add_listener(:property, &callback), "add_listener should return the listener that was added") + + count = 0 + @observable.instance_eval do + count = notify_listeners(:property) + end + assert_equal(1, count, "notify_listeners should have returned the number of listeners that were notified") + assert(heard, "Should have heard the property changed") + + heard = false + assert_equal(callback, @observable.remove_listener(:property, callback), "remove_listener should return the callback") + + count = 1 + @observable.instance_eval do + count = notify_listeners(:property) + end + assert_equal(0, count, "notify_listeners should have returned the number of listeners that were notified") + assert(!heard, "Should not have heard the property change") + end + end + end + end +end diff --git a/test/testunit/util/test_procwrapper.rb b/test/testunit/util/test_procwrapper.rb new file mode 100644 index 000000000..1954044fb --- /dev/null +++ b/test/testunit/util/test_procwrapper.rb @@ -0,0 +1,36 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit' +require 'test/unit/util/procwrapper' + +module Test + module Unit + module Util + class TC_ProcWrapper < TestCase + def munge_proc(&a_proc) + return a_proc + end + def setup + @original = proc {} + @munged = munge_proc(&@original) + @wrapped_original = ProcWrapper.new(@original) + @wrapped_munged = ProcWrapper.new(@munged) + end + def test_wrapping + assert_same(@original, @wrapped_original.to_proc, "The wrapper should return what was wrapped") + end + def test_hashing + assert_not_equal(@original.hash, @munged.hash, "The original and munged procs should not have the same hash") + + assert_equal(@wrapped_original.hash, @wrapped_munged.hash, "The original and munged should have the same hash when wrapped") + assert_equal(@wrapped_original, @wrapped_munged, "The wrappers should be equivalent") + + a_hash = {@wrapped_original => @original} + assert_not_nil(a_hash[@wrapped_original], "Should be able to access the wrapper in the hash") + end + end + end + end +end |