aws-flow 1.0.5 → 1.0.6
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.
- data/lib/aws/decider.rb +0 -9
 - data/lib/aws/decider/async_decider.rb +13 -12
 - data/lib/aws/decider/decider.rb +2 -2
 - data/lib/aws/decider/generic_client.rb +4 -0
 - data/lib/aws/decider/task_poller.rb +5 -2
 - data/lib/aws/decider/version.rb +1 -1
 - data/lib/aws/decider/workflow_client.rb +17 -3
 - data/lib/aws/flow.rb +29 -0
 - data/lib/aws/flow/async_backtrace.rb +134 -0
 - data/lib/aws/flow/async_scope.rb +195 -0
 - data/lib/aws/flow/begin_rescue_ensure.rb +386 -0
 - data/lib/aws/flow/fiber.rb +77 -0
 - data/lib/aws/flow/flow_utils.rb +50 -0
 - data/lib/aws/flow/future.rb +115 -0
 - data/lib/aws/flow/implementation.rb +151 -0
 - data/lib/aws/flow/simple_dfa.rb +85 -0
 - data/lib/aws/flow/tasks.rb +405 -0
 - data/test/aws/async_backtrace_spec.rb +41 -0
 - data/test/aws/async_scope_spec.rb +118 -0
 - data/test/aws/begin_rescue_ensure_spec.rb +665 -0
 - data/test/aws/decider_spec.rb +155 -35
 - data/test/aws/external_task_spec.rb +197 -0
 - data/test/aws/factories.rb +30 -1
 - data/test/aws/fiber_condition_variable_spec.rb +163 -0
 - data/test/aws/fiber_spec.rb +78 -0
 - data/test/aws/flow_spec.rb +255 -0
 - data/test/aws/future_spec.rb +210 -0
 - data/test/aws/integration_spec.rb +83 -39
 - data/test/aws/rubyflow.rb +22 -0
 - data/test/aws/simple_dfa_spec.rb +63 -0
 - data/test/aws/spec_helper.rb +11 -5
 - metadata +23 -3
 
    
        data/test/aws/decider_spec.rb
    CHANGED
    
    | 
         @@ -30,6 +30,14 @@ class TrivialConverter 
     | 
|
| 
       30 
30 
     | 
    
         
             
              end
         
     | 
| 
       31 
31 
     | 
    
         
             
            end
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
      
 33 
     | 
    
         
            +
            class FakeLogger
         
     | 
| 
      
 34 
     | 
    
         
            +
              attr_accessor :level
         
     | 
| 
      
 35 
     | 
    
         
            +
              def info(s); end
         
     | 
| 
      
 36 
     | 
    
         
            +
              def debug(s); end
         
     | 
| 
      
 37 
     | 
    
         
            +
              def warn(s); end
         
     | 
| 
      
 38 
     | 
    
         
            +
              def error(s); end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       33 
41 
     | 
    
         
             
            class FakePage
         
     | 
| 
       34 
42 
     | 
    
         
             
              def initialize(object); @object = object; end
         
     | 
| 
       35 
43 
     | 
    
         
             
              def page; @object; end
         
     | 
| 
         @@ -1095,7 +1103,7 @@ describe "FakeHistory" do 
     | 
|
| 
       1095 
1103 
     | 
    
         
             
                BadWorkflow.trace.should == [:start, :middle, :end]
         
     | 
| 
       1096 
1104 
     | 
    
         
             
              end
         
     | 
| 
       1097 
1105 
     | 
    
         | 
| 
       1098 
     | 
    
         
            -
              it "makes sure that  
     | 
| 
      
 1106 
     | 
    
         
            +
              it "makes sure that raising an error properly fails a workflow" do
         
     | 
| 
       1099 
1107 
     | 
    
         
             
                class SynchronousWorkflowTaskPoller < WorkflowTaskPoller
         
     | 
| 
       1100 
1108 
     | 
    
         
             
                  def get_decision_tasks
         
     | 
| 
       1101 
1109 
     | 
    
         
             
                    fake_workflow_type =  FakeWorkflowType.new(nil, "BadWorkflow.entry_point", "1")
         
     | 
| 
         @@ -1185,6 +1193,85 @@ describe "FakeHistory" do 
     | 
|
| 
       1185 
1193 
     | 
    
         
             
                worker.start
         
     | 
| 
       1186 
1194 
     | 
    
         
             
                swf_client.trace.first[:decisions].first[:start_timer_decision_attributes][:start_to_fire_timeout].should == "5"
         
     | 
| 
       1187 
1195 
     | 
    
         
             
              end
         
     | 
| 
      
 1196 
     | 
    
         
            +
             
     | 
| 
      
 1197 
     | 
    
         
            +
              it "ensures that CompleteWorkflowExecutionFailed is correctly handled" do
         
     | 
| 
      
 1198 
     | 
    
         
            +
                class FakeAttribute
         
     | 
| 
      
 1199 
     | 
    
         
            +
                  def initialize(data)
         
     | 
| 
      
 1200 
     | 
    
         
            +
                    @data = data
         
     | 
