Exemplo n.º 1
0
class TestCommandManagerChainedCalls(TestCommandManagerCallsBase):
    def setUp(self):
        super().setUp()
        self.true_cb = Callback()
        self.false_cb = Callback()

        def true_run(self_, *args, **kwargs):
            assert args is ()
            self.true_cb(**kwargs)

        def false_run(self_, *args, **kwargs):
            assert args is ()
            self.false_cb(**kwargs)
            raise CmdError()

        args = ({'names': ('-a',), 'action': 'store_true', 'description': 'A args'},
                {'names': ('-b',), 'action': 'store_true', 'description': 'B args'},
                {'names': ('-c',), 'action': 'store_true', 'description': 'C args'})
        true_cmd = make_cmdcls(name='true', run=true_run, argspecs=args, provides=('T',))
        false_cmd = make_cmdcls(name='false', run=false_run, argspecs=args, provides=('F',))
        self.cmdmgr.register(true_cmd)
        self.cmdmgr.register(false_cmd)


    async def assert_success(self, cmdchain):
        success = self.cmdmgr.run_sync(cmdchain)
        self.assertEqual(success, True)
        success = await self.cmdmgr.run_async(cmdchain)
        self.assertEqual(success, True)

    async def assert_failure(self, cmdchain, infos=(), errors=()):
        success = self.cmdmgr.run_sync(cmdchain)
        self.assertEqual(success, False)
        if infos: self.assertEqual(self.info_handler.args, list(infos))
        if errors: self.assertEqual(self.error_handler.args, list(errors))
        self.info_handler.reset()
        self.error_handler.reset()
        success = await self.cmdmgr.run_async(cmdchain)
        self.assertEqual(success, False)
        if infos: self.assertEqual(self.info_handler.args, list(infos))
        if errors: self.assertEqual(self.error_handler.args, list(errors))

    async def test_cmdchain_formats(self):
        await self.assert_success('true ; true')
        await self.assert_failure('true ; false')
        await self.assert_success([['true'], ';', ['true']])
        await self.assert_failure([['true'], ';', ['false']])

    async def test_empty_cmdchain(self):
        await self.assert_success('')
        await self.assert_success([])
        await self.assert_success([[], []])

    async def test_valid_cmdchain(self):
        await self.assert_success([['false'], ';', ['true']])
        await self.assert_success([['false'], ['true']])
        await self.assert_success((['true'], '&', ['true']))
        await self.assert_success([['false'], '|', ('true',)])

        await self.assert_failure([('true',), ';', ['false']])
        await self.assert_failure([('true',), ('false',)])
        await self.assert_failure((('true',), '&', ('false',)))
        await self.assert_failure([['false'], '|', ['false']])

    async def test_trailing_operator(self):
        await self.assert_failure('true ; false &')
        await self.assert_success([('false',), ';', ('true',), '|'])


    async def run_testcases(self, testcases, checkfunc):
        # Run each test in list format and in string format
        for cmdchain, kwargs in testcases:
            await checkfunc(cmdchain, **kwargs)
            cmdchain_str = ' '.join(cmd if isinstance(cmd, str) else ' '.join(cmd)
                                    for cmd in cmdchain)
            await checkfunc(cmdchain_str, **kwargs)

    async def test_consecutive_operators(self):
        async def assert_consecutive_ops(cmdchain, op1, op2):
            self.error_handler.reset()
            self.cmdmgr.run_sync(cmdchain)
            self.assertEqual(self.error_handler.args,
                             [('Consecutive operators: %s %s' % (op1, op2),)])
            self.error_handler.reset()
            await self.cmdmgr.run_async(cmdchain)
            self.assertEqual(self.error_handler.args,
                             [('Consecutive operators: %s %s' % (op1, op2),)])

        testcases = (
            ([['true'], ';', ';', ['false']], {'op1': ';', 'op2': ';'}),
            ([['true'], '&', '&', ['false']], {'op1': '&', 'op2': '&'}),
            ([['false'], '|', '|', ['true']], {'op1': '|', 'op2': '|'}),

            ([['true'], ';', '&', ['false']], {'op1': ';', 'op2': '&'}),
            ([['true'], '&', '|', ['false']], {'op1': '&', 'op2': '|'}),
            ([['false'], '|', ';', ['true']], {'op1': '|', 'op2': ';'}),
            ([['false'], ';', '|', ['false']], {'op1': ';', 'op2': '|'}),
        )
        await self.run_testcases(testcases, assert_consecutive_ops)

    async def test_final_process_determines_overall_success(self):
        async def do_test(cmdchain, success):
            result = self.cmdmgr.run_sync(cmdchain)
            self.assertEqual(result, success)
            result = await self.cmdmgr.run_async(cmdchain)
            self.assertEqual(result, success)

        testcases = (
            ([['true'], ';', ['true']], {'success': True}),
            ([['false'], ';', ['true']], {'success': True}),
            ([['true'], ';', ['false']], {'success': False}),
            ([['false'], ';', ['false']], {'success': False}),

            ([['true'], '&', ['true']], {'success': True}),
            ([['false'], '&', ['true']], {'success': False}),
            ([['true'], '&', ['false']], {'success': False}),
            ([['false'], '&', ['false']], {'success': False}),

            ([['true'], '|', ['true']], {'success': True}),
            ([['false'], '|', ['true']], {'success': True}),
            ([['true'], '|', ['false']], {'success': True}),
            ([['false'], '|', ['false']], {'success': False}),
        )
        await self.run_testcases(testcases, do_test)

    async def test_nonexisting_cmd_in_cmdchain(self):
        async def do_test(cmdchain, success, true_calls=0, false_calls=0, errors=()):
            self.error_handler.reset() ; self.true_cb.reset() ; self.false_cb.reset()
            result = self.cmdmgr.run_sync(cmdchain)
            self.assertEqual(result, success)
            self.assertEqual(self.true_cb.calls, true_calls)
            self.assertEqual(self.false_cb.calls, false_calls)
            self.assertEqual(self.error_handler.args, list(errors))

            self.error_handler.reset() ; self.true_cb.reset() ; self.false_cb.reset()
            result = await self.cmdmgr.run_async(cmdchain)
            self.assertEqual(result, success)
            self.assertEqual(self.true_cb.calls, true_calls)
            self.assertEqual(self.false_cb.calls, false_calls)
            self.assertEqual(self.error_handler.args, list(errors))

        testcases = (
            ([['true'], ';', ['foo'], ';', ['false']],
             {'success': False, 'true_calls': 1, 'false_calls': 1, 'errors': [('foo: Unknown command',)]}),
            ([['true'], '|', ['foo'], '|', ['false']],
             {'success': True, 'true_calls': 1, 'false_calls': 0}),
            ([['true'], '&', ['foo'], '&', ['false']],
             {'success': False, 'true_calls': 1, 'false_calls': 0, 'errors': [('foo: Unknown command',)]}),
            ([['true'], '&', ['foo'], '|', ['false']],
             {'success': False, 'true_calls': 1, 'false_calls': 1, 'errors': [('foo: Unknown command',)]}),
            ([['true'], '&', ['foo'], '|', ['true']],
             {'success': True, 'true_calls': 2, 'false_calls': 0, 'errors': [('foo: Unknown command',)]}),
            ([['false'], '|', ['foo'], '&', ['true']],
             {'success': False, 'true_calls': 0, 'false_calls': 1, 'errors': [('foo: Unknown command',)]}),
            ([['false'], '&', ['foo'], '&', ['true']],
             {'success': False, 'true_calls': 0, 'false_calls': 1}),
            ([['false'], '|', ['foo'], '|', ['true']],
             {'success': True, 'true_calls': 1, 'false_calls': 1, 'errors': [('foo: Unknown command',)]}),
        )
        await self.run_testcases(testcases, do_test)

    async def test_cmd_from_inactive_interface_in_cmdchain(self):
        async def do_test(cmdchain, success, true_calls=0, false_calls=0, errors=()):
            self.error_handler.reset() ; self.true_cb.reset() ; self.false_cb.reset()
            result = self.cmdmgr.run_sync(cmdchain)
            self.assertEqual(result, success)
            self.assertEqual(self.true_cb.calls, true_calls)
            self.assertEqual(self.false_cb.calls, false_calls)
            self.assertEqual(self.error_handler.args, list(errors))

            self.error_handler.reset() ; self.true_cb.reset() ; self.false_cb.reset()
            result = await self.cmdmgr.run_async(cmdchain)
            self.assertEqual(result, success)
            self.assertEqual(self.true_cb.calls, true_calls)
            self.assertEqual(self.false_cb.calls, false_calls)
            self.assertEqual(self.error_handler.args, list(errors))

        # Calls to 'false' are ignored and evaluate to True in the command chain
        self.cmdmgr.active_interface = 'T'
        testcases = (
            [[['true'], ';', ['false']], {'success': True, 'true_calls': 1, 'false_calls': 0}],
            [[['false'], ';', ['true']], {'success': True, 'true_calls': 1, 'false_calls': 0}],
            [[['true'], '&', ['false']], {'success': True, 'true_calls': 1, 'false_calls': 0}],
            [[['false'], '&', ['true']], {'success': True, 'true_calls': 1, 'false_calls': 0}],
            [[['true'], '|', ['false']], {'success': True, 'true_calls': 1, 'false_calls': 0}],
            [[['false'], '|', ['true']], {'success': True, 'true_calls': 0, 'false_calls': 0}],
        )
        await self.run_testcases(testcases, do_test)

        # Calls to 'false' are ignored and evaluate to True in the command chain
        self.cmdmgr.active_interface = 'F'
        testcases = (
            [[['true'], ';', ['false']], {'success': False, 'true_calls': 0, 'false_calls': 1}],
            [[['false'], ';', ['true']], {'success': True, 'true_calls': 0, 'false_calls': 1}],
            [[['true'], '&', ['false']], {'success': False, 'true_calls': 0, 'false_calls': 1}],
            [[['false'], '&', ['true']], {'success': False, 'true_calls': 0, 'false_calls': 1}],
            [[['true'], '|', ['false']], {'success': True, 'true_calls': 0, 'false_calls': 0}],
            [[['false'], '|', ['true']], {'success': True, 'true_calls': 0, 'false_calls': 1}],
        )
        await self.run_testcases(testcases, do_test)


    async def test_cmdchain_with_arguments(self):
        async def do_test(cmdchain, success, true_args=(), false_args=(), errors=()):
            self.error_handler.reset() ; self.true_cb.reset() ; self.false_cb.reset()
            result = self.cmdmgr.run_sync(cmdchain)
            self.assertEqual(result, success)
            self.assertEqual(self.true_cb.kwargs, list(true_args))
            self.assertEqual(self.false_cb.kwargs, list(false_args))
            self.assertEqual(self.error_handler.args, list(errors))

            self.error_handler.reset() ; self.true_cb.reset() ; self.false_cb.reset()
            result = await self.cmdmgr.run_async(cmdchain)
            self.assertEqual(result, success)
            self.assertEqual(self.true_cb.kwargs, list(true_args))
            self.assertEqual(self.false_cb.kwargs, list(false_args))
            self.assertEqual(self.error_handler.args, list(errors))

        testcases = (
            ([['true', '-a'], '&', ['true', '-a', '-b'], '&', ['true', '-a', '-b', '-c']],
             {'success': True,
              'true_args': [{'a': True, 'b': False, 'c': False},
                            {'a': True, 'b': True, 'c': False},
                            {'a': True, 'b': True, 'c': True}]}),
            ([['true', '-a'], '&', ['true', '-a', '-b'], '|', ['true', '-a', '-b', '-c']],
             {'success': True,
              'true_args': [{'a': True, 'b': False, 'c': False},
                            {'a': True, 'b': True, 'c': False}]}),
            ([['true', '-a'], '|', ['true', '-a', '-b'], '|', ['true', '-a', '-b', '-c']],
             {'success': True,
              'true_args': [{'a': True, 'b': False, 'c': False}]}),

            ([['false', '-a'], '|', ['false', '-a', '-b'], '|', ['false', '-a', '-b', '-c']],
             {'success': False,
              'false_args': [{'a': True, 'b': False, 'c': False},
                             {'a': True, 'b': True, 'c': False},
                             {'a': True, 'b': True, 'c': True}]}),
            ([['false', '-a'], '|', ['false', '-a', '-b'], '&', ['false', '-a', '-b', '-c']],
             {'success': False,
              'false_args': [{'a': True, 'b': False, 'c': False},
                             {'a': True, 'b': True, 'c': False}]}),
            ([['false', '-a'], '&', ['false', '-a', '-b'], '&', ['false', '-a', '-b', '-c']],
             {'success': False,
              'false_args': [{'a': True, 'b': False, 'c': False}]}),

            ([['false', '-a'], ';', ['true', '-a', '-b', '-x'], ';', ['true', '-a', '-b', '-c']],
             {'success': True,
              'false_args': [{'a': True, 'b': False, 'c': False}],
              'true_args': [{'a': True, 'b': True, 'c': True}],
              'errors': [('true: Unrecognized arguments: -x',)]}),
        )
        await self.run_testcases(testcases, do_test)
