bahuvrihi-tap 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/History +69 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +119 -0
  4. data/bin/tap +114 -0
  5. data/cmd/console.rb +42 -0
  6. data/cmd/destroy.rb +16 -0
  7. data/cmd/generate.rb +16 -0
  8. data/cmd/run.rb +126 -0
  9. data/doc/Class Reference +362 -0
  10. data/doc/Command Reference +153 -0
  11. data/doc/Tutorial +237 -0
  12. data/lib/tap.rb +32 -0
  13. data/lib/tap/app.rb +720 -0
  14. data/lib/tap/constants.rb +8 -0
  15. data/lib/tap/env.rb +640 -0
  16. data/lib/tap/file_task.rb +547 -0
  17. data/lib/tap/generator/base.rb +109 -0
  18. data/lib/tap/generator/destroy.rb +37 -0
  19. data/lib/tap/generator/generate.rb +61 -0
  20. data/lib/tap/generator/generators/command/command_generator.rb +21 -0
  21. data/lib/tap/generator/generators/command/templates/command.erb +32 -0
  22. data/lib/tap/generator/generators/config/config_generator.rb +26 -0
  23. data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
  24. data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
  25. data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
  30. data/lib/tap/generator/generators/root/root_generator.rb +55 -0
  31. data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
  32. data/lib/tap/generator/generators/root/templates/gemspec +27 -0
  33. data/lib/tap/generator/generators/root/templates/tapfile +8 -0
  34. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +27 -0
  38. data/lib/tap/generator/generators/task/templates/task.erb +14 -0
  39. data/lib/tap/generator/generators/task/templates/test.erb +21 -0
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  42. data/lib/tap/patches/rake/testtask.rb +55 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  45. data/lib/tap/root.rb +581 -0
  46. data/lib/tap/support/aggregator.rb +55 -0
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +418 -0
  49. data/lib/tap/support/batchable.rb +47 -0
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +194 -0
  52. data/lib/tap/support/command_line.rb +98 -0
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +114 -0
  55. data/lib/tap/support/configurable_class.rb +296 -0
  56. data/lib/tap/support/configuration.rb +122 -0
  57. data/lib/tap/support/constant.rb +70 -0
  58. data/lib/tap/support/constant_utils.rb +127 -0
  59. data/lib/tap/support/declarations.rb +111 -0
  60. data/lib/tap/support/executable.rb +111 -0
  61. data/lib/tap/support/executable_queue.rb +82 -0
  62. data/lib/tap/support/framework.rb +71 -0
  63. data/lib/tap/support/framework_class.rb +199 -0
  64. data/lib/tap/support/instance_configuration.rb +147 -0
  65. data/lib/tap/support/lazydoc.rb +428 -0
  66. data/lib/tap/support/manifest.rb +89 -0
  67. data/lib/tap/support/run_error.rb +39 -0
  68. data/lib/tap/support/shell_utils.rb +71 -0
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +404 -0
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +410 -0
  75. data/lib/tap/support/versions.rb +97 -0
  76. data/lib/tap/task.rb +259 -0
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +37 -0
  80. data/lib/tap/test/env_vars.rb +29 -0
  81. data/lib/tap/test/file_methods.rb +377 -0
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +420 -0
  84. data/lib/tap/test/tap_methods.rb +237 -0
  85. data/lib/tap/workflow.rb +187 -0
  86. metadata +145 -0
