예제 #1
0
 def setup_action_run(self):
     self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
     self.action_runner = mock.create_autospec(
         actioncommand.NoActionRunnerFactory)
     self.command = "do command %(actionname)s"
     self.rendered_command = "do command action_name"
     self.action_run = ActionRun("id",
                                 "action_name",
                                 mock.create_autospec(node.Node),
                                 self.command,
                                 output_path=self.output_path,
                                 action_runner=self.action_runner)
예제 #2
0
 def test_from_state_with_node_exists(self, mock_store):
     ActionRun.from_state(
         self.state_data,
         self.parent_context,
         self.output_path,
         self.run_node,
         lambda: None,
     )
     mock_store.get_instance().get_node.assert_called_with(
         self.state_data['node_name'],
         self.run_node,
     )
예제 #3
0
 def test_from_state_running(self):
     self.state_data['state'] = 'running'
     action_run = ActionRun.from_state(self.state_data,
             self.parent_context, self.output_path, self.run_node)
     assert action_run.is_unknown
     assert_equal(action_run.exit_status, 0)
     assert_equal(action_run.end_time, self.now)
예제 #4
0
 def _build_run(self, name):
     mock_node = mock.create_autospec(node.Node)
     return ActionRun("id",
                      name,
                      mock_node,
                      self.command,
                      output_path=self.output_path)
예제 #5
0
 def test_from_state_after_rendered_command(self):
     self.state_data['command'] = 'do things theaction'
     self.state_data['rendered_command'] = self.state_data['command']
     action_run = ActionRun.from_state(self.state_data, self.parent_context,
                                       self.output_path, self.run_node)
     assert_equal(action_run.bare_command, self.state_data['command'])
     assert_equal(action_run.rendered_command, self.state_data['command'])
예제 #6
0
 def test_from_state_before_rendered_command(self):
     self.state_data['command'] = 'do things %(actionname)s'
     self.state_data['rendered_command'] = None
     action_run = ActionRun.from_state(self.state_data, self.parent_context,
                                       self.output_path, self.run_node)
     assert_equal(action_run.bare_command, self.state_data['command'])
     assert not action_run.rendered_command
예제 #7
0
 def test_from_state_running(self):
     self.state_data['state'] = 'running'
     action_run = ActionRun.from_state(self.state_data, self.parent_context,
                                       self.output_path, self.run_node)
     assert action_run.is_unknown
     assert_equal(action_run.exit_status, 0)
     assert_equal(action_run.end_time, self.now)
예제 #8
0
 def test_from_state_before_rendered_command(self):
     self.state_data['command'] = 'do things %(actionname)s'
     self.state_data['rendered_command'] = None
     action_run = ActionRun.from_state(self.state_data,
             self.parent_context, self.output_path, self.run_node)
     assert_equal(action_run.bare_command, self.state_data['command'])
     assert not action_run.rendered_command
예제 #9
0
 def test_from_state_after_rendered_command(self):
     self.state_data['command'] = 'do things theaction'
     self.state_data['rendered_command'] = self.state_data['command']
     action_run = ActionRun.from_state(self.state_data,
             self.parent_context, self.output_path, self.run_node)
     assert_equal(action_run.bare_command, self.state_data['command'])
     assert_equal(action_run.rendered_command, self.state_data['command'])
예제 #10
0
 def setup_action_run(self):
     self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
     self.action_runner = actioncommand.NoActionRunnerFactory()
     self.command = "do command {actionname}"
     self.rendered_command = "do command action_name"
     self.action_run = ActionRun(
         job_run_id="ns.id.0",
         name="action_name",
         node=mock.create_autospec(node.Node),
         bare_command=self.command,
         output_path=self.output_path,
         action_runner=self.action_runner,
     )
     # These should be implemented in subclasses, we don't care here
     self.action_run.submit_command = mock.Mock()
     self.action_run.stop = mock.Mock()
     self.action_run.kill = mock.Mock()
