tap 0.7.9 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +28 -0
- data/MIT-LICENSE +1 -1
- data/README +71 -43
- data/Rakefile +81 -64
- data/Tutorial +235 -0
- data/bin/tap +80 -44
- data/lib/tap.rb +41 -12
- data/lib/tap/app.rb +243 -246
- data/lib/tap/file_task.rb +357 -118
- data/lib/tap/generator.rb +88 -29
- data/lib/tap/generator/generators/config/config_generator.rb +4 -2
- data/lib/tap/generator/generators/config/templates/config.erb +1 -2
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +3 -18
- data/lib/tap/generator/generators/file_task/templates/task.erb +22 -15
- data/lib/tap/generator/generators/file_task/templates/test.erb +13 -2
- data/{test/test/inference_methods/test_assert_files_exist/input/input_1.txt → lib/tap/generator/generators/generator/USAGE} +0 -0
- data/lib/tap/generator/generators/generator/generator_generator.rb +21 -0
- data/lib/tap/generator/generators/generator/templates/generator.erb +23 -0
- data/lib/tap/generator/generators/generator/templates/usage.erb +1 -0
- data/{test/test/inference_methods/test_assert_files_exist/input/input_2.txt → lib/tap/generator/generators/package/USAGE} +0 -0
- data/lib/tap/generator/generators/package/package_generator.rb +38 -0
- data/lib/tap/generator/generators/package/templates/package.erb +186 -0
- data/lib/tap/generator/generators/root/root_generator.rb +14 -9
- data/lib/tap/generator/generators/root/templates/Rakefile +20 -14
- data/{test/test/inference_methods/test_infer_glob/expected/file.yml → lib/tap/generator/generators/root/templates/ReadMe.txt} +0 -0
- data/lib/tap/generator/generators/root/templates/tap.yml +82 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -1
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +2 -1
- data/{test/test/inference_methods/test_infer_glob/expected/file_1.txt → lib/tap/generator/generators/script/USAGE} +0 -0
- data/lib/tap/generator/generators/script/script_generator.rb +17 -0
- data/lib/tap/generator/generators/script/templates/script.erb +42 -0
- data/lib/tap/generator/generators/task/task_generator.rb +1 -1
- data/lib/tap/generator/generators/task/templates/task.erb +24 -16
- data/lib/tap/generator/generators/task/templates/test.erb +13 -17
- data/lib/tap/generator/generators/workflow/templates/task.erb +10 -10
- data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +3 -18
- data/lib/tap/root.rb +108 -146
- data/lib/tap/script.rb +362 -0
- data/lib/tap/script/console.rb +28 -0
- data/lib/tap/script/destroy.rb +13 -1
- data/lib/tap/script/generate.rb +13 -1
- data/lib/tap/script/run.rb +100 -57
- data/lib/tap/support/batch_queue.rb +0 -3
- data/lib/tap/support/logger.rb +6 -3
- data/lib/tap/support/rake.rb +54 -0
- data/lib/tap/support/task_configuration.rb +169 -0
- data/lib/tap/support/tdoc.rb +198 -0
- data/lib/tap/support/tdoc/config_attr.rb +338 -0
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
- data/lib/tap/support/versions.rb +33 -1
- data/lib/tap/task.rb +339 -227
- data/lib/tap/test.rb +86 -128
- data/lib/tap/test/env_vars.rb +16 -5
- data/lib/tap/test/file_methods.rb +373 -0
- data/lib/tap/test/subset_methods.rb +299 -180
- data/lib/tap/version.rb +2 -1
- data/lib/tap/workflow.rb +2 -0
- data/test/app/lib/app_test_task.rb +1 -0
- data/test/app_test.rb +327 -83
- data/test/check/binding_eval.rb +23 -0
- data/test/check/define_method_check.rb +22 -0
- data/test/check/dependencies_check.rb +175 -0
- data/test/check/inheritance_check.rb +22 -0
- data/test/file_task_test.rb +524 -291
- data/test/{test/inference_methods/test_infer_glob/expected/file_2.txt → root/glob/one.txt} +0 -0
- data/test/root/glob/two.txt +0 -0
- data/test/root_test.rb +330 -262
- data/test/script_test.rb +194 -0
- data/test/support/audit_test.rb +5 -2
- data/test/support/combinator_test.rb +10 -10
- data/test/support/rake_test.rb +35 -0
- data/test/support/task_configuration_test.rb +272 -0
- data/test/support/tdoc_test.rb +363 -0
- data/test/support/templater_test.rb +2 -2
- data/test/support/versions_test.rb +32 -0
- data/test/tap_test_helper.rb +39 -0
- data/test/task_base_test.rb +115 -0
- data/test/task_class_test.rb +56 -4
- data/test/task_execute_test.rb +29 -0
- data/test/task_test.rb +89 -70
- data/test/test/env_vars_test.rb +48 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/folder/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/folder/file.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/two.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/two.txt +1 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_2.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_2.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_2.yml +0 -0
- data/test/test/file_methods_test.rb +204 -0
- data/test/test/subset_methods_test.rb +93 -33
- data/test/test/test_assert_expected_result_files/expected/task/name/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/expected/task/name/b.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/b.txt +1 -0
- data/test/test/test_file_task_test/expected/one.txt +1 -0
- data/test/test/test_file_task_test/expected/two.txt +1 -0
- data/test/test/test_file_task_test/input/one.txt +1 -0
- data/test/test/test_file_task_test/input/two.txt +1 -0
- data/test/test_test.rb +143 -3
- data/test/workflow_test.rb +2 -0
- data/vendor/rails_generator.rb +56 -0
- data/vendor/rails_generator/base.rb +263 -0
- data/vendor/rails_generator/commands.rb +581 -0
- data/vendor/rails_generator/generated_attribute.rb +42 -0
- data/vendor/rails_generator/lookup.rb +209 -0
- data/vendor/rails_generator/manifest.rb +53 -0
- data/vendor/rails_generator/options.rb +143 -0
- data/vendor/rails_generator/scripts.rb +83 -0
- data/vendor/rails_generator/scripts/destroy.rb +7 -0
- data/vendor/rails_generator/scripts/generate.rb +7 -0
- data/vendor/rails_generator/scripts/update.rb +12 -0
- data/vendor/rails_generator/simple_logger.rb +46 -0
- data/vendor/rails_generator/spec.rb +44 -0
- metadata +180 -196
- data/lib/tap/generator/generators/root/templates/app.yml +0 -19
- data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +0 -4
- data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +0 -26
- data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +0 -57
- data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +0 -108
- data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +0 -40
- data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +0 -21
- data/lib/tap/generator/generators/root/templates/server/config/environment.rb +0 -60
- data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +0 -5
- data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +0 -53
- data/lib/tap/script/server.rb +0 -12
- data/lib/tap/support/rap.rb +0 -38
- data/lib/tap/test/inference_methods.rb +0 -298
- data/test/task/config/task_with_config.yml +0 -1
- data/test/test/inference_methods_test.rb +0 -311
data/lib/tap/test.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require 'test/unit'
|
|
2
|
-
require 'tap/test/
|
|
2
|
+
require 'tap/test/file_methods'
|
|
3
3
|
require 'tap/test/subset_methods'
|
|
4
4
|
|
|
5
5
|
module Test # :nodoc:
|
|
@@ -7,21 +7,21 @@ module Test # :nodoc:
|
|
|
7
7
|
class TestCase
|
|
8
8
|
class << self
|
|
9
9
|
# Causes a unit test to act as a tap test -- resulting in the following:
|
|
10
|
-
# - setup
|
|
11
|
-
# -
|
|
12
|
-
#
|
|
10
|
+
# - setup using acts_as_file_test
|
|
11
|
+
# - inclusion of Tap::Test::SubsetMethods
|
|
12
|
+
# - inclusion of Tap::Test::InstanceMethods
|
|
13
13
|
#
|
|
14
14
|
# Note: Unless otherwise specified, +acts_as_tap_test+ infers a root directory
|
|
15
|
-
# based on the calling file.
|
|
16
|
-
#
|
|
17
|
-
# explicitly if you call +acts_as_tap_test+ from a file that is NOT your test file.
|
|
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.
|
|
18
17
|
def acts_as_tap_test(options={})
|
|
19
|
-
options = {:root =>
|
|
20
|
-
|
|
18
|
+
options = {:root => file_test_root}.merge(options.symbolize_keys)
|
|
19
|
+
acts_as_file_test(options)
|
|
21
20
|
|
|
22
21
|
include Tap::Test::SubsetMethods
|
|
23
22
|
include Tap::Test::InstanceMethods
|
|
24
23
|
end
|
|
24
|
+
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -29,13 +29,13 @@ end
|
|
|
29
29
|
|
|
30
30
|
module Tap
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
# == UNDER CONSTRUCTION
|
|
33
33
|
module Test
|
|
34
34
|
|
|
35
35
|
# Used during check_audit to hold the sources and values of an audit
|
|
36
36
|
# in the correct order. Oriented so that the next value to be checked
|
|
37
37
|
# is at the top of the stack.
|
|
38
|
-
class AuditStack
|
|
38
|
+
class AuditStack # :nodoc:
|
|
39
39
|
attr_reader :test
|
|
40
40
|
|
|
41
41
|
def initialize(test)
|
|
@@ -58,70 +58,21 @@ module Tap
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
module InstanceMethods
|
|
62
|
-
attr_accessor :runlist
|
|
61
|
+
module InstanceMethods # :nodoc:
|
|
63
62
|
|
|
64
63
|
# Setup clears the test using clear_tasks and assures that Tap::App.instance
|
|
65
64
|
# is the test-specific application.
|
|
66
65
|
def setup
|
|
67
66
|
super
|
|
68
67
|
Tap::App.instance = app
|
|
69
|
-
|
|
68
|
+
app.queue.clear
|
|
70
69
|
end
|
|
71
70
|
|
|
72
71
|
# Returns the test-specific application.
|
|
73
72
|
def app
|
|
74
|
-
@app ||= Tap::App.new(:root =>
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Clears all declared tasks, sets the application trace option to false, makes directories (if flagged
|
|
78
|
-
# and as needed), and clears the runlist.
|
|
79
|
-
def clear_runlist
|
|
80
|
-
# clear the attributes
|
|
81
|
-
@runlist = []
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# A tracing procedure. echo adds input to runlist then returns input.
|
|
85
|
-
def echo
|
|
86
|
-
lambda do |task, input|
|
|
87
|
-
@runlist << input
|
|
88
|
-
input
|
|
89
|
-
end
|
|
73
|
+
@app ||= Tap::App.new(:root => trs.root, :directories => trs.directories)
|
|
90
74
|
end
|
|
91
75
|
|
|
92
|
-
# A tracing procedure for numeric inputs. add_one adds the input to
|
|
93
|
-
# runlist then returns input + 1.
|
|
94
|
-
def add_one
|
|
95
|
-
lambda do |task, input|
|
|
96
|
-
@runlist << input
|
|
97
|
-
input += 1
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# A convenience testing method asserting that runlist array is equal to the inputs
|
|
102
|
-
# def assert_runlist(*expected)
|
|
103
|
-
# assert_equal expected, runlist
|
|
104
|
-
# end
|
|
105
|
-
|
|
106
|
-
# A convenience testing method asserting that sorted runlist array is equal to the
|
|
107
|
-
# sorted inputs. Sorting the runlist is unpreferred, because you lose information about
|
|
108
|
-
# the order of items added to the runlist.
|
|
109
|
-
#
|
|
110
|
-
# However, at times there is no good alternative because the order of execution
|
|
111
|
-
# may be determined by a hash.
|
|
112
|
-
# def assert_sorted_runlist(*expected)
|
|
113
|
-
# two-step in case the elements cannot be sorted (as in the case of hashes)
|
|
114
|
-
# found_all = (expected.length == runlist.length)
|
|
115
|
-
# expected.each do |e|
|
|
116
|
-
# break unless found_all
|
|
117
|
-
|
|
118
|
-
# next if runlist.include?(e)
|
|
119
|
-
# found_all = false
|
|
120
|
-
# end
|
|
121
|
-
|
|
122
|
-
# assert found_all, "Expected: #{PP.pp(expected, '')}Was: #{PP.pp(runlist, '')}"
|
|
123
|
-
# end
|
|
124
|
-
|
|
125
76
|
# Recieves a hash of task => expected pairs. Asserts that the last inputs for
|
|
126
77
|
# each task are equal to the expected inputs.
|
|
127
78
|
def assert_inputs(hash)
|
|
@@ -226,80 +177,87 @@ module Tap
|
|
|
226
177
|
end
|
|
227
178
|
end
|
|
228
179
|
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
180
|
+
# Applies the input options to the specified app for the duration
|
|
181
|
+
# of the block. Unless merge_with_existing is false, the input
|
|
182
|
+
# options will be merged with the existing options; otherwise
|
|
183
|
+
# the app options will be reconfigured to just the inputs.
|
|
184
|
+
#
|
|
185
|
+
# app = Tap::App.new(:options => {:one => 1, :two => 2})
|
|
186
|
+
#
|
|
187
|
+
# with_options({:one => 'one'}, app) do
|
|
188
|
+
# app.options.marshal_dump # => {:one => 'one', :two => 2}
|
|
189
|
+
# end
|
|
190
|
+
# app.options.marshal_dump # => {:one => 1, :two => 2}
|
|
191
|
+
#
|
|
192
|
+
def with_options(options, app=self.app, merge_with_existing=true, &block)
|
|
193
|
+
app_config = {:options => options}
|
|
194
|
+
with_config(app_config, app, merge_with_existing, &block)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Applies the input configurations to the specified app for the
|
|
198
|
+
# duration of the block. Unless merge_with_existing is false,
|
|
199
|
+
# the input configurations will be merged with the existing
|
|
200
|
+
# configurations; otherwise the app will be reconfigured to
|
|
201
|
+
# using the inputs as specified.
|
|
202
|
+
#
|
|
203
|
+
# app = Tap::App.new(:directories => {:dir => 'dir', :alt => 'alt_dir'})
|
|
204
|
+
# tmp_config = {
|
|
205
|
+
# :directories => {:alt => 'another', :new => 'new_dir'},
|
|
206
|
+
# :options => {:one => 1}}
|
|
207
|
+
#
|
|
208
|
+
# with_config(tmp_config, app) do
|
|
209
|
+
# app.directories # => {:dir => 'dir', :alt => 'another', :new => 'new_dir'}
|
|
210
|
+
# app.options.marshal_dump # => {:one => 1}
|
|
211
|
+
# end
|
|
212
|
+
# app.directories # => {:dir => 'dir', :alt => 'alt_dir'}
|
|
213
|
+
# app.options.marshal_dump # => {}
|
|
214
|
+
#
|
|
215
|
+
def with_config(app_config, app=self.app, merge_with_existing=true, &block)
|
|
234
216
|
begin
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
217
|
+
hold = app.config
|
|
218
|
+
if merge_with_existing
|
|
219
|
+
hold.each_pair do |key, value|
|
|
220
|
+
next unless app_config.has_key?(key)
|
|
221
|
+
next unless value.kind_of?(Hash)
|
|
222
|
+
|
|
223
|
+
app_config[key] = value.merge(app_config[key])
|
|
224
|
+
end
|
|
238
225
|
end
|
|
239
|
-
|
|
226
|
+
app.reconfigure(app_config)
|
|
227
|
+
|
|
240
228
|
yield block if block_given?
|
|
241
229
|
ensure
|
|
242
|
-
|
|
230
|
+
app.reconfigure(hold)
|
|
243
231
|
end
|
|
244
232
|
end
|
|
245
|
-
|
|
246
|
-
def file_task_test(task, options={})
|
|
247
|
-
make_directory_structure
|
|
248
|
-
|
|
249
|
-
raise "Not a FileTask" unless task.kind_of?(Tap::FileTask)
|
|
250
|
-
|
|
251
|
-
options = {
|
|
252
|
-
:input_files => nil,
|
|
253
|
-
:expected_files => nil,
|
|
254
|
-
:strict => true#,
|
|
255
|
-
#:set_dirname => true
|
|
256
|
-
}.merge(options)
|
|
257
|
-
|
|
258
|
-
input_files = options.delete(:input_files)
|
|
259
|
-
input_files = [input_files] unless input_files.nil? || input_files.kind_of?(Array)
|
|
260
|
-
|
|
261
|
-
expected_files = options.delete(:expected_files)
|
|
262
|
-
expected_files = [expected_files] unless expected_files.nil? || expected_files.kind_of?(Array)
|
|
263
233
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
input_files = infer_glob(:input) if input_files.nil?
|
|
282
|
-
flunk "No input files specified." if input_files.empty?
|
|
283
|
-
|
|
284
|
-
output_files = task.execute(*input_files).collect {|a| a._current}
|
|
285
|
-
expected_files = infer_glob(:expected) if expected_files.nil?
|
|
234
|
+
# assert_expected_result_files runs the input task using the
|
|
235
|
+
# input app config, submitting all method input files for
|
|
236
|
+
# processing. the assertions returns true only if all the
|
|
237
|
+
# expected and output files are equal. As such it is very
|
|
238
|
+
# convenient for file transform tasks.
|
|
239
|
+
#
|
|
240
|
+
# The default application config sets the app root to
|
|
241
|
+
# method_root, silences execution and directs app[:data] to
|
|
242
|
+
# the method output directory.
|
|
243
|
+
#
|
|
244
|
+
# assert_expected_result_files makes the input/expected
|
|
245
|
+
# directories if they do not exist. By default all the
|
|
246
|
+
# output files will be cleaned up during teardown. See
|
|
247
|
+
# FileMethods for more information.
|
|
248
|
+
def assert_expected_result_files(task, app_config={})
|
|
249
|
+
make_test_directories
|
|
286
250
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
translated_files = output_files.collect {|file| infer_translate(file, :output, :expected)}
|
|
293
|
-
assert_equal expected_files, translated_files, "Missing or extra expected files"
|
|
294
|
-
else
|
|
295
|
-
assert_equal expected_files.length, output_files.length, "Missing or extra expected files"
|
|
296
|
-
end
|
|
251
|
+
app_config = {
|
|
252
|
+
:root => method_root,
|
|
253
|
+
:options => {:quiet => true},
|
|
254
|
+
:absolute_paths => {:data => method_filepath(:output)}
|
|
255
|
+
}.merge(app_config)
|
|
297
256
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
end
|
|
257
|
+
with_config(app_config, task.app) do
|
|
258
|
+
assert_output_files_equal do |input_files|
|
|
259
|
+
output_files = task.execute(*input_files).collect {|a| a._current}
|
|
260
|
+
block_given? ? yield(output_files) : output_files
|
|
303
261
|
end
|
|
304
262
|
end
|
|
305
263
|
end
|
data/lib/tap/test/env_vars.rb
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
module Tap
|
|
2
2
|
module Test
|
|
3
|
+
|
|
4
|
+
# Provides a method for case-insensitive access to the ENV variables
|
|
3
5
|
module EnvVars
|
|
4
6
|
|
|
5
|
-
# Access to the case-insensitive ENV variables
|
|
7
|
+
# Access to the case-insensitive ENV variables. Raises an error
|
|
8
|
+
# if multiple case-insensitive values are defined in ENV.
|
|
6
9
|
def env(type)
|
|
7
10
|
type = type.downcase
|
|
8
|
-
ENV.
|
|
9
|
-
|
|
11
|
+
selected = ENV.select {|key, value| key.downcase == type}
|
|
12
|
+
|
|
13
|
+
case selected.length
|
|
14
|
+
when 0 then nil
|
|
15
|
+
when 1 then selected[0][1]
|
|
16
|
+
else
|
|
17
|
+
raise "Multiple env values for '#{type}'"
|
|
10
18
|
end
|
|
11
|
-
nil
|
|
12
19
|
end
|
|
13
|
-
|
|
20
|
+
|
|
21
|
+
# Returns true if the env_var(var) is set and matches /^true%/i
|
|
22
|
+
def env_true?(var)
|
|
23
|
+
env(var) && env(var) =~ /^true$/i
|
|
24
|
+
end
|
|
14
25
|
end
|
|
15
26
|
end
|
|
16
27
|
end
|
|
@@ -0,0 +1,373 @@
|
|
|
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
|
+
# Methods extending TestCase. See the TestTutorial for more information.
|
|
9
|
+
class TestCase
|
|
10
|
+
class << self
|
|
11
|
+
|
|
12
|
+
# Causes a TestCase to act as a file test, by instantiating a class Tap::Root
|
|
13
|
+
# (trs), and including the FileMethods. The root and directories used to
|
|
14
|
+
# instantiate trs can be specified as options. By default file_test_root
|
|
15
|
+
# and default_directories will be used.
|
|
16
|
+
#
|
|
17
|
+
# class FileDependentTest < Test::Unit::TestCase
|
|
18
|
+
# acts_as_file_test(:root => file_test_root, :directories => default_directories)
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# Note: file_test_root determines a root directory +based on the calling file+.
|
|
22
|
+
# Be sure to specify the root directory explicitly if you call acts_as_file_test
|
|
23
|
+
# from a file that is NOT meant to be test file.
|
|
24
|
+
def acts_as_file_test(options={})
|
|
25
|
+
options = {
|
|
26
|
+
:root => file_test_root,
|
|
27
|
+
:directories => {}
|
|
28
|
+
}.merge(options.symbolize_keys)
|
|
29
|
+
|
|
30
|
+
directories = default_directories.merge(options[:directories])
|
|
31
|
+
trs = Tap::Root.new(options[:root], directories)
|
|
32
|
+
|
|
33
|
+
write_inheritable_attribute(:trs, trs)
|
|
34
|
+
class_inheritable_reader :trs
|
|
35
|
+
|
|
36
|
+
include Tap::Test::FileMethods
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The default directories for a file test:
|
|
40
|
+
#
|
|
41
|
+
# :input => 'input'
|
|
42
|
+
# :output => 'output'
|
|
43
|
+
# :expected => 'expected'
|
|
44
|
+
def default_directories
|
|
45
|
+
{:input => 'input', :output => 'output', :expected => 'expected'}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Infers the test root directory from the calling file. Ex:
|
|
49
|
+
# 'some_class.rb' => 'some_class'
|
|
50
|
+
# 'some_class_test.rb' => 'some_class'
|
|
51
|
+
def file_test_root
|
|
52
|
+
# the calling file is not the direct caller of +method_root+... this method is
|
|
53
|
+
# only accessed from within another method call, hence the target caller is caller[1]
|
|
54
|
+
# rather than caller[0].
|
|
55
|
+
|
|
56
|
+
# caller[1] is considered the calling file (which should be the test case)
|
|
57
|
+
# note that the output of calller.first is like:
|
|
58
|
+
# ./path/to/file.rb:10
|
|
59
|
+
# ./path/to/file.rb:10:in 'method'
|
|
60
|
+
calling_file = caller[1].gsub(/:\d+[:in .*]?$/, "" )
|
|
61
|
+
calling_file.chomp!("#{File.extname(calling_file)}")
|
|
62
|
+
calling_file.chomp("_test")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
module Tap
|
|
70
|
+
module Test
|
|
71
|
+
|
|
72
|
+
# == Under Construction
|
|
73
|
+
#
|
|
74
|
+
# FileMethods sets up a TestCase with methods for accessing and utilizing
|
|
75
|
+
# test-specific files and directories. Each class including FileMethods
|
|
76
|
+
# is set up with a Tap::Root structure (trs) that mediates the creation of
|
|
77
|
+
# test method filepaths. FileMethods extends TestCase with the
|
|
78
|
+
# acts_as_file_test to include the FileMethods and initialize the trs.
|
|
79
|
+
#
|
|
80
|
+
# class FileDependentTest < Test::Unit::TestCase
|
|
81
|
+
# acts_as_file_test
|
|
82
|
+
#
|
|
83
|
+
# def test_file_transform
|
|
84
|
+
# trs.root # => "."
|
|
85
|
+
# method_root # => "./file_dependent_test/test_file_transform"
|
|
86
|
+
# method_dir(:input) # => "./file_dependent_test/test_file_transform/input"
|
|
87
|
+
# end
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# == File Transform Tests
|
|
91
|
+
#
|
|
92
|
+
# FileMethods is specifically designed for tests that transform a set of input
|
|
93
|
+
# files into output files. For this type of test, input and expected files can
|
|
94
|
+
# placed into their respective directories then used within the context of
|
|
95
|
+
# assert_output_files_equal to ensure the output files are equal to the expected
|
|
96
|
+
# files.
|
|
97
|
+
#
|
|
98
|
+
# For example, lets define a test that transforms input files into output files
|
|
99
|
+
# in a trivial way, simply by replacing 'input' with 'output' in the file.
|
|
100
|
+
#
|
|
101
|
+
# class FileTest < Test::Unit::TestCase
|
|
102
|
+
# acts_as_file_test
|
|
103
|
+
#
|
|
104
|
+
# def test_transform
|
|
105
|
+
# assert_output_files_equal do |input_files|
|
|
106
|
+
# input_files.collect do |filepath|
|
|
107
|
+
# input = File.read(filepath)
|
|
108
|
+
# output_file = method_filepath(:ouput_files, File.basename(filepath))
|
|
109
|
+
#
|
|
110
|
+
# File.open(output_file, "w") do |f|
|
|
111
|
+
# f << input.gsub(/input/, "output")
|
|
112
|
+
# end
|
|
113
|
+
# end
|
|
114
|
+
# end
|
|
115
|
+
# end
|
|
116
|
+
# end
|
|
117
|
+
#
|
|
118
|
+
# Now make some input and expected files for the 'test_transform' method:
|
|
119
|
+
#
|
|
120
|
+
# FileUtils.mkdir_p("file_test/test_transform/input_files")
|
|
121
|
+
# FileUtils.mkdir_p("file_test/test_transform/expected_files")
|
|
122
|
+
#
|
|
123
|
+
# [file_one.txt", file_two.txt"].each_with_index do |basename, i|
|
|
124
|
+
# input = "file_test/test_transform/input_files/" + basename
|
|
125
|
+
# File.open(input, "w") {|f| f << "test input #{i}"}
|
|
126
|
+
#
|
|
127
|
+
# expected = "file_test/test_transform/expected_files/" + basename
|
|
128
|
+
# File.open(expected, "w") {|f| f << "test output #{i}"}
|
|
129
|
+
# end
|
|
130
|
+
#
|
|
131
|
+
# When you run the FileTest tests, the test_transform test will pass
|
|
132
|
+
# the existing input_files to the assert_output_files_equal block. The
|
|
133
|
+
# block makes output files in "file_test/test_transform/output_files",
|
|
134
|
+
# and then assert_output_files_equal compares the files and finds they
|
|
135
|
+
# are equal. As a result the test passes.
|
|
136
|
+
#
|
|
137
|
+
# The test fails if you alter the expected file contents, add/remove
|
|
138
|
+
# expected files, or in some other way make these files unequal.
|
|
139
|
+
#
|
|
140
|
+
# When the test completes, the default teardown method will clean up the
|
|
141
|
+
# output_files directory. For ease in debugging, ENV variable flags can
|
|
142
|
+
# be specified to keep all output files (KEEP_OUTPUTS) or to keep the
|
|
143
|
+
# output files for just the tests that fail (KEEP_FAILURES). These flags
|
|
144
|
+
# can easily be specified from the command line when running a rake task:
|
|
145
|
+
#
|
|
146
|
+
# % tap run test keep_failures=true
|
|
147
|
+
#
|
|
148
|
+
module FileMethods
|
|
149
|
+
include Tap::Test::EnvVars
|
|
150
|
+
|
|
151
|
+
# Convenience accessor for the test root structure
|
|
152
|
+
def trs
|
|
153
|
+
self.class.trs
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Creates the trs.directories, specific to the method calling make_test_directories
|
|
157
|
+
def make_test_directories
|
|
158
|
+
trs.directories.values.each do |dir|
|
|
159
|
+
FileUtils.mkdir_p( File.join(trs.root, method_name, dir) )
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Setup deletes the the output directory if it exists, and tries to remove the
|
|
164
|
+
# method root directory so the directory structure is reset before running the
|
|
165
|
+
# test, even if outputs were left over from previous tests.
|
|
166
|
+
def setup
|
|
167
|
+
super
|
|
168
|
+
clear_method_dir(:output)
|
|
169
|
+
try_remove_dir(method_root)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Teardown deletes the the output directories unless flagged otherwise. Note
|
|
173
|
+
# that teardown also checks the environment variables for flags. To keep all outputs
|
|
174
|
+
# (or failures) for all tests, flag keep outputs from the command line like:
|
|
175
|
+
#
|
|
176
|
+
# % tap run test KEEP_OUTPUTS=true
|
|
177
|
+
# % tap run test KEEP_FAILURES=true
|
|
178
|
+
def teardown
|
|
179
|
+
# clear out the output folder if it exists, unless flagged otherwise
|
|
180
|
+
unless env("KEEP_OUTPUTS") || (!@test_passed && env("KEEP_FAILURES"))
|
|
181
|
+
begin
|
|
182
|
+
clear_method_dir(:output)
|
|
183
|
+
rescue
|
|
184
|
+
raise("teardown failure: could not remove output files")
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
try_remove_dir(method_root)
|
|
189
|
+
try_remove_dir(trs.root)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# The method_root directory is defined as trs.filepath(method_name)
|
|
193
|
+
def method_root(method=method_name)
|
|
194
|
+
trs.filepath(method)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# The method directory is defined as 'dir/method', where method is the calling method
|
|
198
|
+
# by default. method_dir returns the method directory if it exists, otherwise it returns
|
|
199
|
+
# trs[dir].
|
|
200
|
+
def method_dir(dir, method=method_name)
|
|
201
|
+
File.join(method_root(method), trs.directories[dir] || dir.to_s)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Returns a glob of files matching the input pattern, underneath the method directory
|
|
205
|
+
# if it exists, otherwise the <tt>trs[dir]</tt> directory.
|
|
206
|
+
def method_glob(dir, *patterns)
|
|
207
|
+
dir = trs.relative_filepath(:root, method_dir(dir))
|
|
208
|
+
trs.glob(dir, *patterns)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Returns a filepath constructed from the method directory if it exists,
|
|
212
|
+
# otherwise the filepath will be constructed from <tt>trs[dir]</tt>.
|
|
213
|
+
def method_filepath(dir, *filenames)
|
|
214
|
+
File.join(method_dir(dir), *filenames)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Removes the method directory from the input filepath, returning the resuting filename.
|
|
218
|
+
# If the method directory does not exist, <tt>trs[dir]</tt> will be removed.
|
|
219
|
+
def method_relative_filepath(dir, filepath)
|
|
220
|
+
dir = trs.relative_filepath(:root, method_dir(dir))
|
|
221
|
+
trs.relative_filepath(dir, filepath)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Returns an output file corresponding to the input file, translated from the
|
|
225
|
+
# input directory to the output directory.
|
|
226
|
+
#
|
|
227
|
+
# If the input method directory exists, it will be removed from the filepath.
|
|
228
|
+
# If the output method directory exists, it will be inserted in the filepath.
|
|
229
|
+
def method_translate(filepath, input_dir, output_dir)
|
|
230
|
+
input_dir = trs.relative_filepath(:root, method_dir(input_dir))
|
|
231
|
+
output_dir = trs.relative_filepath(:root, method_dir(output_dir))
|
|
232
|
+
trs.translate(filepath, input_dir, output_dir)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Attempts to recursively remove the specified method directory and all
|
|
236
|
+
# files within it. Raises an error if the removal does not succeed.
|
|
237
|
+
def clear_method_dir(dir)
|
|
238
|
+
# clear out the folder if it exists
|
|
239
|
+
dir_path = method_dir(dir, method_name)
|
|
240
|
+
FileUtils.rm_r(dir_path) if File.exists?(dir_path)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Attempts to remove the specified directory. The root
|
|
244
|
+
# will not be removed if the directory does not exist, or
|
|
245
|
+
# is not empty.
|
|
246
|
+
def try_remove_dir(dir)
|
|
247
|
+
# Remove the directory if possible
|
|
248
|
+
begin
|
|
249
|
+
FileUtils.rmdir(dir) if File.exists?(dir) && Dir.glob(File.join(dir, "*")).empty?
|
|
250
|
+
rescue
|
|
251
|
+
# rescue cases where there is a hidden file, for example .svn
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Generates a temporary filepath formatted like "output_dir\filename.pid.n.ext" where n
|
|
256
|
+
# is a counter that will be incremented from until a non-existant filepath is achieved.
|
|
257
|
+
#
|
|
258
|
+
# Notes:
|
|
259
|
+
# - By default filename is the calling method
|
|
260
|
+
# - The extension is chomped off the end of the filename
|
|
261
|
+
# - If the directory for the filepath does not exist, the directory will be created
|
|
262
|
+
# - Like all files in the output directory, tempfiles will be deleted by the default
|
|
263
|
+
# +teardown+ method
|
|
264
|
+
def tempfile(filename=method_name)
|
|
265
|
+
n = 0
|
|
266
|
+
ext = File.extname(filename)
|
|
267
|
+
basename = filename.chomp(ext)
|
|
268
|
+
filepath = make_tmpname(basename, n, ext)
|
|
269
|
+
while File.exists?(filepath)
|
|
270
|
+
n += 1
|
|
271
|
+
filepath = make_tmpname(basename, n, ext)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
dirname = File.dirname(filepath)
|
|
275
|
+
FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
|
|
276
|
+
filepath
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Asserts that each pair of input files are equal, using FileUtils.cmp.
|
|
280
|
+
def files_equal?(a, b)
|
|
281
|
+
FileUtils.cmp(a, b)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Asserts that each pair of input files produce an equivalent object when
|
|
285
|
+
# loaded as a yaml file.
|
|
286
|
+
def yml_equal?(a, b)
|
|
287
|
+
YAML.load_file(a) == YAML.load_file(b)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Yields to the input block for each pair of input files. An error is raised
|
|
291
|
+
# if the input arrays do not have equal numbers of entries.
|
|
292
|
+
def each_pair(a, b, &block)
|
|
293
|
+
a = [a] unless a.kind_of?(Array)
|
|
294
|
+
b = [b] unless b.kind_of?(Array)
|
|
295
|
+
|
|
296
|
+
raise ArgumentError, "The input arrays must have an equal number of entries." unless a.length == b.length
|
|
297
|
+
a.each_index do |index|
|
|
298
|
+
yield(a[index], b[index])
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def assert_output_files_equal(options={}) # :yields: input_files
|
|
303
|
+
make_test_directories
|
|
304
|
+
|
|
305
|
+
options = {
|
|
306
|
+
:input_files => nil,
|
|
307
|
+
:expected_files => nil,
|
|
308
|
+
:require_expected_files => true,
|
|
309
|
+
:check_for_missing_outputs => true,
|
|
310
|
+
:iterate_inputs => false,
|
|
311
|
+
:include_directories => false
|
|
312
|
+
}.merge(options)
|
|
313
|
+
|
|
314
|
+
# set or search for input and expected files, and arrayify
|
|
315
|
+
input_files, expected_files = [:input, :expected].collect do |key|
|
|
316
|
+
options_key = "#{key}_files".to_sym
|
|
317
|
+
|
|
318
|
+
files = options.delete(options_key)
|
|
319
|
+
files = method_glob(key) if files.nil?
|
|
320
|
+
files = (files.kind_of?(Array) ? files : [files])
|
|
321
|
+
files.collect! {|file| File.expand_path(file) }
|
|
322
|
+
unless options[:include_directories]
|
|
323
|
+
files.delete_if {|file| File.directory?(file)}
|
|
324
|
+
end
|
|
325
|
+
files
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
flunk "No expected files were specfied." if options[:require_expected_files] && expected_files.empty?
|
|
329
|
+
|
|
330
|
+
# create output files
|
|
331
|
+
output_files = if options[:iterate_inputs]
|
|
332
|
+
input_files.collect do |input_file|
|
|
333
|
+
yield(input_file)
|
|
334
|
+
end
|
|
335
|
+
else
|
|
336
|
+
yield(input_files)
|
|
337
|
+
end
|
|
338
|
+
output_files = output_files.flatten.collect {|file| File.expand_path(file) }
|
|
339
|
+
|
|
340
|
+
if options[:check_for_missing_outputs]
|
|
341
|
+
expected_output_files = method_glob(:output)
|
|
342
|
+
unless options[:include_directories]
|
|
343
|
+
expected_output_files.delete_if {|file| File.directory?(file)}
|
|
344
|
+
end
|
|
345
|
+
expected_output_files.collect! {|file| File.expand_path(file) }
|
|
346
|
+
|
|
347
|
+
# assure there are no missing or extra output files
|
|
348
|
+
assert_equal expected_output_files, output_files, "Missing or extra output files"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# check that the expected and output filepaths are the same
|
|
352
|
+
translated_files = output_files.collect {|file| method_translate(file, :output, :expected)}
|
|
353
|
+
translated_files.collect! {|file| File.expand_path(file) }
|
|
354
|
+
assert_equal expected_files, translated_files, "Missing or extra expected files"
|
|
355
|
+
|
|
356
|
+
# check that the expected and output file contents are equal
|
|
357
|
+
errors = []
|
|
358
|
+
each_pair(expected_files, output_files) do |expected_file, output_file|
|
|
359
|
+
unless files_equal?(expected_file, output_file)
|
|
360
|
+
errors << "<#{expected_file}> not equal to\n<#{output_file}>"
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
flunk "File compare failed:\n" + errors.join("\n") unless errors.empty?
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
private
|
|
367
|
+
|
|
368
|
+
def make_tmpname(basename, n, ext="")
|
|
369
|
+
method_filepath(:output, sprintf('%s%d.%d%s', basename, $$, n, ext))
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|