[DRE-commits] [coderay] 05/14: restore tests and rake_tasks v1.1.2

Daisuke Higuchi dai at moszumanska.debian.org
Sun Oct 22 15:26:56 UTC 2017


This is an automated email from the git hooks/post-receive script.

dai pushed a commit to branch master
in repository coderay.

commit 710636716e6ccab751f41785cad31e9b7a3efc1c
Author: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
Date:   Mon Oct 23 00:01:36 2017 +0900

    restore tests and rake_tasks v1.1.2
---
 .../0000-restore_tests_and_rake_tasks_v1.1.2.patch | 4573 ++++++++++++++++++++
 debian/patches/series                              |    1 +
 2 files changed, 4574 insertions(+)

diff --git a/debian/patches/0000-restore_tests_and_rake_tasks_v1.1.2.patch b/debian/patches/0000-restore_tests_and_rake_tasks_v1.1.2.patch
new file mode 100644
index 0000000..59fe03b
--- /dev/null
+++ b/debian/patches/0000-restore_tests_and_rake_tasks_v1.1.2.patch
@@ -0,0 +1,4573 @@
+Description: restore tests and rake_tasks v1.1.2
+Author: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
+Origin: vendor
+Forwarded: not-needed
+Last-Update: 2017-10-22
+
+diff --git a/Rakefile b/Rakefile
+new file mode 100644
+index 0000000..c9b1e8a
+--- /dev/null
++++ b/Rakefile
+@@ -0,0 +1,37 @@
++require 'bundler/gem_tasks'
++
++$:.unshift File.dirname(__FILE__) unless $:.include? '.'
++
++ROOT = '.'
++LIB_ROOT = File.join ROOT, 'lib'
++
++task :default => :test
++
++if File.directory? 'rake_tasks'
++  
++  # load rake tasks from subfolder
++  for task_file in Dir['rake_tasks/*.rake'].sort
++    load task_file
++  end
++  
++else
++  
++  # fallback tasks when rake_tasks folder is not present (eg. in the distribution package)
++  desc 'Run CodeRay tests (basic)'
++  task :test do
++    ruby './test/functional/suite.rb'
++    ruby './test/functional/for_redcloth.rb'
++  end
++  
++  gem 'rdoc' if defined? gem
++  require 'rdoc/task'
++  desc 'Generate documentation for CodeRay'
++  Rake::RDocTask.new :doc do |rd|
++    rd.title = 'CodeRay Documentation'
++    rd.main = 'README_INDEX.rdoc'
++    rd.rdoc_files.add Dir['lib']
++    rd.rdoc_files.add rd.main
++    rd.rdoc_dir = 'doc'
++  end
++  
++end
+diff --git a/rake_tasks/benchmark.rake b/rake_tasks/benchmark.rake
+new file mode 100644
+index 0000000..8edeffb
+--- /dev/null
++++ b/rake_tasks/benchmark.rake
+@@ -0,0 +1,6 @@
++desc 'Do a benchmark'
++task :benchmark do
++  ruby 'bench/bench.rb ruby html'
++end
++
++task :bench => :benchmark
+diff --git a/rake_tasks/code_statistics.rb b/rake_tasks/code_statistics.rb
+new file mode 100644
+index 0000000..0a2016b
+--- /dev/null
++++ b/rake_tasks/code_statistics.rb
+@@ -0,0 +1,171 @@
++# From rails (http://rubyonrails.com)
++#
++# Improved by murphy
++class CodeStatistics
++
++  TEST_TYPES = /\btest/i
++
++  # Create a new Code Statistic.
++  #
++  # Rakefile Example:
++  #
++  #  desc 'Report code statistics (LOC) from the application'
++  #  task :stats => :copy_files do
++  #    require 'rake_helpers/code_statistics'
++  #    CodeStatistics.new(
++  #      ["Main", "lib"],
++  #      ["Tests", "test"],
++  #      ["Demos", "demo"]
++  #    ).to_s
++  #   end
++  def initialize(*pairs)
++    @pairs = pairs
++    @statistics = calculate_statistics
++    @total = if pairs.empty? then nil else calculate_total end
++  end
++
++  # Print a textual table viewing the stats
++  #
++  # Intended for console output.
++  def print
++    print_header
++    @pairs.each { |name, path| print_line name, @statistics[name] }
++    print_splitter
++
++    if @total
++      print_line 'Total', @total
++      print_splitter
++    end
++
++    print_code_test_stats
++  end
++
++private
++
++  DEFAULT_FILE_PATTERN = /\.rb$/
++
++  def calculate_statistics
++    @pairs.inject({}) do |stats, (name, path, pattern, is_ruby_code)|
++      pattern ||= DEFAULT_FILE_PATTERN
++      path = File.join path, '*.rb'
++      stats[name] = calculate_directory_statistics path, pattern, is_ruby_code
++      stats
++    end
++  end
++
++  def calculate_directory_statistics directory, pattern = DEFAULT_FILE_PATTERN, is_ruby_code = true
++    is_ruby_code = true if is_ruby_code.nil?
++    stats = Hash.new 0
++
++    Dir[directory].each do |file_name|
++      p "Scanning #{file_name}..." if $VERBOSE
++      next unless file_name =~ pattern
++
++      lines = codelines = classes = modules = methods = 0
++      empty_lines = comment_lines = 0
++      in_comment_block = false
++
++      File.readlines(file_name).each do |line|
++        lines += 1
++        if line[/^\s*$/]
++          empty_lines += 1
++        elsif is_ruby_code
++          case line
++          when /^=end\b/
++            comment_lines += 1
++            in_comment_block = false
++          when in_comment_block
++            comment_lines += 1
++          when /^\s*class\b/
++            classes += 1
++          when /^\s*module\b/
++            modules += 1
++          when /^\s*def\b/
++            methods += 1
++          when /^\s*#/
++            comment_lines += 1
++          when /^=begin\b/
++            in_comment_block = false
++            comment_lines += 1
++          when /^__END__$/
++            in_comment_block = true
++          end
++        end
++      end
++
++      codelines = lines - comment_lines - empty_lines
++
++      stats[:lines] += lines
++      stats[:comments] += comment_lines
++      stats[:codelines] += codelines
++      stats[:classes] += classes
++      stats[:modules] += modules
++      stats[:methods] += methods
++      stats[:files] += 1
++    end
++
++    stats
++  end
++
++  def calculate_total
++    total = Hash.new 0
++    @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
++    total
++  end
++
++  def calculate_code
++    code_loc = 0
++    @statistics.each { |k, v| code_loc += v[:codelines] unless k[TEST_TYPES] }
++    code_loc
++  end
++
++  def calculate_tests
++    test_loc = 0
++    @statistics.each { |k, v| test_loc += v[:codelines] if k[TEST_TYPES] }
++    test_loc
++  end
++
++  def print_header
++    print_splitter
++    puts "| T=Test  Name              | Files | Lines |   LOC | Comments | Classes | Modules | Methods | M/C | LOC/M |"
++    print_splitter
++  end
++
++  def print_splitter
++    puts "+---------------------------+-------+-------+-------+----------+---------+---------+---------+-----+-------+"
++  end
++
++  def print_line name, statistics
++    m_over_c = (statistics[:methods] / (statistics[:classes] + statistics[:modules])) rescue m_over_c = 0
++    loc_over_m = (statistics[:codelines] / statistics[:methods]) - 2 rescue loc_over_m = 0
++
++    if name[TEST_TYPES]
++      name = "T #{name}"
++    else
++      name = "  #{name}"
++    end
++
++    line = "| %-25s | %5d | %5d | %5d | %8d | %7d | %7d | %7d | %3d | %5d |" % (
++      [name, *statistics.values_at(:files, :lines, :codelines, :comments, :classes, :modules, :methods)] +
++      [m_over_c, loc_over_m] )
++
++    puts line
++  end
++
++  def print_code_test_stats
++    code = calculate_code
++    tests = calculate_tests
++
++    puts "  Code LOC = #{code}     Test LOC = #{tests}     Code:Test Ratio = [1 : #{sprintf("%.2f", tests.to_f/code)}]"
++    puts ""
++  end
++
++end
++
++# Run a test script.
++if $0 == __FILE__
++  $VERBOSE = true
++  CodeStatistics.new(
++    ['This dir', File.dirname(__FILE__)]
++  ).print
++end
+diff --git a/rake_tasks/documentation.rake b/rake_tasks/documentation.rake
+new file mode 100644
+index 0000000..4f7cef7
+--- /dev/null
++++ b/rake_tasks/documentation.rake
+@@ -0,0 +1,23 @@
++begin
++  if RUBY_VERSION >= '1.8.7'
++    gem 'rdoc' if defined? gem
++    require 'rdoc/task'
++  else
++    require 'rake/rdoctask'
++  end
++rescue LoadError
++  warn 'Please gem install rdoc.'
++end
++
++desc 'Generate documentation for CodeRay'
++Rake::RDocTask.new :doc do |rd|
++  rd.main = 'lib/README'
++  rd.title = 'CodeRay Documentation'
++  
++  rd.options << '--line-numbers' << '--tab-width' << '2'
++  
++  rd.main = 'README_INDEX.rdoc'
++  rd.rdoc_files.add 'README_INDEX.rdoc'
++  rd.rdoc_files.add Dir['lib']
++  rd.rdoc_dir = 'doc'
++end if defined? Rake::RDocTask
+diff --git a/rake_tasks/generator.rake b/rake_tasks/generator.rake
+new file mode 100644
+index 0000000..284adcb
+--- /dev/null
++++ b/rake_tasks/generator.rake
+@@ -0,0 +1,72 @@
++namespace :generate do
++  desc 'generates a new scanner NAME=lang [ALT=alternative,plugin,ids] [EXT=file,extensions] [BASE=base lang]'
++  task :scanner do
++    raise 'I need a scanner name; use NAME=lang' unless scanner_class_name = ENV['NAME']
++    raise "Invalid lang: #{scanner_class_name}; use NAME=lang." unless /\A\w+\z/ === scanner_class_name
++    require 'active_support/all'
++    lang = scanner_class_name.underscore
++    class_name = scanner_class_name.camelize
++    
++    def scanner_file_for_lang lang
++      File.join(LIB_ROOT, 'coderay', 'scanners', lang + '.rb')
++    end
++    
++    scanner_file = scanner_file_for_lang lang
++    if File.exist? scanner_file
++      print "#{scanner_file} already exists. Overwrite? [y|N] "
++      exit unless $stdin.gets.chomp.downcase == 'y'
++    end
++    
++    base_lang = ENV.fetch('BASE', 'json')
++    base_scanner_file = scanner_file_for_lang(base_lang)
++    puts "Reading base scanner #{base_scanner_file}..."
++    base_scanner = File.read base_scanner_file
++    puts "Writing new scanner #{scanner_file}..."
++    File.open(scanner_file, 'w') do |file|
++      file.write base_scanner.
++        sub(/class \w+ < Scanner/, "class #{class_name} < Scanner").
++        sub('# Scanner for JSON (JavaScript Object Notation).', "# A scanner for #{scanner_class_name}.").
++        sub(/register_for :\w+/, "register_for :#{lang}").
++        sub(/file_extension '\S+'/, "file_extension '#{ENV.fetch('EXT', lang).split(',').first}'")
++    end
++    
++    test_dir = File.join(ROOT, 'test', 'scanners', lang)
++    unless File.exist? test_dir
++      puts "Creating test folder #{test_dir}..."
++      sh "mkdir #{test_dir}"
++    end
++    test_suite_file = File.join(test_dir, 'suite.rb')
++    unless File.exist? test_suite_file
++      puts "Creating test suite file #{test_suite_file}..."
++      base_suite = File.read File.join(test_dir, '..', 'ruby', 'suite.rb')
++      File.open(test_suite_file, 'w') do |file|
++        file.write base_suite.sub(/class Ruby/, "class #{class_name}")
++      end
++    end
++    
++    if extensions = ENV['EXT']
++      file_type_file = File.join(LIB_ROOT, 'coderay', 'helpers', 'filetype.rb')
++      puts "Not automated. Remember to add your extensions to #{file_type_file}:"
++      for ext in extensions.split(',')
++        puts "    '#{ext}' => :#{lang},"
++      end
++    end
++    
++    if alternative_ids = ENV['ALT'] && alternative_ids != lang
++      map_file = File.join(LIB_ROOT, 'coderay', 'scanners', '_map.rb')
++      puts "Not automated. Remember to add your alternative plugin ids to #{map_file}:"
++      for id in alternative_ids.split(',')
++        puts "  :#{id} => :#{lang},"
++      end
++    end
++    
++    print 'Add to git? [Y|n] '
++    answer = $stdin.gets.chomp.downcase
++    if answer.empty? || answer == 'y'
++      sh "git add #{scanner_file}"
++      cd File.join('test', 'scanners') do
++        sh "git add #{lang}"
++      end
++    end
++  end
++end
+diff --git a/rake_tasks/statistic.rake b/rake_tasks/statistic.rake
+new file mode 100644
+index 0000000..d30e9b1
+--- /dev/null
++++ b/rake_tasks/statistic.rake
+@@ -0,0 +1,19 @@
++desc 'Report code statistics (LOC) from the application'
++task :stats do
++  require './rake_tasks/code_statistics'
++  CodeStatistics.new(
++    ['Main', 'lib', /coderay.rb$/],
++    ['CodeRay', 'lib/coderay/'],
++    ['  Scanners', 'lib/coderay/scanners/**'],
++    ['  Encoders', 'lib/coderay/encoders/**'],
++    ['  Helpers', 'lib/coderay/helpers/**'],
++    ['  Styles', 'lib/coderay/styles/**'],
++    ['Executable', 'bin', /coderay$/],
++    ['Executable Tests', 'test/executable/**'],
++    ['Functional Tests', 'test/functional/**'],
++    ['Scanner Tests', 'test/scanners/**', /suite\.rb$/],
++    ['Unit Tests', 'test/unit/**'],
++    # ['  Test Data', 'test/scanners/**', /\.in\./, false],
++    ['Demos', 'sample/**']
++  ).print
++end
+diff --git a/rake_tasks/test.rake b/rake_tasks/test.rake
+new file mode 100644
+index 0000000..1a23a5b
+--- /dev/null
++++ b/rake_tasks/test.rake
+@@ -0,0 +1,82 @@
++namespace :test do
++  desc 'run functional tests'
++  task :functional do
++    ruby './test/functional/suite.rb'
++    ruby './test/functional/for_redcloth.rb' unless (''.chop! rescue true)
++  end
++  
++  desc 'run unit tests'
++  task :units do
++    ruby './test/unit/suite.rb'
++  end
++  
++  scanner_suite = 'test/scanners/suite.rb'
++  desc 'run all scanner tests'
++  task :scanners => :update_scanner_suite do
++    ruby scanner_suite
++  end
++  
++  desc 'update scanner test suite from GitHub'
++  task :update_scanner_suite do
++    if File.exist? scanner_suite
++      Dir.chdir File.dirname(scanner_suite) do
++        if File.directory? '.git'
++          puts 'Updating scanner test suite...'
++          sh 'git pull'
++        elsif File.directory? '.svn'
++          raise <<-ERROR
++Found the deprecated Subversion scanner test suite in ./#{File.dirname(scanner_suite)}.
++Please rename or remove it and run again to use the GitHub repository:
++
++  mv test/scanners test/scanners-old
++          ERROR
++        else
++          raise 'No scanner test suite found.'
++        end
++      end
++    else
++      puts 'Downloading scanner test suite...'
++      sh 'git clone https://github.com/rubychan/coderay-scanner-tests.git test/scanners/'
++    end
++  end
++  
++  namespace :scanner do
++    Dir['./test/scanners/*'].each do |scanner|
++      next unless File.directory? scanner
++      lang = File.basename(scanner)
++      desc "run all scanner tests for #{lang}"
++      task lang => :update_scanner_suite do
++        ruby "./test/scanners/suite.rb #{lang}"
++      end
++    end
++  end
++  
++  desc 'clean test output files'
++  task :clean do
++    for file in Dir['test/scanners/**/*.actual.*']
++      rm file
++    end
++    for file in Dir['test/scanners/**/*.debug.diff']
++      rm file
++    end
++    for file in Dir['test/scanners/**/*.debug.diff.html']
++      rm file
++    end
++    for file in Dir['test/scanners/**/*.expected.html']
++      rm file
++    end
++  end
++  
++  desc 'test the CodeRay executable'
++  task :exe do
++    if RUBY_VERSION >= '1.8.7'
++      ruby './test/executable/suite.rb'
++    else
++      puts
++      puts "Can't run executable tests because shoulda-context requires Ruby 1.8.7+."
++      puts "Skipping."
++    end
++  end
++end
++
++task :test => %w(test:functional test:units test:exe)
+diff --git a/test/executable/source.py b/test/executable/source.py
+new file mode 100644
+index 0000000..1bb2c00
+--- /dev/null
++++ b/test/executable/source.py
+@@ -0,0 +1 @@
++class ClassName(): pass
+\ No newline at end of file
+diff --git a/test/executable/source.rb b/test/executable/source.rb
+new file mode 100644
+index 0000000..226f15f
+--- /dev/null
++++ b/test/executable/source.rb
+@@ -0,0 +1 @@
++class ClassName; end
+\ No newline at end of file
+diff --git a/test/executable/source_with_comments.rb b/test/executable/source_with_comments.rb
+new file mode 100644
+index 0000000..ec79358
+--- /dev/null
++++ b/test/executable/source_with_comments.rb
+@@ -0,0 +1,3 @@
++# a class
++class ClassName
++end
+diff --git a/test/executable/suite.rb b/test/executable/suite.rb
+new file mode 100644
+index 0000000..997405c
+--- /dev/null
++++ b/test/executable/suite.rb
+@@ -0,0 +1,226 @@
++require 'test/unit'
++require 'rubygems' unless defined? Gem
++require 'shoulda-context'
++
++require 'pathname'
++require 'json'
++
++$:.unshift File.expand_path('../../../lib', __FILE__)
++require 'coderay'
++
++puts "Running CodeRay #{CodeRay::VERSION} executable tests..."
++
++class TestCodeRayExecutable < Test::Unit::TestCase
++  
++  ROOT_DIR = Pathname.new(File.dirname(__FILE__)) + '..' + '..'
++  EXECUTABLE = ROOT_DIR + 'bin' + 'coderay'
++  RUBY_COMMAND = 'ruby'
++  EXE_COMMAND =
++    if RUBY_PLATFORM === 'java' && `ruby --ng -e '' 2> /dev/null` && $?.success?
++      # use Nailgun
++      "#{RUBY_COMMAND}--ng -I%s %s"
++    else
++      "#{RUBY_COMMAND} -I%s %s"
++    end % [ROOT_DIR + 'lib', EXECUTABLE]
++  
++  def coderay args, options = {}
++    if options[:fake_tty]
++      command = "#{EXE_COMMAND} #{args} --tty"
++    else
++      command = "#{EXE_COMMAND} #{args}"
++    end
++    
++    puts command if $DEBUG
++    
++    if options[:input]
++      output = IO.popen "#{command} 2>&1", "r+" do |io|
++        io.write options[:input]
++        io.close_write
++        io.read
++      end
++    else
++      output = `#{command} 2>&1`
++    end
++    
++    if output[EXECUTABLE.to_s]
++      raise output
++    else
++      output
++    end
++  end
++  
++  context 'a simple call with no arguments' do
++    should 'work' do
++      assert_nothing_raised { coderay('') }
++    end
++    should 'print version and help' do
++      assert_match(/CodeRay #{CodeRay::VERSION}/, coderay(''))
++      assert_match(/usage:/, coderay(''))
++    end
++  end
++  
++  context 'version' do
++    should 'be printed with -v' do
++      assert_match(/\ACodeRay #{CodeRay::VERSION}\Z/, coderay('-v'))
++    end
++    should 'be printed with --version' do
++      assert_match(/\ACodeRay #{CodeRay::VERSION}\Z/, coderay('--version'))
++    end
++  end
++  
++  context 'help' do
++    should 'be printed with -h' do
++      assert_match(/^usage:/, coderay('-h'))
++    end
++    should 'be printed with --help' do
++      assert_match(/^usage:/, coderay('--help'))
++    end
++    should 'be printed with subcommand help' do
++      assert_match(/^usage:/, coderay('help'))
++    end
++  end
++  
++  context 'commands' do
++    should 'be printed with subcommand commands' do
++      assert_match(/^ +help/, coderay('commands'))
++      assert_match(/^ +version/, coderay('commands'))
++    end
++  end
++  
++  context 'highlighting a file to the terminal' do
++    source_file = ROOT_DIR + 'test/executable/source.py'
++    
++    source = File.read source_file
++    
++    ansi_seq = /\e\[[0-9;]+m/
++    
++    should 'not throw an error' do
++      assert_nothing_raised { coderay(source_file, :fake_tty => true) }
++    end
++    should 'output its contents to stdout' do
++      target = coderay(source_file, :fake_tty => true)
++      assert_equal source, target.chomp.gsub(ansi_seq, '')
++    end
++    should 'output ANSI-colored text' do
++      target = coderay(source_file, :fake_tty => true)
++      assert_not_equal source, target.chomp
++      assert_equal 6, target.scan(ansi_seq).size
++    end
++  end
++  
++  context 'highlighting a file into a pipe (source.rb -html > source.rb.html)' do
++    source_file = ROOT_DIR + 'test/executable/source.rb'
++    target_file = "#{source_file}.html"
++    command = "#{source_file} -html > #{target_file}"
++    
++    source = File.read source_file
++    
++    pre = %r{<td class="code"><pre>(.*?)</pre>}m
++    tag = /<[^>]*>/
++    
++    should 'not throw an error' do
++      assert_nothing_raised { coderay(command) }
++    end
++    should 'output its contents to the pipe' do
++      coderay(command)
++      target = File.read(target_file)
++      if target = target[pre, 1]
++        assert_equal source, target.gsub(tag, '').strip
++      else
++        flunk "target code has no <pre> tag: #{target}"
++      end
++    end
++    should 'output valid HTML' do
++      coderay(command)
++      target = File.read(target_file)
++      assert_not_equal source, target[pre, 1]
++      assert_equal 6, target[pre, 1].scan(tag).size
++      assert_match %r{\A<!DOCTYPE html>\n<html>\n<head>}, target
++    end
++  end
++  
++  context 'highlighting a file into another file (source.rb source.rb.json)' do
++    source_file = ROOT_DIR + 'test/executable/source.rb'
++    target_file = "#{source_file}.json"
++    command = "#{source_file} #{target_file}"
++    
++    source = File.read source_file
++    
++    text = /"text":"([^"]*)"/
++    
++    should 'not throw an error' do
++      assert_nothing_raised { coderay(command) }
++    end
++    should 'output its contents to the file' do
++      coderay(command)
++      target = File.read(target_file)
++      assert_equal source, target.scan(text).join
++    end
++    should 'output JSON' do
++      coderay(command)
++      target = File.read(target_file)
++      assert_not_equal source, target
++      assert_equal 6, target.scan(text).size
++    end
++  end
++  
++  context 'highlighting a file without explicit input type (source.py)' do
++    source_file = ROOT_DIR + 'test/executable/source.py'
++    command = "#{source_file} -html"
++    
++    source = File.read source_file
++    
++    pre = %r{<td class="code"><pre>(.*?)</pre>}m
++    tag_class = /<span class="([^>"]*)"?[^>]*>/
++    
++    should 'respect the file extension and highlight the input as Python' do
++      target = coderay(command)
++      assert_equal %w(keyword class keyword), target[pre, 1].scan(tag_class).flatten
++    end
++  end
++  
++  context 'highlighting a file with explicit input type (-ruby source.py)' do
++    source_file = ROOT_DIR + 'test/executable/source.py'
++    command = "-ruby #{source_file} -html"
++    
++    source = File.read source_file
++    
++    pre = %r{<td class="code"><pre>(.*?)</pre>}m
++    tag_class = /<span class="([^>"]*)"?[^>]*>/
++    
++    should 'ignore the file extension and highlight the input as Ruby' do
++      target = coderay(command)
++      assert_equal %w(keyword class), target[pre, 1].scan(tag_class).flatten
++    end
++  end
++  
++  context 'highlighting a file with explicit input and output type (-ruby source.py -span)' do
++    source_file = ROOT_DIR + 'test/executable/source.py'
++    command = "-ruby #{source_file} -span"
++    
++    source = File.read source_file
++    
++    span_tags = /<\/?span[^>]*>/
++    
++    should 'just respect the output type and include span tags' do
++      target = coderay(command)
++      assert_equal source, target.chomp.gsub(span_tags, '')
++    end
++  end
++  
++  context 'the LOC counter' do
++    source_file = ROOT_DIR + 'test/executable/source_with_comments.rb'
++    command = "-ruby -loc"
++    
++    should 'work' do
++      output = coderay(command, :input => <<-CODE)
++# test
++=begin
++=end
++test
++      CODE
++      assert_equal "1\n", output
++    end
++  end
++  
++end
+diff --git a/test/functional/basic.rb b/test/functional/basic.rb
+new file mode 100644
+index 0000000..752d4ba
+--- /dev/null
++++ b/test/functional/basic.rb
+@@ -0,0 +1,318 @@
++# encoding: utf-8
++require 'test/unit'
++require File.expand_path('../../lib/assert_warning', __FILE__)
++
++$:.unshift File.expand_path('../../../lib', __FILE__)
++require 'coderay'
++
++class BasicTest < Test::Unit::TestCase
++  
++  def test_version
++    assert_nothing_raised do
++      assert_match(/\A\d\.\d\.\d?\z/, CodeRay::VERSION)
++    end
++  end
++  
++  def with_empty_load_path
++    old_load_path = $:.dup
++    $:.clear
++    yield
++  ensure
++    $:.replace old_load_path
++  end
++  
++  def test_autoload
++    with_empty_load_path do
++      assert_nothing_raised do
++        CodeRay::Scanners::Java::BuiltinTypes
++      end
++    end
++  end
++  
++  RUBY_TEST_CODE = 'puts "Hello, World!"'
++  
++  RUBY_TEST_TOKENS = [
++    ['puts', :ident],
++    [' ', :space],
++    [:begin_group, :string],
++      ['"', :delimiter],
++      ['Hello, World!', :content],
++      ['"', :delimiter],
++    [:end_group, :string]
++  ].flatten
++  def test_simple_scan
++    assert_nothing_raised do
++      assert_equal RUBY_TEST_TOKENS, CodeRay.scan(RUBY_TEST_CODE, :ruby).tokens
++    end
++  end
++  
++  RUBY_TEST_HTML = 'puts <span class="string"><span class="delimiter">"</span>' + 
++    '<span class="content">Hello, World!</span><span class="delimiter">"</span></span>'
++  def test_simple_highlight
++    assert_nothing_raised do
++      assert_equal RUBY_TEST_HTML, CodeRay.scan(RUBY_TEST_CODE, :ruby).html
++    end
++  end
++  
++  def test_scan_file
++    CodeRay.scan_file __FILE__
++  end
++  
++  def test_encode
++    assert_equal 1, CodeRay.encode('test', :python, :count)
++  end
++  
++  def test_encode_tokens
++    assert_equal 1, CodeRay.encode_tokens(CodeRay::Tokens['test', :string], :count)
++  end
++  
++  def test_encode_file
++    assert_equal File.read(__FILE__), CodeRay.encode_file(__FILE__, :text)
++  end
++  
++  def test_highlight
++    assert_match '<pre>test</pre>', CodeRay.highlight('test', :python)
++  end
++  
++  def test_highlight_file
++    assert_match "require <span class=\"string\"><span class=\"delimiter\">'</span><span class=\"content\">test/unit</span><span class=\"delimiter\">'</span></span>\n", CodeRay.highlight_file(__FILE__)
++  end
++  
++  def test_duo
++    assert_equal(RUBY_TEST_CODE,
++      CodeRay::Duo[:plain, :text].highlight(RUBY_TEST_CODE))
++    assert_equal(RUBY_TEST_CODE,
++      CodeRay::Duo[:plain => :text].highlight(RUBY_TEST_CODE))
++  end
++  
++  def test_duo_stream
++    assert_equal(RUBY_TEST_CODE,
++      CodeRay::Duo[:plain, :text].highlight(RUBY_TEST_CODE, :stream => true))
++  end
++  
++  def test_comment_filter
++    assert_equal <<-EXPECTED, CodeRay.scan(<<-INPUT, :ruby).comment_filter.text
++#!/usr/bin/env ruby
++
++code
++
++more code  
++      EXPECTED
++#!/usr/bin/env ruby
++=begin
++A multi-line comment.
++=end
++code
++# A single-line comment.
++more code  # and another comment, in-line.
++      INPUT
++  end
++  
++  def test_lines_of_code
++    assert_equal 2, CodeRay.scan(<<-INPUT, :ruby).lines_of_code
++#!/usr/bin/env ruby
++=begin
++A multi-line comment.
++=end
++code
++# A single-line comment.
++more code  # and another comment, in-line.
++      INPUT
++    rHTML = <<-RHTML
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
++    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
++
++<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
++<head>
++  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
++  <title><%= controller.controller_name.titleize %>: <%= controller.action_name %></title>
++  <%= stylesheet_link_tag 'scaffold' %>
++</head>
++<body>
++
++<p style="color: green"><%= flash[:notice] %></p>
++
++<div id="main">
++  <%= yield %>
++</div>
++
++</body>
++</html>
++      RHTML
++    assert_equal 0, CodeRay.scan(rHTML, :html).lines_of_code
++    assert_equal 0, CodeRay.scan(rHTML, :php).lines_of_code
++    assert_equal 0, CodeRay.scan(rHTML, :yaml).lines_of_code
++    assert_equal 4, CodeRay.scan(rHTML, :erb).lines_of_code
++  end
++  
++  def test_list_of_encoders
++    assert_kind_of(Array, CodeRay::Encoders.list)
++    assert CodeRay::Encoders.list.include?(:count)
++  end
++  
++  def test_list_of_scanners
++    assert_kind_of(Array, CodeRay::Scanners.list)
++    assert CodeRay::Scanners.list.include?(:text)
++  end
++  
++  def test_token_kinds
++    assert_kind_of Hash, CodeRay::TokenKinds
++    for kind, css_class in CodeRay::TokenKinds
++      assert_kind_of Symbol, kind
++      if css_class != false
++        assert_kind_of String, css_class, "TokenKinds[%p] == %p" % [kind, css_class]
++      end
++    end
++    assert_equal 'reserved', CodeRay::TokenKinds[:reserved]
++    assert_equal false,      CodeRay::TokenKinds[:shibboleet]
++  end
++  
++  class Milk < CodeRay::Encoders::Encoder
++    FILE_EXTENSION = 'cocoa'
++  end
++  
++  class HoneyBee < CodeRay::Encoders::Encoder
++  end
++  
++  def test_encoder_file_extension
++    assert_nothing_raised do
++      assert_equal 'html', CodeRay::Encoders::Page::FILE_EXTENSION
++      assert_equal 'cocoa', Milk::FILE_EXTENSION
++      assert_equal 'cocoa', Milk.new.file_extension
++      assert_equal 'honeybee', HoneyBee::FILE_EXTENSION
++      assert_equal 'honeybee', HoneyBee.new.file_extension
++    end
++    assert_raise NameError do
++      HoneyBee::MISSING_CONSTANT
++    end
++  end
++  
++  def test_encoder_tokens
++    encoder = CodeRay::Encoders::Encoder.new
++    encoder.send :setup, {}
++    assert_raise(ArgumentError) { encoder.token :strange, '' }
++    encoder.token 'test', :debug
++  end
++  
++  def test_encoder_deprecated_interface
++    encoder = CodeRay::Encoders::Encoder.new
++    encoder.send :setup, {}
++    assert_warning 'Using old Tokens#<< interface.' do
++      encoder << ['test', :content]
++    end
++    assert_raise ArgumentError do
++      encoder << [:strange, :input]
++    end
++    assert_raise ArgumentError do
++      encoder.encode_tokens [['test', :token]]
++    end
++  end
++  
++  def encoder_token_interface_deprecation_warning_given
++    CodeRay::Encoders::Encoder.send :class_variable_get, :@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN
++  end
++  
++  def test_scanner_file_extension
++    assert_equal 'rb', CodeRay::Scanners::Ruby.file_extension
++    assert_equal 'rb', CodeRay::Scanners::Ruby.new.file_extension
++    assert_equal 'java', CodeRay::Scanners::Java.file_extension
++    assert_equal 'java', CodeRay::Scanners::Java.new.file_extension
++  end
++  
++  def test_scanner_lang
++    assert_equal :ruby, CodeRay::Scanners::Ruby.lang
++    assert_equal :ruby, CodeRay::Scanners::Ruby.new.lang
++    assert_equal :java, CodeRay::Scanners::Java.lang
++    assert_equal :java, CodeRay::Scanners::Java.new.lang
++  end
++  
++  def test_scanner_tokenize
++    assert_equal ['foo', :plain], CodeRay::Scanners::Plain.new.tokenize('foo')
++    assert_equal [['foo', :plain], ['bar', :plain]], CodeRay::Scanners::Plain.new.tokenize(['foo', 'bar'])
++    CodeRay::Scanners::Plain.new.tokenize 42
++  end
++  
++  def test_scanner_tokens
++    scanner = CodeRay::Scanners::Plain.new
++    scanner.tokenize('foo')
++    assert_equal ['foo', :plain], scanner.tokens
++    scanner.string = ''
++    assert_equal ['', :plain], scanner.tokens
++  end
++  
++  def test_scanner_line_and_column
++    scanner = CodeRay::Scanners::Plain.new "foo\nbär+quux"
++    assert_equal 0, scanner.pos
++    assert_equal 1, scanner.line
++    assert_equal 1, scanner.column
++    scanner.scan(/foo/)
++    assert_equal 3, scanner.pos
++    assert_equal 1, scanner.line
++    assert_equal 4, scanner.column
++    scanner.scan(/\n/)
++    assert_equal 4, scanner.pos
++    assert_equal 2, scanner.line
++    assert_equal 1, scanner.column
++    scanner.scan(/b/)
++    assert_equal 5, scanner.pos
++    assert_equal 2, scanner.line
++    assert_equal 2, scanner.column
++    scanner.scan(/a/)
++    assert_equal 5, scanner.pos
++    assert_equal 2, scanner.line
++    assert_equal 2, scanner.column
++    scanner.scan(/ä/)
++    assert_equal 7, scanner.pos
++    assert_equal 2, scanner.line
++    assert_equal 4, scanner.column
++    scanner.scan(/r/)
++    assert_equal 8, scanner.pos
++    assert_equal 2, scanner.line
++    assert_equal 5, scanner.column
++  end
++  
++  def test_scanner_use_subclasses
++    assert_raise NotImplementedError do
++      CodeRay::Scanners::Scanner.new
++    end
++  end
++  
++  class InvalidScanner < CodeRay::Scanners::Scanner
++  end
++  
++  def test_scanner_scan_tokens
++    assert_raise NotImplementedError do
++      InvalidScanner.new.tokenize ''
++    end
++  end
++  
++  class RaisingScanner < CodeRay::Scanners::Scanner
++    def scan_tokens encoder, options
++      raise_inspect 'message', [], :initial
++    end
++  end
++  
++  def test_scanner_raise_inspect
++    assert_raise CodeRay::Scanners::Scanner::ScanError do
++      RaisingScanner.new.tokenize ''
++    end
++  end
++  
++  def test_scan_a_frozen_string
++    assert_nothing_raised do
++      CodeRay.scan RUBY_VERSION, :ruby
++      CodeRay.scan RUBY_VERSION, :plain
++    end
++  end
++  
++  def test_scan_a_non_string
++    assert_nothing_raised do
++      CodeRay.scan 42, :ruby
++      CodeRay.scan nil, :ruby
++      CodeRay.scan self, :ruby
++      CodeRay.encode ENV.to_hash, :ruby, :page
++      CodeRay.highlight CodeRay, :plain
++    end
++  end
++  
++end
+diff --git a/test/functional/examples.rb b/test/functional/examples.rb
+new file mode 100644
+index 0000000..985ef87
+--- /dev/null
++++ b/test/functional/examples.rb
+@@ -0,0 +1,129 @@
++require 'test/unit'
++
++$:.unshift File.expand_path('../../../lib', __FILE__)
++require 'coderay'
++
++class ExamplesTest < Test::Unit::TestCase
++  
++  def test_examples
++    # output as HTML div (using inline CSS styles)
++    div = CodeRay.scan('puts "Hello, world!"', :ruby).div
++    assert_equal <<-DIV, div
++<div class="CodeRay">
++  <div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Hello, world!</span><span style="color:#710">"</span></span></pre></div>
++</div>
++    DIV
++    
++    # ...with line numbers
++    div = CodeRay.scan(<<-CODE.chomp, :ruby).div(:line_numbers => :table)
++5.times do
++  puts 'Hello, world!'
++end
++    CODE
++    assert_equal <<-DIV, div
++<table class="CodeRay"><tr>
++  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
++<a href="#n2" name="n2">2</a>
++<a href="#n3" name="n3">3</a>
++</pre></td>
++  <td class="code"><pre><span style="color:#00D">5</span>.times <span style="color:#080;font-weight:bold">do</span>
++  puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">'</span><span style="color:#D20">Hello, world!</span><span style="color:#710">'</span></span>
++<span style="color:#080;font-weight:bold">end</span></pre></td>
++</tr></table>
++    DIV
++    
++    # output as standalone HTML page (using CSS classes)
++    page = CodeRay.scan('puts "Hello, world!"', :ruby).page
++    assert_match <<-PAGE, page
++<body>
++
++<table class="CodeRay"><tr>
++  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
++</pre></td>
++  <td class="code"><pre>puts <span class="string"><span class="delimiter">"</span><span class="content">Hello, world!</span><span class="delimiter">"</span></span></pre></td>
++</tr></table>
++
++</body>
++    PAGE
++    
++    # keep scanned tokens for later use
++    tokens = CodeRay.scan('{ "just": "an", "example": 42 }', :json)
++    assert_kind_of CodeRay::TokensProxy, tokens
++    
++    assert_equal ["{", :operator, " ", :space, :begin_group, :key,
++      "\"", :delimiter, "just", :content, "\"", :delimiter,
++      :end_group, :key, ":", :operator, " ", :space,
++      :begin_group, :string, "\"", :delimiter, "an", :content,
++      "\"", :delimiter, :end_group, :string, ",", :operator,
++      " ", :space, :begin_group, :key, "\"", :delimiter,
++      "example", :content, "\"", :delimiter, :end_group, :key,
++      ":", :operator, " ", :space, "42", :integer,
++      " ", :space, "}", :operator], tokens.tokens
++    
++    # produce a token statistic
++    assert_equal <<-STATISTIC, tokens.statistic
++
++Code Statistics
++
++Tokens                  26
++  Non-Whitespace        15
++Bytes Total             31
++
++Token Types (7):
++  type                     count     ratio    size (average)
++-------------------------------------------------------------
++  TOTAL                       26  100.00 %     1.2
++  delimiter                    6   23.08 %     1.0
++  operator                     5   19.23 %     1.0
++  space                        5   19.23 %     1.0
++  key                          4   15.38 %     0.0
++  :begin_group                 3   11.54 %     0.0
++  :end_group                   3   11.54 %     0.0
++  content                      3   11.54 %     4.3
++  string                       2    7.69 %     0.0
++  integer                      1    3.85 %     2.0
++
++    STATISTIC
++    
++    # count the tokens
++    assert_equal 26, tokens.count
++    
++    # produce a HTML div, but with CSS classes
++    div = tokens.div(:css => :class)
++    assert_equal <<-DIV, div
++<div class="CodeRay">
++  <div class="code"><pre>{ <span class="key"><span class="delimiter">"</span><span class="content">just</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">an</span><span class="delimiter">"</span></span>, <span class="key"><span class="delimiter">"</span><span class="content">example</span><span class="delimiter">"</span></span>: <span class="integer">42</span> }</pre></div>
++</div>
++    DIV
++    
++    # highlight a file (HTML div); guess the file type base on the extension
++    assert_equal :ruby, CodeRay::FileType[__FILE__]
++    
++    # get a new scanner for Python
++    python_scanner = CodeRay.scanner :python
++    assert_kind_of CodeRay::Scanners::Python, python_scanner
++    
++    # get a new encoder for terminal
++    terminal_encoder = CodeRay.encoder :term
++    assert_kind_of CodeRay::Encoders::Terminal, terminal_encoder
++    
++    # scanning into tokens
++    tokens = python_scanner.tokenize 'import this;  # The Zen of Python'
++    assert_equal ["import", :keyword, " ", :space, "this", :include,
++      ";", :operator, "  ", :space, "# The Zen of Python", :comment], tokens
++    
++    # format the tokens
++    term = terminal_encoder.encode_tokens(tokens)
++    assert_equal "\e[32mimport\e[0m \e[31mthis\e[0m;  \e[1;30m# The Zen of Python\e[0m", term
++    
++    # re-using scanner and encoder
++    ruby_highlighter = CodeRay::Duo[:ruby, :div]
++    div = ruby_highlighter.encode('puts "Hello, world!"')
++    assert_equal <<-DIV, div
++<div class="CodeRay">
++  <div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Hello, world!</span><span style="color:#710">"</span></span></pre></div>
++</div>
++    DIV
++  end
++  
++end
+diff --git a/test/functional/for_redcloth.rb b/test/functional/for_redcloth.rb
+new file mode 100644
+index 0000000..9fd244e
+--- /dev/null
++++ b/test/functional/for_redcloth.rb
+@@ -0,0 +1,78 @@
++require 'test/unit'
++
++$:.unshift File.expand_path('../../../lib', __FILE__)
++require 'coderay'
++
++begin
++  require 'rubygems' unless defined? Gem
++  gem 'RedCloth', '>= 4.0.3' rescue nil
++  require 'redcloth'
++rescue LoadError
++  warn 'RedCloth not found - skipping for_redcloth tests.'
++  undef RedCloth if defined? RedCloth
++end
++
++class BasicTest < Test::Unit::TestCase
++  
++  def test_for_redcloth
++    require 'coderay/for_redcloth'
++    assert_equal "<p><span lang=\"ruby\" class=\"CodeRay\">puts <span style=\"background-color:hsla(0,100%,50%,0.05)\"><span style=\"color:#710\">"</span><span style=\"color:#D20\">Hello, World!</span><span style=\"color:#710\">"</span></span></span></p>",
++      RedCloth.new('@[ruby]puts "Hello, World!"@').to_html
++    assert_equal <<-BLOCKCODE.chomp,
++<div lang="ruby" class="CodeRay">
++  <div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Hello, World!</span><span style="color:#710">"</span></span></pre></div>
++</div>
++      BLOCKCODE
++      RedCloth.new('bc[ruby]. puts "Hello, World!"').to_html
++  end
++  
++  def test_for_redcloth_no_lang
++    require 'coderay/for_redcloth'
++    assert_equal "<p><code>puts \"Hello, World!\"</code></p>",
++      RedCloth.new('@puts "Hello, World!"@').to_html
++    assert_equal <<-BLOCKCODE.chomp,
++<pre><code>puts \"Hello, World!\"</code></pre>
++      BLOCKCODE
++      RedCloth.new('bc. puts "Hello, World!"').to_html
++  end
++  
++  def test_for_redcloth_style
++    require 'coderay/for_redcloth'
++    assert_equal <<-BLOCKCODE.chomp,
++<pre style=\"color: red;\"><code style=\"color: red;\">puts \"Hello, World!\"</code></pre>
++      BLOCKCODE
++      RedCloth.new('bc{color: red}. puts "Hello, World!"').to_html
++  end
++  
++  def test_for_redcloth_escapes
++    require 'coderay/for_redcloth'
++    assert_equal '<p><span lang="ruby" class="CodeRay">></span></p>',
++      RedCloth.new('@[ruby]>@').to_html
++    assert_equal <<-BLOCKCODE.chomp,
++<div lang="ruby" class="CodeRay">
++  <div class="code"><pre>&</pre></div>
++</div>
++      BLOCKCODE
++      RedCloth.new('bc[ruby]. &').to_html
++  end
++  
++  def test_for_redcloth_escapes2
++    require 'coderay/for_redcloth'
++    assert_equal "<p><span lang=\"c\" class=\"CodeRay\"><span style=\"color:#579\">#include</span> <span style=\"color:#B44;font-weight:bold\"><test.h></span></span></p>",
++      RedCloth.new('@[c]#include <test.h>@').to_html
++  end
++  
++  # See http://jgarber.lighthouseapp.com/projects/13054/tickets/124-code-markup-does-not-allow-brackets.
++  def test_for_redcloth_false_positive
++    require 'coderay/for_redcloth'
++    assert_equal '<p><code>[project]_dff.skjd</code></p>',
++      RedCloth.new('@[project]_dff.skjd@').to_html
++    # false positive, but expected behavior / known issue
++    assert_equal "<p><span lang=\"ruby\" class=\"CodeRay\">_dff.skjd</span></p>",
++      RedCloth.new('@[ruby]_dff.skjd@').to_html
++    assert_equal <<-BLOCKCODE.chomp, RedCloth.new('bc. [project]_dff.skjd').to_html
++<pre><code>[project]_dff.skjd</code></pre>
++    BLOCKCODE
++  end
++  
++end if defined? RedCloth
+\ No newline at end of file
+diff --git a/test/functional/suite.rb b/test/functional/suite.rb
+new file mode 100644
+index 0000000..ec23eec
+--- /dev/null
++++ b/test/functional/suite.rb
+@@ -0,0 +1,15 @@
++require 'test/unit'
++
++$VERBOSE = $CODERAY_DEBUG = true
++$:.unshift File.expand_path('../../../lib', __FILE__)
++require 'coderay'
++
++mydir = File.dirname(__FILE__)
++suite = Dir[File.join(mydir, '*.rb')].
++  map { |tc| File.basename(tc).sub(/\.rb$/, '') } - %w'suite for_redcloth'
++
++puts "Running basic CodeRay #{CodeRay::VERSION} tests: #{suite.join(', ')}"
++
++for test_case in suite
++  load File.join(mydir, test_case + '.rb')
++end
+diff --git a/test/lib/README b/test/lib/README
+new file mode 100644
+index 0000000..7c41648
+--- /dev/null
++++ b/test/lib/README
+@@ -0,0 +1,2 @@
++Contents:
++- test/unit: We need the old Test::Unit for the scanner test suite to work with Ruby 1.9.
+diff --git a/test/lib/assert_warning.rb b/test/lib/assert_warning.rb
+new file mode 100644
+index 0000000..828b464
+--- /dev/null
++++ b/test/lib/assert_warning.rb
+@@ -0,0 +1,15 @@
++class Test::Unit::TestCase
++  
++  def assert_warning expected_warning
++    require 'stringio'
++    oldstderr = $stderr
++    $stderr = StringIO.new
++    yield
++    $stderr.rewind
++    given_warning = $stderr.read.chomp
++    assert_equal expected_warning, given_warning
++  ensure
++    $stderr = oldstderr
++  end
++  
++end
+diff --git a/test/lib/test/unit.rb b/test/lib/test/unit.rb
+new file mode 100644
+index 0000000..b71f644
+--- /dev/null
++++ b/test/lib/test/unit.rb
+@@ -0,0 +1,280 @@
++require 'test/unit/testcase'
++require 'test/unit/autorunner'
++
++module Test # :nodoc:
++  #
++  # = 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.
++  # 
++  # 
++  # == 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 at 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
++  #
++  # ----
++  #
++  # = 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).
++  #
++
++  module Unit
++    # If set to false Test::Unit will not automatically run at exit.
++    def self.run=(flag)
++      @run = flag
++    end
++
++    # Automatically run tests at exit?
++    def self.run?
++      @run ||= false
++    end
++  end
++end
++
++at_exit do
++  unless $! || Test::Unit.run?
++    exit Test::Unit::AutoRunner.run
++  end
++end
+diff --git a/test/lib/test/unit/assertionfailederror.rb b/test/lib/test/unit/assertionfailederror.rb
+new file mode 100644
+index 0000000..a21e4b5
+--- /dev/null
++++ b/test/lib/test/unit/assertionfailederror.rb
+@@ -0,0 +1,14 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
++# License:: Ruby license.
++
++module Test
++  module Unit
++
++    # Thrown by Test::Unit::Assertions when an assertion fails.
++    class AssertionFailedError < StandardError
++    end
++  end
++end
+diff --git a/test/lib/test/unit/assertions.rb b/test/lib/test/unit/assertions.rb
+new file mode 100644
+index 0000000..aa97799
+--- /dev/null
++++ b/test/lib/test/unit/assertions.rb
+@@ -0,0 +1,622 @@
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
++# License:: Ruby license.
++
++require 'test/unit/assertionfailederror'
++require 'test/unit/util/backtracefilter'
++
++module Test
++  module Unit
++
++    ##
++    # Test::Unit::Assertions contains the standard Test::Unit assertions.
++    # Assertions is included in Test::Unit::TestCase.
++    #
++    # To include it in your own code and use its functionality, you simply
++    # need to rescue Test::Unit::AssertionFailedError. Additionally you may
++    # override add_assertion to get notified whenever an assertion is made.
++    #
++    # Notes:
++    # * The message to each assertion, if given, will be propagated with the
++    #   failure.
++    # * It is easy to add your own assertions based on assert_block().
++    #
++    # = Example Custom Assertion
++    #
++    #   def deny(boolean, message = nil)
++    #     message = build_message message, '<?> is not false or nil.', boolean
++    #     assert_block message do
++    #       not boolean
++    #     end
++    #   end
++
++    module Assertions
++
++      ##
++      # The assertion upon which all other assertions are based. Passes if the
++      # block yields true.
++      #
++      # Example:
++      #   assert_block "Couldn't do the thing" do
++      #     do_the_thing
++      #   end
++
++      public
++      def assert_block(message="assert_block failed.") # :yields: 
++        _wrap_assertion do
++          if (! yield)
++            raise AssertionFailedError.new(message.to_s)
++          end
++        end
++      end
++
++      ##
++      # Asserts that +boolean+ is not false or nil.
++      #
++      # Example:
++      #   assert [1, 2].include?(5)
++
++      public
++      def assert(boolean, message=nil)
++        _wrap_assertion do
++          assert_block("assert should not be called with a block.") { !block_given? }
++          assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
++        end
++      end
++
++      ##
++      # Passes if +expected+ == +actual.
++      #
++      # Note that the ordering of arguments is important, since a helpful
++      # error message is generated when this one fails that tells you the
++      # values of expected and actual.
++      #
++      # Example:
++      #   assert_equal 'MY STRING', 'my string'.upcase
++
++      public
++      def assert_equal(expected, actual, message=nil)
++        full_message = build_message(message, <<EOT, expected, actual)
++<?> expected but was
++<?>.
++EOT
++        assert_block(full_message) { expected == actual }
++      end
++
++      private
++      def _check_exception_class(args) # :nodoc:
++        args.partition do |klass|
++          next if klass.instance_of?(Module)
++          assert(Exception >= klass, "Should expect a class of exception, #{klass}")
++          true
++        end
++      end
++
++      private
++      def _expected_exception?(actual_exception, exceptions, modules) # :nodoc:
++        exceptions.include?(actual_exception.class) or
++          modules.any? {|mod| actual_exception.is_a?(mod)}
++      end
++
++      ##
++      # Passes if the block raises one of the given exceptions.
++      #
++      # Example:
++      #   assert_raise RuntimeError, LoadError do
++      #     raise 'Boom!!!'
++      #   end
++
++      public
++      def assert_raise(*args)
++        _wrap_assertion do
++          if Module === args.last
++            message = ""
++          else
++            message = args.pop
++          end
++          exceptions, modules = _check_exception_class(args)
++          expected = args.size == 1 ? args.first : args
++          actual_exception = nil
++          full_message = build_message(message, "<?> exception expected but none was thrown.", expected)
++          assert_block(full_message) do
++            begin
++              yield
++            rescue Exception => actual_exception
++              break
++            end
++            false
++          end
++          full_message = build_message(message, "<?> exception expected but was\n?", expected, actual_exception)
++          assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)}
++          actual_exception
++        end
++      end
++
++      ##
++      # Alias of assert_raise.
++      #
++      # Will be deprecated in 1.9, and removed in 2.0.
++
++      public
++      def assert_raises(*args, &block)
++        assert_raise(*args, &block)
++      end
++
++      ##
++      # Passes if +object+ .instance_of? +klass+
++      #
++      # Example:
++      #   assert_instance_of String, 'foo'
++
++      public
++      def assert_instance_of(klass, object, message="")
++        _wrap_assertion do
++          assert_equal(Class, klass.class, "assert_instance_of takes a Class as its first argument")
++          full_message = build_message(message, <<EOT, object, klass, object.class)
++<?> expected to be an instance of
++<?> but was
++<?>.
++EOT
++          assert_block(full_message){object.instance_of?(klass)}
++        end
++      end
++
++      ##
++      # Passes if +object+ is nil.
++      #
++      # Example:
++      #   assert_nil [1, 2].uniq!
++
++      public
++      def assert_nil(object, message="")
++        assert_equal(nil, object, message)
++      end
++
++      ##
++      # Passes if +object+ .kind_of? +klass+
++      #
++      # Example:
++      #   assert_kind_of Object, 'foo'
++
++      public
++      def assert_kind_of(klass, object, message="")
++        _wrap_assertion do
++          assert(klass.kind_of?(Module), "The first parameter to assert_kind_of should be a kind_of Module.")
++          full_message = build_message(message, "<?>\nexpected to be kind_of\\?\n<?> but was\n<?>.", object, klass, object.class)
++          assert_block(full_message){object.kind_of?(klass)}
++        end
++      end
++
++      ##
++      # Passes if +object+ .respond_to? +method+
++      #
++      # Example:
++      #   assert_respond_to 'bugbear', :slice
++
++      public
++      def assert_respond_to(object, method, message="")
++        _wrap_assertion do
++          full_message = build_message(nil, "<?>\ngiven as the method name argument to #assert_respond_to must be a Symbol or #respond_to\\?(:to_str).", method)
++
++          assert_block(full_message) do
++            method.kind_of?(Symbol) || method.respond_to?(:to_str)
++          end
++          full_message = build_message(message, <<EOT, object, object.class, method)
++<?>
++of type <?>
++expected to respond_to\\?<?>.
++EOT
++          assert_block(full_message) { object.respond_to?(method) }
++        end
++      end
++
++      ##
++      # Passes if +string+ =~ +pattern+.
++      #
++      # Example:
++      #   assert_match(/\d+/, 'five, 6, seven')
++
++      public
++      def assert_match(pattern, string, message="")
++        _wrap_assertion do
++          pattern = case(pattern)
++            when String
++              Regexp.new(Regexp.escape(pattern))
++            else
++              pattern
++          end
++          full_message = build_message(message, "<?> expected to be =~\n<?>.", string, pattern)
++          assert_block(full_message) { string =~ pattern }
++        end
++      end
++
++      ##
++      # Passes if +actual+ .equal? +expected+ (i.e. they are the same
++      # instance).
++      #
++      # Example:
++      #   o = Object.new
++      #   assert_same o, o
++
++      public
++      def assert_same(expected, actual, message="")
++        full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
++<?>
++with id <?> expected to be equal\\? to
++<?>
++with id <?>.
++EOT
++        assert_block(full_message) { actual.equal?(expected) }
++      end
++
++      ##
++      # Compares the +object1+ with +object2+ using +operator+.
++      #
++      # Passes if object1.__send__(operator, object2) is true.
++      #
++      # Example:
++      #   assert_operator 5, :>=, 4
++
++      public
++      def assert_operator(object1, operator, object2, message="")
++        _wrap_assertion do
++          full_message = build_message(nil, "<?>\ngiven as the operator for #assert_operator must be a Symbol or #respond_to\\?(:to_str).", operator)
++          assert_block(full_message){operator.kind_of?(Symbol) || operator.respond_to?(:to_str)}
++          full_message = build_message(message, <<EOT, object1, AssertionMessage.literal(operator), object2)
++<?> expected to be
++?
++<?>.
++EOT
++          assert_block(full_message) { object1.__send__(operator, object2) }
++        end
++      end
++
++      ##
++      # Passes if block does not raise an exception.
++      #
++      # Example:
++      #   assert_nothing_raised do
++      #     [1, 2].uniq
++      #   end
++
++      public
++      def assert_nothing_raised(*args)
++        _wrap_assertion do
++          if Module === args.last
++            message = ""
++          else
++            message = args.pop
++          end
++          exceptions, modules = _check_exception_class(args)
++          begin
++            yield
++          rescue Exception => e
++            if ((args.empty? && !e.instance_of?(AssertionFailedError)) ||
++                _expected_exception?(e, exceptions, modules))
++              assert_block(build_message(message, "Exception raised:\n?", e)){false}
++            else
++              raise
++            end
++          end
++          nil
++        end
++      end
++
++      ##
++      # Flunk always fails.
++      #
++      # Example:
++      #   flunk 'Not done testing yet.'
++
++      public
++      def flunk(message="Flunked")
++        assert_block(build_message(message)){false}
++      end
++
++      ##
++      # Passes if ! +actual+ .equal? +expected+
++      #
++      # Example:
++      #   assert_not_same Object.new, Object.new
++
++      public
++      def assert_not_same(expected, actual, message="")
++        full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
++<?>
++with id <?> expected to not be equal\\? to
++<?>
++with id <?>.
++EOT
++        assert_block(full_message) { !actual.equal?(expected) }
++      end
++
++      ##
++      # Passes if +expected+ != +actual+
++      #
++      # Example:
++      #   assert_not_equal 'some string', 5
++
++      public
++      def assert_not_equal(expected, actual, message="")
++        full_message = build_message(message, "<?> expected to be != to\n<?>.", expected, actual)
++        assert_block(full_message) { expected != actual }
++      end
++
++      ##
++      # Passes if ! +object+ .nil?
++      #
++      # Example:
++      #   assert_not_nil '1 two 3'.sub!(/two/, '2')
++
++      public
++      def assert_not_nil(object, message="")
++        full_message = build_message(message, "<?> expected to not be nil.", object)
++        assert_block(full_message){!object.nil?}
++      end
++
++      ##
++      # Passes if +regexp+ !~ +string+ 
++      #
++      # Example:
++      #   assert_no_match(/two/, 'one 2 three')
++
++      public
++      def assert_no_match(regexp, string, message="")
++        _wrap_assertion do
++          assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
++          full_message = build_message(message, "<?> expected to not match\n<?>.", regexp, string)
++          assert_block(full_message) { regexp !~ string }
++        end
++      end
++
++      UncaughtThrow = {NameError => /^uncaught throw \`(.+)\'$/,
++                       ThreadError => /^uncaught throw \`(.+)\' in thread /} #`
++
++      ##
++      # Passes if the block throws +expected_symbol+
++      #
++      # Example:
++      #   assert_throws :done do
++      #     throw :done
++      #   end
++
++      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("Should have passed a block to assert_throws."){block_given?}
++          caught = true
++          begin
++            catch(expected_symbol) do
++              proc.call
++              caught = false
++            end
++            full_message = build_message(message, "<?> should have been thrown.", expected_symbol)
++            assert_block(full_message){caught}
++          rescue NameError, ThreadError => error
++            if UncaughtThrow[error.class] !~ error.message
++              raise error
++            end
++            full_message = build_message(message, "<?> expected to be thrown but\n<?> was thrown.", expected_symbol, $1.intern)
++            flunk(full_message)
++          end
++        end
++      end
++
++      ##
++      # Passes if block does not throw anything.
++      #
++      # Example:
++      #  assert_nothing_thrown do
++      #    [1, 2].uniq
++      #  end
++
++      public
++      def assert_nothing_thrown(message="", &proc)
++        _wrap_assertion do
++          assert(block_given?, "Should have passed a block to assert_nothing_thrown")
++          begin
++            proc.call
++          rescue NameError, ThreadError => error
++            if UncaughtThrow[error.class] !~ error.message
++              raise error
++            end
++            full_message = build_message(message, "<?> was thrown when nothing was expected", $1.intern)
++            flunk(full_message)
++          end
++          assert(true, "Expected nothing to be thrown")
++        end
++      end
++
++      ##
++      # Passes if +expected_float+ and +actual_float+ are equal
++      # within +delta+ tolerance.
++      #
++      # Example:
++      #   assert_in_delta 0.05, (50000.0 / 10**6), 0.00001
++
++      public
++      def assert_in_delta(expected_float, actual_float, delta, message="")
++        _wrap_assertion do
++          {expected_float => "first float", actual_float => "second float", delta => "delta"}.each do |float, name|
++            assert_respond_to(float, :to_f, "The arguments must respond to to_f; the #{name} did not")
++          end
++          assert_operator(delta, :>=, 0.0, "The delta should not be negative")
++          full_message = build_message(message, <<EOT, expected_float, actual_float, delta)
++<?> and
++<?> expected to be within
++<?> of each other.
++EOT
++          assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f }
++        end
++      end
++
++      ##
++      # Passes if the method send returns a true value.
++      #
++      # +send_array+ is composed of:
++      # * A receiver
++      # * A method
++      # * Arguments to the method
++      #
++      # Example:
++      #   assert_send [[1, 2], :include?, 4]
++
++      public
++      def assert_send(send_array, message="")
++        _wrap_assertion do
++          assert_instance_of(Array, send_array, "assert_send requires an array of send information")
++          assert(send_array.size >= 2, "assert_send requires at least a receiver and a message name")
++          full_message = build_message(message, <<EOT, send_array[0], AssertionMessage.literal(send_array[1].to_s), send_array[2..-1])
++<?> expected to respond to
++<?(?)> with a true value.
++EOT
++          assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) }
++        end
++      end
++
++      ##
++      # Builds a failure message.  +head+ is added before the +template+ and
++      # +arguments+ replaces the '?'s positionally in the template.
++
++      public
++      def build_message(head, template=nil, *arguments)
++        template &&= template.chomp
++        return AssertionMessage.new(head, template, arguments)
++      end
++
++      private
++      def _wrap_assertion
++        @_assertion_wrapped ||= false
++        unless (@_assertion_wrapped)
++          @_assertion_wrapped = true
++          begin
++            add_assertion
++            return yield
++          ensure
++            @_assertion_wrapped = false
++          end
++        else
++          return yield
++        end
++      end
++      
++      ##
++      # Called whenever an assertion is made.  Define this in classes that
++      # include Test::Unit::Assertions to record assertion counts.
++
++      private
++      def add_assertion
++      end
++
++      ##
++      # Select whether or not to use the pretty-printer. If this option is set
++      # to false before any assertions are made, pp.rb will not be required.
++
++      public
++      def self.use_pp=(value)
++        AssertionMessage.use_pp = value
++      end
++      
++      # :stopdoc:
++
++      class AssertionMessage
++        @use_pp = true
++        class << self
++          attr_accessor :use_pp
++        end
++
++        class Literal
++          def initialize(value)
++            @value = value
++          end
++          
++          def inspect
++            @value.to_s
++          end
++        end
++
++        class Template
++          def self.create(string)
++            parts = (string ? string.scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/m) : [])
++            self.new(parts)
++          end
++
++          attr_reader :count
++
++          def initialize(parts)
++            @parts = parts
++            @count = parts.find_all{|e| e == '?'}.size
++          end
++
++          def result(parameters)
++            raise "The number of parameters does not match the number of substitutions." if(parameters.size != count)
++            params = parameters.dup
++            @parts.collect{|e| e == '?' ? params.shift : e.gsub(/\\\?/m, '?')}.join('')
++          end
++        end
++
++        def self.literal(value)
++          Literal.new(value)
++        end
++
++        include Util::BacktraceFilter
++
++        def initialize(head, template_string, parameters)
++          @head = head
++          @template_string = template_string
++          @parameters = parameters
++        end
++
++        def convert(object)
++          case object
++            when Exception
++              <<EOM.chop
++Class: <#{convert(object.class)}>
++Message: <#{convert(object.message)}>
++---Backtrace---
++#{filter_backtrace(object.backtrace).join("\n")}
++---------------
++EOM
++            else
++              if(self.class.use_pp)
++                begin
++                  require 'pp'
++                rescue LoadError
++                  self.class.use_pp = false
++                  return object.inspect
++                end unless(defined?(PP))
++                PP.pp(object, '').chomp
++              else
++                object.inspect
++              end
++          end
++        end
++
++        def template
++          @template ||= Template.create(@template_string)
++        end
++
++        def add_period(string)
++          (string =~ /\.\Z/ ? string : string + '.')
++        end
++
++        def to_s
++          message_parts = []
++          if (@head)
++            head = @head.to_s 
++            unless(head.empty?)
++              message_parts << add_period(head)
++            end
++          end
++          tail = template.result(@parameters.collect{|e| convert(e)})
++          message_parts << tail unless(tail.empty?)
++          message_parts.join("\n")
++        end
++      end
++
++      # :startdoc:
++
++    end
++  end
++end
+diff --git a/test/lib/test/unit/autorunner.rb b/test/lib/test/unit/autorunner.rb
+new file mode 100644
+index 0000000..0dfc01c
+--- /dev/null
++++ b/test/lib/test/unit/autorunner.rb
+@@ -0,0 +1,219 @@
++require 'test/unit/ui/testrunnerutilities'
++require 'optparse'
++
++module Test
++  module Unit
++    class AutoRunner
++      def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block)
++        r = new(force_standalone || standalone?, &block)
++        r.base = default_dir
++        r.process_args(argv)
++        r.run
++      end
++      
++      def self.standalone?
++        return false unless("-e" == $0)
++        ObjectSpace.each_object(Class) do |klass|
++          return false if(klass < TestCase)
++        end
++        true
++      end
++
++      RUNNERS = {
++        :console => proc do |r|
++          require 'test/unit/ui/console/testrunner'
++          Test::Unit::UI::Console::TestRunner
++        end,
++        :gtk => proc do |r|
++          require 'test/unit/ui/gtk/testrunner'
++          Test::Unit::UI::GTK::TestRunner
++        end,
++        :gtk2 => proc do |r|
++          require 'test/unit/ui/gtk2/testrunner'
++          Test::Unit::UI::GTK2::TestRunner
++        end,
++        :fox => proc do |r|
++          require 'test/unit/ui/fox/testrunner'
++          Test::Unit::UI::Fox::TestRunner
++        end,
++        :tk => proc do |r|
++          require 'test/unit/ui/tk/testrunner'
++          Test::Unit::UI::Tk::TestRunner
++        end,
++      }
++
++      OUTPUT_LEVELS = [
++        [:silent, UI::SILENT],
++        [:progress, UI::PROGRESS_ONLY],
++        [:normal, UI::NORMAL],
++        [:verbose, UI::VERBOSE],
++      ]
++
++      COLLECTORS = {
++        :objectspace => proc do |r|
++          require 'test/unit/collector/objectspace'
++          c = Collector::ObjectSpace.new
++          c.filter = r.filters
++          c.collect($0.sub(/\.rb\Z/, ''))
++        end,
++        :dir => proc do |r|
++          require 'test/unit/collector/dir'
++          c = Collector::Dir.new
++          c.filter = r.filters
++          c.pattern.concat(r.pattern) if(r.pattern)
++          c.exclude.concat(r.exclude) if(r.exclude)
++          c.base = r.base
++          $:.push(r.base) if r.base
++          c.collect(*(r.to_run.empty? ? ['.'] : r.to_run))
++        end,
++      }
++
++      attr_reader :suite
++      attr_accessor :output_level, :filters, :to_run, :pattern, :exclude, :base, :workdir
++      attr_writer :runner, :collector
++
++      def initialize(standalone)
++        Unit.run = true
++        @standalone = standalone
++        @runner = RUNNERS[:console]
++        @collector = COLLECTORS[(standalone ? :dir : :objectspace)]
++        @filters = []
++        @to_run = []
++        @output_level = UI::NORMAL
++        @workdir = nil
++        yield(self) if(block_given?)
++      end
++
++      def process_args(args = ARGV)
++        begin
++          options.order!(args) {|arg| @to_run << arg}
++        rescue OptionParser::ParseError => e
++          puts e
++          puts options
++          $! = nil
++          abort
++        else
++          @filters << proc{false} unless(@filters.empty?)
++        end
++        not @to_run.empty?
++      end
++
++      def options
++        @options ||= OptionParser.new do |o|
++          o.banner = "Test::Unit automatic runner."
++          o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
++
++          o.on
++          o.on('-r', '--runner=RUNNER', RUNNERS,
++               "Use the given RUNNER.",
++               "(" + keyword_display(RUNNERS) + ")") do |r|
++            @runner = r
++          end
++
++          if(@standalone)
++            o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b|
++              @base = b
++            end
++
++            o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w|
++              @workdir = w
++            end
++
++            o.on('-a', '--add=TORUN', Array,
++                 "Add TORUN to the list of things to run;",
++                 "can be a file or a directory.") do |a|
++              @to_run.concat(a)
++            end
++
++            @pattern = []
++            o.on('-p', '--pattern=PATTERN', Regexp,
++                 "Match files to collect against PATTERN.") do |e|
++              @pattern << e
++            end
++
++            @exclude = []
++            o.on('-x', '--exclude=PATTERN', Regexp,
++                 "Ignore files to collect against PATTERN.") do |e|
++              @exclude << e
++            end
++          end
++
++          o.on('-n', '--name=NAME', String,
++               "Runs tests matching NAME.",
++               "(patterns may be used).") do |n|
++            n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
++            case n
++            when Regexp
++              @filters << proc{|t| n =~ t.method_name ? true : nil}
++            else
++              @filters << proc{|t| n == t.method_name ? true : nil}
++            end
++          end
++
++          o.on('-t', '--testcase=TESTCASE', String,
++               "Runs tests in TestCases matching TESTCASE.",
++               "(patterns may be used).") do |n|
++            n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
++            case n
++            when Regexp
++              @filters << proc{|t| n =~ t.class.name ? true : nil}
++            else
++              @filters << proc{|t| n == t.class.name ? true : nil}
++            end
++          end
++
++          o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]",
++               "Appends directory list to $LOAD_PATH.") do |dirs|
++            $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR))
++          end
++
++          o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS,
++               "Set the output level (default is verbose).",
++               "(" + keyword_display(OUTPUT_LEVELS) + ")") do |l|
++            @output_level = l || UI::VERBOSE
++          end
++
++          o.on('--',
++               "Stop processing options so that the",
++               "remaining options will be passed to the",
++               "test."){o.terminate}
++
++          o.on('-h', '--help', 'Display this help.'){puts o; exit}
++
++          o.on_tail
++          o.on_tail('Deprecated options:')
++
++          o.on_tail('--console', 'Console runner (use --runner).') do
++            warn("Deprecated option (--console).")
++            @runner = RUNNERS[:console]
++          end
++
++          o.on_tail('--gtk', 'GTK runner (use --runner).') do
++            warn("Deprecated option (--gtk).")
++            @runner = RUNNERS[:gtk]
++          end
++
++          o.on_tail('--fox', 'Fox runner (use --runner).') do
++            warn("Deprecated option (--fox).")
++            @runner = RUNNERS[:fox]
++          end
++
++          o.on_tail
++        end
++      end
++
++      def keyword_display(array)
++        list = array.collect {|e, *| e.to_s}
++        Array === array or list.sort!
++        list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ")
++      end
++
++      def run
++        @suite = @collector[self]
++        result = @runner[self] or return false
++        Dir.chdir(@workdir) if @workdir
++        result.run(@suite, @output_level).passed?
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/collector.rb b/test/lib/test/unit/collector.rb
+new file mode 100644
+index 0000000..9e9e654
+--- /dev/null
++++ b/test/lib/test/unit/collector.rb
+@@ -0,0 +1,43 @@
++module Test
++  module Unit
++    module Collector
++      def initialize
++        @filters = []
++      end
++
++      def filter=(filters)
++        @filters = case(filters)
++          when Proc
++            [filters]
++          when Array
++            filters
++        end
++      end
++
++      def add_suite(destination, suite)
++        to_delete = suite.tests.find_all{|t| !include?(t)}
++        to_delete.each{|t| suite.delete(t)}
++        destination << suite unless(suite.size == 0)
++      end
++
++      def include?(test)
++        return true if(@filters.empty?)
++        @filters.each do |filter|
++          result = filter[test]
++          if(result.nil?)
++            next
++          elsif(!result)
++            return false
++          else
++            return true
++          end
++        end
++        true
++      end
++
++      def sort(suites)
++        suites.sort_by{|s| s.name}
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/collector/dir.rb b/test/lib/test/unit/collector/dir.rb
+new file mode 100644
+index 0000000..97c8d28
+--- /dev/null
++++ b/test/lib/test/unit/collector/dir.rb
+@@ -0,0 +1,107 @@
++require 'test/unit/testsuite'
++require 'test/unit/collector'
++
++module Test
++  module Unit
++    module Collector
++      class Dir
++        include Collector
++
++        attr_reader :pattern, :exclude
++        attr_accessor :base
++
++        def initialize(dir=::Dir, file=::File, object_space=::ObjectSpace, req=nil)
++          super()
++          @dir = dir
++          @file = file
++          @object_space = object_space
++          @req = req
++          @pattern = [/\btest_.*\.rb\Z/m]
++          @exclude = []
++        end
++
++        def collect(*from)
++          basedir = @base
++          $:.push(basedir) if basedir
++          if(from.empty?)
++            recursive_collect('.', find_test_cases)
++          elsif(from.size == 1)
++            recursive_collect(from.first, find_test_cases)
++          else
++            suites = []
++            from.each do |f|
++              suite = recursive_collect(f, find_test_cases)
++              suites << suite unless(suite.tests.empty?)
++            end
++            suite = TestSuite.new("[#{from.join(', ')}]")
++            sort(suites).each{|s| suite << s}
++            suite
++          end
++        ensure
++          $:.delete_at($:.rindex(basedir)) if basedir
++        end
++
++        def find_test_cases(ignore=[])
++          cases = []
++          @object_space.each_object(Class) do |c|
++            cases << c if(c < TestCase && !ignore.include?(c))
++          end
++          ignore.concat(cases)
++          cases
++        end
++
++        def recursive_collect(name, already_gathered)
++          sub_suites = []
++          path = realdir(name)
++          if @file.directory?(path)
++	    dir_name = name unless name == '.'
++            @dir.entries(path).each do |e|
++              next if(e == '.' || e == '..')
++              e_name = dir_name ? @file.join(dir_name, e) : e
++              if @file.directory?(realdir(e_name))
++                next if /\ACVS\z/ =~ e
++                sub_suite = recursive_collect(e_name, already_gathered)
++                sub_suites << sub_suite unless(sub_suite.empty?)
++              else
++                next if /~\z/ =~ e_name or /\A\.\#/ =~ e
++                if @pattern and !@pattern.empty?
++                  next unless @pattern.any? {|pat| pat =~ e_name}
++                end
++                if @exclude and !@exclude.empty?
++                  next if @exclude.any? {|pat| pat =~ e_name}
++                end
++                collect_file(e_name, sub_suites, already_gathered)
++              end
++            end
++          else
++            collect_file(name, sub_suites, already_gathered)
++          end
++          suite = TestSuite.new(@file.basename(name))
++          sort(sub_suites).each{|s| suite << s}
++          suite
++        end
++
++        def collect_file(name, suites, already_gathered)
++          dir = @file.dirname(@file.expand_path(name, @base))
++          $:.unshift(dir)
++          if(@req)
++            @req.require(name)
++          else
++            require(name)
++          end
++          find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)}
++        ensure
++          $:.delete_at($:.rindex(dir)) if(dir)
++        end
++
++	def realdir(path)
++	  if @base
++	    @file.join(@base, path)
++	  else
++	    path
++	  end
++	end
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/error.rb b/test/lib/test/unit/error.rb
+new file mode 100644
+index 0000000..43a813f
+--- /dev/null
++++ b/test/lib/test/unit/error.rb
+@@ -0,0 +1,56 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
++# License:: Ruby license.
++
++require 'test/unit/util/backtracefilter'
++
++module Test
++  module Unit
++
++    # Encapsulates an error in a test. Created by
++    # Test::Unit::TestCase when it rescues an exception thrown
++    # during the processing of a test.
++    class Error
++      include Util::BacktraceFilter
++
++      attr_reader(:test_name, :exception)
++
++      SINGLE_CHARACTER = 'E'
++
++      # Creates a new Error with the given test_name and
++      # exception.
++      def initialize(test_name, exception)
++        @test_name = test_name
++        @exception = exception
++      end
++
++      # Returns a single character representation of an error.
++      def single_character_display
++        SINGLE_CHARACTER
++      end
++
++      # Returns the message associated with the error.
++      def message
++        "#{@exception.class.name}: #{@exception.message}"
++      end
++
++      # Returns a brief version of the error description.
++      def short_display
++        "#@test_name: #{message.split("\n")[0]}"
++      end
++
++      # Returns a verbose version of the error description.
++      def long_display
++        backtrace = filter_backtrace(@exception.backtrace).join("\n    ")
++        "Error:\n#@test_name:\n#{message}\n    #{backtrace}"
++      end
++
++      # Overridden to return long_display.
++      def to_s
++        long_display
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/failure.rb b/test/lib/test/unit/failure.rb
+new file mode 100644
+index 0000000..832c998
+--- /dev/null
++++ b/test/lib/test/unit/failure.rb
+@@ -0,0 +1,51 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
++# License:: Ruby license.
++
++module Test
++  module Unit
++
++    # Encapsulates a test failure. Created by Test::Unit::TestCase
++    # when an assertion fails.
++    class Failure
++      attr_reader :test_name, :location, :message
++      
++      SINGLE_CHARACTER = 'F'
++
++      # Creates a new Failure with the given location and
++      # message.
++      def initialize(test_name, location, message)
++        @test_name = test_name
++        @location = location
++        @message = message
++      end
++      
++      # Returns a single character representation of a failure.
++      def single_character_display
++        SINGLE_CHARACTER
++      end
++
++      # Returns a brief version of the error description.
++      def short_display
++        "#@test_name: #{@message.split("\n")[0]}"
++      end
++
++      # Returns a verbose version of the error description.
++      def long_display
++        location_display = if(location.size == 1)
++          location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
++        else
++          "\n    [#{location.join("\n     ")}]"
++        end
++        "Failure:\n#@test_name#{location_display}:\n#@message"
++      end
++
++      # Overridden to return long_display.
++      def to_s
++        long_display
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/testcase.rb b/test/lib/test/unit/testcase.rb
+new file mode 100644
+index 0000000..f53b460
+--- /dev/null
++++ b/test/lib/test/unit/testcase.rb
+@@ -0,0 +1,160 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2003 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'
++require 'test/unit/util/backtracefilter'
++
++module Test
++  module Unit
++
++    # Ties everything together. If you subclass and add your own
++    # test methods, it takes care of making them into tests and
++    # wrapping those tests into a suite. It also does the
++    # nitty-gritty of actually running an individual test and
++    # collecting its results into a Test::Unit::TestResult object.
++    class TestCase
++      include Assertions
++      include Util::BacktraceFilter
++      
++      attr_reader :method_name
++      
++      STARTED = name + "::STARTED"
++      FINISHED = name + "::FINISHED"
++
++      ##
++      # These exceptions are not caught by #run.
++
++      PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt,
++                                SystemExit]
++
++      # Creates a new instance of the fixture for running the
++      # test represented by test_method_name.
++      def initialize(test_method_name)
++        unless(respond_to?(test_method_name) and
++               (method(test_method_name).arity == 0 ||
++                method(test_method_name).arity == -1))
++          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.sort.each do
++          |test|
++          catch(:invalid_test) do
++            suite << new(test)
++          end
++        end
++        if (suite.empty?)
++          catch(:invalid_test) do
++            suite << new("default_test")
++          end
++        end
++        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 Exception
++          raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
++          add_error($!)
++        ensure
++          begin
++            teardown
++          rescue AssertionFailedError => e
++            add_failure(e.message, e.backtrace)
++          rescue Exception
++            raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
++            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
++        1
++      end
++
++      def add_assertion
++        @_result.add_assertion
++      end
++      private :add_assertion
++
++      def add_failure(message, all_locations=caller())
++        @test_passed = false
++        @_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message))
++      end
++      private :add_failure
++
++      def add_error(exception)
++        @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
++
++      # Overridden to return #name.
++      def to_s
++        name
++      end
++      
++      # It's handy to be able to compare TestCase instances.
++      def ==(other)
++        return false unless(other.kind_of?(self.class))
++        return false unless(@method_name == other.method_name)
++        self.class == other.class
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/testresult.rb b/test/lib/test/unit/testresult.rb
+new file mode 100644
+index 0000000..e3a472e
+--- /dev/null
++++ b/test/lib/test/unit/testresult.rb
+@@ -0,0 +1,80 @@
++#--
++# 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
+diff --git a/test/lib/test/unit/testsuite.rb b/test/lib/test/unit/testsuite.rb
+new file mode 100644
+index 0000000..6fea976
+--- /dev/null
++++ b/test/lib/test/unit/testsuite.rb
+@@ -0,0 +1,76 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2003 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.each do |test|
++          test.run(result, &progress_block)
++        end
++        yield(FINISHED, name)
++      end
++
++      # Adds the test to the suite.
++      def <<(test)
++        @tests << test
++        self
++      end
++
++      def delete(test)
++        @tests.delete(test)
++      end
++
++      # Retuns the rolled up number of tests in this suite;
++      # i.e. if the suite contains other suites, it counts the
++      # tests within those suites, not the suites themselves.
++      def size
++        total_size = 0
++        @tests.each { |test| total_size += test.size }
++        total_size
++      end
++      
++      def empty?
++        tests.empty?
++      end
++
++      # Overridden to return the name given the suite at
++      # creation.
++      def to_s
++        @name
++      end
++      
++      # It's handy to be able to compare TestSuite instances.
++      def ==(other)
++        return false unless(other.kind_of?(self.class))
++        return false unless(@name == other.name)
++        @tests == other.tests
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/ui/console/testrunner.rb b/test/lib/test/unit/ui/console/testrunner.rb
+new file mode 100644
+index 0000000..6b600e3
+--- /dev/null
++++ b/test/lib/test/unit/ui/console/testrunner.rb
+@@ -0,0 +1,127 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2003 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
++
++        # Runs a Test::Unit::TestSuite on the console.
++        class TestRunner
++          extend TestRunnerUtilities
++
++          # 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
++          # STDOUT.
++          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
++            @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)
++            return TestRunnerMediator.new(suite)
++          end
++          
++          def attach_to_mediator
++            @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
++            return @mediator.run_suite
++          end
++          
++          def add_fault(fault)
++            @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))
++            @io.flush
++          end
++          
++          def output_single(something, level=NORMAL)
++            @io.write(something) if (output?(level))
++            @io.flush
++          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
+diff --git a/test/lib/test/unit/ui/testrunnermediator.rb b/test/lib/test/unit/ui/testrunnermediator.rb
+new file mode 100644
+index 0000000..d34510d
+--- /dev/null
++++ b/test/lib/test/unit/ui/testrunnermediator.rb
+@@ -0,0 +1,68 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
++# License:: Ruby license.
++
++require 'test/unit'
++require 'test/unit/util/observable'
++require 'test/unit/testresult'
++
++module Test
++  module Unit
++    module UI
++
++      # 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
++        
++        # 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
++          Unit.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
+diff --git a/test/lib/test/unit/ui/testrunnerutilities.rb b/test/lib/test/unit/ui/testrunnerutilities.rb
+new file mode 100644
+index 0000000..70b885b
+--- /dev/null
++++ b/test/lib/test/unit/ui/testrunnerutilities.rb
+@@ -0,0 +1,46 @@
++#--
++#
++# Author:: Nathaniel Talbott.
++# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
++# License:: Ruby license.
++
++module Test
++  module Unit
++    module UI
++
++      SILENT = 0
++      PROGRESS_ONLY = 1
++      NORMAL = 2
++      VERBOSE = 3
++
++      # 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
++
++        # Creates a new TestRunner and runs the suite.
++        def run(suite, output_level=NORMAL)
++          return new(suite, output_level).start
++        end
++
++        # 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
+diff --git a/test/lib/test/unit/util/backtracefilter.rb b/test/lib/test/unit/util/backtracefilter.rb
+new file mode 100644
+index 0000000..7ebec2d
+--- /dev/null
++++ b/test/lib/test/unit/util/backtracefilter.rb
+@@ -0,0 +1,40 @@
++module Test
++  module Unit
++    module Util
++      module BacktraceFilter
++        TESTUNIT_FILE_SEPARATORS = %r{[\\/:]}
++        TESTUNIT_PREFIX = __FILE__.split(TESTUNIT_FILE_SEPARATORS)[0..-3]
++        TESTUNIT_RB_FILE = /\.rb\Z/
++        
++        def filter_backtrace(backtrace, prefix=nil)
++          return ["No backtrace"] unless(backtrace)
++          split_p = if(prefix)
++            prefix.split(TESTUNIT_FILE_SEPARATORS)
++          else
++            TESTUNIT_PREFIX
++          end
++          match = proc do |e|
++            split_e = e.split(TESTUNIT_FILE_SEPARATORS)[0, split_p.size]
++            next false unless(split_e[0..-2] == split_p[0..-2])
++            split_e[-1].sub(TESTUNIT_RB_FILE, '') == split_p[-1]
++          end
++          return backtrace unless(backtrace.detect(&match))
++          found_prefix = false
++          new_backtrace = backtrace.reverse.reject do |e|
++            if(match[e])
++              found_prefix = true
++              true
++            elsif(found_prefix)
++              false
++            else
++              true
++            end
++          end.reverse
++          new_backtrace = (new_backtrace.empty? ? backtrace : new_backtrace)
++          new_backtrace = new_backtrace.reject(&match)
++          new_backtrace.empty? ? backtrace : new_backtrace
++        end
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/util/observable.rb b/test/lib/test/unit/util/observable.rb
+new file mode 100644
+index 0000000..3567d34
+--- /dev/null
++++ b/test/lib/test/unit/util/observable.rb
+@@ -0,0 +1,90 @@
++#--
++#
++# 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
++
++      # 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
++          @channels ||= {}
++          return @channels
++        end
++      end
++    end
++  end
++end
+diff --git a/test/lib/test/unit/util/procwrapper.rb b/test/lib/test/unit/util/procwrapper.rb
+new file mode 100644
+index 0000000..ad72521
+--- /dev/null
++++ b/test/lib/test/unit/util/procwrapper.rb
+@@ -0,0 +1,48 @@
++#--
++#
++# 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
++          return @hash
++        end
++
++        def ==(other)
++          case(other)
++            when ProcWrapper
++              return @a_proc == other.to_proc
++            else
++              return super
++          end
++        end
++        alias :eql? :==
++
++        def to_proc
++          return @a_proc
++        end
++      end
++    end
++  end
++end
+diff --git a/test/unit/comment_filter.rb b/test/unit/comment_filter.rb
+new file mode 100644
+index 0000000..e255d07
+--- /dev/null
++++ b/test/unit/comment_filter.rb
+@@ -0,0 +1,53 @@
++require 'test/unit'
++require 'coderay'
++
++class CommentFilterTest < Test::Unit::TestCase
++  
++  def test_filtering_comments
++    tokens = CodeRay.scan <<-RUBY, :ruby
++#!/usr/bin/env ruby
++# a minimal Ruby program
++puts "Hello world!"
++    RUBY
++    assert_equal <<-RUBY_FILTERED, tokens.comment_filter.text
++#!/usr/bin/env ruby
++
++puts "Hello world!"
++    RUBY_FILTERED
++  end
++  
++  def test_filtering_docstrings
++    tokens = CodeRay.scan <<-PYTHON, :python
++'''
++Assuming this is file mymodule.py then this string, being the
++first statement in the file will become the mymodule modules
++docstring when the file is imported
++'''
++
++class Myclass():
++    """
++    The class's docstring
++    """
++
++    def mymethod(self):
++        '''The method's docstring'''
++
++def myfunction():
++    """The function's docstring"""
++    PYTHON
++    assert_equal <<-PYTHON_FILTERED.chomp, tokens.comment_filter.text
++
++
++class Myclass():
++    
++
++    def mymethod(self):
++        
++
++def myfunction():
++    
++
++PYTHON_FILTERED
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/count.rb b/test/unit/count.rb
+new file mode 100644
+index 0000000..448e8f1
+--- /dev/null
++++ b/test/unit/count.rb
+@@ -0,0 +1,15 @@
++require 'test/unit'
++require 'coderay'
++
++class CountTest < Test::Unit::TestCase
++  
++  def test_count
++    tokens = CodeRay.scan <<-RUBY.strip, :ruby
++#!/usr/bin/env ruby
++# a minimal Ruby program
++puts "Hello world!"
++    RUBY
++    assert_equal 11, tokens.encode(:count)
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/debug.rb b/test/unit/debug.rb
+new file mode 100644
+index 0000000..88baf56
+--- /dev/null
++++ b/test/unit/debug.rb
+@@ -0,0 +1,77 @@
++require 'test/unit'
++require 'coderay'
++
++class DebugEncoderTest < Test::Unit::TestCase
++  
++  def test_creation
++    debug = nil
++    assert_nothing_raised do
++      debug = CodeRay.encoder :debug
++    end
++    assert CodeRay::Encoders::Debug < CodeRay::Encoders::Encoder
++    assert_kind_of CodeRay::Encoders::Encoder, debug
++  end
++  
++  TEST_INPUT = CodeRay::Tokens[
++    ['10', :integer],
++    ['(\\)', :operator],
++    [:begin_group, :string],
++    ['test', :content],
++    [:end_group, :string],
++    [:begin_line, :head],
++    ["\n", :space],
++    ["\n  \t", :space],
++    ["   \n", :space],
++    ["[]", :method],
++    [:end_line, :head],
++  ].flatten
++  TEST_OUTPUT = <<-'DEBUG'.chomp
++integer(10)operator((\\\))string<content(test)>head[
++
++  	   
++method([])]
++  DEBUG
++  
++  def test_filtering_text_tokens
++    assert_equal TEST_OUTPUT, CodeRay::Encoders::Debug.new.encode_tokens(TEST_INPUT)
++    assert_equal TEST_OUTPUT, TEST_INPUT.debug
++  end
++  
++end
++
++class DebugScannerTest < Test::Unit::TestCase
++  
++  def test_creation
++    assert CodeRay::Scanners::Debug < CodeRay::Scanners::Scanner
++    debug = nil
++    assert_nothing_raised do
++      debug = CodeRay.scanner :debug
++    end
++    assert_kind_of CodeRay::Scanners::Scanner, debug
++  end
++  
++  TEST_INPUT = <<-'DEBUG'.chomp
++integer(10)operator((\\\))string<content(test)>test[
++
++  	   
++method([])]
++  DEBUG
++  TEST_OUTPUT = CodeRay::Tokens[
++    ['10', :integer],
++    ['(\\)', :operator],
++    [:begin_group, :string],
++    ['test', :content],
++    [:end_group, :string],
++    [:begin_line, :unknown],
++    ["\n\n  \t   \n", :space],
++    ["[]", :method],
++    [:end_line, :unknown],
++  ].flatten
++  
++  def test_filtering_text_tokens
++    assert_equal TEST_OUTPUT, CodeRay::Scanners::Debug.new.tokenize(TEST_INPUT)
++    assert_kind_of CodeRay::TokensProxy, CodeRay.scan(TEST_INPUT, :debug)
++    assert_equal TEST_OUTPUT, CodeRay.scan(TEST_INPUT, :debug).tokens
++  end
++  
++end
+diff --git a/test/unit/duo.rb b/test/unit/duo.rb
+new file mode 100644
+index 0000000..05c26a5
+--- /dev/null
++++ b/test/unit/duo.rb
+@@ -0,0 +1,35 @@
++require 'test/unit'
++require 'yaml'
++require 'coderay'
++
++class DuoTest < Test::Unit::TestCase
++  
++  def test_two_arguments
++    duo = CodeRay::Duo[:ruby, :html]
++    assert_kind_of CodeRay::Scanners[:ruby], duo.scanner
++    assert_kind_of CodeRay::Encoders[:html], duo.encoder
++  end
++  
++  def test_two_hash
++    duo = CodeRay::Duo[:ruby => :html]
++    assert_kind_of CodeRay::Scanners[:ruby], duo.scanner
++    assert_kind_of CodeRay::Encoders[:html], duo.encoder
++  end
++  
++  def test_call
++    duo = CodeRay::Duo[:python => :yml]
++    yaml = [["def", :keyword],
++            [" ", :space],
++            ["test", :method],
++            [":", :operator],
++            [" ", :space],
++            [:begin_group, :string],
++            ["\"", :delimiter],
++            ["pass", :content],
++            ["\"", :delimiter],
++            [:end_group, :string]]
++    
++    assert_equal yaml, YAML.load(duo.call('def test: "pass"'))
++  end
++  
++end
+diff --git a/test/unit/file_type.rb b/test/unit/file_type.rb
+new file mode 100644
+index 0000000..263517b
+--- /dev/null
++++ b/test/unit/file_type.rb
+@@ -0,0 +1,116 @@
++require 'test/unit'
++require File.expand_path('../../lib/assert_warning', __FILE__)
++
++require 'coderay/helpers/file_type'
++
++class FileTypeTests < Test::Unit::TestCase
++  
++  include CodeRay
++  
++  def test_fetch
++    assert_raise FileType::UnknownFileType do
++      FileType.fetch ''
++    end
++    
++    assert_throws :not_found do
++      FileType.fetch '.' do
++        throw :not_found
++      end
++    end
++    
++    assert_equal :default, FileType.fetch('c', :default)
++  end
++  
++  def test_block_supersedes_default_warning
++    assert_warning 'Block supersedes default value argument; use either.' do
++      FileType.fetch('c', :default) { }
++    end
++  end
++  
++  def test_ruby
++    assert_equal :ruby, FileType[__FILE__]
++    assert_equal :ruby, FileType['test.rb']
++    assert_equal :ruby, FileType['test.java.rb']
++    assert_equal :java, FileType['test.rb.java']
++    assert_equal :ruby, FileType['C:\\Program Files\\x\\y\\c\\test.rbw']
++    assert_equal :ruby, FileType['/usr/bin/something/Rakefile']
++    assert_equal :ruby, FileType['~/myapp/gem/Rantfile']
++    assert_equal :ruby, FileType['./lib/tasks\repository.rake']
++    assert_not_equal :ruby, FileType['test_rb']
++    assert_not_equal :ruby, FileType['Makefile']
++    assert_not_equal :ruby, FileType['set.rb/set']
++    assert_not_equal :ruby, FileType['~/projects/blabla/rb']
++  end
++  
++  def test_c
++    assert_equal :c, FileType['test.c']
++    assert_equal :c, FileType['C:\\Program Files\\x\\y\\c\\test.h']
++    assert_not_equal :c, FileType['test_c']
++    assert_not_equal :c, FileType['Makefile']
++    assert_not_equal :c, FileType['set.h/set']
++    assert_not_equal :c, FileType['~/projects/blabla/c']
++  end
++  
++  def test_cpp
++    assert_equal :cpp, FileType['test.c++']
++    assert_equal :cpp, FileType['test.cxx']
++    assert_equal :cpp, FileType['test.hh']
++    assert_equal :cpp, FileType['test.hpp']
++    assert_equal :cpp, FileType['test.cu']
++    assert_equal :cpp, FileType['test.C']
++    assert_not_equal :cpp, FileType['test.c']
++    assert_not_equal :cpp, FileType['test.h']
++  end
++  
++  def test_html
++    assert_equal :html, FileType['test.htm']
++    assert_equal :html, FileType['test.xhtml']
++    assert_equal :html, FileType['test.html.xhtml']
++    assert_equal :erb, FileType['_form.rhtml']
++    assert_equal :erb, FileType['_form.html.erb']
++  end
++  
++  def test_yaml
++    assert_equal :yaml, FileType['test.yml']
++    assert_equal :yaml, FileType['test.yaml']
++    assert_equal :yaml, FileType['my.html.yaml']
++    assert_not_equal :yaml, FileType['YAML']
++  end
++  
++  def test_pathname
++    require 'pathname'
++    pn = Pathname.new 'test.rb'
++    assert_equal :ruby, FileType[pn]
++    dir = Pathname.new '/etc/var/blubb'
++    assert_equal :ruby, FileType[dir + pn]
++    assert_equal :cpp, FileType[dir + 'test.cpp']
++  end
++  
++  def test_no_shebang
++    dir = './test'
++    if File.directory? dir
++      Dir.chdir dir do
++        assert_equal :c, FileType['test.c']
++      end
++    end
++  end
++  
++  def test_shebang_empty_file
++    require 'tmpdir'
++    tmpfile = File.join(Dir.tmpdir, 'bla')
++    File.open(tmpfile, 'w') { }  # touch
++    assert_equal nil, FileType[tmpfile, true]
++  end
++  
++  def test_shebang_no_file
++    assert_equal nil, FileType['i do not exist', true]
++  end
++  
++  def test_shebang
++    require 'tmpdir'
++    tmpfile = File.join(Dir.tmpdir, 'bla')
++    File.open(tmpfile, 'w') { |f| f.puts '#!/usr/bin/env ruby' }
++    assert_equal :ruby, FileType[tmpfile, true]
++  end
++  
++end
+diff --git a/test/unit/filter.rb b/test/unit/filter.rb
+new file mode 100644
+index 0000000..25dff77
+--- /dev/null
++++ b/test/unit/filter.rb
+@@ -0,0 +1,38 @@
++require 'test/unit'
++require 'coderay'
++
++class FilterTest < Test::Unit::TestCase
++  
++  def test_creation
++    filter = nil
++    assert_nothing_raised do
++      filter = CodeRay.encoder :filter
++    end
++    assert CodeRay::Encoders::Filter < CodeRay::Encoders::Encoder
++    assert_kind_of CodeRay::Encoders::Encoder, filter
++  end
++  
++  def test_filtering_text_tokens
++    tokens = CodeRay::Tokens.new
++    10.times do |i|
++      tokens.text_token i.to_s, :index
++    end
++    assert_equal tokens, CodeRay::Encoders::Filter.new.encode_tokens(tokens)
++    assert_equal tokens, tokens.filter
++  end
++  
++  def test_filtering_block_tokens
++    tokens = CodeRay::Tokens.new
++    10.times do |i|
++      tokens.begin_group :index
++      tokens.text_token i.to_s, :content
++      tokens.end_group :index
++      tokens.begin_line :index
++      tokens.text_token i.to_s, :content
++      tokens.end_line :index
++    end
++    assert_equal tokens, CodeRay::Encoders::Filter.new.encode_tokens(tokens)
++    assert_equal tokens, tokens.filter
++  end
++  
++end
+diff --git a/test/unit/html.rb b/test/unit/html.rb
+new file mode 100644
+index 0000000..0072635
+--- /dev/null
++++ b/test/unit/html.rb
+@@ -0,0 +1,103 @@
++require 'test/unit'
++require 'coderay'
++
++class HtmlTest < Test::Unit::TestCase
++  
++  def test_break_lines_option
++    snippets = {}
++    
++    snippets[:ruby] = {}
++    
++    snippets[:ruby][:in] = <<-RUBY
++ruby_inside = <<-RUBY_INSIDE
++This is tricky,
++isn't it?
++RUBY_INSIDE
++    RUBY
++    
++    snippets[:ruby][:expected_with_option_off] = <<-HTML_OPT_INDEPENDENT_LINES_OFF
++ruby_inside = <span class=\"string\"><span class=\"delimiter\"><<-RUBY_INSIDE</span></span><span class=\"string\"><span class=\"content\">
++This is tricky,
++isn't it?</span><span class=\"delimiter\">
++RUBY_INSIDE</span></span>
++    HTML_OPT_INDEPENDENT_LINES_OFF
++    
++    snippets[:ruby][:expected_with_option_on] = <<-HTML_OPT_INDEPENDENT_LINES_ON
++ruby_inside = <span class=\"string\"><span class=\"delimiter\"><<-RUBY_INSIDE</span></span><span class=\"string\"><span class=\"content\"></span></span>
++<span class=\"string\"><span class=\"content\">This is tricky,</span></span>
++<span class=\"string\"><span class=\"content\">isn't it?</span><span class=\"delimiter\"></span></span>
++<span class=\"string\"><span class=\"delimiter\">RUBY_INSIDE</span></span>
++    HTML_OPT_INDEPENDENT_LINES_ON
++    
++    snippets[:java] = {}
++    
++    snippets[:java][:in] = <<-JAVA
++import java.lang.*;
++
++/**
++ * This is some multiline javadoc
++ * used to test the
++ */
++public class Test {
++  public static final String MESSAGE = "My message\
++    To the world";
++
++  static void main() {
++    /*
++     * Another multiline
++     * comment
++     */
++    System.out.println(MESSAGE);
++  }
++}
++    JAVA
++    
++    snippets[:java][:expected_with_option_off] = <<-HTML_OPT_INDEPENDENT_LINES_OFF
++<span class=\"keyword\">import</span> <span class=\"include\">java.lang</span>.*;
++
++<span class=\"comment\">/**
++ * This is some multiline javadoc
++ * used to test the
++ */</span>
++<span class=\"directive\">public</span> <span class=\"type\">class</span> <span class=\"class\">Test</span> {
++  <span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"directive\">final</span> <span class=\"predefined-type\">String</span> MESSAGE = <span class=\"string\"><span class=\"delimiter\">"</span><span class=\"content\">My message    To the world</span><span class=\"delimiter\">"</span></span>;
++
++  <span class=\"directive\">static</span> <span class=\"type\">void</span> main() {
++    <span class=\"comment\">/*
++     * Another multiline
++     * comment
++     */</span>
++    <span class=\"predefined-type\">System</span>.out.println(MESSAGE);
++  }
++}
++    HTML_OPT_INDEPENDENT_LINES_OFF
++    
++    snippets[:java][:expected_with_option_on] = <<-HTML_OPT_INDEPENDENT_LINES_ON
++<span class=\"keyword\">import</span> <span class=\"include\">java.lang</span>.*;
++
++<span class=\"comment\">/**</span>
++<span class=\"comment\"> * This is some multiline javadoc</span>
++<span class=\"comment\"> * used to test the</span>
++<span class=\"comment\"> */</span>
++<span class=\"directive\">public</span> <span class=\"type\">class</span> <span class=\"class\">Test</span> {
++  <span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"directive\">final</span> <span class=\"predefined-type\">String</span> MESSAGE = <span class=\"string\"><span class=\"delimiter\">"</span><span class=\"content\">My message    To the world</span><span class=\"delimiter\">"</span></span>;
++
++  <span class=\"directive\">static</span> <span class=\"type\">void</span> main() {
++    <span class=\"comment\">/*</span>
++<span class=\"comment\">     * Another multiline</span>
++<span class=\"comment\">     * comment</span>
++<span class=\"comment\">     */</span>
++    <span class=\"predefined-type\">System</span>.out.println(MESSAGE);
++  }
++}
++    HTML_OPT_INDEPENDENT_LINES_ON
++    
++    for lang, code in snippets
++      tokens = CodeRay.scan code[:in], lang
++      
++      assert_equal code[:expected_with_option_off], tokens.html
++      assert_equal code[:expected_with_option_off], tokens.html(:break_lines => false)
++      assert_equal code[:expected_with_option_on],  tokens.html(:break_lines => true)
++    end
++  end
++end
+diff --git a/test/unit/json_encoder.rb b/test/unit/json_encoder.rb
+new file mode 100644
+index 0000000..4e44a64
+--- /dev/null
++++ b/test/unit/json_encoder.rb
+@@ -0,0 +1,28 @@
++require 'test/unit'
++require 'coderay'
++
++class JSONEncoderTest < Test::Unit::TestCase
++  
++  def test_json_output
++    old_load_paths = $:.dup
++    begin
++      $:.delete '.'
++      $:.delete File.dirname(__FILE__)
++      json = CodeRay.scan('puts "Hello world!"', :ruby).json
++      assert_equal [
++        {"type"=>"text", "text"=>"puts", "kind"=>"ident"},
++        {"type"=>"text", "text"=>" ", "kind"=>"space"},
++        {"type"=>"block", "action"=>"open", "kind"=>"string"},
++        {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
++        {"type"=>"text", "text"=>"Hello world!", "kind"=>"content"},
++        {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
++        {"type"=>"block", "action"=>"close", "kind"=>"string"},
++      ], JSON.load(json)
++    ensure
++      for path in old_load_paths - $:
++        $: << path
++      end
++    end
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/lines_of_code.rb b/test/unit/lines_of_code.rb
+new file mode 100644
+index 0000000..e2c0caf
+--- /dev/null
++++ b/test/unit/lines_of_code.rb
+@@ -0,0 +1,52 @@
++require 'test/unit'
++require 'coderay'
++$VERBOSE = true
++
++require File.expand_path('../../lib/assert_warning', __FILE__)
++
++class LinesOfCodeTest < Test::Unit::TestCase
++  
++  def test_creation
++    assert CodeRay::Encoders::LinesOfCode < CodeRay::Encoders::Encoder
++    filter = nil
++    assert_nothing_raised do
++      filter = CodeRay.encoder :loc
++    end
++    assert_kind_of CodeRay::Encoders::LinesOfCode, filter
++    assert_nothing_raised do
++      filter = CodeRay.encoder :lines_of_code
++    end
++    assert_kind_of CodeRay::Encoders::LinesOfCode, filter
++  end
++  
++  def test_lines_of_code
++    tokens = CodeRay.scan <<-RUBY, :ruby
++#!/usr/bin/env ruby
++
++# a minimal Ruby program
++puts "Hello world!"
++    RUBY
++    assert_equal 1, CodeRay::Encoders::LinesOfCode.new.encode_tokens(tokens)
++    assert_equal 1, tokens.lines_of_code
++    assert_equal 1, tokens.loc
++  end
++  
++  class ScannerMockup
++    KINDS_NOT_LOC = [:space]
++  end
++  
++  def test_filtering_block_tokens
++    tokens = CodeRay::Tokens.new
++    tokens.concat ["Hello\n", :world]
++    tokens.concat ["\n", :space]
++    tokens.concat ["Hello\n", :comment]
++    
++    assert_warning 'Tokens have no associated scanner, counting all nonempty lines.' do
++      assert_equal 1, tokens.lines_of_code
++    end
++    
++    tokens.scanner = ScannerMockup.new
++    assert_equal 2, tokens.lines_of_code
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/null.rb b/test/unit/null.rb
+new file mode 100644
+index 0000000..d3a9b0d
+--- /dev/null
++++ b/test/unit/null.rb
+@@ -0,0 +1,14 @@
++require 'test/unit'
++require 'coderay'
++
++class NullTest < Test::Unit::TestCase
++  
++  def test_null
++    ruby = <<-RUBY
++puts "Hello world!"
++    RUBY
++    tokens = CodeRay.scan ruby, :ruby
++    assert_equal '', tokens.encode(:null)
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/plugin.rb b/test/unit/plugin.rb
+new file mode 100755
+index 0000000..41eec51
+--- /dev/null
++++ b/test/unit/plugin.rb
+@@ -0,0 +1,69 @@
++require 'test/unit'
++require 'pathname'
++
++$:.unshift File.expand_path('../../../lib', __FILE__)
++require 'coderay'
++
++class PluginScannerTest < Test::Unit::TestCase
++  
++  module Plugins
++    extend CodeRay::PluginHost
++    plugin_path File.dirname(__FILE__), 'plugins'
++    class Plugin
++      extend CodeRay::Plugin
++      plugin_host Plugins
++    end
++  end
++  
++  module PluginsWithDefault
++    extend CodeRay::PluginHost
++    plugin_path File.dirname(__FILE__), 'plugins_with_default'
++    class Plugin
++      extend CodeRay::Plugin
++      plugin_host PluginsWithDefault
++    end
++    default :default_plugin
++  end
++  
++  def test_load
++    require Pathname.new(__FILE__).realpath.dirname + 'plugins' + 'user_defined' + 'user_plugin'
++    assert_equal 'UserPlugin', Plugins.load(:user_plugin).name
++  end
++  
++  def test_load_all
++    assert_instance_of Symbol, Plugins.load_all.first
++    assert_operator Plugins.all_plugins.first, :<, Plugins::Plugin
++    assert_equal 'The Example', Plugins.all_plugins.map { |plugin| plugin.title }.sort.first
++  end
++  
++  def test_default
++    assert_nothing_raised do
++      assert_operator PluginsWithDefault[:gargamel], :<, PluginsWithDefault::Plugin
++    end
++    assert_equal PluginsWithDefault::Default, PluginsWithDefault.default
++  end
++  
++  def test_plugin_not_found
++    assert_raise CodeRay::PluginHost::PluginNotFound do
++      Plugins[:thestral]
++    end
++    assert_raise ArgumentError do
++      Plugins[14]
++    end
++    assert_raise ArgumentError do
++      Plugins['test/test']
++    end
++    assert_raise CodeRay::PluginHost::PluginNotFound do
++      PluginsWithDefault[:example_without_register_for]
++    end
++  end
++  
++  def test_autoload_constants
++    assert_operator Plugins::Example, :<, Plugins::Plugin
++  end
++  
++  def test_title
++    assert_equal 'The Example', Plugins::Example.title
++  end
++  
++end
+diff --git a/test/unit/plugins/example.rb b/test/unit/plugins/example.rb
+new file mode 100644
+index 0000000..af1aeba
+--- /dev/null
++++ b/test/unit/plugins/example.rb
+@@ -0,0 +1,6 @@
++class Example < PluginScannerTest::Plugins::Plugin
++  
++  register_for :example
++  title 'The Example'
++  
++end
+diff --git a/test/unit/plugins/user_defined/user_plugin.rb b/test/unit/plugins/user_defined/user_plugin.rb
+new file mode 100644
+index 0000000..f47c934
+--- /dev/null
++++ b/test/unit/plugins/user_defined/user_plugin.rb
+@@ -0,0 +1,5 @@
++class UserPlugin < PluginScannerTest::Plugins::Plugin
++  
++  register_for :user_plugin
++  
++end
+diff --git a/test/unit/plugins_with_default/default_plugin.rb b/test/unit/plugins_with_default/default_plugin.rb
+new file mode 100644
+index 0000000..ae9e4c5
+--- /dev/null
++++ b/test/unit/plugins_with_default/default_plugin.rb
+@@ -0,0 +1,5 @@
++class DefaultPlugin < PluginScannerTest::PluginsWithDefault::Plugin
++  
++  register_for :default_plugin
++  
++end
+diff --git a/test/unit/plugins_with_default/example_without_register_for.rb b/test/unit/plugins_with_default/example_without_register_for.rb
+new file mode 100644
+index 0000000..083baf6
+--- /dev/null
++++ b/test/unit/plugins_with_default/example_without_register_for.rb
+@@ -0,0 +1,5 @@
++class ExampleWithoutRegisterFor < PluginScannerTest::PluginsWithDefault::Plugin
++  
++  register_for :wrong_id
++  
++end
+diff --git a/test/unit/statistic.rb b/test/unit/statistic.rb
+new file mode 100644
+index 0000000..1326dca
+--- /dev/null
++++ b/test/unit/statistic.rb
+@@ -0,0 +1,59 @@
++require 'test/unit'
++require 'coderay'
++
++class StatisticEncoderTest < Test::Unit::TestCase
++  
++  def test_creation
++    assert CodeRay::Encoders::Statistic < CodeRay::Encoders::Encoder
++    stats = nil
++    assert_nothing_raised do
++      stats = CodeRay.encoder :statistic
++    end
++    assert_kind_of CodeRay::Encoders::Encoder, stats
++  end
++  
++  TEST_INPUT = CodeRay::Tokens[
++    ['10', :integer],
++    ['(\\)', :operator],
++    [:begin_group, :string],
++    ['test', :content],
++    [:end_group, :string],
++    [:begin_line, :test],
++    ["\n", :space],
++    ["\n  \t", :space],
++    ["   \n", :space],
++    ["[]", :method],
++    [:end_line, :test],
++  ].flatten
++  TEST_OUTPUT = <<-'DEBUG'
++
++Code Statistics
++
++Tokens                  11
++  Non-Whitespace         4
++Bytes Total             20
++
++Token Types (7):
++  type                     count     ratio    size (average)
++-------------------------------------------------------------
++  TOTAL                       11  100.00 %     1.8
++  space                        3   27.27 %     3.0
++  string                       2   18.18 %     0.0
++  test                         2   18.18 %     0.0
++  :begin_group                 1    9.09 %     0.0
++  :begin_line                  1    9.09 %     0.0
++  :end_group                   1    9.09 %     0.0
++  :end_line                    1    9.09 %     0.0
++  content                      1    9.09 %     4.0
++  integer                      1    9.09 %     2.0
++  method                       1    9.09 %     2.0
++  operator                     1    9.09 %     3.0
++
++  DEBUG
++  
++  def test_filtering_text_tokens
++    assert_equal TEST_OUTPUT, CodeRay::Encoders::Statistic.new.encode_tokens(TEST_INPUT)
++    assert_equal TEST_OUTPUT, TEST_INPUT.statistic
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/suite.rb b/test/unit/suite.rb
+new file mode 100755
+index 0000000..417dfed
+--- /dev/null
++++ b/test/unit/suite.rb
+@@ -0,0 +1,16 @@
++require 'test/unit'
++require 'rubygems'
++
++$VERBOSE = $CODERAY_DEBUG = true
++$:.unshift 'lib'
++
++mydir = File.dirname(__FILE__)
++suite = Dir[File.join(mydir, '*.rb')].
++  map { |tc| File.basename(tc).sub(/\.rb$/, '') } - %w'suite vhdl'
++
++puts "Running CodeRay unit tests: #{suite.join(', ')}"
++
++helpers = %w(file_type word_list tokens)
++for test_case in helpers + (suite - helpers)
++  load File.join(mydir, test_case + '.rb')
++end
+diff --git a/test/unit/text.rb b/test/unit/text.rb
+new file mode 100644
+index 0000000..db086f5
+--- /dev/null
++++ b/test/unit/text.rb
+@@ -0,0 +1,14 @@
++require 'test/unit'
++require 'coderay'
++
++class TextTest < Test::Unit::TestCase
++  
++  def test_count
++    ruby = <<-RUBY
++puts "Hello world!"
++    RUBY
++    tokens = CodeRay.scan ruby, :ruby
++    assert_equal ruby, tokens.encode(:text)
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/token_kind_filter.rb b/test/unit/token_kind_filter.rb
+new file mode 100644
+index 0000000..13bae52
+--- /dev/null
++++ b/test/unit/token_kind_filter.rb
+@@ -0,0 +1,50 @@
++require 'test/unit'
++require 'coderay'
++
++class TokenKindFilterTest < Test::Unit::TestCase
++  
++  def test_creation
++    assert CodeRay::Encoders::TokenKindFilter < CodeRay::Encoders::Encoder
++    assert CodeRay::Encoders::TokenKindFilter < CodeRay::Encoders::Filter
++    filter = nil
++    assert_nothing_raised do
++      filter = CodeRay.encoder :token_kind_filter
++    end
++    assert_instance_of CodeRay::Encoders::TokenKindFilter, filter
++  end
++  
++  def test_filtering_text_tokens
++    tokens = CodeRay::Tokens.new
++    for i in 1..10
++      tokens.text_token i.to_s, :index
++      tokens.text_token ' ', :space if i < 10
++    end
++    assert_equal 10, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :exclude => :space).count
++    assert_equal 10, tokens.token_kind_filter(:exclude => :space).count
++    assert_equal 9, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :include => :space).count
++    assert_equal 9, tokens.token_kind_filter(:include => :space).count
++    assert_equal 0, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :exclude => :all).count
++    assert_equal 0, tokens.token_kind_filter(:exclude => :all).count
++  end
++  
++  def test_filtering_block_tokens
++    tokens = CodeRay::Tokens.new
++    10.times do |i|
++      tokens.begin_group :index
++      tokens.text_token i.to_s, :content
++      tokens.end_group :index
++      tokens.begin_group :naught if i == 5
++      tokens.end_group :naught if i == 7
++      tokens.begin_line :blubb
++      tokens.text_token i.to_s, :content
++      tokens.end_line :blubb
++    end
++    assert_equal 16, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :include => :blubb).count
++    assert_equal 16, tokens.token_kind_filter(:include => :blubb).count
++    assert_equal 24, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :include => [:blubb, :content]).count
++    assert_equal 24, tokens.token_kind_filter(:include => [:blubb, :content]).count
++    assert_equal 32, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :exclude => :index).count
++    assert_equal 32, tokens.token_kind_filter(:exclude => :index).count
++  end
++  
++end
+diff --git a/test/unit/tokens.rb b/test/unit/tokens.rb
+new file mode 100644
+index 0000000..73b0fd5
+--- /dev/null
++++ b/test/unit/tokens.rb
+@@ -0,0 +1,77 @@
++require 'test/unit'
++require 'coderay'
++
++class TokensTest < Test::Unit::TestCase
++  
++  def test_creation
++    assert CodeRay::Tokens < Array
++    tokens = nil
++    assert_nothing_raised do
++      tokens = CodeRay::Tokens.new
++    end
++    assert_kind_of Array, tokens
++  end
++  
++  def test_adding_tokens
++    tokens = make_tokens
++    assert_equal tokens.size, 8
++    assert_equal tokens.count, 4
++  end
++  
++  def test_to_s
++    assert_equal 'string()', make_tokens.to_s
++  end
++  
++  def test_encode_with_nonsense
++    assert_raise NoMethodError do
++      make_tokens.nonsense
++    end
++  end
++  
++  def test_split_into_parts
++    parts_4_3 = [
++      ["stri", :type],
++      ["ng", :type, :begin_group, :operator, "(", :content, :end_group, :operator],
++      [:begin_group, :operator, ")", :content, :end_group, :operator]
++    ]
++    assert_equal parts_4_3, make_tokens.split_into_parts(4, 3)
++    assert_equal [make_tokens.to_a], make_tokens.split_into_parts
++    
++    parts_7_0_1 = [
++      ["string", :type, :begin_group, :operator, "(", :content, :end_group, :operator],
++      [],
++      [:begin_group, :operator, ")", :content, :end_group, :operator]
++    ]
++    assert_equal parts_7_0_1, make_tokens.split_into_parts(7, 0, 1)
++    
++    line = CodeRay::Tokens[:begin_line, :head, '...', :plain]
++    line_parts = [
++      [:begin_line, :head, ".", :plain, :end_line, :head],
++      [:begin_line, :head, "..", :plain]
++    ]
++    assert_equal line_parts, line.split_into_parts(1)
++    
++    assert_raise ArgumentError do
++      CodeRay::Tokens[:bullshit, :input].split_into_parts
++    end
++    assert_raise ArgumentError do
++      CodeRay::Tokens[42, 43].split_into_parts
++    end
++  end
++  
++  def test_encode
++    assert_match(/\A\[\{(?:"type":"text"|"text":"string"|"kind":"type"|,){5}\},/, make_tokens.encode(:json))
++  end
++  
++  def make_tokens
++    tokens = CodeRay::Tokens.new
++    assert_nothing_raised do
++      tokens.text_token 'string', :type
++      tokens.begin_group :operator
++      tokens.text_token '()', :content
++      tokens.end_group :operator
++    end
++    tokens
++  end
++  
++end
+\ No newline at end of file
+diff --git a/test/unit/word_list.rb b/test/unit/word_list.rb
+new file mode 100644
+index 0000000..40d5a26
+--- /dev/null
++++ b/test/unit/word_list.rb
+@@ -0,0 +1,64 @@
++require 'test/unit'
++require 'coderay/helpers/word_list'
++
++class WordListTest < Test::Unit::TestCase
++  
++  include CodeRay
++  
++  # define word arrays
++  RESERVED_WORDS = %w[
++    asm break case continue default do else
++    ...
++  ]
++  
++  PREDEFINED_TYPES = %w[
++    int long short char void
++    ...
++  ]
++  
++  PREDEFINED_CONSTANTS = %w[
++    EOF NULL ...
++  ]
++  
++  # make a WordList
++  IDENT_KIND = WordList.new(:ident).
++    add(RESERVED_WORDS, :reserved).
++    add(PREDEFINED_TYPES, :predefined_type).
++    add(PREDEFINED_CONSTANTS, :predefined_constant)
++  
++  def test_word_list_example
++    assert_equal :predefined_type, IDENT_KIND['void']
++    # assert_equal :predefined_constant, IDENT_KIND['...']  # not specified
++  end
++  
++  def test_word_list
++    list = WordList.new(:ident).add(['foobar'], :reserved)
++    assert_equal :reserved, list['foobar']
++    assert_equal :ident, list['FooBar']
++    assert_equal 1, list.size
++  end
++  
++  def test_case_ignoring_word_list
++    list = WordList::CaseIgnoring.new(:ident).add(['foobar'], :reserved)
++    assert_equal :ident, list['foo']
++    assert_equal :reserved, list['foobar']
++    assert_equal :reserved, list['FooBar']
++    assert_equal 1, list.size
++    
++    list = WordList::CaseIgnoring.new(:ident).add(['FooBar'], :reserved)
++    assert_equal :ident, list['foo']
++    assert_equal :reserved, list['foobar']
++    assert_equal :reserved, list['FooBar']
++    assert_equal 1, list.size
++  end
++  
++  def test_dup
++    list = WordList.new(:ident).add(['foobar'], :reserved)
++    assert_equal :reserved, list['foobar']
++    list2 = list.dup
++    list2.add(%w[foobar], :keyword)
++    assert_equal :keyword, list2['foobar']
++    assert_equal :reserved, list['foobar']
++  end
++  
++end
diff --git a/debian/patches/series b/debian/patches/series
index 7d4702b..191475a 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,4 @@
+0000-restore_tests_and_rake_tasks_v1.1.2.patch
 0001-Remove-rubygems-depends.patch
 0002-Add-lack-file-for-UnitTest.patch
 0003-Add-Encoding.default_external.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/coderay.git



More information about the Pkg-ruby-extras-commits mailing list