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
@@ -0,0 +1,420 @@
1
+ require 'test/unit'
2
+ require 'benchmark'
3
+ require 'pp'
4
+ require 'tap/test/env_vars'
5
+
6
+ module Test # :nodoc:
7
+ module Unit # :nodoc:
8
+ class TestCase
9
+ class << self
10
+ include Tap::Test::EnvVars
11
+
12
+ # Passes conditions to subclass
13
+ def inherited(subclass) # :nodoc:
14
+ super
15
+ subclass_conditions = conditions.inject({}) do |memo, (key, value)|
16
+ memo.update(key => (value.dup rescue value))
17
+ end
18
+ subclass.instance_variable_set(:@conditions, subclass_conditions)
19
+ subclass.instance_variable_set(:@run_test_suite, nil)
20
+ subclass.instance_variable_set(:@skip_messages, [])
21
+
22
+ # subclass_inputs = prompt_inputs.inject({}) do |memo, (key, value)|
23
+ # memo.update(key => (value.dup rescue value))
24
+ # end
25
+ # subclass.instance_variable_set("@prompt_inputs", subclass_inputs)
26
+ end
27
+
28
+ # Experimental -- The idea is to provide a way to prompt once for inputs
29
+ # that get used multiple times in a test. Perhaps create accessors
30
+ # automatically?
31
+
32
+ # def prompt_inputs
33
+ # @prompt_inputs ||= {}
34
+ # end
35
+
36
+ # def require_inputs(*keys, &block)
37
+ # if run_subset?("PROMPT")
38
+ # puts "\n#{name} requires inputs -- Enter values or 'skip'."
39
+
40
+ # argv = ARGV.dup
41
+ # begin
42
+ # ARGV.clear
43
+ # keys.collect do |key|
44
+ # print "#{key}: "
45
+ # value = gets.strip
46
+ # if value =~ /skip/i
47
+ # skip_test "missing inputs"
48
+ # break
49
+ # end
50
+ # prompt_inputs[key] = value
51
+ # end
52
+ # ensure
53
+ # ARGV.clear
54
+ # ARGV.concat(argv)
55
+ # end
56
+ # else
57
+ # skip_test "prompt test"
58
+ # end
59
+ # end
60
+
61
+ #
62
+ # conditions
63
+ #
64
+
65
+ # A hash of defined conditions
66
+ def conditions
67
+ @conditions ||= {}
68
+ end
69
+
70
+ # Defines a condition block and associated message.
71
+ # Raises an error if no condition block is given.
72
+ def condition(name, msg=nil, &block)
73
+ raise "no condition block given" unless block_given?
74
+ conditions[name.to_sym] = [msg, block]
75
+ end
76
+
77
+ # Returns true if the all blocks for the named conditions return true.
78
+ #
79
+ # condition(:is_true) { true }
80
+ # condition(:is_false) { false }
81
+ # satisfied?(:is_true) # => true
82
+ # satisfied?(:is_true, :is_false) # => false
83
+ #
84
+ def satisfied?(*conditions)
85
+ unsatisfied_conditions(*conditions).empty?
86
+ end
87
+
88
+ # Returns an array of the unsatified conditions. Raises
89
+ # an error if the named condition has not been defined.
90
+ #
91
+ # condition(:is_true) { true }
92
+ # condition(:is_false) { false }
93
+ # unsatisfied_conditions(:is_true, :is_false) # => [:is_false]
94
+ #
95
+ def unsatisfied_conditions(*conditions)
96
+ conditions = self.conditions.keys if conditions.empty?
97
+ unsatified = []
98
+ conditions.each do |condition|
99
+ raise "Unknown condition: #{condition}" unless self.conditions.has_key?(condition)
100
+ unsatified << condition unless (self.conditions[condition.to_sym].last.call() ? true : false)
101
+ end
102
+ unsatified
103
+ end
104
+
105
+ # Returns true if RUBY_PLATFORM matches one of the specfied
106
+ # platforms. Use the prefix 'non_' to specify any plaform
107
+ # except the specified platform (ex: 'non_mswin'). Returns
108
+ # true if no platforms are specified.
109
+ #
110
+ # Some common platforms:
111
+ # mswin Windows
112
+ # darwin Mac
113
+ def match_platform?(*platforms)
114
+ platforms.each do |platform|
115
+ platform.to_s =~ /^(non_)?(.*)/
116
+
117
+ non = true if $1
118
+ match_platform = !RUBY_PLATFORM.index($2).nil?
119
+ return false unless (non && !match_platform) || (!non && match_platform)
120
+ end
121
+
122
+ true
123
+ end
124
+
125
+ # Returns true if the subset type or 'ALL' is specified in ENV
126
+ def run_subset?(type)
127
+ env_true?(type) || env_true?("ALL") ? true : false
128
+ end
129
+
130
+ #
131
+ # Methods for skipping a test suite
132
+ #
133
+ attr_accessor :run_test_suite
134
+
135
+ # Returns run_test_suite, or true if run_test_suite is not set.
136
+ def run_test_suite?
137
+ run_test_suite.nil? ? true : run_test_suite
138
+ end
139
+
140
+ # Causes a test suite to be skipped. If a message is given, it will
141
+ # print and notify the user the test suite has been skipped.
142
+ def skip_test(msg=nil)
143
+ self.run_test_suite = false
144
+
145
+ # experimental -- perhaps use this so that a test can be skipped
146
+ # for multiple reasons?
147
+ @skip_messages << msg unless msg.nil?
148
+ end
149
+
150
+ alias :original_suite :suite
151
+
152
+ # Modifies the default suite method to include/exclude tests based on platform.
153
+ def suite # :nodoc:
154
+ if run_test_suite?
155
+ original_suite
156
+ else
157
+ puts "Skipping #{name}: #{@skip_messages.join(', ')}" unless @skip_messages.empty?
158
+ Test::Unit::TestSuite.new(name)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ module Tap
167
+ module Test
168
+
169
+ # Ideally you always run all of your tests and they all run and pass everywhere. In
170
+ # practice it's useful to suppress the execution of some tests -- long running tests,
171
+ # tests specific for a given platform, or tests that depend on some condition, such
172
+ # as the version of some optional third-party software your code interacts with.
173
+ #
174
+ # SubsetMethods extends TestCase with methods for defining conditions that can be
175
+ # used to conditionally perform some action, or skip a test suite entirely. When
176
+ # you include SubsetMethods within a specific TestCase, additional methods are
177
+ # made available for filtering tests.
178
+ #
179
+ # require 'tap/test/subset_methods'
180
+ # class Test::Unit::TestCase
181
+ # # only true if running on windows
182
+ # condition(:windows) { match_platform?('mswin') }
183
+ #
184
+ # # only true if running on anything but windows
185
+ # condition(:non_windows) { match_platform?('non_mswin') }
186
+ # end
187
+ #
188
+ # class WindowsOnlyTest < Test::Unit::TestCase
189
+ # skip_test unless satisfied?(:windows)
190
+ # end
191
+ #
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.
194
+ #
195
+ # class RunOnlyAFewTest < Test::Unit::TestCase
196
+ # include SubsetMethods
197
+ #
198
+ # def test_runs_all_the_time
199
+ # assert true
200
+ # end
201
+ #
202
+ # def test_runs_only_if_non_windows_condition_is_true
203
+ # condition_test(:non_windows) { assert true }
204
+ # end
205
+ # end
206
+ #
207
+ # def test_runs_only_when_ENV_variable_EXTENDED_is_true
208
+ # extended_test { assert true }
209
+ # end
210
+ #
211
+ # def test_runs_only_when_ENV_variable_BENCHMARK_is_true
212
+ # benchmark_test do |x|
213
+ # x.report("init speed") { 10000.times { Object.new } }
214
+ # end
215
+ # end
216
+ #
217
+ # def test_runs_only_when_ENV_variable_CUSTOM_is_true
218
+ # subset_test('CUSTOM') { assert true }
219
+ # end
220
+ # end
221
+ #
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:
225
+ #
226
+ # % rake test EXTENDED=true
227
+ # % rake test BENCHMARK=true
228
+ #
229
+ # Since tap can run rake tasks as well, these are equivalent:
230
+ #
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
246
+ #
247
+ # See {Test::Unit::TestCase}[link:classes/Test/Unit/TestCase.html] for documentation of the class methods added by SubsetMethods
248
+ module SubsetMethods
249
+ include Tap::Test::EnvVars
250
+
251
+ def satisfied?(*conditions)
252
+ self.class.satisfied?(*conditions)
253
+ end
254
+
255
+ # Returns true if the subset type or 'ALL' is specified in ENV
256
+ def run_subset?(type)
257
+ self.class.run_subset?(type)
258
+ end
259
+
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)
264
+ return true if env_true?("ALL")
265
+ return default unless env(type)
266
+
267
+ str =~ Regexp.new(env(type)) ? true : false
268
+ end
269
+
270
+ # Platform-specific test. Useful for specifying test that should only
271
+ # be run on a subset of platforms. Prints ' ' if the test is not run.
272
+ #
273
+ # def test_only_on_windows
274
+ # platform_test('mswin') { ... }
275
+ # end
276
+ #
277
+ # See TestCase#match_platform? for matching details.
278
+ def platform_test(*platforms)
279
+ if self.class.match_platform?(*platforms)
280
+ yield
281
+ else
282
+ print ' '
283
+ end
284
+ end
285
+
286
+ # Conditonal test. Only runs if the named conditions are satisfied.
287
+ # If no conditons are explicitly set, only runs if all conditions
288
+ # are satisfied.
289
+ #
290
+ # condition(:is_true) { true }
291
+ # condition(:is_false) { false }
292
+ #
293
+ # def test_only_if_true_is_satisfied
294
+ # condition_test(:is_true) { # runs }
295
+ # end
296
+ #
297
+ # def test_only_if_all_conditions_are_satisfied
298
+ # condition_test { # does not run }
299
+ # end
300
+ #
301
+ # See TestCase#condition for more details.
302
+ def condition_test(*conditions)
303
+ unsatisfied_conditions = self.class.unsatisfied_conditions(*conditions)
304
+ if unsatisfied_conditions.empty?
305
+ yield
306
+ else
307
+ print ' '
308
+ end
309
+ end
310
+
311
+ # Basic method for a subset test. The provided block will run if:
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
315
+ #
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
+ #
348
+ def subset_test(type, skip=type[0..0].downcase)
349
+ type = type.upcase
350
+ type_test = "#{type}_TEST"
351
+ if run_subset?(type) || env(type_test)
352
+ if match_regexp?(type_test, method_name.to_s)
353
+ yield
354
+ else
355
+ print skip
356
+ end
357
+ else
358
+ print skip
359
+ end
360
+ end
361
+
362
+ # Declares a subset_test for the ENV variable 'EXTENDED'.
363
+ # Prints 'x' if the test is not run.
364
+ #
365
+ # def test_some_long_process
366
+ # extended_test { ... }
367
+ # end
368
+ def extended_test(&block)
369
+ subset_test("EXTENDED", "x", &block)
370
+ end
371
+
372
+ # Declares a subset_test for the ENV variable 'BENCHMARK'. If 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.
375
+ #
376
+ # include Benchmark
377
+ # def test_speed
378
+ # benchmark_test(10) {|x| ... }
379
+ # end
380
+ def benchmark_test(length=10, &block)
381
+ subset_test("BENCHMARK") do
382
+ puts
383
+ puts method_name
384
+ Benchmark.bm(length, &block)
385
+ end
386
+ end
387
+
388
+ # Declares a subset_test for the ENV variable 'PROMPT'. When run, prompts
389
+ # the user for each input specified in array. Inputs will then be passed
390
+ # as a hash to the block. Prints 'p' unless run.
391
+ #
392
+ # def test_requiring_inputs
393
+ # prompt_test(:a, :b, :c) {|a, b, c| ... }
394
+ # end
395
+ #
396
+ # If run, the command line prompt will be like the following:
397
+ #
398
+ # test_requiring_inputs: Enter values or 'skip'
399
+ # a: avalue
400
+ # b: bvalue
401
+ # c: cvalue
402
+ #
403
+ # The block recieves ['avalue', 'bvalue', 'cvalue'].
404
+ def prompt_test(*keys, &block)
405
+ subset_test("PROMPT", "p") do
406
+ puts "\n#{method_name} -- Enter values or 'skip'."
407
+
408
+ values = keys.collect do |key|
409
+ print "#{key}: "
410
+ value = gets.strip
411
+ flunk "skipped test" if value =~ /skip/i
412
+ value
413
+ end
414
+
415
+ yield(*values)
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end
@@ -0,0 +1,237 @@
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 = options.inject({:root => file_test_root}) do |hash, (key, value)|
19
+ hash[key.to_sym || key] = value
20
+ hash
21
+ end
22
+ acts_as_file_test(options)
23
+
24
+ include Tap::Test::SubsetMethods
25
+ include Tap::Test::TapMethods
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ module Tap
34
+ module Test
35
+
36
+ # Used during check_audit to hold the sources and values of an audit
37
+ # in the correct order. Oriented so that the next value to be checked
38
+ # is at the top of the stack. Used internally.
39
+ class AuditStack # :nodoc:
40
+ attr_reader :test
41
+
42
+ def initialize(test)
43
+ @test = test
44
+ @stack = []
45
+ end
46
+
47
+ def load_audit(values)
48
+ [values._sources, values._values].transpose.reverse_each do |sv|
49
+ load(*sv)
50
+ end
51
+ end
52
+
53
+ def load(source, value)
54
+ @stack.unshift [source, value]
55
+ end
56
+
57
+ def next
58
+ @stack.shift
59
+ end
60
+ end
61
+
62
+ # Tap-specific testing methods to help with testing Tasks, such as the
63
+ # checking of audits and test-specific modification of application
64
+ # configuration.
65
+ #
66
+ # === Class Methods
67
+ #
68
+ # See {Test::Unit::TestCase}[link:classes/Test/Unit/TestCase.html] for documentation of the class methods added by TapMethods.
69
+ module TapMethods
70
+
71
+ # Returns the test-method-specific application.
72
+ attr_reader :app
73
+
74
+ # Setup creates a test-method-specific application that is initialized
75
+ # to the method_root, and uses the directories and absolute paths from
76
+ # trs (the test root structure, see Tap::Test::FileMethods).
77
+ #
78
+ # Also makes sure Tap::App.instance returns the test method app.
79
+ def setup
80
+ super
81
+ @app = Tap::App.new(app_config)
82
+ Tap::App.instance = @app
83
+ end
84
+
85
+ #
86
+ # audit test methods
87
+ #
88
+
89
+ # Used to define expected audits in Tap::Test::TapMethods#assert_audit_equal
90
+ class ExpAudit < Array
91
+ end
92
+
93
+ # Used to define merged audit trails in Tap::Test::TapMethods#assert_audit_equal
94
+ class ExpMerge < Array
95
+ end
96
+
97
+ # Asserts that an array of audits are all equal, basically feeding
98
+ # each pair of audits to assert_audit_equal.
99
+ def assert_audits_equal(expected, audits)
100
+ each_pair_with_index(expected, audits) do |exp, audit, index|
101
+ assert_audit_equal(exp, audit, [index])
102
+ end
103
+ end
104
+
105
+ # Asserts that an audit is as expected. The expected audit should
106
+ # be an ExpAudit (just a subclass of Array) that records the sources
107
+ # and the values at each step in the audit trail. Proc objects can
108
+ # be provided in place of expected records that are hard or impossible
109
+ # to provide directly; the Proc will be used to validate the actual
110
+ # record. Merges must be marked in the ExpAudit using ExpMerge.
111
+ #
112
+ # Simple assertion:
113
+ #
114
+ # a = Tap::Support::Audit.new
115
+ # a._record(:a, 'a')
116
+ # a._record(:b, 'b')
117
+ #
118
+ # e = ExpAudit[[:a, 'a'], [:b, 'b']]
119
+ # assert_audit_equal(e, a)
120
+ #
121
+ # Assertion validating a record with a Proc (any number of
122
+ # records can be validated with a Proc):
123
+ #
124
+ # a = Tap::Support::Audit.new
125
+ # a._record(:a, 'a')
126
+ # a._record(:b, 'b')
127
+ #
128
+ # e = ExpAudit[
129
+ # lambda {|source, value| source == :a && value == 'a'},
130
+ # [:b, 'b']]
131
+ # assert_audit_equal(e, a)
132
+ #
133
+ # Assertion with merge:
134
+ #
135
+ # a = Tap::Support::Audit.new
136
+ # a._record(:a, 'a')
137
+ # a._record(:b, 'b')
138
+ #
139
+ # b = Tap::Support::Audit.new
140
+ # b._record(:c, 'c')
141
+ # b._record(:d, 'd')
142
+ #
143
+ # c = Tap::Support::Audit.merge(a,b)
144
+ # c._record(:e, 'e')
145
+ # c._record(:f, 'f')
146
+ #
147
+ # ea = ExpAudit[[:a, "a"], [:b, "b"]]
148
+ # eb = ExpAudit[[:c, "c"], [:d, "d"]]
149
+ # e = ExpAudit[ExpMerge[ea, eb], [:e, "e"], [:f, "f"]]
150
+ #
151
+ # assert_audit_equal(e, c)
152
+ #
153
+ # When assert_audit_equal fails, a string of indicies is provided
154
+ # to help locate which record was unequal. For instance in the last
155
+ # example, say we used:
156
+ #
157
+ # ea = ExpAudit[[:a, "a"], [:b, "FLUNK"]]
158
+ # eb = ExpAudit[[:c, "c"], [:d, "d"]]
159
+ # e = ExpAudit[ExpMerge[ea, eb], [:e, "e"], [:f, "f"]]
160
+ #
161
+ # The failure message will read something like 'unequal record 0:0:1'
162
+ # indicating it was e[0][0][1] that failed. Working through it,
163
+ # remembering that ExpAudit and ExpMerge are just subclasses of
164
+ # Array:
165
+ #
166
+ # e # => ExpAudit[ExpMerge[ea, eb], [:e, "e"], [:f, "f"]]
167
+ # e[0] # => ExpMerge[ea, eb]
168
+ # e[0][0] # => ExpAudit[[:a, "a"], [:b, "FLUNK"]]
169
+ # e[0][0][1] # => [:b, "FLUNK"]
170
+ #
171
+ def assert_audit_equal(expected, audit, nesting=[])
172
+ actual = audit._collect_records {|source, value| [source, value]}
173
+ assert_audit_records_equal(expected, actual, nesting)
174
+ end
175
+
176
+ private
177
+
178
+ def assert_audit_records_equal(expected, actual, nesting=[])
179
+ assert_equal ExpAudit, expected.class
180
+ assert_equal expected.length, actual.length, "unequal number of records"
181
+
182
+ expected.each_with_index do |exp_record, i|
183
+ case exp_record
184
+ when ExpMerge
185
+ exp_record.each_with_index do |exp_audit, j|
186
+ assert_audit_records_equal(exp_audit, actual[i][j], nesting + [i,j])
187
+ end
188
+ when Proc
189
+ assert exp_record.call(*actual[i]), "unconfirmed record #{(nesting + [i]).join(':')}"
190
+ else
191
+ assert_equal exp_record, actual[i], "unequal record #{(nesting + [i]).join(':')}"
192
+ end
193
+ end
194
+ end
195
+
196
+ public
197
+
198
+ # The configurations used to initialize self.app
199
+ def app_config
200
+ { :root => method_root,
201
+ :directories => trs.directories,
202
+ :absolute_paths => trs.absolute_paths,
203
+ :quiet => true,
204
+ :debug => true}
205
+ end
206
+
207
+ # Reconfigures app with the input configurations for the
208
+ # duration of the block.
209
+ #
210
+ # app = Tap::App.new(:quiet => true, :debug => false)
211
+ # with_config({:quiet => false}, app) do
212
+ # app.quiet # => false
213
+ # app.debug # => false
214
+ # end
215
+ #
216
+ # app.quiet # => true
217
+ # app.debug # => false
218
+ #
219
+ def with_config(config={}, app=self.app, &block)
220
+ begin
221
+ hold = app.config.to_hash
222
+
223
+ app.reconfigure(config)
224
+
225
+ yield block if block_given?
226
+ ensure
227
+ app.send(:initialize_config, hold)
228
+ end
229
+ end
230
+
231
+ end
232
+ end
233
+ end
234
+
235
+
236
+
237
+