| 
      
 1201 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1202 
     | 
    
         
            +
                  def method_missing(method_name, *args, &block)
         
     | 
| 
      
 1203 
     | 
    
         
            +
                    if @data.keys.include? method_name
         
     | 
| 
      
 1204 
     | 
    
         
            +
                      return @data[method_name]
         
     | 
| 
      
 1205 
     | 
    
         
            +
                    end
         
     | 
| 
      
 1206 
     | 
    
         
            +
                    super
         
     | 
| 
      
 1207 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1208 
     | 
    
         
            +
                end
         
     | 
| 
      
 1209 
     | 
    
         
            +
             
     | 
| 
      
 1210 
     | 
    
         
            +
                class SynchronousWorkflowTaskPoller < WorkflowTaskPoller
         
     | 
| 
      
 1211 
     | 
    
         
            +
                  def get_decision_tasks
         
     | 
| 
      
 1212 
     | 
    
         
            +
                    fake_workflow_type = FakeWorkflowType.new(nil, "CompleteWorkflowExecutionFailedWorkflow.entry_point", "1")
         
     | 
| 
      
 1213 
     | 
    
         
            +
                    TestHistoryWrapper.new(fake_workflow_type,
         
     | 
| 
      
 1214 
     | 
    
         
            +
                                           [
         
     | 
| 
      
 1215 
     | 
    
         
            +
                                            TestHistoryEvent.new("WorkflowExecutionStarted", 1, {}),
         
     | 
| 
      
 1216 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskScheduled", 2, {}),
         
     | 
| 
      
 1217 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskStarted", 3, {}),
         
     | 
| 
      
 1218 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskCompleted", 4, {}),
         
     | 
| 
      
 1219 
     | 
    
         
            +
                                            TestHistoryEvent.new("ActivityTaskScheduled", 5, {:activity_id => "Activity1"}),
         
     | 
| 
      
 1220 
     | 
    
         
            +
                                            TestHistoryEvent.new("ActivityTaskScheduled", 6, {:activity_id => "Activity2"}),
         
     | 
| 
      
 1221 
     | 
    
         
            +
                                            TestHistoryEvent.new("ActivityTaskStarted", 7, {}),
         
     | 
| 
      
 1222 
     | 
    
         
            +
                                            TestHistoryEvent.new("ActivityTaskFailed", 8, {:scheduled_event_id => 5}),
         
     | 
| 
      
 1223 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskScheduled", 9, {}),
         
     | 
| 
      
 1224 
     | 
    
         
            +
                                            TestHistoryEvent.new("ActivityTaskStarted", 10, {}),
         
     | 
| 
      
 1225 
     | 
    
         
            +
                                            TestHistoryEvent.new("ActivityTaskFailed", 11, {:scheduled_event_id => 6}),
         
     | 
| 
      
 1226 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskStarted", 12, {}),
         
     | 
| 
      
 1227 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskCompleted", 13, {}),
         
     | 
| 
      
 1228 
     | 
    
         
            +
                                            TestHistoryEvent.new("RequestCancelActivityTaskFailed", 14, FakeAttribute.new({:activity_id => "Activity2"}) ) ,
         
     | 
| 
      
 1229 
     | 
    
         
            +
                                            TestHistoryEvent.new("CompleteWorkflowExecutionFailed", 15, {}),
         
     | 
| 
      
 1230 
     | 
    
         
            +
                                            TestHistoryEvent.new("DecisionTaskScheduled", 16, {}),
         
     | 
| 
      
 1231 
     | 
    
         
            +
                                           ])
         
     | 
| 
      
 1232 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1233 
     | 
    
         
            +
                end
         
     | 
| 
      
 1234 
     | 
    
         
            +
                workflow_type_object = double("workflow_type", :name => "CompleteWorkflowExecutionFailedWorkflow.entry_point", :start_execution => "" )
         
     | 
| 
      
 1235 
     | 
    
         
            +
                domain = FakeDomain.new(workflow_type_object)
         
     | 
| 
      
 1236 
     | 
    
         
            +
             
     | 
| 
      
 1237 
     | 
    
         
            +
                class CompleteWorkflowExecutionFailedActivity
         
     | 
| 
      
 1238 
     | 
    
         
            +
                  extend Activity
         
     | 
| 
      
 1239 
     | 
    
         
            +
                  activity :run_activity1
         
     | 
| 
      
 1240 
     | 
    
         
            +
                  def run_activity1; raise StandardError; end
         
     | 
| 
      
 1241 
     | 
    
         
            +
                end
         
     | 
| 
      
 1242 
     | 
    
         
            +
                class CompleteWorkflowExecutionFailedWorkflow
         
     | 
| 
      
 1243 
     | 
    
         
            +
                  extend Workflows
         
     | 
| 
      
 1244 
     | 
    
         
            +
                  workflow(:entry_point) { {:version => "1"} }
         
     | 
| 
      
 1245 
     | 
    
         
            +
                  activity_client(:activity) { {:version => "1", :prefix_name => "CompleteWorkflowExecutionFailedActivity" } }
         
     | 
| 
      
 1246 
     | 
    
         
            +
                  def entry_point
         
     | 
