Module: Tap::Test::FileTest

Includes:
Assertions, EnvVars
Defined in:
lib/tap/test/file_test.rb

Overview

FileTest facilitates access and utilization of test-specific files and directories. FileTest provides each test method with a Tap::Root (method_root) specific for the method, and defines a new assertion method (assert_files) to facilitate tests which involve the production and/or modification of files.

[file_test_doc_test.rb]
class FileTestDocTest < Test::Unit::TestCase
  acts_as_file_test

  def test_something
    # each test class has a class test root (ctr)
    # and each test method has a method-specific
    # root (method_root)

    ctr.root                        # => File.expand_path(__FILE__.chomp('_test.rb'))
    method_root.root                # => File.join(ctr.root, "/test_something")
    method_root[:input]             # => File.join(ctr.root, "/test_something/input")

    # files in the :output and :tmp directories are cleared
    # before and after each test; this passes each time the
    # test is run with no additional cleanup:

    assert !File.exists?(method_root[:tmp])

    tmp_file = method_root.prepare(:tmp, 'sample.txt') {|file| file << "content" }
    assert_equal "content", File.read(tmp_file)

    # the assert_files method compares files produced
    # by the block the expected files, ensuring they
    # are the same (see the documentation for the 
    # simplest use of assert_files)

    expected_file = method_root.prepare(:expected, 'output.txt') {|file| file << 'expected output' }

    # passes
    assert_files do 
      method_root.prepare(:output, 'output.txt') {|file| file << 'expected output' }
    end 
  end
end

See Test::Unit::TestCase and FileTestClass for more information.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Assertions

#assert_alike, #assert_output_equal

Methods included from EnvVars

#env, #env_true?

Instance Attribute Details

#method_rootObject (readonly)

The test-method-specific Tap::Root which may be used to access test files. method_root is a duplicate of ctr reconfigured so that method_root.root is ctr



71
72
73
# File 'lib/tap/test/file_test.rb', line 71

def method_root
  @method_root
end

Class Method Details

.included(base) ⇒ Object

:nodoc:



57
58
59
60
61
# File 'lib/tap/test/file_test.rb', line 57

def self.included(base) # :nodoc:
  super
  base.extend FileTestClass
  base.cleanup_dirs = [:output, :tmp]
end

Instance Method Details

#assert_files(options = {}, &block) ⇒ Object

Runs a file-based test that compares files created by the block with files in an expected directory. The block receives files from the input directory, and should return a list of files relative to the output directory. Only the files returned by the block are compared; additional files in the output directory are effectively ignored.

Example

Lets define a test that transforms input files into output files in a trivial way, simply by replacing ‘input’ with ‘output’ in the file.

class FileTestDocTest < Test::Unit::TestCase
  acts_as_file_test

  def test_assert_files
    assert_files do |input_files|
      input_files.collect do |filepath|
        input = File.read(filepath)
        output_file = method_root.filepath(:output, File.basename(filepath))

        File.open(output_file, "w") do |f|
          f << input.gsub(/input/, "output")
        end 

        output_file
      end
    end
  end
end

Now say you had some input and expected files for test_assert_files:

