From fae6bc00ca39681265836927ebb749aa1ef4fc08 Mon Sep 17 00:00:00 2001 From: Tanya Date: Sun, 16 Jul 2023 10:10:47 +0300 Subject: [PATCH] Fixed corner-case bug in CanonicalHyperCubeSet. (#546) * Fixed corner-case bug in CanonicalHyperCubeSet. Signed-off-by: Tanya * additional updates Signed-off-by: adisos * Removed unnecessary data from the test. Signed-off-by: Tanya --------- Signed-off-by: Tanya Signed-off-by: adisos Co-authored-by: adisos --- nca/CoreDS/CanonicalHyperCubeSet.py | 15 ++++--- .../testCanonicalHyperCubeSet.py | 44 +++++++++++++++++++ .../testCanonicalHyperCubeSetNew.py | 28 ++++++++++++ 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/nca/CoreDS/CanonicalHyperCubeSet.py b/nca/CoreDS/CanonicalHyperCubeSet.py index ad556817..a64c8b81 100644 --- a/nca/CoreDS/CanonicalHyperCubeSet.py +++ b/nca/CoreDS/CanonicalHyperCubeSet.py @@ -541,20 +541,23 @@ def _contained_in_aux(self, other, all_active_dims): # noqa: C901 common_part = current_layer_0 & other_layer has_common_part = bool(common_part) if has_common_part: + # if it's not last dim for both self and other, determine containment recursively if not self._is_last_dimension() and not other._is_last_dimension() and \ not (self.layers[layer])._contained_in_aux(other_sub_elem, all_active_dims[1:]): return False + # if it's last dim for self but not for other: the remaining of other should be entire cube + if self._is_last_dimension() and not other._is_last_dimension() and \ + not other_sub_elem._is_sub_elem_entire_sub_space(): + return False + # if it's the last dim for other but not for self -> containment is satisfied on this part + # at this point, sub-object from common_part is contained remaining = current_layer_0 - common_part if remaining: # continue exploring other's cubes for containment of the remaining part from self current_layer_0 = remaining else: - if self._is_last_dimension() and not other._is_last_dimension(): - # if it's last dim for self but not for other: the remaining of other should be entire cube - if other_sub_elem._is_sub_elem_entire_sub_space(): - is_subset_count += 1 - else: - is_subset_count += 1 + # count current cube (from current_layer_0) as contained in other + is_subset_count += 1 break return is_subset_count == len(self.layers) diff --git a/tests/classes_unit_tests/testCanonicalHyperCubeSet.py b/tests/classes_unit_tests/testCanonicalHyperCubeSet.py index 4ec26a6a..d270ca41 100644 --- a/tests/classes_unit_tests/testCanonicalHyperCubeSet.py +++ b/tests/classes_unit_tests/testCanonicalHyperCubeSet.py @@ -1434,6 +1434,50 @@ def tearDown(self): # undo changes this test did to DimensionsManager singleton DimensionsManager.reset() + def test_contained_in_issue(self): + my_dimensions1 = ["x", "y"] + my_dimensions2 = ["x", "y", "z"] + cube_values_1 = [CanonicalIntervalSet.get_interval_set(2, 2), + CanonicalIntervalSet.get_interval_set(1, 3)] + conns1 = CanonicalHyperCubeSet.create_from_cube(my_dimensions2, cube_values_1, my_dimensions1) + cube_values_2 = [ + CanonicalIntervalSet.get_interval_set(3, 3), + CanonicalIntervalSet.get_interval_set(1, 1) + ] + conns1.add_cube(cube_values_2, my_dimensions1) + + cube_values_3 = [ + CanonicalIntervalSet.get_interval_set(2, 2), + CanonicalIntervalSet.get_interval_set(2, 3), + CanonicalIntervalSet.get_interval_set(1, 100), + ] + conns2 = CanonicalHyperCubeSet.create_from_cube(my_dimensions2, cube_values_3, my_dimensions2) + conns3 = CanonicalHyperCubeSet.create_from_cube(my_dimensions2, cube_values_3, my_dimensions2) + + cube_values_4 = [ + CanonicalIntervalSet.get_interval_set(2, 2), + CanonicalIntervalSet.get_interval_set(1, 1) + ] + cube_values_5 = [ + CanonicalIntervalSet.get_interval_set(3, 3), + CanonicalIntervalSet.get_interval_set(1, 1) + ] + cube_values_6 = [ + CanonicalIntervalSet.get_interval_set(2, 3), + CanonicalIntervalSet.get_interval_set(1, 1) + ] + conns2.add_cube(cube_values_4, my_dimensions1) + conns2.add_cube(cube_values_5, my_dimensions1) + conns3.add_cube(cube_values_6, my_dimensions1) + + # conns2 should be contained in conns1 + self.assertFalse(conns1.contained_in(conns2)) + self.assertTrue(conns2.contained_in(conns1)) + + self.assertEqual(conns2, conns3) + + + def test_basic(self): a = CanonicalHyperCubeSet(dimensions4) a.add_cube([CanonicalIntervalSet.get_interval_set(1, 2)], ["x"]) diff --git a/tests/classes_unit_tests/testCanonicalHyperCubeSetNew.py b/tests/classes_unit_tests/testCanonicalHyperCubeSetNew.py index c9f6ce38..29140bd9 100644 --- a/tests/classes_unit_tests/testCanonicalHyperCubeSetNew.py +++ b/tests/classes_unit_tests/testCanonicalHyperCubeSetNew.py @@ -4,6 +4,8 @@ from nca.CoreDS.MinDFA import MinDFA from nca.CoreDS.CanonicalHyperCubeSet import CanonicalHyperCubeSet from nca.CoreDS.DimensionsManager import DimensionsManager +from nca.CoreDS.Peer import BasePeerSet, IpBlock +from nca.CoreDS.ProtocolSet import ProtocolSet dimensions = ["src_ports", "ports", "methods", "paths"] dimensions2 = ["ports", "src_ports", "methods", "paths"] @@ -971,6 +973,32 @@ def test_contained_in_new(self): d = CanonicalHyperCubeSet.create_from_cube(dimensions, [get_str_dfa("x|y|z")], ["paths"]) self.assertTrue(c.contained_in(d)) + def test_bug_in_contained(self): + BasePeerSet.reset() + BasePeerSet().add_peer("A") + BasePeerSet().add_peer("B") + BasePeerSet().add_peer("C") + my_dimensions1 = ["src_peers", "dst_peers"] + my_dimensions2 = ["src_peers", "dst_peers", "protocols"] + conns1 = CanonicalHyperCubeSet.create_from_cube(my_dimensions2, + [BasePeerSet().get_peer_interval_of({"B"}), + BasePeerSet().get_peer_interval_of({"A", "B", "C"})], + my_dimensions1) + conns1.add_cube([BasePeerSet().get_peer_interval_of({"C"}), + BasePeerSet().get_peer_interval_of({"A"})], my_dimensions1) + + conns2 = CanonicalHyperCubeSet.create_from_cube(my_dimensions2, + [BasePeerSet().get_peer_interval_of({"B"}), + BasePeerSet().get_peer_interval_of({"B", "C"}), + ProtocolSet.get_non_tcp_protocols()], + my_dimensions2) + conns2.add_cube([BasePeerSet().get_peer_interval_of({"B"}), + BasePeerSet().get_peer_interval_of({"A"})], my_dimensions1) + conns2.add_cube([BasePeerSet().get_peer_interval_of({"C"}), + BasePeerSet().get_peer_interval_of({"A"})], my_dimensions1) + self.assertFalse(conns1.contained_in(conns2)) + self.assertTrue(conns2.contained_in(conns1)) + def test_subtract_basic(self): x = CanonicalHyperCubeSet(dimensions) y = CanonicalHyperCubeSet(dimensions)