tap 0.8.0 → 0.9.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.
Files changed (185) hide show
  1. data/Basic Overview +151 -0
  2. data/Command Reference +99 -0
  3. data/History +24 -0
  4. data/MIT-LICENSE +1 -1
  5. data/README +29 -57
  6. data/Rakefile +30 -37
  7. data/Tutorial +243 -191
  8. data/bin/tap +66 -35
  9. data/lib/tap.rb +47 -29
  10. data/lib/tap/app.rb +700 -342
  11. data/lib/tap/{script → cmd}/console.rb +0 -0
  12. data/lib/tap/{script → cmd}/destroy.rb +0 -0
  13. data/lib/tap/{script → cmd}/generate.rb +0 -0
  14. data/lib/tap/cmd/run.rb +156 -0
  15. data/lib/tap/constants.rb +4 -0
  16. data/lib/tap/dump.rb +57 -0
  17. data/lib/tap/env.rb +316 -0
  18. data/lib/tap/file_task.rb +106 -109
  19. data/lib/tap/generator.rb +4 -1
  20. data/lib/tap/generator/generators/command/USAGE +6 -0
  21. data/lib/tap/generator/generators/command/command_generator.rb +17 -0
  22. data/lib/tap/generator/generators/{script/templates/script.erb → command/templates/command.erb} +10 -10
  23. data/lib/tap/generator/generators/config/USAGE +21 -0
  24. data/lib/tap/generator/generators/config/config_generator.rb +17 -7
  25. data/lib/tap/generator/generators/file_task/USAGE +3 -0
  26. data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -0
  27. data/lib/tap/generator/generators/file_task/templates/file.txt +2 -0
  28. data/lib/tap/generator/generators/file_task/templates/file.yml +3 -0
  29. data/lib/tap/generator/generators/file_task/templates/task.erb +26 -20
  30. data/lib/tap/generator/generators/file_task/templates/test.erb +20 -10
  31. data/lib/tap/generator/generators/generator/generator_generator.rb +1 -1
  32. data/lib/tap/generator/generators/generator/templates/generator.erb +21 -12
  33. data/lib/tap/generator/generators/root/templates/Rakefile +33 -24
  34. data/lib/tap/generator/generators/root/templates/tap.yml +28 -31
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +1 -0
  36. data/lib/tap/generator/generators/task/USAGE +3 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +18 -5
  38. data/lib/tap/generator/generators/task/templates/task.erb +7 -12
  39. data/lib/tap/generator/generators/task/templates/test.erb +10 -11
  40. data/lib/tap/generator/generators/workflow/templates/task.erb +1 -1
  41. data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
  42. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  43. data/lib/tap/patches/rake/testtask.rb +55 -0
  44. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  45. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  46. data/lib/tap/root.rb +172 -67
  47. data/lib/tap/script.rb +70 -336
  48. data/lib/tap/support/aggregator.rb +55 -0
  49. data/lib/tap/support/audit.rb +281 -280
  50. data/lib/tap/support/batchable.rb +59 -0
  51. data/lib/tap/support/class_configuration.rb +279 -0
  52. data/lib/tap/support/configurable.rb +92 -0
  53. data/lib/tap/support/configurable_methods.rb +296 -0
  54. data/lib/tap/support/executable.rb +98 -0
  55. data/lib/tap/support/executable_queue.rb +82 -0
  56. data/lib/tap/support/logger.rb +9 -15
  57. data/lib/tap/support/rake.rb +43 -54
  58. data/lib/tap/support/run_error.rb +32 -13
  59. data/lib/tap/support/shell_utils.rb +47 -0
  60. data/lib/tap/support/tdoc.rb +9 -8
  61. data/lib/tap/support/tdoc/config_attr.rb +40 -16
  62. data/lib/tap/support/validation.rb +77 -0
  63. data/lib/tap/support/versions.rb +36 -36
  64. data/lib/tap/task.rb +276 -482
  65. data/lib/tap/test.rb +20 -261
  66. data/lib/tap/test/env_vars.rb +7 -5
  67. data/lib/tap/test/file_methods.rb +126 -121
  68. data/lib/tap/test/subset_methods.rb +86 -45
  69. data/lib/tap/test/tap_methods.rb +271 -0
  70. data/lib/tap/workflow.rb +174 -46
  71. data/test/app/config/another/task.yml +1 -0
  72. data/test/app/config/erb.yml +2 -1
  73. data/test/app/config/some/task.yml +1 -0
  74. data/test/app/config/template.yml +2 -6
  75. data/test/app_test.rb +1241 -1008
  76. data/test/env/test_configure/recurse_a.yml +2 -0
  77. data/test/env/test_configure/recurse_b.yml +2 -0
  78. data/test/env/test_configure/tap.yml +23 -0
  79. data/test/env/test_load_env_config/dir/tap.yml +3 -0
  80. data/test/env/test_load_env_config/recurse_a.yml +2 -0
  81. data/test/env/test_load_env_config/recurse_b.yml +2 -0
  82. data/test/env/test_load_env_config/tap.yml +3 -0
  83. data/test/env_test.rb +198 -0
  84. data/test/file_task_test.rb +70 -53
  85. data/{lib/tap/generator/generators/package/USAGE → test/root/file.txt} +0 -0
  86. data/test/root_test.rb +621 -454
  87. data/test/script_test.rb +38 -174
  88. data/test/support/aggregator_test.rb +99 -0
  89. data/test/support/audit_test.rb +409 -416
  90. data/test/support/batchable_test.rb +74 -0
  91. data/test/support/{task_configuration_test.rb → class_configuration_test.rb} +106 -47
  92. data/test/{task/config/overriding.yml → support/configurable/config/configured.yml} +0 -0
  93. data/test/support/configurable_test.rb +295 -0
  94. data/test/support/executable_queue_test.rb +103 -0
  95. data/test/support/executable_test.rb +38 -0
  96. data/test/support/logger_test.rb +17 -17
  97. data/test/support/rake_test.rb +4 -2
  98. data/test/support/shell_utils_test.rb +24 -0
  99. data/test/support/tdoc_test.rb +265 -258
  100. data/test/support/validation_test.rb +54 -0
  101. data/test/support/versions_test.rb +38 -38
  102. data/test/tap_test_helper.rb +19 -5
  103. data/test/tap_test_suite.rb +5 -2
  104. data/test/task_base_test.rb +13 -104
  105. data/test/task_syntax_test.rb +300 -0
  106. data/test/task_test.rb +258 -381
  107. data/test/test/env_vars_test.rb +40 -40
  108. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/one.txt +0 -0
  109. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/two.txt +0 -0
  110. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/one.txt +0 -0
  111. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/two.txt +0 -0
  112. data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/one.txt +0 -0
  113. data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/two.txt +0 -0
  114. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +1 -0
  115. data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_different_content}/expected/two.txt +0 -0
  116. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +1 -0
  117. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +1 -0
  118. data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_missing_expected_file}/expected/one.txt +0 -0
  119. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +1 -0
  120. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +1 -0
  121. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +1 -0
  122. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +1 -0
  123. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +1 -0
  124. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +1 -0
  125. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +1 -0
  126. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +1 -0
  127. data/test/test/file_methods_doc/test_sub/expected/one.txt +1 -0
  128. data/test/test/file_methods_doc/test_sub/expected/two.txt +1 -0
  129. data/test/test/file_methods_doc/test_sub/input/one.txt +1 -0
  130. data/test/test/file_methods_doc/test_sub/input/two.txt +1 -0
  131. data/test/test/file_methods_doc_test.rb +29 -0
  132. data/test/test/file_methods_test.rb +214 -143
  133. data/test/test/subset_methods_test.rb +111 -115
  134. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/a.txt +0 -0
  135. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/b.txt +0 -0
  136. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/a.txt +0 -0
  137. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/b.txt +0 -0
  138. data/test/test/tap_methods_test.rb +399 -0
  139. data/test/workflow_test.rb +101 -91
  140. metadata +86 -70
  141. data/lib/tap/generator/generators/package/package_generator.rb +0 -38
  142. data/lib/tap/generator/generators/package/templates/package.erb +0 -186
  143. data/lib/tap/generator/generators/script/USAGE +0 -0
  144. data/lib/tap/generator/generators/script/script_generator.rb +0 -17
  145. data/lib/tap/script/run.rb +0 -154
  146. data/lib/tap/support/batch_queue.rb +0 -162
  147. data/lib/tap/support/combinator.rb +0 -114
  148. data/lib/tap/support/task_configuration.rb +0 -169
  149. data/lib/tap/support/template.rb +0 -81
  150. data/lib/tap/support/templater.rb +0 -155
  151. data/lib/tap/version.rb +0 -4
  152. data/test/app/config/addition_template.yml +0 -6
  153. data/test/app_class_test.rb +0 -33
  154. data/test/check/binding_eval.rb +0 -23
  155. data/test/check/define_method_check.rb +0 -22
  156. data/test/check/dependencies_check.rb +0 -175
  157. data/test/check/inheritance_check.rb +0 -22
  158. data/test/support/batch_queue_test.rb +0 -320
  159. data/test/support/combinator_test.rb +0 -249
  160. data/test/support/template_test.rb +0 -122
  161. data/test/support/templater/erb.txt +0 -2
  162. data/test/support/templater/erb.yml +0 -2
  163. data/test/support/templater/somefile.txt +0 -2
  164. data/test/support/templater_test.rb +0 -192
  165. data/test/task/config/template.yml +0 -4
  166. data/test/task_class_test.rb +0 -170
  167. data/test/task_execute_test.rb +0 -262
  168. data/test/test/file_methods/test_assert_expected/expected/file.txt +0 -1
  169. data/test/test/file_methods/test_assert_expected/expected/folder/file.txt +0 -1
  170. data/test/test/file_methods/test_assert_expected/input/file.txt +0 -1
  171. data/test/test/file_methods/test_assert_expected/input/folder/file.txt +0 -1
  172. data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
  173. data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
  174. data/test/test/file_methods/test_file_compare/expected/output_1.txt +0 -3
  175. data/test/test/file_methods/test_file_compare/expected/output_2.txt +0 -1
  176. data/test/test/file_methods/test_file_compare/input/input_1.txt +0 -3
  177. data/test/test/file_methods/test_file_compare/input/input_2.txt +0 -3
  178. data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
  179. data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
  180. data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
  181. data/test/test/file_methods/test_yml_compare/expected/output_1.yml +0 -6
  182. data/test/test/file_methods/test_yml_compare/expected/output_2.yml +0 -6
  183. data/test/test/file_methods/test_yml_compare/input/input_1.yml +0 -4
  184. data/test/test/file_methods/test_yml_compare/input/input_2.yml +0 -4
  185. data/test/test_test.rb +0 -373
