def test_exchange_views(self): p1 = PartialView("First IP", 4, 3) p1.add_peer(PodDescriptor("172.0.1.6", 0)) p1.add_peer(PodDescriptor("172.0.1.3", 2)) p1.add_peer(PodDescriptor("172.0.1.5", 3)) p1.add_peer(PodDescriptor("Second IP", 5)) p2 = PartialView("Second IP", 4, 3) p2.add_peer(PodDescriptor("172.0.1.3", 0)) p2.add_peer(PodDescriptor("172.0.1.5", 1)) p2.add_peer(PodDescriptor("172.0.1.2", 2)) p2.add_peer(PodDescriptor("172.0.1.1", 4)) ######################## # P1 starts the exchange ######################## # 1) Increase by one the age of all neighbors p1.increment() # 2) Select neighbor Q with the highest age among all neighbors. oldest = p1.get_oldest_peer() # 3) Select l - 1 other random neighbors (meaning avoid oldest). request = p1.select_neighbors_for_request(oldest) # 4) Replace Q's entry with a new entry of age 0 and with P's address. request.add_peer_ip(p1.ip, allow_self_ip=True) self.assertTrue(request.is_full()) self.assertEqual(request.size, p1.shuffle_length) ################################################ # P2 receives neighbors and prepares a reply ################################################ reply = p2.select_neighbors_for_reply() self.assertTrue(request.is_full()) self.assertEqual(request.size, p1.shuffle_length) # Note that in p1 the oldest is p2 # p1 and p2 know two peers in common # p2 does not have an entry with p1's ip # p1.merge should: # - Discard 172.0.1.3 and 172.0.1.5 # - Put in unknown list 172.0.1.2, 172.0.1.1 # 6) I remove the oldest peer from my view p1.remove_peer(oldest) p1.merge(request, reply) self.assertTrue(p1.is_full()) for peer in reply.get_peer_list(): self.assertTrue(p1.contains(peer)) self.assertLessEqual(self.partialView.size, self.partialView.limit)
class TestPartialView(unittest.TestCase): @classmethod def setUpClass(cls): pass def setUp(self): self.partialView = PartialView("172.0.1.0") self.descriptors = [] self.ips = [ "172.0.1.1", "172.0.1.2", "172.0.1.3", "172.0.1.4", "172.0.1.5" ] for ip in self.ips: self.descriptors.append(PodDescriptor(ip)) # Limit should be equal to VIEW_LIMIT and shuffle_length should be equal to SHUFFLE_LENGTH def test_set_up_ok(self): self.assertEqual(self.partialView.limit, int(os.environ['VIEW_LIMIT'])) self.assertEqual(self.partialView.shuffle_length, int(os.environ['SHUFFLE_LENGTH'])) # Initial partialView should be empty def test_initial_partial_view_empty(self): self.assertEqual(self.partialView.size, 0) self.assertTrue(self.partialView.is_empty()) # Method is_full should return false if partial view is not full def test_initial_partial_view_should_not_be_full(self): self.assertFalse(self.partialView.is_full()) # Method is_full should return true if partial view is full def test_is_full_should_return_true_if_full(self): for i in range(self.partialView.limit): self.partialView.add_peer(self.descriptors[i]) self.assertTrue(self.partialView.is_full()) self.assertEqual(self.partialView.size, self.partialView.limit) # Method add_peer should return false if peer already contained def test_add_peer_should_return_false_if_peer_already_contained(self): peer = PodDescriptor("A new IP") self.partialView.add_peer(peer) size = self.partialView.size duplicated = PodDescriptor("A new IP") success = self.partialView.add_peer(duplicated) self.assertFalse(success) self.assertEqual(self.partialView.size, size) self.assertEqual(self.partialView.size, len(self.partialView.peer_list)) # Method add_peer should not allow to insert a self entry def test_add_peer_should_not_allow_self_entry(self): ip = "my ip" p1 = PartialView(ip) peer = PodDescriptor(ip) size = self.partialView.size success = p1.add_peer(peer) self.assertFalse(success) self.assertFalse(p1.contains_ip(ip)) self.assertEqual(p1.size, size) # Method add_peer should allow to insert a self entry if forced def test_add_peer_with_allow_self_should_allow_self_entry(self): ip = "my ip" p1 = PartialView(ip) peer = PodDescriptor(ip) size = self.partialView.size success = p1.add_peer(peer, True) self.assertTrue(success) self.assertTrue(p1.contains_ip(ip)) self.assertEqual(p1.size, size + 1) # Method add_peer should increment size if view is not full def test_add_peer_should_increment_size_if_not_full(self): size = self.partialView.size peer = PodDescriptor("A new IP") self.partialView.add_peer(peer) self.assertTrue(self.partialView.contains(peer)) self.assertEqual(self.partialView.size, size + 1) self.assertEqual(self.partialView.size, len(self.partialView.peer_list)) # Method add_peer should not increment size if view is full def test_add_peer_should_not_increment_size_if_full(self): peer = PodDescriptor("A new IP") for i in range(self.partialView.limit): self.partialView.add_peer(self.descriptors[i]) size = self.partialView.size success = self.partialView.add_peer(peer) self.assertFalse(success) self.assertFalse(self.partialView.contains(peer)) self.assertEqual(self.partialView.size, size) self.assertEqual(self.partialView.size, len(self.partialView.peer_list)) # Method add_peer_ip should return false if peer already contained def test_add_peer_ip_should_return_false_if_peer_already_contained(self): peer = "A new IP" self.partialView.add_peer_ip(peer) size = self.partialView.size duplicated = "A new IP" success = self.partialView.add_peer_ip(duplicated) self.assertFalse(success) self.assertEqual(self.partialView.size, size) self.assertEqual(self.partialView.size, len(self.partialView.peer_list)) # Method add_peer_ip should not allow to insert a self entry def test_add_peer_ip_should_not_allow_self_entry(self): ip = "my ip" p1 = PartialView(ip) size = self.partialView.size success = p1.add_peer_ip(ip) self.assertFalse(success) self.assertFalse(p1.contains_ip(ip)) self.assertEqual(p1.size, size) # Method add_peer_ip should allow to insert a self entry if forced def test_add_peer_ip_with_allow_self_should_allow_self_entry(self): ip = "my ip" p1 = PartialView(ip) size = self.partialView.size success = p1.add_peer_ip(ip, True) self.assertTrue(success) self.assertTrue(p1.contains_ip(ip)) self.assertEqual(p1.size, size + 1) # Method add_peer_ip should increment size if view is not full def test_add_peer_ip_should_increment_size_if_not_full(self): size = self.partialView.size peer = "A new IP" self.partialView.add_peer_ip(peer) self.assertTrue(self.partialView.contains_ip(peer)) self.assertEqual(self.partialView.size, size + 1) self.assertEqual(self.partialView.size, len(self.partialView.peer_list)) # Method add_peer_ip should not increment size if view is full def test_add_peer_ip_should_not_increment_size_if_full(self): peer = "A new IP" for i in range(self.partialView.limit): self.partialView.add_peer(self.descriptors[i]) size = self.partialView.size success = self.partialView.add_peer_ip(peer) self.assertFalse(success) self.assertFalse(self.partialView.contains_ip(peer)) self.assertEqual(self.partialView.size, size) self.assertEqual(self.partialView.size, len(self.partialView.peer_list)) # Initial age should be zero def test_initial_age_peer(self): self.partialView.add_peer(PodDescriptor("172.0.1.5")) self.assertEqual(self.partialView.peer_list[0].age, 0) # Initial age should be zero def test_initial_age_peer_ip(self): self.partialView.add_peer_ip("172.0.1.5") self.assertEqual(self.partialView.peer_list[0].age, 0) # Method get_peer_ip_list should return a list of ips def test_get_peer_ip_list_returns_ips(self): for ip in self.ips: self.partialView.add_peer_ip(ip) self.assertEqual(self.partialView.get_peer_ip_list(), self.ips[:self.partialView.limit]) # Initial age should be zero def test_partial_view_size_limit(self): for ip in self.ips: self.partialView.add_peer_ip(ip) self.assertEqual(self.partialView.size, self.partialView.limit) self.assertTrue(self.partialView.is_full()) for i in range(self.partialView.limit): self.assertEqual(self.partialView.peer_list[i].ip, self.ips[i]) def test_contains_return_true_if_contained(self): for descr in self.descriptors: self.partialView.add_peer(descr) for descr in self.descriptors[:self.partialView.size]: self.assertTrue(self.partialView.contains(descr)) for descr in self.descriptors[self.partialView.size:]: self.assertFalse(self.partialView.contains(descr)) def test_contains_ip_return_true_if_contained(self): for ip in self.ips: self.partialView.add_peer_ip(ip) for ip in self.ips[:self.partialView.size]: self.assertTrue(self.partialView.contains_ip(ip)) for ip in self.ips[self.partialView.size:]: self.assertFalse(self.partialView.contains_ip(ip)) # Age should be incremented by one def test_increment(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 1)) self.partialView.add_peer(PodDescriptor("172.0.1.7", 3)) self.partialView.increment() self.assertEqual(self.partialView.peer_list[0].age, 2) self.assertEqual(self.partialView.peer_list[1].age, 4) # Sort should sort view by peer's age def test_sort(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.8", 1)) self.partialView.sort() self.assertEqual(self.partialView.peer_list[0].ip, "172.0.1.8") self.assertEqual(self.partialView.peer_list[1].ip, "172.0.1.5") self.assertEqual(self.partialView.peer_list[2].ip, "172.0.1.4") # Method sample_descriptors should return an empty list if view is empty def test_sample_descriptors_should_return_empty_list_if_empty_view(self): sample = self.partialView.sample_descriptors(3) self.assertEqual(len(sample), 0) self.assertEqual(sample, []) self.assertTrue(isinstance(sample, list)) # Method sample_descriptors should return a list of size element if the view's size is less than the limit given as parameter def test_sample_descriptors_should_return_less_than_limit_peers_if_size_less_than_limit( self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) size = self.partialView.size sample = self.partialView.sample_descriptors(3) self.assertEqual(len(sample), size) # Method sample_descriptors should return a list of limit peers despite the size of the the view is greater then limit def test_sample_descriptors_should_return_no_more_than_limit_peers(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.9", 4)) limit = 2 size = self.partialView.size sample = self.partialView.sample_descriptors(limit) self.assertNotEqual(limit, size) self.assertEqual(len(sample), limit) # Method sample_descriptors should return a list of 1 peer and avoid the peer given as parameter def test_sample_descriptors_with_avoid_peer_more_than_limit(self): to_avoid = PodDescriptor("172.0.1.9", 4) self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(to_avoid) limit = 1 sample = self.partialView.sample_descriptors(limit, to_avoid) self.assertEqual(len(sample), limit) self.assertFalse(to_avoid in sample) # Method sample_descriptors should return a list of 2 peers and avoid the peer given as parameter def test_sample_descriptors_with_avoid_peer_less_than_limit(self): to_avoid = PodDescriptor("172.0.1.9", 4) self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(to_avoid) limit = 3 sample = self.partialView.sample_descriptors(limit, to_avoid) self.assertEqual(len(sample), 2) self.assertFalse(to_avoid in sample) # Method sample_descriptors should return a list of 3 peers if the peer to avoid is not contained in the view def test_sample_descriptors_with_avoid_peer_not_in_view(self): to_avoid = PodDescriptor("172.0.1.9", 4) self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.10", 8)) limit = 3 sample = self.partialView.sample_descriptors(limit, to_avoid) self.assertEqual(len(sample), limit) self.assertFalse(to_avoid in sample) # Method sample_ips should return a list of 3 ips def test_sample_ips_should_return_a_list_of_ips(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.10", 8)) limit = 3 sample = self.partialView.sample_ips(limit) self.assertIn("172.0.1.5", sample) self.assertIn("172.0.1.4", sample) self.assertIn("172.0.1.10", sample) # Method sample_ips should return a list of 3 ips loadable by epto # def test_sample_ips_should_return_a_list_of_ips_loadable_by_epto(self): # self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) # self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) # self.partialView.add_peer(PodDescriptor("172.0.1.10", 8)) # sample = json.dumps(self.partialView.sample_ips(2)) # # EpTO's code when EpTO invokes get_k_view() # view = [ip.encode('ascii', 'ignore') for ip in json.loads(sample)] # for destination in view: # self.assertIsInstance(destination, str) # self.assertIn(destination, self.partialView.get_peer_ip_list()) # Test the exchange of views. # P1 plays the role of P while P2 plays the role of Q described in comments def test_exchange_views(self): p1 = PartialView("First IP", 4, 3) p1.add_peer(PodDescriptor("172.0.1.6", 0)) p1.add_peer(PodDescriptor("172.0.1.3", 2)) p1.add_peer(PodDescriptor("172.0.1.5", 3)) p1.add_peer(PodDescriptor("Second IP", 5)) p2 = PartialView("Second IP", 4, 3) p2.add_peer(PodDescriptor("172.0.1.3", 0)) p2.add_peer(PodDescriptor("172.0.1.5", 1)) p2.add_peer(PodDescriptor("172.0.1.2", 2)) p2.add_peer(PodDescriptor("172.0.1.1", 4)) ######################## # P1 starts the exchange ######################## # 1) Increase by one the age of all neighbors p1.increment() # 2) Select neighbor Q with the highest age among all neighbors. oldest = p1.get_oldest_peer() # 3) Select l - 1 other random neighbors (meaning avoid oldest). request = p1.select_neighbors_for_request(oldest) # 4) Replace Q's entry with a new entry of age 0 and with P's address. request.add_peer_ip(p1.ip, allow_self_ip=True) self.assertTrue(request.is_full()) self.assertEqual(request.size, p1.shuffle_length) ################################################ # P2 receives neighbors and prepares a reply ################################################ reply = p2.select_neighbors_for_reply() self.assertTrue(request.is_full()) self.assertEqual(request.size, p1.shuffle_length) # Note that in p1 the oldest is p2 # p1 and p2 know two peers in common # p2 does not have an entry with p1's ip # p1.merge should: # - Discard 172.0.1.3 and 172.0.1.5 # - Put in unknown list 172.0.1.2, 172.0.1.1 # 6) I remove the oldest peer from my view p1.remove_peer(oldest) p1.merge(request, reply) self.assertTrue(p1.is_full()) for peer in reply.get_peer_list(): self.assertTrue(p1.contains(peer)) self.assertLessEqual(self.partialView.size, self.partialView.limit) # Test the exchange of views. # P1 plays the role of P while P2 plays the role of Q described in comments # def test_exchange_views_2(self): # # p1 = PartialView("First IP", 4, 3) # p1.add_peer(PodDescriptor("172.0.1.6", 0)) # p1.add_peer(PodDescriptor("172.0.1.3", 2)) # p1.add_peer(PodDescriptor("172.0.1.5", 3)) # p1.add_peer(PodDescriptor("Second IP", 5)) # # p2 = PartialView("Second IP", 4, 3) # p2.add_peer(PodDescriptor("172.0.1.3", 0)) # p2.add_peer(PodDescriptor("172.0.1.5", 1)) # p2.add_peer(PodDescriptor("172.0.1.2", 2)) # p2.add_peer(PodDescriptor("First IP", 4)) # # ######################## # # P1 starts the exchange # ######################## # # # 1) Increase by one the age of all neighbors # p1.increment() # # 2) Select neighbor Q with the highest age among all neighbors. # oldest = p1.get_oldest_peer() # # 3) Select l - 1 other random neighbors (meaning avoid oldest). # request = p1.select_neighbors_for_request(oldest) # # 4) Replace Q's entry with a new entry of age 0 and with P's address. # request.add_peer_ip(p1.ip, allow_self_ip=True) # # self.assertTrue(request.is_full()) # self.assertEqual(request.size, p1.shuffle_length) # # ################################################ # # P2 receives neighbors and prepares a reply # ################################################ # # reply = p2.select_neighbors_for_reply() # # self.assertTrue(request.is_full()) # self.assertEqual(request.size, p1.shuffle_length) # # # Note that in p1 the oldest is p2 # # p1 and p2 know two peers in common # # p2 does have an entry with p1's ip # # p1.merge should: # # - Discard 172.0.1.3 and 172.0.1.5 because are well known # # - Discard First IP because self ip is not allowed # # # 6) I remove the oldest peer from my view # p1.remove_peer(oldest) # p1.merge(request, reply) # # for peer in reply.get_peer_list(): # if peer != p1.ip: # self.assertTrue(p1.contains(peer)) # # self.assertLessEqual(self.partialView.size, self.partialView.limit) # Method get_oldest_peer should return a PodDescriptor def test_get_oldest_peer_should_return_none_if_empty_view(self): oldest = self.partialView.get_oldest_peer() self.assertEqual(oldest, None) # Method get_oldest_peer should return a PodDescriptor def test_get_oldest_peer_should_return_a_pod_descriptor(self): self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 1)) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) oldest = self.partialView.get_oldest_peer() self.assertTrue(isinstance(oldest, PodDescriptor)) # Method get_oldest_peer should return the peer with the highest age def test_get_oldest_peer(self): self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 1)) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) oldest = self.partialView.get_oldest_peer() self.assertEqual(oldest.ip, "172.0.1.5") self.assertEqual(oldest.age, 4) def test_select_neighbors_for_request_should_return_a_non_full_view(self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_request(oldest) self.assertFalse(neighbors.is_full()) self.assertEqual(neighbors.size, neighbors.shuffle_length - 1) def test_select_neighbors_for_request_should_not_contain_oldest_peer(self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_request(oldest) self.assertFalse(neighbors.contains(oldest)) def test_select_neighbors_for_request_and_add_peer_should_return_full_view( self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_request(oldest) neighbors.add_peer_ip(self.partialView.ip, allow_self_ip=True) self.assertEqual(neighbors.size, self.partialView.shuffle_length) self.assertTrue(neighbors.is_full()) def test_select_neighbors_for_reply_should_return_a_full_view(self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_reply(oldest) self.assertTrue(neighbors.is_full()) self.assertEqual(neighbors.size, neighbors.shuffle_length) def test_select_neighbors_for_reply_should_contain_avoid_peer_if_size_eq_shuffle_length( self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) neighbors = self.partialView.select_neighbors_for_reply(oldest) self.assertTrue(neighbors.is_full()) self.assertEqual(neighbors.size, neighbors.shuffle_length) def test_select_neighbors_for_reply_should_not_contain_oldest_peer(self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_reply(oldest) self.assertFalse(neighbors.contains(oldest)) def test_empty_partial_view_to_json(self): jsonized = self.partialView.to_json() self.assertEqual( jsonized, { "ip": "172.0.1.0", "limit": 3, "shuffle_length": 2, "peer_list": [], "size": 0 }) def test_unmarshal_partial_view(self): for ip in self.ips: self.partialView.add_peer_ip(ip) jsonized = self.partialView.to_json() partial_view = PartialView.from_dict(jsonized) self.assertIsInstance(partial_view, PartialView) for peer in partial_view.peer_list: self.assertIsInstance(peer, PodDescriptor) self.assertEqual(partial_view.ip, self.partialView.ip) self.assertEqual(partial_view.size, 3) self.assertEqual(partial_view.limit, 3) for i in range(self.partialView.limit): self.assertEqual(partial_view.peer_list[i].ip, self.descriptors[i].ip) self.assertEqual(partial_view.peer_list[i].age, self.descriptors[i].age) # Every time the view size is checked if it is equal to the actual size def tearDown(self): self.assertEqual(len(self.partialView.peer_list), self.partialView.size)