dynflow 1.3.0 → 1.4.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 (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