remove pending associations by owner

Changes:
Match with the service while removing the pending association for an
object. Only the pending association introduced by the service which
removes the object/interface will be erased.

If Service A has an association on Object X with Object Y (which comes
from Service C), and Service B also has an association definition for
Object X, there is a scenario where the X-Y association will not be
created, even though both objects are present in the D-Bus tree.

Steps to Reproduce:
1. Service A starts and creates Object X. It creates a pending
association Y-X.
2. Service B starts, registers properties for Object X, and creates
other associations.
3. Service B drops Object X and its associated definitions. As a
result, all pending associations for Object X are wiped out (including
X-Y which is from A).
4. Service B creates Object X again along with other associations
related to Service B (but not the ones from Service A).
5. Service C finally starts and creates Object Y, but no pending
association is processed to complete the X-Y link.

Tested:
Unit tested. Also testing on a machine with the above steps and all
the associations were still created.

Patch Tracking Bug: b/491287006
Upstream info / review: https://gerrit.openbmc.org/c/openbmc/phosphor-objmgr/+/88131
Upstream-Status: Accepted
Justification: Bugfix
  - b/488925115

Google-Bug-Id: 488925115
Change-Id: I86788e115c4431e2103fb8fc0692f8188f48ac1b
Signed-off-by: Munawar Hussain <munawarhussain@google.com>
Signed-off-by: Jane Kramer <jkrm@google.com>
Google-Bug-Id: 572860608
diff --git a/src/associations.cpp b/src/associations.cpp
index db911f9..1eceddd 100644
--- a/src/associations.cpp
+++ b/src/associations.cpp
@@ -129,7 +129,7 @@
 
     // If we were still waiting on the other side of this association to
     // show up, cancel that wait.
-    removeFromPendingAssociations(sourcePath, assocMaps);
+    removeFromPendingAssociations(sourcePath, owner, assocMaps);
 }
 
 void removeAssociationEndpoints(
@@ -345,6 +345,7 @@
 }
 
 void removeFromPendingAssociations(const std::string& endpointPath,
+                                   const std::string& owner,
                                    AssociationMaps& assocMaps)
 {
     auto assoc = assocMaps.pending.begin();
@@ -354,7 +355,8 @@
         while (endpoint != assoc->second.end())
         {
             auto& e = std::get<assocPos>(*endpoint);
-            if (std::get<reversePathPos>(e) == endpointPath)
+            if (std::get<reversePathPos>(e) == endpointPath &&
+                std::get<ownerPos>(*endpoint) == owner)
             {
                 endpoint = assoc->second.erase(endpoint);
                 continue;
diff --git a/src/associations.hpp b/src/associations.hpp
index 1d7b778..d9bdbb1 100644
--- a/src/associations.hpp
+++ b/src/associations.hpp
@@ -126,9 +126,11 @@
  * If the last endpoint is removed, removes the whole entry
  *
  * @param[in] endpointPath  - the endpoint path to remove
+ * @param[in] owner         - The service name that owns the association
  * @param[in,out] assocMaps - The association maps
  */
 void removeFromPendingAssociations(const std::string& endpointPath,
+                                   const std::string& owner,
                                    AssociationMaps& assocMaps);
 
 /** @brief Adds a single association D-Bus object (<path>/<type>)
diff --git a/src/test/associations.cpp b/src/test/associations.cpp
index fe0a826..cfd7de9 100644
--- a/src/test/associations.cpp
+++ b/src/test/associations.cpp
@@ -433,7 +433,8 @@
 
     EXPECT_EQ(assocMaps.pending.size(), 1);
 
-    removeFromPendingAssociations("some/other/endpoint", assocMaps);
+    removeFromPendingAssociations("some/other/endpoint", defaultDbusSvc,
+                                  assocMaps);
 
     // Still 1 pending entry, but down to 1 endpoint
     EXPECT_EQ(assocMaps.pending.size(), 1);
@@ -444,10 +445,34 @@
     EXPECT_EQ(endpoints.size(), 1);
 
     // Now nothing pending
-    removeFromPendingAssociations(defaultEndpoint, assocMaps);
+    removeFromPendingAssociations(defaultEndpoint, defaultDbusSvc, assocMaps);
     EXPECT_EQ(assocMaps.pending.size(), 0);
 }
 
+// Test removing pending associations by multiple owners
+TEST_F(TestAssociations, testRemoveFromPendingAssociationsMultipleOwners)
+{
+    AssociationMaps assocMaps;
+
+    addPendingAssociation(defaultSourcePath, "inventory", defaultEndpoint,
+                          "error", "serviceA", assocMaps);
+
+    addPendingAssociation(defaultSourcePath, "inventory", defaultEndpoint,
+                          "error", "serviceB", assocMaps);
+
+    EXPECT_EQ(assocMaps.pending.size(), 1);
+
+    removeFromPendingAssociations(defaultEndpoint, "serviceB", assocMaps);
+
+    EXPECT_EQ(assocMaps.pending.size(), 1);
+
+    auto assoc = assocMaps.pending.find(defaultSourcePath);
+    EXPECT_NE(assoc, assocMaps.pending.end());
+    auto& endpoints = assoc->second;
+    EXPECT_EQ(endpoints.size(), 1);
+    EXPECT_EQ(std::get<ownerPos>(endpoints[0]), "serviceA");
+}
+
 // Test moving a pending association to a real one
 TEST_F(TestAssociations, checkIfPending)
 {