tap 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +12 -0
- data/MIT-LICENSE +0 -2
- data/README +23 -32
- data/bin/rap +116 -0
- data/bin/tap +6 -9
- data/cgi/run.rb +67 -0
- data/cmd/console.rb +1 -1
- data/cmd/destroy.rb +4 -4
- data/cmd/generate.rb +4 -4
- data/cmd/manifest.rb +61 -53
- data/cmd/run.rb +8 -75
- data/doc/Class Reference +130 -121
- data/doc/Command Reference +76 -124
- data/doc/Syntax Reference +290 -0
- data/doc/Tutorial +305 -237
- data/lib/tap/app.rb +140 -467
- data/lib/tap/constants.rb +2 -2
- data/lib/tap/declarations.rb +211 -0
- data/lib/tap/env.rb +171 -193
- data/lib/tap/exe.rb +100 -21
- data/lib/tap/file_task.rb +3 -3
- data/lib/tap/generator/base.rb +1 -1
- data/lib/tap/generator/destroy.rb +10 -10
- data/lib/tap/generator/generate.rb +29 -18
- data/lib/tap/generator/generators/command/command_generator.rb +2 -2
- data/lib/tap/generator/generators/command/templates/command.erb +2 -2
- data/lib/tap/generator/generators/config/config_generator.rb +3 -3
- data/lib/tap/generator/generators/config/templates/doc.erb +1 -1
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +1 -1
- data/lib/tap/generator/generators/file_task/templates/task.erb +1 -1
- data/lib/tap/generator/generators/file_task/templates/test.erb +1 -1
- data/lib/tap/generator/generators/generator/generator_generator.rb +27 -0
- data/lib/tap/generator/generators/generator/templates/task.erb +27 -0
- data/lib/tap/generator/generators/root/root_generator.rb +13 -13
- data/lib/tap/generator/generators/root/templates/README +0 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +2 -2
- data/lib/tap/generator/generators/root/templates/gemspec +4 -5
- data/lib/tap/generator/generators/root/templates/tapfile +11 -8
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
- data/lib/tap/generator/generators/task/task_generator.rb +1 -3
- data/lib/tap/generator/generators/task/templates/test.erb +1 -3
- data/lib/tap/patches/optparse/summarize.rb +62 -0
- data/lib/tap/root.rb +41 -29
- data/lib/tap/support/aggregator.rb +16 -3
- data/lib/tap/support/assignments.rb +10 -9
- data/lib/tap/support/audit.rb +58 -64
- data/lib/tap/support/class_configuration.rb +33 -44
- data/lib/tap/support/combinator.rb +125 -0
- data/lib/tap/support/configurable.rb +13 -14
- data/lib/tap/support/configurable_class.rb +21 -43
- data/lib/tap/support/configuration.rb +55 -9
- data/lib/tap/support/constant.rb +87 -13
- data/lib/tap/support/constant_manifest.rb +116 -0
- data/lib/tap/support/dependencies.rb +54 -0
- data/lib/tap/support/dependency.rb +44 -0
- data/lib/tap/support/executable.rb +247 -32
- data/lib/tap/support/executable_queue.rb +1 -1
- data/lib/tap/support/gems/rake.rb +29 -8
- data/lib/tap/support/gems.rb +10 -30
- data/lib/tap/support/instance_configuration.rb +29 -3
- data/lib/tap/support/intern.rb +46 -0
- data/lib/tap/support/join.rb +143 -0
- data/lib/tap/support/joins/fork.rb +19 -0
- data/lib/tap/support/joins/merge.rb +22 -0
- data/lib/tap/support/joins/sequence.rb +21 -0
- data/lib/tap/support/joins/switch.rb +25 -0
- data/lib/tap/support/joins/sync_merge.rb +63 -0
- data/lib/tap/support/joins.rb +15 -0
- data/lib/tap/support/lazy_attributes.rb +17 -2
- data/lib/tap/support/lazydoc/comment.rb +503 -0
- data/lib/tap/support/lazydoc/config.rb +17 -0
- data/lib/tap/support/lazydoc/definition.rb +36 -0
- data/lib/tap/support/lazydoc/document.rb +152 -0
- data/lib/tap/support/lazydoc/method.rb +24 -0
- data/lib/tap/support/lazydoc.rb +269 -343
- data/lib/tap/support/manifest.rb +121 -103
- data/lib/tap/support/minimap.rb +90 -0
- data/lib/tap/support/node.rb +56 -0
- data/lib/tap/support/parser.rb +436 -0
- data/lib/tap/support/schema.rb +359 -0
- data/lib/tap/support/shell_utils.rb +3 -5
- data/lib/tap/support/string_ext.rb +60 -0
- data/lib/tap/support/tdoc.rb +7 -2
- data/lib/tap/support/templater.rb +30 -16
- data/lib/tap/support/validation.rb +77 -8
- data/lib/tap/task.rb +431 -143
- data/lib/tap/tasks/dump.rb +15 -10
- data/lib/tap/tasks/load.rb +112 -0
- data/lib/tap/tasks/rake.rb +4 -41
- data/lib/tap/test/assertions.rb +38 -0
- data/lib/tap/test/env_vars.rb +1 -1
- data/lib/tap/test/extensions.rb +79 -0
- data/lib/tap/test/file_test.rb +420 -0
- data/lib/tap/test/file_test_class.rb +12 -0
- data/lib/tap/test/regexp_escape.rb +87 -0
- data/lib/tap/test/script_test.rb +46 -0
- data/lib/tap/test/script_tester.rb +115 -0
- data/lib/tap/test/subset_test.rb +260 -0
- data/lib/tap/test/subset_test_class.rb +99 -0
- data/lib/tap/test/{tap_methods.rb → tap_test.rb} +45 -43
- data/lib/tap/test/utils.rb +231 -0
- data/lib/tap/test.rb +53 -26
- data/lib/tap.rb +3 -20
- metadata +50 -27
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +0 -15
- data/lib/tap/patches/rake/rake_test_loader.rb +0 -8
- data/lib/tap/patches/rake/testtask.rb +0 -57
- data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -51
- data/lib/tap/patches/ruby19/parsedate.rb +0 -16
- data/lib/tap/support/batchable.rb +0 -47
- data/lib/tap/support/batchable_class.rb +0 -107
- data/lib/tap/support/command_line.rb +0 -98
- data/lib/tap/support/comment.rb +0 -270
- data/lib/tap/support/constant_utils.rb +0 -127
- data/lib/tap/support/declarations.rb +0 -111
- data/lib/tap/support/framework.rb +0 -83
- data/lib/tap/support/framework_class.rb +0 -180
- data/lib/tap/support/run_error.rb +0 -39
- data/lib/tap/support/summary.rb +0 -30
- data/lib/tap/test/file_methods.rb +0 -377
- data/lib/tap/test/script_methods/script_test.rb +0 -98
- data/lib/tap/test/script_methods.rb +0 -107
- data/lib/tap/test/subset_methods.rb +0 -420
- data/lib/tap/workflow.rb +0 -200
@@ -1,420 +0,0 @@
|
|
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
|
data/lib/tap/workflow.rb
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
module Tap
|
2
|
-
|
3
|
-
# Workflow is a specialized type of Task allowing the encapsulation and reuse of
|
4
|
-
# workflow logic. Workflows are still under construction.
|
5
|
-
#
|
6
|
-
# === Workflow Definition
|
7
|
-
#
|
8
|
-
# During initialization, Workflow executes the workflow method (by default the
|
9
|
-
# block provided to Workflow.new) to define the workflow logic. This method
|
10
|
-
# defines one or more entry_points and zero or more exit points, as well as
|
11
|
-
# the internal logic for the workflow.
|
12
|
-
#
|
13
|
-
# Workflow.new do |w|
|
14
|
-
# factor = w.config[:factor] || 1
|
15
|
-
#
|
16
|
-
# t1 = Task.new {|task, input| input += 1 }
|
17
|
-
# t2 = Task.new {|task, input| input += 10 }
|
18
|
-
# t3 = Task.new {|task, input| input *= factor }
|
19
|
-
#
|
20
|
-
# w.app.sequence(t1, t2, t3)
|
21
|
-
# w.entry_point = t1
|
22
|
-
# w.exit_point = t3
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# Or equivalently:
|
26
|
-
#
|
27
|
-
# class SimpleSequence < Workflow
|
28
|
-
# config :factor, 1
|
29
|
-
#
|
30
|
-
# def workflow
|
31
|
-
# t1 = Task.new {|task, input| input += 5 }
|
32
|
-
# t2 = Task.new {|task, input| input += 3 }
|
33
|
-
# t3 = Task.new {|task, input| input *= factor }
|
34
|
-
#
|
35
|
-
# app.sequence(t1, t2, t3)
|
36
|
-
# self.entry_point = t1
|
37
|
-
# self.exit_point = t3
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# To facilitate the specification of entry and exit points, workflow
|
42
|
-
# can accomodate either single-task assignments or a collection. By
|
43
|
-
# default both are hashes, but they can be reassigned:
|
44
|
-
#
|
45
|
-
# Workflow.new do |w|
|
46
|
-
# w.entry_point.class # => Hash
|
47
|
-
# w.exit_point.class # => Hash
|
48
|
-
# w.entry_point[:main] = Task.new
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# Workflow.new {|w| w.entry_point = Task.new }
|
52
|
-
# Workflow.new {|w| w.entry_point = [Task.new, Task.new] }
|
53
|
-
#
|
54
|
-
# Access to the group of entry/exit points is standardized to an
|
55
|
-
# array via the entry_points and exit_points methods.
|
56
|
-
#
|
57
|
-
# === Workflow Behavior
|
58
|
-
#
|
59
|
-
# The entry points act as an enque batch; when the workflow is enqued, the
|
60
|
-
# entry points are enqued. The exit points act as an on_complete batch; their
|
61
|
-
# on_complete blocks are set for workflow.on_complete.
|
62
|
-
#
|
63
|
-
# w = SimpleSequence.new
|
64
|
-
# w.enq(0)
|
65
|
-
# app.run
|
66
|
-
# app.results(w.exit_points) # => [8]
|
67
|
-
#
|
68
|
-
# The batching of entry and exit points is distinct from workflow.batch itself.
|
69
|
-
# Workflows can be batched like Tasks, such that all entry points from all
|
70
|
-
# workflows in a batch are enqued at once.
|
71
|
-
#
|
72
|
-
# w1 = SimpleSequence.new nil, :factor => 1
|
73
|
-
# w2 = w1.initialize_batch_obj nil, :factor => -1
|
74
|
-
#
|
75
|
-
# w1.enq(0)
|
76
|
-
# app.run
|
77
|
-
# app.results(w1.exit_points, w2.exit_points)) # => [8, -8]
|
78
|
-
#
|
79
|
-
class Workflow
|
80
|
-
include Support::Framework
|
81
|
-
|
82
|
-
class << self
|
83
|
-
protected
|
84
|
-
|
85
|
-
def define(name, klass=Tap::Task, &block)
|
86
|
-
instance_var = "@#{name}".to_sym
|
87
|
-
|
88
|
-
define_method(name) do |*args|
|
89
|
-
raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
|
90
|
-
|
91
|
-
instance_name = args[0] || name
|
92
|
-
instance_variable_set(instance_var, {}) unless instance_variable_defined?(instance_var)
|
93
|
-
instance_variable_get(instance_var)[instance_name] ||= task(instance_name, klass, &block)
|
94
|
-
end
|
95
|
-
|
96
|
-
define_method("#{name}=") do |input|
|
97
|
-
input = {name => input} unless input.kind_of?(Hash)
|
98
|
-
instance_variable_set(instance_var, input)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# The entry point for self.
|
104
|
-
attr_accessor :entry_point
|
105
|
-
|
106
|
-
# The exit point for self.
|
107
|
-
attr_accessor :exit_point
|
108
|
-
|
109
|
-
# The task block provided during initialization.
|
110
|
-
attr_reader :task_block
|
111
|
-
|
112
|
-
# Creates a new Task with the specified attributes.
|
113
|
-
def initialize(config={}, name=nil, app=App.instance, &task_block)
|
114
|
-
super(config, name, app)
|
115
|
-
@task_block = (task_block == nil ? default_task_block : task_block)
|
116
|
-
initialize_workflow
|
117
|
-
end
|
118
|
-
|
119
|
-
# Initializes a new batch object, running workflow to set the
|
120
|
-
# instance-specific entry/exit points. Raises an error if
|
121
|
-
# no entry points are defined.
|
122
|
-
def initialize_copy(orig)
|
123
|
-
super
|
124
|
-
initialize_workflow
|
125
|
-
end
|
126
|
-
|
127
|
-
def initialize_workflow
|
128
|
-
@entry_point = {}
|
129
|
-
@exit_point = {}
|
130
|
-
workflow
|
131
|
-
end
|
132
|
-
|
133
|
-
# Returns an array of entry points, determined from entry_point.
|
134
|
-
def entry_points
|
135
|
-
case @entry_point
|
136
|
-
when Hash then @entry_point.values
|
137
|
-
when Support::Executable then [@entry_point]
|
138
|
-
when Array then @entry_point
|
139
|
-
when nil then []
|
140
|
-
else raise "unable to determine entry points from entry_point: #{@entry_point}"
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Returns an array of exit points, determined from exit_point.
|
145
|
-
def exit_points
|
146
|
-
case @exit_point
|
147
|
-
when Hash then @exit_point.values
|
148
|
-
when Support::Executable then [@exit_point]
|
149
|
-
when Array then @exit_point
|
150
|
-
when nil then []
|
151
|
-
else raise "unable to determine exit points from exit_point: #{@exit_point}"
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
# Enqueues all entry points for self and self.batch to app
|
156
|
-
# with the inputs. The number of inputs provided should match
|
157
|
-
# the number of inputs required by all the entry points;
|
158
|
-
# if the entry points have different input requirements, they
|
159
|
-
# have to be enqued separately.
|
160
|
-
def enq(*inputs)
|
161
|
-
entry_points.each do |task|
|
162
|
-
app.enq(task, *inputs)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
batch_function :enq
|
167
|
-
|
168
|
-
# Sets the on_complete_block for all exit points for self and
|
169
|
-
# self.batch. Use unbatched_on_complete to set the on_complete_block
|
170
|
-
# for just self.exit_points.
|
171
|
-
def on_complete(override=false, &block)
|
172
|
-
exit_points.each do |task|
|
173
|
-
task.on_complete(override, &block)
|
174
|
-
end
|
175
|
-
self
|
176
|
-
end
|
177
|
-
|
178
|
-
batch_function(:on_complete) {}
|
179
|
-
|
180
|
-
def task(name, klass=Tap::Task, &block)
|
181
|
-
configs = config[name] || {}
|
182
|
-
raise ArgumentError, "config '#{name}' is not a hash" unless configs.kind_of?(Hash)
|
183
|
-
klass.new(configs, name, &block)
|
184
|
-
end
|
185
|
-
|
186
|
-
# The workflow definition method. By default workflow
|
187
|
-
# simply calls the task_block. In subclasses, workflow
|
188
|
-
# should be overridden to provide the workflow definition.
|
189
|
-
def workflow
|
190
|
-
task_block.call(self) if task_block
|
191
|
-
end
|
192
|
-
|
193
|
-
protected
|
194
|
-
|
195
|
-
# Hook to set a default task block. By default, nil.
|
196
|
-
def default_task_block
|
197
|
-
nil
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|