tap 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,75 +1,203 @@
1
1
  module Tap
2
2
 
3
- # == UNDER CONSTRUCTION
3
+ # == Overview
4
4
  #
5
5
  # App can build workflows directly, using methods like sequence, merge, and
6
6
  # multithread, but these workflows are hard to abstract and resuse. Workflow
7
7
  # is a specialized type of Task allows the encapsulation and reuse of workflow
8
- # logic.
9
- #
10
- # During initialization, Workflows execute the workflow method to define the
11
- # workflow logic. The workflow method defines an entry_point task, which is
12
- # sequenced to execute after the Workflow instance completes. Workflows by
13
- # default just pass their inputs along, essentially acting as a gateway for
14
- # executing entry_point:
15
- #
16
- # inputs to workflow
17
- # workflow executes (does nothing)
18
- # on_complete entry_point recieves inputs
19
- # entry_point executes, beginning workflow logic
8
+ # logic. See Tap::Task for the shared documentation.
9
+ #
10
+ # === Workflow Definition
11
+ #
12
+ # During initialization, Workflow executes the workflow method (by default the
13
+ # block provided to Workflow.new) to define the workflow logic. This method
14
+ # defines one or more entry_points and zero or more exit points, as well as
15
+ # the internal logic for the workflow.
16
+ #
17
+ # Workflow.new do |w|
18
+ # factor = w.config[:factor] || 1
19
+ #
20
+ # t1 = Task.new {|task, input| input += 1 }
21
+ # t2 = Task.new {|task, input| input += 10 }
22
+ # t3 = Task.new {|task, input| input *= factor }
23
+ #
24
+ # w.app.sequence(t1, t2, t3)
25
+ # w.entry_point = t1
26
+ # w.exit_point = t3
27
+ # end
28
+ #
29
+ # Or equivalently:
30
+ #
31
+ # class SimpleSequence < Workflow
32
+ # config :factor, 1
33
+ #
34
+ # def workflow
35
+ # t1 = Task.new {|task, input| input += 5 }
36
+ # t2 = Task.new {|task, input| input += 3 }
37
+ # t3 = Task.new {|task, input| input *= factor }
38
+ #
39
+ # app.sequence(t1, t2, t3)
40
+ # self.entry_point = t1
41
+ # self.exit_point = t3
42
+ # end
43
+ # end
44
+ #
45
+ # To facilitate the specification of entry and exit points, workflow
46
+ # can accomodate either single-task assignments or a collection. By
47
+ # default both are hashes, but they can be reassigned:
20
48
  #
21
- # Since entry_point and all other tasks in the workflow logic are defined
22
- # in the workflow method, they can be made instance-specific. Thus you can
23
- # instantiate a Workflow many times and use them at many points in a larger
24
- # workflow, or in a multithreaded way.
25
- #
26
- # Notes:
27
- # - The default Workflow uses the task blocks to define workflow logic, NOT
28
- # process logic as in Task.
29
- class Workflow < Task
30
- attr_accessor :entry_point, :exit_point
31
-
32
- def initialize(*args)
33
- super
49
+ # Workflow.new do |w|
50
+ # w.entry_point.class # => Hash
51
+ # w.exit_point.class # => Hash
52
+ # w.entry_point[:main] = Task.new
53
+ # end
54
+ #
55
+ # Workflow.new {|w| w.entry_point = Task.new }
56
+ # Workflow.new {|w| w.entry_point = [Task.new, Task.new] }
57
+ #
58
+ # Access to the group of entry/exit points is standardized to an
59
+ # array via the entry_points and exit_points methods.
60
+ #
61
+ # === Workflow Behavior
62
+ #
63
+ # The entry points act as an enque batch; when the workflow is enqued, the
64
+ # entry points are enqued. The exit points act as an on_complete batch; their
65
+ # on_complete blocks are set for workflow.on_complete.
66
+ #
67
+ # w = SimpleSequence.new
68
+ # w.enq(0)
69
+ # app.run
70
+ # app.results(w.exit_points) # => [8]
71
+ #
72
+ # The batching of entry and exit points is distinct from workflow.batch itself.
73
+ # Workflows can be batched like Tasks, such that all entry points from all
74
+ # workflows in a batch are enqued at once.
75
+ #
76
+ # w1 = SimpleSequence.new nil, :factor => 1
77
+ # w2 = w1.initialize_batch_obj nil, :factor => -1
78
+ #
79
+ # w1.enq(0)
80
+ # app.run
81
+ # app.results(w1.exit_points, w2.exit_points)) # => [8, -8]
82
+ #
83
+ class Workflow
84
+ include Support::Configurable
85
+
86
+ # The entry point for self.
87
+ attr_accessor :entry_point
88
+
89
+ # The exit point for self.
90
+ attr_accessor :exit_point
91
+
92
+ # The task block provided during initialization.
93
+ attr_reader :task_block
94
+
95
+ # Creates a new Task with the specified attributes.
96
+ def initialize(name=nil, config={}, app=App.instance, &task_block)
97
+ @task_block = (task_block == nil ? default_task_block : task_block)
98
+ super(name, config, app)
99
+ end
100
+
101
+ # Initializes a new batch object, running workflow to set the
102
+ # instance-specific entry/exit points. Raises an error if
103
+ # no entry points are defined.
104
+ def initialize_batch_obj(name=nil, config={})
105
+ task = super(name, config)
106
+
107
+ task.entry_point = {}
108
+ task.exit_point = {}
109
+ task.workflow
34
110
 
35
- self.entry_point = {}
36
- self.exit_point = {}
37
- self.workflow
111
+ raise WorkflowError.new("No entry points defined") if task.entry_points.empty?
38
112
 
113
+ task
114
+ end
115
+
116
+ # Returns an array of entry points, determined from entry_point.
117
+ def entry_points
39
118
  case entry_point
40
- when Hash, Array
41
- raise WorkflowError.new("No entry points defined") if entry_point.empty?
42
- targets = entry_point.kind_of?(Hash) ? entry_point.values : entry_point
43
- fork(self, *targets)
44
- when Task
45
- sequence(self, entry_point)
119
+ when Hash then entry_point.values
120
+ when Support::Executable then [entry_point]
121
+ when Array then entry_point
46
122
  else
47
- raise WorkflowError.new("No entry point defined")
123
+ raise "unable to determine entry points from entry_point (should be Hash, Array, or Executable): #{entry_point}"
48
124
  end
49
125
  end
50
-
51
- def sequence(*tasks)
52
- app.sequence(*tasks)
126
+
127
+ # Returns an array of exit points, determined from exit_point.
128
+ def exit_points
129
+ case exit_point
130
+ when Hash then exit_point.values
131
+ when Support::Executable then [exit_point]
132
+ when Array then exit_point
133
+ else
134
+ raise "unable to determine exit points from exit_point (should be Hash, Array, or Executable): #{exit_point}"
135
+ end
136
+ end
137
+
138
+ # Enqueues all entry points for self and self.batch to app
139
+ # with the inputs. The number of inputs provided should match
140
+ # the number of inputs required by all the entry points;
141
+ # if the entry points have different input requirements, they
142
+ # have to be enqued separately.
143
+ def enq(*inputs)
144
+ batch.each {|t| t.unbatched_enq(*inputs) }
145
+ self
53
146
  end
54
147
 
55
- def fork(source, *targets)
56
- app.fork(source, *targets)
148
+ # Enques all entry points to app with the inputs.
149
+ def unbatched_enq(*inputs)
150
+ entry_points.each do |task|
151
+ app.enq(task, *inputs)
152
+ end
153
+ self
57
154
  end
58
155
 
59
- def merge(target, *sources)
60
- app.merge(target, *sources)
156
+ # Sets the on_complete_block for all exit points for self and
157
+ # self.batch. Use unbatched_on_complete to set the on_complete_block
158
+ # for just self.exit_points.
159
+ def on_complete(override=false, &block)
160
+ batch.each {|t| t.unbatched_on_complete(override, &block)}
161
+ self
61
162
  end
62
163
 
164
+ # Sets the on_complete_block for all exit points for self.
165
+ def unbatched_on_complete(override=false, &block)
166
+ exit_points.each do |task|
167
+ task.on_complete(override, &block)
168
+ end
169
+ self
170
+ end
171
+
172
+ # The workflow definition method. By default workflow
173
+ # simply calls the task_block. In subclasses, workflow
174
+ # should be overridden to provide the workflow definition.
63
175
  def workflow
64
176
  raise WorkflowError.new("No workflow definition provided.") unless task_block
65
177
  task_block.call(self)
66
178
  end
179
+
180
+ class WorkflowError < Exception # :nodoc:
181
+ end
67
182
 
68
- def process(input)
69
- input
183
+ # Returns the name of the workflow joined to the input. This
184
+ # can be convenient when naming internal tasks, as they can
185
+ # be grouped based on the name of the workflow. Returns
186
+ # the name of the workflow if input == nil.
187
+ def name(input=nil)
188
+ input == nil ? @name : File.join(@name, input)
70
189
  end
71
190
 
72
- class WorkflowError < Exception # :nodoc:
191
+ # Returns name
192
+ def to_s
193
+ name
194
+ end
195
+
196
+ protected
197
+
198
+ # Hook to set a default task block. By default, nil.
199
+ def default_task_block
200
+ nil
73
201
  end
74
202
  end
75
203
  end
@@ -0,0 +1 @@
1
+ key: two
@@ -1 +1,2 @@
1
- sum: <%= 1 + 2 %>
1
+ app: <%= app.object_id %>
2
+ filepath: <%= filepath %>
@@ -0,0 +1 @@
1
+ key: one
@@ -1,6 +1,2 @@
1
- default_field: default value
2
-
3
- variations!:
4
- - field: item one
5
- - field: item two
6
- default_field: non-default value
1
+ - factor: 10
2
+ - factor: 22
@@ -4,107 +4,225 @@ require 'stringio'
4
4
  require 'logger'
5
5
 
6
6
  class AppTest < Test::Unit::TestCase
7
- include Tap
8
- include TapTestMethods
9
-
7
+ include Tap
8
+ include TapTestMethods
9
+
10
10
  acts_as_tap_test
