tap-test 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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