예제 #11
0
    def test_from_state_with_node_exists(self):
        anode = turtle.Turtle(name="anode", hostname="box")
        node_store = node.NodePoolStore.get_instance()
        node_store.put(anode)

        action_run = ActionRun.from_state(self.state_data,
                self.parent_context, self.output_path, self.run_node)

        assert_equal(action_run.node, anode)
        node_store.clear()
예제 #12
0
 def test_from_state_queued(self):
     self.state_data['state'] = 'queued'
     action_run = ActionRun.from_state(
         self.state_data,
         self.parent_context,
         self.output_path,
         self.run_node,
         lambda: None,
     )
     assert action_run.is_queued
예제 #13
0
 def test_from_state_no_node_name(self):
     del self.state_data['node_name']
     action_run = ActionRun.from_state(
         self.state_data,
         self.parent_context,
         self.output_path,
         self.run_node,
         lambda: None,
     )
     assert_equal(action_run.node, self.run_node)
예제 #14
0
 def setup_action_run(self):
     anode = turtle.Turtle()
     self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
     self.command = "do command %(actionname)s"
     self.rendered_command = "do command action_name"
     self.action_run = ActionRun(
             "id",
             "action_name",
             anode,
             self.command,
             output_path=self.output_path)
예제 #15
0
 def test_from_state_old_state(self):
     self.state_data['command'] = 'do things {actionname}'
     action_run = ActionRun.from_state(
         self.state_data,
         self.parent_context,
         self.output_path,
         self.run_node,
         lambda: None,
     )
     assert_equal(action_run.bare_command, self.state_data['command'])
     assert not action_run.rendered_command
예제 #16
0
    def test_from_state(self):
        state_data = self.state_data
        action_run = ActionRun.from_state(state_data, self.parent_context,
                list(self.output_path), self.run_node)

        for key, value in self.state_data.iteritems():
            if key in ['state', 'node_name']:
                continue
            assert_equal(getattr(action_run, key), value)

        assert action_run.is_succeeded
        assert not action_run.is_cleanup
        assert_equal(action_run.output_path[:2], self.output_path)
예제 #17
0
 def setup_action_run(self):
     self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
     self.action_runner = mock.create_autospec(
         actioncommand.NoActionRunnerFactory)
     self.command = "do command %(actionname)s"
     self.rendered_command = "do command action_name"
     self.action_run = ActionRun(
             "id",
             "action_name",
             mock.create_autospec(node.Node),
             self.command,
             output_path=self.output_path,
             action_runner=self.action_runner)
예제 #18
0
    def test_from_state(self):
        state_data = self.state_data
        action_run = ActionRun.from_state(state_data, self.parent_context,
                                          list(self.output_path),
                                          self.run_node)

        for key, value in self.state_data.iteritems():
            if key in ['state', 'node_name']:
                continue
            assert_equal(getattr(action_run, key), value)

        assert action_run.is_succeeded
        assert not action_run.is_cleanup
        assert_equal(action_run.output_path[:2], self.output_path)
예제 #19
0
 def test_from_state_with_node_exists(self, mock_store):
     ActionRun.from_state(self.state_data,
             self.parent_context, self.output_path, self.run_node)
     mock_store.get_instance().get_node.assert_called_with(
         self.state_data['node_name'], self.run_node)
예제 #20
0
 def test_from_state_no_node_name(self):
     del self.state_data['node_name']
     action_run = ActionRun.from_state(self.state_data,
             self.parent_context, self.output_path, self.run_node)
     assert_equal(action_run.node, self.run_node)
예제 #21
0
 def test_from_state_queued(self):
     self.state_data['state'] = 'queued'
     action_run = ActionRun.from_state(self.state_data, self.parent_context,
             self.output_path, self.run_node)
     assert action_run.is_queued
예제 #22
0
 def test_from_state_queued(self):
     self.state_data['state'] = 'queued'
     action_run = ActionRun.from_state(self.state_data, self.parent_context,
             self.output_path, self.run_node)
     assert action_run.is_failed
     assert_equal(action_run.end_time, self.now)
