dynflow 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +3 -4
  4. data/Dockerfile +9 -0
  5. data/Gemfile +6 -0
  6. data/Rakefile +1 -0
  7. data/doc/pages/Gemfile +1 -0
  8. data/doc/pages/Rakefile +1 -0
  9. data/doc/pages/plugins/alert_block.rb +1 -0
  10. data/doc/pages/plugins/div_tag.rb +1 -0
  11. data/doc/pages/plugins/graphviz.rb +1 -0
  12. data/doc/pages/plugins/plantuml.rb +1 -0
  13. data/doc/pages/plugins/play.rb +1 -0
  14. data/doc/pages/plugins/tags.rb +1 -0
  15. data/doc/pages/plugins/toc.rb +1 -0
  16. data/docker-compose.yml +41 -0
  17. data/dynflow.gemspec +1 -0
  18. data/examples/clock_benchmark.rb +1 -0
  19. data/examples/example_helper.rb +19 -2
  20. data/examples/future_execution.rb +2 -1
  21. data/examples/memory_limit_watcher.rb +1 -0
  22. data/examples/orchestrate.rb +4 -5
  23. data/examples/orchestrate_evented.rb +3 -2
  24. data/examples/remote_executor.rb +68 -0
  25. data/examples/singletons.rb +4 -3
  26. data/examples/sub_plan_concurrency_control.rb +2 -1
  27. data/examples/sub_plans.rb +3 -2
  28. data/examples/termination.rb +1 -0
  29. data/lib/dynflow.rb +20 -0
  30. data/lib/dynflow/action.rb +28 -3
  31. data/lib/dynflow/action/cancellable.rb +1 -0
  32. data/lib/dynflow/action/format.rb +1 -0
  33. data/lib/dynflow/action/missing.rb +1 -0
  34. data/lib/dynflow/action/polling.rb +3 -1
  35. data/lib/dynflow/action/progress.rb +1 -0
  36. data/lib/dynflow/action/rescue.rb +1 -0
  37. data/lib/dynflow/action/singleton.rb +1 -0
  38. data/lib/dynflow/action/suspended.rb +9 -2
  39. data/lib/dynflow/action/timeouts.rb +2 -1
  40. data/lib/dynflow/action/with_bulk_sub_plans.rb +2 -1
  41. data/lib/dynflow/action/with_polling_sub_plans.rb +7 -5
  42. data/lib/dynflow/action/with_sub_plans.rb +1 -0
  43. data/lib/dynflow/active_job/queue_adapter.rb +1 -0
  44. data/lib/dynflow/actor.rb +13 -5
  45. data/lib/dynflow/actors.rb +1 -0
  46. data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -0
  47. data/lib/dynflow/clock.rb +27 -47
  48. data/lib/dynflow/config.rb +11 -2
  49. data/lib/dynflow/connectors.rb +1 -0
  50. data/lib/dynflow/connectors/abstract.rb +1 -0
  51. data/lib/dynflow/connectors/database.rb +1 -0
  52. data/lib/dynflow/connectors/direct.rb +1 -0
  53. data/lib/dynflow/coordinator.rb +1 -0
  54. data/lib/dynflow/coordinator_adapters.rb +1 -0
  55. data/lib/dynflow/coordinator_adapters/abstract.rb +1 -0
  56. data/lib/dynflow/coordinator_adapters/sequel.rb +1 -0
  57. data/lib/dynflow/dead_letter_silencer.rb +2 -0
  58. data/lib/dynflow/debug/telemetry/persistence.rb +1 -0
  59. data/lib/dynflow/delayed_executors.rb +1 -0
  60. data/lib/dynflow/delayed_executors/abstract.rb +1 -0
  61. data/lib/dynflow/delayed_executors/abstract_core.rb +1 -0
  62. data/lib/dynflow/delayed_executors/polling.rb +1 -0
  63. data/lib/dynflow/delayed_plan.rb +1 -0
  64. data/lib/dynflow/director.rb +80 -15
  65. data/lib/dynflow/director/execution_plan_manager.rb +17 -3
  66. data/lib/dynflow/director/flow_manager.rb +1 -0
  67. data/lib/dynflow/director/{work_queue.rb → queue_hash.rb} +9 -8
  68. data/lib/dynflow/director/running_steps_manager.rb +55 -18
  69. data/lib/dynflow/director/sequence_cursor.rb +1 -0
  70. data/lib/dynflow/director/sequential_manager.rb +12 -2
  71. data/lib/dynflow/dispatcher.rb +4 -2
  72. data/lib/dynflow/dispatcher/abstract.rb +1 -0
  73. data/lib/dynflow/dispatcher/client_dispatcher.rb +6 -4
  74. data/lib/dynflow/dispatcher/executor_dispatcher.rb +13 -1
  75. data/lib/dynflow/errors.rb +1 -0
  76. data/lib/dynflow/execution_history.rb +1 -0
  77. data/lib/dynflow/execution_plan.rb +3 -2
  78. data/lib/dynflow/execution_plan/dependency_graph.rb +1 -0
  79. data/lib/dynflow/execution_plan/hooks.rb +1 -0
  80. data/lib/dynflow/execution_plan/output_reference.rb +2 -1
  81. data/lib/dynflow/execution_plan/steps.rb +1 -0
  82. data/lib/dynflow/execution_plan/steps/abstract.rb +10 -5
  83. data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +2 -0
  84. data/lib/dynflow/execution_plan/steps/error.rb +1 -0
  85. data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -0
  86. data/lib/dynflow/execution_plan/steps/plan_step.rb +1 -0
  87. data/lib/dynflow/execution_plan/steps/run_step.rb +1 -0
  88. data/lib/dynflow/executors.rb +1 -1
  89. data/lib/dynflow/executors/abstract/core.rb +132 -0
  90. data/lib/dynflow/executors/parallel.rb +24 -11
  91. data/lib/dynflow/executors/parallel/core.rb +10 -91
  92. data/lib/dynflow/executors/parallel/pool.rb +4 -2
  93. data/lib/dynflow/executors/parallel/worker.rb +2 -1
  94. data/lib/dynflow/executors/sidekiq/core.rb +121 -0
  95. data/lib/dynflow/executors/sidekiq/internal_job_base.rb +24 -0
  96. data/lib/dynflow/executors/sidekiq/orchestrator_jobs.rb +60 -0
  97. data/lib/dynflow/executors/sidekiq/redis_locking.rb +69 -0
  98. data/lib/dynflow/executors/sidekiq/serialization.rb +33 -0
  99. data/lib/dynflow/executors/sidekiq/worker_jobs.rb +42 -0
  100. data/lib/dynflow/flows.rb +1 -0
  101. data/lib/dynflow/flows/abstract.rb +1 -0
  102. data/lib/dynflow/flows/abstract_composed.rb +1 -0
  103. data/lib/dynflow/flows/atom.rb +1 -0
  104. data/lib/dynflow/flows/concurrence.rb +1 -0
  105. data/lib/dynflow/flows/sequence.rb +1 -0
  106. data/lib/dynflow/logger_adapters.rb +1 -0
  107. data/lib/dynflow/logger_adapters/abstract.rb +1 -0
  108. data/lib/dynflow/logger_adapters/delegator.rb +1 -0
  109. data/lib/dynflow/logger_adapters/formatters.rb +1 -0
  110. data/lib/dynflow/logger_adapters/formatters/abstract.rb +1 -0
  111. data/lib/dynflow/logger_adapters/formatters/exception.rb +1 -0
  112. data/lib/dynflow/logger_adapters/simple.rb +1 -0
  113. data/lib/dynflow/middleware.rb +1 -0
  114. data/lib/dynflow/middleware/common/singleton.rb +1 -0
  115. data/lib/dynflow/middleware/common/transaction.rb +1 -0
  116. data/lib/dynflow/middleware/register.rb +1 -0
  117. data/lib/dynflow/middleware/resolver.rb +1 -0
  118. data/lib/dynflow/middleware/stack.rb +1 -0
  119. data/lib/dynflow/middleware/world.rb +1 -0
  120. data/lib/dynflow/persistence.rb +3 -2
  121. data/lib/dynflow/persistence_adapters.rb +1 -0
  122. data/lib/dynflow/persistence_adapters/abstract.rb +1 -0
  123. data/lib/dynflow/persistence_adapters/sequel.rb +10 -7
  124. data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +1 -0
  125. data/lib/dynflow/persistence_adapters/sequel_migrations/002_incremental_progress.rb +1 -0
  126. data/lib/dynflow/persistence_adapters/sequel_migrations/003_parent_action.rb +1 -0
  127. data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +1 -0
  128. data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +1 -0
  129. data/lib/dynflow/persistence_adapters/sequel_migrations/006_fix_data_length.rb +1 -0
  130. data/lib/dynflow/persistence_adapters/sequel_migrations/007_future_execution.rb +1 -0
  131. data/lib/dynflow/persistence_adapters/sequel_migrations/008_rename_scheduled_plans_to_delayed_plans.rb +1 -0
  132. data/lib/dynflow/persistence_adapters/sequel_migrations/009_fix_mysql_data_length.rb +1 -0
  133. data/lib/dynflow/persistence_adapters/sequel_migrations/010_add_execution_plans_label.rb +1 -0
  134. data/lib/dynflow/persistence_adapters/sequel_migrations/011_placeholder.rb +1 -0
  135. data/lib/dynflow/persistence_adapters/sequel_migrations/012_add_delayed_plans_serialized_args.rb +1 -0
  136. data/lib/dynflow/persistence_adapters/sequel_migrations/013_add_action_columns.rb +1 -0
  137. data/lib/dynflow/persistence_adapters/sequel_migrations/014_add_step_columns.rb +1 -0
  138. data/lib/dynflow/persistence_adapters/sequel_migrations/015_add_execution_plan_columns.rb +1 -0
  139. data/lib/dynflow/persistence_adapters/sequel_migrations/016_add_step_queue.rb +1 -0
  140. data/lib/dynflow/persistence_adapters/sequel_migrations/017_add_delayed_plan_frozen.rb +1 -0
  141. data/lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb +1 -0
  142. data/lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb +48 -0
  143. data/lib/dynflow/rails.rb +1 -0
  144. data/lib/dynflow/rails/configuration.rb +6 -3
  145. data/lib/dynflow/rails/daemon.rb +1 -0
  146. data/lib/dynflow/round_robin.rb +1 -0
  147. data/lib/dynflow/semaphores.rb +1 -0
  148. data/lib/dynflow/semaphores/abstract.rb +1 -0
  149. data/lib/dynflow/semaphores/aggregating.rb +1 -0
  150. data/lib/dynflow/semaphores/dummy.rb +1 -0
  151. data/lib/dynflow/semaphores/stateful.rb +1 -0
  152. data/lib/dynflow/serializable.rb +13 -4
  153. data/lib/dynflow/serializer.rb +24 -0
  154. data/lib/dynflow/serializers.rb +1 -0
  155. data/lib/dynflow/serializers/abstract.rb +1 -0
  156. data/lib/dynflow/serializers/noop.rb +1 -0
  157. data/lib/dynflow/stateful.rb +1 -0
  158. data/lib/dynflow/telemetry.rb +1 -0
  159. data/lib/dynflow/telemetry_adapters/abstract.rb +1 -0
  160. data/lib/dynflow/telemetry_adapters/dummy.rb +1 -0
  161. data/lib/dynflow/telemetry_adapters/statsd.rb +1 -0
  162. data/lib/dynflow/testing.rb +1 -0
  163. data/lib/dynflow/testing/assertions.rb +6 -5
  164. data/lib/dynflow/testing/dummy_execution_plan.rb +1 -0
  165. data/lib/dynflow/testing/dummy_executor.rb +19 -2
  166. data/lib/dynflow/testing/dummy_planned_action.rb +1 -0
  167. data/lib/dynflow/testing/dummy_step.rb +3 -1
  168. data/lib/dynflow/testing/dummy_world.rb +9 -0
  169. data/lib/dynflow/testing/factories.rb +6 -1
  170. data/lib/dynflow/testing/in_thread_executor.rb +22 -3
  171. data/lib/dynflow/testing/in_thread_world.rb +9 -0
  172. data/lib/dynflow/testing/managed_clock.rb +1 -0
  173. data/lib/dynflow/testing/mimic.rb +1 -0
  174. data/lib/dynflow/throttle_limiter.rb +1 -0
  175. data/lib/dynflow/transaction_adapters.rb +1 -0
  176. data/lib/dynflow/transaction_adapters/abstract.rb +1 -0
  177. data/lib/dynflow/transaction_adapters/active_record.rb +1 -0
  178. data/lib/dynflow/transaction_adapters/none.rb +1 -0
  179. data/lib/dynflow/utils.rb +1 -0
  180. data/lib/dynflow/utils/indifferent_hash.rb +1 -0
  181. data/lib/dynflow/utils/priority_queue.rb +1 -0
  182. data/lib/dynflow/version.rb +2 -1
  183. data/lib/dynflow/watchers/memory_consumption_watcher.rb +1 -0
  184. data/lib/dynflow/web.rb +1 -0
  185. data/lib/dynflow/web/console.rb +1 -0
  186. data/lib/dynflow/web/console_helpers.rb +1 -0
  187. data/lib/dynflow/web/filtering_helpers.rb +1 -0
  188. data/lib/dynflow/web/world_helpers.rb +1 -0
  189. data/lib/dynflow/web_console.rb +1 -0
  190. data/lib/dynflow/world.rb +11 -1
  191. data/lib/dynflow/world/invalidation.rb +7 -1
  192. data/test/abnormal_states_recovery_test.rb +41 -40
  193. data/test/action_test.rb +160 -110
  194. data/test/activejob_adapter_test.rb +1 -0
  195. data/test/batch_sub_tasks_test.rb +12 -11
  196. data/test/clock_test.rb +2 -1
  197. data/test/concurrency_control_test.rb +20 -19
  198. data/test/coordinator_test.rb +20 -21
  199. data/test/daemon_test.rb +2 -1
  200. data/test/dead_letter_silencer_test.rb +9 -7
  201. data/test/dispatcher_test.rb +2 -1
  202. data/test/execution_plan_cleaner_test.rb +13 -12
  203. data/test/execution_plan_hooks_test.rb +3 -2
  204. data/test/execution_plan_test.rb +33 -32
  205. data/test/executor_test.rb +533 -489
  206. data/test/future_execution_test.rb +45 -44
  207. data/test/memory_cosumption_watcher_test.rb +5 -4
  208. data/test/middleware_test.rb +55 -54
  209. data/test/persistence_test.rb +56 -53
  210. data/test/rescue_test.rb +36 -35
  211. data/test/round_robin_test.rb +13 -12
  212. data/test/semaphores_test.rb +31 -30
  213. data/test/support/code_workflow_example.rb +1 -0
  214. data/test/support/dummy_example.rb +14 -1
  215. data/test/support/middleware_example.rb +2 -1
  216. data/test/support/rails/config/environment.rb +1 -0
  217. data/test/support/rescue_example.rb +1 -0
  218. data/test/support/test_execution_log.rb +1 -0
  219. data/test/test_helper.rb +18 -17
  220. data/test/testing_test.rb +45 -44
  221. data/test/utils_test.rb +18 -17
  222. data/test/web_console_test.rb +1 -0
  223. data/test/world_test.rb +7 -6
  224. metadata +13 -4
  225. data/lib/dynflow/executors/abstract.rb +0 -40
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Utils
3
4
  # Heavily inspired by rubyworks/pqueue
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
- VERSION = '1.3.0'.freeze
3
+ VERSION = '1.4.0'
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'get_process_mem'
2
3
 
