예제 #1
0
async def test_clients_modified_during_runtime(udp_port, udp_port0):
    loop = asyncio.get_event_loop()

    sender = UmdSender()
    receiver1 = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)
    receiver2 = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port0)

    add_listener1 = EventListener()
    add_listener2 = EventListener()
    tally_listener1 = EventListener()
    tally_listener2 = EventListener()

    async def drain_listeners():
        async def drain(listener):
            while not listener.empty():
                await listener.get()

        coros = [
            drain(add_listener1),
            drain(add_listener2),
            drain(tally_listener1),
            drain(tally_listener2),
        ]
        await asyncio.gather(*coros)

    receiver1.bind_async(
        loop,
        on_tally_added=add_listener1.callback,
        on_tally_updated=tally_listener1.callback,
    )
    receiver2.bind_async(
        loop,
        on_tally_added=add_listener2.callback,
        on_tally_updated=tally_listener2.callback,
    )

    screen_index = 1

    async with receiver1:
        async with receiver2:
            async with sender:
                # Add 10 tallies and add the first receiver to clients after the third one
                for i in range(10):
                    t_id = (screen_index, i)
                    sender.set_tally_text(t_id, f'Tally {i}')
                    if i == 2:
                        sender.clients.add(('127.0.0.1', udp_port))

                # Update the tallies and add the second receiver after the third one
                for i in range(10):
                    t_id = (screen_index, i)
                    tx_tally = sender.tallies[t_id]
                    sender.set_tally_color(t_id, 'rh|lh', 'red')
                    if i == 2:
                        sender.clients.add(('127.0.0.1', udp_port0))

                # Wait for a full update then allow the messages to come in
                await asyncio.sleep(sender.tx_interval)
                await drain_listeners()
                assert receiver1.tallies == receiver2.tallies == sender.tallies
예제 #2
0
async def test_broadcast_display(uhs500_msg_bytes, uhs500_msg_parsed,
                                 udp_endpoint, udp_port, faker):
    transport, protocol, endpoint_port = udp_endpoint
    assert udp_port != endpoint_port

    loop = asyncio.get_event_loop()

    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    displays_by_index = {
        disp.index: disp
        for disp in uhs500_msg_parsed.displays
    }

    async with receiver:
        # Populate the receiver's tallies and wait for them to be added
        transport.sendto(uhs500_msg_bytes, ('127.0.0.1', udp_port))
        _ = await evt_listener.get()
        while not evt_listener.empty():
            _ = await evt_listener.get()

        assert len(receiver.tallies) == len(uhs500_msg_parsed.displays)

        receiver.unbind(evt_listener)
        receiver.bind_async(loop, on_tally_updated=evt_listener.callback)

        # Send a broadcast display message for each TallyColor with a random brightness
        # The control field is set so the Tally text field is unchanged
        for color in TallyColor:
            brightness = faker.pyint(max_value=3)
            bc_disp = Display.broadcast(
                control=b'foo',
                rh_tally=color,
                lh_tally=color,
                txt_tally=color,
                brightness=brightness,
            )
            msg = Message(displays=[bc_disp])
            transport.sendto(msg.build_message(), ('127.0.0.1', udp_port))

            _ = await evt_listener.get()
            while not evt_listener.empty():
                _ = await evt_listener.get()

            # Check each of the receiver's tallies against the bc_disp values
            # and make sure the text didn't change
            for tally in receiver.tallies.values():
                assert tally.index < 0xffff
                assert not tally.is_broadcast
                assert tally.rh_tally == color
                assert tally.txt_tally == color
                assert tally.lh_tally == color
                assert tally.control == bc_disp.control
                assert tally.brightness == brightness
                assert tally.text == displays_by_index[tally.index].text
