def test_set_link_enable_blocks(abc, bc):
    # Make sure that the set power command can block
    event = threading.Event()
    bc.write_fpga_register.side_effect = (lambda *a, **k: event.wait())
    bc.read_bmp_version.side_effect = mock_read_bmp_version

    done_event = OnDoneEvent()
    requests = AtomicRequests(done_event)
    requests.link(10, Links.east, True)
    abc.add_requests(requests)

    # Block for a short time to ensure the background thread gets chance to
    # execute
    assert done_event.wait(0.1) is False

    # We should be sure the power command is blocking on the BMP call
    bc.write_fpga_register.assert_called_with(0,
                                              0x5C,
                                              False,
                                              board=10,
                                              frame=0,
                                              cabinet=0)

    # When the BMP call completes, so should the done_event!
    event.set()
    done_event.wait()
def test_set_link_enable(abc, bc, link, fpga, addr, enable, value,
                         side_effect, success):
    # Make sure that the set link command works (and failure is reported)
    e = OnDoneEvent()
    bc.write_fpga_register.side_effect = side_effect
    bc.read_bmp_version.side_effect = mock_read_bmp_version
    requests = AtomicRequests(e)
    requests.link(10, link, enable)
    abc.add_requests(requests)
    e.wait()
    assert e.success is success
    bc.write_fpga_register.assert_called_with(fpga, addr, value, board=10,
                                              frame=0, cabinet=0)
    bc.write_fpga_register.reset_mock()
def test_set_power(abc, bc, power_side_effect, success):
    # Make sure that the set power command works (and failure is reported)
    e = OnDoneEvent()
    bc.power_on.side_effect = power_side_effect
    bc.power_off.side_effect = power_side_effect
    requests = AtomicRequests(e)
    requests.power(10, False)
    abc.add_requests(requests)
    e.wait()
    assert e.success is success
    assert len(bc.power_on.mock_calls) == 0
    bc.power_off.assert_called_with(boards=[10], frame=0, cabinet=0)
    bc.power_off.reset_mock()

    bc.power_on.side_effect = power_side_effect
    bc.power_off.side_effect = power_side_effect
    bc.read_fpga_register.side_effect = mock_read_fpga_register
    bc.read_bmp_version.side_effect = mock_read_bmp_version
    e = OnDoneEvent()
    requests = AtomicRequests(e)
    requests.power(11, True)
    abc.add_requests(requests)
    e.wait()
    assert e.success is success
    bc.power_on.assert_called_with(boards=[11], frame=0, cabinet=0)
    bc.power_on.reset_mock()
    assert len(bc.power_off.mock_calls) == 0
def test_atomic_order(abc, bc):
    # Make sure that requests are run in order
    power_on_event = threading.Event()
    power_off_event = threading.Event()
    link_event = threading.Event()
    bc.power_on.side_effect = (lambda *a, **k: power_on_event.wait(1.0))
    bc.power_off.side_effect = (lambda *a, **k: power_off_event.wait(1.0))
    bc.write_fpga_register.side_effect = (lambda *a, **k: link_event.wait(1.0))
    bc.read_fpga_register.side_effect = mock_read_fpga_register
    bc.read_bmp_version.side_effect = mock_read_bmp_version

    event = OnDoneEvent()
    requests = AtomicRequests(event)
    with abc:
        requests.link(11, Links.east, True)
        requests.power(12, False)
        requests.power(10, True)
        abc.add_requests(requests)

    # Block for a short time to ensure the background thread gets chance to
    # execute
    assert event.wait(0.1) is False

    # Make sure just the power command has been called
    bc.power_on.assert_called_with(boards=[10], frame=0, cabinet=0)
    bc.power_on.reset_mock()
    assert len(bc.power_off.mock_calls) == 0
    assert len(bc.write_fpga_register.mock_calls) == 0

    # Let the first power command complete
    power_on_event.set()

    # Block for a short time to ensure background thread gets chance to execute
    assert event.wait(0.1) is False

    # We should be sure the power command is blocking on the BMP call
    assert len(bc.power_on.mock_calls) == 0
    assert len(bc.power_off.mock_calls) == 0
    bc.write_fpga_register.assert_called_with(0, 0x5C, False, board=11,
                                              frame=0, cabinet=0)
    bc.write_fpga_register.reset_mock()

    # Make BMP call complete and the last event finish
    link_event.set()

    # Block for a short time to ensure background thread gets chance to execute
    assert event.wait(0.1) is False

    # Make sure just the power command has been called a second time (and not
    # the link setting command)
    bc.power_off.assert_called_with(boards=[12], frame=0, cabinet=0)
    bc.power_off.reset_mock()
    assert len(bc.power_on.mock_calls) == 0
    assert len(bc.write_fpga_register.mock_calls) == 0

    # Let the second power command complete
    power_off_event.set()
    event.wait(2.0)
