Example #1
0
    def test_run_to_idle(self):
        b = ProcessBus()
        b.transition('RUN')
        self.log(b, level=20)

        try:
            self.responses = []
            num = 3
            for index in range(num):
                b.subscribe('STOP', self.get_listener('STOP', index))

            b.transition('IDLE')

            # The idle transition MUST call all 'stop' listeners.
            self.assertEqual(
                set(self.responses),
                set(msg % (i, 'STOP', None) for i in range(num))
            )
            # The idle method MUST move the state to IDLE
            self.assertEqual(b.state, 'IDLE')
            # The idle method MUST log its states.
            self.assertLog([
                'Bus state: STOP',
                'Bus state: IDLE'
            ])
        finally:
            # Exit so the atexit handler doesn't complain.
            b.transition('EXITED')
Example #2
0
    def test_keyboard_interrupt(self):
        bus = ProcessBus()

        Handler.bus = bus
        service = WebService(address=('127.0.0.1', 38002),
                             handler_class=Handler)
        adapter = servers.ServerPlugin(bus, service, service.address)
        adapter.subscribe()

        # Raise a keyboard interrupt in the HTTP server's main thread.
        bus.transition('RUN')
        resp = service.do_GET('/ctrlc')
        assertEqual(resp.status, 200)
        bus.block()
        assertEqual(bus.state, 'EXITED')
Example #3
0
    def test_block(self):
        b = ProcessBus()
        self.log(b)

        def f():
            time.sleep(0.2)
            b.transition('EXITED')

        def g():
            time.sleep(0.4)

        def main_listener():
            main_calls.append(1)
        main_calls = []
        b.subscribe("main", main_listener)

        f_thread = threading.Thread(target=f, name='f')
        f_thread.start()
        threading.Thread(target=g, name='g').start()
        threads = [t for t in threading.enumerate() if not t.daemon]
        self.assertEqual(len(threads), 3)

        b.block()
        f_thread.join()

        # The block method MUST wait for the EXITED state.
        self.assertEqual(b.state, 'EXITED')
        # The block method MUST wait for ALL non-main, non-daemon threads to
        # finish.
        threads = [t for t in threading.enumerate() if not t.daemon]
        self.assertEqual(len(threads), 1)
        # The last message will mention an indeterminable thread name; ignore
        # it
        self.assertEqual(
            [entry for entry in self._log_entries
             if not entry.startswith('Publishing')
             and not entry.startswith('Waiting')],
            [
                'Bus state: ENTER',
                'Bus state: IDLE',
                'Bus state: EXIT',
                'Bus state: EXITED'
            ]
        )

        # While the bus was blocked, it should have published periodically
        # to the "main" channel.
        self.assertGreater(len(main_calls), 0)
Example #4
0
    def test_idle_to_exit(self):
        b = ProcessBus()
        self.log(b, level=20)

        self.responses = []
        num = 3
        for index in range(num):
            b.subscribe('EXIT', self.get_listener('EXIT', index))
            b.subscribe('EXITED', self.get_listener('EXITED', index))

        b.transition('EXITED')

        # The bus MUST call all 'EXIT' listeners,
        # and then all 'EXITED' listeners.
        self.assertEqual(
            set(self.responses),
            set([msg % (i, 'EXIT', None) for i in range(num)] +
                [msg % (i, 'EXITED', None) for i in range(num)])
        )
        # The bus MUST move the state to EXITED
        self.assertEqual(b.state, 'EXITED')

        # The bus MUST log its states.
        self.assertLog([
            'Bus state: ENTER',
            'Bus state: IDLE',
            'Bus state: EXIT',
            'Waiting for child threads to terminate...',
            'Bus state: EXITED'
        ])
Example #5
0
    def test_idle_to_run(self):
        b = ProcessBus()
        self.log(b, level=20)

        self.responses = []
        num = 3
        for index in range(num):
            b.subscribe('START', self.get_listener('START', index))

        b.transition('RUN')
        try:
            # The start method MUST call all 'start' listeners.
            self.assertEqual(
                set(self.responses),
                set([msg % (i, 'START', None) for i in range(num)])
            )
            # The transition method MUST move the state to RUN
            # (or START_ERROR, if errors occur)
            self.assertEqual(b.state, 'RUN')

            # The start method MUST log its states.
            self.assertLog([
                'Bus state: ENTER',
                'Bus state: IDLE',
                'Bus state: START',
                'Bus state: RUN'
            ])
        finally:
            # Exit so the atexit handler doesn't complain.
            b.transition('EXITED')