예제 #3
0
async def test_rebind(uhs500_msg_bytes, uhs500_msg_parsed, udp_endpoint,
                      udp_port, unused_tcp_port_factory):
    transport, protocol, endpoint_port = udp_endpoint
    assert udp_port != endpoint_port

    loop = asyncio.get_event_loop()

    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    async with receiver:

        # Send message bytes to receiver and wait for ``on_tally_added`` events
        transport.sendto(uhs500_msg_bytes, ('127.0.0.1', udp_port))
        _ = await evt_listener.get()
        while not evt_listener.empty():
            _ = await evt_listener.get()

        assert len(receiver.tallies) == len(uhs500_msg_parsed.displays)

        # Set up to get ``on_tally_updated`` callbacks
        receiver.unbind(evt_listener)
        receiver.bind_async(loop, on_tally_updated=evt_listener.callback)

        # Change bind address and trigger a change
        await receiver.set_hostaddr('0.0.0.0')
        assert receiver.hostaddr == '0.0.0.0'

        disp = uhs500_msg_parsed.displays[0]
        disp.brightness = 1

        transport.sendto(uhs500_msg_parsed.build_message(),
                         ('0.0.0.0', udp_port))

        evt_args, evt_kwargs = await evt_listener.get()
        evt_tally = evt_args[0]
        assert disp == evt_tally

        # Change bind port and trigger a change
        new_port = unused_tcp_port_factory()
        assert new_port != udp_port

        await receiver.set_hostport(new_port)
        assert receiver.hostport == new_port

        disp.brightness = 2
        transport.sendto(uhs500_msg_parsed.build_message(),
                         ('0.0.0.0', new_port))

        evt_args, evt_kwargs = await evt_listener.get()
        evt_tally = evt_args[0]
        assert disp == evt_tally
예제 #4
0
    def __init__(self,
                 config: MultiTallyConfig,
                 hostaddr: str = UmdReceiver.DEFAULT_HOST,
                 hostport: int = UmdReceiver.DEFAULT_PORT):

        super().__init__(config)
        self.loop = asyncio.get_event_loop()
        self._screen_indices = set()
        self._tally_keys = set()
        self.receiver = UmdReceiver(hostaddr=hostaddr, hostport=hostport)
        self.receiver.bind(
            on_screen_added=self._on_receiver_screen_added,
            on_tally_added=self._on_receiver_tally_added,
            on_tally_updated=self._on_receiver_tally_updated
        )
예제 #5
0
async def test_disp_control(faker, udp_port):
    loop = asyncio.get_event_loop()

    sender = UmdSender(clients=[('127.0.0.1', udp_port)])
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    add_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=add_listener.callback)

    tally_listener = EventListener()
    receiver.bind_async(loop, on_tally_control=tally_listener.callback)

    screen_index = 1

    async with receiver:
        async with sender:
            for i in range(100):
                t_id = (screen_index, i)
                sender.set_tally_text(t_id, f'Tally-{i}')
                tx_tally = sender.tallies[t_id]

                evt_args, evt_kwargs = await add_listener.get()
                rx_tally = evt_args[0]
                assert rx_tally == tx_tally

                data_len = faker.pyint(min_value=1, max_value=1024)
                control_data = faker.binary(length=data_len)

                await sender.send_tally_control(t_id, control_data)
                assert tx_tally.control == control_data

                evt_args, evt_kwargs = await tally_listener.get()
                _rx_tally, rx_data = evt_args
                assert _rx_tally is rx_tally

                assert rx_data == rx_tally.control == tx_tally.control == control_data

            t_id = (screen_index, 200)
            data_len = faker.pyint(min_value=1, max_value=1024)
            control_data = faker.binary(length=data_len)
            await sender.send_tally_control(t_id, control_data)

            tx_tally = sender.tallies[t_id]

            evt_args, evt_kwargs = await add_listener.get()
            rx_tally = evt_args[0]
            assert rx_tally.control == tx_tally.control == control_data
            assert rx_tally == tx_tally
예제 #6
0
 def __init__(self):
     self._reading_config = False
     self._config_read = asyncio.Event()
     self._connect_lock = asyncio.Lock()
     super().__init__()
     self.receiver = UmdReceiver()
     self.hostaddr = self.receiver.hostaddr
     self.hostport = self.receiver.hostport
     self.receiver.bind_async(self.loop,
         on_tally_added=self._on_receiver_tally_added,
         on_tally_updated=self._on_receiver_tally_updated,
     )
     self.bind_async(self.loop,
         config=self.read_config,
     )
     self.bind(**{prop:self.update_config for prop in ['hostaddr', 'hostport']})