3
4
  module Dynflow
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'dynflow'
2
3
  require 'pp'
3
4
  require 'sinatra/base'
@@ -1,4 +1,5 @@
1
1
 
2
+ # frozen_string_literal: true
2
3
  module Dynflow
3
4
  module Web
4
5
  class Console < Sinatra::Base
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Web
3
4
  module ConsoleHelpers
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Web
3
4
  module FilteringHelpers
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Web
3
4
  module WorldHelpers
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'dynflow/web'
2
3
 
3
4
  warn %{"require 'dynflow/web_console'" is deprecated, use "require 'dynflow/web'" instead}
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  require 'dynflow/world/invalidation'
3
4
 
4
5
  module Dynflow
@@ -29,7 +30,12 @@ module Dynflow
29
30
  :backup_deleted_plans => @config.backup_deleted_plans,
30
31
  :backup_dir => @config.backup_dir)
31
32
  @coordinator = Coordinator.new(@config.coordinator_adapter)
32
- @executor = @config.executor
33
+ if @config.executor
34
+ @executor = Executors::Parallel.new(self,
35
+ executor_class: @config.executor,
36
+ heartbeat_interval: @config.executor_heartbeat_interval,
37
+ queues_options: @config.queues)
38
+ end
33
39
  @action_classes = @config.action_classes