예제 #23
0
class ActionRunTestCase(TestCase):

    @setup
    def setup_action_run(self):
        self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
        self.action_runner = mock.create_autospec(
            actioncommand.NoActionRunnerFactory)
        self.command = "do command %(actionname)s"
        self.rendered_command = "do command action_name"
        self.action_run = ActionRun(
                "id",
                "action_name",
                mock.create_autospec(node.Node),
                self.command,
                output_path=self.output_path,
                action_runner=self.action_runner)

    @teardown
    def teardown_action_run(self):
        shutil.rmtree(self.output_path.base, ignore_errors=True)

    def test_init_state(self):
        assert_equal(self.action_run.state, ActionRun.STATE_SCHEDULED)

    def test_start(self):
        self.action_run.machine.transition('ready')
        assert self.action_run.start()
        assert self.action_run.is_starting
        assert self.action_run.start_time

    def test_start_bad_state(self):
        self.action_run.fail()
        assert not self.action_run.start()

    def test_start_invalid_command(self):
        self.action_run.bare_command = "%(notfound)s"
        self.action_run.machine.transition('ready')
        assert not self.action_run.start()
        assert self.action_run.is_failed
        assert_equal(self.action_run.exit_status, -1)

    def test_start_node_error(self):
        def raise_error(c):
            raise node.Error("The error")
        self.action_run.node = turtle.Turtle(submit_command=raise_error)
        self.action_run.machine.transition('ready')
        assert not self.action_run.start()
        assert_equal(self.action_run.exit_status, -2)
        assert self.action_run.is_failed

    @mock.patch('tron.core.actionrun.filehandler', autospec=True)
    def test_build_action_command(self, mock_filehandler):
        autospec_method(self.action_run.watch)
        serializer = mock_filehandler.OutputStreamSerializer.return_value
        action_command = self.action_run.build_action_command()
        assert_equal(action_command, self.action_run.action_command)
        assert_equal(action_command, self.action_runner.create.return_value)
        self.action_runner.create.assert_called_with(
            self.action_run.id, self.action_run.command, serializer)
        mock_filehandler.OutputStreamSerializer.assert_called_with(
            self.action_run.output_path)
        self.action_run.watch.assert_called_with(action_command)

    def test_handler_running(self):
        self.action_run.build_action_command()
        self.action_run.machine.transition('start')
        assert self.action_run.handler(
                self.action_run.action_command, ActionCommand.RUNNING)
        assert self.action_run.is_running

    def test_handler_failstart(self):
        self.action_run.build_action_command()
        assert self.action_run.handler(
                self.action_run.action_command, ActionCommand.FAILSTART)
        assert self.action_run.is_failed

    def test_handler_exiting_fail(self):
        self.action_run.build_action_command()
        self.action_run.action_command.exit_status = -1
        self.action_run.machine.transition('start')
        assert self.action_run.handler(
            self.action_run.action_command, ActionCommand.EXITING)
        assert self.action_run.is_failed
        assert_equal(self.action_run.exit_status, -1)

    def test_handler_exiting_success(self):
        self.action_run.build_action_command()
        self.action_run.action_command.exit_status = 0
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        assert self.action_run.handler(
            self.action_run.action_command, ActionCommand.EXITING)
        assert self.action_run.is_succeeded
        assert_equal(self.action_run.exit_status, 0)

    def test_handler_exiting_failunknown(self):
        self.action_run.action_command = mock.create_autospec(
            actioncommand.ActionCommand, exit_status=None)
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        assert self.action_run.handler(
            self.action_run.action_command, ActionCommand.EXITING)
        assert self.action_run.is_unknown
        assert_equal(self.action_run.exit_status, None)

    def test_handler_unhandled(self):
        self.action_run.build_action_command()
        assert self.action_run.handler(
            self.action_run.action_command, ActionCommand.PENDING) is None
        assert self.action_run.is_scheduled

    def test_success(self):
        assert self.action_run.ready()
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')

        assert self.action_run.is_running
        assert self.action_run.success()
        assert not self.action_run.is_running
        assert self.action_run.is_done
        assert self.action_run.end_time
        assert_equal(self.action_run.exit_status, 0)

    def test_success_bad_state(self):
        self.action_run.cancel()
        assert not self.action_run.success()

    def test_failure(self):
        self.action_run.fail(1)
        assert not self.action_run.is_running
        assert self.action_run.is_done
        assert self.action_run.end_time
        assert_equal(self.action_run.exit_status, 1)

    def test_failure_bad_state(self):
        self.action_run.fail(444)
        assert not self.action_run.fail(123)
        assert_equal(self.action_run.exit_status, 444)

    def test_skip(self):
        assert not self.action_run.is_running
        self.action_run.ready()
        assert self.action_run.start()

        assert self.action_run.fail(-1)
        assert self.action_run.skip()
        assert self.action_run.is_skipped

    def test_skip_bad_state(self):
        assert not self.action_run.skip()

    def test_state_data(self):
        state_data = self.action_run.state_data
        assert_equal(state_data['command'], self.action_run.bare_command)
        assert not self.action_run.rendered_command
        assert not state_data['rendered_command']

    def test_state_data_after_rendered(self):
        command = self.action_run.command
        state_data = self.action_run.state_data
        assert_equal(state_data['command'], command)
        assert_equal(state_data['rendered_command'], command)

    def test_render_command(self):
        self.action_run.context = {'stars': 'bright'}
        self.action_run.bare_command = "%(stars)s"
        assert_equal(self.action_run.render_command(), 'bright')

    def test_command_not_yet_rendered(self):
        assert_equal(self.action_run.command, self.rendered_command)

    def test_command_already_rendered(self):
        assert self.action_run.command
        self.action_run.bare_command = "new command"
        assert_equal(self.action_run.command, self.rendered_command)

    def test_command_failed_render(self):
        self.action_run.bare_command = "%(this_is_missing)s"
        assert_equal(self.action_run.command, ActionRun.FAILED_RENDER)

    def test_is_complete(self):
        self.action_run.machine.state = ActionRun.STATE_SUCCEEDED
        assert self.action_run.is_complete
        self.action_run.machine.state = ActionRun.STATE_SKIPPED
        assert self.action_run.is_complete
        self.action_run.machine.state = ActionRun.STATE_RUNNING
        assert not self.action_run.is_complete

    def test_is_broken(self):
        self.action_run.machine.state = ActionRun.STATE_UNKNOWN
        assert self.action_run.is_broken
        self.action_run.machine.state = ActionRun.STATE_FAILED
        assert self.action_run.is_broken
        self.action_run.machine.state = ActionRun.STATE_QUEUED
        assert not self.action_run.is_broken

    def test__getattr__(self):
        assert not self.action_run.is_succeeded
        assert not self.action_run.is_failed
        assert not self.action_run.is_queued
        assert self.action_run.is_scheduled
        assert self.action_run.cancel()
        assert self.action_run.is_cancelled

    def test__getattr__missing_attribute(self):
        assert_raises(AttributeError,
            self.action_run.__getattr__, 'is_not_a_real_state')