| 
      
 1247 
     | 
    
         
            +
                    child_futures = []
         
     | 
| 
      
 1248 
     | 
    
         
            +
                    error_handler do |t|
         
     | 
| 
      
 1249 
     | 
    
         
            +
                      t.begin do
         
     | 
| 
      
 1250 
     | 
    
         
            +
                        child_futures << activity.send_async(:run_activity1)
         
     | 
| 
      
 1251 
     | 
    
         
            +
                        child_futures << activity.send_async(:run_activity1)
         
     | 
| 
      
 1252 
     | 
    
         
            +
                        wait_for_all(child_futures)
         
     | 
| 
      
 1253 
     | 
    
         
            +
                      end
         
     | 
| 
      
 1254 
     | 
    
         
            +
                      t.rescue(Exception) do |error|
         
     | 
| 
      
 1255 
     | 
    
         
            +
                      end
         
     | 
| 
      
 1256 
     | 
    
         
            +
                      t.ensure do
         
     | 
| 
      
 1257 
     | 
    
         
            +
                      end
         
     | 
| 
      
 1258 
     | 
    
         
            +
                    end
         
     | 
| 
      
 1259 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1260 
     | 
    
         
            +
                end
         
     | 
| 
      
 1261 
     | 
    
         
            +
                swf_client = FakeServiceClient.new
         
     | 
| 
      
 1262 
     | 
    
         
            +
                task_list = "CompleteWorkflowExecutionFailedWorkflow_tasklist"
         
     | 
| 
      
 1263 
     | 
    
         
            +
                my_workflow_factory = workflow_factory(swf_client, domain) do |options|
         
     | 
| 
      
 1264 
     | 
    
         
            +
                  options.workflow_name = "CompleteWorkflowExecutionFailedWorkflow"
         
     | 
| 
      
 1265 
     | 
    
         
            +
                  options.execution_start_to_close_timeout = 3600
         
     | 
| 
      
 1266 
     | 
    
         
            +
                  options.task_list = task_list
         
     | 
| 
      
 1267 
     | 
    
         
            +
                end
         
     | 
| 
      
 1268 
     | 
    
         
            +
                worker = SynchronousWorkflowWorker.new(swf_client, domain, task_list)
         
     | 
| 
      
 1269 
     | 
    
         
            +
                worker.add_workflow_implementation(CompleteWorkflowExecutionFailedWorkflow)
         
     | 
| 
      
 1270 
     | 
    
         
            +
                my_workflow = my_workflow_factory.get_client
         
     | 
| 
      
 1271 
     | 
    
         
            +
                workflow_execution = my_workflow.start_execution
         
     | 
| 
      
 1272 
     | 
    
         
            +
                worker.start
         
     | 
| 
      
 1273 
     | 
    
         
            +
                swf_client.trace.first[:decisions].first[:decision_type].should == "CompleteWorkflowExecution"
         
     | 
| 
      
 1274 
     | 
    
         
            +
              end
         
     | 
| 
       1188 
1275 
     | 
    
         
             
            end
         
     | 
| 
       1189 
1276 
     | 
    
         | 
| 
       1190 
1277 
     | 
    
         
             
            describe "Misc tests" do
         
     | 
| 
         @@ -1224,6 +1311,33 @@ describe "Misc tests" do 
     | 
|
| 
       1224 
1311 
     | 
    
         
             
              end
         
     | 
| 
       1225 
1312 
     | 
    
         | 
| 
       1226 
1313 
     | 
    
         | 
| 
      
 1314 
     | 
    
         
            +
              it "ensures that using send_async doesn't mutate the original hash" do
         
     | 
| 
      
 1315 
     | 
    
         
            +
                class GenericClientTest < GenericClient
         
     | 
| 
      
 1316 
     | 
    
         
            +
                  def call_options(*args, &options)
         
     | 
| 
      
 1317 
     | 
    
         
            +
                    options.call
         
     | 
| 
      
 1318 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1319 
     | 
    
         
            +
                end
         
     | 
| 
      
 1320 
     | 
    
         
            +
                # Instead of setting up the fiber, just pretend we're internal
         
     | 
| 
      
 1321 
     | 
    
         
            +
                module Utilities
         
     | 
| 
      
 1322 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 1323 
     | 
    
         
            +
                    alias_method :old_is_external, :is_external
         
     | 
| 
      
 1324 
     | 
    
         
            +
                    def is_external
         
     | 
| 
      
 1325 
     | 
    
         
            +
                      return false
         
     | 
| 
      
 1326 
     | 
    
         
            +
                    end
         
     | 
| 
      
 1327 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1328 
     | 
    
         
            +
                end
         
     | 
| 
      
 1329 
     | 
    
         
            +
                generic_client = GenericClientTest.new
         
     | 
| 
      
 1330 
     | 
    
         
            +
                previous_hash = {:key => :value}
         
     | 
| 
      
 1331 
     | 
    
         
            +
                previous_hash_copy = previous_hash.dup
         
     | 
| 
      
 1332 
     | 
    
         
            +
                generic_client.send_async(:call_options) { previous_hash }
         
     | 