34
40
  @auto_rescue = @config.auto_rescue
35
41
  @exit_on_terminate = Concurrent::AtomicBoolean.new(@config.exit_on_terminate)
@@ -217,6 +223,10 @@ module Dynflow
217
223
  publish_request(Dispatcher::Event[execution_plan_id, step_id, event], done, false)
218
224
  end
219
225
 
226
+ def plan_event(execution_plan_id, step_id, event, time, accepted = Concurrent::Promises.resolvable_future)
227
+ publish_request(Dispatcher::Event[execution_plan_id, step_id, event, time], accepted, false)
228
+ end
229
+
220
230
  def ping(world_id, timeout, done = Concurrent::Promises.resolvable_future)
221
231
  publish_request(Dispatcher::Ping[world_id, true], done, false, timeout)
222
232
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  class World
3
4
  module Invalidation
@@ -35,8 +36,13 @@ module Dynflow
35
36
  with_valid_execution_plan_for_lock(planning_lock) do |plan|
36
37
  plan.steps.values.each { |step| invalidate_step step }
37
38
 
38
- state = plan.plan_steps.all? { |step| step.state == :success } ? :planned : :stopped
39
+ state = if plan.plan_steps.any? && plan.plan_steps.all? { |step| step.state == :success }
40
+ :planned
41
+ else
42
+ :stopped
43
+ end
39
44
  plan.update_state(state)
45
+
40
46
  coordinator.release(planning_lock)
41
47
  execute(plan.id) if plan.state == :planned
42
48
  end
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  require_relative 'test_helper'
3
4
  require 'ostruct'
4
5
 
@@ -64,11 +65,11 @@ module Dynflow
64
65
  it 'when no executor is available, marks the plans as paused' do
65
66
  executor_world_2.terminate.wait
66
67
  with_invalidation_while_executing(false) do |plan|
67
- plan.state.must_equal :paused
68
- plan.result.must_equal :pending
68
+ _(plan.state).must_equal :paused
69
+ _(plan.result).must_equal :pending
69
70
  expected_history = [['start execution', executor_world.id],
70
71
  ['terminate execution', executor_world.id]]
71
- plan.execution_history.map { |h| [h.name, h.world_id] }.must_equal(expected_history)
72
+ _(plan.execution_history.map { |h| [h.name, h.world_id] }).must_equal(expected_history)
72
73
  end
73
74
  end
74
75
 
@@ -103,14 +104,14 @@ module Dynflow
103
104
  client_world.invalidate(executor_world.registered_world)
104
105
  expected_locks = ["lock world-invalidation:#{executor_world.id}",
105
106
  "unlock world-invalidation:#{executor_world.id}"]
106
- client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
107
+ _(client_world.coordinator.adapter.lock_log).must_equal(expected_locks)
107
108
  end
108
109
 
109
110
  it "prevents from running the consistency checks twice on the same world concurrently" do
110
111
  client_world.invalidate(executor_world.registered_world)
111
112
  expected_locks = ["lock world-invalidation:#{executor_world.id}",
112
113
  "unlock world-invalidation:#{executor_world.id}"]
113
- client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
114
+ _(client_world.coordinator.adapter.lock_log).must_equal(expected_locks)
114
115
  end
115
116
 
116
117
  it "handles missing execution plans" do
@@ -120,7 +121,7 @@ module Dynflow
120
121
  expected_locks = ["lock world-invalidation:#{executor_world.id}",
121
122
  "unlock execution-plan:missing",
122
123
  "unlock world-invalidation:#{executor_world.id}"]
123
- client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
124
+ _(client_world.coordinator.adapter.lock_log).must_equal(expected_locks)
124
125
  end
125
126
 
126
127
  it 'releases singleton locks belonging to missing execution plan' do
@@ -134,7 +135,7 @@ module Dynflow
134
135
  "unlock execution-plan:#{execution_plan_id}",
135
136
  "unlock singleton-action:#{action_class}",
136
137
  "unlock world-invalidation:#{executor_world.id}"]
137
- client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
138
+ _(client_world.coordinator.adapter.lock_log).must_equal(expected_locks)
138
139
  end
139
140
 
140
141
  describe 'planning locks' do
@@ -148,7 +149,7 @@ module Dynflow
148
149
  "unlock execution-plan:#{plan.id}", # planning lock
149
150
  "lock execution-plan:#{plan.id}", # execution lock
150
151
  "unlock world-invalidation:#{client_world.id}"]
