CORD-1801: [2/2] Fix dependencies involving controller objects + Unit
test updates and additions

Change-Id: I12565db9e3a5f1fe2d376aa4e763330826771bea
(cherry picked from commit 2263db14363b4825168c34e1c9e42b69a569e7d6)
diff --git a/lib/xos-genx/xosgenx/targets/model-deps.xtarget b/lib/xos-genx/xosgenx/targets/model-deps.xtarget
index 74a8647..7beccfd 100644
--- a/lib/xos-genx/xosgenx/targets/model-deps.xtarget
+++ b/lib/xos-genx/xosgenx/targets/model-deps.xtarget
@@ -1,9 +1,22 @@
-{ {%- for model in proto.messages %} "{{ model.name }}": [
-        {% for l in xproto_base_links(model, proto.message_table) + model.links -%}
-        {% if "Controller" + l.peer.name in proto.message_names -%}
-        ["Controller{{ l.peer.name }}", "{{ l.src_port }}", "{{ xproto_camel_to_underscore(l.peer.name).lower() }}"],
-        {% endif %}
-        ["{{ l.peer.name }}", "{{ l.src_port }}", ""]{% if not loop.last %},{% endif %}
+{ {%- for model in proto.messages %} 
+    {% if model.name not in ["Controller", "XOSBase"] %}
+    "{{ model.name }}": [
+        {% set controller_model_name = "Controller" + model.name %}
+        {% set controller_model_name_plural = "Controller" + xproto_pluralize(model) %}
+        {% set controller_models = (proto.message_table[controller_model_name], proto.message_table[controller_model_name_plural]) %}
+        {% set controller_model = xproto_first_non_empty(controller_models) %}
+        {% if controller_model %}
+        {% set controller_links = xproto_base_links(controller_model, proto.message_table) + controller_model.links  %} 
+        {% for cl in controller_links %}
+        {% set _=cl.__setitem__("model_name", cl.options.model) %}
         {% endfor %}
-    ]{{ "," if not loop.last }}{% endfor %}
+        {% set controller_backlinks = controller_links | selectattr("model_name", "equalto", model.name) %}
+        {% for backlink in controller_backlinks %}
+        ["{{ controller_model.name }}", "{{ backlink.dst_port }}", "{{ backlink.src_port }}"],
+        {% endfor %}
+        {% endif %}
+        {% for l in xproto_base_links(model, proto.message_table) + model.links -%}
+        ["{{ l.peer.name }}", "{{ l.src_port }}", "{{ l.dst_port }}"]{% if not loop.last %},{% endif %}
+        {% endfor %}
+    ]{{ "," if not loop.last }}{% endif %}{% endfor %}
 }
diff --git a/xos/synchronizers/new_base/event_loop.py b/xos/synchronizers/new_base/event_loop.py
index 9a87b56..2919d26 100644
--- a/xos/synchronizers/new_base/event_loop.py
+++ b/xos/synchronizers/new_base/event_loop.py
@@ -16,7 +16,6 @@
 import sys
 import threading
 import json
-import pdb
 import pprint
 import traceback
 from collections import defaultdict
@@ -24,7 +23,6 @@
 from networkx.algorithms.dag import topological_sort
 
 from datetime import datetime
-#from multistructlog import create_logger
 from xosconfig import Config
 from synchronizers.new_base.steps import *
 from syncstep import InnocuousException, DeferredException, SyncStep
@@ -56,6 +54,8 @@
 # set_driver() below.
 DRIVER = NoOpDriver()
 
+DIRECT_EDGE = 1
+PROXY_EDGE = 2
 
 def set_driver(x):
     global DRIVER
@@ -403,6 +403,28 @@
         returns False.
     """
 
+    def same_object(self, o1, o2):
+        if not o1 or not o2:
+            return False, None
+
+        try:
+            o1_lst = (o for o in o1.all())
+            edge_type = PROXY_EDGE
+        except (AttributeError, TypeError):
+            o1_lst = [o1]
+            edge_type = DIRECT_EDGE
+
+        try:
+            found = next(obj for obj in o1_lst if obj.leaf_model_name ==
+                         o2.leaf_model_name and obj.pk == o2.pk)
+        except AttributeError as e:
+            self.log.exception('Compared objects could not be identified', e=e)
+            raise e
+        except StopIteration:
+            found = False
+
+        return found, edge_type
+
     def concrete_path_exists(self, o1, o2):
         try:
             m1 = o1.leaf_model_name
@@ -410,7 +432,7 @@
         except AttributeError:
             # One of the nodes is not in the dependency graph
             # No dependency
-            return False
+            return False, None
 
         # FIXME: Dynamic dependency check
         G = self.model_dependency_graph[False]
diff --git a/xos/synchronizers/new_base/tests/model-deps-onos.yaml b/xos/synchronizers/new_base/tests/model-deps-onos.yaml
deleted file mode 100644
index a459052..0000000
--- a/xos/synchronizers/new_base/tests/model-deps-onos.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-# 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.
-
-
-{
-        "ONOSApp": [
-                    ["ONOSService", "", ""]
-                        ]
-              }
diff --git a/xos/synchronizers/new_base/tests/model-deps.yaml b/xos/synchronizers/new_base/tests/model-deps.yaml
deleted file mode 100644
index 0c92522..0000000
--- a/xos/synchronizers/new_base/tests/model-deps.yaml
+++ /dev/null
@@ -1,378 +0,0 @@
-
-# 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.
-
-
-{
-    "XOSBase": [
-        
-    ],
-    "User": [
-        "ControllerSite",
-        
-        "Site",
-        "ControllerDashboardView",
-        
-        "DashboardView"
-        
-    ],
-    "Privilege": [
-        
-    ],
-    "AddressPool": [
-        
-        "Service"
-        
-    ],
-    "Controller": [
-        
-        "Deployment"
-        
-    ],
-    "ControllerDashboardView": [
-        
-        "Controller",
-        "ControllerDashboardView",
-        
-        "DashboardView"
-        
-    ],
-    "ControllerImages": [
-        
-        "Image",
-        
-        "Controller"
-        
-    ],
-    "ControllerNetwork": [
-        "ControllerNetwork",
-        
-        "Network",
-        
-        "Controller"
-        
-    ],
-    "ControllerRole": [
-        
-    ],
-    "ControllerSite": [
-        "ControllerSite",
-        
-        "Site",
-        
-        "Controller"
-        
-    ],
-    "ControllerPrivilege": [
-        
-        "Controller",
-        "ControllerPrivilege",
-        
-        "Privilege"
-        
-    ],
-    "ControllerSitePrivilege": [
-        
-        "Controller",
-        "ControllerSitePrivilege",
-        
-        "SitePrivilege"
-        
-    ],
-    "ControllerSlice": [
-        
-        "Controller",
-        "ControllerSlice",
-        
-        "Slice"
-        
-    ],
-    "ControllerSlicePrivilege": [
-        
-        "Controller",
-        "ControllerSlicePrivilege",
-        
-        "SlicePrivilege"
-        
-    ],
-    "ControllerUser": [
-        "ControllerUser",
-        
-        "User",
-        
-        "Controller"
-        
-    ],
-    "DashboardView": [
-        
-        "Controller",
-        
-        "Deployment"
-        
-    ],
-    "Deployment": [
-        
-    ],
-    "DeploymentPrivilege": [
-        "ControllerUser",
-        
-        "User",
-        
-        "Deployment",
-        
-        "DeploymentRole"
-        
-    ],
-    "DeploymentRole": [
-        
-    ],
-    "Diag": [
-        
-    ],
-    "Flavor": [
-        
-    ],
-    "Image": [
-        
-    ],
-    "ImageDeployments": [
-        
-        "Image",
-        
-        "Deployment"
-        
-    ],
-    "Instance": [
-        
-        "Image",
-        "ControllerUser",
-        
-        "User",
-        "ControllerSlice",
-        
-        "Slice",
-        
-        "Deployment",
-        
-        "Node",
-        
-        "Flavor",
-        
-        "Instance"
-        
-    ],
-    "Network": [
-        
-        "NetworkTemplate",
-        "ControllerSlice",
-        
-        "Slice",
-        "ControllerSlice",
-        
-        "Slice",
-        "ControllerSlice",
-        
-        "Slice",
-        
-        "Instance"
-        
-    ],
-    "NetworkParameter": [
-        
-        "NetworkParameterType"
-        
-    ],
-    "NetworkParameterType": [
-        
-    ],
-    "NetworkSlice": [
-        "ControllerNetwork",
-        
-        "Network",
-        "ControllerSlice",
-        
-        "Slice"
-        
-    ],
-    "NetworkTemplate": [
-        
-    ],
-    "Node": [
-        
-        "SiteDeployment"
-        
-    ],
-    "NodeLabel": [
-        
-        "Node"
-        
-    ],
-    "Port": [
-        "ControllerNetwork",
-        
-        "Network",
-        
-        "Instance"
-        
-    ],
-    "Role": [
-        
-    ],
-    "Service": [
-        
-    ],
-    "ServiceAttribute": [
-        
-        "Service"
-        
-    ],
-    "ServiceDependency": [
-        
-        "Service",
-        
-        "Service"
-        
-    ],
-    "ServiceMonitoringAgentInfo": [
-        
-        "Service"
-        
-    ],
-    "ServicePrivilege": [
-        "ControllerUser",
-        
-        "User",
-        
-        "Service",
-        
-        "ServiceRole"
-        
-    ],
-    "ServiceRole": [
-        
-    ],
-    "Site": [
-        
-        "Deployment"
-        
-    ],
-    "SiteDeployment": [
-        "ControllerSite",
-        
-        "Site",
-        
-        "Deployment",
-        
-        "Controller"
-        
-    ],
-    "SitePrivilege": [
-        "ControllerUser",
-        
-        "User",
-        "ControllerSite",
-        
-        "Site",
-        
-        "SiteRole"
-        
-    ],
-    "SiteRole": [
-        
-    ],
-    "Slice": [
-        "ControllerSite",
-        
-        "Site",
-        
-        "Service",
-        "ControllerUser",
-        
-        "User",
-        
-        "Flavor",
-        
-        "Image",
-        
-        "Node"
-        
-    ],
-    "SlicePrivilege": [
-        "ControllerUser",
-        
-        "User",
-        "ControllerSlice",
-        
-        "Slice",
-        
-        "SliceRole"
-        
-    ],
-    "SliceRole": [
-        
-    ],
-    "Tag": [
-        
-        "Service"
-        
-    ],
-    "InterfaceType": [
-        
-    ],
-    "ServiceInterface": [
-        
-        "Service",
-        
-        "InterfaceType"
-        
-    ],
-    "ServiceInstance": [
-        
-        "Service"
-        
-    ],
-    "ServiceInstanceLink": [
-        
-        "ServiceInstance",
-        
-        "ServiceInterface",
-        
-        "ServiceInstance",
-        
-        "Service",
-        "ControllerNetwork",
-        
-        "Network"
-        
-    ],
-    "ServiceInstanceAttribute": [
-        
-        "ServiceInstance"
-        
-    ],
-    "TenantWithContainer": [
-        
-        "Instance",
-        "ControllerUser",
-        
-        "User"
-        
-    ],
-    "XOS": [
-        
-    ],
-    "XOSGuiExtension": [
-        
-    ]
-}
diff --git a/xos/synchronizers/new_base/tests/steps/mock_modelaccessor.py b/xos/synchronizers/new_base/tests/steps/mock_modelaccessor.py
index c390327..ef038cc 100644
--- a/xos/synchronizers/new_base/tests/steps/mock_modelaccessor.py
+++ b/xos/synchronizers/new_base/tests/steps/mock_modelaccessor.py
@@ -12,36 +12,42 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
 from mock import Mock
 import random
 
+def mock_enumerator(items):
+    e = lambda:None
+    e.all = lambda:items
+    return e
+
 class Object:
     objects = Mock()
     def __init__(self, **kwargs):
-        for (k,v) in kwargs.items():
-            setattr(self,k,v)
-
         setattr(self, 'save', Mock())
         setattr(self, 'delete', Mock())
         setattr(self, 'backend_code', 0)
         setattr(self, 'id', 98052)
+        setattr(self, 'pk', random.randint(0, 1<<30))
+        
+        for (k,v) in kwargs.items():
+            setattr(self,k,v)
+
 
     def tologdict(self):
         return {}
 
 class ONOSService(Object):
-    pass
+    leaf_model_name = 'ONOSService'
 
-class ONOSTenant(Object):
-    pass
+class ONOSApp(Object):
+    leaf_model_name = 'ONOSApp'
 
 class ModelAccessor:
     def check_db_connection_ok(self):
         return True
 
     def fetch_pending(self, model, deleted = False):
-        num = random.randint(1,5)
+        num = random.randint(1, 5)
         object_list = []
 
         for i in range(num):
@@ -61,6 +67,13 @@
 
 model_accessor = ModelAccessor()
 
+class ObjectSet(object):
+    def __init__(self, objects):
+        self.objects = objects
+
+    def all(self):
+        return self.objects
+    
 #####
 # DO NOT MODIFY THE CLASSES BELOW. THEY ARE AUTOGENERATED.
 # 
@@ -84,6 +97,7 @@
     leaf_model_name = None
     
     leaf_model_name = "XOSBase"
+
 class User(Object):
     email = None
     username = "Something"
diff --git a/xos/synchronizers/new_base/tests/steps/sync_controller_networks.py b/xos/synchronizers/new_base/tests/steps/sync_controller_networks.py
index f4f31f2..793b87d 100644
--- a/xos/synchronizers/new_base/tests/steps/sync_controller_networks.py
+++ b/xos/synchronizers/new_base/tests/steps/sync_controller_networks.py
@@ -55,17 +55,7 @@
         pass
 
     def map_delete_inputs(self, controller_network):
-	network_name = controller_network.network.name
-        subnet_name = '%s-%d'%(network_name,controller_network.pk)
-	cidr = controller_network.subnet
-	network_fields = {'endpoint':controller_network.controller.auth_url,
-                    'admin_user':slice.creator.email, # XXX: FIXME
-                    'admin_project':slice.name, # XXX: FIXME
-                    'admin_password':slice.creator.remote_password,
-                    'name':network_name,
-                    'subnet_name':subnet_name,
-                    'ansible_tag':'%s-%s@%s'%(network_name,slice.slicename,controller_network.controller.name),
-                    'cidr':cidr,
+	network_fields = {'endpoint':None,
 		    'delete':True	
                     }
 
diff --git a/xos/synchronizers/new_base/tests/test_config.yaml b/xos/synchronizers/new_base/tests/test_config.yaml
index e5ec0d3..5dc99fe 100644
--- a/xos/synchronizers/new_base/tests/test_config.yaml
+++ b/xos/synchronizers/new_base/tests/test_config.yaml
@@ -20,5 +20,13 @@
   kind: testframework
 logging:
   version: 1
-dependency_graph: "model-deps.yaml"
+  handlers:
+    console:
+      class: logging.StreamHandler
+  loggers:
+    '':
+      handlers:
+          - console
+      level: DEBUG 
+dependency_graph: "tests/model-deps"
 steps_dir: "tests/steps"
diff --git a/xos/synchronizers/new_base/tests/test_config_onos.yaml b/xos/synchronizers/new_base/tests/test_config_onos.yaml
index 8862ff6..c3eb562 100644
--- a/xos/synchronizers/new_base/tests/test_config_onos.yaml
+++ b/xos/synchronizers/new_base/tests/test_config_onos.yaml
@@ -20,5 +20,5 @@
   kind: testframework
 logging:
   version: 1
-dependency_graph: "tests/model-deps-onos.yaml"
+dependency_graph: "tests/model-deps-onos"
 steps_dir: "tests/steps"
diff --git a/xos/synchronizers/new_base/tests/test_load.py b/xos/synchronizers/new_base/tests/test_load.py
index 575732b..8f75f03 100644
--- a/xos/synchronizers/new_base/tests/test_load.py
+++ b/xos/synchronizers/new_base/tests/test_load.py
@@ -56,14 +56,14 @@
         graph = self.synchronizer.model_dependency_graph
         self.assertTrue(graph[False].has_edge('Instance','Slice'))
         self.assertTrue(graph[True].has_edge('Slice','Instance'))
-        self.assertTrue(graph[False].has_edge('Instance','ControllerSlice'))
-        self.assertTrue(graph[True].has_edge('ControllerSlice','Instance'))
+        self.assertTrue(graph[False].has_edge('Slice','ControllerSlice'))
+        self.assertTrue(graph[True].has_edge('ControllerSlice','Slice'))
 
     def test_load_dep_accessors(self):
         self.synchronizer.load_dependency_graph()
         graph = self.synchronizer.model_dependency_graph
-        self.assertDictContainsSubset({'src_accessor': 'slice'}, graph[False]['Instance']['ControllerSlice'])
-        self.assertDictContainsSubset({'src_accessor': 'slice', 'dst_accessor': 'slice'}, graph[True]['ControllerSlice']['Instance'])
+        self.assertDictContainsSubset({'src_accessor': 'controllerslices'}, graph[False]['Slice']['ControllerSlice'])
+        self.assertDictContainsSubset({'src_accessor': 'slice', 'dst_accessor': 'controllerslices'}, graph[True]['Slice']['ControllerSlice'])
 
     def test_load_sync_steps(self):
         self.synchronizer.load_sync_steps()
diff --git a/xos/synchronizers/new_base/tests/test_payload.py b/xos/synchronizers/new_base/tests/test_payload.py
index 909c739..15c5874 100644
--- a/xos/synchronizers/new_base/tests/test_payload.py
+++ b/xos/synchronizers/new_base/tests/test_payload.py
@@ -37,6 +37,9 @@
 
 import steps.sync_instances
 import steps.sync_controller_slices
+from multistructlog import create_logger
+
+log = create_logger()
 
 ANSIBLE_FILE='/tmp/payload_test'
 
@@ -62,7 +65,7 @@
         o.name = "Sisi Pascal"
 
         o.synchronizer_step = steps.sync_instances.SyncInstances()
-        self.synchronizer.delete_record(o)
+        self.synchronizer.delete_record(o, log)
 
         a = get_ansible_output()
         self.assertDictContainsSubset({'delete':True, 'name':o.name}, a)
@@ -75,7 +78,7 @@
         o.name = "Sisi Pascal"
 
         o.synchronizer_step = steps.sync_instances.SyncInstances()
-        self.synchronizer.sync_record(o)
+        self.synchronizer.sync_record(o, log)
 
         a = get_ansible_output()
         self.assertDictContainsSubset({'delete':False, 'name':o.name}, a)
@@ -162,8 +165,9 @@
 
         self.synchronizer.external_dependencies = []
         cohorts = self.synchronizer.compute_dependent_cohorts(pending_objects, False)
+        flat_objects = [item for cohort in cohorts for item in cohort]
        
-        self.assertEqual(len(cohorts), len(pending_objects) - 1)
+        self.assertEqual(set(flat_objects), set(pending_objects))
     
     @mock.patch("steps.sync_instances.syncstep.run_template",side_effect=run_fake_ansible_template)
     @mock.patch("event_loop.model_accessor")
@@ -178,7 +182,8 @@
 
         cohorts = self.synchronizer.compute_dependent_cohorts(pending_objects, False)
        
-        self.assertEqual(len(cohorts), len(pending_objects))
+        flat_objects = [item for cohort in cohorts for item in cohort]
+        self.assertEqual(set(flat_objects), set(pending_objects))
 
         # These cannot be None, but for documentation purposes
         self.assertIsNotNone(any_cn)
diff --git a/xos/synchronizers/new_base/tests/test_run.py b/xos/synchronizers/new_base/tests/test_run.py
index 1ca1a2c..3a52449 100644
--- a/xos/synchronizers/new_base/tests/test_run.py
+++ b/xos/synchronizers/new_base/tests/test_run.py
@@ -54,8 +54,14 @@
         steps_dir = Config.get("steps_dir")
         self.steps = b.load_sync_step_modules(steps_dir)
         self.synchronizer = event_loop.XOSObserver(self.steps)
-        os.remove('/tmp/sync_ports')
-        os.remove('/tmp/delete_ports')
+        try:
+            os.remove('/tmp/sync_ports')
+        except OSError:
+            pass
+        try:
+            os.remove('/tmp/delete_ports')
+        except OSError:
+            pass
 
     @mock.patch("steps.sync_instances.syncstep.run_template",side_effect=run_fake_ansible_template)
     @mock.patch("event_loop.model_accessor")
diff --git a/xos/synchronizers/new_base/tests/test_scheduler.py b/xos/synchronizers/new_base/tests/test_scheduler.py
index 8bf5652..db1698a 100644
--- a/xos/synchronizers/new_base/tests/test_scheduler.py
+++ b/xos/synchronizers/new_base/tests/test_scheduler.py
@@ -47,10 +47,53 @@
         self.steps = b.load_sync_step_modules(steps_dir)
         self.synchronizer = event_loop.XOSObserver(self.steps)
 
+    def test_same_object_trivial(self):
+        s = Slice(pk=4)
+        t = Slice(pk=4)
+        same, t = self.synchronizer.same_object(s, t)
+        self.assertTrue(same)
+        self.assertEqual(t, event_loop.DIRECT_EDGE)
+
+    def test_same_object_trivial2(self):
+        s = Slice(pk=4)
+        t = Slice(pk=5)
+        same, t = self.synchronizer.same_object(s, t)
+        self.assertFalse(same)
+
+    def test_same_object_lst(self):
+        s = Slice(pk=5)
+        t = ControllerSlice(slice=s)
+        u = ControllerSlice(slice=s)
+
+        s.controllerslices = mock_enumerator([t,u])
+
+        same, et = self.synchronizer.same_object(s.controllerslices, u)
+        self.assertTrue(same)
+        self.assertEqual(et, event_loop.PROXY_EDGE)
+
+        same, et = self.synchronizer.same_object(s.controllerslices, t)
+
+        self.assertTrue(same)
+        self.assertEqual(et, event_loop.PROXY_EDGE)
+
+    def test_same_object_lst_dc(self):
+        r = Slice(pk=4)
+        s = Slice(pk=5)
+        t = ControllerSlice(slice=r)
+        u = ControllerSlice(slice=s)
+
+        s.controllerslices = mock_enumerator([u])
+
+        same, et = self.synchronizer.same_object(s.controllerslices, t)
+        self.assertFalse(same)
+
+        same, et = self.synchronizer.same_object(s.controllerslices, u)
+        self.assertTrue(same)
+
     def test_concrete_path_no_model_path(self):
         p = Port()
         n = NetworkParameter()
-        verdict = self.synchronizer.concrete_path_exists(p, n)
+        verdict,_ = self.synchronizer.concrete_path_exists(p, n)
         self.assertFalse(verdict)
 
     def test_concrete_no_object_path_adjacent(self):
@@ -58,7 +101,7 @@
         s1 = Slice()
         s2 = Slice()
         p.slice = s2
-        verdict = self.synchronizer.concrete_path_exists(p, s1)
+        verdict,_ = self.synchronizer.concrete_path_exists(p, s1)
         
         self.assertFalse(verdict)
     
@@ -66,9 +109,10 @@
         p = Instance()
         s = Slice()
         p.slice = s
-        verdict = self.synchronizer.concrete_path_exists(p, s)
+        verdict, edge_type = self.synchronizer.concrete_path_exists(p, s)
         
         self.assertTrue(verdict)
+        self.assertEqual(edge_type, event_loop.DIRECT_EDGE)
 
     def test_concrete_object_controller_path_adjacent(self):
         p = Instance()
@@ -80,15 +124,20 @@
         p.slice = s1
         q.slice = s2
         cs.slice = s1
+        s1.controllerslices = mock_enumerator([cs])
+        s2.controllerslices = mock_enumerator([])
 
-        verdict1 = self.synchronizer.concrete_path_exists(p, cs)
-        verdict2 = self.synchronizer.concrete_path_exists(q, cs)
-        verdict3 = self.synchronizer.concrete_path_exists(p, cs2)
+
+        verdict1, edge_type1 = self.synchronizer.concrete_path_exists(p, cs)
+        verdict2, _ = self.synchronizer.concrete_path_exists(q, cs)
+        verdict3, _ = self.synchronizer.concrete_path_exists(p, cs2)
         
         self.assertTrue(verdict1)
         self.assertFalse(verdict2)
         self.assertFalse(verdict3)
 
+        self.assertEqual(edge_type1, event_loop.PROXY_EDGE)
+
     def test_concrete_object_controller_path_distant(self):
         p = Instance()
         s = Slice()
@@ -112,18 +161,27 @@
     def test_concrete_no_object_path_distant(self):
         p = Instance()
         s = Slice()
+        s.controllerslice=mock_enumerator([])
+
         t = Site()
+        t.controllersite=mock_enumerator([])
+
         ct = ControllerSite()
         ct.site = Site()
         p.slice = s
         s.site = t
-        verdict = self.synchronizer.concrete_path_exists(p, ct)
-        self.assertTrue(verdict)
+
+        verdict, _ = self.synchronizer.concrete_path_exists(p, ct)
+        self.assertFalse(verdict)
 
     def test_cohorting_independent(self):
         i = Image()
+
         p = Slice()
         c = Instance()
+        c.slice = None
+        c.image = None
+
         cohorts = self.synchronizer.compute_dependent_cohorts([i,p,c], False)
         self.assertEqual(len(cohorts), 3)
     
@@ -147,6 +205,7 @@
         cs = ControllerSlice()
         s = Slice()
         cs.slice = s
+        s.controllerslices = mock_enumerator([cs])
         c.slice = s
 
         cohorts = self.synchronizer.compute_dependent_cohorts([i,p,c,s,cs], False)
@@ -156,6 +215,23 @@
         self.assertGreater(big_cohort.index(cs), big_cohort.index(s))
         self.assertIn([p], cohorts)
 
+    def test_cohorting_related_multi_delete(self):
+        i = Image()
+        p = Port()
+        c = Instance()
+        c.image = i
+        cs = ControllerSlice()
+        s = Slice()
+        cs.slice = s
+        c.slice = s
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([i,p,c,s,cs], True)
+
+        big_cohort = max(cohorts, key=len)
+        self.assertGreater(big_cohort.index(i), big_cohort.index(c))
+        self.assertGreater(big_cohort.index(s), big_cohort.index(cs))
+        self.assertIn([p], cohorts)
+
     def test_cohorting_related_delete(self):
         i = Image()
         p = Port()
diff --git a/xos/synchronizers/new_base/tests/test_services.py b/xos/synchronizers/new_base/tests/test_services.py
index d3b67c7..418b36e 100644
--- a/xos/synchronizers/new_base/tests/test_services.py
+++ b/xos/synchronizers/new_base/tests/test_services.py
@@ -42,14 +42,20 @@
         self.synchronizer = event_loop.XOSObserver(self.steps)
 
     def test_service_models(self):
-        o = ONOSApp()
-        t = ONOSService()
+        a = ONOSApp()
+        s = ONOSService()
 
-        cohorts = self.synchronizer.compute_dependent_cohorts([o,t], False)
-        self.assertIn([t,o], cohorts)
+        cohorts = self.synchronizer.compute_dependent_cohorts([a,s], False)
+        self.assertIn([s,a], cohorts)
 
-        cohorts = self.synchronizer.compute_dependent_cohorts([t,o], False)
-        self.assertIn([t,o], cohorts)
+        cohorts = self.synchronizer.compute_dependent_cohorts([s,a], False)
+        self.assertIn([s,a], cohorts)
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([a,s], True)
+        self.assertIn([a,s], cohorts)
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([s,a], True)
+        self.assertIn([a,s], cohorts)
 
 if __name__ == '__main__':
     unittest.main()