| 
      
 1333 
     | 
    
         
            +
                # Put is_external back before we have a chance of failing
         
     | 
| 
      
 1334 
     | 
    
         
            +
                module Utilities
         
     | 
| 
      
 1335 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 1336 
     | 
    
         
            +
                    alias_method :is_external, :old_is_external
         
     | 
| 
      
 1337 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1338 
     | 
    
         
            +
                end
         
     | 
| 
      
 1339 
     | 
    
         
            +
                previous_hash.should == previous_hash_copy
         
     | 
| 
      
 1340 
     | 
    
         
            +
              end
         
     | 
| 
       1227 
1341 
     | 
    
         
             
            end
         
     | 
| 
       1228 
1342 
     | 
    
         | 
| 
       1229 
1343 
     | 
    
         
             
            describe FlowConstants do
         
     | 
| 
         @@ -1280,40 +1394,43 @@ class TestActivityWorker < ActivityWorker 
     | 
|
| 
       1280 
1394 
     | 
    
         | 
| 
       1281 
1395 
     | 
    
         
             
              attr_accessor :executor
         
     | 
| 
       1282 
1396 
     | 
    
         
             
              def initialize(service, domain, task_list, forking_executor, *args, &block)
         
     | 
| 
       1283 
     | 
    
         
            -
                super(service, domain, task_list, *args)
         
     | 
| 
      
 1397 
     | 
    
         
            +
                super(service, domain, task_list, *args, &block)
         
     | 
| 
       1284 
1398 
     | 
    
         
             
                @executor = forking_executor
         
     | 
| 
       1285 
1399 
     | 
    
         
             
              end
         
     | 
| 
       1286 
1400 
     | 
    
         
             
            end
         
     | 
| 
       1287 
1401 
     | 
    
         | 
| 
       1288 
1402 
     | 
    
         
             
            describe ActivityWorker do
         
     | 
| 
       1289 
1403 
     | 
    
         | 
| 
       1290 
     | 
    
         
            -
              it "will test whether the ActivityWorker shuts down cleanly when an interrupt is received" do
         
     | 
| 
       1291 
     | 
    
         
            -
             
     | 
| 
       1292 
     | 
    
         
            -
             
     | 
| 
       1293 
     | 
    
         
            -
             
     | 
| 
       1294 
     | 
    
         
            -
             
     | 
| 
       1295 
     | 
    
         
            -
             
     | 
| 
       1296 
     | 
    
         
            -
             
     | 
| 
       1297 
     | 
    
         
            -
             
     | 
| 
       1298 
     | 
    
         
            -
             
     | 
| 
       1299 
     | 
    
         
            -
             
     | 
| 
       1300 
     | 
    
         
            -
             
     | 
| 
       1301 
     | 
    
         
            -
             
     | 
| 
       1302 
     | 
    
         
            -
             
     | 
| 
       1303 
     | 
    
         
            -
             
     | 
| 
       1304 
     | 
    
         
            -
             
     | 
| 
       1305 
     | 
    
         
            -
             
     | 
| 
       1306 
     | 
    
         
            -
             
     | 
| 
       1307 
     | 
    
         
            -
             
     | 
| 
       1308 
     | 
    
         
            -
             
     | 
| 
       1309 
     | 
    
         
            -
             
     | 
| 
       1310 
     | 
    
         
            -
             
     | 
| 
       1311 
     | 
    
         
            -
             
     | 
| 
       1312 
     | 
    
         
            -
             
     | 
| 
       1313 
     | 
    
         
            -
             
     | 
| 
       1314 
     | 
    
         
            -
               
     | 
| 
       1315 
     | 
    
         
            -
             
     | 
| 
       1316 
     | 
    
         
            -
              #  
     | 
| 
      
 1404 
     | 
    
         
            +
              # it "will test whether the ActivityWorker shuts down cleanly when an interrupt is received" do
         
     | 
| 
      
 1405 
     | 
    
         
            +
             
     | 
| 
      
 1406 
     | 
    
         
            +
              #   task_list = "TestWorkflow_tasklist"
         
     | 
| 
      
 1407 
     | 
    
         
            +
              #   service = FakeServiceClient.new
         
     | 
| 
      
 1408 
     | 
    
         
            +
              #   workflow_type_object = double("workflow_type", :name => "TestWorkflow.entry_point", :start_execution => "" )
         
     | 
| 
      
 1409 
     | 
    
         
            +
              #   domain = FakeDomain.new(workflow_type_object)
         
     | 
| 
      
 1410 
     | 
    
         
            +
              #   forking_executor = ForkingExecutor.new
         
     | 
| 
      
 1411 
     | 
    
         
            +
              #   activity_worker = TestActivityWorker.new(service, domain, task_list, forking_executor) { {:logger => FakeLogger.new} }
         
     | 
| 
      
 1412 
     | 
    
         
            +
             
     | 
| 
      
 1413 
     | 
    
         
            +
              #   activity_worker.add_activities_implementation(TestActivity)
         
     | 