@@ -5,7 +5,6 @@ require 'tap/test/env_vars'
5
5
 
6
6
  module Test # :nodoc:
7
7
  module Unit # :nodoc:
8
- # Methods extending TestCase. See the TestTutorial for more information.
9
8
  class TestCase
10
9
  class << self
11
10
  include Tap::Test::EnvVars
@@ -16,7 +15,9 @@ module Test # :nodoc:
16
15
  subclass_conditions = conditions.inject({}) do |memo, (key, value)|
17
16
  memo.update(key => (value.dup rescue value))
18
17
  end
19
- subclass.instance_variable_set("@conditions", subclass_conditions)
18
+ subclass.instance_variable_set(:@conditions, subclass_conditions)
19
+ subclass.instance_variable_set(:@run_test_suite, nil)
20
+ subclass.instance_variable_set(:@skip_messages, [])
20
21
 
21
22
  # subclass_inputs = prompt_inputs.inject({}) do |memo, (key, value)|
22
23
  # memo.update(key => (value.dup rescue value))
@@ -107,8 +108,8 @@ module Test # :nodoc:
107
108
  # true if no platforms are specified.
108
109
  #
109
110
  # Some common platforms:
110
- # - mswin:: Windows
111
- # - darwin:: Mac
111
+ # mswin Windows
112
+ # darwin Mac
112
113
  def match_platform?(*platforms)
