tap 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. data/Basic Overview +151 -0
  2. data/Command Reference +99 -0
  3. data/History +24 -0
  4. data/MIT-LICENSE +1 -1
  5. data/README +29 -57
  6. data/Rakefile +30 -37
  7. data/Tutorial +243 -191
  8. data/bin/tap +66 -35
  9. data/lib/tap.rb +47 -29
  10. data/lib/tap/app.rb +700 -342
  11. data/lib/tap/{script → cmd}/console.rb +0 -0
  12. data/lib/tap/{script → cmd}/destroy.rb +0 -0
  13. data/lib/tap/{script → cmd}/generate.rb +0 -0
  14. data/lib/tap/cmd/run.rb +156 -0
  15. data/lib/tap/constants.rb +4 -0
  16. data/lib/tap/dump.rb +57 -0
  17. data/lib/tap/env.rb +316 -0
  18. data/lib/tap/file_task.rb +106 -109
  19. data/lib/tap/generator.rb +4 -1
  20. data/lib/tap/generator/generators/command/USAGE +6 -0
  21. data/lib/tap/generator/generators/command/command_generator.rb +17 -0
  22. data/lib/tap/generator/generators/{script/templates/script.erb → command/templates/command.erb} +10 -10
  23. data/lib/tap/generator/generators/config/USAGE +21 -0
  24. data/lib/tap/generator/generators/config/config_generator.rb +17 -7
  25. data/lib/tap/generator/generators/file_task/USAGE +3 -0
  26. data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -0
  27. data/lib/tap/generator/generators/file_task/templates/file.txt +2 -0
  28. data/lib/tap/generator/generators/file_task/templates/file.yml +3 -0
  29. data/lib/tap/generator/generators/file_task/templates/task.erb +26 -20
  30. data/lib/tap/generator/generators/file_task/templates/test.erb +20 -10
  31. data/lib/tap/generator/generators/generator/generator_generator.rb +1 -1
  32. data/lib/tap/generator/generators/generator/templates/generator.erb +21 -12
  33. data/lib/tap/generator/generators/root/templates/Rakefile +33 -24
  34. data/lib/tap/generator/generators/root/templates/tap.yml +28 -31
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +1 -0
  36. data/lib/tap/generator/generators/task/USAGE +3 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +18 -5
  38. data/lib/tap/generator/generators/task/templates/task.erb +7 -12
  39. data/lib/tap/generator/generators/task/templates/test.erb +10 -11
  40. data/lib/tap/generator/generators/workflow/templates/task.erb +1 -1
  41. data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
  42. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  43. data/lib/tap/patches/rake/testtask.rb +55 -0
  44. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  45. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  46. data/lib/tap/root.rb +172 -67
  47. data/lib/tap/script.rb +70 -336
  48. data/lib/tap/support/aggregator.rb +55 -0
  49. data/lib/tap/support/audit.rb +281 -280
  50. data/lib/tap/support/batchable.rb +59 -0
  51. data/lib/tap/support/class_configuration.rb +279 -0
  52. data/lib/tap/support/configurable.rb +92 -0
  53. data/lib/tap/support/configurable_methods.rb +296 -0
  54. data/lib/tap/support/executable.rb +98 -0
  55. data/lib/tap/support/executable_queue.rb +82 -0
  56. data/lib/tap/support/logger.rb +9 -15
  57. data/lib/tap/support/rake.rb +43 -54
  58. data/lib/tap/support/run_error.rb +32 -13
  59. data/lib/tap/support/shell_utils.rb +47 -0
  60. data/lib/tap/support/tdoc.rb +9 -8
  61. data/lib/tap/support/tdoc/config_attr.rb +40 -16
  62. data/lib/tap/support/validation.rb +77 -0
  63. data/lib/tap/support/versions.rb +36 -36
  64. data/lib/tap/task.rb +276 -482
  65. data/lib/tap/test.rb +20 -261
  66. data/lib/tap/test/env_vars.rb +7 -5
  67. data/lib/tap/test/file_methods.rb +126 -121
  68. data/lib/tap/test/subset_methods.rb +86 -45
  69. data/lib/tap/test/tap_methods.rb +271 -0
  70. data/lib/tap/workflow.rb +174 -46
  71. data/test/app/config/another/task.yml +1 -0
  72. data/test/app/config/erb.yml +2 -1
  73. data/test/app/config/some/task.yml +1 -0
  74. data/test/app/config/template.yml +2 -6
  75. data/test/app_test.rb +1241 -1008
  76. data/test/env/test_configure/recurse_a.yml +2 -0
  77. data/test/env/test_configure/recurse_b.yml +2 -0
  78. data/test/env/test_configure/tap.yml +23 -0
  79. data/test/env/test_load_env_config/dir/tap.yml +3 -0
  80. data/test/env/test_load_env_config/recurse_a.yml +2 -0
  81. data/test/env/test_load_env_config/recurse_b.yml +2 -0
  82. data/test/env/test_load_env_config/tap.yml +3 -0
  83. data/test/env_test.rb +198 -0
  84. data/test/file_task_test.rb +70 -53
  85. data/{lib/tap/generator/generators/package/USAGE → test/root/file.txt} +0 -0
  86. data/test/root_test.rb +621 -454
  87. data/test/script_test.rb +38 -174
  88. data/test/support/aggregator_test.rb +99 -0
  89. data/test/support/audit_test.rb +409 -416
  90. data/test/support/batchable_test.rb +74 -0
  91. data/test/support/{task_configuration_test.rb → class_configuration_test.rb} +106 -47
  92. data/test/{task/config/overriding.yml → support/configurable/config/configured.yml} +0 -0
  93. data/test/support/configurable_test.rb +295 -0
  94. data/test/support/executable_queue_test.rb +103 -0
  95. data/test/support/executable_test.rb +38 -0
  96. data/test/support/logger_test.rb +17 -17
  97. data/test/support/rake_test.rb +4 -2
  98. data/test/support/shell_utils_test.rb +24 -0
  99. data/test/support/tdoc_test.rb +265 -258
  100. data/test/support/validation_test.rb +54 -0
  101. data/test/support/versions_test.rb +38 -38
  102. data/test/tap_test_helper.rb +19 -5
  103. data/test/tap_test_suite.rb +5 -2
  104. data/test/task_base_test.rb +13 -104
  105. data/test/task_syntax_test.rb +300 -0
  106. data/test/task_test.rb +258 -381
  107. data/test/test/env_vars_test.rb +40 -40
  108. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/one.txt +0 -0
  109. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/two.txt +0 -0
  110. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/one.txt +0 -0
  111. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/two.txt +0 -0
  112. data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/one.txt +0 -0
  113. data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/two.txt +0 -0
  114. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +1 -0
  115. data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_different_content}/expected/two.txt +0 -0
  116. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +1 -0
  117. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +1 -0
  118. data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_missing_expected_file}/expected/one.txt +0 -0
  119. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +1 -0
  120. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +1 -0
  121. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +1 -0
  122. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +1 -0
  123. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +1 -0
  124. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +1 -0
  125. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +1 -0
  126. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +1 -0
  127. data/test/test/file_methods_doc/test_sub/expected/one.txt +1 -0
  128. data/test/test/file_methods_doc/test_sub/expected/two.txt +1 -0
  129. data/test/test/file_methods_doc/test_sub/input/one.txt +1 -0
  130. data/test/test/file_methods_doc/test_sub/input/two.txt +1 -0
  131. data/test/test/file_methods_doc_test.rb +29 -0
  132. data/test/test/file_methods_test.rb +214 -143
  133. data/test/test/subset_methods_test.rb +111 -115
  134. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/a.txt +0 -0
  135. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/b.txt +0 -0
  136. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/a.txt +0 -0
  137. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/b.txt +0 -0
  138. data/test/test/tap_methods_test.rb +399 -0
  139. data/test/workflow_test.rb +101 -91
  140. metadata +86 -70
  141. data/lib/tap/generator/generators/package/package_generator.rb +0 -38
  142. data/lib/tap/generator/generators/package/templates/package.erb +0 -186
  143. data/lib/tap/generator/generators/script/USAGE +0 -0
  144. data/lib/tap/generator/generators/script/script_generator.rb +0 -17
  145. data/lib/tap/script/run.rb +0 -154
  146. data/lib/tap/support/batch_queue.rb +0 -162
  147. data/lib/tap/support/combinator.rb +0 -114
  148. data/lib/tap/support/task_configuration.rb +0 -169
  149. data/lib/tap/support/template.rb +0 -81
  150. data/lib/tap/support/templater.rb +0 -155
  151. data/lib/tap/version.rb +0 -4
  152. data/test/app/config/addition_template.yml +0 -6
  153. data/test/app_class_test.rb +0 -33
  154. data/test/check/binding_eval.rb +0 -23
  155. data/test/check/define_method_check.rb +0 -22
  156. data/test/check/dependencies_check.rb +0 -175
  157. data/test/check/inheritance_check.rb +0 -22
  158. data/test/support/batch_queue_test.rb +0 -320
  159. data/test/support/combinator_test.rb +0 -249
  160. data/test/support/template_test.rb +0 -122
  161. data/test/support/templater/erb.txt +0 -2
  162. data/test/support/templater/erb.yml +0 -2
  163. data/test/support/templater/somefile.txt +0 -2
  164. data/test/support/templater_test.rb +0 -192
  165. data/test/task/config/template.yml +0 -4
  166. data/test/task_class_test.rb +0 -170
  167. data/test/task_execute_test.rb +0 -262
  168. data/test/test/file_methods/test_assert_expected/expected/file.txt +0 -1
  169. data/test/test/file_methods/test_assert_expected/expected/folder/file.txt +0 -1
  170. data/test/test/file_methods/test_assert_expected/input/file.txt +0 -1
  171. data/test/test/file_methods/test_assert_expected/input/folder/file.txt +0 -1
  172. data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
  173. data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
  174. data/test/test/file_methods/test_file_compare/expected/output_1.txt +0 -3
  175. data/test/test/file_methods/test_file_compare/expected/output_2.txt +0 -1
  176. data/test/test/file_methods/test_file_compare/input/input_1.txt +0 -3
  177. data/test/test/file_methods/test_file_compare/input/input_2.txt +0 -3
  178. data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
  179. data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
  180. data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
  181. data/test/test/file_methods/test_yml_compare/expected/output_1.yml +0 -6
  182. data/test/test/file_methods/test_yml_compare/expected/output_2.yml +0 -6
  183. data/test/test/file_methods/test_yml_compare/input/input_1.yml +0 -4
  184. data/test/test/file_methods/test_yml_compare/input/input_2.yml +0 -4
  185. data/test/test_test.rb +0 -373
@@ -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