11
-
12
- def stub_gemspec(name, version)
13
- spec = Gem::Specification.new
14
- spec.name = name
15
- spec.version = version
16
- spec.loaded_from = "/path/to/gems"
17
- spec
18
- end
19
-
20
- def test_app_documentation
21
- task = Tap::Task.new {|task, input| input += 1 }
22
- assert_equal Tap::App.instance, task.app
23
- task.enq 1,2,3
24
-
25
- task.app.run
26
-
27
- assert_equal [2,3,4], task.results.collect {|audit| audit._current}
28
- end
29
-
30
- #
31
- # App instance test
32
- #
33
-
34
- def test_instance_returns_current_instance
35
- a = Tap::App.new
36
- Tap::App.instance = a
37
- assert_equal a, Tap::App.instance
38
- end
39
-
40
- def test_instance_initializes_new_App_if_not_set
41
- Tap::App.instance = nil
42
- assert Tap::App.instance.kind_of?(Tap::App)
43
- end
44
-
45
- #
46
- # App parse_yaml test
47
- #
48
-
49
- def test_parse_yaml_documentation
50
- str = {'key' => 'value'}.to_yaml
51
- assert_equal "--- \nkey: value\n", str
52
- assert_equal({'key' => 'value'}, Tap::App.parse_yaml(str))
53
- assert_equal "str", Tap::App.parse_yaml("str")
54
- end
55
-
56
- def test_parse_yaml_parses_correctly_formatted_strings
57
- assert_equal({'key' => 'value'}, Tap::App.parse_yaml("---\nkey: value"))
58
- end
59
-
60
- def test_parse_yaml_returns_strings_not_matching_yaml_format
61
- assert_equal("str", Tap::App.parse_yaml("str"))
62
- end
63
-
64
- #
65
- # App read_erb_yml test
66
- #
67
-
68
- def test_read_erb_yaml_templates_file_using_erb_then_loads_as_yaml
69
- file = tempfile
70
- File.open(file, "w") do |f|
71
- f << %Q{
72
- <% value = "value" %>
73
- <%= "k" + "ey" %>: <%= value %>}
74
- end
75
-
76
- assert_equal({'key' => 'value'}, Tap::App.read_erb_yaml(file))
77
- end
78
-
79
- def test_read_erb_yaml_returns_nil_if_filepath_does_not_exist_or_is_a_dir
80
- file = File.dirname(__FILE__) + "/app/no_existant_file.txt"
81
- dir = File.dirname(__FILE__) + "/app"
82
-
83
- assert !File.exists?(file)
84
- assert File.directory?(dir)
85
- assert_nil Tap::App.read_erb_yaml(file)
86
- assert_nil Tap::App.read_erb_yaml(dir)
87
- end
88
-
89
- #
90
- # initialization tests
91
- #
92
-
93
- def test_default_app
94
- app = App.new
95
-
96
- assert_equal Dir.pwd, app.root
97
- assert_equal({}, app.directories)
98
- assert_equal({}, app.options.marshal_dump)
99
- assert_equal({}, app.map)
100
- assert_equal App::State::READY, app.state
101
- end
102
-
103
- #
104
- # task config tests
105
- #
106
-
107
- def test_config_returns_current_configurations
11
+
12
+ def setup
13
+ super
14
+ app.root = trs.root
15
+ end
16
+
17
+ #
18
+ # instance tests
19
+ #
20
+
21
+ def test_instance_returns_current_instance_or_a_default_app
22
+ a = App.new
23
+ App.instance = a
24
+ assert_equal a, App.instance
25
+
26
+ App.instance = nil
27
+ assert_equal App, App.instance.class
28
+ end
29
+
30
+ def test_instance_initializes_new_App_if_not_set
31
+ Tap::App.instance = nil
32
+ assert Tap::App.instance.kind_of?(Tap::App)
33
+ end
34
+
35
+ #
36
+ # helpers
37
+ #
38
+
39
+ # def stub_gemspec(name, version)
40
+ # spec = Gem::Specification.new
41
+ # spec.name = name
42
+ # spec.version = version
43
+ # spec.loaded_from = "/path/to/gems"
44
+ # spec
45
+ # end
46
+
47
+ def current_threads
48
+ threads = []
49
+ ObjectSpace.garbage_collect
50
+ sleep 0.2 # sleep to give garbage collect time to run
51
+
52
+ # JRuby limits ObjectSpace to only work for Class by default
53
+ # so this line has to be made less elegant and a little slower
54
+ # ObjectSpace.each_object(Thread) {|t| threads << t.object_id}
55
+ ObjectSpace.each_object(Class) {|t| threads << t.object_id if t.kind_of?(Thread)}
56
+ threads
57
+ end
58
+
59
+ # check that no new threads are created during the block
60
+ def extended_test_with_thread_check
61
+ extended_test do
62
+ prior_threads = current_threads
63
+ yield
64
+ assert_equal prior_threads, current_threads
65
+ end
66
+ end
67
+
68
+ def test_app_documentation
69
+ pwd = app.root
70
+ assert_equal(pwd, app.root)
71
+ assert_equal( File.expand_path(pwd +'/config'), app[:config])
72
+
73
+ some_task = Task.new 'some/task'
74
+ assert_equal( App.instance , some_task.app )
75
+ assert_equal( File.expand_path(pwd +'/config/some/task.yml') , some_task.config_file)
76
+ assert_equal( {:key => 'one'}, some_task.config)
77
+
78
+ another_task = Task.new 'another/task'
79
+ assert_equal( App.instance , another_task.app )
80
+ assert_equal( File.expand_path(pwd + '/config/another/task.yml') , another_task.config_file)
81
+ assert_equal( {:key => 'two'}, another_task.config)
82
+
83
+ ###
84
+ t1 = Task.new {|task, input| input += 1 }
85
+ t1.enq 0
86
+ t1.enq 10
87
+
88
+ app.run
89
+ assert_equal [1, 11], app.results(t1)
90
+
91
+ app.aggregator.clear
92
+
93
+ t2= Task.new {|task, input| input += 10 }
94
+ t1.on_complete {|_result| t2.enq(_result) }
95
+
96
+ t1.enq 0
97
+ t1.enq 10
98
+
99
+ app.run
100
+ assert_equal [], app.results(t1)
101
+ assert_equal [11, 21], app.results(t2)
102
+
103
+ ###
104
+ t1 = Task.new {|task, input| input += 1 }
105
+ t2 = Task.new {|task, input| input += 10 }
106
+ assert_equal [t1, t2], Task.batch(t1, t2)
107
+
108
+ t1.enq 0
109
+ t2.enq 10
110
+
111
+ app.run
112
+ assert_equal [1, 11], app.results(t1)
113
+ assert_equal [10, 20], app.results(t2)
114
+
115
+ lock = Mutex.new
116
+ array = []
117
+ t1 = Task.new {|task| lock.synchronize { array << Thread.current.object_id }; sleep 0.1 }
118
+ t2 = Task.new {|task| lock.synchronize { array << Thread.current.object_id }; sleep 0.1 }
119
+
120
+ t1.multithread = true
121
+ t1.enq
122
+ t2.multithread = true
123
+ t2.enq
124
+
125
+ app.run
126
+ assert_equal 2, array.length
127
+ assert_not_equal array[0], array[1]
128
+
129
+ array = []
130
+ Task::Base.initialize(array, :push)
131
+
132
+ array.enq(1)
133
+ array.enq(2)
134
+
135
+ assert array.empty?
136
+ app.run
137
+ assert_equal [1, 2], array
138
+
139
+ array = []
140
+ m = array._method(:push)
141
+
142
+ app.enq(m, 1)
143
+ app.mq(array, :push, 2)
144
+
145
+ assert array.empty?
146
+ app.run
147
+ assert_equal [1, 2], array
148
+
149
+ ###
150
+ t1 = Tap::Task.new('add_one') {|task, input| input += 1 }
151
+ t2 = Tap::Task.new('add_five') {|task, input| input += 5 }
152
+
153
+ t1.on_complete do |_result|
154
+ _result._current < 3 ? t1.enq(_result) : t2.enq(_result)
155
+ end
156
+
157
+ t1.enq(0)
158
+ t1.enq(1)
159
+ t1.enq(2)
160
+
161
+ app.run
162
+ assert_equal [8,8,8], app.results(t2)
163
+
164
+ strio = StringIO.new("")
165
+ app._results(t2).each do |_result|
166
+ strio.puts "How #{_result._original} became #{_result._current}:"
167
+ strio.puts _result._to_s
168
+ strio.puts
169
+ end
170
+
171
+ assert_equal(
172
+ %Q{How 2 became 8:
173
+ o-[] 2
174
+ o-[add_one] 3
175
+ o-[add_five] 8
176
+
177
+ How 1 became 8:
178
+ o-[] 1
179
+ o-[add_one] 2
180
+ o-[add_one] 3
181
+ o-[add_five] 8
182
+
183
+ How 0 became 8:
184
+ o-[] 0
185
+ o-[add_one] 1
186
+ o-[add_one] 2
187
+ o-[add_one] 3
188
+ o-[add_five] 8
189
+
190
+ }, strio.string)
191
+
192
+ end
193
+
194
+ #
195
+ # State test
196
+ #
197
+
198
+ def test_state_str_documentation
199
+ assert_equal 'READY', App::State.state_str(0)
200
+ assert_nil App::State.state_str(12)
201
+ end
202
+
203
+ #
204
+ # initialization tests
205
+ #
206
+
207
+ def test_default_app
208
+ app = App.new
209
+
210
+ assert_equal Dir.pwd, app.root
211
+ assert_equal({}, app.directories)
212
+ assert_equal({}, app.options.marshal_dump)
213
+ assert_equal({}, app.map)
214
+ assert_equal(Support::ExecutableQueue, app.queue.class)
215
+ assert app.queue.empty?
216
+ assert_equal(Support::Aggregator, app.aggregator.class)
217
+ assert app.aggregator.empty?
218
+ assert_equal App::State::READY, app.state
219
+ end
220
+
221
+ #
222
+ # task config tests
223
+ #
224
+
225
+ def test_config_returns_current_configurations
108
226
  app = App.new