113
114
  platforms.each do |platform|
114
115
  platform.to_s =~ /^(non_)?(.*)/
@@ -139,11 +140,11 @@ module Test # :nodoc:
139
140
  # Causes a test suite to be skipped. If a message is given, it will
140
141
  # print and notify the user the test suite has been skipped.
141
142
  def skip_test(msg=nil)
142
- @run_test_suite = false
143
+ self.run_test_suite = false
143
144
 
144
145
  # experimental -- perhaps use this so that a test can be skipped
145
146
  # for multiple reasons?
146
- (@skip_messages ||= []) << msg unless msg.nil?
147
+ @skip_messages << msg unless msg.nil?
147
148
  end
148
149
 
149
150
  alias :original_suite :suite
@@ -153,7 +154,6 @@ module Test # :nodoc:
153
154
  if run_test_suite?
154
155
  original_suite
155
156
  else
156
- @skip_messages ||= []
157
157
  puts "Skipping #{name}: #{@skip_messages.join(', ')}" unless @skip_messages.empty?
158
158
  Test::Unit::TestSuite.new(name)
159
159
  end
@@ -169,7 +169,7 @@ module Tap
169
169
  # Ideally you always run all of your tests and they all run and pass everywhere. In
170
170
  # practice it's useful to suppress the execution of some tests -- long running tests,
