tap-test 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History ADDED
@@ -0,0 +1,5 @@
1
+ == 0.1.0 / 2009-05-25
2
+
3
+ As of the 0.17.0 release, Tap is distributes its test modules independently
4
+ in this gem. Test modules have been cleaned up and are in many cases not
5
+ compatible with earlier versions distributed with Tap.
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009, Regents of the University of Colorado.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,123 @@
1
+ = {Tap Test}[http://tap.rubyforge.org/tap-test]
2
+
3
+ test v. to reveal the strengths or capabilities of something
4
+
5
+ Test modules for Tap.
6
+
7
+ == Description
8
+
9
+ Provides test modules to simplify testing of common Tap tasks, especially
10
+ tasks that require interaction with files. Modules are also provided to test
11
+ the shell behavior of executables and to define conditions for tests (ex
12
+ *nix/windows only). Tap-Test is not a testing framework. By default Tap-Test
13
+ integrates with Test::Unit and Mini::Test, but it is possible to include the
14
+ test modules into other test frameworks.
15
+
16
+ {Tap-Test}[http://tap.rubyforge.org/tap-test] is a part of the
17
+ {Tap-Suite}[http://tap.rubyforge.org/tap-suite]. Check out these links for
18
+ documentation, development, and bug tracking.
19
+
20
+ * Website[http://tap.rubyforge.org]
21
+ * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/9908-tap-task-application/tickets]
22
+ * Github[http://github.com/bahuvrihi/tap/tree/master]
23
+ * {Google Group}[http://groups.google.com/group/ruby-on-tap]
24
+
25
+ == Usage
26
+
27
+ The Tap-Test modules are small and targeted. More complete examples can be
28
+ found in the documentation, these are intended to show the point of each
29
+ module. To enable a module in Test::Unit or Mini::Test, require
30
+ 'tap/test/unit' and simply call the acts_as_x_test method. Multiple modules
31
+ can be overlaid.
32
+
33
+ ==== {FileTest}[link:classes/Tap/Test/FileTest.html] (acts_as_file_test)
34
+
35
+ Sets up a test-specific method_root for working with temporary files. Better
36
+ in most cases than using Tempfile because you can flag temporary files to be
37
+ saved on a failure (using ENV['KEEP_OUTPUTS']='true'). Also provides a new
38
+ assertion to test that all the files in an expected directory are equal to
39
+ those in an output directory.
40
+
41
+ require 'tap/test/unit'
42
+ class FileTestTest < Test::Unit::TestCase
43
+ acts_as_file_test
44
+
45
+ def test_method_root
46
+ assert_equal "test_method_root", File.basename(method_root.root)
47
+
48
+ path = method_root.prepare(:tmp, 'file.txt') {|io| io << 'content' }
49
+ assert_equal "content", File.read(path)
50
+ end
51
+ end
52
+
53
+ ==== {ShellTest}[link:classes/Tap/Test/ShellTest.html] (acts_as_shell_test)
54
+
55
+ Simple testing of shell commands.
56
+
57
+ require 'tap/test/unit'
58
+ class ShellTestTest < Test::Unit::TestCase
59
+ acts_as_shell_test :cmd_pattern => '% alias', :cmd => 'echo'
60
+
61
+ def test_echo
62
+ assert_equal "goodnight moon", sh("echo goodnight moon").strip
63
+ end
64
+
65
+ def test_echo_with_an_alias
66
+ sh_test %q{
67
+ % alias goodnight moon
68
+ goodnight moon
69
+ }
70
+ end
71
+ end
72
+
73
+ ==== {SubsetTest}[link:classes/Tap/Test/SubsetTest.html] (acts_as_subset_test)
74
+
75
+ Allows in-file subsetting of tests into groups. Easy to get carried away with
76
+ this one, but handy, especially for platform-specific tests. Turn on a subset
77
+ or all tests using an ENV variable (ex ENV['ALL']='true').
78
+
79
+ require 'tap/test/unit'
80
+ class SubsetTestTest < Test::Unit::TestCase
81
+ acts_as_subset_test
82
+
83
+ condition(:windows) { match_platform?('mswin') }
84
+
85
+ def test_something_for_windows_only
86
+ condition_test(:windows) do
87
+ # ...
88
+ end
89
+ end
90
+
91
+ def test_something_that_takes_forever
92
+ extended_test do
93
+ # ...
94
+ end
95
+ end
96
+ end
97
+
98
+ ==== {TapTest}[link:classes/Tap/Test/TapTest.html] (acts_as_tap_test)
99
+
100
+ Sets up Tap::App.instance for testing tasks.
101
+
102
+ require 'tap/test/unit'
103
+ class TapTestTest < Test::Unit::TestCase
104
+ acts_as_tap_test
105
+
106
+ def test_task
107
+ t = Tap::Task.intern {|task| "result" }
108
+ assert_equal "result", t.process
109
+ end
110
+ end
111
+
112
+ == Installation
113
+
114
+ Tap-Test is available as a gem on RubyForge[http://rubyforge.org/projects/tap]. Use:
115
+
116
+ % gem install tap-test
117
+
118
+ == Info
119
+
120
+ Copyright (c) 2009, Regents of the University of Colorado.
121
+ Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
122
+ Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
123
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
@@ -0,0 +1,17 @@
1
+ module Tap
2
+ module Test
3
+ module FileTest
4
+
5
+ # Class methods extending tests which include FileTest.
6
+ module ClassMethods
7
+
8
+ # The class-level test root (a Tap::Root)
9
+ attr_accessor :class_test_root
10
+
11
+ # An array of directories to be cleaned up by cleanup
12
+ attr_accessor :cleanup_dirs
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,387 @@
1
+ require 'tap/test/file_test/class_methods'
2
+ require 'tap/test/utils'
3
+
4
+ module Tap
5
+ module Test
6
+
7
+ # FileTest facilitates access and utilization of test-specific files and
8
+ # directories. FileTest provides each test method with a Tap::Root
9
+ # (method_root) specific for the method, and defines a new assertion method
10
+ # (assert_files) to facilitate tests which involve the production and/or
11
+ # modification of files.
12
+ #
13
+ # [file_test_doc_test.rb]
14
+ # class FileTestDocTest < Test::Unit::TestCase
15
+ # acts_as_file_test
16
+ #
17
+ # def test_something
18
+ # # each test class has a class test root (ctr)
19
+ # # and each test method has a method-specific
20
+ # # root (method_root)
21
+ #
22
+ # ctr.root # => File.expand_path(__FILE__.chomp('_test.rb'))
23
+ # method_root.root # => File.join(ctr.root, "/test_something")
24
+ # method_root[:input] # => File.join(ctr.root, "/test_something/input")
25
+ #
26
+ # # files in the :output and :tmp directories are cleared
27
+ # # before and after each test; this passes each time the
28
+ # # test is run with no additional cleanup:
29
+ #
30
+ # assert !File.exists?(method_root[:tmp])
31
+ #
32
+ # tmp_file = method_root.prepare(:tmp, 'sample.txt') {|file| file << "content" }
33
+ # assert_equal "content", File.read(tmp_file)
34
+ #
35
+ # # the assert_files method compares files produced
36
+ # # by the block the expected files, ensuring they
37
+ # # are the same (see the documentation for the
38
+ # # simplest use of assert_files)
39
+ #
40
+ # expected_file = method_root.prepare(:expected, 'output.txt') {|file| file << 'expected output' }
41
+ #
42
+ # # passes
43
+ # assert_files do
44
+ # method_root.prepare(:output, 'output.txt') {|file| file << 'expected output' }
45
+ # end
46
+ # end
47
+ # end
48
+ #
49
+ # FileTest requires that a method_name method is provided by the including
50
+ # class, in order to properly set the directory for method_root.
51
+ module FileTest
52
+
53
+ def self.included(base) # :nodoc:
54
+ super
55
+ base.extend FileTest::ClassMethods
56
+ base.cleanup_dirs = [:output, :tmp]
57
+ end
58
+
59
+ # The test-method-specific Tap::Root which may be used to
60
+ # access test files. method_root is a duplicate of ctr
61
+ # reconfigured so that method_root.root is ctr[method_name.to_sym]
62
+ attr_reader :method_root
63
+
64
+ # Sets up method_root and calls cleanup. Be sure to call super when
65
+ # overriding this method.
66
+ def setup
67
+ super
68
+ @method_root = ctr.dup.reconfigure(:root => ctr[method_name.to_sym])
69
+ cleanup
70
+ end
71
+
72
+ # Cleans up the method_root.root directory by removing the class
73
+ # cleanup_dirs (by default :tmp and :output). The root directory
74
+ # will also be removed if it is empty.
75
+ #
76
+ # Override as necessary in subclasses.
77
+ def cleanup
78
+ self.class.cleanup_dirs.each do |dir|
79
+ clear_dir(method_root[dir])
80
+ end
81
+ try_remove_dir(method_root.root)
82
+ end
83
+
84
+ # Calls cleanup unless flagged otherwise by an ENV variable. To prevent
85
+ # cleanup (when debugging for example), set the 'KEEP_OUTPUTS' or
86
+ # 'KEEP_FAILURES' ENV variables:
87
+ #
88
+ # % rap test KEEP_OUTPUTS=true
89
+ # % rap test KEEP_FAILURES=true
90
+ #
91
+ # Cleanup is only suppressed for failing tests when KEEP_FAILURES is
92
+ # specified. Be sure to call super when overriding this method.
93
+ def teardown
94
+ # check that method_root still exists (nil may
95
+ # indicate setup was overridden without super)
96
+ unless method_root
97
+ raise "teardown failure: method_root is nil (does setup call super?)"
98
+ end
99
+
100
+ # clear out the output folder if it exists, unless flagged otherwise
101
+ unless ENV["KEEP_OUTPUTS"] == "true" || (!passed? && ENV["KEEP_FAILURES"] == "true")
102
+ begin
103
+ cleanup
104
+ rescue
105
+ raise("cleanup failure: #{$!.message}")
106
+ end
107
+ end
108
+
109
+ try_remove_dir(ctr.root)
110
+ end
111
+
112
+ # Convenience method to access the class_test_root.
113
+ def ctr
114
+ self.class.class_test_root or raise "setup failure: no class_test_root has been set for #{self.class}"
115
+ end
116
+
117
+ # Runs a file-based test that compares files created by the block with
118
+ # files in an expected directory. The block receives files from the
119
+ # input directory, and should return a list of files relative to the
120
+ # output directory. Only the files returned by the block are compared;
121
+ # additional files in the output directory are effectively ignored.
122
+ #
123
+ # === Example
124
+ # Lets define a test that transforms input files into output files in a
125
+ # trivial way, simply by replacing 'input' with 'output' in the file.
126
+ #
127
+ # class FileTestDocTest < Test::Unit::TestCase
128
+ # acts_as_file_test
129
+ #
130
+ # def test_assert_files
131
+ # assert_files do |input_files|
132
+ # input_files.collect do |path|
133
+ # input = File.read(path)
134
+ # output_file = method_root.path(:output, File.basename(path))
135
+ #
136
+ # File.open(output_file, "w") do |f|
137
+ # f << input.gsub(/input/, "output")
138
+ # end
139
+ #
140
+ # output_file
141
+ # end
142
+ # end
143
+ # end
144
+ # end
145
+ #
146
+ # Now say you had some input and expected files for test_assert_files:
147
+ #
148
+ # file_test_doc/test_assert_files
149
+ # |- expected
150
+ # | |- one.txt
151
+ # | `- two.txt
152
+ # `- input
153
+ # |- one.txt
154
+ # `- two.txt
155
+ #
156
+ # [input/one.txt]
157
+ # test input 1
158
+ #
159
+ # [input/two.txt]
160
+ # test input 2
161
+ #
162
+ # [expected/one.txt]
163
+ # test output 1
164
+ #
165
+ # [expected/two.txt]
166
+ # test output 2
167
+ #
168
+ # When you run the test, the assert_files passes the input files to the
169
+ # block. When the block completes, assert_files compares the output
170
+ # files returned by the block with the files in the expected directory.
171
+ # In this case, the files are equal and the test passes.
172
+ #
173
+ # Say you changed the content of one of the expected files:
174
+ #
175
+ # [expected/one.txt]
176
+ # test flunk 1
177
+ #
178
+ # Now the test fails because the output files aren't equal to the
179
+ # expected files. The test also fails if there are missing or extra
180
+ # files.
181
+ #
182
+ # === Options
183
+ # A variety of options adjust the behavior of assert_files:
184
+ #
185
+ # :input_dir specify the directory to glob for input files
186
+ # (default method_root[:input])
187
+ # :output_dir specify the output directory
188
+ # (default method_root[:output])
189
+ # :expected_dir specify the directory to glob for expected files
190
+ # (default method_root[:expected])
191
+ # :input_files directly specify the input files for the block
192
+ # :expected_files directly specify the expected files for comparison
193
+ # :include_input_directories specifies directories to be included in the
194
+ # input_files array (by default dirs are excluded)
195
+ # :include_expected_directories specifies directories to be included in the
196
+ # expected-output file list comparison
197
+ # (by default dirs are excluded)
198
+ #
199
+ # assert_files will fail if <tt>:expected_files</tt> was not specified
200
+ # in the options and no files were found in <tt>:expected_dir</tt>. This
201
+ # check tries to prevent silent false-positive results when you forget to
202
+ # put expected files in their place.
203
+ #
204
+ # === File References
205
+ #
206
+ # Sometimes the same files will get used across multiple tests. To allow
207
+ # separate management of test files and prevent duplication, file
208
+ # references can be provided in place of test files. For instance, with a
209
+ # test directory like:
210
+ #
211
+ # method_root
212
+ # |- expected
213
+ # | |- one.txt.ref
214
+ # | `- two.txt.ref
215
+ # |- input
216
+ # | |- one.txt.ref
217
+ # | `- two.txt.ref
218
+ # `- ref
219
+ # |- one.txt
220
+ # `- two.txt
221
+ #
222
+ # The input and expected files (all references in this case) can be
223
+ # dereferenced to the 'ref' paths like so:
224
+ #
225
+ # assert_files :reference_dir => method_root[:ref] do |input_files|
226
+ # input_files # => ['method_root/ref/one.txt', 'method_root/ref/two.txt']
227
+ #
228
+ # input_files.collect do |input_file|
229
+ # output_file = method_root.path(:output, File.basename(input_file)
230
+ # FileUtils.cp(input_file, output_file)
231
+ # output_file
232
+ # end
233
+ # end
234
+ #
235
+ # Dereferencing occurs relative to the input_dir/expected_dir
236
+ # configurations; a reference_dir must be specified for dereferencing to
237
+ # occur (see Utils.dereference for more details).
238
+ #
239
+ # === Keeping Outputs
240
+ #
241
+ # By default FileTest cleans up everything under method_root except the
242
+ # input and expected directories. For ease in debugging, ENV variable
243
+ # flags can be specified to prevent cleanup for all tests (KEEP_OUTPUTS)
244
+ # or just tests that fail (KEEP_FAILURES). These flags can be specified
245
+ # from the command line if you're running the tests with rake or rap:
246
+ #
247
+ # % rake test keep_outputs=true
248
+ # % rap test keep_failures=true
249
+ #
250
+ #--
251
+ # TODO:
252
+ # * add debugging information to indicate, for instance,
253
+ # when dereferencing is going on.
254
+ def assert_files(options={}, &block) # :yields: input_files
255
+ transform_test(block, options) do |expected_file, output_file|
256
+ unless FileUtils.cmp(expected_file, output_file)
257
+ flunk "<#{expected_file}> not equal to\n<#{output_file}>"
258
+ end
259
+ end
260
+ end
261
+
262
+ # The default assert_files options
263
+ def assert_files_options
264
+ {
265
+ :input_dir => method_root[:input],
266
+ :output_dir => method_root[:output],
267
+ :expected_dir => method_root[:expected],
268
+
269
+ :input_files => nil,
270
+ :expected_files => nil,
271
+ :include_input_directories => false,
272
+ :include_expected_directories => false,
273
+
274
+ :reference_dir => nil,
275
+ :reference_extname => '.ref'
276
+ }
277
+ end
278
+
279
+ private
280
+
281
+ # Yields to the input block for each pair of entries in the input
282
+ # arrays. An error is raised if the input arrays do not have equal
283
+ # numbers of entries.
284
+ def each_pair(a, b, &block) # :yields: entry_a, entry_b,
285
+ each_pair_with_index(a,b) do |entry_a, entry_b, index|
286
+ yield(entry_a, entry_b)
287
+ end
288
+ end
289
+
290
+ # Same as each_pair but yields the index of the entries as well.
291
+ def each_pair_with_index(a, b, error_msg=nil, &block) # :yields: entry_a, entry_b, index
292
+ a = [a] unless a.kind_of?(Array)
293
+ b = [b] unless b.kind_of?(Array)
294
+
295
+ unless a.length == b.length
296
+ raise ArgumentError, (error_msg || "The input arrays must have an equal number of entries.")
297
+ end
298
+
299
+ 0.upto(a.length-1) do |index|
300
+ yield(a[index], b[index], index)
301
+ end
302
+ end
303
+
304
+ # Attempts to recursively remove the specified method directory and all
305
+ # files within it. Raises an error if the removal does not succeed.
306
+ def clear_dir(dir)
307
+ # clear out the folder if it exists
308
+ FileUtils.rm_r(dir) if File.exists?(dir)
309
+ end
310
+
311
+ # Attempts to remove the specified directory. The root
312
+ # will not be removed if the directory does not exist, or
313
+ # is not empty.
314
+ def try_remove_dir(dir)
315
+ # Remove the directory if possible
316
+ begin
317
+ FileUtils.rmdir(dir) if File.exists?(dir) && Dir.glob(File.join(dir, "*")).empty?
318
+ rescue
319
+ # rescue cases where there is a hidden file, for example .svn
320
+ end
321
+ end
322
+
323
+ def transform_test(block, options={}) # :yields: expected_files, output_files
324
+ options = assert_files_options.merge(options)
325
+ input_dir = options[:input_dir]
326
+ output_dir = options[:output_dir]
327
+ expected_dir = options[:expected_dir]
328
+
329
+ reference_dir = options[:reference_dir]
330
+ reference_pattern = options[:reference_pattern]
331
+
332
+ Utils.dereference([input_dir, expected_dir], reference_dir, reference_pattern || '**/*.ref') do
333
+
334
+ # Get the input and expected files in this manner:
335
+ # - look for manually specified files
336
+ # - glob for files if none were specified
337
+ # - expand paths and sort
338
+ # - remove directories unless specified not to do so
339
+ input_files, expected_files = [:input, :expected].collect do |key|
340
+ files = options["#{key}_files".to_sym]
341
+ if files.nil?
342
+ pattern = File.join(options["#{key}_dir".to_sym], "**/*")
343
+ files = Dir.glob(pattern)
344
+ end
345
+ files = [files].flatten.collect {|file| File.expand_path(file) }.sort
346
+
347
+ unless options["include_#{key}_directories".to_sym]
348
+ files.delete_if {|file| File.directory?(file)}
349
+ end
350
+
351
+ files
352
+ end
353
+
354
+ # check at least one expected file was found
355
+ if expected_files.empty? && options[:expected_files] == nil
356
+ flunk "No expected files specified."
357
+ end
358
+
359
+ # get output files from the block, expand and sort
360
+ output_files = [*block.call(input_files)].collect do |output_file|
361
+ File.expand_path(output_file)
362
+ end.sort
363
+
364
+ # check that the expected and output paths are the same
365
+ translated_expected_files = expected_files.collect do |expected_file|
366
+ Tap::Root::Utils.translate(expected_file, expected_dir, output_dir)
367
+ end
368
+ assert_equal translated_expected_files, output_files, "Missing, extra, or unexpected output files"
369
+
370
+ # check that the expected and output file contents are equal
371
+ errors = []
372
+ each_pair(expected_files, output_files) do |expected_file, output_file|
373
+ unless (File.directory?(expected_file) && File.directory?(output_file)) || FileUtils.cmp(expected_file, output_file)
374
+ begin
375
+ yield(expected_file, output_file)
376
+ rescue
377
+ errors << $!
378
+ end
379
+ end
380
+ end
381
+ flunk "File compare failed:\n" + errors.join("\n") unless errors.empty?
382
+ end
383
+ end
384
+
385
+ end
386
+ end
387
+ end
@@ -0,0 +1,24 @@
1
+ module Tap
2
+ module Test
3
+ module ShellTest
4
+ # Class methods extending tests which include ShellTest.
5
+ module ClassMethods
6
+
7
+ # Sets the default sh_test_options
8
+ attr_writer :sh_test_options
9
+
10
+ # Returns a hash of the default sh_test options.
11
+ def sh_test_options
12
+ @sh_test_options ||= {}
13
+ end
14
+
15
+ private
16
+
17
+ # helper to retrieve class constants
18
+ def class_const(const_name) # :nodoc:
19
+ const_defined?(const_name) ? const_get(const_name) : nil
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end