151
- executor_world.coordinator.adapter.lock_log.must_equal(expected_locks)
152
+ _(executor_world.coordinator.adapter.lock_log).must_equal(expected_locks)
152
153
  wait_for do
153
154
  plan = client_world_2.persistence.load_execution_plan(plan.id)
154
155
  plan.state == :stopped
@@ -167,9 +168,9 @@ module Dynflow
167
168
  expected_locks = ["lock world-invalidation:#{client_world.id}",
168
169
  "unlock execution-plan:#{plan.id}",
169
170
  "unlock world-invalidation:#{client_world.id}"]
170
- client_world_2.coordinator.adapter.lock_log.must_equal(expected_locks)
171
+ _(client_world_2.coordinator.adapter.lock_log).must_equal(expected_locks)
171
172
  plan = client_world_2.persistence.load_execution_plan(plan.id)
172
- plan.state.must_equal :stopped
173
+ _(plan.state).must_equal :stopped
173
174
  end
174
175
 
175
176
  it 'releases orphaned planning locks without execution plans' do
@@ -179,7 +180,7 @@ module Dynflow
179
180
  expected_locks = ["lock world-invalidation:#{client_world.id}",
180
181
  "unlock execution-plan:#{uuid}",
181
182
  "unlock world-invalidation:#{client_world.id}"]
182
- client_world_2.coordinator.adapter.lock_log.must_equal(expected_locks)
183
+ _(client_world_2.coordinator.adapter.lock_log).must_equal(expected_locks)
183
184
  end
184
185
  end
185
186
  end
@@ -194,10 +195,10 @@ module Dynflow
194
195
  it "prevents from running the auto-execution twice" do
195
196
  client_world.auto_execute
196
197
  expected_locks = ["lock auto-execute", "unlock auto-execute"]
197
- client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
198
+ _(client_world.coordinator.adapter.lock_log).must_equal(expected_locks)
198
199
  lock = Coordinator::AutoExecuteLock.new(client_world)
199
200
  client_world.coordinator.acquire(lock)
200
- client_world.auto_execute.must_equal []
201
+ _(client_world.auto_execute).must_equal []
201
202
  end
202
203
 
203
204
  it "re-runs the plans that were planned but not executed" do
@@ -212,7 +213,7 @@ module Dynflow
212
213
  end
213
214
  expected_history = [['start execution', executor_world.id],
214
215
  ['finish execution', executor_world.id]]
215
- plan.execution_history.map { |h| [h.name, h.world_id] }.must_equal(expected_history)
216
+ _(plan.execution_history.map { |h| [h.name, h.world_id] }).must_equal(expected_history)
216
217
  end
217
218
 
218
219
  it "re-runs the plans that were terminated but not re-executed (because no available executor)" do
@@ -236,10 +237,10 @@ module Dynflow
236
237
  retries = executor_world.auto_execute
237
238
  retries.each(&:wait)
238
239
  plan = client_world.persistence.load_execution_plan(triggered.id)
239
- plan.state.must_equal :paused
240
+ _(plan.state).must_equal :paused
240
241
  expected_history = [['start execution', executor_world.id],
241
242
  ['pause execution', executor_world.id]]
242
- plan.execution_history.map { |h| [h.name, h.world_id] }.must_equal(expected_history)
243
+ _(plan.execution_history.map { |h| [h.name, h.world_id] }).must_equal(expected_history)
243
244
  end
244
245
  end
245
246
 
@@ -266,45 +267,45 @@ module Dynflow
266
267
 
267
268
  it 'performs the validity check on world creation if auto_validity_check enabled' do
268
269
  client_world.coordinator.register_world(invalid_world)
269
- client_world.coordinator.find_worlds(false, id: invalid_world.id).wont_be_empty
270
+ _(client_world.coordinator.find_worlds(false, id: invalid_world.id)).wont_be_empty
270
271
  world_with_auto_validity_check
271
- client_world.coordinator.find_worlds(false, id: invalid_world.id).must_be_empty
272
+ _(client_world.coordinator.find_worlds(false, id: invalid_world.id)).must_be_empty
272
273
  end
273
274
 
274
275
  it 'by default, the auto_validity_check is enabled only for executor words' do
275
276
  client_world_config = Config::ForWorld.new(Config.new.tap { |c| c.executor = false }, create_world )
276
- client_world_config.auto_validity_check.must_equal false
277
+ _(client_world_config.auto_validity_check).must_equal false
277
278
 
278
- executor_world_config = Config::ForWorld.new(Config.new.tap { |c| c.executor = lambda { |w, _| Executors::Parallel.new(w, 15) } }, create_world )
279
- executor_world_config.auto_validity_check.must_equal true
279
+ executor_world_config = Config::ForWorld.new(Config.new.tap { |c| c.executor = Executors::Parallel::Core }, create_world )
280
+ _(executor_world_config.auto_validity_check).must_equal true
280
281
  end
281
282
 
282
283
  it 'reports the validation status' do
283
284
  client_world.coordinator.register_world(invalid_world)
284
285
  results = client_world.worlds_validity_check
285
- client_world.coordinator.find_worlds(false, id: invalid_world.id).must_be_empty
286
+ _(client_world.coordinator.find_worlds(false, id: invalid_world.id)).must_be_empty
286
287
 
287
- results[invalid_world.id].must_equal :invalidated
288
+ _(results[invalid_world.id]).must_equal :invalidated
288
289
 
289
- results[client_world.id].must_equal :valid
290
+ _(results[client_world.id]).must_equal :valid
290
291
  end
291
292
 
292
293
  it 'allows checking only, without actual invalidation' do
293
294
  client_world.coordinator.register_world(invalid_world)
294
295
  results = client_world.worlds_validity_check(false)
295
- client_world.coordinator.find_worlds(false, id: invalid_world.id).wont_be_empty
296
+ _(client_world.coordinator.find_worlds(false, id: invalid_world.id)).wont_be_empty
296
297
 
297
- results[invalid_world.id].must_equal :invalid
298
+ _(results[invalid_world.id]).must_equal :invalid
298
299
  end
299
300
 
300
301
  it 'allows to filter the worlds to run the check on' do
301
302
  client_world.coordinator.register_world(invalid_world)
302
303
  client_world.coordinator.register_world(invalid_world_2)
303
- client_world.coordinator.find_worlds(false, id: [invalid_world.id, invalid_world_2.id]).size.must_equal 2
304
+ _(client_world.coordinator.find_worlds(false, id: [invalid_world.id, invalid_world_2.id]).size).must_equal 2
304
305
 
305
306
  results = client_world.worlds_validity_check(true, :id => invalid_world.id)
306
- results.must_equal(invalid_world.id => :invalidated)
307
- client_world.coordinator.find_worlds(false, id: [invalid_world.id, invalid_world_2.id]).size.must_equal 1
307
+ _(results).must_equal(invalid_world.id => :invalidated)
308
+ _(client_world.coordinator.find_worlds(false, id: [invalid_world.id, invalid_world_2.id]).size).must_equal 1
308
309
  end
309
310
  end
310
311
  end
@@ -327,22 +328,22 @@ module Dynflow
327
328
  before do
328
329
  client_world.coordinator.acquire(valid_lock)
329
330
  client_world.coordinator.acquire(invalid_lock)