예제 #7
0
async def test_parse_errors(faker, udp_endpoint, udp_port):
    transport, protocol, endpoint_port = udp_endpoint
    assert udp_port != endpoint_port

    loop = asyncio.get_event_loop()

    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    msgobj = Message()
    disp = Display(index=0, text='foo')
    msgobj.displays.append(disp)

    async with receiver:
        transport.sendto(msgobj.build_message(), ('127.0.0.1', udp_port))

        _ = await evt_listener.get()

        receiver.unbind(evt_listener)
        receiver.bind_async(loop, on_tally_updated=evt_listener.callback)

        screen = receiver.screens[msgobj.screen]
        rx_disp = screen[disp.index]
        assert rx_disp is receiver.tallies[rx_disp.id]
        assert rx_disp == disp

        for i in range(100):
            num_bytes = faker.pyint(min_value=1, max_value=1024)
            bad_bytes = faker.binary(length=num_bytes)

            with pytest.raises(ParseError):
                receiver.parse_incoming(bad_bytes,
                                        ('127.0.0.1', endpoint_port))

            transport.sendto(bad_bytes, ('127.0.0.1', udp_port))

            disp.text = f'foo_{i}'
            transport.sendto(msgobj.build_message(), ('127.0.0.1', udp_port))

            _ = await evt_listener.get()

            assert rx_disp == disp
예제 #8
0
async def test_all_off_on_close(faker, udp_port):
    loop = asyncio.get_event_loop()

    sender = UmdSender(
        clients=[('127.0.0.1', udp_port)],
        all_off_on_close=True,
    )
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    add_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=add_listener.callback)

    tally_listener = EventListener()
    receiver.bind_async(loop, on_tally_updated=tally_listener.callback)

    async with receiver:
        async with sender:
            for screen_index in range(10):
                for i in range(10):
                    t_id = (screen_index, i)
                    sender.set_tally_text(t_id, f'Tally-{i}')
                    tx_tally = sender.tallies[t_id]

                    evt_args, evt_kwargs = await add_listener.get()
                    rx_tally = evt_args[0]
                    assert rx_tally == tx_tally

                    for ttype in TallyType.all():
                        setattr(tx_tally, ttype.name, TallyColor.RED)

                        evt_args, evt_kwargs = await tally_listener.get()
                        assert getattr(rx_tally, ttype.name) == TallyColor.RED

        # Sender is closed and should have broadcast "all-off"
        _ = await asyncio.wait_for(tally_listener.get(), timeout=1)
        while not tally_listener.empty():
            _ = await tally_listener.get()
        for rx_tally in receiver.tallies.values():
            assert rx_tally.rh_tally == TallyColor.OFF
            assert rx_tally.txt_tally == TallyColor.OFF
            assert rx_tally.lh_tally == TallyColor.OFF
예제 #9
0
async def test_scontrol(faker, udp_port):
    loop = asyncio.get_event_loop()

    sender = UmdSender(clients=[('127.0.0.1', udp_port)])
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_scontrol=evt_listener.callback)
    bc_listener = EventListener()
    receiver.broadcast_screen.bind_async(loop, on_control=bc_listener.callback)

    async with receiver:
        async with sender:
            for i in range(100):
                data_len = faker.pyint(min_value=1, max_value=1024)
                control_data = faker.binary(length=data_len)

                await sender.send_scontrol(screen_index=i, data=control_data)

                evt_args, evt_kwargs = await evt_listener.get()

                rx_screen, rx_data = evt_args
                assert rx_screen.index == i
                assert rx_data == control_data

                # Send broadcast
                await sender.send_broadcast_scontrol(data=control_data)

                # Wait for the broadcast screen
                evt_args, evt_kwargs = await bc_listener.get()
                rx_screen, rx_data = evt_args
                assert rx_screen is receiver.broadcast_screen
                assert rx_data == control_data

                # Wait for each currently existing screen
                num_screens = i + 1
                for j in range(num_screens):
                    evt_args, evt_kwargs = await evt_listener.get()
                    rx_screen, rx_data = evt_args
                    assert rx_data == control_data
예제 #10
0
async def test_scontrol(faker, udp_endpoint, udp_port):
    transport, protocol, endpoint_port = udp_endpoint
    assert udp_port != endpoint_port

    loop = asyncio.get_event_loop()

    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_scontrol=evt_listener.callback)

    async with receiver:
        for i in range(100):
            data_len = faker.pyint(min_value=1, max_value=1024)
            control_data = faker.binary(length=data_len)

            msgobj = Message(screen=i, scontrol=control_data)
            transport.sendto(msgobj.build_message(), ('127.0.0.1', udp_port))

            args, kwargs = await evt_listener.get()

            screen, rx_data = args
            assert screen.index == i
            assert rx_data == control_data