Exemplo n.º 2
0
class TestCommandBase(asynctest.TestCase):
    def setUp(self):
        def run_sync(self_, A, B):
            assert isinstance(self_, _CommandBase)
            self_.info(round(int(A) / int(B)))

        async def run_async(self_, A, B):
            assert isinstance(self_, _CommandBase)
            await asyncio.sleep(0)
            self_.info(round(int(A) / int(B)))

        argspecs = ({
            'names': ('A', ),
            'type': int,
            'description': 'First number'
        }, {
            'names': ('B', ),
            'type': int,
            'description': 'Second number'
        })

        self.div_sync = make_cmdcls(name='div',
                                    run=run_sync,
                                    argspecs=argspecs,
                                    provides=('sync', ))
        self.div_async = make_cmdcls(name='div',
                                     run=run_async,
                                     argspecs=argspecs,
                                     provides=('async', ))
        self.info_handler = Callback()
        self.error_handler = Callback()

    def test_mandatory_properties(self):
        attrs = {
            'name': 'foo',
            'run': lambda self: None,
            'category': 'catfoo',
            'provides': ('tui', 'cli'),
            'description': 'This command is the foo!'
        }

        for missing_attr in ('name', 'category', 'provides', 'description',
                             'run'):
            this_attrs = attrs.copy()
            del this_attrs[missing_attr]
            with self.assertRaises(RuntimeError) as cm:
                make_cmdcls(**this_attrs, defaults=False)
            self.assertIn(missing_attr, str(cm.exception))
            self.assertIn('attribute', str(cm.exception).lower())
        # assertRaisesNot
        make_cmdcls(**attrs, defaults=False)

    def test_kwargs_become_instance_attributes(self):
        cmdcls = make_cmdcls()
        cmdinst = cmdcls(foo='bar', one=1)
        self.assertEqual(cmdinst.foo, 'bar')
        self.assertEqual(cmdinst.one, 1)

    def test_argparser(self):
        argspecs = ({
            'names': ('ARG1', ),
            'description': 'First arg'
        }, {
            'names': ('ARG2', ),
            'description': 'Second arg'
        })
        cmdcls = make_cmdcls(argspecs=argspecs)
        self.assertTrue(hasattr(cmdcls, '_argparser'))
        kwargs = vars(cmdcls._argparser.parse_args(['foo', 'bar']))
        self.assertEqual(kwargs, {'ARG1': 'foo', 'ARG2': 'bar'})

        with self.assertRaises(CmdArgError):
            cmdcls._argparser.parse_args(['foo', 'bar', 'baz'])

    def test_names_and_aliases(self):
        cmdcls = make_cmdcls(name='foo', aliases=('bar', 'baz'))
        self.assertEqual(cmdcls.names, ['foo', 'bar', 'baz'])
        cmdcls = make_cmdcls(name='foo')
        self.assertEqual(cmdcls.names, ['foo'])

    def test_argparser_error(self):
        process = self.div_sync(['10', '2', '--frobnicate'],
                                info_handler=self.info_handler,
                                error_handler=self.error_handler)

        self.assertEqual(process.finished, True)
        self.assertEqual(process.success, False)
        self.assertEqual(self.info_handler.calls, 0)
        self.assertEqual(self.error_handler.calls, 1)
        self.assertEqual(str(self.error_handler.args[0][0]),
                         'div: Unrecognized arguments: --frobnicate')

    def check(self, process, success, infos=(), errors=()):
        self.assertEqual(process.finished, True)
        self.assertEqual(process.success, success)
        self.assertEqual(self.error_handler.args, list(errors))
        self.assertEqual(self.info_handler.args, list(infos))

    # Test running commands with stopped asyncio loop

    def test_run_does_not_raise_exception_in_sync_context(self):
        process = self.div_sync(['10', '2'],
                                info_handler=self.info_handler,
                                error_handler=self.error_handler)
        self.check(process, success=True, infos=[('div: 5', )])

        self.info_handler.reset()
        self.error_handler.reset()

        process = self.div_async(['50', '2'],
                                 info_handler=self.info_handler,
                                 error_handler=self.error_handler)
        process.wait_sync()
        self.check(process, success=True, infos=[('div: 25', )])

    def test_run_raises_CmdError_in_sync_context(self):
        process = self.div_sync(['10', 'foo'],
                                info_handler=self.info_handler,
                                error_handler=self.error_handler)
        self.check(process,
                   success=False,
                   errors=[("div: Argument B: invalid int value: 'foo'", )])

        self.info_handler.reset()
        self.error_handler.reset()

        process = self.div_async(['1', 'bar'],
                                 info_handler=self.info_handler,
                                 error_handler=self.error_handler)
        process.wait_sync()
        self.check(process,
                   success=False,
                   errors=[("div: Argument B: invalid int value: 'bar'", )])

    def test_run_raises_Exception_in_sync_context(self):
        with self.assertRaises(ZeroDivisionError):
            self.div_sync(['10', '0'],
                          info_handler=self.info_handler,
                          error_handler=self.error_handler)
        self.assertEqual(self.info_handler.args, [])
        self.assertEqual(self.error_handler.args, [])

        self.info_handler.reset()
        self.error_handler.reset()

        process = self.div_async(['1', '0'],
                                 info_handler=self.info_handler,
                                 error_handler=self.error_handler)
        with self.assertRaises(ZeroDivisionError):
            process.wait_sync()
        self.assertEqual(self.info_handler.args, [])
        self.assertEqual(self.error_handler.args, [])

    # Test running commands with running asyncio loop

    async def test_run_does_not_raise_exception_in_async_context(self):
        process = self.div_sync(['10', '2'],
                                info_handler=self.info_handler,
                                error_handler=self.error_handler)
        self.check(process, success=True, infos=[('div: 5', )])

        self.info_handler.reset()
        self.error_handler.reset()

        process = self.div_async(['10', '5'],
                                 info_handler=self.info_handler,
                                 error_handler=self.error_handler)
        await process.wait_async()
        self.check(process, success=True, infos=[('div: 2', )])

    async def test_run_raises_CmdError_in_async_context(self):
        process = self.div_sync(['10', 'foo'],
                                info_handler=self.info_handler,
                                error_handler=self.error_handler)
        self.check(process,
                   success=False,
                   errors=[("div: Argument B: invalid int value: 'foo'", )])

        self.info_handler.reset()
        self.error_handler.reset()

        process = self.div_async(['100', 'bar'],
                                 info_handler=self.info_handler,
                                 error_handler=self.error_handler)
        await process.wait_async()
        self.check(process,
                   success=False,
                   errors=[("div: Argument B: invalid int value: 'bar'", )])

    async def test_run_raises_Exception_in_async_context(self):
        with self.assertRaises(ZeroDivisionError):
            self.div_sync(['10', '0'],
                          info_handler=self.info_handler,
                          error_handler=self.error_handler)
        self.assertEqual(self.info_handler.calls, 0)
        self.assertEqual(self.error_handler.calls, 0)