| 
      
 1414 
     | 
    
         
            +
              #   # Starts the activity worker in a forked process. Also, attaches an at_exit
         
     | 
| 
      
 1415 
     | 
    
         
            +
              #   # handler to the process. When the process exits, the handler checks whether
         
     | 
| 
      
 1416 
     | 
    
         
            +
              #   # the executor's internal is_shutdown variable is set correctly or not.
         
     | 
| 
      
 1417 
     | 
    
         
            +
              #   pid = fork do
         
     | 
| 
      
 1418 
     | 
    
         
            +
              #     at_exit {
         
     | 
| 
      
 1419 
     | 
    
         
            +
              #       activity_worker.executor.is_shutdown.should == true
         
     | 
| 
      
 1420 
     | 
    
         
            +
              #     }
         
     | 
| 
      
 1421 
     | 
    
         
            +
              #     activity_worker.start true
         
     | 
| 
      
 1422 
     | 
    
         
            +
              #   end
         
     | 
| 
      
 1423 
     | 
    
         
            +
              #   # Adding a sleep to let things get setup correctly (not ideal but going with
         
     | 
| 
      
 1424 
     | 
    
         
            +
              #   # this for now)
         
     | 
| 
      
 1425 
     | 
    
         
            +
              #   #sleep 1
         
     | 
| 
      
 1426 
     | 
    
         
            +
              #   # Send an interrupt to the child process
         
     | 
| 
      
 1427 
     | 
    
         
            +
              #   Process.kill("INT", pid)
         
     | 
| 
      
 1428 
     | 
    
         
            +
              #   status = Process.waitall
         
     | 
| 
      
 1429 
     | 
    
         
            +
              #   status[0][1].success?.should be_true
         
     | 
| 
      
 1430 
     | 
    
         
            +
              # end
         
     | 
| 
      
 1431 
     | 
    
         
            +
             
     | 
| 
      
 1432 
     | 
    
         
            +
              # This method will take a long time to run, allowing us to test our shutdown
         
     | 
| 
      
 1433 
     | 
    
         
            +
              # scenarios
         
     | 
| 
       1317 
1434 
     | 
    
         
             
              def dumb_fib(n)
         
     | 
| 
       1318 
1435 
     | 
    
         
             
                n < 1 ? 1 : dumb_fib(n - 1) + dumb_fib(n - 2)
         
     | 
| 
       1319 
1436 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1324,20 +1441,23 @@ describe ActivityWorker do 
     | 
|
| 
       1324 
1441 
     | 
    
         
             
                workflow_type_object = double("workflow_type", :name => "TestWorkflow.entry_point", :start_execution => "" )
         
     | 
| 
       1325 
1442 
     | 
    
         
             
                domain = FakeDomain.new(workflow_type_object)
         
     | 
| 
       1326 
1443 
     | 
    
         
             
                forking_executor = ForkingExecutor.new
         
     | 
| 
       1327 
     | 
    
         
            -
                activity_worker = TestActivityWorker.new(service, domain, task_list, forking_executor)
         
     | 
| 
      
 1444 
     | 
    
         
            +
                activity_worker = TestActivityWorker.new(service, domain, task_list, forking_executor) { {:logger => FakeLogger.new} }
         
     | 
| 
       1328 
1445 
     | 
    
         | 
| 
       1329 
1446 
     | 
    
         
             
                activity_worker.add_activities_implementation(TestActivity)
         
     | 
| 
       1330 
     | 
    
         
            -
                # Starts the activity worker in a forked process. Also, executes a task 
     | 
| 
       1331 
     | 
    
         
            -
                #  
     | 
| 
       1332 
     | 
    
         
            -
                #  
     | 
| 
      
 1447 
     | 
    
         
            +
                # Starts the activity worker in a forked process. Also, executes a task
         
     | 
| 
      
 1448 
     | 
    
         
            +
                # using the forking executor of the activity worker. The executor will
         
     | 
| 
      
 1449 
     | 
    
         
            +
                # create a child process to run that task. The task (dumb_fib) is
         
     | 
| 
      
 1450 
     | 
    
         
            +
                # purposefully designed to be long running so that we can test our shutdown
         
     | 
| 
      
 1451 
     | 
    
         
            +
                # scenario.
         
     | 
| 
       1333 
1452 
     | 
    
         
             
                pid = fork do
         
     | 
| 
       1334 
1453 
     | 
    
         
             
                  activity_worker.executor.execute {
         
     | 
| 
       1335 
1454 
     | 
    
         
             
                    dumb_fib(1000)
         
     | 
| 
       1336 
1455 
     | 
    
         
             
                  }
         
     | 
| 
       1337 
1456 
     | 
    
         
             
                  activity_worker.start true
         
     | 
| 
       1338 
1457 
     | 
    
         
             
                end
         
     | 
| 
       1339 
     | 
    
         
            -
                # Adding a sleep to let things get setup correctly (not idea but going with 
     | 
| 
       1340 
     | 
    
         
            -
                 
     | 
| 
      
 1458 
     | 
    
         
            +
                # Adding a sleep to let things get setup correctly (not idea but going with
         
     | 