171
171
  # tests specific for a given platform, or tests that depend on some condition, such
172
- # as the version of some third-party software your code interacts with.
172
+ # as the version of some optional third-party software your code interacts with.
173
173
  #
174
174
  # SubsetMethods extends TestCase with methods for defining conditions that can be
175
175
  # used to conditionally perform some action, or skip a test suite entirely. When
@@ -185,54 +185,64 @@ module Tap
185
185
  # condition(:non_windows) { match_platform?('non_mswin') }
186
186
  # end
187
187
  #
188
- # class AfterHoursTest < Test::Unit::TestCase
189
- # # only true between 6pm and 6am
190
- # # (maybe you're not allowed to tie up a computer until after hours)
191
- # condition(:afterhours) { Time.now > 18 || Time.now < 6 }
192
- #
193
- # skip_test unless satisfied?(:windows, :afterhours)
188
+ # class WindowsOnlyTest < Test::Unit::TestCase
189
+ # skip_test unless satisfied?(:windows)
194
190
  # end
195
191
  #
196
- # AfterHoursTest will only run between the hours of 6pm and 6am, on a Windows platform.
197
- # These conditions can be used in specific tests, when only some tests need to be skipped.
192
+ # WindowsOnlyTest will only run on a Windows platform. These conditions can be used
193
+ # in specific tests, when only some tests need to be skipped.
198
194
  #
199
195
  # class RunOnlyAFewTest < Test::Unit::TestCase
200
196
  # include SubsetMethods
201
- # include Benchmark
202
197
  #
203
198
  # def test_runs_all_the_time
204
199
  # assert true
205
200
  # end
206
201
  #
207
- # def test_runs_only_if_afterhours_condition_is_true
208
- # condition_test(:afterhours) do
209
- # ...
202
+ # def test_runs_only_if_non_windows_condition_is_true
203
+ # condition_test(:non_windows) { assert true }
210
204
  # end
211
205
  # end
212
206
  #
213
- # def test_runs_only_when_EXTENDED_is_true
214
- # extended_test do
215
- # ...
216
- # end
207
+ # def test_runs_only_when_ENV_variable_EXTENDED_is_true
208
+ # extended_test { assert true }
217
209
  # end
218
210
  #
219
- # def test_runs_only_when_BENCHMARK_is_true
211
+ # def test_runs_only_when_ENV_variable_BENCHMARK_is_true
220
212
  # benchmark_test do |x|
221
213
  # x.report("init speed") { 10000.times { Object.new } }
222
214
  # end
223
215
  # end
216
+ #
217
+ # def test_runs_only_when_ENV_variable_CUSTOM_is_true
218
+ # subset_test('CUSTOM') { assert true }
219
+ # end
224
220
  # end
225
221
  #