330
- current_locks.must_include(valid_lock)
331
- current_locks.must_include(invalid_lock)
331
+ _(current_locks).must_include(valid_lock)
332
+ _(current_locks).must_include(invalid_lock)
332
333
  end
333
334
 
334
335
  it 'performs the validity check on world creation if auto_validity_check enabled' do
335
336
  world_with_auto_validity_check
336
- current_locks.must_include(valid_lock)
337
- current_locks.wont_include(invalid_lock)
337
+ _(current_locks).must_include(valid_lock)
338
+ _(current_locks).wont_include(invalid_lock)
338
339
  end
339
340
 
340
341
  it 'performs the validity check on world creation if auto_validity_check enabled' do
341
342
  invalid_locks = client_world.locks_validity_check
342
- current_locks.must_include(valid_lock)
343
- current_locks.wont_include(invalid_lock)
344
- invalid_locks.must_include(invalid_lock)
345
- invalid_locks.wont_include(valid_lock)
343
+ _(current_locks).must_include(valid_lock)
344
+ _(current_locks).wont_include(invalid_lock)
345
+ _(invalid_locks).must_include(invalid_lock)
346
+ _(invalid_locks).wont_include(valid_lock)
346
347
  end
347
348
  end
348
349
 
@@ -372,9 +373,9 @@ module Dynflow
372
373
  client_world.coordinator.acquire(invalid_lock2)
373
374
  invalid_locks = client_world.coordinator.clean_orphaned_locks
374
375
  # It must invalidate locks which are missing or in paused/stopped
375
- invalid_locks.must_include(invalid_lock)
376
- invalid_locks.must_include(invalid_lock2)
377
- invalid_locks.wont_include(valid_lock)
376
+ _(invalid_locks).must_include(invalid_lock)
377
+ _(invalid_locks).must_include(invalid_lock2)
378
+ _(invalid_locks).wont_include(valid_lock)
378
379
  end
379
380
  end
380
381
  end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require_relative 'test_helper'
2
- require 'mocha/mini_test'
3
+ require 'mocha/minitest'
3
4
 
4
5
  module Dynflow
5
6
  describe 'action' do
@@ -26,7 +27,7 @@ module Dynflow
26
27
  Action.from_hash(action_data.merge(step: step), world)
27
28
  end
28
29
 
29
- specify { subject.class.name.must_equal 'RenamedAction' }
30
+ specify { _(subject.class.name).must_equal 'RenamedAction' }
30
31
  specify { assert subject.is_a? Action }
31
32
  end
32
33
 
@@ -35,16 +36,16 @@ module Dynflow
35
36
  smart_action_class = Class.new(Dynflow::Action)
36
37
  smarter_action_class = Class.new(smart_action_class)
37
38
 
38
- specify { smart_action_class.all_children.must_include smarter_action_class }
39
- specify { smart_action_class.all_children.size.must_equal 1 }
39
+ specify { _(smart_action_class.all_children).must_include smarter_action_class }
40
+ specify { _(smart_action_class.all_children.size).must_equal 1 }
40
41
 
41
42
  describe 'World#subscribed_actions' do
42
43
  event_action_class = Support::CodeWorkflowExample::Triage
43
44
  subscribed_action_class = Support::CodeWorkflowExample::NotifyAssignee
44
45
 
45
- specify { subscribed_action_class.subscribe.must_equal event_action_class }
46
- specify { world.subscribed_actions(event_action_class).must_include subscribed_action_class }
47
- specify { world.subscribed_actions(event_action_class).size.must_equal 1 }
46
+ specify { _(subscribed_action_class.subscribe).must_equal event_action_class }
47
+ specify { _(world.subscribed_actions(event_action_class)).must_include subscribed_action_class }
48
+ specify { _(world.subscribed_actions(event_action_class).size).must_equal 1 }
48
49
  end
49
50
  end
50
51
 
@@ -52,7 +53,7 @@ module Dynflow
52
53
 
53
54
  let :execution_plan do
54
55
  result = world.trigger(Support::CodeWorkflowExample::IncomingIssues, issues_data)
55
- result.must_be :planned?
56
+ _(result).must_be :planned?
56
57
  result.finished.value
57
58
  end
58
59
 
@@ -65,10 +66,10 @@ module Dynflow
65
66
  execution_plan.root_plan_step.action execution_plan
66
67
  end
67
68
 
68
- specify { presenter.class.must_equal Support::CodeWorkflowExample::IncomingIssues }
69
+ specify { _(presenter.class).must_equal Support::CodeWorkflowExample::IncomingIssues }
69
70
 
70
71
  it 'allows aggregating data from other actions' do
71
- presenter.summary.must_equal(assignees: ["John Doe"])
72
+ _(presenter.summary).must_equal(assignees: ["John Doe"])
72
73
  end
73
74
  end
74
75
 
@@ -78,7 +79,7 @@ module Dynflow
78
79
 
79
80
  it 'fails when input is not serializable' do
80
81
  klass = Class.new(Dynflow::Action)
81
- -> { create_and_plan_action klass, key: Object.new }.must_raise NoMethodError
82
+ _(-> { create_and_plan_action klass, key: Object.new }).must_raise NoMethodError
82
83
  end
83
84
 
84
85
  it 'fails when output is not serializable' do
@@ -88,7 +89,7 @@ module Dynflow
88
89
  end
89
90
  end
90
91
  action = create_and_plan_action klass, {}
91
- -> { run_action action }.must_raise NoMethodError
92
+ _(-> { run_action action }).must_raise NoMethodError
92
93
  end
93
94
  end
94
95
 
@@ -113,7 +114,56 @@ module Dynflow
113
114
  it 'is customizable from an action' do
114
115
  plan = create_and_plan_action ActionWithHumanizedState, {}
115
116
  action = run_action(plan)
116
- action.humanized_state.must_equal "waiting"
117
+ _(action.humanized_state).must_equal "waiting"
118
+ end
119
+ end
120
+
121
+ describe 'evented action' do
122
+ include Testing
123
+
124
+ class PlanEventedAction < Dynflow::Action
125
+ def run(event = nil)
126
+ case event
127
+ when "ping"
128
+ output[:status] = 'pinged'
129
+ when nil
130
+ plan_event('ping', input[:time])
131
+ suspend
132
+ else
133
+ self.output[:event] = event
134
+ end
135
+ end
136
+ end
137
+
138
+ it 'send planned event' do
139
+ plan = create_and_plan_action(PlanEventedAction, { time: 0.5 })
140
+ action = run_action plan
141
+
142
+ _(action.output[:status]).must_equal nil
143
+ _(action.world.clock.pending_pings.first).wont_be_nil
144
+ _(action.state).must_equal :suspended
145
+
146
+ progress_action_time action
147
+
148
+ _(action.output[:status]).must_equal 'pinged'
149
+ _(action.world.clock.pending_pings.first).must_be_nil
150
+ _(action.state).must_equal :success
151
+ end
152
+
153
+ it 'plans event immediately if no time is given' do
154
+ plan = create_and_plan_action(PlanEventedAction, { time: nil })
155
+ action = run_action plan
156
+
157
+ _(action.output[:status]).must_equal nil
158
+ _(action.world.clock.pending_pings.first).must_be_nil
159
+ _(action.world.executor.events_to_process.first).wont_be_nil
160
+ _(action.state).must_equal :suspended
161
+
162
+ action.world.executor.progress
163
+
164
+ _(action.output[:status]).must_equal 'pinged'
165
+ _(action.world.clock.pending_pings.first).must_be_nil
166
+ _(action.state).must_equal :success
117
167
  end