def test_set_power_blocks(abc, bc):
    # Make sure that the set power command can block
    event = threading.Event()
    bc.power_off.side_effect = (lambda *a, **k: event.wait())

    done_event = OnDoneEvent()
    requests = AtomicRequests(done_event)
    requests.power(10, False)
    abc.add_requests(requests)

    # Block for a short time to ensure the background thread gets chance to
    # execute
    assert done_event.wait(0.1) is False

    # We should be sure the power command is blocking on the BMP call
    bc.power_off.assert_called_with(boards=[10], frame=0, cabinet=0)

    # When the BMP call completes, so should the done_event!
    event.set()
    done_event.wait()
    assert done_event.success is True
def test_set_power_dont_merge(abc, bc):
    # Make sure power commands are only merged with those of the same type
    bc.read_fpga_register.side_effect = mock_read_fpga_register
    bc.read_bmp_version.side_effect = mock_read_bmp_version

    event = OnDoneEvent()
    with abc:
        requests = AtomicRequests(event)
        requests.power(10, False)
        requests.power(11, True)
        requests.power(12, False)
        abc.add_requests(requests)

    event.wait()

    assert event.success

    assert bc.power_off.mock_calls == [
        call(boards=[10, 12], frame=0, cabinet=0)
    ]
    assert bc.power_on.mock_calls == [
        call(boards=[11], frame=0, cabinet=0),
    ]
def test_set_power_merge(abc, bc, power_side_effect, success):
    bc.power_off.side_effect = power_side_effect

    # Make sure we can queue up several power commands which will get merged
    # (and any errors duplicated).
    event = OnDoneEvent()
    with abc:
        requests = AtomicRequests(event)
        requests.power(10, False)
        requests.power(11, False)
        requests.power(13, False)
        abc.add_requests(requests)

    event.wait()
    assert event.success is success

    bc.power_off.assert_called_with(boards=[10, 11, 13], frame=0, cabinet=0)
def test_stop_drains(abc, bc):
    # Make sure that the queues are emptied before the stop command is
    # processed
    event = OnDoneEvent()
    bc.read_bmp_version.side_effect = mock_read_bmp_version
    with abc:
        requests = AtomicRequests(event)
        requests.power(10, False)
        requests.link(11, Links.east, False)
        abc.add_requests(requests)
        abc.stop()

    # Both of these should be carried out
    event.wait(0.5)
    assert event.success is True

    # And the loop should stop!
    abc.join()
def test_mock_abc(mock_abc, expected_success):
    """Meta testing: make sure the MockAsyncBMPController works."""
    # Make sure callbacks are called from another thread
    threads = []

    def cb(success):
        threads.append(threading.current_thread())
        assert success is expected_success

    abc = mock_abc("foo")
    abc.success = expected_success

    assert abc.running_theads == 1

    with abc:
        # If made atomically, should all fire at the end...
        requests = AtomicRequests(cb)
        requests.power(None, None)
        requests.link(None, None, None)
        abc.add_requests(requests)

        assert abc.add_requests_calls == [requests]

        # Make sure the background thread gets some execution time
        time.sleep(0.05)

        assert threads == []

    # Make sure the background thread gets some execution time
    time.sleep(0.05)

    assert len(threads) == 1
    assert threads[0] != threading.current_thread()

    abc.stop()
    abc.join()

    assert abc.running_theads == 0