| 
      
 1459 
     | 
    
         
            +
                # this for now)
         
     | 
| 
      
 1460 
     | 
    
         
            +
                sleep 3
         
     | 
| 
       1341 
1461 
     | 
    
         
             
                # Send 2 interrupts to the child process
         
     | 
| 
       1342 
1462 
     | 
    
         
             
                2.times { Process.kill("INT", pid); sleep 3 }
         
     | 
| 
       1343 
1463 
     | 
    
         
             
                status = Process.waitall
         
     | 
| 
         @@ -0,0 +1,197 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ##
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Licensed under the Apache License, Version 2.0 (the "License").
         
     | 
| 
      
 5 
     | 
    
         
            +
            # You may not use this file except in compliance with the License.
         
     | 
| 
      
 6 
     | 
    
         
            +
            # A copy of the License is located at
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            #  http://aws.amazon.com/apache2.0
         
     | 
| 
      
 9 
     | 
    
         
            +
            #
         
     | 
| 
      
 10 
     | 
    
         
            +
            # or in the "license" file accompanying this file. This file is distributed
         
     | 
| 
      
 11 
     | 
    
         
            +
            # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
         
     | 
| 
      
 12 
     | 
    
         
            +
            # express or implied. See the License for the specific language governing
         
     | 
| 
      
 13 
     | 
    
         
            +
            # permissions and limitations under the License.
         
     | 
| 
      
 14 
     | 
    
         
            +
            ##
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            describe "ExternalTask#alive?" do
         
     | 
| 
      
 17 
     | 
    
         
            +
              let(:scope) { AsyncScope.new }
         
     | 
| 
      
 18 
     | 
    
         
            +
              let(:simple_unit) do
         
     | 
| 
      
 19 
     | 
    
         
            +
                ExternalTask.new(:parent => scope ) do |t|
         
     | 
| 
      
 20 
     | 
    
         
            +
                  t.initiate_task { |h| 2 + 2; h.complete }
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
              it "tells you that ExternalTask is alive before the ExternalTask starts" do
         
     | 
| 
      
 24 
     | 
    
         
            +
                simple_unit.alive?.should == true
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
              it "tells you that ExternalTask is not alive once ExternalTask has exited" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                task_context = TaskContext.new(:parent => scope.get_closest_containing_scope, :task => simple_unit)
         
     | 
| 
      
 28 
     | 
    
         
            +
                simple_unit.resume
         
     | 
| 
      
 29 
     | 
    
         
            +
                simple_unit.alive?.should == false
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            describe ExternalTask do
         
     | 
| 
      
 34 
     | 
    
         
            +
              let(:trace) { [] }
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              before(:each) do
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                class ExternalTaskException < Exception; end
         
     | 
| 
      
 39 
     | 
    
         
            +
                @this_scope = AsyncScope.new() do
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                @this_task = ExternalTask.new(:parent => @this_scope) do |t|
         
     | 
| 
      
 42 
     | 
    
         
            +
                  t.cancellation_handler { |h, cause| h.fail(cause) }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  t.initiate_task { |h| trace << :task_started; h.complete }
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
         
     | 
| 
      
 46 
     | 
    
         
            +
                @this_scope << @this_task
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              it "should complete okay" do
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                @this_scope.eventLoop
         
     | 
| 
      
 52 
     | 
    
         
            +
                trace.should == [:task_started]
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              it "cancelling should cause an exception to be raised" do
         
     | 
| 
      
 56 
     | 
    
         
            +
                @this_task.cancel(ExternalTaskException.new)
         
     | 
| 
      
 57 
     | 
    
         
            +
                expect { @this_scope.eventLoop }.to raise_error ExternalTaskException
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              it "shouldn't cancel an already completed task" do
         
     | 
| 
      
 61 
     | 
    
         
            +
                @this_scope.eventLoop
         
     | 
| 
      
 62 
     | 
    
         
            +
                @this_task.cancel(Exception)
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              describe "cancelling an external task without a cancellation handler" do
         
     | 
| 
      
 66 
     | 
    
         
            +
                before(:each) do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @this_scope = AsyncScope.new() do
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @this_task = ExternalTask.new() do |t|
         
     | 
| 
      
 71 
     | 
    
         
            +
                    t.initiate_task { |h| trace << :task_started; h.complete }
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @this_scope << @this_task
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
                it "ensures that calling cancel results in no error" do
         
     | 
| 
      
 76 
     | 
    
         
            +
                  task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @this_task.cancel(ExternalTaskException)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
                it "ensures that attempting to run a cancelled task has no effect" do
         
     | 
| 
      
 80 
     | 
    
         
            +
                  task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @this_task.cancel(ExternalTaskException)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @this_scope.eventLoop
         
     | 
| 
      
 83 
     | 
    
         
            +
                  trace.should == []
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              it "ensures that raising in the cancellation handler is handled " do
         
     | 
| 
      
 88 
     | 
    
         
            +
                scope = AsyncScope.new
         
     | 
| 
      
 89 
     | 
    
         
            +
                external_task = ExternalTask.new() do |t|
         
     | 