118
168
  end
119
169
 
@@ -222,7 +272,7 @@ module Dynflow
222
272
  end
223
273
  end
224
274
 
225
- describe'without timeout' do
275
+ describe 'without timeout' do
226
276
  let(:plan) do
227
277
  create_and_plan_action TestPollingAction, { task_args: 'do something' }
228
278
  end
@@ -238,37 +288,37 @@ module Dynflow
238
288
  it 'initiates the external task' do
239
289
  action = run_action plan
240
290
 
241
- action.output[:task][:task_id].must_equal 123
291
+ _(action.output[:task][:task_id]).must_equal 123
242
292
  end
243
293
 
244
294
  it 'polls till the task is done' do
245
295
  action = run_action plan
246
296
 
247
297
  9.times { progress_action_time action }
248
- action.done?.must_equal false
249
- next_ping(action).wont_be_nil
250
- action.state.must_equal :suspended
298
+ _(action.done?).must_equal false
299
+ _(next_ping(action)).wont_be_nil
300
+ _(action.state).must_equal :suspended
251
301
 
252
302
  progress_action_time action
253
- action.done?.must_equal true
254
- next_ping(action).must_be_nil
255
- action.state.must_equal :success
303
+ _(action.done?).must_equal true
304
+ _(next_ping(action)).must_be_nil
305
+ _(action.state).must_equal :success
256
306
  end
257
307
 
258
308
  it 'tries to poll for the old task when resuming' do
259
309
  action = run_action plan
260
- action.output[:task][:progress].must_equal 0
310
+ _(action.output[:task][:progress]).must_equal 0
261
311
  run_action action
262
- action.output[:task][:progress].must_equal 10
312
+ _(action.output[:task][:progress]).must_equal 10
263
313
  end
264
314
 
265
315
  it 'invokes the external task again when polling on the old one fails' do
266
316
  action = run_action plan
267
317
  action.world.silence_logger!
268
318
  action.external_service.will_fail
269
- action.output[:task][:progress].must_equal 0
319
+ _(action.output[:task][:progress]).must_equal 0
270
320
  run_action action
271
- action.output[:task][:progress].must_equal 0
321
+ _(action.output[:task][:progress]).must_equal 0
272
322
  end
273
323
 
274
324
  it 'tolerates some failure while polling' do
@@ -279,15 +329,15 @@ module Dynflow
279
329
  TestPollingAction.config.poll_max_retries = 3
280
330
  (1..2).each do |attempt|
281
331
  progress_action_time action
282
- action.poll_attempts[:failed].must_equal attempt
283
- next_ping(action).wont_be_nil
284
- action.state.must_equal :suspended
332
+ _(action.poll_attempts[:failed]).must_equal attempt
333
+ _(next_ping(action)).wont_be_nil
334
+ _(action.state).must_equal :suspended
285
335
  end
286
336
 
287
337
  progress_action_time action
288
- action.poll_attempts[:failed].must_equal 3
289
- next_ping(action).must_be_nil
290
- action.state.must_equal :error
338
+ _(action.poll_attempts[:failed]).must_equal 3
339
+ _(next_ping(action)).must_be_nil
340
+ _(action.state).must_equal :error
291
341
  end
292
342
 
293
343
  it 'allows increasing poll interval in a time' do
@@ -302,8 +352,8 @@ module Dynflow
302
352
  progress_action_time action
303
353
  pings << next_ping(action)
304
354
  progress_action_time action
305
- (pings[1].when - pings[0].when).must_be_close_to 1
306
- (pings[2].when - pings[1].when).must_be_close_to 2
355
+ _((pings[1].when - pings[0].when)).must_be_close_to 1
356
+ _((pings[2].when - pings[1].when)).must_be_close_to 2
307
357
  end
308
358
  end
309
359
 
@@ -324,10 +374,10 @@ module Dynflow
324
374
  # we count the number of iterations till the timeout occurs
325
375
  iterations += 1
326
376
  end
327
- action.state.must_equal :error
377
+ _(action.state).must_equal :error
328
378
  # two polls in 2 seconds intervals untill the 5 seconds
329
379
  # timeout appears
330
- iterations.must_equal 3
380
+ _(iterations).must_equal 3
331
381
  end
332
382
  end
333
383
  end
@@ -442,9 +492,9 @@ module Dynflow
442
492
 
443
493
  specify "the sub-plan stores the information about its parent" do
444
494
  sub_plans = execution_plan.sub_plans
445
- sub_plans.size.must_equal 2
446
- execution_plan.sub_plans_count.must_equal 2
447
- sub_plans.each { |sub_plan| sub_plan.caller_execution_plan_id.must_equal execution_plan.id }
495
+ _(sub_plans.size).must_equal 2
496
+ _(execution_plan.sub_plans_count).must_equal 2
497
+ sub_plans.each { |sub_plan| _(sub_plan.caller_execution_plan_id).must_equal execution_plan.id }
448
498
  end
449
499
 
450
500
  specify "the parent and sub-plan actions return root_action? properly" do
@@ -456,7 +506,7 @@ module Dynflow
456
506
  end
457
507
 
458
508
  specify "it saves the information about number for sub plans in the output" do
