CORD-1920: Fix synchronizer unit tests + 6 new tests + enable
parallelization of Instance and ControllerNetwork

Change-Id: Ied77aeb1c6e7bdf6c5cb5dc252fd8a998bca79de
diff --git a/xos/synchronizers/new_base/event_loop.py b/xos/synchronizers/new_base/event_loop.py
index a64283a..98ce2a3 100644
--- a/xos/synchronizers/new_base/event_loop.py
+++ b/xos/synchronizers/new_base/event_loop.py
@@ -489,6 +489,8 @@
                     """
 
                     if dst_objects==[]:
+                        # Workaround for ORM not returning linked deleted
+                        # objects
                         if o2.deleted:
                             return True, edge_type
                         else:
@@ -499,9 +501,22 @@
                     else:
                          dst_object = dst_objects[0]
                 except AttributeError as e:
-                    if sa!='fake_accessor':
-                        self.log.debug(
-                            'Could not check object dependencies, making conservative choice', src_object=src_object, sa=sa, o1=o1, o2=o2)
+                    # Fake accessors are links between models that have a dependency
+                    # but that do not have a path in the data model, such as Instance and ControllerNetowrk.
+                    # They are implemented as a workaround until a true dependency path is created in
+                    # the data model.
+                    if sa.startswith('fake_accessor'):
+                        if sa.endswith('deletion'):
+                            if o2.deleted:
+                                return True, edge_type
+                            else:
+                                break
+                        else:
+                            return True, edge_type
+
+                    self.log.debug(
+                        'Could not check object dependencies, making conservative choice', src_object=src_object, sa=sa, o1=o1, o2=o2)
+
                     return True, edge_type
 
                 src_object = dst_object
@@ -509,9 +524,12 @@
                 if not src_object:
                     break
 
-            verdict, edge_type = self.same_object(src_object, o2)
+            verdict, leaf_edge_type = self.same_object(src_object, o2)
             if verdict:
-                return verdict, edge_type
+                if edge_type == PROXY_EDGE:
+                    leaf_edge_type = PROXY_EDGE
+
+                return verdict, leaf_edge_type
 
             # Otherwise try other paths
 
diff --git a/xos/synchronizers/new_base/tests/model-deps b/xos/synchronizers/new_base/tests/model-deps
index 75a60e0..66b3abb 100644
--- a/xos/synchronizers/new_base/tests/model-deps
+++ b/xos/synchronizers/new_base/tests/model-deps
@@ -275,6 +275,8 @@
         ["Deployment", "deployment", "instance_deployment"],
         ["Node", "node", "instances"],
         ["Flavor", "flavor", "instance"],
+        ["ControllerNetwork", "fake_accessor_deletion", "fake_accessor_deletion"],
+        ["ImageDeployments", "fake_accessor", "fake_accessor"],
         ["Instance", "parent", "instance"]
         
     ], 
diff --git a/xos/synchronizers/new_base/tests/steps/__init__.py b/xos/synchronizers/new_base/tests/steps/__init__.py
new file mode 100644
index 0000000..d4e8062
--- /dev/null
+++ b/xos/synchronizers/new_base/tests/steps/__init__.py
@@ -0,0 +1,16 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
diff --git a/xos/synchronizers/new_base/tests/test_controller_dependencies.py b/xos/synchronizers/new_base/tests/test_controller_dependencies.py
index f5deb84..268e062 100644
--- a/xos/synchronizers/new_base/tests/test_controller_dependencies.py
+++ b/xos/synchronizers/new_base/tests/test_controller_dependencies.py
@@ -101,6 +101,38 @@
         cohorts = self.synchronizer.compute_dependent_cohorts([i, slice, site, csl, csi], False)
         self.assertEqual([site, csi, slice, csl, i], cohorts[0])
 
+    def test_instance_fake_dependency_path(self):
+        i = Instance()
+        id = ImageDeployments()
+        id.deleted = False
+        verdict, edge_type = self.synchronizer.concrete_path_exists(i, id)
+
+        self.assertTrue(verdict)
+
+    def test_instance_fake_deletion_dependency_path_delete(self):
+        i = Instance()
+        cn = ControllerNetwork()
+        cn.deleted = True
+        verdict, edge_type = self.synchronizer.concrete_path_exists(i, cn)
+
+        self.assertTrue(verdict)
+
+    def test_instance_fake_deletion_dependency_path(self):
+        i = Instance()
+        cn = ControllerNetwork()
+        cn.deleted = False
+        verdict, edge_type = self.synchronizer.concrete_path_exists(i, cn)
+
+        self.assertFalse(verdict)
+
+    def test_instance_fake_dependency_path_delete(self):
+        i = Instance()
+        cn = ControllerNetwork()
+        cn.deleted = True
+        verdict, edge_type = self.synchronizer.concrete_path_exists(i, cn)
+
+        self.assertTrue(verdict)
+
     def test_multi_controller_path_negative(self):
         csl = ControllerSlice()
         csi = ControllerSite()
@@ -152,6 +184,7 @@
         csl = ControllerSlice()
         cn = ControllerNetwork()
         site = Site()
+        csi = ControllerSite()
         slice = Slice()
         slice.site = site
         slice.controllerslices = mock_enumerator([])
@@ -179,6 +212,124 @@
         self.assertIn([site, slice, i], cohorts)
         self.assertIn([csl], cohorts)
         self.assertIn([csi], cohorts)
+    
+    def test_multiple_service_cohorts(self):
+        csl0 = ControllerSlice()
+        csl0.tag = 0
+        csi0 = ControllerSite()
+        csi0.tag = 0
+        site0 = Site()
+        site0.tag = 0
+        slice0 = Slice()
+        slice0.tag = 0
+        slice0.site = site0
+        slice0.controllerslices = mock_enumerator([csl0])
+        site0.controllersite = mock_enumerator([csi0])
+        site0.tag = 0
+        i0 = Instance()
+        i0.tag = 0
+        i0.slice = slice0
+
+        csl1 = ControllerSlice()
+        csi1 = ControllerSite()
+        site1 = Site()
+        slice1 = Slice()
+        slice1.site = site1
+        slice1.controllerslices = mock_enumerator([csl1])
+        site1.controllersite = mock_enumerator([csi1])
+        i1 = Instance()
+        i1.slice = slice1
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([i0, slice0, site0, csl0, csi0, i1, slice1, site1, csl1, csi1], False)
+
+        self.assertEqual(len(cohorts), 2)
+        self.assertEqual(len(cohorts[0]), len(cohorts[1]))
+
+    def test_multiple_service_cohorts_with_fake_dependency(self):
+        csl0 = ControllerSlice()
+        csl0.tag = 0
+        csi0 = ControllerSite()
+        csi0.tag = 0
+        site0 = Site()
+        site0.tag = 0
+        slice0 = Slice()
+        slice0.tag = 0
+        slice0.site = site0
+        slice0.controllerslices = mock_enumerator([csl0])
+        site0.controllersite = mock_enumerator([csi0])
+        site0.tag = 0
+        i0 = Instance()
+        i0.tag = 0
+        i0.slice = slice0
+        csn0 = ControllerNetwork()
+        csn0.tag = 0
+        n0 = Network()
+        n0.tag = 0
+        n0.controllernetworks = mock_enumerator([csn0])
+        n0.owner = slice0
+
+        csl1 = ControllerSlice()
+        csi1 = ControllerSite()
+        site1 = Site()
+        slice1 = Slice()
+        slice1.site = site1
+        slice1.controllerslices = mock_enumerator([csl1])
+        site1.controllersite = mock_enumerator([csi1])
+        i1 = Instance()
+        i1.slice = slice1
+        csn1 = ControllerNetwork()
+        n1 = Network()
+        n1.controllernetworks = mock_enumerator([csn1])
+        n1.owner = slice1
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([i0, slice0, site0, csl0, csi0, csn0, i1, slice1, site1, csl1, csi1, csn1], False)
+
+        self.assertEqual(len(cohorts), 4)
+        self.assertEqual(len(cohorts[1]), len(cohorts[3]))
+
+
+    def test_multiple_service_cohorts_with_fake_dependency_deletion(self):
+        csl0 = ControllerSlice()
+        csl0.tag = 0
+        csi0 = ControllerSite()
+        csi0.tag = 0
+        site0 = Site()
+        site0.tag = 0
+        slice0 = Slice()
+        slice0.tag = 0
+        slice0.site = site0
+        slice0.controllerslices = mock_enumerator([csl0])
+        site0.controllersite = mock_enumerator([csi0])
+        site0.tag = 0
+        i0 = Instance()
+        i0.tag = 0
+        i0.slice = slice0
+        csn0 = ControllerNetwork()
+        csn0.tag = 0
+        n0 = Network()
+        n0.tag = 0
+        n0.controllernetworks = mock_enumerator([csn0])
+        n0.owner = slice0
+        csn0.deleted = True
+
+        csl1 = ControllerSlice()
+        csi1 = ControllerSite()
+        site1 = Site()
+        slice1 = Slice()
+        slice1.site = site1
+        slice1.controllerslices = mock_enumerator([csl1])
+        site1.controllersite = mock_enumerator([csi1])
+        i1 = Instance()
+        i1.slice = slice1
+        csn1 = ControllerNetwork()
+        n1 = Network()
+        n1.controllernetworks = mock_enumerator([csn1])
+        n1.owner = slice1
+        csn1.deleted = True
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([i0, slice0, site0, csl0, csi0, csn0, i1, slice1, site1, csl1, csi1, csn1], True)
+
+        self.assertEqual(len(cohorts), 1)
 
 if __name__ == '__main__':
     unittest.main()