| 
      
 90 
     | 
    
         
            +
                  t.initiate_task { |h| h.complete }
         
     | 
| 
      
 91 
     | 
    
         
            +
                  t.cancellation_handler { |h, cause| raise "Oh no!" }
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
                scope << external_task
         
     | 
| 
      
 94 
     | 
    
         
            +
                task_context = TaskContext.new(:parent => scope.get_closest_containing_scope, :task => external_task)
         
     | 
| 
      
 95 
     | 
    
         
            +
                external_task.cancel(Exception)
         
     | 
| 
      
 96 
     | 
    
         
            +
                expect { scope.eventLoop }.to raise_error /Oh no!/
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
              it "ensures that cancelling a completed task is handled" do
         
     | 
| 
      
 100 
     | 
    
         
            +
                @this_scope.eventLoop
         
     | 
| 
      
 101 
     | 
    
         
            +
                @this_task.cancel(Exception)
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
              it "ensures that cancelling a cancelled task is handled" do
         
     | 
| 
      
 105 
     | 
    
         
            +
                @this_scope.eventLoop
         
     | 
| 
      
 106 
     | 
    
         
            +
                @this_task.cancel(Exception)
         
     | 
| 
      
 107 
     | 
    
         
            +
                @this_task.cancel(Exception)
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
              it "ensures that failing a task after completion raises an error" do
         
     | 
| 
      
 111 
     | 
    
         
            +
                @this_scope = AsyncScope.new() do
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
                @this_task = ExternalTask.new() do |t|
         
     | 
| 
      
 114 
     | 
    
         
            +
                  t.cancellation_handler { |h| h.fail }
         
     | 
| 
      
 115 
     | 
    
         
            +
                  t.initiate_task { |h| trace << :task_started; h.complete; h.fail(Exception) }
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
                task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
         
     | 
| 
      
 118 
     | 
    
         
            +
                @this_scope << @this_task
         
     | 
| 
      
 119 
     | 
    
         
            +
                expect { @this_scope.eventLoop }.to raise_error /Already completed/
         
     | 
| 
      
 120 
     | 
    
         
            +
              end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
              it "ensures that completing an external_task allows the asyncScope to move forward" do
         
     | 
| 
      
 123 
     | 
    
         
            +
                @handle = nil
         
     | 
| 
      
 124 
     | 
    
         
            +
                @this_scope = AsyncScope.new() do
         
     | 
| 
      
 125 
     | 
    
         
            +
                  external_task do |t|
         
     | 
| 
      
 126 
     | 
    
         
            +
                    t.cancellation_handler {|h| h.fail}
         
     | 
| 
      
 127 
     | 
    
         
            +
                    t.initiate_task {|h| @handle = h }
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
                @this_scope.eventLoop
         
     | 
| 
      
 131 
     | 
    
         
            +
                @this_scope.is_complete?.should == false
         
     | 
| 
      
 132 
     | 
    
         
            +
                @handle.complete
         
     | 
| 
      
 133 
     | 
    
         
            +
                @this_scope.eventLoop
         
     | 
| 
      
 134 
     | 
    
         
            +
                @this_scope.is_complete?.should == true
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
            end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
            describe "external_task function" do
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              it "ensures that cancelling will raise the externalTasks cancellation handler" do
         
     | 
| 
      
 141 
     | 
    
         
            +
                trace = []
         
     | 
| 
      
 142 
     | 
    
         
            +
                this_scope = AsyncScope.new() do
         
     | 
| 
      
 143 
     | 
    
         
            +
                  trace << :async_start
         
     | 
| 
      
 144 
     | 
    
         
            +
                  external_task do |t|
         
     | 
| 
      
 145 
     | 
    
         
            +
                    t.cancellation_handler {|h, cause| trace << :cancellation_handler; h.complete }
         
     | 
| 
      
 146 
     | 
    
         
            +
                    t.initiate_task { |h|  trace << :external_task}
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
                  task { trace << :task; raise IllegalStateException }
         
     | 
| 
      
 149 
     | 
    
         
            +
                  trace << :async_done
         
     | 
| 
      
 150 
     | 
    
         
            +
                end
         
     | 
| 
      
 151 
     | 
    
         
            +
                expect { this_scope.eventLoop }.to raise_error IllegalStateException
         
     | 
| 
      
 152 
     | 
    
         
            +
                trace.should == [:async_start, :async_done, :external_task, :task, :cancellation_handler]
         
     | 
| 
      
 153 
     | 
    
         
            +
              end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
              it "tests explicit cancellation of BRE cancelling externalTask correctly" do
         
     | 
| 
      
 156 
     | 
    
         
            +
                handle_future = Future.new
         
     | 
| 
      
 157 
     | 
    
         
            +
                trace = []
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                scope = AsyncScope.new do
         
     | 
| 
      
 160 
     | 
    
         
            +
                  trace << :async_start
         
     | 
| 
      
 161 
     | 
    
         
            +
                  bre = error_handler do |t|
         
     | 
| 
      
 162 
     | 
    
         
            +
                    t.begin do
         
     | 
| 
      
 163 
     | 
    
         
            +
                      external_task do |t|
         
     | 
