Module: Tap::Test::FileTest

Defined in:
lib/tap/test/file_test.rb,
lib/tap/test/file_test/class_methods.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

Cleanup

By default the method_root directory is cleaned up at the end of the test, making FileTest very conveient for creating temporary test files. To prevent cleanup, either set the KEEP_OUTPUTS or KEEP_FAILURES ENV variable to ‘true’. The cleanup directories can be specified manually using cleanup_dirs class variable:

class LimitedCleanupTest < Test::Unit::TestCase

  # only cleanup the method_root[:output] directory
  acts_as_file_test :cleanup_dirs => [:output]
end

This technique is useful when you want to keep certain static files under version control, for instance.

Requirements

FileTest requires that a method_name method is provided by the including class, in order to properly set the directory for method_root. Test::Unit::TestCase satisfies this requirement already.

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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



82
83
84
# File 'lib/tap/test/file_test.rb', line 82

def method_root
  @method_root
end

Class Method Details

.included(base) ⇒ Object

:nodoc:



73
74
75
76
77
# File 'lib/tap/test/file_test.rb', line 73

def self.included(base) # :nodoc:
  super
  base.extend FileTest::ClassMethods
  base.cleanup_dirs = [:root]
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 |path|
        input = File.read(path)
        output_file = method_root.path(:output, File.basename(path))

        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’ paths 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.path(: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).

– TODO:

  • add debugging information to indicate, for instance, when dereferencing is going on.



256
257
258
259
260
261
262
# File 'lib/tap/test/file_test.rb', line 256

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_optionsObject

The default assert_files options



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/tap/test/file_test.rb', line 265

def 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

#cleanupObject

Cleans up the method_root.root directory by removing the class cleanup_dirs (by default :root, ie the method_root directory itself). The root directory will be removed if it is empty.

Override as necessary in subclasses.



97
98
99
100
101
102
# File 'lib/tap/test/file_test.rb', line 97

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

#ctrObject

Convenience method to access the class_test_root.



126
127
128
# File 'lib/tap/test/file_test.rb', line 126

def ctr
  self.class.class_test_root or raise "setup failure: no class_test_root has been set for #{self.class}"
end

#setupObject

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



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

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 (see above). Be sure to call super when overriding this method.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/tap/test/file_test.rb', line 106

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 (does setup call super?)"
  end
  
  # clear out the output folder if it exists, unless flagged otherwise
  unless ENV["KEEP_OUTPUTS"] == "true" || (!passed? && ENV["KEEP_FAILURES"] == "true")
    begin
      cleanup
    rescue
      raise("cleanup failure: #{$!.message}")
    end
  end
  
  try_remove_dir(ctr.root)
end