Route commands without path to suitable component

Use the trait name to find the first component that implements
that trait to send the command to.

BUG: 25917421
Change-Id: Ife284100fb7d0bf94416bf5ba2ab9b797076ce23
Reviewed-on: https://weave-review.googlesource.com/1783
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/src/component_manager_unittest.cc b/src/component_manager_unittest.cc
index 83f9933..31949d7 100644
--- a/src/component_manager_unittest.cc
+++ b/src/component_manager_unittest.cc
@@ -31,38 +31,38 @@
 }
 
 // Creates sample trait/component trees:
-// {

-//   "traits": {

-//     "t1": {},

-//     "t2": {},

-//     "t3": {},

-//     "t4": {},

-//     "t5": {},

-//     "t6": {},

-//   },

-//   "components": {

-//     "comp1": {

-//       "traits": [ "t1" ],

-//       "components": {

-//         "comp2": [

-//           { "traits": [ "t2" ] },

-//           {

-//             "traits": [ "t3" ],

-//             "components": {

-//               "comp3": {

-//                 "traits": [ "t4" ],

-//                 "components": {

-//                   "comp4": {

-//                     "traits": [ "t5", "t6" ]

-//                   }

-//                 }

-//               }

-//             }

-//           }

-//         ],

-//       }

-//     }

-//   }

+// {
+//   "traits": {
+//     "t1": {},
+//     "t2": {},
+//     "t3": {},
+//     "t4": {},
+//     "t5": {},
+//     "t6": {},
+//   },
+//   "components": {
+//     "comp1": {
+//       "traits": [ "t1" ],
+//       "components": {
+//         "comp2": [
+//           { "traits": [ "t2" ] },
+//           {
+//             "traits": [ "t3" ],
+//             "components": {
+//               "comp3": {
+//                 "traits": [ "t4" ],
+//                 "components": {
+//                   "comp4": {
+//                     "traits": [ "t5", "t6" ]
+//                   }
+//                 }
+//               }
+//             }
+//           }
+//         ],
+//       }
+//     }
+//   }
 // }
 void CreateTestComponentTree(ComponentManager* manager) {
   const char kTraits[] = R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})";
@@ -670,7 +670,7 @@
   // Component comp1 doesn't have trait2.
   EXPECT_FALSE(manager.AddCommand(*command3, UserRole::kOwner, &id, nullptr));
 
-  // No component specified, use the first top-level component (comp1)
+  // No component specified, find the suitable component
   const char kCommand4[] = R"({
     "name": "trait1.command1",
     "parameters": {}
@@ -680,6 +680,16 @@
   auto cmd = manager.FindCommand(id);
   ASSERT_NE(nullptr, cmd);
   EXPECT_EQ("comp1", cmd->GetComponent());
+
+  const char kCommand5[] = R"({
+    "name": "trait2.command1",
+    "parameters": {}
+  })";
+  auto command5 = CreateDictionaryValue(kCommand5);
+  EXPECT_TRUE(manager.AddCommand(*command5, UserRole::kOwner, &id, nullptr));
+  cmd = manager.FindCommand(id);
+  ASSERT_NE(nullptr, cmd);
+  EXPECT_EQ("comp2", cmd->GetComponent());
 }
 
 TEST(ComponentManager, AddCommandHandler) {
@@ -750,30 +760,30 @@
   auto state1 = CreateDictionaryValue(kState1);
   ASSERT_TRUE(manager.SetStateProperties("comp1", *state1, nullptr));
   const char kExpected1[] = R"({
-    "comp1": {

-      "traits": [ "t1" ],

-      "state": {"t1": {"p1": 0, "p2": "foo"}},

-      "components": {

-        "comp2": [

-          {

-            "traits": [ "t2" ]

-          },

-          {

-            "traits": [ "t3" ],

-            "components": {

-              "comp3": {

-                "traits": [ "t4" ],

-                "components": {

-                  "comp4": {

-                    "traits": [ "t5", "t6" ]

-                  }

-                }

-              }

-            }

-          }

-        ]

-      }

-    }

+    "comp1": {
+      "traits": [ "t1" ],
+      "state": {"t1": {"p1": 0, "p2": "foo"}},
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ]
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
   })";
   EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
 