예제 #11
0
async def test_with_uhs_data(uhs500_msg_bytes, uhs500_msg_parsed, udp_endpoint,
                             udp_port):
    transport, protocol, endpoint_port = udp_endpoint
    assert udp_port != endpoint_port

    loop = asyncio.get_event_loop()

    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    uhs_screen = uhs500_msg_parsed.screen

    async with receiver:

        # Send message bytes to receiver
        transport.sendto(uhs500_msg_bytes, ('127.0.0.1', udp_port))

        # Wait for all ``on_tally_added`` events
        _ = await evt_listener.get()
        while not evt_listener.empty():
            _ = await evt_listener.get()

        screen = receiver.screens[uhs_screen]

        # Check all receiver tallies against the expected ones
        assert len(receiver.tallies) == len(uhs500_msg_parsed.displays)

        for disp in uhs500_msg_parsed.displays:
            assert disp.index in screen
            tally = screen.tallies[disp.index]
            assert tally.id == (uhs_screen, disp.index)
            assert receiver.tallies[tally.id] is tally
            assert disp == tally

        # Change each display and send the updated message to receiver
        # Then wait for ``on_tally_updated`` events
        receiver.unbind(evt_listener)
        receiver.bind_async(loop, on_tally_updated=evt_listener.callback)
        for disp in uhs500_msg_parsed.displays:
            tally = screen.tallies[disp.index]

            for tally_type in TallyType.all():
                attr = tally_type.name
                cur_value = getattr(disp, attr)
                if cur_value == TallyColor.RED:
                    new_value = TallyColor.GREEN
                else:
                    new_value = TallyColor.RED
                setattr(disp, attr, new_value)
            disp.text = f'{disp.text}-foo'
            disp.brightness = 1

            transport.sendto(uhs500_msg_parsed.build_message(),
                             ('127.0.0.1', udp_port))

            evt_args, evt_kwargs = await evt_listener.get()
            evt_tally = evt_args[0]
            assert evt_tally is tally
            assert disp == tally
예제 #12
0
async def test_with_uhs_data(udp_port):

    loop = asyncio.get_event_loop()

    sender = UmdSender(clients=[('127.0.0.1', udp_port)])
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    screen_index = 1

    async with receiver:
        async with sender:
            # Create initial tallies using text method
            for i in range(100):
                t_id = (screen_index, i)
                sender.set_tally_text(t_id, f'Tally-{i}')
                tx_tally = sender.tallies[t_id]
                screen = sender.screens[screen_index]
                assert screen[i] is tx_tally

                evt_args, evt_kwargs = await evt_listener.get()
                rx_tally = evt_args[0]
                assert rx_tally == tx_tally

            # Create one more tally using ``set_tally_color``
            t_id = (screen_index, 200)
            sender.set_tally_color(t_id, TallyType.lh_tally, TallyColor.GREEN)
            tx_tally = sender.tallies[t_id]
            assert screen[200] is tx_tally
            evt_args, evt_kwargs = await evt_listener.get()
            rx_tally = evt_args[0]
            assert rx_tally == tx_tally

            # Allow the sender to do a full refresh.  Nothing should have changed
            await asyncio.sleep(sender.tx_interval)
            assert evt_listener.empty()

            # Connect to ``on_tally_updated`` events
            receiver.unbind(evt_listener)
            receiver.bind_async(loop, on_tally_updated=evt_listener.callback)

            # Change each tally/tally_type color to red and check the received values
            for tx_tally in sender.tallies.values():
                for tally_type in TallyType.all():
                    sender.set_tally_color(tx_tally.id, tally_type,
                                           TallyColor.RED)

                    evt_args, evt_kwargs = await evt_listener.get()
                    rx_tally = evt_args[0]
                    assert rx_tally is receiver.tallies[tx_tally.id]
                    assert rx_tally == tx_tally

            # Change the text of the extra tally from above and check
            t_id = (screen_index, 200)
            sender.set_tally_text(t_id, 'Tally-200')
            tx_tally = sender.tallies[t_id]
            evt_args, evt_kwargs = await evt_listener.get()
            rx_tally = evt_args[0]
            assert rx_tally == tx_tally

            # Let the sender to another full refresh
            await asyncio.sleep(sender.tx_interval)
            assert evt_listener.empty()

            # Change all tally/tally_type colors, but don't wait for results yet
            for tx_tally in sender.tallies.values():
                for tally_type in TallyType.all():
                    sender.set_tally_color(tx_tally.id, tally_type,
                                           TallyColor.AMBER)
                sender.set_tally_text(tx_tally.id, f'foo-{tx_tally.index}')

            # Wait for updates from last loop to get to the receiver
            # and check the results
            _ = await evt_listener.get()
            while not evt_listener.empty():
                _ = await evt_listener.get()

            for tx_tally in sender.tallies.values():
                rx_tally = receiver.tallies[tx_tally.id]
                assert rx_tally == tx_tally
