def test_e2_replace_br_is_not_nbr(self):
        # Advertise a route that replaces the best route but does not become
        # the new best route
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 2 sources : A and B
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')

        # Source A advertises a route1 for NLRI1
        self._append_call("RE1")
        route1_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 300)
        # Source B advertises a route2. Route1 is better than Route2
        self._append_call("RE2")
        route2_nrli1b = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_b, t.NH1, 200)
        # Source A advertises a route3 for NLRI1. Route3 replaces Route1.
        # Route2 is better than route3.
        self._append_call("RE3")
        route3_nrli1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 100,
                                              route1_nlri1a.route_entry)
        # Source B withdraws route2 for NLRI1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_b, t.NH1, 200)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = [
            "RE1", t.NBR, "RE2", "RE3", t.NBR, t.BRR, "RE4", t.NBR, t.BRR
        ]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(3, self.tracker_worker.new_best_route.call_count,
                         '3 new new_best_route calls for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1_nlri1a.route_entry),
                           (t.NLRI1, route2_nrli1b.route_entry),
                           (t.NLRI1, route3_nrli1a.route_entry)])
        self.assertEqual(2, self.tracker_worker.best_route_removed.call_count,
                         '2 best_route_removed calls for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1_nlri1a.route_entry, False),
             (t.NLRI1, route2_nrli1b.route_entry, False)])
    def test_e5_replace_br_is_nbr_equal(self):
        # Same as E3, but the route that replaces our current best compares
        # equally to the two initially less preferred routes, and becomes best
        # route with them
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 3 sources: A, B and C
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')
        worker_c = worker.Worker(mock.Mock(), 'worker.Worker-C')

        # Source A advertises route1 for NLRI1
        route1 = self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                                       [t.RT1, t.RT2], worker_a, t.NH1, 300)

        # We will only check events after this first one
        # to allow for a order-independent test after RE4
        del self.tracker_worker.new_best_route.call_args_list[:]

        # Source B advertises route2 for NLRI1 : route1 is better than route2
        self._append_call("RE2")
        route2 = self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                                       [t.RT1, t.RT2], worker_b, t.NH1, 200)
        # Source C advertises also route2
        self._append_call("RE3")
        route3 = self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                                       [t.RT1, t.RT2], worker_c, t.NH2, 200)
        # Source A advertises route3 which replaces route1
        self._append_call("RE4")
        route4 = self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                                       [t.RT1, t.RT2], worker_a, t.NH3, 200,
                                       route1.route_entry)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = [
            t.NBR, "RE2", "RE3", "RE4", t.NBR, t.NBR, t.NBR, t.BRR
        ]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route2.route_entry),
                           (t.NLRI1, route3.route_entry),
                           (t.NLRI1, route4.route_entry)], False)

        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1.route_entry, False)])
    def test_c1_route1_best_route(self):
        # Route1 is the best route
        # Mock objects
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 2 sources : A and B
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')

        # Source A advertises a route1 for NLRI1
        self._append_call("RE1")
        route1_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 300)
        # Source B advertises a route2 for NLRI1 with different attributes.
        # Route1 is better than Route2
        self._append_call("RE2")
        route2_nlri1b = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_b, t.NH1, 200)
        # Source A withdraws route1 for NLRI1
        self._append_call("RE3")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 300)
        # Source B withdraws route2 for NLRI1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_b, t.NH1, 200)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = [
            "RE1", t.NBR, "RE2", "RE3", t.NBR, t.BRR, "RE4", t.BRR
        ]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(2, self.tracker_worker.new_best_route.call_count,
                         '2 new new_best_route calls for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1_nlri1a.route_entry),
                           (t.NLRI1, route2_nlri1b.route_entry)])
        self.assertEqual(2, self.tracker_worker.best_route_removed.call_count,
                         '2 best_route_removed calls for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1_nlri1a.route_entry, False),
             (t.NLRI1, route2_nlri1b.route_entry, True)])
    def test_e3_replace_br_is_not_nbr(self):
        # Advertise a route that replaces the best route but does not become
        # the new best route
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 3 sources: A, B and C
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')
        worker_c = worker.Worker(mock.Mock(), 'worker.Worker-C')

        # Source A advertises route1 for NLRI1
        self._append_call("RE1")
        route1_nlri1 = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_a,
                                             t.NH1, 300)
        # Source B advertises route2 for NLRI1 : route1 is better than route2
        self._append_call("RE2")
        route2_nlri1 = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_b,
                                             t.NH1, 200)
        # Source C advertises also route2
        self._append_call("RE3")
        self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                              [t.RT1, t.RT2], worker_c, t.NH1, 200)
        # Source A advertises route3 which replaces route1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100,
                              route1_nlri1.route_entry)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = ["RE1", t.NBR, "RE2", "RE3", "RE4", t.NBR, t.BRR]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(2, self.tracker_worker.new_best_route.call_count,
                         '2 new best route call for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1_nlri1.route_entry),
                           (t.NLRI1, route2_nlri1.route_entry)])
        self.assertEqual(1, self.tracker_worker.best_route_removed.call_count,
                         '1 best_route_removed call for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1_nlri1.route_entry)])
    def test_b2_is_not_the_current_best_route(self):
        # The route which is advertised by an other source is not the current
        # best route but will become the best route
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 3 sources: A, B and C
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')
        worker_c = worker.Worker(mock.Mock(), 'worker.Worker-C')

        # Source A advertises route1 for NLRI1
        self._append_call("RE1")
        route1Nlri1 = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                            t.NLRI1, [t.RT1, t.RT2], worker_a,
                                            t.NH1, 300)
        # Source B advertises route2 for NLRI1 : route1 is better than route2
        self._append_call("RE2")
        route2Nlri1 = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                            t.NLRI1, [t.RT1, t.RT2], worker_b,
                                            t.NH1, 200)
        # Source C advertises also route2
        self._append_call("RE3")
        self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                              [t.RT1, t.RT2], worker_c, t.NH1, 200)
        # Source A withdraws route1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 300)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = ["RE1", t.NBR, "RE2", "RE3", "RE4", t.NBR, t.BRR]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(2, self.tracker_worker.new_best_route.call_count,
                         '2 new best route call for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1Nlri1.route_entry),
                           (t.NLRI1, route2Nlri1.route_entry)])
        self.assertEqual(1, self.tracker_worker.best_route_removed.call_count,
                         '1 best_route_removed call for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1Nlri1.route_entry, False)])
    def test_b1_is_the_current_best_route(self):
        # The route which is advertised by another source is the current best
        # route
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 2 sources: A and B
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')

        # Source A advertises a route for NLRI1
        self._append_call("RE1")
        route_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_a,
                                             t.NH1, 100)
        # Source B advertises the same route for NLRI1
        self._append_call("RE2")
        route_nlri1B = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_b,
                                             t.NH1, 100)
        # Source A withdraws the route for NLRI1
        self._append_call("RE3")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)
        # Source B withdraws the route for NLRI1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_b, t.NH1, 100)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        self.assertEqual(1, self.tracker_worker.new_best_route.call_count,
                         '1 new best route call for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route_nlri1a.route_entry)])
        self.assertEqual(1, self.tracker_worker.best_route_removed.call_count,
                         '1 best_route_removed call for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route_nlri1B.route_entry, True)])

        expected_calls = ["RE1", t.NBR, "RE2", "RE3", "RE4", t.BRR]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')
    def test_a2_different_nlri_different_source(self):
        # 2 sources A and B advertise and withdraw routes for different NLRI.
        # Mock objects
        self.tracker_worker.new_best_route = mock.Mock()
        self.tracker_worker.best_route_removed = mock.Mock()

        # 2 sources: A and B
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')
        # Source A advertises a route for NLRI1
        route_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_a,
                                             t.NH1, 100)
        # Source B advertises a route for NLRI2
        route_nlri2B = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI2, [t.RT1, t.RT2], worker_b,
                                             t.NH1, 100)
        # Source A withdraws the route for NLRI1
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)
        # Source B withdraws the route for NLRI2
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI2,
                              [t.RT1, t.RT2], worker_b, t.NH1, 100)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        self.assertEqual(
            2, self.tracker_worker.new_best_route.call_count,
            '2 new_best_route calls: 1 for NLRI1 and 1 for NLRI2')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route_nlri1a.route_entry),
                           (t.NLRI2, route_nlri2B.route_entry)])
        self.assertEqual(
            2, self.tracker_worker.best_route_removed.call_count,
            '2 best_route_removed calls: 1 for NLRI1 and 1 for '
            'NLRI2')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route_nlri1a.route_entry, True),
             (t.NLRI2, route_nlri2B.route_entry, True)])
    def test_d1_ecmp_routes(self):
        # ECMP routes are routes advertised by the same worker with the same
        # LP and different NH
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 1 source: A
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')

        # Source A advertises a route1 for NLRI1
        self._append_call("RE1")
        route1_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 100)
        # Source A advertises a route2 for NLRI1. route2 is equal to route1
        # with compare_routes, but the next_hop are different
        self._append_call("RE2")
        route2_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH2, 100)
        # Source A withdraws route1 for NLRI1
        self._append_call("RE3")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)
        # Source A withdraws route2 for NLRI1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH2, 100)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = [
            "RE1", t.NBR, "RE2", t.NBR, "RE3", t.BRR, "RE4", t.BRR
        ]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(2, self.tracker_worker.new_best_route.call_count,
                         '2 new new_best_route calls for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1_nlri1a.route_entry),
                           (t.NLRI1, route2_nlri1a.route_entry)])
        self.assertEqual(2, self.tracker_worker.best_route_removed.call_count,
                         '2 best_route_removed calls for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1_nlri1a.route_entry, False),
             (t.NLRI1, route2_nlri1a.route_entry, True)])
    def test_a4_withdraw_nlri_not_known(self):
        # A source A withdraws a route that does not exist.
        self.tracker_worker.new_best_route = mock.Mock()
        self.tracker_worker.best_route_removed = mock.Mock()

        # 1 source: A
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        # Source A withdraws a route for NLRI1 which is not known by
        # tracker_worker
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)

        # Check calls to new_best_route and best_route_removed
        self.assertEqual(0, self.tracker_worker.new_best_route.call_count,
                         'new_best_route should not have been called')
        self.assertEqual(0, self.tracker_worker.best_route_removed.call_count,
                         'best_route_removed should not have been called')
    def test_max_lp_is_best(self):
        worker_a = worker.Worker(mock.Mock(), 'worker')

        route_lp55 = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                           t.NLRI1, [t.RT1, t.RT2], worker_a,
                                           t.NH1, 55).route_entry

        route_lp45 = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                           t.NLRI1, [t.RT1, t.RT2], worker_a,
                                           t.NH1, 45).route_entry

        self.assertTrue(
            tracker_worker.compare_ecmp(mock.Mock(), route_lp55, route_lp45) >
            0)

        self.assertTrue(
            tracker_worker.compare_no_ecmp(mock.Mock(), route_lp55, route_lp45)
            > 0)
    def test_e1_replace_br_is_nbr(self):
        # Advertise a route that replaces the best route and becomes the new
        # best route
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 1 source: A
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')

        # Source A advertises a route1 for NLRI1
        self._append_call("RE1")
        route1_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 200)
        # Source A advertises a route2 for NLRI1. Route1 is better than Route2
        # BUT Route2 replaces Route1
        self._append_call("RE2")
        route2_nrli1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 100,
                                              route1_nlri1a.route_entry)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = ["RE1", t.NBR, "RE2", t.NBR, t.BRR]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(2, self.tracker_worker.new_best_route.call_count,
                         '2 new new_best_route calls for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1_nlri1a.route_entry),
                           (t.NLRI1, route2_nrli1a.route_entry)])
        self.assertEqual(1, self.tracker_worker.best_route_removed.call_count,
                         '1 best_route_removed call for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1_nlri1a.route_entry, False)])
    def test_a3_same_nlri_same_source(self):
        # A source A advertises the same route for the same NLRI
        # Mock objects
        self.tracker_worker.new_best_route = mock.Mock()
        self.tracker_worker.best_route_removed = mock.Mock()

        # 1 source: A
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        # Source A advertises a route for NLRI1
        route_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_a,
                                             t.NH1, 100)
        # Source A advertises the same route for NLRI1
        self._new_route_event(engine.RouteEvent.ADVERTISE, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        self.assertEqual(1, self.tracker_worker.new_best_route.call_count,
                         'expected 1 new_best_route call for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route_nlri1a.route_entry),
                           (t.NLRI1, route_nlri1a.route_entry)])