data/lib/tap/test.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'tap/test/tap_methods'
2
+
3
+ module Test # :nodoc:
4
+ module Unit # :nodoc:
5
+
6
+ # Methods extending TestCase.
7
+ #
8
+ # === Method Availability
9
+ # Note that these methods are added piecemeal by Tap::Test::SubsetMethods,
10
+ # Tap::Test::FileMethods and Tap::Test::TapMethods, but that fact doesn't
11
+ # come through in the documentation. Hence, not all of them will be available
12
+ # if you're only using SubsetMethods or FileMethods. Breaks down like this:
13
+ #
14
+ # Using: Methods Available:
15
+ # TapMethods all
16
+ # FileMethods all, except acts_as_tap_test
17
+ # SubsetMethods all, except acts_as_tap_test, acts_as_file_test, file_test_root
18
+ #
19
+ #--
20
+ #See the TestTutorial for more information.
21
+ class TestCase
22
+ end
23
+ end
24
+ end
25
+
26
+ module Tap
27
+
28
+ # Modules facilitating testing. TapMethods are specific to
29
+ # Tap, but SubsetMethods and FileMethods are more general in
30
+ # their utility.
31
+ module Test
32
+ end
33
+ end
34
+
35
+
36
+
37
+
@@ -0,0 +1,29 @@
1
+ module Tap
2
+ module Test
3
+
4
+ # Provides for case-insensitive access to the ENV variables
5
+ module EnvVars
6
+
7
+ # Access to the case-insensitive ENV variables. Raises an error
8
+ # if multiple case-insensitive values are defined in ENV.
9
+ def env(type)
10
+ type = type.downcase
11
+
12
+ # ruby 1.9 returns a hash instead of an array
13
+ selected = ENV.select {|key, value| key.downcase == type}.to_a
14
+
15
+ case selected.length
16
+ when 0 then nil
17
+ when 1 then selected[0][1]
18
+ else
19
+ raise "Multiple env values for '#{type}'"
20
+ end
21
+ end
22
+
23
+ # Returns true if the env_var(var) is set and matches /^true$/i
24
+ def env_true?(var)
25
+ env(var) && env(var) =~ /^true$/i
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,377 @@
1
+ require 'tap/root'
2
+ require 'tap/test/env_vars'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+
6
+ module Test # :nodoc:
7
+ module Unit # :nodoc:
8
+ class TestCase
9
+ class << self
10
+
11
+ # Access the test root structure (a Tap::Root)
12
+ attr_accessor :trs
13
+
14
+ # Causes a TestCase to act as a file test, by instantiating a class Tap::Root
15
+ # (trs), and including FileMethods. The root and directories used to
16
+ # instantiate trs can be specified as options. By default file_test_root
17
+ # and the directories {:input => 'input', :output => 'output', :expected => 'expected'}
18
+ # will be used.
19
+ #
20
+ # Note: file_test_root determines a root directory <em>based on the calling file</em>.
21
+ # Be sure to specify the root directory explicitly if you call acts_as_file_test
22
+ # from a file that is NOT meant to be test file.
23
+ def acts_as_file_test(options={})
24
+ options = {
25
+ :root => file_test_root,
26
+ :directories => {:input => 'input', :output => 'output', :expected => 'expected'}
27
+ }.merge(options)
28
+
29
+ directories = options[:directories]
30
+ self.trs = Tap::Root.new(options[:root], directories)
31
+
32
+ include Tap::Test::FileMethods
33
+ end
34
+
35
+ # Infers the test root directory from the calling file. Ex:
36
+ # 'some_class.rb' => 'some_class'
37
+ # 'some_class_test.rb' => 'some_class'
38
+ def file_test_root
39
+ # the calling file is not the direct caller of +method_root+... this method is
40
+ # only accessed from within another method call, hence the target caller is caller[1]
41
+ # rather than caller[0].
42
+
43
+ # caller[1] is considered the calling file (which should be the test case)
44
+ # note that the output of calller.first is like:
45
+ # ./path/to/file.rb:10
46
+ # ./path/to/file.rb:10:in 'method'
47
+ calling_file = caller[1].gsub(/:\d+(:in .*)?$/, "")
48
+ calling_file.chomp!("#{File.extname(calling_file)}")
49
+ calling_file.chomp("_test")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ module Tap
57
+ module Test
58
+
59
+
60
+ # FileMethods sets up a TestCase with methods for accessing and utilizing
61
+ # test-specific files and directories. Each class that acts_as_file_test
62
+ # is set up with a Tap::Root structure (trs) that mediates the creation of
63
+ # test method filepaths.
64
+ #
65
+ # class FileMethodsDocTest < Test::Unit::TestCase
66
+ # acts_as_file_test
67
+ #
68
+ # def test_something
69
+ # # dir = File.expand_path( File.dirname(__FILE__) )
70
+ # trs.root # => dir + "/file_methods_doc",
71
+ # method_root # => dir + "/file_methods_doc/test_something",
72
+ # method_dir(:input) # => dir + "/file_methods_doc/test_something/input"
73
+ # end
74
+ # end
75
+ #
76
+ # === assert_files
77
+ #
78
+ # FileMethods is specifically designed for tests that transform a set of input
79
+ # files into output files. For this type of test, input and expected files can
80
+ # placed into their respective directories then used within the context of
81
+ # assert_files to ensure the output files are equal to the expected files.
82
+ #
83
+ # For example, lets define a test that transforms input files into output files
84
+ # in a trivial way, simply by replacing 'input' with 'output' in the file.
85
+ #
86
+ # class FileMethodsDocTest < Test::Unit::TestCase
87
+ # acts_as_file_test
88
+ #
89
+ # def test_sub
90
+ # assert_files do |input_files|
91
+ # input_files.collect do |filepath|
92
+ # input = File.read(filepath)
93
+ # output_file = method_filepath(:output, File.basename(filepath))
94
+ #
95
+ # File.open(output_file, "w") do |f|
96
+ # f << input.gsub(/input/, "output")
97
+ # end
98
+ #
99
+ # output_file
100
+ # end
101
+ # end
102
+ # end
103
+ # end
104
+ #
105
+ # Now say you had some input and expected files for the 'test_sub' method:
106
+ #
107
+ # [file_methods_doc/test_sub/input/one.txt]
108
+ # test input 1
109
+ #
110
+ # [file_methods_doc/test_sub/input/two.txt]
111
+ # test input 2
112
+ #
113
+ # [file_methods_doc/test_sub/expected/one.txt]
114
+ # test output 1
115
+ #
116
+ # [file_methods_doc/test_sub/expected/two.txt]
117
+ # test output 2
118
+ #
119
+ # When you run the FileMethodsDocTest test, the test_sub test will pass
120
+ # the input files to the assert_files block. Then assert_files compares
121
+ # the returned filepaths with the expected files translated from the
122
+ # expected directory to the output directory. In this case, the files
123
+ # are equal and the test passes.
124
+ #
125
+ # The test fails if the returned files aren't equal to the expected files,
126
+ # either because there are missing or extra files, or if the file contents
127
+ # are different.
128
+ #
129
+ # When the test completes, the teardown method cleans up the output directory.
130
+ # For ease in debugging, ENV variable flags can be specified to keep all
131
+ # output files (KEEP_OUTPUTS) or to keep the output files for just the tests
132
+ # that fail (KEEP_FAILURES). These flags can be specified from the command
133
+ # line if you're running the tests with rake or tap:
134
+ #
135
+ # % rake test keep_outputs=true
136
+ # % tap run test keep_failures=true
137
+ #
138
+ #
139
+ # === Class Methods
140
+ #
141
+ # See {Test::Unit::TestCase}[link:classes/Test/Unit/TestCase.html] for documentation of the class methods added by FileMethods.
142
+ module FileMethods
143
+ include Tap::Test::EnvVars
144
+
145
+ # Convenience accessor for the test root structure
146
+ def trs
147
+ self.class.trs
148
+ end
149
+
150
+ # Creates the trs.directories, specific to the method calling make_test_directories
151
+ def make_test_directories
152
+ trs.directories.values.each do |dir|
153
+ FileUtils.mkdir_p( File.join(trs.root, method_name_str, dir) )
154
+ end
155
+ end
156
+
157
+ attr_reader :method_tempfiles
158
+
159
+ # Setup deletes the the output directory if it exists, and tries to remove the
160
+ # method root directory so the directory structure is reset before running the
161
+ # test, even if outputs were left over from previous tests.
162
+ def setup
163
+ super
164
+ @method_tempfiles = []
165
+ clear_method_dir(:output)
166
+ try_remove_dir(method_root)
167
+ end
168
+
169
+ # Teardown deletes the the output directories unless flagged otherwise. Note
170
+ # that teardown also checks the environment variables for flags. To keep all outputs
171
+ # (or failures) for all tests, flag keep outputs from the command line like:
172
+ #
173
+ # % tap run test KEEP_OUTPUTS=true
174
+ # % tap run test KEEP_FAILURES=true
175
+ def teardown
176
+ # clear out the output folder if it exists, unless flagged otherwise
177
+ unless env("KEEP_OUTPUTS") || (!@test_passed && env("KEEP_FAILURES"))
178
+ begin
179
+ clear_method_dir(:output)
180
+ rescue
181
+ raise("teardown failure: could not remove output files")
182
+ end
183
+ end
184
+
185
+ try_remove_dir(method_root)
186
+ try_remove_dir(trs.root)
187
+ end
188
+
189
+ # Returns method_name as a string (Ruby 1.9 symbolizes method_name)
190
+ def method_name_str
191
+ method_name.to_s
192
+ end
193
+
194
+ # The method_root directory is defined as trs.filepath(method_name)
195
+ def method_root(method=method_name_str)
196
+ trs.filepath(method)
197
+ end
198
+
199
+ # The method directory is defined as 'dir/method', where method is the calling method
200
+ # by default. method_dir returns the method directory if it exists, otherwise it returns
201
+ # trs[dir].
202
+ def method_dir(dir, method=method_name_str)
203
+ File.join(method_root(method), trs.directories[dir] || dir.to_s)
204
+ end
205
+
206
+ # Returns a glob of files matching the input pattern, underneath the method directory
207
+ # if it exists, otherwise the <tt>trs[dir]</tt> directory.
208
+ def method_glob(dir, *patterns)
209
+ dir = trs.relative_filepath(:root, method_dir(dir))
210
+ trs.glob(dir, *patterns)
211
+ end
212
+
213
+ # Returns a filepath constructed from the method directory if it exists,
214
+ # otherwise the filepath will be constructed from <tt>trs[dir]</tt>.
215
+ def method_filepath(dir, *filenames)
216
+ File.join(method_dir(dir), *filenames)
217
+ end
218
+
219
+ # Removes the method directory from the input filepath, returning the resuting filename.
220
+ # If the method directory does not exist, <tt>trs[dir]</tt> will be removed.
221
+ def method_relative_filepath(dir, filepath)
222
+ dir = trs.relative_filepath(:root, method_dir(dir))
223
+ trs.relative_filepath(dir, filepath)
224
+ end
225
+
226
+ # Returns an output file corresponding to the input file, translated from the
227
+ # input directory to the output directory.
228
+ #
229
+ # If the input method directory exists, it will be removed from the filepath.
230
+ # If the output method directory exists, it will be inserted in the filepath.
231
+ def method_translate(filepath, input_dir, output_dir)
232
+ input_dir = trs.relative_filepath(:root, method_dir(input_dir))
233
+ output_dir = trs.relative_filepath(:root, method_dir(output_dir))
234
+ trs.translate(filepath, input_dir, output_dir)
235
+ end
236
+
237
+ # Attempts to recursively remove the specified method directory and all
238
+ # files within it. Raises an error if the removal does not succeed.
239
+ def clear_method_dir(dir)
240
+ # clear out the folder if it exists
241
+ dir_path = method_dir(dir, method_name_str)
242
+ FileUtils.rm_r(dir_path) if File.exists?(dir_path)
243
+ end
244
+
245
+ # Attempts to remove the specified directory. The root
246
+ # will not be removed if the directory does not exist, or
247
+ # is not empty.
248
+ def try_remove_dir(dir)
249
+ # Remove the directory if possible
250
+ begin
251
+ FileUtils.rmdir(dir) if File.exists?(dir) && Dir.glob(File.join(dir, "*")).empty?
252
+ rescue
253
+ # rescue cases where there is a hidden file, for example .svn
254
+ end
255
+ end
256
+
257
+ # Generates a temporary filepath formatted like "output_dir\filename.pid.n.ext" where n
258
+ # is a counter that will be incremented from until a non-existant filepath is achieved.
259
+ #
260
+ # Notes:
261
+ # - By default filename is the calling method
262
+ # - The extension is chomped off the end of the filename
263
+ # - If the directory for the filepath does not exist, the directory will be created
264
+ # - Like all files in the output directory, tempfiles will be deleted by the default
265
+ # +teardown+ method
266
+ def method_tempfile(filename=method_name_str, &block)
267
+ ext = File.extname(filename)
268
+ basename = filename.chomp(ext)
269
+ filepath = method_filepath(:output, sprintf('%s%d.%d%s', basename, $$, method_tempfiles.length, ext))
270
+ method_tempfiles << filepath
271
+
272
+ dirname = File.dirname(filepath)
273
+ FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
274
+ if block_given?
275
+ File.open(filepath, "w", &block)
276
+ end
277
+ filepath
278
+ end
279
+
280
+ # Yields to the input block for each pair of entries in the input
281
+ # arrays. An error is raised if the input arrays do not have equal
282
+ # numbers of entries.
283
+ def each_pair(a, b, &block) # :yields: entry_a, entry_b,
284
+ each_pair_with_index(a,b) do |entry_a, entry_b, index|
285
+ yield(entry_a, entry_b)
286
+ end
287
+ end
288
+
289
+ # Same as each_pair but yields the index of the entries as well.
290
+ def each_pair_with_index(a, b, &block) # :yields: entry_a, entry_b, index
291
+ a = [a] unless a.kind_of?(Array)
292
+ b = [b] unless b.kind_of?(Array)
293
+
294
+ raise ArgumentError, "The input arrays must have an equal number of entries." unless a.length == b.length
295
+ 0.upto(a.length-1) do |index|
296
+ yield(a[index], b[index], index)
297
+ end
298
+ end
299
+
300
+ # assert_files runs a file-based test that feeds all files in method_dir(:input)
301
+ # to the block, then compares the resulting files (which should be relative to
302
+ # method_dir(:output)) with all the files in method_dir(:expected). Note that
303
+ # since only the files returned by the block are used in the comparison,
304
+ # additional files in the output directory are effectively ignored.
305
+ #
306
+ # A variety of options can be specified to adjust the behavior:
307
+ #
308
+ # :input_files specify the input files to pass to the block
309
+ # :expected_files specify the expected files used in comparison
310
+ # :include_input_directories specifies directories to be included in the
311
+ # input_files array (by default dirs are excluded)
312
+ # :include_expected_directories specifies directories to be included in the
313
+ # expected-output file list comparison (by default
314
+ # dirs are excluded, note that naturally only files
315
+ # have their actual content compared)
316
+ #
317
+ # Option keys should be symbols. assert_files will fail if :expected_files was
318
+ # not specified in the options and no files were found in method_dir(:expected).
319
+ # This tries to prevent silent false-positive results when you forget to put
320
+ # expected files in their place.
321
+ def assert_files(options={}) # :yields: input_files
322
+ make_test_directories
323
+
324
+ options = {
325
+ :input_files => nil,
326
+ :expected_files => nil,
327
+
328
+ :include_input_directories => false,
329
+ :include_expected_directories => false
330
+ }.merge(options)
331
+
332
+ # Get the input and expected files in this manner:
333
+ # - look for manually specified files
334
+ # - glob for files if none were specified
335
+ # - expand paths and sort
336
+ # - remove directories unless specified not to do so
337
+ input_files, expected_files = [:input, :expected].collect do |key|
338
+ files = options["#{key}_files".to_sym]
339
+ files = method_glob(key) if files.nil?
340
+ files = [files].flatten.collect {|file| File.expand_path(file) }.sort
341
+
342
+ unless options["include_#{key}_directories".to_sym]
343
+ files.delete_if {|file| File.directory?(file)}
344
+ end
345
+
346
+ files
347
+ end
348
+
349
+ # check at least one expected file was found
350
+ if expected_files.empty? && options[:expected_files] == nil
351
+ flunk "No expected files specified."
352
+ end
353
+
354
+ # get output files from the block, expand and sort
355
+ output_files = [yield(input_files)].flatten.collect do |output_file|
356
+ output_file = File.expand_path(output_file)
357
+ end.sort
358
+
359
+ # check that the expected and output filepaths are the same
360
+ translated_expected_files = expected_files.collect do |expected_file|
361
+ method_translate(expected_file, :expected, :output)
362
+ end
363
+ assert_equal translated_expected_files, output_files, "Missing, extra, or unexpected output files"
364
+
365
+ # check that the expected and output file contents are equal
366
+ errors = []
367
+ each_pair(expected_files, output_files) do |expected_file, output_file|
368
+ unless (File.directory?(expected_file) && File.directory?(output_file)) || FileUtils.cmp(expected_file, output_file)
369
+ errors << "<#{expected_file}> not equal to\n<#{output_file}>"
370
+ end
371
+ end
372
+ flunk "File compare failed:\n" + errors.join("\n") unless errors.empty?
373
+ end
374
+
375
+ end
376
+ end
377
+ end
@@ -0,0 +1,144 @@
1
+ require 'test/unit'
2
+ require 'tap/test/file_methods'
3
+ require 'tap/test/subset_methods'
4
+ #require 'tap/support/shell_utils'
5
+
6
+ module Test # :nodoc:
7
+ module Unit # :nodoc:
8
+ class TestCase
9
+ class << self
10
+
11
+ def acts_as_script_test(options={})
12
+ options = options.inject({:root => file_test_root}) do |hash, (key, value)|
13
+ hash[key.to_sym || key] = value
14
+ hash
15
+ end
16
+ acts_as_file_test(options)
17
+ include Tap::Test::SubsetMethods
18
+ include Tap::Test::ScriptMethods
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module Tap
27
+ module Test
28
+
29
+ module ScriptMethods
30
+ class CommandTest
31
+ attr_accessor :command_path
32
+ attr_reader :commands
33
+
34
+ def initialize
35
+ @command_path = nil
36
+ @commands = []
37
+ end
38
+
39
+ def check(argstr, msg=nil, expected=nil, &block)
40
+ commands << ["#{command_path}#{argstr}", msg, expected, block]
41
+ end
42
+
43
+ def check_cmd(cmd, msg=nil, expected=nil, &block)
44
+ commands << [cmd, msg, expected, block]
45
+ end
46
+ end
47
+
48
+ include Tap::Support::ShellUtils
49
+
50
+ def assert_output_equal(a, b, cmd, msg)
51
+ a = a[1..-1] if a[0] == ?\n
52
+ if a == b
53
+ assert true
54
+ else
55
+ flunk %Q{
56
+ #{msg}
57
+ % #{cmd}
58
+ ==================== expected output ====================
59
+ #{a.gsub(/\t/, "\\t").gsub(/\r\n/, "\\r\\n\n").gsub(/\n/, "\\n\n")}
60
+ ======================== but was ========================
61
+ #{b.gsub(/\t/, "\\t").gsub(/\r\n/, "\\r\\n\n").gsub(/\n/, "\\n\n")}
62
+ =========================================================
63
+ }
64
+ end
65
+ end
66
+
67
+ def assert_alike(a, b, cmd, msg)
68
+ if b =~ a
69
+ assert true
70
+ else
71
+ flunk %Q{
72
+ #{msg}
73
+ % #{cmd}
74
+ ================= expected output like ==================
75
+ #{a}
76
+ ======================== but was ========================
77
+ #{b.gsub(/\t/, "\\t").gsub(/\r\n/, "\\r\\n\n").gsub(/\n/, "\\n\n")}
78
+ =========================================================
79
+ }
80
+ end
81
+ end
82
+
83
+ def script_test(test_dir=method_dir(:output))
84
+ subset_test("SCRIPT", "s") do
85
+ test = CommandTest.new
86
+ yield(test)
87
+
88
+ current_dir = Dir.pwd
89
+ current_argv = ARGV.dup
90
+ begin
91
+ ARGV.clear
92
+ make_test_directories
93
+ Dir.chdir(test_dir)
94
+
95
+ puts "\n# == #{method_name}"
96
+
97
+ test.commands.each do |cmd, msg, expected, block|
98
+ start = Time.now
99
+ result = capture_sh(cmd) {|ok, status, tempfile_path| }
100
+ elapsed = Time.now - start
101
+
102
+ case expected
103
+ when String
104
+ assert_output_equal(expected, result, cmd, msg)
105
+ when Regexp
106
+ assert_alike(expected, result, cmd, msg)
107
+ end
108
+
109
+ if block
110
+ block.call(result)
111
+ end
112
+
113
+ if env('stepwise') || (expected == nil && block == nil)
114
+ print %Q{
115
+ ------------------------------------
116
+ %s
117
+ > %s
118
+ %s
119
+ Time Elapsed: %.3fs} % [msg, cmd, result, elapsed]
120
+
121
+ if env('stepwise')
122
+ print "\nContinue? (y/n): "
123
+ break if gets.strip =~ /^no?$/i
124
+ end
125
+ else
126
+ puts "%.3fs : %s" % [elapsed, msg]
127
+ end
128
+ end
129
+ ensure
130
+ Dir.chdir(current_dir)
131
+ ARGV.clear
132
+ ARGV.concat(current_argv)
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ end
139
+ end
140
+ end
141
+
142
+
143
+
144
+