예제 #13
0
async def test_broadcast_display(udp_port):
    loop = asyncio.get_event_loop()

    sender = UmdSender(clients=[('127.0.0.1', udp_port)])
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    async def wait_for_receiver():
        _ = await evt_listener.get()
        while not evt_listener.empty():
            _ = await evt_listener.get()

        await asyncio.sleep(sender.tx_interval)
        assert evt_listener.empty()

    color_kw = {
        attr: TallyColor.RED
        for attr in ['rh_tally', 'txt_tally', 'lh_tally']
    }

    screen_index = 1

    async with receiver:
        async with sender:
            # Create initial tallies
            for i in range(10):
                t_id = (screen_index, i)
                tx_tally = sender.add_tally(t_id, **color_kw)
                screen = sender.screens[screen_index]
                assert screen[i] is tx_tally
                tx_tally.text = f'Tally-{i}'

                evt_args, evt_kwargs = await evt_listener.get()
                rx_tally = evt_args[0]
                assert rx_tally == tx_tally

            # Connect to ``on_tally_updated`` events
            receiver.unbind(evt_listener)
            receiver.bind_async(loop, on_tally_updated=evt_listener.callback)

            # Send a broadcast tally for each color setting all TallyType's to it
            for color in TallyColor:
                color_kw = {k: color for k in color_kw.keys()}
                await sender.send_broadcast_tally(screen_index, **color_kw)
                await wait_for_receiver()

                # Check the tally colors and make sure the text values remained
                for rx_tally in receiver.tallies.values():
                    tx_tally = sender.tallies[rx_tally.id]
                    assert rx_tally.text == tx_tally.text == f'Tally-{rx_tally.index}'
                    assert rx_tally.rh_tally == tx_tally.rh_tally == color
                    assert rx_tally.txt_tally == tx_tally.txt_tally == color
                    assert rx_tally.lh_tally == tx_tally.lh_tally == color

            # Broadcast all colors to "OFF" and set all names to 'foo'
            color_kw = {k: TallyColor.OFF for k in color_kw.keys()}
            await sender.send_broadcast_tally(screen_index,
                                              text='foo',
                                              **color_kw)
            await wait_for_receiver()

            # Check the tally colors and text values
            for rx_tally in receiver.tallies.values():
                tx_tally = sender.tallies[rx_tally.id]
                assert rx_tally.text == tx_tally.text == 'foo'
                assert rx_tally.rh_tally == tx_tally.rh_tally == TallyColor.OFF
                assert rx_tally.txt_tally == tx_tally.txt_tally == TallyColor.OFF
                assert rx_tally.lh_tally == tx_tally.lh_tally == TallyColor.OFF

            # Send broadcast tally control messages
            for control_data in [b'foo', b'bar', b'baz']:
                await sender.send_broadcast_tally(screen_index,
                                                  control=control_data)
                await wait_for_receiver()

                # Check for the correct control data and ensure other values
                # remain unchanged
                for rx_tally in receiver.tallies.values():
                    tx_tally = sender.tallies[rx_tally.id]
                    assert rx_tally.control == tx_tally.control == control_data
                    assert rx_tally.text == tx_tally.text == 'foo'
                    assert rx_tally.rh_tally == tx_tally.rh_tally == TallyColor.OFF
                    assert rx_tally.txt_tally == tx_tally.txt_tally == TallyColor.OFF
                    assert rx_tally.lh_tally == tx_tally.lh_tally == TallyColor.OFF

            # Do the same as above, but using the `sender.send_broadcast_tally_control` method
            # and change one tally color
            for control_data in [b'abc', b'def', b'ghi']:
                await sender.send_broadcast_tally_control(
                    screen_index, control_data, rh_tally=TallyColor.RED)

                await wait_for_receiver()

                # Check for the correct control data and ensure other values
                for rx_tally in receiver.tallies.values():
                    tx_tally = sender.tallies[rx_tally.id]
                    assert rx_tally.control == tx_tally.control == control_data
                    assert rx_tally.text == tx_tally.text == 'foo'
                    assert rx_tally.rh_tally == tx_tally.rh_tally == TallyColor.RED
                    assert rx_tally.txt_tally == tx_tally.txt_tally == TallyColor.OFF
                    assert rx_tally.lh_tally == tx_tally.lh_tally == TallyColor.OFF