Example #6
0
    def test_wait(self):
        b = ProcessBus()
        self.log(b)

        def f(desired_state):
            time.sleep(0.2)
            b.transition(desired_state)

        for desired_state_, states_to_wait_for in [
            ('RUN', ['RUN']),
            ('IDLE', ['IDLE']),
            ('RUN', ['START', 'RUN']),
            ('EXITED', ['EXITED'])
        ]:
            threading.Thread(target=f, args=(desired_state_,)).start()
            b.wait(states_to_wait_for)

            # The wait method MUST wait for the given state(s).
            if b.state not in states_to_wait_for:
                self.fail('State %r not in %r' % (b.state, states_to_wait_for))
Example #7
0
    def test_start_with_callback(self):
        b = ProcessBus()
        self.log(b)
        try:
            events = []

            def f(*args, **kwargs):
                events.append(('f', args, kwargs))

            def g():
                events.append('g')

            b.subscribe('RUN', g)
            b.start_with_callback(f, (1, 3, 5), {'foo': 'bar'})
            # Give wait() time to run f()
            time.sleep(0.2)

            # The callback method MUST wait for the STARTED state.
            self.assertEqual(b.state, 'RUN')
            # The callback method MUST run after all start methods.
            self.assertEqual(events, ['g', ('f', (1, 3, 5), {'foo': 'bar'})])
        finally:
            b.transition('EXITED')
Example #8
0
    def test_builtin_channels(self):
        b = ProcessBus()
        listeners = [l for l in b.listeners
                     if l != 'log' and not l.endswith('_ERROR')]

        self.responses, expected = [], []

        for channel in listeners:
            if channel != 'log':
                for index, priority in enumerate([100, 50, 0, 51]):
                    b.subscribe(channel,
                                self.get_listener(channel, index), priority)

        try:
            for channel in listeners:
                if channel != 'log':
                    b.publish(channel)
                    expected.extend([msg % (i, channel, ()) for i in (2, 1, 3, 0)])

            self.assertEqual(self.responses, expected)
        finally:
            # Exit so the atexit handler doesn't complain.
            b.transition('EXITED')
    def test_block(self):
        b = ProcessBus()
        self.log(b)

        def f():
            time.sleep(0.2)
            b.transition('EXITED')

        def g():
            time.sleep(0.4)

        def main_listener():
            main_calls.append(1)

        main_calls = []
        b.subscribe('main', main_listener)

        f_thread = threading.Thread(target=f, name='f')
        f_thread.start()
        threading.Thread(target=g, name='g').start()
        threads = [t for t in threading.enumerate() if not t.daemon]
        assert len(threads) == 3

        b.block()
        f_thread.join()

        # The block method MUST wait for the EXITED state.
        assert b.state == 'EXITED'
        # The block method MUST wait for ALL non-main, non-daemon threads to
        # finish.
        threads = [t for t in threading.enumerate() if not t.daemon]
        assert len(threads) == 1
        # The last message will mention an indeterminable thread name; ignore
        # it
        assert ([
            entry for entry in self._log_entries
            if not entry.startswith('Publishing')
            and not entry.startswith('Waiting')
        ] == [
            'Bus state: ENTER', 'Bus state: IDLE', 'Bus state: EXIT',
            'Bus state: EXITED'
        ])

        # While the bus was blocked, it should have published periodically
        # to the "main" channel.
        assert len(main_calls) > 0
Example #10
0
    def test_thread_manager(self):
        bus = ProcessBus()

        class Handler(WebHandler):

            def do_GET(self):
                if self.path == '/':
                    self.respond('Hello World')
                else:
                    self.respond(status=404)
        Handler.bus = bus

        service = WebService(address=('127.0.0.1', 38001),
                             handler_class=Handler)
        WebAdapter(bus, service).subscribe()

        tm = tasks.ThreadManager(bus)
        tm.subscribe()
        assertEqual(len(tm.threads), 0)

        # Test server start
        bus.transition('RUN')
        try:
            assertEqual(bus.state, 'RUN')
            assertEqual(service.ready, True)
            assertEqual(len(tm.threads), 0)

            assertEqual(service.do_GET('/').read(), b'Hello World')
            assertEqual(len(tm.threads), 1)

            # Test bus stop. This will also stop the WebService.
            bus.transition('IDLE')
            assertEqual(bus.state, 'IDLE')

            # Verify that our custom stop function was called
            assertEqual(len(tm.threads), 0)
        finally:
            bus.transition('EXITED')