109
227
  expected = {
110
228
  :root => Dir.pwd,
@@ -155,7 +273,7 @@ class AppTest < Test::Unit::TestCase
155
273
  assert_equal File.expand_path("./new/root/path/to/dir"), app[:dir]
156
274
  assert_equal Logger::DEBUG, app.logger.level
157
275
  end
158
-
276
+
159
277
  def test_reconfigure_root_sets_app_root
160
278
  app = App.new
161
279
 
@@ -219,8 +337,11 @@ class AppTest < Test::Unit::TestCase
219
337
 
220
338
  class AppHandlesConfig < App
221
339
  attr_accessor :handled_configs
340
+ def initialize
341
+ self.handled_configs = []
342
+ end
222
343
  def handle_configuation(key, value)
223
- (self.handled_configs ||= []).concat [key, value]
344
+ handled_configs.concat [key, value]
224
345
  true
225
346
  end
226
347
  end
@@ -237,7 +358,7 @@ class AppTest < Test::Unit::TestCase
237
358
 
238
359
  class AppDoesNotHandleConfig < AppHandlesConfig
239
360
  def handle_configuation(key, value)
240
- (self.handled_configs ||= []).concat [key, value]
361
+ handled_configs.concat [key, value]
241
362
  false
242
363
  end
243
364
  end
@@ -251,27 +372,61 @@ class AppTest < Test::Unit::TestCase
251
372
  assert_equal [:unknown, 'value'], app.handled_configs
252
373
  assert was_in_block
253
374
  end
375
+
376
+ #
377
+ # reload test
378
+ #
379
+
380
+ def test_reload_returns_unloaded_constants
381
+ app = Tap::App.instance
382
+
383
+ Dependencies.clear
384
+ assert_equal [], app.reload
385
+
386
+ begin
387
+ assert !Object.const_defined?("AppTestTask")
388
+
389
+ Dependencies.load_paths << app['lib']
390
+ app_task_test_mod = AppTestTask
391
+
392
+ assert Object.const_defined?("AppTestTask")
393
+ assert_equal [:AppTestTask], app.reload.collect {|c| c.to_sym }
394
+ assert !Object.const_defined?("AppTestTask")
395
+ ensure
396
+ Dependencies.clear
397
+ Dependencies.load_paths.delete(app['lib'])
398
+ end
399
+ end
254
400
 
255
- #
256
- # set logger tests
257
- #
258
-
259
- def test_set_logger_extends_logger_with_support_logger
260
- output = StringIO.new('')
261
- logger = Logger.new(output)
262
- assert !logger.respond_to?(:section_break)
263
-
264
- app.logger = logger
265
- assert logger.respond_to?(:section_break)
266
- end
267
-
268
- #
269
- # TODO -- Add logging tests
270
- #
401
+ #
402
+ # lookup_const test
403
+ #
404
+
405
+ def test_lookup_const_does_not_mishandle_top_level_constants
406
+ assert !Object.const_defined?('LookupModule')
407
+ Object.const_set('LookupModule', Module.new)
408
+ assert_raise(Tap::App::LookupError) { app.lookup_const('lookup_module/file') }
409
+ end
271
410
 
411
+ #
412
+ # set logger tests
413
+ #
414
+
415
+ def test_set_logger_extends_logger_with_support_logger
416
+ output = StringIO.new('')
417
+ logger = Logger.new(output)
418
+ assert !logger.respond_to?(:section_break)
419
+
420
+ app.logger = logger
421
+ assert logger.respond_to?(:section_break)
422
+ end
423
+
424
+ #
425
+ # TODO -- Add logging tests
426
+ #
272
427
 
273
428
  #
274
- # task tests
429
+ # task_class tests
275
430
  #
276
431
 
277
432
  class TaskSubClass < Task
@@ -280,11 +435,28 @@ class AppTest < Test::Unit::TestCase
280
435
  class AnotherTask < Task
281
436
  end
282
437
 
438
+ def test_task_class_documentation
439
+ t_class = app.task_class('tap/file_task')
440
+ assert_equal Tap::FileTask, t_class
441
+
442
+ app.map = {"mapped-task" => "Tap::FileTask"}
443
+ t_class = app.task_class('mapped-task-1.0')
444
+ assert_equal Tap::FileTask, t_class
445
+ end
446
+
447
+ #
448
+ # task tests
449
+ #
450
+
283
451
  def test_task_documentation
284
- app.map = {"mapped-task" => "AppTest::AnotherTask"}
452
+ t = app.task('tap/file_task')
453
+ assert_equal Tap::FileTask, t.class
454
+ assert_equal 'tap/file_task', t.name
455
+
456
+ app.map = {"mapped-task" => "Tap::FileTask"}
285
457
 
286
458
  t = app.task('mapped-task-1.0', :key => 'value')
287
- assert_equal AnotherTask, t.class
459
+ assert_equal Tap::FileTask, t.class
288
460
  assert_equal "mapped-task-1.0", t.name
289
461
  assert_equal 'value', t.config[:key]
290
462
  end
@@ -317,9 +489,15 @@ class AppTest < Test::Unit::TestCase
317
489
 
318
490
  def test_task_looks_up_task_classes_along_Dependencies_load_paths
319
491
  begin
492
+ assert !Object.const_defined?("AppTestTask")
493
+
320
494
  Dependencies.load_paths << app['lib']
321
- assert_equal AppTestTask, app.task("AppTestTask").class
495
+ t = app.task("AppTestTask")
496
+
497
+ assert Object.const_defined?("AppTestTask")
498
+ assert_equal AppTestTask, t.class
322
499
  ensure
500
+ Dependencies.clear
323
501
  Dependencies.load_paths.delete(app['lib'])
324
502
  end
325
503
  end
@@ -333,10 +511,15 @@ class AppTest < Test::Unit::TestCase
333
511
  #
334
512
 
335
513
  def test_task_class_name_documentation
336
- t = Task.new
337
- app.map = {"mapped-task" => "AnotherTask"}
338
- assert_equal "Tap::Task", app.task_class_name(t)
339
- assert_equal "AnotherTask", app.task_class_name('mapped-task-1.0')
514
+ app.map = {"mapped-task" => "Tap::FileTask"}
515
+ assert_equal "some/task_class", app.task_class_name('some/task_class')
516
+ assert_equal "Tap::FileTask", app.task_class_name('mapped-task-1.0')
517
+
518
+ t1 = Task.new
519
+ assert_equal "Tap::Task", app.task_class_name(t1)
520
+
521
+ t2 = ObjectWithExecute.new.extend Tap::Task::Base
522
+ assert_equal "ObjectWithExecute", app.task_class_name(t2)
340
523
  end
341
524
 
342
525
  def test_task_class_name_returns_task_class_name
@@ -346,13 +529,13 @@ class AppTest < Test::Unit::TestCase
346
529
  subtask = TaskSubClass.new
347
530
  assert_equal "AppTest::TaskSubClass", app.task_class_name(subtask)
348
531
 
349
- non_task = Object.new
532
+ non_task = ObjectWithExecute.new
350
533
  non_task.extend Tap::Task::Base
351
- assert_equal "Object", app.task_class_name(non_task)
534
+ assert_equal "ObjectWithExecute", app.task_class_name(non_task)
352
535
  end
353
536
 
354
- def test_task_class_name_returns_deversioned_camelized_name
355
- assert_equal "AppTest::TaskSubClass", app.task_class_name("app_test/task_sub_class-1.1")
537
+ def test_task_class_name_returns_deversioned_name
538
+ assert_equal "app_test/task_sub_class", app.task_class_name("app_test/task_sub_class-1.1")
356
539
  end
357
540
 
358
541
  def test_task_class_name_resolves_names_using_map
@@ -361,145 +544,185 @@ class AppTest < Test::Unit::TestCase
361
544
  end
362
545
 
363
546
  #
364
- # config_templates tests
365
- #
547
+ # each_config_template tests
548
+ #
366
549
 
367
- def test_config_templates_documentation
368
- simple = tempfile
550
+ def test_each_config_template_documentation
551
+ simple = output_tempfile
369
552
  File.open(simple, "w") {|f| f << "key: value"}
370
- assert_equal([{"key" => "value"}], app.config_templates(simple))
553
+ assert_equal([{"key" => "value"}], app.each_config_template(simple))
371
554
 
372
- array_with_erb = tempfile
373
- File.open(array_with_erb, "w") do |f|
555
+ erb = output_tempfile
556
+ File.open(erb, "w") {|f| f << "app: <%= app.object_id %>\nfilepath: <%= filepath %>"}
557
+ assert_equal([{"app" => app.object_id, "filepath" => erb}], app.each_config_template(erb))
558
+
559
+ batched_with_erb = output_tempfile
560
+ File.open(batched_with_erb, "w") do |f|
374
561
  f << %Q{
375
562
  - key: <%= 1 %>
376
563
  - key: <%= 1 + 1 %>}
377
564
  end
378
- assert_equal([{"key" => 1}, {"key" => 2}], app.config_templates(array_with_erb))
379
- end
380
-
381
- def test_config_templates_retrieves_templates_for_the_specified_task_config_file
382
- filepath = app.filepath('config', "version.yml")
383
- assert File.exists?(filepath)
384
- assert_equal [{"version" => "empty"}], app.config_templates(filepath)
385
-
386
- filepath = app.filepath('config', "version-0.1.yml")
387
- assert File.exists?(filepath)
388
- assert_equal [{"version" => 0.1}], app.config_templates(filepath)
389
- end
390
-
391
- def test_config_templates_can_load_an_array_of_templates
392
- filepath = app.filepath('config', "batch.yml")
393
- assert File.exists?(filepath)
394
- assert_equal [{"key" => "one"}, {"key" => "two"}], app.config_templates(filepath)
395
- end
565
+ assert_equal([{"key" => 1}, {"key" => 2}], app.each_config_template(batched_with_erb))
566
+ end
567
+
568
+ def test_each_config_template_retrieves_templates_for_versioned_config_files
569
+ filepath = app.filepath('config', "version.yml")
570
+ assert File.exists?(filepath)
571
+ assert_equal [{"version" => "empty"}], app.each_config_template(filepath)
572
+
573
+ filepath = app.filepath('config', "version-0.1.yml")
574
+ assert File.exists?(filepath)
575
+ assert_equal [{"version" => 0.1}], app.each_config_template(filepath)
576
+ end
577
+
578
+ def test_each_config_template_can_load_an_array_of_templates
579
+ filepath = app.filepath('config', "batch.yml")
580
+ assert File.exists?(filepath)
581
+ assert_equal [{"key" => "one"}, {"key" => "two"}], app.each_config_template(filepath)
582
+ end
396
583
 
397
- def test_config_templates_returns_empty_template_even_if_config_file_does_not_exist
584
+ def test_each_config_template_returns_empty_template_even_if_config_file_does_not_exist
398
585
  filepath = app.filepath('config', "non_existant.yml")
399
586
  assert !File.exists?(filepath)
400
- assert_equal [{}], app.config_templates(filepath)
587
+ assert_equal [{}], app.each_config_template(filepath)
401
588
  end
402
589
 
403
- def test_config_templates_returns_empty_template_if_config_file_is_empty
590
+ def test_each_config_template_returns_empty_template_if_config_file_is_empty
404
591
  filepath = app.filepath('config', "empty.yml")
405
592
  assert File.exists?(filepath)
406
593
  assert_equal "", File.read(filepath)
407
- assert_equal [{}], app.config_templates(filepath)
594
+ assert_equal [{}], app.each_config_template(filepath)
408
595
  end
409
596
 
410
- def test_config_templates_templates_using_erb
411
- filepath = app.filepath('config', "erb.yml")
412
- assert_equal [{"sum" => 3}], app.config_templates(filepath)
413
- end
597
+ def test_each_config_template_templates_using_erb
598
+ filepath = app.filepath('config', "erb.yml")
599
+ assert_equal [{"filepath" => filepath, "app" => app.object_id}], app.each_config_template(filepath)
600
+ end
414
601
 
415
602
  #
416
- # execute test
603
+ # config_filepath test
417
604
  #
418
605
 
419
- # def test_execute_executes_task_with_inputs
420
- # t = Task.new(&add_one)
421
- # t.enq(1,2,3)
422
- #
423
- # app.execute(t)
424
- # assert_inputs(t => [1,2,3])
425
- # assert_outputs(t => [2,3,4])
426
- # assert_equal [1,2,3], runlist
427
- # assert_audits(t.results,
428
- # 0 => [[nil,1], [t,2]],
429
- # 1 => [[nil,2], [t,3]],
430
- # 2 => [[nil,3], [t,4]])
431
- # end
606
+ def test_config_filepath
607
+ assert_equal nil, app.config_filepath(nil)
608
+ assert_equal File.join(app['config'], "task/name.yml"), app.config_filepath("task/name")
609
+ end
610
+
611
+ #
612
+ # ready test
613
+ #
614
+
615
+ def test_ready_sets_state_to_READY_unless_running
616
+ app.instance_variable_set('@state', App::State::STOP)
617
+ assert_not_equal App::State::READY, app.state
618
+
619
+ assert_equal app, app.ready
620
+ assert_equal App::State::READY, app.state
621
+ end
622
+
623
+ def test_ready_does_not_sets_state_to_READY_when_running
624
+ was_in_block = false
625
+
626
+ t = Tap::Task.new do |task|
627
+ assert_equal App::State::RUN, app.state
628
+ task.app.ready
629
+ assert_equal App::State::RUN, app.state
630
+
631
+ task.app.stop
632
+ assert_equal App::State::STOP, app.state
633
+ task.app.ready
634
+ assert_equal App::State::STOP, app.state
635
+
636
+ was_in_block = true
637
+ end
638
+
639
+ with_options :debug => true do
640
+ t.enq
641
+ app.run
642
+ end
643
+
644
+ assert was_in_block
645
+ end
646
+
647
+ #
648
+ # run tests
649
+ #
650
+
651
+ def test_run_single_task
652
+ t = Task.new(&add_one)
653
+ with_options :debug => true do
654
+ t.enq 1
655
+ app.run
656
+ end
657
+
658
+ assert_audit_equal(ExpAudit[[nil, 1], [t,2]], app._results(t).first)
659
+ assert_equal [1], runlist
660
+ end
432
661
 
433
- #
434
- # run tests
435
- #
662
+ def test_run_single_task_from_a_thread
663
+ t = Task.new(&add_one)
664
+ with_options :debug => true do
665
+ t.enq 1
666
+ th = Thread.new { app.run }
667
+ th.join
668
+ end
436
669
 
437
- def test_run_single_task
438
- t = Task.new(&add_one)
439
- app.run(t,1,2,3)
440
- assert_inputs(t => [1,2,3])
441
- assert_outputs(t => [2,3,4])
442
- assert_equal [1,2,3], runlist
443
- assert_audits(t.results,
444
- 0 => [[nil,1], [t,2]],
445
- 1 => [[nil,2], [t,3]],
446
- 2 => [[nil,3], [t,4]])
447
- end
448
-
449
- def test_run_single_task_from_a_thread
450
- t = Task.new(&add_one)
451
-
452
- th = Thread.new { app.run(t,1,2,3) }
453
- th.join
454
-
455
- assert_inputs(t => [1,2,3])
456
- assert_outputs(t => [2,3,4])
457
- assert_equal [1,2,3], runlist
458
- assert_audits(t.results,
459
- 0 => [[nil,1], [t,2]],
460
- 1 => [[nil,2], [t,3]],
461
- 2 => [[nil,3], [t,4]])
462
- end
463
-
464
- #
465
- # multithread test
466
- #
670
+ assert_audit_equal(ExpAudit[[nil, 1], [t,2]], app._results(t).first)
671
+ assert_equal [1], runlist
672
+ end
673
+
674
+ #
675
+ # multithread test
676
+ #
467
677
 
468
678
  def test_main_thread_execution_waits_for_multithread_execution
469
679
  extended_test do
470
680
  main_thread_ran = false
681
+ check_thread_ran = false
471
682
  multithread_ran = false
472
683
 
473
684
  multithread_executing = false
474
685
  main_thread_did_not_wait = false
686
+ multithread_did_not_wait = false
475
687
 
476
- t1 = Task.new do |task, input|
688
+ t1 = Task.new do |task|
477
689
  main_thread_ran = true
478
690
  main_thread_did_not_wait = true if multithread_executing
479
691
  end
480
692
 
481
- t2 = Task.new do |task, input|
693
+ t2 = Task.new do |task|
694
+ check_thread_ran = true
695
+ multithread_did_not_wait = true if multithread_executing
696
+ end
697
+ t2.multithread = true
698
+
699
+ t3 = Task.new do |task|
482
700
  multithread_ran = true
483
701
  multithread_executing = true
484
702
 
485
703
  # sleep is necessary so the other threads
486
704
  # have an opportunity to execute
487
- sleep(0.2)
705
+ sleep(0.1)
488
706
 
489
707
  multithread_executing = false
490
708
  end
491
- t2.multithread = true
709
+ t3.multithread = true
492
710
 
493
- t2.enq nil
494
- t1.enq nil
711
+ t3.enq
712
+ t2.enq
713
+ t1.enq
495
714
 
496
- app.run
715
+ with_options :debug => true do
716
+ app.run
717
+ end
497
718
 
498
719
  assert main_thread_ran
499
720
  assert multithread_ran
721
+ assert check_thread_ran
500
722
  assert !main_thread_did_not_wait
723
+ assert multithread_did_not_wait
501
724
  end
502
- end
725
+ end
503
726
 
504
727
  def test_multithread_execution_waits_for_main_thread_execution
505
728
  extended_test do
@@ -509,430 +732,326 @@ class AppTest < Test::Unit::TestCase
509
732
  multithread_did_not_wait = false
510
733
  main_thread_executing = false
511
734
 
512
- t1 = Task.new do |task, input|
735
+ t1 = Task.new do |task|
513
736
  multithread_ran = true
514
737
  multithread_did_not_wait = true if main_thread_executing
515
738
  end
516
739
  t1.multithread = true
517
-
518
- t2 = Task.new do |task, input|
740
+
741
+ t2 = Task.new do |task|
519
742
  main_thread_ran = true
520
743
  main_thread_executing = true
521
744
 
522
745
  # sleep is necessary so the other threads
523
746
  # have an opportunity to execute
524
- sleep(0.2)
747
+ sleep(0.1)
525
748
 
526
749
  main_thread_executing = false
527
750
  end
528
751
 
529
- t1.enq nil
530
- t2.enq nil
752
+ t2.enq
753
+ t1.enq
531
754
 
532
- app.run
755
+ with_options :debug => true do
756
+ app.run
757
+ end
533
758
 
534
759
  assert main_thread_ran
535
760
  assert multithread_ran
536
761
  assert !multithread_did_not_wait
537
762
  end
538
- end
539
-
540
- def test_only_max_threads_will_be_executed_at_a_time
541
- extended_test do
542
- max_threads = 0
543
- n_threads = 0
544
-
545
- # the logic of this test is that IF app executes
546
- # in a multithreaded manner, then max_threads will
547
- # be greater than 1. Furthermore, IF the :max_threads
548
- # option is respected, then max_threads shouldn't be
549
- # greater than 2.
550
- block = lambda do
551
- n_threads += 1
552
- max_threads = n_threads if n_threads > max_threads
553
-
554
- # sleep is necessary so the other threads
555
- # have an opportunity to execute
556
- sleep(0.2)
557
-
558
- n_threads -= 1
559
- nil
560
- end
561
-
562
- t1 = Task.new(&block)
563
- t2 = Task.new(&block)
564
- t3 = Task.new(&block)
565
-
566
- with_options(:max_threads => 2) do
567
- t1.enq nil
568
- t2.enq nil
569
- t3.enq nil
570
- app.multithread(t1,t2,t3)
571
-
572
- app.run
573
-
574
- assert_equal 2, max_threads
575
- end
576
- end
577
- end
578
-
579
- def test_a_multithreaded_task_executes_only_on_one_thread_at_a_time
580
- extended_test do
581
- max_threads = 0
582
- n_threads = 0
583
- count = 0
584
- threads = []
763
+ end
764
+
765
+ def test_only_max_threads_will_be_executed_at_a_time
766
+ extended_test do
767
+ max_threads = 0
768
+ n_threads = 0
585
769
 
586
- # if multithread execution happened, then max_threads will be greater than 1.
587
- # (here we want max_threads to be == 1 indicating t1 was only executed on one
588
- # task at a time, even though it may have been executed on multiple threads)
589
- t1 = Task.new do |task, input|
590
- n_threads += 1
591
- max_threads = n_threads if n_threads > max_threads
592
-
593
- threads << Thread.current
594
- count += 1
595
- task.enq nil if count < 3
770
+ # the logic of this test is that if app executes
771
+ # in a multithreaded manner, then max_threads will
772
+ # be greater than 1. Furthermore, if the max_threads
773
+ # option is respected, then max_threads shouldn't be
774
+ # greater than 2.
775
+ block = lambda do |task|
776
+ n_threads += 1
777
+ max_threads = n_threads if n_threads > max_threads
596
778
 
597
779
  # sleep is necessary so the other threads
598
780
  # have an opportunity to execute
599
- sleep(0.2)
781
+ sleep(0.1)
600
782
 
601
- n_threads -= 1
602
- nil
603
- end
783
+ n_threads -= 1
784
+ nil
785
+ end
604
786
 
605
- t1.enq nil
606
- t1.multithread = true
607
-
608
- app.run
609
-
610
- assert max_threads == 1
611
- assert count == 3
612
- assert threads[0] != threads[1]
613
- end
614
- end
615
-
616
- # inconsistent test ruby, windows
617
- def test_multithreaded_tasks_execute_in_order
618
- extended_test do
619
- max_threads = 0
620
- n_threads = 0
621
- runlist = []
622
-
623
- # if the :max_threads option is respected, then max_threads
624
- # shouldn't be greater than 2. if multithread execution
625
- # happened, then max_threads will be greater than 1.
626
- block = lambda do |task, input|
627
- runlist << input
628
-
629
- n_threads += 1
630
- max_threads = n_threads if n_threads > max_threads
631
-
632
- # sleep is necessary so the other threads
633
- # have an opportunity to execute
634
- sleep(0.2)
635
-
636
- n_threads -= 1
637
- nil
638
- end
639
-
640
- t1 = Task.new(&block)
641
- t2 = Task.new(&block)
642
- t3 = Task.new(&block)
643
-
644
- with_options(:max_threads => 2) do
645
- t1.enq 1, 2
646
- t2.enq 3
647
- t3.enq 4
648
- app.multithread(t1,t2,t3)
649
-
650
- app.run
651
-
652
- assert_equal 2, max_threads
653
- # t1 passes [1,2] as inputs
654
- # t1(1) executes, then pauses, leading to
655
- # t2(3) executing. Next t1 unpauses to execute
656
- # t1(2) because no more threads are available. Lastly
657
- # t3(4) executes
658
- assert_equal [1,3,2,4], runlist
659
- end
660
- end
661
- end
662
-
663
- def current_threads
664
- threads = []
665
- ObjectSpace.garbage_collect
666
- sleep 0.2 # sleep to give garbage collect time to run
667
-
668
- # JRuby limits ObjectSpace to only work for Class by default
669
- # so this line has to be made less elegant and a little slower
670
- # ObjectSpace.each_object(Thread) {|t| threads << t.object_id}
671
- ObjectSpace.each_object(Class) {|t| threads << t.object_id if t.kind_of?(Thread)}
672
- threads
673
- end
674
-
675
- def extended_test_with_thread_check
676
- extended_test do
677
- prior_threads = current_threads
678
- yield
679
- assert_equal prior_threads, current_threads
787
+ t1 = Task.new(&block)
788
+ t2 = Task.new(&block)
789
+ t3 = Task.new(&block)
790
+
791
+ with_options(:max_threads => 2) do
792
+ [t1,t2,t3].each do |t|
793
+ t.enq
794
+ t.multithread = true
795
+ end
796
+
797
+ with_options :debug => true do
798
+ app.run
799
+ end
800
+
801
+ assert_equal 2, max_threads
802
+ end
680
803
  end
681
- end
682
-
683
- def test_threads_are_clear_after_clean_multithread_exit
684
- extended_test_with_thread_check do
685
- max_threads = 0
686
- n_threads = 0
687
-
688
- tasks = Array.new(3) do
689
- Task.new do |task, inputs|
690
- n_threads += 1
691
- max_threads = n_threads if n_threads > max_threads
692
- sleep 0.2
693
- n_threads -= 1
804
+ end
805
+
806
+ def test_no_new_threads_appear_after_clean_multithread_exit
807
+ extended_test_with_thread_check do
808
+ max_threads = 0
809
+ n_threads = 0
810
+
811
+ tasks = Array.new(3) do
812
+ Task.new do |task|
813
+ n_threads += 1
814
+ max_threads = n_threads if n_threads > max_threads
815
+ sleep 0.1
816
+ n_threads -= 1
694
817
  end
695
818
  end
696
819
  tasks.each do |task|
697
820
  task.multithread = true
698
- task.enq nil
821
+ task.enq
699
822
  end
700
823
 
701
824
  app.run
702
825
 
703
826
  assert_equal 3, max_threads
704
- end
705
- end
706
-
707
- def test_threads_are_clear_after_clean_main_thread_exit
708
- extended_test_with_thread_check do
709
- max_threads = 0
710
- n_threads = 0
711
-
712
- tasks = Array.new(3) do
713
- Task.new do |task, inputs|
714
- n_threads += 1
715
- max_threads = n_threads if n_threads > max_threads
716
- sleep 0.2
717
- n_threads -= 1
827
+ end
828
+ end
829
+
830
+ # JRuby inconsistent test?
831
+ # 2 max_threads expected but was 1
832
+ def test_no_new_threads_appear_after_clean_main_thread_exit
833
+ extended_test_with_thread_check do
834
+ max_threads = 0
835
+ n_threads = 0
836
+
837
+ tasks = Array.new(3) do
838
+ Task.new do |task|
839
+ n_threads += 1
840
+ max_threads = n_threads if n_threads > max_threads
841
+ sleep 0.1
842
+ n_threads -= 1
718
843
  end
719
844
  end
720
845
  non_threaded = tasks.shift
721
846
  tasks.each do |task|
722
847
  task.multithread = true
723
- task.enq nil
848
+ task.enq
724
849
  end
725
- non_threaded.enq nil
850
+ non_threaded.enq
726
851
 
727
852
  app.run
728
853
 
729
854
  assert_equal 2, max_threads
730
- end
731
- end
855
+ end
856
+ end
732
857
 
733
-
734
858
  #
735
859
  # stop test
736
860
  #
737
861
 
738
862
  def test_stop_prevents_non_executing_tasks_from_executing_on_main_thread
739
- extended_test do
863
+ extended_test_with_thread_check do
740
864
  count = 0
741
865
  tasks = Array.new(5) do
742
- Task.new do |task, inputs|
866
+ Task.new do |task|
743
867
  count += 1
744
868
  app.stop if count == 2
745
869
  end
746
870
  end
747
- tasks.each do |task|
748
- task.enq nil
749
- end
871
+ tasks.each do |task|
872
+ task.enq
873
+ end
750
874
 
751
- # under these conditions, 2 tasks should be
752
- # executed on 2 threads, and 2 additional tasks
753
- # dequeued into the thread queue. on stop, the 2
754
- # executing tasks should finish normally, and NO MORE
755
- # tasks executed. The waiting tasks will be requeued.
756
- with_options :max_threads => 2 do
757
- app.run
758
- end
759
-
760
- assert_equal 2, count
761
- assert_equal 3, app.queue.size
762
-
763
- queued_tasks = []
764
- while !app.queue.empty?
765
- task, inputs = app.queue.deq
766
- queued_tasks << task.object_id
767
- end
768
-
769
- # check that the requeued tasks are in order
770
- assert_equal tasks[2...5].collect {|t| t.object_id}, queued_tasks
771
- end
875
+ # under these conditions, 2 tasks should be
876
+ # executed on 2 threads, and 2 additional tasks
877
+ # dequeued into the thread queue. on stop, the 2
878
+ # executing tasks should finish normally, and NO MORE
879
+ # tasks executed. The waiting tasks will be requeued.
880
+ with_options :max_threads => 2, :debug => true do
881
+ app.run
882
+ end
883
+
884
+ assert_equal 2, count
885
+ assert_equal 3, app.queue.size
886
+
887
+ queued_tasks = []
888
+ while !app.queue.empty?
889
+ task, inputs = app.queue.deq
890
+ queued_tasks << task
891
+ end
892
+
893
+ # check that the requeued tasks are in order
894
+ assert_equal tasks[2...5], queued_tasks
895
+ end
772
896
  end
773
897
 
774
898
  def test_stop_prevents_non_executing_tasks_from_executing_on_threads_and_requeues_thread_queue
775
- extended_test do
899
+ extended_test_with_thread_check do
776
900
  count = 0
777
901
  tasks = Array.new(5) do
778
- Task.new do |task, inputs|
902
+ Task.new do |task|
779
903
  count += 1
780
- sleep 0.2
904
+ sleep 0.1
781
905
  app.stop if count == 2
782
- sleep 0.2
906
+ sleep 0.1
783
907
  end
784
908
  end
785
- tasks.each do |task|
786
- task.multithread = true
787
- task.enq nil
788
- end
909
+ tasks.each do |task|
910
+ task.multithread = true
911
+ task.enq
912
+ end
789
913
 
790
- # under these conditions, 2 tasks should be
791
- # executed on 2 threads, and 2 additional tasks
792
- # dequeued into the thread queue. on stop, the 2
793
- # executing tasks should finish normally, and NO MORE
794
- # tasks executed. The waiting tasks will be requeued.
795
- with_options :max_threads => 2 do
796
- app.run
797
- end
798
-
799
- assert_equal 2, count
800
- assert_equal 3, app.queue.size
801
-
802
- queued_tasks = []
803
- while !app.queue.empty?
804
- task, inputs = app.queue.deq
805
- queued_tasks << task.object_id
806
- end
807
-
808
- # check that the requeued tasks are in order
809
- assert_equal tasks[2...5].collect {|t| t.object_id}, queued_tasks
810
- end
914
+ # under these conditions, 2 tasks should be
915
+ # executed on 2 threads, and 2 additional tasks
916
+ # dequeued into the thread queue. on stop, the 2
917
+ # executing tasks should finish normally, and NO MORE
918
+ # tasks executed. The waiting tasks will be requeued.
919
+ with_options :max_threads => 2, :debug => true do
920
+ app.run
921
+ end
922
+
923
+ assert_equal 2, count
924
+ assert_equal 3, app.queue.size
925
+
926
+ queued_tasks = []
927
+ while !app.queue.empty?
928
+ task, inputs = app.queue.deq
929
+ queued_tasks << task
930
+ end
931
+
932
+ # check that the requeued tasks are in order
933
+ assert_equal tasks[2...5], queued_tasks
934
+ end
811
935
  end
812
936
 
813
937
  #
814
938
  # terminate test
815
939
  #
816
940
 
817
- # TODO -- REWORD
818
941
  def test_terminate_from_main_thread_raises_run_error
819
- extended_test do
820
- was_not_terminated = false
821
- task = Task.new do |task, inputs|
942
+ extended_test_with_thread_check do
943
+ was_terminated = true
944
+ task = Task.new do |t|
822
945
  app.terminate
823
- task.check_terminate
824
- was_not_terminated = true
946
+ t.check_terminate
947
+ was_terminated = false
825
948
  end
826
949
 
827
- task.enq nil
950
+ task.enq
828
951
  with_options :debug => true do
829
952
  begin
830
953
  app.run
831
954
  flunk "no error was raised"
832
955
  rescue
833
956
  assert $!.kind_of?(Tap::Support::RunError)
834
- assert $!.original_error.kind_of?(Tap::App::TerminateError)
835
- assert $!.terminate_errors.empty?
957
+ assert $!.errors.empty?
836
958
  end
837
959
  end
838
-
839
- assert !was_not_terminated
840
- end
960
+
961
+ assert was_terminated
962
+ end
841
963
  end
842
964
 
843
- # TODO -- REWORD
844
965
  def test_terminate_from_main_thread_when_error_is_handled_still_raises_error
845
- extended_test do
966
+ extended_test_with_thread_check do
846
967
  terminate_error_handled = false
847
- task = Task.new do |task, inputs|
968
+ task = Task.new do |t|
848
969
  begin
849
970
  app.terminate
850
- task.check_terminate
971
+ t.check_terminate
851
972
  rescue
852
973
  terminate_error_handled = true
853
974
  end
854
975
  end
855
976
 
856
- task.enq nil
977
+ task.enq
857
978
  with_options :debug => true do
858
979
  begin
859
980
  app.run
860
981
  flunk "no error was raised"
861
982
  rescue
862
983
  assert $!.kind_of?(Tap::Support::RunError)
863
- assert $!.original_error.kind_of?(Tap::App::TerminateError)
864
- assert $!.terminate_errors.empty?
984
+ assert $!.errors.empty?
865
985
  end
866
986
  end
867
-
868
- assert terminate_error_handled
869
- end
987
+
988
+ assert terminate_error_handled
989
+ end
870
990
  end
871
991
 
872
- # TODO -- REWORD
873
992
  def test_terminate_raises_error_on_each_execution_thread_and_requeues_thread_queue
874
- extended_test do
993
+ extended_test_with_thread_check do
875
994
  count = 0
876
995
  some_thread_was_not_terminated = false
877
996
  tasks = Array.new(5) do
878
- Task.new do |task, inputs|
997
+ Task.new do |task|
879
998
  count += 1
880
999
  app.terminate if count == 2
881
- sleep 1
1000
+ sleep 0.8
882
1001
  task.check_terminate
883
1002
  some_thread_was_not_terminated = true
884
1003
  end
885
1004
  end
886
- tasks.each do |task|
887
- task.multithread = true
888
- task.enq nil
889
- end
1005
+ tasks.each do |task|
1006
+ task.multithread = true
1007
+ task.enq
1008
+ end
890
1009
 
891
- # under these conditions, 2 tasks should be
892
- # executed on 2 threads, and 2 additional tasks
893
- # dequeued into the thread queue. on stop, the 2
894
- # executing tasks should be terminated, and NO MORE
895
- # tasks executed. The waiting tasks will be requeued.
896
- with_options :max_threads => 2, :debug => true do
1010
+ # under these conditions, 2 tasks should be
1011
+ # executed on 2 threads, and 2 additional tasks
1012
+ # dequeued into the thread queue. on stop, the 2
1013
+ # executing tasks should be terminated, and NO MORE
1014
+ # tasks executed. The waiting tasks will be requeued.
1015
+ with_options :max_threads => 2, :debug => true do
897
1016
  begin
898
1017
  app.run
899
1018
  flunk "no error was raised"
900
1019
  rescue
901
1020
  assert $!.kind_of?(Tap::Support::RunError)
902
- assert $!.original_error.kind_of?(Tap::App::TerminateError)
903
- assert $!.terminate_errors.empty?
1021
+ assert $!.errors.empty?
904
1022
  end
905
- end
906
-
907
- assert_equal 2, count
908
- assert_equal 3, app.queue.size
909
- assert !some_thread_was_not_terminated
1023
+ end
1024
+
1025
+ assert_equal 2, count
1026
+ assert_equal 3, app.queue.size
1027
+ assert !some_thread_was_not_terminated
910
1028
 
911
- queued_tasks = []
912
- while !app.queue.empty?
913
- task, inputs = app.queue.deq
914
- queued_tasks << task.object_id
915
- end
916
-
917
- # check that the requeued tasks are in order
918
- assert_equal tasks[2...5].collect {|t| t.object_id}, queued_tasks
919
- end
1029
+ queued_tasks = []
1030
+ while !app.queue.empty?
1031
+ task, inputs = app.queue.deq
1032
+ queued_tasks << task
1033
+ end
1034
+
1035
+ # check that the requeued tasks are in order
1036
+ assert_equal tasks[2...5], queued_tasks
1037
+ end
920
1038
  end
921
1039
 
922
- # TODO -- REWORD
923
1040
  # JRuby inconsistent test
1041
+ # RESOLVED? 2008/01/29
1042
+ # -- still can be an issue, if the sleep time is too short --
924
1043
  def test_terminate_on_thread_when_error_is_handled_still_raises_error
925
- extended_test do
1044
+ extended_test_with_thread_check do
926
1045
  count = 0
927
1046
  some_thread_was_not_terminated = false
928
1047
  handled_count = 0
929
1048
 
930
1049
  tasks = Array.new(5) do
931
- Task.new do |task, inputs|
1050
+ Task.new do |task|
932
1051
  count += 1
933
1052
  begin
934
1053
  app.terminate if count == 2
935
- sleep 1
1054
+ sleep 0.8
936
1055
  task.check_terminate
937
1056
  some_thread_was_not_terminated = true
938
1057
  rescue
@@ -940,31 +1059,30 @@ class AppTest < Test::Unit::TestCase
940
1059
  end
941
1060
  end
942
1061
  end
943
- tasks.each do |task|
944
- task.multithread = true
945
- task.enq nil
946
- end
1062
+ tasks.each do |task|
1063
+ task.multithread = true
1064
+ task.enq
1065
+ end
947
1066
 
948
- # under these conditions, 2 tasks should be
949
- # executed on 2 threads, and 2 additional tasks
950
- # dequeued into the thread queue. on stop, the 2
951
- # executing tasks should be terminated, and NO MORE
952
- # tasks executed. The waiting tasks will be requeued.
953
- with_options :max_threads => 2, :debug => true do
1067
+ # under these conditions, 2 tasks should be
1068
+ # executed on 2 threads, and 2 additional tasks
1069
+ # dequeued into the thread queue. on stop, the 2
1070
+ # executing tasks should be terminated, and NO MORE
1071
+ # tasks executed. The waiting tasks will be requeued.
1072
+ with_options :max_threads => 2, :debug => true do
954
1073
  begin
955
1074
  app.run
956
1075
  flunk "no error was raised"
957
1076
  rescue
958
1077
  assert $!.kind_of?(Tap::Support::RunError)
959
- assert $!.original_error.kind_of?(Tap::App::TerminateError)
960
- assert $!.terminate_errors.empty?
1078
+ assert $!.errors.empty?
961
1079
  end
962
- end
963
-
964
- assert !some_thread_was_not_terminated
965
- assert_equal 2, count
966
- assert_equal 2, handled_count
967
- assert_equal 3, app.queue.size
1080
+ end
1081
+
1082
+ assert !some_thread_was_not_terminated
1083
+ assert_equal 2, count
1084
+ assert_equal 2, handled_count
1085
+ assert_equal 3, app.queue.size
968
1086
  end
969
1087
  end
970
1088
 
@@ -973,477 +1091,567 @@ class AppTest < Test::Unit::TestCase
973
1091
  #
974
1092
 
975
1093
  def test_info_provides_information_string
976
- assert_equal 'state: 0 (READY) queue: 0 waiting: 0 (0) threads: 0', app.info
1094
+ assert_equal 'state: 0 (READY) queue: 0 thread_queue: 0 threads: 0 results: 0', app.info
977
1095
  end
978
1096
 
979
1097
  # TODO -- JRuby inconsistent test
1098
+ # RESOLVED? 2007/01/30
980
1099
  def test_info_can_be_called_during_a_run
981
1100
  extended_test do
1101
+ lock = Monitor.new
982
1102
  count = 0
983
1103
  info_str = nil
984
1104
 
985
1105
  tasks = Array.new(5) do
986
- Task.new do |task, inputs|
987
- count += 1
988
- if count == 2
989
- info_str = app.info
990
- app.stop
1106
+ Task.new do |task|
1107
+
1108
+ lock.synchronize do
1109
+ count += 1
1110
+ if count == 2
1111
+ info_str = app.info
1112
+ app.stop
1113
+ end
991
1114
  end
992
- sleep 2
1115
+
1116
+ sleep 0.8
993
1117
  end
994
1118
  end
995
- tasks.each do |task|
996
- task.enq nil
997
- task.multithread = true
998
- end
999
-
1000
- with_options :max_threads => 2 do
1119
+ tasks.each do |task|
1120
+ task.enq
1121
+ task.multithread = true
1122
+ end
1123
+
1124
+ with_options :max_threads => 2 do
1001
1125
  app.run
1002
- end
1003
-
1004
- # There is some ambiguity in the info string -- threads could be
1005
- # waiting in the queue or in the thread queue, but their sum must be 3
1006
- # Regexp patterned from 'state: 1 (RUN) queue: 3 waiting: 3 (0) threads: 2'
1007
- assert info_str =~ /state: 1 \(RUN\) queue: 3 waiting: (\d) \((\d)\) threads: 2/, info_str
1008
- assert_equal 3, $1.to_i + $2.to_i
1009
- assert_equal 'state: 0 (READY) queue: 3 waiting: 3 (0) threads: 0', app.info
1010
- end
1126
+ end
1127
+
1128
+ # There is some ambiguity in the info string -- tasks
1129
+ # could be waiting in the queue or in the thread queue
1130
+ # and additionally, the thread queue may have nils to
1131
+ # signal the threads to terminate
1132
+ assert info_str =~ /state: 1 \(RUN\) queue: \d thread_queue: \d threads: 2 results: \d/, info_str
1133
+ assert_equal 'state: 0 (READY) queue: 3 thread_queue: 0 threads: 0 results: 2', app.info
1134
+ end
1135
+ end
1136
+
1137
+ #
1138
+ # enq test
1139
+ #
1140
+
1141
+ def test_enq
1142
+ t = Task.new
1143
+ assert app.queue.empty?
1144
+ app.enq(t)
1145
+ assert_equal [[t, []]], app.queue.to_a
1146
+ end
1147
+
1148
+ def test_enq_enques_each_task_in_task_batch
1149
+ t1 = Task.new
1150
+ t2 = t1.initialize_batch_obj
1151
+
1152
+ assert app.queue.empty?
1153
+ app.enq(t1)
1154
+ assert_equal [[t1, []], [t2, []]], app.queue.to_a
1155
+ end
1156
+
1157
+ def test_enq_allows_methods
1158
+ m = []._method(:push)
1159
+ assert app.queue.empty?
1160
+ app.enq(m)
1161
+ assert_equal [[m, []]], app.queue.to_a
1162
+ end
1163
+
1164
+ def test_enq_returns_enqued_task
1165
+ t = Task.new
1166
+ assert_equal t, app.enq(t)
1167
+ end
1168
+
1169
+ #
1170
+ # mq test
1171
+ #
1172
+
1173
+ def test_mq
1174
+ a = []
1175
+ assert app.queue.empty?
1176
+ m = app.mq(a, :push, 1, 2)
1177
+ assert_equal [[m, [1,2]]], app.queue.to_a
1011
1178
  end
1012
1179
 
1013
- #
1014
- # sequence tests
1015
- #
1016
-
1017
- def test_run_sequence
1018
- t1 = Task.new(&add_one)
1019
- t2 = Task.new(&add_one)
1020
-
1021
- app.sequence(t1,t2)
1022
- app.run(t1,1,2,3)
1023
-
1024
- assert_inputs(t1 => [1,2,3], t2 => [2,3,4])
1025
- assert_outputs(t1 => [2,3,4], t2 => [3,4,5])
1026
- assert_equal [1,2,3,2,3,4], runlist
1027
- assert_audits(t2.results,
1028
- 0 => [[nil,1], [t1,2], [t2,3]],
1029
- 1 => [[nil,2], [t1,3], [t2,4]],
1030
- 2 => [[nil,3], [t1,4], [t2,5]])
1031
- end
1032
-
1033
- def test_run_sequence_from_trailing_task
1034
- t1 = Task.new(&add_one)
1035
- t2 = Task.new(&add_one)
1036
-
1037
- app.sequence(t1,t2)
1038
- app.run(t2,1,2,3)
1039
-
1040
- assert_inputs(t1 => [], t2 => [1,2,3])
1041
- assert_outputs(t1 => [], t2 => [2,3,4])
1042
- assert_equal [1,2,3], runlist
1043
- assert_audits(t2.results,
1044
- 0 => [[nil,1], [t2,2]],
1045
- 1 => [[nil,2], [t2,3]],
1046
- 2 => [[nil,3], [t2,4]])
1047
- end
1048
-
1049
- #
1050
- # fork tests
1051
- #
1052
-
1053
- def test_run_fork
1054
- t1 = Task.new(&add_one)
1055
- t2 = Task.new(&add_one)
1056
- t3 = Task.new(&add_one)
1057
-
1058
- app.fork(t1, t2, t3)
1059
- app.run(t1,1,2,3)
1060
-
1061
- assert_inputs(t1 => [1,2,3], t2 => [2,3,4], t3 => [2,3,4])
1062
- assert_outputs(t1 => [2,3,4], t2 => [3,4,5], t3 => [3,4,5])
1063
- assert_equal [1,2,3, 2,3,4, 2,3,4], runlist
1064
- assert_audits(t2.results,
1065
- 0 => [[nil,1], [t1,2], [t2,3]],
1066
- 1 => [[nil,2], [t1,3], [t2,4]],
1067
- 2 => [[nil,3], [t1,4], [t2,5]])
1068
- assert_audits(t3.results,
1069
- 0 => [[nil,1], [t1,2], [t3,3]],
1070
- 1 => [[nil,2], [t1,3], [t3,4]],
1071
- 2 => [[nil,3], [t1,4], [t3,5]])
1072
- end
1073
-
1074
- #
1075
- # merge tests
1076
- #
1077
-
1078
- def test_run_merge
1079
- t1 = Task.new(&add_one)
1080
- t2 = Task.new(&add_one)
1081
- t3 = Task.new(&add_one)
1082
-
1083
- # merge by default has no conditions on execution
1084
- # so t3 should execute immediately after t1/t2 finishes
1085
- # as such the results need to be collected separately
1086
- t3_results = []
1087
- t3.on_complete do |results|
1088
- t3_results.concat results
1180
+ # #
1181
+ # # on_complete tests
1182
+ # #
1183
+ #
1184
+ # def test_on_complete
1185
+ # t1 = Task.new(&add_one)
1186
+ # t2 = Task.new(&add_one)
1187
+ # t3 = Task.new(&add_one)
1188
+ #
1189
+ # app.on_complete(t1) do |result|
1190
+ # t2.enq result
1191
+ # t3.enq result
1192
+ # end
1193
+ # with_options :debug => true do
1194
+ # t1.enq 0
1195
+ # app.run
1196
+ # end
1197
+ #
1198
+ # assert_equal [0,1,1], runlist
1199
+ # assert_audit_equal(ExpAudit[[nil,0],[t1,1],[t2,2]], app._results(t2).first)
1200
+ # assert_audit_equal(ExpAudit[[nil,0],[t1,1],[t3,2]], app._results(t3).first)
1201
+ # end
1202
+
1203
+ #
1204
+ # sequence tests
1205
+ #
1206
+
1207
+ def test_run_sequence
1208
+ t1 = Task.new(&add_one)
1209
+ t2 = Task.new(&add_one)
1210
+
1211
+ app.sequence(t1,t2)
1212
+ with_options :debug => true do
1213
+ t1.enq 0
1214
+ app.run
1089
1215
  end
1216
+
1217
+ assert_equal [0,1], runlist
1218
+ assert_audit_equal(ExpAudit[[nil,0],[t1,1],[t2,2]], app._results(t2).first)
1219
+ end
1090
1220
 
1091
- app.merge(t3, t1, t2)
1092
- app.run(t1,1,2,3)
1093
- app.run(t2,2,3,4)
1094
-
1095
- assert_inputs(t1 => [1,2,3], t2 => [2,3,4])
1096
- assert_outputs(t1 => [2,3,4], t2 => [3,4,5])
1097
- assert_equal [1,2,3, 2,3,4, 2,3,4, 3,4,5], runlist
1098
- assert_audits(t3_results,
1099
- # from t1...
1100
- 0 => [[nil,1], [t1,2], [t3,3]],
1101
- 1 => [[nil,2], [t1,3], [t3,4]],
1102
- 2 => [[nil,3], [t1,4], [t3,5]],
1103
- # from t2...
1104
- 3 => [[nil,2], [t2,3], [t3,4]],
1105
- 4 => [[nil,3], [t2,4], [t3,5]],
1106
- 5 => [[nil,4], [t2,5], [t3,6]])
1107
- end
1108
-
1109
- #
1110
- # run batched task tests
1111
- #
1112
-
1113
- def test_run_batched_task
1114
- t1 = Task.new('addition_template') do |task, input|
1115
- runlist << input
1116
- input + task.config[:factor0] + task.config[:factor1]
1117
- end
1118
- assert_equal 2, t1.batch.length
1119
-
1120
- t1_0 = t1.batch[0]
1121
- t1_1 = t1.batch[1]
1122
-
1123
- assert_equal [0, 10], [t1_0.config[:factor0], t1_0.config[:factor1]]
1124
- assert_equal [1, 20], [t1_1.config[:factor0], t1_1.config[:factor1]]
1125
-
1126
- app.run(t1,1,2,3)
1127
-
1128
- # note same inputs fed to each template
1129
- assert_equal [1,2,3, 1,2,3], runlist
1130
- assert_audits(t1_0.results,
1131
- 0 => [[nil,1], [t1_0,11]],
1132
- 1 => [[nil,2], [t1_0,12]],
1133
- 2 => [[nil,3], [t1_0,13]])
1134
- assert_audits(t1_1.results,
1135
- 0 => [[nil,1], [t1_1,22]],
1136
- 1 => [[nil,2], [t1_1,23]],
1137
- 2 => [[nil,3], [t1_1,24]])
1138
- end
1221
+ def test_run_sequence_from_trailing_task
1222
+ t1 = Task.new(&add_one)
1223
+ t2 = Task.new(&add_one)
1224
+
1225
+ app.sequence(t1,t2)
1226
+ with_options :debug => true do
1227
+ t2.enq 1
1228
+ app.run
1229
+ end
1230
+
1231
+ assert_equal [1], runlist
1232
+ assert_equal 0, app._results(t1).length
1233
+ assert_audit_equal(ExpAudit[[nil,1],[t2,2]], app._results(t2).first)
1234
+ end
1139
1235
 
1140
- def test_run_batched_task_with_existing_audit_trails
1141
- t1 = Task.new('addition_template') do |task, input|
1142
- runlist << input
1143
- input + task.config[:factor0] + task.config[:factor1]
1144
- end
1145
- assert_equal 2, t1.batch.length
1146
-
1147
- t1_0 = t1.batch[0]
1148
- t1_1 = t1.batch[1]
1149
-
1150
- assert_equal [0, 10], [t1_0.config[:factor0], t1_0.config[:factor1]]
1151
- assert_equal [1, 20], [t1_1.config[:factor0], t1_1.config[:factor1]]
1152
-
1153
- a = Support::Audit.new(1)
1154
- b = Support::Audit.new(2)
1155
- c = Support::Audit.new(3)
1156
- app.run(t1,a,b,c)
1157
-
1158
- # note same inputs fed to each template
1159
- assert_equal [1,2,3, 1,2,3], runlist
1160
- assert_audits(t1_0.results,
1161
- 0 => [[nil,1], [t1_0,11]],
1162
- 1 => [[nil,2], [t1_0,12]],
1163
- 2 => [[nil,3], [t1_0,13]])
1164
- assert_audits(t1_1.results,
1165
- 0 => [[nil,1], [t1_1,22]],
1166
- 1 => [[nil,2], [t1_1,23]],
1167
- 2 => [[nil,3], [t1_1,24]])
1168
- end
1169
-
1170
- # TODO -- JRuby inconsistent test
1171
- def test_multithread_batched_tasks_execute_cosynchronously
1236
+ #
1237
+ # fork tests
1238
+ #
1239
+
1240
+ def test_run_fork
1241
+ t1 = Task.new(&add_one)
1242
+ t2 = Task.new(&add_one)
1243
+ t3 = Task.new(&add_one)
1244
+
1245
+ app.fork(t1, t2, t3)
1246
+ with_options :debug => true do
1247
+ t1.enq 0
1248
+ app.run
1249
+ end
1250
+
1251
+ assert_equal [0,1,1], runlist
1252
+ assert_audit_equal(ExpAudit[[nil,0],[t1,1],[t2,2]], app._results(t2).first)
1253
+ assert_audit_equal(ExpAudit[[nil,0],[t1,1],[t3,2]], app._results(t3).first)
1254
+ end
1255
+
1256
+ #
1257
+ # merge tests
1258
+ #
1259
+
1260
+ def test_run_merge
1261
+ t1 = Task.new(&add_one)
1262
+ t2 = Task.new(&add_one)
1263
+ t3 = Task.new(&add_one)
1264
+
1265
+ app.merge(t3, t1, t2)
1266
+ with_options :debug => true do
1267
+ t1.enq 0
1268
+ t2.enq 10
1269
+ app.run
1270
+ end
1271
+
1272
+ assert_equal [0,10,1,11], runlist
1273
+
1274
+ assert_audits_equal([
1275
+ ExpAudit[[nil,0],[t1,1],[t3,2]],
1276
+ ExpAudit[[nil,10],[t2,11],[t3,12]]
1277
+ ], app._results(t3))
1278
+ end
1279
+
1280
+ #
1281
+ # run batched task tests
1282
+ #
1283
+
1284
+ def test_run_batched_task
1285
+ t1 = Task.new('template') do |task, input|
1286
+ runlist << input
1287
+ input + task.config[:factor]
1288
+ end
1289
+ assert_equal 2, t1.batch.length
1290
+
1291
+ t1_0 = t1.batch[0]
1292
+ t1_1 = t1.batch[1]
1293
+
1294
+ assert_equal 10, t1_0.config[:factor]
1295
+ assert_equal 22, t1_1.config[:factor]
1296
+
1297
+ with_options :debug => true do
1298
+ t1.enq 0
1299
+ app.run
1300
+ end
1301
+
1302
+ # note same input fed to each template
1303
+ assert_equal [0,0], runlist
1304
+
1305
+ assert_audits_equal([
1306
+ ExpAudit[[nil,0],[t1_0,10]],
1307
+ ExpAudit[[nil,0],[t1_1,22]]
1308
+ ], app._results(*t1.batch))
1309
+ end
1310
+
1311
+ def test_run_batched_task_with_existing_audit_trails
1312
+ t1 = Task.new('template') do |task, input|
1313
+ runlist << input
1314
+ input + task.config[:factor]
1315
+ end
1316
+ assert_equal 2, t1.batch.length
1317
+
1318
+ t1_0 = t1.batch[0]
1319
+ t1_1 = t1.batch[1]
1320
+
1321
+ assert_equal 10, t1_0.config[:factor]
1322
+ assert_equal 22, t1_1.config[:factor]
1323
+
1324
+ a = Support::Audit.new(0, :a)
1325
+ with_options :debug => true do
1326
+ t1.enq a
1327
+ app.run
1328
+ end
1329
+
1330
+ # note same input fed to each template
1331
+ assert_equal [0,0], runlist
1332
+
1333
+ assert_audits_equal([
1334
+ ExpAudit[[:a,0],[t1_0,10]],
1335
+ ExpAudit[[:a,0],[t1_1,22]]
1336
+ ], app._results(t1.batch))
1337
+ end
1338
+
1339
+ # TODO -- JRuby inconsistent test
1340
+ # RESOLVED? 2007/01/30
1341
+ def test_multithread_batched_tasks_execute_cosynchronously
1172
1342
  extended_test do
1343
+ lock = Monitor.new
1173
1344
  max_threads = 0
1174
- n_threads = 0
1175
-
1176
- block = lambda do
1177
- n_threads += 1
1178
- max_threads = n_threads if n_threads > max_threads
1345
+ n_threads = 0
1346
+
1347
+ block = lambda do |task|
1348
+ lock.synchronize do
1349
+ n_threads += 1
1350
+ max_threads = n_threads if n_threads > max_threads
1351
+ end
1179
1352
 
1180
1353
  # sleep is necessary so the other threads
1181
1354
  # have an opportunity to execute
1182
- sleep(1)
1183
-
1184
- n_threads -= 1
1185
- nil
1186
- end
1187
-
1188
- t1 = Task.new('addition_template', &block)
1189
-
1190
- assert_equal 2, t1.batch.length
1191
- t1.batch.each do |task|
1192
- task.multithread = true
1193
- task.enq nil
1194
- end
1195
-
1196
- app.run
1197
- assert_equal 2, max_threads
1355
+ sleep(0.1)
1356
+
1357
+ lock.synchronize { n_threads -= 1 }
1358
+ nil
1359
+ end
1360
+
1361
+ t1 = Task.new(&block)
1362
+ t1.initialize_batch_obj
1363
+
1364
+ assert_equal 2, t1.batch.length
1365
+ t1.multithread = true
1366
+ t1.enq
1367
+
1368
+ with_options :debug => true do
1369
+ app.run
1370
+ end
1371
+ assert_equal 2, max_threads
1198
1372
  end
1199
- end
1200
-
1201
- def test_fork_in_batched_task
1202
- t1, t2, t3 = Array.new(3) do
1203
- Task.new('addition_template') do |task, input|
1204
- runlist << input
1205
- input + task.config[:factor0] + task.config[:factor1]
1206
- end
1207
- end
1208
-
1209
- app.fork(t1, t2, t3)
1210
- app.run(t1, 1,2)
1211
-
1212
- assert_equal [
1213
- 1,2,1,2, # once for each t1 template
1214
- 11,12,22,23, 11,12,22,23, # each result into each t2 template
1215
- 11,12,22,23, 11,12,22,23 # each result into each t3 template
1216
- ], runlist
1217
-
1218
- # Note how all the inputs were registered to the tasks
1219
- # before execution... so all 4 inputs execute as a batch
1220
- # and the inputs execute for both templates
1221
- t1_0 = t1.batch[0]
1222
- t1_1 = t1.batch[1]
1223
-
1224
- t3_0 = t3.batch[0]
1225
- assert_audits(t3_0.results,
1226
- 0 => [[nil,1], [t1_0,11], [t3_0, 21]],
1227
- 1 => [[nil,2], [t1_0,12], [t3_0, 22]],
1228
- 2 => [[nil,1], [t1_1,22], [t3_0, 32]],
1229
- 3 => [[nil,2], [t1_1,23], [t3_0, 33]])
1230
-
1231
- t3_1 = t3.batch[1]
1232
- assert_audits(t3_1.results,
1233
- 0 => [[nil,1], [t1_0,11], [t3_1, 32]],
1234
- 1 => [[nil,2], [t1_0,12], [t3_1, 33]],
1235
- 2 => [[nil,1], [t1_1,22], [t3_1, 43]],
1236
- 3 => [[nil,2], [t1_1,23], [t3_1, 44]])
1237
- end
1238
-
1239
- def test_merge_batched_task
1240
- t1, t2, t3 = Array.new(3) do
1241
- Task.new('addition_template') do |task, input|
1242
- runlist << input
1243
- input + task.config[:factor0] + task.config[:factor1]
1244
- end
1245
- end
1246
-
1247
- app.merge(t3, t1, t2)
1248
- t1.enq(1,2)
1249
- t2.enq(3,4)
1250
- app.run
1251
-
1252
- assert_equal [
1253
- 1,2,1,2, # 1,2 inputs to each t1
1254
- 3,4,3,4, # 3,4 inputs to each t2
1255
- 11,12,22,23, 13,14,24,25, # t1 then t2 outputs to first t3
1256
- 11,12,22,23, 13,14,24,25 # t1 then t2 outputs to second t3
1257
- ], runlist
1258
-
1259
- t1_0 = t1.batch[0]
1260
- t1_1 = t1.batch[1]
1261
- t2_0 = t2.batch[0]
1262
- t2_1 = t2.batch[1]
1263
- t3_0 = t3.batch[0]
1264
- assert_audits(t3_0.results,
1265
- # t1 0
1266
- 0 => [[nil,1], [t1_0,11], [t3_0,21]],
1267
- 1 => [[nil,2], [t1_0,12], [t3_0,22]],
1268
- # t1 1
1269
- 2 => [[nil,1], [t1_1,22], [t3_0,32]],
1270
- 3 => [[nil,2], [t1_1,23], [t3_0,33]],
1271
- # t2 0
1272
- 4 => [[nil,3], [t2_0,13], [t3_0,23]],
1273
- 5 => [[nil,4], [t2_0,14], [t3_0,24]],
1274
- # t2 1
1275
- 6 => [[nil,3], [t2_1,24], [t3_0,34]],
1276
- 7 => [[nil,4], [t2_1,25], [t3_0,35]])
1277
- end
1278
-
1279
- #
1280
- # on_complete tests
1281
- #
1282
-
1283
- def test_on_complete
1284
- t1 = Task.new(&add_one)
1285
- t2 = Task.new(&add_one)
1286
- t3 = Task.new(&add_one)
1287
-
1288
- app.on_complete(t1) do |results|
1289
- t2.enq results.first
1290
- t3.enq results.last
1291
- end
1292
- app.run(t1,1,2,3)
1293
-
1294
- assert_inputs(t1 => [1,2,3], t2 => [2], t3 => [4])
1295
- assert_outputs(t1 => [2,3,4], t2 => [3], t3 => [5])
1296
- assert_equal [1,2,3,2,4], runlist
1297
- assert_audits(t1.results,
1298
- 0 => [[nil,1], [t1,2], [t2,3]],
1299
- 1 => [[nil,2], [t1,3]],
1300
- 2 => [[nil,3], [t1,4], [t3,5]])
1301
- end
1302
-
1303
- def test_feedback_loop
1304
- t1 = Task.new(&add_one)
1305
- t2 = Task.new(&add_one)
1306
- t3 = Task.new(&add_one)
1307
-
1373
+ end
1374
+
1375
+ def test_fork_in_batched_task
1376
+ t1, t2, t3 = Array.new(3) do
1377
+ t = Task.new(nil, :factor => 10) do |task, input|
1378
+ runlist << input
1379
+ input + task.config[:factor]
1380
+ end
1381
+ t.initialize_batch_obj(nil, :factor => 22)
1382
+ end
1383
+
1384
+ app.fork(t1, t2, t3)
1385
+ with_options :debug => true do
1386
+ t1.enq 0
1387
+ app.run
1388
+ end
1389
+
1390
+ assert_equal [
1391
+ 0,0, # once for each t1 template
1392
+ 10,10, 10,10, # first result into t2, t3 tasks
1393
+ 22,22, 22,22 # second result into t2, t3 tasks
1394
+ ], runlist
1395
+
1396
+ t1_0 = t1.batch[0]
1397
+ t1_1 = t1.batch[1]
1398
+
1399
+ t2_0 = t2.batch[0]
1400
+ t2_1 = t2.batch[1]
1401
+
1402
+ t3_0 = t3.batch[0]
1403
+ t3_1 = t3.batch[1]
1404
+
1405
+ # check t2 results
1406
+ assert_audits_equal([
1407
+ ExpAudit[[nil,0],[t1_0,10],[t2_0,20]],
1408
+ ExpAudit[[nil,0],[t1_1,22],[t2_0,32]],
1409
+ ExpAudit[[nil,0],[t1_0,10],[t2_1,32]],
1410
+ ExpAudit[[nil,0],[t1_1,22],[t2_1,44]]
1411
+ ], app._results(t2.batch))
1412
+
1413
+ # check t3 results
1414
+ assert_audits_equal([
1415
+ ExpAudit[[nil,0],[t1_0,10],[t3_0,20]],
1416
+ ExpAudit[[nil,0],[t1_1,22],[t3_0,32]],
1417
+ ExpAudit[[nil,0],[t1_0,10],[t3_1,32]],
1418
+ ExpAudit[[nil,0],[t1_1,22],[t3_1,44]]
1419
+ ], app._results(t3.batch))
1420
+ end
1421
+
1422
+ def test_merge_batched_task
1423
+ t1, t2, t3 = Array.new(3) do
1424
+ t = Task.new(nil, :factor => 10) do |task, input|
1425
+ runlist << input
1426
+ input + task.config[:factor]
1427
+ end
1428
+ t.initialize_batch_obj(nil, :factor => 22)
1429
+ end
1430
+
1431
+ app.merge(t3, t1, t2)
1432
+ t1.enq(0)
1433
+ t2.enq(2)
1434
+ with_options :debug => true do
1435
+ app.run
1436
+ end
1437
+
1438
+ assert_equal [
1439
+ 0,0, # 1 input to each t1
1440
+ 2,2, # 2 input to each t2
1441
+ 10,10,22,22, # t1 outputs to each t3
1442
+ 12,12,24,24 # t2 outputs to each t3
1443
+ ], runlist
1444
+
1445
+ t1_0 = t1.batch[0]
1446
+ t1_1 = t1.batch[1]
1447
+
1448
+ t2_0 = t2.batch[0]
1449
+ t2_1 = t2.batch[1]
1450
+
1451
+ t3_0 = t3.batch[0]
1452
+ t3_1 = t3.batch[1]
1453
+
1454
+ # check results
1455
+ assert_audits_equal([
1456
+ ExpAudit[[nil,0],[t1_0,10],[t3_0,20]],
1457
+ ExpAudit[[nil,0],[t1_1,22],[t3_0,32]],
1458
+ ExpAudit[[nil,2],[t2_0,12],[t3_0,22]],
1459
+ ExpAudit[[nil,2],[t2_1,24],[t3_0,34]],
1460
+ ExpAudit[[nil,0],[t1_0,10],[t3_1,32]],
1461
+ ExpAudit[[nil,0],[t1_1,22],[t3_1,44]],
1462
+ ExpAudit[[nil,2],[t2_0,12],[t3_1,34]],
1463
+ ExpAudit[[nil,2],[t2_1,24],[t3_1,46]]
1464
+ ], app._results(t3.batch))
1465
+ end
1466
+
1467
+ #
1468
+ # other run tests
1469
+ #
1470
+
1471
+ def test_feedback_loop
1472
+ t1 = Task.new(&add_one)
1473
+ t2 = Task.new(&add_one)
1474
+ t3 = Task.new(&add_one)
1475
+
1308
1476
  # distribute the results of t1 based on value
1309
- app.on_complete(t1) do |audits|
1310
- audits.each do |audit|
1311
- if audit < 4
1312
- t2.enq audit
1313
- else
1314
- t3.enq audit
1315
- end
1316
- end
1317
- end
1318
-
1477
+ t1.on_complete do |result|
1478
+ if result._current < 4
1479
+ t2.enq result
1480
+ else
1481
+ t3.enq result
1482
+ end
1483
+ end
1484
+
1319
1485
  # set the results of t2 to reinvoke the workflow
1320
- app.sequence(t2, t1)
1321
-
1322
- # collect the results as they come to t3
1323
- # (t3.results does not suffice because t3.results will be
1324
- # reset on each invokation)
1325
- t3_results = []
1326
- app.on_complete(t3) {|audits| t3_results.concat(audits) }
1327
-
1328
- app.run(t1,1,2,3)
1329
-
1330
- assert_audits(t3_results,
1331
- 0 => [[nil,3], [t1,4], [t3,5]],
1332
- 1 => [[nil,1], [t1,2], [t2,3], [t1,4], [t3,5]],
1333
- 2 => [[nil,2], [t1,3], [t2,4], [t1,5], [t3,6]])
1334
- end
1335
-
1336
- #
1337
- # synchronization tests
1338
- #
1339
-
1340
- def BREAK_DOES_NOT_ACTUALLY_TEST_THIStest_run_is_synchronized
1341
- extended_test do
1342
- count = 0
1343
- counter = Task.new do
1344
- count += 1
1345
- end
1346
-
1347
- t1 = Thread.new { 10000.times { app.run(counter, nil) } }
1348
- t2 = Thread.new { 10000.times { app.run(counter, nil) } }
1349
- t1.join
1350
- t2.join
1351
-
1352
- assert_equal 20000, count
1353
- end
1354
- end
1486
+ app.sequence(t2, t1)
1487
+
1488
+ with_options :debug => true do
1489
+ t1.enq(0)
1490
+ t1.enq(2)
1491
+ app.run
1492
+ end
1493
+
1494
+ assert_equal [0,2,1,3,2,4,3,5,4,5], runlist
1355
1495
 
1356
- def test_task_may_be_queued_from_task_while_task_is_running
1357
- count = 0
1358
- counter = Task.new do
1359
- count += 1
1360
- counter.enq nil if count < 3
1361
- end
1362
-
1363
- app.run(counter, nil)
1364
- assert_equal 3, count
1365
- end
1366
-
1367
- def test_task_can_queue_from_within_threaded_and_unthreaded_tasks
1368
- threaded_count = 0
1369
- threaded = Task.new do
1370
- runlist << "t"
1371
- threaded_count += 1
1372
- threaded.enq nil if threaded_count < 3
1373
- end
1374
-
1375
- not_threaded_count = 0
1376
- not_threaded = Task.new do
1377
- runlist << "n"
1378
- not_threaded_count += 1
1379
- not_threaded.enq nil if not_threaded_count < 3
1380
- end
1381
-
1382
- app.multithread(threaded)
1383
- threaded.enq nil
1384
- not_threaded.enq nil
1496
+ assert_audits_equal([
1497
+ ExpAudit[[nil,2],[t1,3],[t2,4],[t1,5],[t3,6]],
1498
+ ExpAudit[[nil,0],[t1,1],[t2,2],[t1,3],[t2,4],[t1,5],[t3,6]]
1499
+ ], app._results(t3.batch))
1500
+ end
1501
+
1502
+ #
1503
+ # _results test
1504
+ #
1385
1505
 
1386
- app.run
1387
-
1388
- assert_equal [
1389
- "t", "n",
1390
- "t", "n",
1391
- "t", "n"], runlist
1392
- assert_equal 3, threaded_count
1393
- assert_equal 3, not_threaded_count
1394
- end
1395
-
1396
- # def test_execute_is_allowed_within_non_threaded_task
1397
- # # execute within a threaded task deadlocks
1398
- # t2 = Task.new(&add_one)
1399
- # t1 = Task.new do |task, input|
1400
- # runlist << input
1401
- # app.queue.enq(t2, input)
1402
- # app.execute(t2)
1403
- #
1404
- # input += 1
1405
- # end
1406
- #
1407
- # app.run(t1, 1,2,3)
1408
- #
1409
- # # remember t2, will only show the LAST input
1410
- # # to have been processed
1411
- # assert_inputs(t1 => [1,2,3], t2 => [3])
1412
- # assert_outputs(t1 => [2,3,4], t2 => [4])
1413
- # assert_equal [1,1,2,2,3,3], runlist
1414
- # assert_audits(t1.results,
1415
- # 0 => [[nil,1], [t1,2]],
1416
- # 1 => [[nil,2], [t1,3]],
1417
- # 2 => [[nil,3], [t1,4]])
1418
- # assert_audits(t2.results,
1419
- # 0 => [[nil,3], [t2,4]])
1420
- # end
1506
+ def test__results_returns_audited_results_for_listed_sources
1507
+ t1 = Task.new {|task, input| input + 1 }
1508
+ a1 = t1._execute(0)
1509
+
1510
+ t2 = Task.new {|task, input| input + 1 }
1511
+ a2 = t2._execute(1)
1512
+
1513
+ app.aggregator.store a1
1514
+ app.aggregator.store a2
1515
+ assert_equal [a1], app._results(t1)
1516
+ assert_equal [a2, a1], app._results(t2, t1)
1517
+ assert_equal [a1, a1], app._results(t1, t1)
1518
+ end
1519
+
1520
+ #
1521
+ # results test
1522
+ #
1523
+
1524
+ def test_results_documentation
1525
+ t1 = Task.new {|task, input| input += 1 }
1526
+ t2 = Task.new {|task, input| input += 10 }
1527
+ t3 = t2.initialize_batch_obj
1528
+
1529
+ t1.enq(0)
1530
+ t2.enq(1)
1531
+
1532
+ app.run
1533
+ assert_equal [1, 11, 11], app.results(t1, t2.batch)
1534
+ assert_equal [11, 1], app.results(t2, t1)
1535
+ end
1536
+
1537
+ def test_results_returns_current_values_of__results
1538
+ t1 = Task.new {|task, input| input + 1 }
1539
+ a1 = t1._execute(0)
1540
+
1541
+ t2 = Task.new {|task, input| input + 1 }
1542
+ a2 = t2._execute(1)
1543
+
1544
+ app.aggregator.store a1
1545
+ app.aggregator.store a2
1546
+ assert_equal [1], app.results(t1)
1547
+ assert_equal [2, 1], app.results(t2, t1)
1548
+ assert_equal [1, 1], app.results(t1, t1)
1549
+ end
1550
+
1551
+ def test_results_for_various_objects
1552
+ t1 = Task.new {|task, input| input}
1421
1553
 
1554
+ t1.enq({:key => 'value'})
1555
+ t1.enq([1,2,3])
1556
+ t1.enq(2)
1557
+ t1.enq("str")
1558
+
1559
+ app.run
1560
+ assert_equal [{:key => 'value'}, [1,2,3], 2, "str"], app.results(t1)
1561
+ end
1562
+
1563
+ #
1564
+ # synchronization tests
1565
+ #
1566
+
1567
+ def test_task_may_be_queued_from_task_while_task_is_running
1568
+ count = 0
1569
+ counter = Task.new do |task|
1570
+ count += 1
1571
+ counter.enq if count < 3
1572
+ end
1573
+
1574
+ with_options :debug => true do
1575
+ counter.enq
1576
+ app.run
1577
+ end
1578
+
1579
+ assert_equal 3, count
1580
+ end
1581
+
1582
+ def test_task_can_queue_from_within_threaded_and_unthreaded_tasks
1583
+ threaded_count = 0
1584
+ threaded = Task.new do |task|
1585
+ runlist << "t"
1586
+ threaded_count += 1
1587
+ threaded.enq if threaded_count < 3
1588
+ end
1589
+
1590
+ not_threaded_count = 0
1591
+ not_threaded = Task.new do |task|
1592
+ runlist << "n"
1593
+ not_threaded_count += 1
1594
+ not_threaded.enq if not_threaded_count < 3
1595
+ end
1596
+
1597
+ threaded.multithread = true
1598
+ threaded.enq
1599
+ not_threaded.enq
1600
+
1601
+ with_options :debug => true do
1602
+ app.run
1603
+ end
1604
+
1605
+ assert_equal [
1606
+ "t", "n",
1607
+ "t", "n",
1608
+ "t", "n"], runlist
1609
+ assert_equal 3, threaded_count
1610
+ assert_equal 3, not_threaded_count
1611
+ end
1612
+
1422
1613
  def test_run_is_allowed_within_non_threaded_task
1423
- # run within a threaded task deadlocks
1424
1614
  t2 = Task.new(&add_one)
1425
1615
  t1 = Task.new do |task, input|
1426
1616
  runlist << input
1427
- app.run(t2, input)
1428
-
1617
+ t2.enq input
1618
+ app.run
1619
+
1429
1620
  input += 1
1430
1621
  end
1431
-
1432
- app.run(t1, 1,2,3)
1433
-
1434
- # remember t2, will only show the LAST input
1435
- # to have been processed
1436
- assert_inputs(t1 => [1,2,3], t2 => [3])
1437
- assert_outputs(t1 => [2,3,4], t2 => [4])
1438
- assert_equal [1,1,2,2,3,3], runlist
1439
- assert_audits(t1.results,
1440
- 0 => [[nil,1], [t1,2]],
1441
- 1 => [[nil,2], [t1,3]],
1442
- 2 => [[nil,3], [t1,4]])
1443
- assert_audits(t2.results,
1444
- 0 => [[nil,3], [t2,4]])
1445
- end
1446
-
1622
+
1623
+ with_options :debug => true do
1624
+ t1.enq 0
1625
+ app.run
1626
+ end
1627
+
1628
+ assert_equal [0,0], runlist
1629
+ assert_audit_equal(ExpAudit[[nil,0],[t1,1]], app._results(t1).first)
1630
+ assert_audit_equal(ExpAudit[[nil,0],[t2,1]], app._results(t2).first)
1631
+ end
1632
+
1633
+ def test_run_is_allowed_within_threaded_task
1634
+ t2 = Task.new(&add_one)
1635
+ t1 = Task.new do |task, input|
1636
+ runlist << input
1637
+ t2.enq input
1638
+ app.run
1639
+
1640
+ input += 1
1641
+ end
1642
+
1643
+ t1.multithread = true
1644
+ t2.multithread = true
1645
+ with_options :debug => true do
1646
+ t1.enq 0
1647
+ app.run
1648
+ end
1649
+
1650
+ assert_equal [0,0], runlist
1651
+ assert_audit_equal(ExpAudit[[nil,0],[t1,1]], app._results(t1).first)
1652
+ assert_audit_equal(ExpAudit[[nil,0],[t2,1]], app._results(t2).first)
1653
+ end
1654
+
1447
1655
  #
1448
1656
  # error tests
1449
1657
  #
@@ -1455,36 +1663,39 @@ class AppTest < Test::Unit::TestCase
1455
1663
  end
1456
1664
 
1457
1665
  def test_unhandled_exception_on_main_thread_is_logged_by_default
1458
- t = Task.new {|t,i| raise "error"}
1666
+ task = Task.new {|t| raise "error"}
1459
1667
 
1460
1668
  string = set_stringio_logger
1461
- app.run(t, nil)
1669
+ task.enq
1670
+ app.run
1462
1671
 
1463
1672
  assert string =~ /RuntimeError error/
1464
1673
  end
1465
1674
 
1466
1675
  def test_unhandled_exception_raises_run_error_on_main_thread_when_debug
1467
- t = Task.new {|t,i| raise "error"}
1676
+ task = Task.new {|t| raise "error"}
1468
1677
 
1469
1678
  with_options :debug => true do
1470
1679
  begin
1471
- app.run(t, nil)
1680
+ task.enq
1681
+ app.run
1472
1682
  flunk "no error was raised"
1473
1683
  rescue
1474
1684
  assert $!.kind_of?(Tap::Support::RunError)
1475
- assert $!.original_error.kind_of?(RuntimeError)
1476
- assert_equal "error", $!.original_error.message
1477
- assert $!.terminate_errors.empty?
1685
+ assert_equal 1 , $!.errors.length
1686
+ assert $!.errors[0].kind_of?(RuntimeError)
1687
+ assert_equal "error", $!.errors[0].message
1478
1688
  end
1479
- end
1689
+ end
1480
1690
  end
1481
1691
 
1482
1692
  def test_unhandled_exception_on_thread_is_logged_by_default
1483
- t = Task.new {|t,i| raise "error"}
1484
- t.multithread = true
1693
+ task = Task.new {|t| raise "error"}
1694
+ task.multithread = true
1485
1695
 
1486
1696
  string = set_stringio_logger
1487
- app.run(t, nil)
1697
+ task.enq
1698
+ app.run
1488
1699
 
1489
1700
  assert string =~ /RuntimeError error/
1490
1701
  end
@@ -1492,103 +1703,115 @@ class AppTest < Test::Unit::TestCase
1492
1703
  # Ruby inconsistent test
1493
1704
  # <"error"> expected but was
1494
1705
  # <"Tap::App::TerminateError">.
1706
+ # RESOLVED? -- 2008/01/29
1707
+ #
1495
1708
  def test_unhandled_exception_raises_run_error_on_thread_when_debug
1496
- t = Task.new {|t,i| raise "error"}
1497
- t.multithread = true
1709
+ task = Task.new {|t| raise "error"}
1710
+ task.multithread = true
1498
1711
 
1499
1712
  with_options :debug => true do
1500
1713
  begin
1501
- app.run(t, nil)
1714
+ task.enq
1715
+ app.run
1502
1716
  flunk "no error was raised"
1503
1717
  rescue
1504
1718
  assert $!.kind_of?(Tap::Support::RunError)
1505
- assert $!.original_error.kind_of?(RuntimeError)
1506
- assert_equal "error", $!.original_error.message
1507
- assert $!.terminate_errors.empty?
1719
+ assert_equal 1 , $!.errors.length
1720
+ assert $!.errors[0].kind_of?(RuntimeError)
1721
+ assert_equal "error", $!.errors[0].message
1508
1722
  end
1509
- end
1723
+ end
1510
1724
  end
1511
1725
 
1512
- # TODO -- REWORD
1513
-
1514
1726
  # Ruby inconsistent test
1515
1727
  # <"error"> expected but was
1516
1728
  # <"Tap::App::TerminateError">.
1729
+ # RESOLVED? -- 2008/01/29
1730
+ #
1517
1731
  def test_unhandled_exception_on_thread_teminates_threads
1518
1732
  extended_test do
1519
1733
  count = 0
1520
1734
  terminated_count = 0
1521
1735
 
1522
1736
  tasks = Array.new(2) do
1523
- Task.new do |t,i|
1737
+ Task.new do |t|
1524
1738
  # count to make sure the tasks actually executed
1525
1739
  count += 1
1526
1740
 
1527
1741
  terminated_count += 1
1528
- sleep 1
1742
+ sleep 0.8
1529
1743
  t.check_terminate
1530
1744
 
1531
1745
  # this should not happen
1532
1746
  terminated_count -= 1
1533
1747
  end
1534
1748
  end
1535
- terr = Task.new {|t,i| raise "error"}
1749
+ terr = Task.new {|t| raise "error"}
1536
1750
 
1537
1751
  tasks << terr
1538
1752
  tasks.each do |task|
1539
1753
  task.multithread = true
1540
- task.enq nil
1754
+ task.enq
1541
1755
  end
1542
-
1756
+
1543
1757
  with_options :debug => true do
1544
1758
  begin
1545
1759
  app.run
1546
1760
  flunk "no error was raised"
1547
1761
  rescue
1548
1762
  assert $!.kind_of?(Tap::Support::RunError)
1549
- assert $!.original_error.kind_of?(RuntimeError)
1550
- assert_equal "error", $!.original_error.message
1551
- assert $!.terminate_errors.empty?
1763
+ assert_equal 1 , $!.errors.length
1764
+ assert $!.errors[0].kind_of?(RuntimeError)
1765
+ assert_equal "error", $!.errors[0].message
1552
1766
  end
1553
- end
1554
-
1767
+ end
1768
+
1555
1769
  assert_equal 2, count
1556
1770
  assert_equal 2, terminated_count
1557
1771
  end
1558
1772
  end
1559
-
1773
+
1560
1774
  # Ruby inconsistent test
1561
1775
  # <"error"> expected but was
1562
1776
  # <"Tap::App::TerminateError">.
1563
1777
  #
1564
1778
  # <"term error 0"> expected but was
1565
1779
  # <"term error 1">.
1780
+ # RESOLVED? -- 2008/01/29
1566
1781
  #
1567
1782
  # JRuby inconsistent test
1783
+ # RESOLVED? -- 2008/01/30
1784
+ #
1568
1785
  def test_exceptions_from_handling_termination_error_are_collected
1569
1786
  extended_test do
1787
+ lock = Monitor.new
1570
1788
  count = 0
1571
1789
  count_in_threaded_error_handling = 0
1572
1790
 
1573
1791
  tasks = Array.new(2) do
1574
- Task.new do |t,i|
1575
- n = count
1576
- begin
1792
+ Task.new do |t|
1793
+ n = nil
1794
+ lock.synchronize do
1795
+ n = count
1577
1796
  count += 1
1578
- sleep 1
1797
+ end
1798
+
1799
+ sleep 0.8
1800
+
1801
+ begin
1579
1802
  t.check_terminate
1580
1803
  rescue
1581
- count_in_threaded_error_handling += 1
1804
+ lock.synchronize { count_in_threaded_error_handling += 1 }
1582
1805
  raise "term error #{n}"
1583
1806
  end
1584
1807
  end
1585
1808
  end
1586
- terr = Task.new {|t,i| raise "error"}
1809
+ terr = Task.new {|t| raise "error"}
1587
1810
 
1588
1811
  tasks << terr
1589
1812
  tasks.each do |task|
1590
1813
  task.multithread = true
1591
- task.enq nil
1814
+ task.enq
1592
1815
  end
1593
1816
 
1594
1817
  with_options :debug => true do
@@ -1597,20 +1820,30 @@ class AppTest < Test::Unit::TestCase
1597
1820
  flunk "no error was raised"
1598
1821
  rescue
1599
1822
  assert $!.kind_of?(Tap::Support::RunError)
1600
- assert $!.original_error.kind_of?(RuntimeError)
1601
- assert_equal "error", $!.original_error.message
1602
-
1603
- assert_equal 2, $!.terminate_errors.length
1604
-
1605
- $!.terminate_errors.each_with_index do |term_err, i|
1606
- assert term_err.kind_of?(RuntimeError)
1607
- assert_equal "term error #{i}", term_err.message
1608
- end
1823
+ assert_equal 3, $!.errors.length
1824
+ $!.errors.each {|error| assert error.kind_of?(RuntimeError) }
1825
+ messages = $!.errors.collect {|error| error.message}.sort
1826
+ assert_equal ["error", "term error 0", "term error 1"], messages
1609
1827
  end
1610
-
1828
+
1611
1829
  assert_equal 2, count
1612
1830
  assert_equal 2, count_in_threaded_error_handling
1613
1831
  end
1614
1832
  end
1615
1833
  end
1834
+
1835
+ #
1836
+ # benchmarks
1837
+ #
1838
+
1839
+ def test_run_speed
1840
+ t = Tap::Task.new
1841
+ benchmark_test(20) do |x|
1842
+ n = 10000
1843
+
1844
+ x.report("10k enq ") { n.times { t.enq(1) } }
1845
+ x.report("10k run ") { n.times {}; app.run }
1846
+ x.report("10k _execute ") { n.times { t._execute(1) } }
1847
+ end
1848
+ end
1616
1849
  end