Exemplo n.º 3
0
class TestCommandManagerChainedCalls(TestCommandManagerCallsBase):
    def setUp(self):
        super().setUp()
        self.true_cb = Callback()
        self.false_cb = Callback()

        def true_run(self_, *args, **kwargs):
            self.true_cb(*args, **kwargs)
            return True

        def false_run(self_, *args, **kwargs):
            self.false_cb(*args, **kwargs)
            raise CmdError('Nope')

        args = ({
            'names': ('-a', ),
            'action': 'store_true',
            'description': 'A args'
        }, {
            'names': ('-b', ),
            'action': 'store_true',
            'description': 'B args'
        }, {
            'names': ('-c', ),
            'action': 'store_true',
            'description': 'C args'
        })
        true_cmd = make_cmdcls(name='true',
                               run=true_run,
                               argspecs=args,
                               provides=('T', ))
        false_cmd = make_cmdcls(name='false',
                                run=false_run,
                                argspecs=args,
                                provides=('F', ))

        self.cmdmgr.register(true_cmd)
        self.cmdmgr.register(false_cmd)

    def assert_success(self, cmdchain):
        success = self.cmdmgr.run_sync(cmdchain,
                                       on_success=self.cb_success,
                                       on_error=self.cb_error)
        self.assertEqual(success, True)

    def assert_failure(self, cmdchain):
        success = self.cmdmgr.run_sync(cmdchain,
                                       on_success=self.cb_success,
                                       on_error=self.cb_error)
        self.assertEqual(success, False)

    @asynctest.ignore_loop
    def test_empty_cmdchain(self):
        self.assert_success([])
        self.assert_success('')

    @asynctest.ignore_loop
    def test_run_good_parsed_cmdchain(self):
        self.assert_success([['false'], ';', ('true', )])
        self.assert_success([['false'], ['true']])
        self.assert_success((('true', ), '&', ['true']))
        self.assert_success(iter([['false'], '|', ['true']]))

        self.assert_failure([['true'], ';', ('false', )])
        self.assert_failure([['true'], ['false']])
        self.assert_failure((['true'], '&', ['false']))
        self.assert_failure((['false'], '|', ['false']))

    def assert_invalid_cmdchain_format(self, cmdchain):
        with self.assertRaises(RuntimeError):
            self.cmdmgr.run_sync(cmdchain)

    @asynctest.ignore_loop
    def test_run_bad_parsed_cmdchain(self):
        self.assert_invalid_cmdchain_format([['true'], ';', 'true'])
        self.assert_invalid_cmdchain_format((('true', ), '&', iter(['true'])))
        self.assert_invalid_cmdchain_format((('true', ), '&&&', ['true']))

    @asynctest.ignore_loop
    def test_consecutive_operators(self):
        def assert_consecutive_ops(cmdchain, op1, op2):
            with self.assertRaises(CmdError) as cm:
                self.cmdmgr.run_sync(cmdchain)
            self.assertIn('Consecutive operators', str(cm.exception))
            self.assertIn('%s %s' % (op1, op2), str(cm.exception))

        assert_consecutive_ops('true ; ; true', ';', ';')
        assert_consecutive_ops('true ; & true', ';', '&')
        assert_consecutive_ops('true &   | true', '&', '|')
        assert_consecutive_ops([['true'], '&', '|', ['true']], '&', '|')

    @asynctest.ignore_loop
    def test_nonexisting_cmd_in_cmdchain(self):
        def do_test(cmdchain, success, success_calls, error_calls, true_calls,
                    false_calls, true_args, false_args):
            succ = self.cmdmgr.run_sync(cmdchain,
                                        on_success=self.cb_success,
                                        on_error=self.cb_error)
            self.assertEqual(succ, success)
            self.assertEqual(self.cb_success.calls, success_calls)
            self.assertEqual(self.cb_error.calls, error_calls)
            self.assertEqual(self.true_cb.calls, true_calls)
            self.assertEqual(self.false_cb.calls, false_calls)
            self.assertEqual(self.true_cb.kwargs, true_args)
            self.assertEqual(self.false_cb.kwargs, false_args)

            self.cb_success.reset()
            self.cb_error.reset()
            self.true_cb.reset()
            self.false_cb.reset()

        do_test('true ; foo', False, 1, 1, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])
        do_test('false ; foo', False, 0, 2, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('false | foo', False, 0, 2, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('false & foo', False, 0, 1, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('true & foo', False, 1, 1, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])

        do_test('foo & true', False, 0, 1, 0, 0, [], [])
        do_test('foo | false', False, 0, 2, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('foo ; true', True, 1, 1, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])

    @asynctest.ignore_loop
    def test_only_cmds_from_active_interface_are_called(self):
        def do_test(cmdchain, success, success_calls, error_calls, true_calls,
                    false_calls, true_args, false_args):
            succ = self.cmdmgr.run_sync(cmdchain,
                                        on_success=self.cb_success,
                                        on_error=self.cb_error)
            self.assertEqual(succ, success)
            self.assertEqual(self.cb_success.calls, success_calls)
            self.assertEqual(self.cb_error.calls, error_calls)
            self.assertEqual(self.true_cb.calls, true_calls)
            self.assertEqual(self.false_cb.calls, false_calls)
            self.assertEqual(self.true_cb.kwargs, true_args)
            self.assertEqual(self.false_cb.kwargs, false_args)

            self.cb_success.reset()
            self.cb_error.reset()
            self.true_cb.reset()
            self.false_cb.reset()

        # Calls to 'false' are ignored and evaluate to False in the command chain
        self.cmdmgr.active_interface = 'T'
        do_test('true ; false', False, 1, 0, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])
        do_test('false ; true', True, 1, 0, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])
        do_test('true & false', False, 1, 0, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])
        do_test('false & true', False, 0, 0, 0, 0, [], [])
        do_test('true | false', True, 1, 0, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])
        do_test('false | true', True, 1, 0, 1, 0, [{
            'a': False,
            'b': False,
            'c': False
        }], [])

        # Calls to 'true' are ignored and evaluate to False in the command chain
        self.cmdmgr.active_interface = 'F'
        do_test('true ; false', False, 0, 1, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('false ; true', False, 0, 1, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('true & false', False, 0, 0, 0, 0, [], [])
        do_test('false & true', False, 0, 1, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('true | false', False, 0, 1, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])
        do_test('false | true', False, 0, 1, 0, 1, [], [{
            'a': False,
            'b': False,
            'c': False
        }])

    @asynctest.ignore_loop
    def test_final_process_determines_overall_success(self):
        def do_test(cmdchain, success):
            result = self.cmdmgr.run_sync(cmdchain,
                                          on_success=self.cb_success,
                                          on_error=self.cb_error)
            self.assertEqual(result, success)

        do_test([['true'], ';', ['true']], success=True)
        do_test([['false'], ';', ['true']], success=True)
        do_test([['true'], ';', ['false']], success=False)
        do_test([['false'], ';', ['false']], success=False)

        do_test([['true'], '&', ['true']], success=True)
        do_test([['false'], '&', ['true']], success=False)
        do_test([['true'], '&', ['false']], success=False)
        do_test([['false'], '&', ['false']], success=False)

        do_test([['true'], '|', ['true']], success=True)
        do_test([['false'], '|', ['true']], success=True)
        do_test([['true'], '|', ['false']], success=True)
        do_test([['false'], '|', ['false']], success=False)

    @asynctest.ignore_loop
    def test_sync_complete_chain_with_AND_operator(self):
        result = self.cmdmgr.run_sync(
            'true -a  &  true -a -b  &  true -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_complete_chain_with_AND_operator(result)

    async def test_async_complete_chain_with_AND_operator(self):
        result = await self.cmdmgr.run_async(
            'true -a  &  true -a -b  &  true -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_complete_chain_with_AND_operator(result)

    def confirm_complete_chain_with_AND_operator(self, result):
        self.assertEqual(result, True)
        self.assertEqual(self.cb_success.calls, 3)
        self.assertEqual(self.cb_error.calls, 0)
        self.assertEqual(self.true_cb.calls, 3)
        self.assertEqual(self.true_cb.kwargs, [{
            'a': True,
            'b': False,
            'c': False
        }, {
            'a': True,
            'b': True,
            'c': False
        }, {
            'a': True,
            'b': True,
            'c': True
        }])
        self.assertEqual(self.false_cb.calls, 0)

    @asynctest.ignore_loop
    def test_sync_broken_chain_with_AND_operator(self):
        result = self.cmdmgr.run_sync(
            'true -a  &  false -a -b  &  true -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_broken_chain_with_AND_operator(result)

    async def test_async_broken_chain_with_AND_operator(self):
        result = await self.cmdmgr.run_async(
            'true -a  &  false -a -b  &  true -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_broken_chain_with_AND_operator(result)

    def confirm_broken_chain_with_AND_operator(self, result):
        self.assertEqual(result, False)
        self.assertEqual(self.cb_success.calls, 1)
        self.assertEqual(self.cb_error.calls, 1)
        self.assertEqual(self.true_cb.calls, 1)
        self.assertEqual(self.true_cb.kwargs, [{
            'a': True,
            'b': False,
            'c': False
        }])
        self.assertEqual(self.false_cb.calls, 1)
        self.assertEqual(self.false_cb.kwargs, [{
            'a': True,
            'b': True,
            'c': False
        }])

    @asynctest.ignore_loop
    def test_sync_complete_chain_with_OR_operator(self):
        result = self.cmdmgr.run_sync(
            'false -a  |  false -a -b  |  true -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_complete_chain_with_OR_operator(result)

    async def test_async_complete_chain_with_OR_operator(self):
        result = await self.cmdmgr.run_async(
            'false -a  |  false -a -b  |  true -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_complete_chain_with_OR_operator(result)

    def confirm_complete_chain_with_OR_operator(self, result):
        self.assertEqual(result, True)
        self.assertEqual(self.cb_success.calls, 1)
        self.assertEqual(self.cb_error.calls, 2)
        self.assertEqual(self.false_cb.calls, 2)
        self.assertEqual(self.false_cb.kwargs, [{
            'a': True,
            'b': False,
            'c': False
        }, {
            'a': True,
            'b': True,
            'c': False
        }])
        self.assertEqual(self.true_cb.calls, 1)
        self.assertEqual(self.true_cb.kwargs, [{
            'a': True,
            'b': True,
            'c': True
        }])

    @asynctest.ignore_loop
    def test_sync_broken_chain_with_OR_operator(self):
        result = self.cmdmgr.run_sync(
            'false -a  |  true -a -b  |  false -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_broken_chain_with_OR_operator(result)

    async def test_async_broken_chain_with_OR_operator(self):
        result = await self.cmdmgr.run_async(
            'false -a  |  true -a -b  |  false -a -b -c',
            on_success=self.cb_success,
            on_error=self.cb_error)
        self.confirm_broken_chain_with_OR_operator(result)

    def confirm_broken_chain_with_OR_operator(self, result):
        self.assertEqual(result, True)
        self.assertEqual(self.cb_success.calls, 1)
        self.assertEqual(self.cb_error.calls, 1)
        self.assertEqual(self.false_cb.calls, 1)
        self.assertEqual(self.false_cb.kwargs, [{
            'a': True,
            'b': False,
            'c': False
        }])
        self.assertEqual(self.true_cb.calls, 1)
        self.assertEqual(self.true_cb.kwargs, [{
            'a': True,
            'b': True,
            'c': False
        }])