def test_receiving_automatic_sync_message():

    c = SynchC("test", ["bar"])
    c.on_new_cycle = MagicMock()
    c.start()

    sync_msg = SynchronizationMsg()
    sync_msg.cycle_id = 0
    c.on_message("bar", sync_msg, 42)

    # Received a sync message from our single neighbor, we should switch cycle
    assert c.cycle_count == 1
    c.on_new_cycle.assert_called_once_with({}, 0)
def test_sending_automatic_cycle_sync_message_2_neighbors():

    c = SynchC("test", ["bar", "yup"])

    def on_cycle(messages, cycle_id):
        return [("bar", FooMsg(3))]

    c.on_new_cycle = on_cycle

    c.start()
    c.message_sender.reset_mock()  # reset startup messages

    # When receiving a message from all neighbors, we switch to the next cycle and
    # should thus send an automatic sync message for this cycle to yup, as we did not
    # send any algo-level message to him on `on_cycle`.
    msgbar1 = FooMsg(1)
    msgbar1.cycle_id = 0
    c.on_message("bar", msgbar1, 42)

    msgyup1 = FooMsg(1)
    msgyup1.cycle_id = 0
    c.on_message("yup", msgyup1, 42)

    c.message_sender.assert_any_call("test", "yup", SynchronizationMsg(), None,
                                     None)
    assert c.message_sender.call_count == 2
def test_on_start_is_a_cycle_no_message_during_startup():
    # The startup phase is considered as cycle 0
    # If computation do not send any algo messages, synchronization messages must be
    # sent instead.

    c1 = SynchC("t1", ["t2"])
    c1.on_new_cycle = MagicMock()

    c2 = SynchC("t2", ["t1"])
    c2.on_new_cycle = MagicMock()

    assert c1.current_cycle == 0
    assert c2.current_cycle == 0

    # Only c1 is started, all computations stays in cycle 0 until all
    # computations have started
    c1.start()
    assert c1.current_cycle == 0 and c1.started == True
    assert c2.current_cycle == 0 and c2.started == False
    c1.on_new_cycle.assert_not_called()
    c2.on_new_cycle.assert_not_called()
    # C1 did not send any algo-level message during startup: a sync message is sent instead:
    c1.message_sender.assert_any_call("t1", "t2", SynchronizationMsg(), None,
                                      None)

    c2.start()
    assert c1.current_cycle == 0 and c1.started == True
    assert c2.current_cycle == 0 and c2.started == True
    # c2 did not send any algo-level message during startup: a sync message is sent instead:
    c2.message_sender.assert_any_call("t2", "t1", SynchronizationMsg(), None,
                                      None)

    # Deliver manually the messages we just asserted.
    # all computations move to cycle 1 and the message sent during startup are received.
    msg = SynchronizationMsg()
    msg.cycle_id = 0
    c1.on_message("t2", msg, 42)
    c2.on_message("t1", msg, 42)

    assert c1.current_cycle == 1
    assert c2.current_cycle == 1
    c1.on_new_cycle.assert_any_call({}, 0)
    c2.on_new_cycle.assert_any_call({}, 0)
def test_on_start_is_a_cycle_some_message_during_startup():
    # In this test, c1 send a message during startup while c2 does not
    # at the end of startup, both computations must still move to cycle 1

    c1 = SynchC("t1", ["t2"])
    c1.on_new_cycle = MagicMock()

    def on_start_c1(self):
        self.started = True
        self.post_msg("t2", FooMsg(1))

    c1.on_start = types.MethodType(on_start_c1, c1)

    c2 = SynchC("t2", ["t1"])
    c2.on_new_cycle = MagicMock()

    # Only C1 is started, all computations stays in cycle 0 until all
    # computations have started
    c1.start()
    assert c1.current_cycle == 0 and c1.started == True
    assert c2.current_cycle == 0 and c2.started == False
    c1.on_new_cycle.assert_not_called()
    c2.on_new_cycle.assert_not_called()
    # Check the startup message to c2 has been sent:
    msg1 = FooMsg(1)
    msg1.cycle_id = 0
    c1.message_sender.assert_any_call("t1", "t2", msg1, None, None)

    c2.start()
    # Check the startup message to c1 has been sent:
    msg2 = SynchronizationMsg()
    msg2.cycle_id = 0
    c2.message_sender.assert_any_call("t2", "t1", msg2, None, None)

    # Deliver manually the messages we just asserted:
    c1.on_message("t2", msg2, 42)
    c2.on_message("t1", msg1, 42)

    assert c1.current_cycle == 1
    assert c2.current_cycle == 1
    c1.on_new_cycle.assert_any_call({}, 0)
    c2.on_new_cycle.assert_any_call({"t1": (msg1, 42)}, 0)
def test_sending_automatic_cycle_sync_message():

    c = SynchC("test", ["bar"])

    def on_cycle(messages, cycle_id):
        # We do not send any algo-level message in this cycle,
        # but, when the cycle is over,  a sync message but still be sent to our
        # neighbors so that they can switch to the next cycle.
        pass

    c.on_new_cycle = on_cycle

    c.start()
    c.message_sender.reset_mock()  # reset startup messages

    # When receiving a message from bar, we switch to the next cycle and
    # should thus send an automatic sync message for this cycle.
    msgbar1 = FooMsg(1)
    msgbar1.cycle_id = 0
    c.on_message("bar", msgbar1, 42)

    c.message_sender.assert_called_once_with("test", "bar",
                                             SynchronizationMsg(), None, None)