| 
      
 164 
     | 
    
         
            +
                        trace << :external_task;
         
     | 
| 
      
 165 
     | 
    
         
            +
                        t.cancellation_handler {|h, cause| trace << :cancellation_handler; h.complete}
         
     | 
| 
      
 166 
     | 
    
         
            +
                        t.initiate_task { |h|  handle_future.set(h) }
         
     | 
| 
      
 167 
     | 
    
         
            +
                      end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                      task do
         
     | 
| 
      
 170 
     | 
    
         
            +
                        trace << :in_the_cancel_task
         
     | 
| 
      
 171 
     | 
    
         
            +
                        handle_future.get
         
     | 
| 
      
 172 
     | 
    
         
            +
                        bre.cancel(nil)
         
     | 
| 
      
 173 
     | 
    
         
            +
                      end
         
     | 
| 
      
 174 
     | 
    
         
            +
                    end
         
     | 
| 
      
 175 
     | 
    
         
            +
                    t.rescue(Exception) { |e| e.class.should <= CancellationException }
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                  trace << :async_done
         
     | 
| 
      
 179 
     | 
    
         
            +
                end
         
     | 
| 
      
 180 
     | 
    
         
            +
                scope.eventLoop
         
     | 
| 
      
 181 
     | 
    
         
            +
                trace.should == [:async_start, :async_done, :external_task, :in_the_cancel_task, :cancellation_handler]
         
     | 
| 
      
 182 
     | 
    
         
            +
              end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
              it "ensures that having an external_task within an AsyncScope works" do
         
     | 
| 
      
 185 
     | 
    
         
            +
                trace = []
         
     | 
| 
      
 186 
     | 
    
         
            +
                this_scope = AsyncScope.new() do
         
     | 
| 
      
 187 
     | 
    
         
            +
                  external_task do |t|
         
     | 
| 
      
 188 
     | 
    
         
            +
                    t.cancellation_handler { |h| h.fail }
         
     | 
| 
      
 189 
     | 
    
         
            +
                    t.initiate_task { |h| trace << :task_started; h.complete; }
         
     | 
| 
      
 190 
     | 
    
         
            +
                  end
         
     | 
| 
      
 191 
     | 
    
         
            +
                end
         
     | 
| 
      
 192 
     | 
    
         
            +
                trace.should == []
         
     | 
| 
      
 193 
     | 
    
         
            +
                this_scope.eventLoop
         
     | 
| 
      
 194 
     | 
    
         
            +
                trace.should == [:task_started]
         
     | 
| 
      
 195 
     | 
    
         
            +
              end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
            end
         
     | 
    
        data/test/aws/factories.rb
    CHANGED
    
    | 
         @@ -13,7 +13,36 @@ 
     | 
|
| 
       13 
13 
     | 
    
         
             
            # permissions and limitations under the License.
         
     | 
| 
       14 
14 
     | 
    
         
             
            ##
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            require ' 
     | 
| 
      
 16 
     | 
    
         
            +
            require 'aws/flow'
         
     | 
| 
      
 17 
     | 
    
         
            +
            include AWS::Flow::Core
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            class FlowFactory
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              attr_accessor :async_scope
         
     | 
| 
      
 23 
     | 
    
         
            +
              def generate_BRE(options = {})
         
     | 
| 
      
 24 
     | 
    
         
            +
                scope = generate_scope
         
     | 
| 
      
 25 
     | 
    
         
            +
                bre = BeginRescueEnsure.new(:parent => scope.root_context)
         
     | 
| 
      
 26 
     | 
    
         
            +
                begin_statement = options[:begin] ? options[:begin] : lambda {}
         
     | 
| 
      
 27 
     | 
    
         
            +
                rescue_statement = options[:rescue] ? options[:rescue] : lambda {|x| }
         
     | 
| 
      
 28 
     | 
    
         
            +
                rescue_exception = options[:rescue_exceptions] ? options[:rescue_exceptions] : StandardError
         
     | 
| 
      
 29 
     | 
    
         
            +
                ensure_statement = options[:ensure] ? options[:ensure] : lambda {}
         
     | 
| 
      
 30 
     | 
    
         
            +
                bre.begin begin_statement
         
     | 
| 
      
 31 
     | 
    
         
            +
                bre.rescue(rescue_exception, rescue_statement)
         
     | 
| 
      
 32 
     | 
    
         
            +
                bre.ensure ensure_statement
         
     | 
| 
      
 33 
     | 
    
         
            +
                scope << bre
         
     | 
| 
      
 34 
     | 
    
         
            +
                bre
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def generate_scope(options = {})
         
     | 
| 
      
 38 
     | 
    
         
            +
                lambda = options[:lambda] || lambda {}
         
     | 
| 
      
 39 
     | 
    
         
            +
                @async_scope ||= AsyncScope.new do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  lambda.call
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
                return @async_scope
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
       17 
46 
     | 
    
         | 
| 
       18 
47 
     | 
    
         
             
            class WorkflowGenerator
         
     | 
| 
       19 
48 
     | 
    
         
             
              class << self
         
     |