226
- # In the example, EXTENDED and BENCHMARK refer to environment (ENV) variables.
227
- # ENV variables can be set from the command line like so:
222
+ # In the example, the ENV variables EXTENDED, BENCHMARK, and CUSTOM act as flags
223
+ # to run specific tests. If you're running your test using Rake, ENV variables
224
+ # can be set from the command line like so:
228
225
  #
229
- # % tap run test extended=true
230
- # % tap run test BENCHMARK=true
226
+ # % rake test EXTENDED=true
227
+ # % rake test BENCHMARK=true
231
228
  #
232
- # In so far as SubsetMethods is concerned, the environment variables are case-insensitive.
233
- # To run all tests that get switched using an environment variable, set ALL=true.
229
+ # Since tap can run rake tasks as well, these are equivalent:
234
230
  #
235
- # == Class Methods
231
+ # % tap run test EXTENDED=true
232
+ # % tap run test BENCHMARK=true
233
+ #
234
+ # In so far as SubsetMethods is concerned, the environment variables are
235
+ # case-insensitive. As in the example, additional ENV-variable-dependent
236
+ # tests can be defined using the subset_test method. To run all tests that
237
+ # get switched using an environment variable, set ALL=true.
238
+ #
239
+ # # also runs benchmark tests
240
+ # % tap run test BenchMark=true
241
+ #
242
+ # # runs all tests
243
+ # % tap run test all=true
244
+ #
245
+ # === Class Methods
236
246
  #
237
247
  # See Test::Unit::TestCase for documentation of the class methods added by SubsetMethods
238
248
  module SubsetMethods
@@ -247,13 +257,14 @@ module Tap
247
257
  self.class.run_subset?(type)
248
258
  end
249
259
 
250
- # Returns true if the pretty-print string for obj matches the regexp specified in env_var(type).
251
- # Returns the default value if 'ALL' is specified in ENV or type is not specified in ENV.
252
- def match_regexp?(type, obj, default=true)
260
+ # Returns true if the input string matches the regexp specified in
261
+ # env_var(type). Returns the default value if 'ALL' is specified in
262
+ # ENV or type is not specified in ENV.
263
+ def match_regexp?(type, str, default=true)
253
264
  return true if env_true?("ALL")
254
265
  return default unless env(type)
255
266
 
256
- PP.singleline_pp(obj, '') =~ Regexp.new(env(type)) ? true : false
267
+ str =~ Regexp.new(env(type)) ? true : false
257
268
  end
258
269
 
259
270
  # Platform-specific test. Useful for specifying test that should only
@@ -298,16 +309,47 @@ module Tap
298
309
  end
299
310
 
300
311
  # Basic method for a subset test. The provided block will run if:
301
- # - The subset type or 'ALL' is specified in ENV
302
- # - The calling method matches the regexp provided in the "TYPE_TEST" ENV variable
312
+ # - The subset type is specified in ENV
313
+ # - The subset 'ALL' is specified in ENV
314
+ # - The test method name matches the regexp provided in the <TYPE>_TEST ENV variable
303
315
  #
304
- # Otherwise the block will be skipped and +skip+ will be printed. By default skip
305
- # is the first letter of +type+.
316
+ # Otherwise the block will be skipped and +skip+ will be printed in the test output.
317
+ # By default skip is the first letter of +type+.
318
+ #
319
+ # For example, with these methods:
320
+ #
321
+ # def test_one
322
+ # subset_test('CUSTOM') { assert true }
323
+ # end
324
+ #
325
+ # def test_two
326
+ # subset_test('CUSTOM') { assert true }
327
+ # end
328
+ #
329
+ # Condition Tests that get run
330
+ # ENV['ALL']=true test_one, test_two
331
+ # ENV['CUSTOM']=true test_one, test_two
332
+ # ENV['CUSTOM_TEST']=test_ test_one, test_two
333
+ # ENV['CUSTOM_TEST']=test_one test_one
334
+ # ENV['CUSTOM']=nil no tests get run
335
+ #
336
+ # If you're running your tests with Rake, ENV variables can be set from the
337
+ # command line, so you might use these command line statements:
338
+ #
339
+ # # all tests
340
+ # % rake test all=true
341
+ #
342
+ # # custom subset tests
343
+ # % rake test custom=true
344
+ #
345
+ # # just test_one
346
+ # % rake test custom_test=test_one
347
+ #
306
348
  def subset_test(type, skip=type[0..0].downcase)