예제 #14
0
async def test_tally_type_variations(udp_port):

    loop = asyncio.get_event_loop()

    sender = UmdSender(clients=[('127.0.0.1', udp_port)])
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    tally_listener = EventListener()
    receiver.bind_async(loop, on_tally_updated=tally_listener.callback)

    tally_type_strs = ('rh', 'txt', 'lh')
    tally_types = (TallyType.rh_tally, TallyType.txt_tally, TallyType.lh_tally)

    def get_tally_colors(tally):
        d = {}
        for tt in tally_types:
            d[tt] = tally[tt]
        return d

    async def wait_for_rx(tally_type):
        tally_types = set()
        if not isinstance(tally_type, TallyType):
            tally_type = TallyType.from_str(tally_type)
        if tally_type.is_iterable:
            for tt in tally_type:
                tally_types.add(tt.name)
        else:
            tally_types.add(tally_type.name)
        props = set()
        for _ in range(len(tally_types)):
            evt_args, evt_kwargs = await tally_listener.get()
            props |= evt_args[1]
            if props == tally_types:
                break
        assert props == tally_types

    screen_index = 1

    async with receiver:
        async with sender:

            for i in range(10):
                t_id = (screen_index, i)
                expected = {key: TallyColor.OFF for key in tally_types}

                tally = None
                rx_tally = None

                for tt_str, tt in zip(tally_type_strs, tally_types):

                    sender.set_tally_color(t_id, tt_str, TallyColor.RED)
                    expected[tt] = TallyColor.RED

                    tally = sender.tallies[t_id]
                    assert get_tally_colors(tally) == expected

                    if rx_tally is None:
                        evt_args, evt_kwargs = await evt_listener.get()
                        rx_tally = evt_args[0]
                        assert rx_tally == tally
                    else:
                        await wait_for_rx(tt)
                        assert get_tally_colors(rx_tally) == expected

                    sender.set_tally_color(t_id, tt, TallyColor.GREEN)
                    expected[tt] = TallyColor.GREEN
                    assert get_tally_colors(tally) == expected

                    await wait_for_rx(tt)
                    assert get_tally_colors(rx_tally) == expected

                    sender.set_tally_color(t_id, tt_str, 'off')
                    expected[tt] = TallyColor.OFF
                    assert get_tally_colors(tally) == expected

                    await wait_for_rx(tt)
                    assert get_tally_colors(rx_tally) == expected

                    sender.set_tally_color(t_id, tt, 'red')
                    expected[tt] = TallyColor.RED
                    assert get_tally_colors(tally) == expected

                    await wait_for_rx(tt)
                    assert get_tally_colors(rx_tally) == expected

                sender.set_tally_color(t_id, 'all', 'off')
                expected = {key: TallyColor.OFF for key in tally_types}
                assert get_tally_colors(tally) == expected
                assert tally['all'] == TallyColor.OFF

                await wait_for_rx('rh|txt|lh')
                assert get_tally_colors(rx_tally) == expected

                sender.set_tally_color(t_id, 'lh|rh', 'red')
                expected[TallyType.rh_tally] = TallyColor.RED
                expected[TallyType.lh_tally] = TallyColor.RED
                assert get_tally_colors(tally) == expected
                assert tally['all'] == TallyColor.RED

                await wait_for_rx('lh|rh')
                assert get_tally_colors(rx_tally) == expected

                sender.set_tally_color(t_id, 'txt', 'green')
                expected[TallyType.txt_tally] = TallyColor.GREEN
                assert get_tally_colors(tally) == expected

                await wait_for_rx('txt')
                assert get_tally_colors(rx_tally) == expected

                assert tally['all'] == tally['lh|txt'] == tally[
                    'rh|txt'] == TallyColor.AMBER