@@ -782,30 +792,30 @@
   ASSERT_TRUE(manager.SetStateProperties("comp1", *state2, nullptr));
 
   const char kExpected2[] = R"({
-    "comp1": {

-      "traits": [ "t1" ],

-      "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},

-      "components": {

-        "comp2": [

-          {

-            "traits": [ "t2" ]

-          },

-          {

-            "traits": [ "t3" ],

-            "components": {

-              "comp3": {

-                "traits": [ "t4" ],

-                "components": {

-                  "comp4": {

-                    "traits": [ "t5", "t6" ]

-                  }

-                }

-              }

-            }

-          }

-        ]

-      }

-    }

+    "comp1": {
+      "traits": [ "t1" ],
+      "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ]
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
   })";
   EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
 
@@ -815,31 +825,31 @@
                                          nullptr));
 
   const char kExpected3[] = R"({
-    "comp1": {

-      "traits": [ "t1" ],

-      "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},

-      "components": {

-        "comp2": [

-          {

-            "traits": [ "t2" ]

-          },

-          {

-            "traits": [ "t3" ],

-            "components": {

-              "comp3": {

-                "traits": [ "t4" ],

-                "components": {

-                  "comp4": {

-                    "traits": [ "t5", "t6" ],

-                    "state": { "t5": { "p1": 1 } }

-                  }

-                }

-              }

-            }

-          }

-        ]

-      }

-    }

+    "comp1": {
+      "traits": [ "t1" ],
+      "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ],
+                    "state": { "t5": { "p1": 1 } }
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
   })";
   EXPECT_JSON_EQ(kExpected3, manager.GetComponents());
 }
@@ -853,33 +863,33 @@
       nullptr));
 
   const char kExpected[] = R"({
-    "comp1": {

-      "traits": [ "t1" ],

-      "components": {

-        "comp2": [

-          {

-            "traits": [ "t2" ]

-          },

-          {

-            "traits": [ "t3" ],

-            "components": {

-              "comp3": {

-                "traits": [ "t4" ],

-                "components": {

-                  "comp4": {

-                    "traits": [ "t5", "t6" ],

-                    "state": {

-                      "t5": { "p1": 3 },

-                      "t6": { "p2": 5 }

-                    }

-                  }

-                }

-              }

-            }

-          }

-        ]

-      }

-    }

+    "comp1": {
+      "traits": [ "t1" ],
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ],
+                    "state": {
+                      "t5": { "p1": 3 },
+                      "t6": { "p2": 5 }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
   })";
   EXPECT_JSON_EQ(kExpected, manager.GetComponents());
 }
@@ -908,12 +918,12 @@
   ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
 
   const char kExpected1[] = R"({
-    "comp1": {

-      "traits": [ "trait1", "trait2" ],

-      "state": {

-        "trait1": { "prop1": "foo" }

-      }

-    }

+    "comp1": {
+      "traits": [ "trait1", "trait2" ],
+      "state": {
+        "trait1": { "prop1": "foo" }
+      }
+    }
   })";
   EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
 
@@ -921,13 +931,13 @@
   ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
 
   const char kExpected2[] = R"({
-    "comp1": {

-      "traits": [ "trait1", "trait2" ],

-      "state": {

-        "trait1": { "prop1": "foo" },

-        "trait2": { "prop3": 2 }

-      }

-    }

+    "comp1": {
+      "traits": [ "trait1", "trait2" ],
+      "state": {
+        "trait1": { "prop1": "foo" },
+        "trait2": { "prop3": 2 }
+      }
+    }
   })";
   EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
   // Just the package name without property:
@@ -1085,4 +1095,22 @@
   EXPECT_EQ(snapshot.update_id, updates2.front());
 }
 
+TEST(ComponentManager, FindComponentWithTrait) {
+  ComponentManager manager;
+  const char kTraits[] = R"({
+    "trait1": {},
+    "trait2": {},
+    "trait3": {}
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
+
+  EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait1"));
+  EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait2"));
+  EXPECT_EQ("comp2", manager.FindComponentWithTrait("trait3"));
+  EXPECT_EQ("", manager.FindComponentWithTrait("trait4"));
+}
+
 }  // namespace weave