file_test_doc/test_assert_files
|- expected
|   |- one.txt
|   `- two.txt
`- input
    |- one.txt
    `- two.txt

[input/one.txt]
test input 1

[input/two.txt]
test input 2

[expected/one.txt]
test output 1

[expected/two.txt]
test output 2

When you run the test, the assert_files passes the input files to the block. When the block completes, assert_files compares the output files returned by the block with the files in the expected directory. In this case, the files are equal and the test passes.

Say you changed the content of one of the expected files:

[expected/one.txt]
test flunk 1

Now the test fails because the output files aren’t equal to the expected files. The test also fails if there are missing or extra files.

Options

A variety of options adjust the behavior of assert_files:

:input_dir                      specify the directory to glob for input files
                                  (default method_root[:input])
:output_dir                     specify the output directory
                                  (default method_root[:output])
:expected_dir                   specify the directory to glob for expected files
                                  (default method_root[:expected])
:input_files                    directly specify the input files for the block
:expected_files                 directly specify the expected files for comparison
:include_input_directories      specifies directories to be included in the 
                                  input_files array (by default dirs are excluded)
:include_expected_directories   specifies directories to be included in the
                                  expected-output file list comparison 
                                  (by default dirs are excluded)

assert_files will fail if :expected_files was not specified in the options and no files were found in :expected_dir. This check tries to prevent silent false-positive results when you forget to put expected files in their place.

File References

Sometimes the same files will get used across multiple tests. To allow separate management of test files and prevent duplication, file references can be provided in place of test files. For instance, with a test directory like:

method_root
|- expected
|   |- one.txt.ref
|   `- two.txt.ref
|- input
|   |- one.txt.ref
|   `- two.txt.ref
`- ref
    |- one.txt
    `- two.txt

The input and expected files (all references in this case) can be dereferenced to the ‘ref’ filepaths like so:

assert_files :reference_dir => method_root[:ref] do |input_files|
  input_files # => ['method_root/ref/one.txt', 'method_root/ref/two.txt']

  input_files.collect do |input_file|
    output_file = method_root.filepath(:output, File.basename(input_file)
    FileUtils.cp(input_file, output_file)
    output_file
  end
end

Dereferencing occurs relative to the input_dir/expected_dir configurations; a reference_dir must be specified for dereferencing to occur (see Utils.dereference for more details).

Keeping Outputs

By default FileTest cleans up everything under method_root except the input and expected directories. For ease in debugging, ENV variable flags can be specified to prevent cleanup for all tests (KEEP_OUTPUTS) or just tests that fail (KEEP_FAILURES). These flags can be specified from the command line if you’re running the tests with rake or rap:

% rake test keep_outputs=true
% rap test keep_failures=true

– TODO:

  • add debugging information to indicate, for instance,

    when dereferencing is going on.



263
264
265
266
267
268
269
# File 'lib/tap/test/file_test.rb', line 263

def assert_files(options={}, &block) # :yields: input_files
  transform_test(block, options) do |expected_file, output_file|
    unless FileUtils.cmp(expected_file, output_file) 
      flunk "<#{expected_file}> not equal to\n<#{output_file}>"
    end
  end
end

#assert_files_alike(options = {}, &block) ⇒ Object

:yields: input_files



271
272
273
274
275
276
277
# File 'lib/tap/test/file_test.rb', line 271

def assert_files_alike(options={}, &block) # :yields: input_files
  transform_test(block, options) do |expected_file, output_file|
    regexp = RegexpEscape.new(File.read(expected_file)) 
    str = File.read(output_file)
    assert_alike(regexp, str, "<#{expected_file}> not equal to\n<#{output_file}>")
  end
end

#cleanupObject

Cleans up the method_root.root directory by removing the class cleanup_dirs (by default :tmp and :output). The root directory will also be removed if it is empty.

Override as necessary in subclasses.



86
87
88
89
90
91
# File 'lib/tap/test/file_test.rb', line 86

def cleanup
  self.class.cleanup_dirs.each do |dir|
    Utils.clear_dir(method_root[dir])
  end
  Utils.try_remove_dir(method_root.root)
end

#ctrObject

Convenience method to access the class_test_root.



64
65
66
# File 'lib/tap/test/file_test.rb', line 64

def ctr
  self.class.class_test_root
end

#default_assert_files_optionsObject

The default assert_files options



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/tap/test/file_test.rb', line 280

def default_assert_files_options
  {
    :input_dir => method_root[:input],
    :output_dir => method_root[:output],
    :expected_dir => method_root[:expected],
    
    :input_files => nil,
    :expected_files => nil,
    :include_input_directories => false,
    :include_expected_directories => false,
    
    :reference_dir => nil,
    :reference_extname => '.ref'
  }
end

#method_name_strObject

Returns method_name as a string (Ruby 1.9 symbolizes method_name)



122
123
124
# File 'lib/tap/test/file_test.rb', line 122

def method_name_str
  method_name.to_s
end

#setupObject

Sets up method_root and calls cleanup. Be sure to call super when overriding this method.



75
76
77
78
79
# File 'lib/tap/test/file_test.rb', line 75

def setup
  super
  @method_root = ctr.dup.reconfigure(:root => ctr[method_name.to_sym])
  cleanup
end

#teardownObject

Calls cleanup unless flagged otherwise by an ENV variable. To prevent cleanup (when debugging for example), set the ‘KEEP_OUTPUTS’ or ‘KEEP_FAILURES’ ENV variables:

% rap test KEEP_OUTPUTS=true
% rap test KEEP_FAILURES=true

Cleanup is only suppressed for failing tests when KEEP_FAILURES is specified. Be sure to call super when overriding this method.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/tap/test/file_test.rb', line 102

def teardown
  # check that method_root still exists (nil may
  # indicate setup was overridden without super)
  unless method_root
    raise "teardown failure: method_root is nil"
  end
  
  # clear out the output folder if it exists, unless flagged otherwise
  unless env("KEEP_OUTPUTS") || (!passed? && env("KEEP_FAILURES"))
    begin
      cleanup
    rescue
      raise("cleanup failure: #{$!.message}")
    end
  end
  
  Utils.try_remove_dir(ctr.root)
end