예제 #24
0
class TestActionRun(TestCase):
    @setup
    def setup_action_run(self):
        self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
        self.action_runner = actioncommand.NoActionRunnerFactory()
        self.command = "do command {actionname}"
        self.rendered_command = "do command action_name"
        self.action_run = ActionRun(
            job_run_id="ns.id.0",
            name="action_name",
            node=mock.create_autospec(node.Node),
            bare_command=self.command,
            output_path=self.output_path,
            action_runner=self.action_runner,
        )
        # These should be implemented in subclasses, we don't care here
        self.action_run.submit_command = mock.Mock()
        self.action_run.stop = mock.Mock()
        self.action_run.kill = mock.Mock()

    def test_init_state(self):
        assert_equal(self.action_run.state, ActionRun.SCHEDULED)

    def test_start(self):
        self.action_run.machine.transition('ready')
        assert self.action_run.start()
        assert_equal(self.action_run.submit_command.call_count, 1)
        assert self.action_run.is_starting
        assert self.action_run.start_time

    def test_start_bad_state(self):
        self.action_run.fail()
        assert not self.action_run.start()

    @mock.patch('tron.core.actionrun.log', autospec=True)
    def test_start_invalid_command(self, _log):
        self.action_run.bare_command = "{notfound}"
        self.action_run.machine.transition('ready')
        assert not self.action_run.start()
        assert self.action_run.is_failed
        assert_equal(self.action_run.exit_status, -1)

    def test_success(self):
        assert self.action_run.ready()
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')

        assert self.action_run.is_running
        assert self.action_run.success()
        assert not self.action_run.is_running
        assert self.action_run.is_done
        assert self.action_run.end_time
        assert_equal(self.action_run.exit_status, 0)

    def test_success_emits_not(self):
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        self.action_run.trigger_downstreams = None
        self.action_run.emit_triggers = mock.Mock()
        assert self.action_run.success()
        assert self.action_run.emit_triggers.call_count == 0

    def test_success_emits_on_true(self):
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        self.action_run.trigger_downstreams = True
        self.action_run.emit_triggers = mock.Mock()
        assert self.action_run.success()
        assert self.action_run.emit_triggers.call_count == 1

    def test_success_emits_on_dict(self):
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        self.action_run.trigger_downstreams = dict(foo="bar")
        self.action_run.emit_triggers = mock.Mock()
        assert self.action_run.success()
        assert self.action_run.emit_triggers.call_count == 1

    @mock.patch('tron.core.actionrun.EventBus')
    def test_emit_triggers(self, eventbus):
        self.action_run.context = {'shortdate': 'foo'}

        self.action_run.trigger_downstreams = True
        self.action_run.emit_triggers()

        self.action_run.trigger_downstreams = dict(foo="bar")
        self.action_run.emit_triggers()

        assert eventbus.publish.mock_calls == [
            mock.call(f"ns.id.action_name.shortdate.foo"),
            mock.call(f"ns.id.action_name.foo.bar"),
        ]

    def test_success_bad_state(self):
        self.action_run.cancel()
        assert not self.action_run.success()

    def test_failure(self):
        self.action_run._exit_unsuccessful(1)
        assert not self.action_run.is_running
        assert self.action_run.is_done
        assert self.action_run.end_time
        assert_equal(self.action_run.exit_status, 1)

    def test_failure_bad_state(self):
        self.action_run.fail(444)
        assert not self.action_run.fail(123)
        assert_equal(self.action_run.exit_status, 444)

    def test_skip(self):
        assert not self.action_run.is_running
        self.action_run.ready()
        assert self.action_run.start()

        assert self.action_run.fail(-1)
        assert self.action_run.skip()
        assert self.action_run.is_skipped

    def test_skip_bad_state(self):
        assert not self.action_run.skip()

    def test_state_data(self):
        state_data = self.action_run.state_data
        assert_equal(state_data['command'], self.action_run.bare_command)
        assert not self.action_run.rendered_command
        assert not state_data['rendered_command']

    def test_state_data_after_rendered(self):
        command = self.action_run.command
        state_data = self.action_run.state_data
        assert_equal(state_data['command'], command)
        assert_equal(state_data['rendered_command'], command)

    def test_render_command(self):
        self.action_run.context = {'stars': 'bright'}
        self.action_run.bare_command = "{stars}"
        assert_equal(self.action_run.render_command(), 'bright')

    def test_command_not_yet_rendered(self):
        assert_equal(self.action_run.command, self.rendered_command)

    def test_command_already_rendered(self):
        assert self.action_run.command
        self.action_run.bare_command = "new command"
        assert_equal(self.action_run.command, self.rendered_command)

    @mock.patch('tron.core.actionrun.log', autospec=True)
    def test_command_failed_render(self, _log):
        self.action_run.bare_command = "{this_is_missing}"
        assert_equal(self.action_run.command, ActionRun.FAILED_RENDER)

    def test_is_complete(self):
        self.action_run.machine.state = ActionRun.SUCCEEDED
        assert self.action_run.is_complete
        self.action_run.machine.state = ActionRun.SKIPPED
        assert self.action_run.is_complete
        self.action_run.machine.state = ActionRun.RUNNING
        assert not self.action_run.is_complete

    def test_is_broken(self):
        self.action_run.machine.state = ActionRun.UNKNOWN
        assert self.action_run.is_broken
        self.action_run.machine.state = ActionRun.FAILED
        assert self.action_run.is_broken
        self.action_run.machine.state = ActionRun.QUEUED
        assert not self.action_run.is_broken

    def test__getattr__(self):
        assert not self.action_run.is_succeeded
        assert not self.action_run.is_failed
        assert not self.action_run.is_queued
        assert self.action_run.is_scheduled
        assert self.action_run.cancel()
        assert self.action_run.is_cancelled

    def test__getattr__missing_attribute(self):
        assert_raises(
            AttributeError,
            self.action_run.__getattr__,
            'is_not_a_real_state',
        )