307
349
  type = type.upcase
308
350
  type_test = "#{type}_TEST"
309
351
  if run_subset?(type) || env(type_test)
310
- if match_regexp?(type_test, method_name)
352
+ if match_regexp?(type_test, method_name.to_s)
311
353
  yield
312
354
  else
313
355
  print skip
@@ -328,9 +370,8 @@ module Tap
328
370
  end
329
371
 
330
372
  # Declares a subset_test for the ENV variable 'BENCHMARK'. If run,
331
- # benchmark_test sets up benchmarking using the bm method using the
332
- # input length and block. As a result you MUST 'include Benchmark'
333
- # in the test class. Prints 'b' if the test is not run.
373
+ # benchmark_test sets up benchmarking using the Benchmark.bm method
374
+ # using the input length and block. Prints 'b' if the test is not run.
334
375
  #
335
376
  # include Benchmark
336
377
  # def test_speed
@@ -340,7 +381,7 @@ module Tap
340
381
  subset_test("BENCHMARK") do
341
382
  puts
342
383
  puts method_name
343
- bm(length, &block)
384
+ Benchmark.bm(length, &block)
344
385
  end
345
386
  end
346
387
 
@@ -0,0 +1,271 @@
1
+ require 'test/unit'
2
+ require 'tap/test/file_methods'
3
+ require 'tap/test/subset_methods'
4
+
5
+ module Test # :nodoc:
6
+ module Unit # :nodoc:
7
+ class TestCase
8
+ class << self
9
+ # Causes a unit test to act as a tap test -- resulting in the following:
10
+ # - setup using acts_as_file_test
11
+ # - inclusion of Tap::Test::SubsetMethods
12
+ # - inclusion of Tap::Test::InstanceMethods
13
+ #
14
+ # Note: Unless otherwise specified, <tt>acts_as_tap_test</tt> infers a root directory
15
+ # based on the calling file. Be sure to specify the root directory explicitly
16
+ # if you call acts_as_file_test from a file that is NOT meant to be test file.
17
+ def acts_as_tap_test(options={})
18
+ options = {:root => file_test_root}.merge(options.symbolize_keys)
19
+ acts_as_file_test(options)
20
+
21
+ include Tap::Test::SubsetMethods
22
+ include Tap::Test::TapMethods
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ module Tap
31
+ module Test
32
+
33
+ # Used during check_audit to hold the sources and values of an audit
34
+ # in the correct order. Oriented so that the next value to be checked
35
+ # is at the top of the stack. Used internally.
36
+ class AuditStack # :nodoc:
37
+ attr_reader :test
38
+
39
+ def initialize(test)
40
+ @test = test
41
+ @stack = []
42
+ end
43
+
44
+ def load_audit(values)
45
+ [values._sources, values._values].transpose.reverse_each do |sv|
46
+ load(*sv)
47
+ end
48
+ end
49
+
50
+ def load(source, value)
51
+ @stack.unshift [source, value]
52
+ end
53
+
54
+ def next
55
+ @stack.shift
56
+ end
57
+ end
58
+
59
+ # Tap-specific testing methods to help with testing Tasks, such as the
60
+ # checking of audits and test-specific modification of application
61
+ # configuration.
62
+ module TapMethods
63
+
64
+ # Returns the test-method-specific application.
65
+ attr_reader :app
66
+
67
+ # Setup creates a test-method-specific application that is initialized
68
+ # to the method_root, and uses the directories and absolute paths from
69
+ # trs (the test root structure, see Tap::Test::FileMethods).
70
+ #
71
+ # Also makes sure Tap::App.instance returns the test method app.
72
+ def setup
73
+ super
74
+ @app = Tap::App.new(
75
+ :root => method_root,
76
+ :directories => trs.directories,
77
+ :absolute_paths => trs.absolute_paths)
78
+ Tap::App.instance = @app
79
+ end
80
+
81
+ #
82
+ # audit test methods
83
+ #
84
+
85
+ # Used to define expected audits in Tap::Test::TapMethods#assert_audit_equal
86
+ class ExpAudit < Array
87
+ end
88
+
89
+ # Used to define merged audit trails in Tap::Test::TapMethods#assert_audit_equal
90
+ class ExpMerge < Array
91
+ end
92
+
93
+ # Asserts that an array of audits are all equal, basically feeding
94
+ # each pair of audits to assert_audit_equal.
95
+ def assert_audits_equal(expected, audits)
96
+ each_pair_with_index(expected, audits) do |exp, audit, index|
97
+ assert_audit_equal(exp, audit, [index])
98
+ end
99
+ end
100
+
101
+ # Asserts that an audit is as expected. The expected audit should
102
+ # be an ExpAudit (just a subclass of Array) that records the sources
103
+ # and the values at each step in the audit trail. Proc objects can
104
+ # be provided in place of expected records that are hard or impossible
105
+ # to provide directly; the Proc will be used to validate the actual
106
+ # record. Merges must be marked in the ExpAudit using ExpMerge.
107
+ #
108
+ # Simple assertion:
109
+ #
110
+ # a = Tap::Support::Audit.new
111
+ # a._record(:a, 'a')
112
+ # a._record(:b, 'b')
113
+ #
114
+ # e = ExpAudit[[:a, 'a'], [:b, 'b']]
115
+ # assert_audit_equal(e, a)
116
+ #
117
+ # Assertion validating a record with a Proc (any number of
118
+ # records can be validated with a Proc):
119
+ #
120
+ # a = Tap::Support::Audit.new
121
+ # a._record(:a, 'a')
122
+ # a._record(:b, 'b')
123
+ #
124
+ # e = ExpAudit[
125
+ # lambda {|source, value| source == :a && value == 'a'},
126
+ # [:b, 'b']]
127
+ # assert_audit_equal(e, a)
128
+ #
129
+ # Assertion with merge:
130
+ #
131
+ # a = Tap::Support::Audit.new
132
+ # a._record(:a, 'a')
133
+ # a._record(:b, 'b')
134
+ #
135
+ # b = Tap::Support::Audit.new
136
+ # b._record(:c, 'c')
137
+ # b._record(:d, 'd')
138
+ #
139
+ # c = Tap::Support::Audit.merge(a,b)
140
+ # c._record(:e, 'e')
141
+ # c._record(:f, 'f')
142
+ #
143
+ # ea = ExpAudit[[:a, "a"], [:b, "b"]]
144
+ # eb = ExpAudit[[:c, "c"], [:d, "d"]]
145
+ # e = ExpAudit[ExpMerge[ea, eb], [:e, "e"], [:f, "f"]]
146
+ #
147
+ # assert_audit_equal(e, c)
148
+ #
149
+ # When assert_audit_equal fails, a string of indicies is provided
150
+ # to help locate which record was unequal. For instance in the last
151
+ # example, say we used:
152
+ #
153
+ # ea = ExpAudit[[:a, "a"], [:b, "FLUNK"]]
154
+ # eb = ExpAudit[[:c, "c"], [:d, "d"]]
155
+ # e = ExpAudit[ExpMerge[ea, eb], [:e, "e"], [:f, "f"]]
156
+ #
157
+ # The failure message will read something like 'unequal record 0:0:1'
158
+ # indicating it was e[0][0][1] that failed. Working through it,
159
+ # remembering that ExpAudit and ExpMerge are just subclasses of
160
+ # Array:
161
+ #
162
+ # e # => ExpAudit[ExpMerge[ea, eb], [:e, "e"], [:f, "f"]]
163
+ # e[0] # => ExpMerge[ea, eb]
164
+ # e[0][0] # => ExpAudit[[:a, "a"], [:b, "FLUNK"]]
165
+ # e[0][0][1] # => [:b, "FLUNK"]
166
+ #
167
+ def assert_audit_equal(expected, audit, nesting=[])
168
+ actual = audit._collect_records {|source, value| [source, value]}
169
+ assert_audit_records_equal(expected, actual, nesting)
170
+ end
171
+
172
+ private
173
+
174
+ def assert_audit_records_equal(expected, actual, nesting=[])
175
+ assert_equal ExpAudit, expected.class
176
+ assert_equal expected.length, actual.length, "unequal number of records"
177
+
178
+ expected.each_with_index do |exp_record, i|
179
+ case exp_record
180
+ when ExpMerge
181
+ exp_record.each_with_index do |exp_audit, j|
182
+ assert_audit_records_equal(exp_audit, actual[i][j], nesting + [i,j])
183
+ end
184
+ when Proc
185
+ assert exp_record.call(*actual[i]), "unconfirmed record #{(nesting + [i]).join(':')}"
186
+ else
187
+ assert_equal exp_record, actual[i], "unequal record #{(nesting + [i]).join(':')}"
188
+ end
189
+ end
190
+ end
191
+
192
+ public
193
+
194
+ #
195
+ # application methods
196
+ #
197
+
198
+ # Applies the input options to the specified app for the duration
199
+ # of the block. Unless merge_with_existing is false, the input
200
+ # options will be merged with the existing options; otherwise
201
+ # the app options will be reconfigured to just the inputs.
202
+ #
203
+ # For convenience, with_options is setup such that options
204
+ # {:debug => true, :quiet => true} are implicitly specified.
205
+ # Overrides are respected.
206
+ #
207
+ # app = Tap::App.new(:options => {:one => 1, :two => 2})
208
+ #
209
+ # with_options({:one => 'one', :quiet => false}, app) do
210
+ # app.options.marshal_dump # => {:one => 'one', :two => 2, :debug => true, :quiet => false}
211
+ # end
212
+ #
213
+ # app.options.marshal_dump # => {:one => 1, :two => 2}
214
+ #
215
+ def with_options(options={}, app=self.app, merge_with_existing=true, &block)
216
+ app_config = {:options => options}
217
+ with_config(app_config, app, merge_with_existing, &block)
218
+ end
219
+
220
+ # Applies the input configurations to the specified app for the
221
+ # duration of the block. Unless merge_with_existing is false,
222
+ # the input configurations will be merged with the existing
223
+ # configurations; otherwise the app will be reconfigured to
224
+ # using the inputs as specified.
225
+ #
226
+ # For convenience, with_config is setup such that options
227
+ # {:debug => true, :quiet => true} are implicitly specified.
228
+ # Overrides are respected.
229
+ #
230
+ # app = Tap::App.new(:directories => {:dir => 'dir', :alt => 'alt_dir'})
231
+ # tmp_config = {
232
+ # :directories => {:alt => 'another', :new => 'new_dir'},
233
+ # :options => {:one => 1, :quiet => false}}
234
+ #
235
+ # with_config(tmp_config, app) do
236
+ # app.directories # => {:dir => 'dir', :alt => 'another', :new => 'new_dir'}
237
+ # app.options.marshal_dump # => {:one => 1, :debug => true, :quiet => false}
238
+ # end
239
+ #
240
+ # app.directories # => {:dir => 'dir', :alt => 'alt_dir'}
241
+ # app.options.marshal_dump # => {}
242
+ #
243
+ def with_config(app_config={}, app=self.app, merge_with_existing=true, default_options={:debug => true, :quiet => true}, &block)
244
+ begin
245
+ hold = app.config
246
+
247
+ app_config[:options] = default_options.merge(app_config[:options] || {})
248
+ if merge_with_existing
249
+ hold.each_pair do |key, value|
250
+ next unless app_config.has_key?(key)
251
+ next unless value.kind_of?(Hash)
252
+
253
+ app_config[key] = value.merge(app_config[key])
254
+ end
255
+ end
256
+
257
+ app.reconfigure(app_config)
258
+
259
+ yield block if block_given?
260
+ ensure
261
+ app.reconfigure(hold)
262
+ end
263
+ end
264
+
265
+ end
266
+ end
267
+ end
268
+
269
+
270
+
271
+