Exemplo n.º 13
0
    def test_a1_different_nlri_same_source(self):
        # A source A advertises and withdraws routes for different NLRI.
        # Mock objects
        self.tracker_worker._new_best_route = mock.Mock()
        self.tracker_worker._best_route_removed = mock.Mock()

        # Only 1 source A
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        # Source A advertises a route for NLRI1
        route_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI1, [t.RT1, t.RT2], worker_a,
                                             t.NH1, 100)
        # Source A advertises a route for NLRI2
        route_nlri2a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                             t.NLRI2, [t.RT1, t.RT2], worker_a,
                                             t.NH1, 100)
        # Source A withdraws the route for NLRI1
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)
        # Source A withdraws the route for NLRI2
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI2,
                              [t.RT1, t.RT2], worker_a, t.NH1, 100)

        # Check calls and arguments list to _new_best_route and
        # _best_route_removed
        self.assertEqual(2, self.tracker_worker._new_best_route.call_count,
                         '2 new best routes: 1 for NLRI1 and 1 for NLRI2')
        self._check_calls(self.tracker_worker._new_best_route.call_args_list,
                          [(t.NLRI1, route_nlri1a.route_entry),
                           (t.NLRI2, route_nlri2a.route_entry)])
        self.assertEqual(2, self.tracker_worker._best_route_removed.call_count,
                         '2 old routes removed: 1 for NLRI1 and 1 for NLRI2')
        self._check_calls(
            self.tracker_worker._best_route_removed.call_args_list,
            [(t.NLRI1, route_nlri1a.route_entry, True),
             (t.NLRI2, route_nlri2a.route_entry, True)])
    def test_c3_select_new_best_route_among_several(self):
        # When current best route is withdrawn, the new best route should be
        # selected among several routes
        self.tracker_worker.new_best_route = mock.Mock(
            side_effect=self._call_list(t.NBR))
        self.tracker_worker.best_route_removed = mock.Mock(
            side_effect=self._call_list(t.BRR))

        # 3 sources: A, B and C
        worker_a = worker.Worker(mock.Mock(), 'worker.Worker-A')
        worker_b = worker.Worker(mock.Mock(), 'worker.Worker-B')
        worker_c = worker.Worker(mock.Mock(), 'worker.Worker-C')

        # Source A advertises a route1 for NLRI1
        self._append_call("RE1")
        route1_nlri1a = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_a, t.NH1, 300)
        # Source B advertises a route2 for NLRI1. Route1 is better than Route2
        self._append_call("RE2")
        route2_nlri1b = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_b, t.NH1, 200)
        # Source C advertises a route3 for NLRI1. Route2 is better than Route3
        self._append_call("RE3")
        route3_nlri1c = self._new_route_event(engine.RouteEvent.ADVERTISE,
                                              t.NLRI1, [t.RT1, t.RT2],
                                              worker_c, t.NH1, 100)
        # Source A withdraws route1 for NLRI1
        self._append_call("RE4")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_a, t.NH1, 300)
        # Source B withdraws route2 for NLRI1
        self._append_call("RE5")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_b, t.NH1, 200)
        # Source C withdraws route3 for NLRI1
        self._append_call("RE6")
        self._new_route_event(engine.RouteEvent.WITHDRAW, t.NLRI1,
                              [t.RT1, t.RT2], worker_c, t.NH1, 100)

        # Check calls and arguments list to new_best_route and
        # best_route_removed
        expected_calls = [
            "RE1", t.NBR, "RE2", "RE3", "RE4", t.NBR, t.BRR, "RE5", t.NBR,
            t.BRR, "RE6", t.BRR
        ]
        self.assertEqual(expected_calls, self._calls, 'Wrong call sequence')

        self.assertEqual(3, self.tracker_worker.new_best_route.call_count,
                         '3 new new_best_route calls for NLRI1')
        self._check_calls(self.tracker_worker.new_best_route.call_args_list,
                          [(t.NLRI1, route1_nlri1a.route_entry),
                           (t.NLRI1, route2_nlri1b.route_entry),
                           (t.NLRI1, route3_nlri1c.route_entry)])
        self.assertEqual(3, self.tracker_worker.best_route_removed.call_count,
                         '3 best_route_removed calls for NLRI1')
        self._check_calls(
            self.tracker_worker.best_route_removed.call_args_list,
            [(t.NLRI1, route1_nlri1a.route_entry, False),
             (t.NLRI1, route2_nlri1b.route_entry, False),
             (t.NLRI1, route3_nlri1c.route_entry, True)])