예제 #25
0
class ActionRunTestCase(TestCase):
    @setup
    def setup_action_run(self):
        self.output_path = filehandler.OutputPath(tempfile.mkdtemp())
        self.action_runner = mock.create_autospec(
            actioncommand.NoActionRunnerFactory)
        self.command = "do command %(actionname)s"
        self.rendered_command = "do command action_name"
        self.action_run = ActionRun("id",
                                    "action_name",
                                    mock.create_autospec(node.Node),
                                    self.command,
                                    output_path=self.output_path,
                                    action_runner=self.action_runner)

    @teardown
    def teardown_action_run(self):
        shutil.rmtree(self.output_path.base, ignore_errors=True)

    def test_init_state(self):
        assert_equal(self.action_run.state, ActionRun.STATE_SCHEDULED)

    def test_start(self):
        self.action_run.machine.transition('ready')
        assert self.action_run.start()
        assert self.action_run.is_starting
        assert self.action_run.start_time

    def test_start_bad_state(self):
        self.action_run.fail()
        assert not self.action_run.start()

    def test_start_invalid_command(self):
        self.action_run.bare_command = "%(notfound)s"
        self.action_run.machine.transition('ready')
        assert not self.action_run.start()
        assert self.action_run.is_failed
        assert_equal(self.action_run.exit_status, -1)

    def test_start_node_error(self):
        def raise_error(c):
            raise node.Error("The error")

        self.action_run.node = turtle.Turtle(submit_command=raise_error)
        self.action_run.machine.transition('ready')
        assert not self.action_run.start()
        assert_equal(self.action_run.exit_status, -2)
        assert self.action_run.is_failed

    @mock.patch('tron.core.actionrun.filehandler', autospec=True)
    def test_build_action_command(self, mock_filehandler):
        autospec_method(self.action_run.watch)
        serializer = mock_filehandler.OutputStreamSerializer.return_value
        action_command = self.action_run.build_action_command()
        assert_equal(action_command, self.action_run.action_command)
        assert_equal(action_command, self.action_runner.create.return_value)
        self.action_runner.create.assert_called_with(self.action_run.id,
                                                     self.action_run.command,
                                                     serializer)
        mock_filehandler.OutputStreamSerializer.assert_called_with(
            self.action_run.output_path)
        self.action_run.watch.assert_called_with(action_command)

    def test_handler_running(self):
        self.action_run.build_action_command()
        self.action_run.machine.transition('start')
        assert self.action_run.handler(self.action_run.action_command,
                                       ActionCommand.RUNNING)
        assert self.action_run.is_running

    def test_handler_failstart(self):
        self.action_run.build_action_command()
        assert self.action_run.handler(self.action_run.action_command,
                                       ActionCommand.FAILSTART)
        assert self.action_run.is_failed

    def test_handler_exiting_fail(self):
        self.action_run.build_action_command()
        self.action_run.action_command.exit_status = -1
        self.action_run.machine.transition('start')
        assert self.action_run.handler(self.action_run.action_command,
                                       ActionCommand.EXITING)
        assert self.action_run.is_failed
        assert_equal(self.action_run.exit_status, -1)

    def test_handler_exiting_success(self):
        self.action_run.build_action_command()
        self.action_run.action_command.exit_status = 0
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        assert self.action_run.handler(self.action_run.action_command,
                                       ActionCommand.EXITING)
        assert self.action_run.is_succeeded
        assert_equal(self.action_run.exit_status, 0)

    def test_handler_exiting_failunknown(self):
        self.action_run.action_command = mock.create_autospec(
            actioncommand.ActionCommand, exit_status=None)
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')
        assert self.action_run.handler(self.action_run.action_command,
                                       ActionCommand.EXITING)
        assert self.action_run.is_unknown
        assert_equal(self.action_run.exit_status, None)

    def test_handler_unhandled(self):
        self.action_run.build_action_command()
        assert self.action_run.handler(self.action_run.action_command,
                                       ActionCommand.PENDING) is None
        assert self.action_run.is_scheduled

    def test_success(self):
        assert self.action_run.ready()
        self.action_run.machine.transition('start')
        self.action_run.machine.transition('started')

        assert self.action_run.is_running
        assert self.action_run.success()
        assert not self.action_run.is_running
        assert self.action_run.is_done
        assert self.action_run.end_time
        assert_equal(self.action_run.exit_status, 0)

    def test_success_bad_state(self):
        self.action_run.cancel()
        assert not self.action_run.success()

    def test_failure(self):
        self.action_run.fail(1)
        assert not self.action_run.is_running
        assert self.action_run.is_done
        assert self.action_run.end_time
        assert_equal(self.action_run.exit_status, 1)

    def test_failure_bad_state(self):
        self.action_run.fail(444)
        assert not self.action_run.fail(123)
        assert_equal(self.action_run.exit_status, 444)

    def test_skip(self):
        assert not self.action_run.is_running
        self.action_run.ready()
        assert self.action_run.start()

        assert self.action_run.fail(-1)
        assert self.action_run.skip()
        assert self.action_run.is_skipped

    def test_skip_bad_state(self):
        assert not self.action_run.skip()

    def test_state_data(self):
        state_data = self.action_run.state_data
        assert_equal(state_data['command'], self.action_run.bare_command)
        assert not self.action_run.rendered_command
        assert not state_data['rendered_command']

    def test_state_data_after_rendered(self):
        command = self.action_run.command
        state_data = self.action_run.state_data
        assert_equal(state_data['command'], command)
        assert_equal(state_data['rendered_command'], command)

    def test_render_command(self):
        self.action_run.context = {'stars': 'bright'}
        self.action_run.bare_command = "%(stars)s"
        assert_equal(self.action_run.render_command(), 'bright')

    def test_command_not_yet_rendered(self):
        assert_equal(self.action_run.command, self.rendered_command)

    def test_command_already_rendered(self):
        assert self.action_run.command
        self.action_run.bare_command = "new command"
        assert_equal(self.action_run.command, self.rendered_command)

    def test_command_failed_render(self):
        self.action_run.bare_command = "%(this_is_missing)s"
        assert_equal(self.action_run.command, ActionRun.FAILED_RENDER)

    def test_is_complete(self):
        self.action_run.machine.state = ActionRun.STATE_SUCCEEDED
        assert self.action_run.is_complete
        self.action_run.machine.state = ActionRun.STATE_SKIPPED
        assert self.action_run.is_complete
        self.action_run.machine.state = ActionRun.STATE_RUNNING
        assert not self.action_run.is_complete

    def test_is_broken(self):
        self.action_run.machine.state = ActionRun.STATE_UNKNOWN
        assert self.action_run.is_broken
        self.action_run.machine.state = ActionRun.STATE_FAILED
        assert self.action_run.is_broken
        self.action_run.machine.state = ActionRun.STATE_QUEUED
        assert not self.action_run.is_broken

    def test__getattr__(self):
        assert not self.action_run.is_succeeded
        assert not self.action_run.is_failed
        assert not self.action_run.is_queued
        assert self.action_run.is_scheduled
        assert self.action_run.cancel()
        assert self.action_run.is_cancelled

    def test__getattr__missing_attribute(self):
        assert_raises(AttributeError, self.action_run.__getattr__,
                      'is_not_a_real_state')