459
- execution_plan.entry_action.output.must_equal('total_count' => 2,
509
+ _(execution_plan.entry_action.output).must_equal('total_count' => 2,
460
510
  'failed_count' => 0,
461
511
  'success_count' => 2,
462
512
  'pending_count' => 0)
@@ -464,49 +514,49 @@ module Dynflow
464
514
 
465
515
  specify "when a sub plan fails, the caller action fails as well" do
466
516
  FailureSimulator.fail_in_child_run = true
467
- execution_plan.entry_action.output.must_equal('total_count' => 2,
517
+ _(execution_plan.entry_action.output).must_equal('total_count' => 2,
468
518
  'failed_count' => 2,
469
519
  'success_count' => 0,
470
520
  'pending_count' => 0)
471
- execution_plan.state.must_equal :paused
472
- execution_plan.result.must_equal :error
521
+ _(execution_plan.state).must_equal :paused
522
+ _(execution_plan.result).must_equal :error
473
523
  end
474
524
 
475
525
  describe 'resuming' do
476
526
  specify "resuming the action depends on the resume method definition" do
477
527
  FailureSimulator.fail_in_child_plan = true
478
- execution_plan.state.must_equal :paused
528
+ _(execution_plan.state).must_equal :paused
479
529
  FailureSimulator.fail_in_child_plan = false
480
530
  resumed_plan = world.execute(execution_plan.id).value
481
- resumed_plan.entry_action.output[:custom_resume].must_equal true
531
+ _(resumed_plan.entry_action.output[:custom_resume]).must_equal true
482
532
  end
483
533
 
484
534
  specify "by default, when no sub plans were planned successfully, it call create_sub_plans again" do
485
535
  FailureSimulator.fail_in_child_plan = true
486
- execution_plan.state.must_equal :paused
536
+ _(execution_plan.state).must_equal :paused
487
537
  FailureSimulator.fail_in_child_plan = false
488
538
  resumed_plan = world.execute(execution_plan.id).value
489
- resumed_plan.state.must_equal :stopped
490
- resumed_plan.result.must_equal :success
539
+ _(resumed_plan.state).must_equal :stopped
540
+ _(resumed_plan.result).must_equal :success
491
541
  end
492
542
 
493
543
  specify "by default, when any sub-plan was planned, it succeeds only when the sub-plans were already finished" do
494
544
  FailureSimulator.fail_in_child_run = true
495
- execution_plan.state.must_equal :paused
545
+ _(execution_plan.state).must_equal :paused
496
546
  sub_plans = execution_plan.sub_plans
497
547
 
498
548
  FailureSimulator.fail_in_child_run = false
499
549
  resumed_plan = world.execute(execution_plan.id).value
500
- resumed_plan.state.must_equal :paused
550
+ _(resumed_plan.state).must_equal :paused
501
551
 
502
552
  world.execute(sub_plans.first.id).wait
503
553
  resumed_plan = world.execute(execution_plan.id).value
504
- resumed_plan.state.must_equal :paused
554
+ _(resumed_plan.state).must_equal :paused
505
555
 
506
556
  sub_plans.drop(1).each { |sub_plan| world.execute(sub_plan.id).wait }
507
557
  resumed_plan = world.execute(execution_plan.id).value
508
- resumed_plan.state.must_equal :stopped
509
- resumed_plan.result.must_equal :success
558
+ _(resumed_plan.state).must_equal :stopped
559
+ _(resumed_plan.result).must_equal :success
510
560
  end
511
561
 
512
562
  describe ::Dynflow::Action::WithPollingSubPlans do
@@ -520,18 +570,18 @@ module Dynflow
520
570
  total = 2
521
571
  FailureSimulator.fail_in_child_plan = true
522
572
  triggered_plan = world.trigger(PollingParentAction, count: total)
523
- polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
524
573
 
525
- wait_for do # Waiting for the sub plans to be spawned
574
+ polling_plan = nil
575
+ wait_for('the subplans to be spawned') do
526
576
  polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
527
577
  polling_plan.sub_plans_count == total
528
578
  end
529
579
 
530
580
  # Moving the clock to make the parent check on sub plans
531
- clock.pending_pings.count.must_equal 1
581
+ _(clock.pending_pings.count).must_equal 1
532
582
  clock.progress
533
583
 
534
- wait_for do # Waiting for the parent to realise the sub plans failed
584
+ wait_for('the parent to realise the sub plans failed') do
535
585
  polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
536
586
  polling_plan.state == :paused
537
587
  end
@@ -540,15 +590,15 @@ module Dynflow
540
590
 
541
591
  world.execute(polling_plan.id) # The actual resume
542
592
 
543
- wait_for do # Waiting for new generation of sub plans to be spawned
593
+ wait_for('new generation of sub plans to be spawned') do
544
594
  polling_plan.sub_plans_count == 2 * total
545
595
  end
546
596
 
547
597
  # Move the clock again
548
- clock.pending_pings.count.must_equal 1
598
+ _(clock.pending_pings.count).must_equal 1
549
599
  clock.progress
550
600
 
551
- wait_for do # Waiting for everything to finish successfully
601
+ wait_for('everything to finish successfully') do
552
602
  polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
553
603
  polling_plan.state == :stopped && polling_plan.result == :success
554
604
  end
@@ -569,9 +619,9 @@ module Dynflow
569
619
  end
570
620
 
571
621
  # Moving the clock to make the parent check on sub plans
572
- clock.pending_pings.count.must_equal 1
622
+ _(clock.pending_pings.count).must_equal 1
573
623
  clock.progress
574
- clock.pending_pings.count.must_equal 0
624
+ _(clock.pending_pings.count).must_equal 0
575
625
 
576
626
  wait_for do # Waiting for the parent to realise the sub plans failed
577
627
  polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
@@ -613,8 +663,8 @@ module Dynflow
613
663
  end
614
664
  plan.cancel
615
665
  triggered_plan.finished.wait
616
- triggered_plan.finished.value.state.must_equal :stopped
617
- triggered_plan.finished.value.result.must_equal :success
666
+ _(triggered_plan.finished.value.state).must_equal :stopped
667
+ _(triggered_plan.finished.value.result).must_equal :success
618
668
  end
619
669
 
620
670
  it "sends the abort event to all actions that are running and support cancelling" do
@@ -627,10 +677,10 @@ module Dynflow
627
677
  end
628
678
  plan.cancel true
629
679
  triggered_plan.finished.wait
630
- triggered_plan.finished.value.state.must_equal :stopped
631
- triggered_plan.finished.value.result.must_equal :success
680
+ _(triggered_plan.finished.value.state).must_equal :stopped
681
+ _(triggered_plan.finished.value.result).must_equal :success
632
682
  plan.sub_plans.each do |sub_plan|
633
- sub_plan.entry_action.output[:aborted].must_equal true
683
+ _(sub_plan.entry_action.output[:aborted]).must_equal true
634
684
  end
635
685
  end
636
686
  end
@@ -645,20 +695,20 @@ module Dynflow
645
695
  world.stub :clock, clock do
646
696
  total = 2
647
697
  plan = world.plan(PollingParentAction, count: total)
648
- plan.state.must_equal :planned
649
- clock.pending_pings.count.must_equal 0
698
+ _(plan.state).must_equal :planned
699
+ _(clock.pending_pings.count).must_equal 0
650
700
  world.execute(plan.id)
651
701
  wait_for do
652
702
  plan.sub_plans_count == total &&
653
703
  plan.sub_plans.all? { |sub| sub.result == :success }
654
704
  end
655
- clock.pending_pings.count.must_equal 1
705
+ _(clock.pending_pings.count).must_equal 1
656
706
  clock.progress
657
707
  wait_for do
658
708
  plan = world.persistence.load_execution_plan(plan.id)
659
709
  plan.state == :stopped
660
710
  end
661
- clock.pending_pings.count.must_equal 0
711
+ _(clock.pending_pings.count).must_equal 0
662
712
  end
663
713
  end
664
714
 
@@ -667,14 +717,14 @@ module Dynflow
667
717
  total = 2
668
718
  plan = world.plan(PollingBulkParentAction, count: total)
669
719
  assert_nil plan.entry_action.output[:planning_finished]
670
- clock.pending_pings.count.must_equal 0
720
+ _(clock.pending_pings.count).must_equal 0
671
721
  world.execute(plan.id)
672
722
  wait_for do
673
723
  plan = world.persistence.load_execution_plan(plan.id)
674
724
  plan.entry_action.output[:planning_finished] == 1
675
725
  end
676
726
  # Poll was set during #initiate
677
- clock.pending_pings.count.must_equal 1
727
+ _(clock.pending_pings.count).must_equal 1
678
728
 
679
729
  # Wait for the sub plans to finish
680
730
  wait_for do
@@ -688,14 +738,14 @@ module Dynflow
688
738
  plan = world.persistence.load_execution_plan(plan.id)
689
739
  plan.state == :stopped
690
740
  end
691
- plan.entry_action.output[:poll].must_equal 1
692
- clock.pending_pings.count.must_equal 0
741
+ _(plan.entry_action.output[:poll]).must_equal 1
742
+ _(clock.pending_pings.count).must_equal 0
693
743
  end
694
744
  end
695
745
 
696
746
  it 'handles empty sub plans when calculating progress' do
697
747
  action = create_and_plan_action(PollingBulkParentAction, :count => 0)
698
- action.run_progress.must_equal 0.1
748
+ _(action.run_progress).must_equal 0.1
699
749
  end
700
750
 
701
751
  describe ::Dynflow::Action::Singleton do
@@ -737,58 +787,58 @@ module Dynflow
737
787
 
738
788
  it 'unlocks the locks after #plan if no #run or #finalize' do
739
789
  plan = world.plan(SingletonAction)
740
- plan.state.must_equal :planned
790
+ _(plan.state).must_equal :planned
741
791
  lock_filter = ::Dynflow::Coordinator::SingletonActionLock
742
792
  .unique_filter plan.entry_action.class.name
743
- world.coordinator.find_locks(lock_filter).count.must_equal 1
793
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 1
744
794
  plan = world.execute(plan.id).wait!.value
745
- plan.state.must_equal :stopped
746
- plan.result.must_equal :success
747
- world.coordinator.find_locks(lock_filter).count.must_equal 0
795
+ _(plan.state).must_equal :stopped
796
+ _(plan.result).must_equal :success
797
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 0
748
798
  end
749
799
 
750
800
  it 'unlocks the locks after #finalize' do
751
801
  plan = world.plan(SingletonActionWithFinalize)
752
- plan.state.must_equal :planned
802
+ _(plan.state).must_equal :planned
753
803
  lock_filter = ::Dynflow::Coordinator::SingletonActionLock
754
804
  .unique_filter plan.entry_action.class.name
755
- world.coordinator.find_locks(lock_filter).count.must_equal 1
805
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 1
756
806
  plan = world.execute(plan.id).wait!.value
757
- plan.state.must_equal :stopped
758
- plan.result.must_equal :success
759
- world.coordinator.find_locks(lock_filter).count.must_equal 0
807
+ _(plan.state).must_equal :stopped
808
+ _(plan.result).must_equal :success
809
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 0
760
810
  end
761
811
 
762
812
  it 'does not unlock when getting suspended' do
763
813
  plan = world.plan(SuspendedSingletonAction)
764
- plan.state.must_equal :planned
814
+ _(plan.state).must_equal :planned
765
815
  lock_filter = ::Dynflow::Coordinator::SingletonActionLock
766
816
  .unique_filter plan.entry_action.class.name
767
- world.coordinator.find_locks(lock_filter).count.must_equal 1
817
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 1
768
818
  future = world.execute(plan.id)
769
819
  wait_for do
770
820
  plan = world.persistence.load_execution_plan(plan.id)
771
821
  plan.state == :running && plan.result == :pending
772
822
  end
773
- world.coordinator.find_locks(lock_filter).count.must_equal 1
823
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 1
774
824
  world.event(plan.id, 2, nil)
775
825
  plan = future.wait!.value
776
- plan.state.must_equal :stopped
777
- plan.result.must_equal :success
778
- world.coordinator.find_locks(lock_filter).count.must_equal 0
826
+ _(plan.state).must_equal :stopped
827
+ _(plan.result).must_equal :success
828
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 0
779
829
  end
780
830
 
781
831
  it 'can be triggered only once' do
782
832
  # plan1 acquires the lock in plan phase
783
833
  plan1 = world.plan(SingletonActionWithRun)
784
- plan1.state.must_equal :planned
785
- plan1.result.must_equal :pending
834
+ _(plan1.state).must_equal :planned
835
+ _(plan1.result).must_equal :pending
786
836
 
787
837
  # plan2 tries to acquire the lock in plan phase and fails
788
838
  plan2 = world.plan(SingletonActionWithRun)
789
- plan2.state.must_equal :stopped
790
- plan2.result.must_equal :error
791
- plan2.errors.first.message.must_equal 'Action Dynflow::SingletonActionWithRun is already active'
839
+ _(plan2.state).must_equal :stopped
840
+ _(plan2.result).must_equal :error
841
+ _(plan2.errors.first.message).must_equal 'Action Dynflow::SingletonActionWithRun is already active'
792
842
 
793
843
  # Simulate some bad things happening
794
844
  plan1.entry_action.send(:singleton_unlock!)
@@ -799,42 +849,42 @@ module Dynflow
799
849
  # plan1 tries to relock on run
800
850
  # This should fail because the lock was taken by plan3
801
851
  plan1 = world.execute(plan1.id).wait!.value
802
- plan1.state.must_equal :paused
803
- plan1.result.must_equal :error
852
+ _(plan1.state).must_equal :paused
853
+ _(plan1.result).must_equal :error
804
854
 
805
855
  # plan3 can finish successfully because it holds the lock
806
856
  plan3 = world.execute(plan3.id).wait!.value
807
- plan3.state.must_equal :stopped
808
- plan3.result.must_equal :success
857
+ _(plan3.state).must_equal :stopped
858
+ _(plan3.result).must_equal :success
809
859
 
810
860
  # The lock was released when plan3 stopped
811
861
  lock_filter = ::Dynflow::Coordinator::SingletonActionLock
812
862
  .unique_filter plan3.entry_action.class.name
813
- world.coordinator.find_locks(lock_filter).must_be :empty?
863
+ _(world.coordinator.find_locks(lock_filter)).must_be :empty?
814
864
  end
815
865
 
816
866
  it 'cannot be unlocked by another action' do
817
867
  # plan1 doesn't keep its locks
818
868
  plan1 = world.plan(BadAction, true)
819
- plan1.state.must_equal :planned
869
+ _(plan1.state).must_equal :planned
820
870
  lock_filter = ::Dynflow::Coordinator::SingletonActionLock
821
871
  .unique_filter plan1.entry_action.class.name
822
- world.coordinator.find_locks(lock_filter).count.must_equal 0
872
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 0
823
873
  plan2 = world.plan(BadAction, false)
824
- plan2.state.must_equal :planned
825
- world.coordinator.find_locks(lock_filter).count.must_equal 1
874
+ _(plan2.state).must_equal :planned
875
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 1
826
876
 
827
877
  # The locks held by plan2 can't be unlocked by plan1
828
878
  plan1.entry_action.singleton_unlock!
829
- world.coordinator.find_locks(lock_filter).count.must_equal 1
879
+ _(world.coordinator.find_locks(lock_filter).count).must_equal 1
830
880
 
831
881
  plan1 = world.execute(plan1.id).wait!.value
832
- plan1.state.must_equal :paused
833
- plan1.result.must_equal :error
882
+ _(plan1.state).must_equal :paused
883
+ _(plan1.result).must_equal :error
834
884
 
835
885
  plan2 = world.execute(plan2.id).wait!.value
836
- plan2.state.must_equal :stopped
837
- plan2.result.must_equal :success
886
+ _(plan2.state).must_equal :stopped
887
+ _(plan2.result).must_equal :success
838
888
  end
839
889
  end
840
890
  end