1 // Copyright 2015 The Weave Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/component_manager_impl.h"
6 
7 #include <map>
8 
9 #include <gtest/gtest.h>
10 #include <weave/provider/test/fake_task_runner.h>
11 #include <weave/test/unittest_utils.h>
12 
13 #include "src/bind_lambda.h"
14 #include "src/commands/schema_constants.h"
15 #include "src/mock_component_manager.h"
16 #include "src/test/mock_clock.h"
17 
18 namespace weave {
19 
20 using test::CreateDictionaryValue;
21 using testing::Return;
22 using testing::StrictMock;
23 
24 namespace {
25 
HasTrait(const base::DictionaryValue & comp,const std::string & trait)26 bool HasTrait(const base::DictionaryValue& comp, const std::string& trait) {
27   const base::ListValue* list = nullptr;
28   if (!comp.GetList("traits", &list))
29     return false;
30   for (const base::Value* item : *list) {
31     std::string value;
32     if (item->GetAsString(&value) && value == trait)
33       return true;
34   }
35   return false;
36 }
37 
38 // Creates sample trait/component trees:
39 // {
40 //   "traits": {
41 //     "t1": {},
42 //     "t2": {},
43 //     "t3": {},
44 //     "t4": {},
45 //     "t5": {},
46 //     "t6": {},
47 //   },
48 //   "components": {
49 //     "comp1": {
50 //       "traits": [ "t1" ],
51 //       "components": {
52 //         "comp2": [
53 //           { "traits": [ "t2" ] },
54 //           {
55 //             "traits": [ "t3" ],
56 //             "components": {
57 //               "comp3": {
58 //                 "traits": [ "t4" ],
59 //                 "components": {
60 //                   "comp4": {
61 //                     "traits": [ "t5", "t6" ]
62 //                   }
63 //                 }
64 //               }
65 //             }
66 //           }
67 //         ],
68 //       }
69 //     }
70 //   }
71 // }
72 class ComponentManagerTest : public ::testing::Test {
73  protected:
SetUp()74   void SetUp() override {
75     EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(base::Time::Now()));
76   }
77 
CreateTestComponentTree(ComponentManager * manager)78   void CreateTestComponentTree(ComponentManager* manager) {
79     const char kTraits[] =
80         R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})";
81     auto json = CreateDictionaryValue(kTraits);
82     ASSERT_TRUE(manager->LoadTraits(*json, nullptr));
83     EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr));
84     EXPECT_TRUE(
85         manager->AddComponentArrayItem("comp1", "comp2", {"t2"}, nullptr));
86     EXPECT_TRUE(
87         manager->AddComponentArrayItem("comp1", "comp2", {"t3"}, nullptr));
88     EXPECT_TRUE(
89         manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"}, nullptr));
90     EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4",
91                                       {"t5", "t6"}, nullptr));
92   }
93 
94   StrictMock<provider::test::FakeTaskRunner> task_runner_;
95   StrictMock<test::MockClock> clock_;
96   ComponentManagerImpl manager_{&task_runner_, &clock_};
97 };
98 
99 }  // anonymous namespace
100 
TEST_F(ComponentManagerTest,Empty)101 TEST_F(ComponentManagerTest, Empty) {
102   EXPECT_TRUE(manager_.GetTraits().empty());
103   EXPECT_TRUE(manager_.GetComponents().empty());
104 }
105 
TEST_F(ComponentManagerTest,LoadTraits)106 TEST_F(ComponentManagerTest, LoadTraits) {
107   const char kTraits[] = R"({
108     "trait1": {
109       "commands": {
110         "command1": {
111           "minimalRole": "user",
112           "parameters": {"height": {"type": "integer"}}
113         }
114       },
115       "state": {
116         "property1": {"type": "boolean"}
117       }
118     },
119     "trait2": {
120       "state": {
121         "property2": {"type": "string"}
122       }
123     }
124   })";
125   auto json = CreateDictionaryValue(kTraits);
126   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
127   EXPECT_JSON_EQ(kTraits, manager_.GetTraits());
128   EXPECT_TRUE(manager_.GetComponents().empty());
129 }
130 
TEST_F(ComponentManagerTest,LoadTraitsDuplicateIdentical)131 TEST_F(ComponentManagerTest, LoadTraitsDuplicateIdentical) {
132   const char kTraits1[] = R"({
133     "trait1": {
134       "commands": {
135         "command1": {
136           "minimalRole": "user",
137           "parameters": {"height": {"type": "integer"}}
138         }
139       },
140       "state": {
141         "property1": {"type": "boolean"}
142       }
143     },
144     "trait2": {
145       "state": {
146         "property2": {"type": "string"}
147       }
148     }
149   })";
150   auto json = CreateDictionaryValue(kTraits1);
151   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
152   const char kTraits2[] = R"({
153     "trait1": {
154       "commands": {
155         "command1": {
156           "minimalRole": "user",
157           "parameters": {"height": {"type": "integer"}}
158         }
159       },
160       "state": {
161         "property1": {"type": "boolean"}
162       }
163     },
164     "trait3": {
165       "state": {
166         "property3": {"type": "string"}
167       }
168     }
169   })";
170   json = CreateDictionaryValue(kTraits2);
171   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
172   const char kExpected[] = R"({
173     "trait1": {
174       "commands": {
175         "command1": {
176           "minimalRole": "user",
177           "parameters": {"height": {"type": "integer"}}
178         }
179       },
180       "state": {
181         "property1": {"type": "boolean"}
182       }
183     },
184     "trait2": {
185       "state": {
186         "property2": {"type": "string"}
187       }
188     },
189     "trait3": {
190       "state": {
191         "property3": {"type": "string"}
192       }
193     }
194   })";
195   EXPECT_JSON_EQ(kExpected, manager_.GetTraits());
196 }
197 
TEST_F(ComponentManagerTest,LoadTraitsDuplicateOverride)198 TEST_F(ComponentManagerTest, LoadTraitsDuplicateOverride) {
199   const char kTraits1[] = R"({
200     "trait1": {
201       "commands": {
202         "command1": {
203           "minimalRole": "user",
204           "parameters": {"height": {"type": "integer"}}
205         }
206       },
207       "state": {
208         "property1": {"type": "boolean"}
209       }
210     },
211     "trait2": {
212       "state": {
213         "property2": {"type": "string"}
214       }
215     }
216   })";
217   auto json = CreateDictionaryValue(kTraits1);
218   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
219   const char kTraits2[] = R"({
220     "trait1": {
221       "commands": {
222         "command1": {
223           "minimalRole": "user",
224           "parameters": {"height": {"type": "string"}}
225         }
226       },
227       "state": {
228         "property1": {"type": "boolean"}
229       }
230     },
231     "trait3": {
232       "state": {
233         "property3": {"type": "string"}
234       }
235     }
236   })";
237   json = CreateDictionaryValue(kTraits2);
238   EXPECT_FALSE(manager_.LoadTraits(*json, nullptr));
239 }
240 
TEST_F(ComponentManagerTest,AddTraitDefChangedCallback)241 TEST_F(ComponentManagerTest, AddTraitDefChangedCallback) {
242   int count = 0;
243   int count2 = 0;
244   manager_.AddTraitDefChangedCallback(base::Bind([&count]() { count++; }));
245   manager_.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; }));
246   EXPECT_EQ(1, count);
247   EXPECT_EQ(1, count2);
248   // New definitions.
249   const char kTraits1[] = R"({
250     "trait1": {
251       "state": {
252         "property1": {"type": "boolean"}
253       }
254     },
255     "trait2": {
256       "state": {
257         "property2": {"type": "string"}
258       }
259     }
260   })";
261   auto json = CreateDictionaryValue(kTraits1);
262   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
263   EXPECT_EQ(2, count);
264   // Duplicate definition, shouldn't call the callback.
265   const char kTraits2[] = R"({
266     "trait1": {
267       "state": {
268         "property1": {"type": "boolean"}
269       }
270     }
271   })";
272   json = CreateDictionaryValue(kTraits2);
273   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
274   EXPECT_EQ(2, count);
275   // New definition, should call the callback now.
276   const char kTraits3[] = R"({
277     "trait3": {
278       "state": {
279         "property3": {"type": "string"}
280       }
281     }
282   })";
283   json = CreateDictionaryValue(kTraits3);
284   EXPECT_TRUE(manager_.LoadTraits(*json, nullptr));
285   EXPECT_EQ(3, count);
286   // Wrong definition, shouldn't call the callback.
287   const char kTraits4[] = R"({
288     "trait4": "foo"
289   })";
290   json = CreateDictionaryValue(kTraits4);
291   EXPECT_FALSE(manager_.LoadTraits(*json, nullptr));
292   EXPECT_EQ(3, count);
293   // Make sure both callbacks were called the same number of times.
294   EXPECT_EQ(count2, count);
295 }
296 
TEST_F(ComponentManagerTest,LoadTraitsNotAnObject)297 TEST_F(ComponentManagerTest, LoadTraitsNotAnObject) {
298   const char kTraits1[] = R"({"trait1": 0})";
299   auto json = CreateDictionaryValue(kTraits1);
300   ErrorPtr error;
301   EXPECT_FALSE(manager_.LoadTraits(*json, &error));
302   EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
303 }
304 
TEST_F(ComponentManagerTest,FindTraitDefinition)305 TEST_F(ComponentManagerTest, FindTraitDefinition) {
306   const char kTraits[] = R"({
307     "trait1": {
308       "commands": {
309         "command1": {
310           "minimalRole": "user",
311           "parameters": {"height": {"type": "integer"}}
312         }
313       },
314       "state": {
315         "property1": {"type": "boolean"}
316       }
317     },
318     "trait2": {
319       "state": {
320         "property2": {"type": "string"}
321       }
322     }
323   })";
324   auto json = CreateDictionaryValue(kTraits);
325   ASSERT_TRUE(manager_.LoadTraits(*json, nullptr));
326 
327   const base::DictionaryValue* trait = manager_.FindTraitDefinition("trait1");
328   ASSERT_NE(nullptr, trait);
329   const char kExpected1[] = R"({
330     "commands": {
331       "command1": {
332         "minimalRole": "user",
333         "parameters": {"height": {"type": "integer"}}
334       }
335     },
336     "state": {
337       "property1": {"type": "boolean"}
338     }
339   })";
340   EXPECT_JSON_EQ(kExpected1, *trait);
341 
342   trait = manager_.FindTraitDefinition("trait2");
343   ASSERT_NE(nullptr, trait);
344   const char kExpected2[] = R"({
345     "state": {
346       "property2": {"type": "string"}
347     }
348   })";
349   EXPECT_JSON_EQ(kExpected2, *trait);
350 
351   EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait3"));
352 }
353 
TEST_F(ComponentManagerTest,FindCommandDefinition)354 TEST_F(ComponentManagerTest, FindCommandDefinition) {
355   const char kTraits[] = R"({
356     "trait1": {
357       "commands": {
358         "command1": {
359           "minimalRole": "user",
360           "parameters": {"height": {"type": "integer"}}
361         }
362       }
363     },
364     "trait2": {
365       "commands": {
366         "command1": {
367           "minimalRole": "manager"
368         },
369         "command2": {
370           "minimalRole": "owner"
371         }
372       }
373     }
374   })";
375   auto json = CreateDictionaryValue(kTraits);
376   ASSERT_TRUE(manager_.LoadTraits(*json, nullptr));
377 
378   const auto* cmd_def = manager_.FindCommandDefinition("trait1.command1");
379   ASSERT_NE(nullptr, cmd_def);
380   const char kExpected1[] = R"({
381     "minimalRole": "user",
382     "parameters": {"height": {"type": "integer"}}
383   })";
384   EXPECT_JSON_EQ(kExpected1, *cmd_def);
385 
386   cmd_def = manager_.FindCommandDefinition("trait2.command1");
387   ASSERT_NE(nullptr, cmd_def);
388   const char kExpected2[] = R"({
389     "minimalRole": "manager"
390   })";
391   EXPECT_JSON_EQ(kExpected2, *cmd_def);
392 
393   cmd_def = manager_.FindCommandDefinition("trait2.command2");
394   ASSERT_NE(nullptr, cmd_def);
395   const char kExpected3[] = R"({
396     "minimalRole": "owner"
397   })";
398   EXPECT_JSON_EQ(kExpected3, *cmd_def);
399 
400   EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait1.command2"));
401   EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait3.command1"));
402   EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait"));
403   EXPECT_EQ(nullptr,
404             manager_.FindTraitDefinition("trait1.command1.parameters"));
405 }
406 
TEST_F(ComponentManagerTest,GetMinimalRole)407 TEST_F(ComponentManagerTest, GetMinimalRole) {
408   const char kTraits[] = R"({
409     "trait1": {
410       "commands": {
411         "command1": { "minimalRole": "user" },
412         "command2": { "minimalRole": "viewer" }
413       }
414     },
415     "trait2": {
416       "commands": {
417         "command1": { "minimalRole": "manager" },
418         "command2": { "minimalRole": "owner" }
419       }
420     }
421   })";
422   auto json = CreateDictionaryValue(kTraits);
423   ASSERT_TRUE(manager_.LoadTraits(*json, nullptr));
424 
425   UserRole role;
426   ASSERT_TRUE(manager_.GetMinimalRole("trait1.command1", &role, nullptr));
427   EXPECT_EQ(UserRole::kUser, role);
428 
429   ASSERT_TRUE(manager_.GetMinimalRole("trait1.command2", &role, nullptr));
430   EXPECT_EQ(UserRole::kViewer, role);
431 
432   ASSERT_TRUE(manager_.GetMinimalRole("trait2.command1", &role, nullptr));
433   EXPECT_EQ(UserRole::kManager, role);
434 
435   ASSERT_TRUE(manager_.GetMinimalRole("trait2.command2", &role, nullptr));
436   EXPECT_EQ(UserRole::kOwner, role);
437 
438   EXPECT_FALSE(manager_.GetMinimalRole("trait1.command3", &role, nullptr));
439 }
440 
TEST_F(ComponentManagerTest,AddComponent)441 TEST_F(ComponentManagerTest, AddComponent) {
442   const char kTraits[] = R"({"trait1": {}, "trait2": {}, "trait3": {}})";
443   auto json = CreateDictionaryValue(kTraits);
444   ASSERT_TRUE(manager_.LoadTraits(*json, nullptr));
445   EXPECT_TRUE(
446       manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
447   EXPECT_TRUE(manager_.AddComponent("", "comp2", {"trait3"}, nullptr));
448   const char kExpected[] = R"({
449     "comp1": {
450       "traits": ["trait1", "trait2"]
451     },
452     "comp2": {
453       "traits": ["trait3"]
454     }
455   })";
456   EXPECT_JSON_EQ(kExpected, manager_.GetComponents());
457 
458   // 'trait4' is undefined, so can't add a component referring to it.
459   EXPECT_FALSE(manager_.AddComponent("", "comp3", {"trait4"}, nullptr));
460 }
461 
TEST_F(ComponentManagerTest,AddSubComponent)462 TEST_F(ComponentManagerTest, AddSubComponent) {
463   EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr));
464   EXPECT_TRUE(manager_.AddComponent("comp1", "comp2", {}, nullptr));
465   EXPECT_TRUE(manager_.AddComponent("comp1", "comp3", {}, nullptr));
466   EXPECT_TRUE(manager_.AddComponent("comp1.comp2", "comp4", {}, nullptr));
467   const char kExpected[] = R"({
468     "comp1": {
469       "traits": [],
470       "components": {
471         "comp2": {
472           "traits": [],
473           "components": {
474             "comp4": {
475               "traits": []
476             }
477           }
478         },
479         "comp3": {
480           "traits": []
481         }
482       }
483     }
484   })";
485   EXPECT_JSON_EQ(kExpected, manager_.GetComponents());
486 }
487 
TEST_F(ComponentManagerTest,AddComponentArrayItem)488 TEST_F(ComponentManagerTest, AddComponentArrayItem) {
489   const char kTraits[] = R"({"foo": {}, "bar": {}})";
490   auto json = CreateDictionaryValue(kTraits);
491   ASSERT_TRUE(manager_.LoadTraits(*json, nullptr));
492 
493   EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr));
494   EXPECT_TRUE(
495       manager_.AddComponentArrayItem("comp1", "comp2", {"foo"}, nullptr));
496   EXPECT_TRUE(
497       manager_.AddComponentArrayItem("comp1", "comp2", {"bar"}, nullptr));
498   EXPECT_TRUE(manager_.AddComponent("comp1.comp2[1]", "comp3", {}, nullptr));
499   EXPECT_TRUE(
500       manager_.AddComponent("comp1.comp2[1].comp3", "comp4", {}, nullptr));
501   const char kExpected[] = R"({
502     "comp1": {
503       "traits": [],
504       "components": {
505         "comp2": [
506           {
507             "traits": ["foo"]
508           },
509           {
510             "traits": ["bar"],
511             "components": {
512               "comp3": {
513                 "traits": [],
514                 "components": {
515                   "comp4": {
516                     "traits": []
517                   }
518                 }
519               }
520             }
521           }
522         ]
523       }
524     }
525   })";
526   EXPECT_JSON_EQ(kExpected, manager_.GetComponents());
527 }
528 
TEST_F(ComponentManagerTest,RemoveComponent)529 TEST_F(ComponentManagerTest, RemoveComponent) {
530   CreateTestComponentTree(&manager_);
531   EXPECT_TRUE(manager_.RemoveComponent("comp1.comp2[1].comp3", "comp4",
532                                        nullptr));
533   const char kExpected1[] = R"({
534     "comp1": {
535       "traits": [ "t1" ],
536       "components": {
537         "comp2": [
538           {
539             "traits": [ "t2" ]
540           },
541           {
542             "traits": [ "t3" ],
543             "components": {
544               "comp3": {
545                 "traits": [ "t4" ],
546                 "components": {}
547               }
548             }
549           }
550         ]
551       }
552     }
553   })";
554   EXPECT_JSON_EQ(kExpected1, manager_.GetComponents());
555   EXPECT_TRUE(manager_.RemoveComponentArrayItem("comp1", "comp2", 1, nullptr));
556   const char kExpected2[] = R"({
557     "comp1": {
558       "traits": [ "t1" ],
559       "components": {
560         "comp2": [
561           {
562             "traits": [ "t2" ]
563           }
564         ]
565       }
566     }
567   })";
568   EXPECT_JSON_EQ(kExpected2, manager_.GetComponents());
569 }
570 
TEST_F(ComponentManagerTest,AddComponentExist)571 TEST_F(ComponentManagerTest, AddComponentExist) {
572   EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr));
573   EXPECT_FALSE(manager_.AddComponent("", "comp1", {}, nullptr));
574   EXPECT_TRUE(manager_.AddComponent("comp1", "comp2", {}, nullptr));
575   EXPECT_FALSE(manager_.AddComponent("comp1", "comp2", {}, nullptr));
576 }
577 
TEST_F(ComponentManagerTest,AddComponentDoesNotExist)578 TEST_F(ComponentManagerTest, AddComponentDoesNotExist) {
579   EXPECT_FALSE(manager_.AddComponent("comp1", "comp2", {}, nullptr));
580 }
581 
TEST_F(ComponentManagerTest,AddComponentTreeChangedCallback)582 TEST_F(ComponentManagerTest, AddComponentTreeChangedCallback) {
583   int count = 0;
584   int count2 = 0;
585   manager_.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; }));
586   manager_.AddComponentTreeChangedCallback(
587       base::Bind([&count2]() { count2++; }));
588   EXPECT_EQ(1, count);
589   EXPECT_EQ(1, count2);
590   EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr));
591   EXPECT_EQ(2, count);
592   EXPECT_TRUE(manager_.AddComponent("comp1", "comp2", {}, nullptr));
593   EXPECT_EQ(3, count);
594   EXPECT_TRUE(manager_.AddComponent("comp1.comp2", "comp4", {}, nullptr));
595   EXPECT_EQ(4, count);
596   EXPECT_TRUE(manager_.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
597   EXPECT_EQ(5, count);
598   EXPECT_TRUE(manager_.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
599   EXPECT_EQ(6, count);
600   EXPECT_TRUE(manager_.RemoveComponentArrayItem("comp1", "comp3", 1, nullptr));
601   EXPECT_EQ(7, count);
602   EXPECT_TRUE(manager_.RemoveComponent("", "comp1", nullptr));
603   EXPECT_EQ(8, count);
604   // Make sure both callbacks were called the same number of times.
605   EXPECT_EQ(count2, count);
606 }
607 
TEST_F(ComponentManagerTest,FindComponent)608 TEST_F(ComponentManagerTest, FindComponent) {
609   CreateTestComponentTree(&manager_);
610 
611   const base::DictionaryValue* comp = manager_.FindComponent("comp1", nullptr);
612   ASSERT_NE(nullptr, comp);
613   EXPECT_TRUE(HasTrait(*comp, "t1"));
614 
615   comp = manager_.FindComponent("comp1.comp2[0]", nullptr);
616   ASSERT_NE(nullptr, comp);
617   EXPECT_TRUE(HasTrait(*comp, "t2"));
618 
619   comp = manager_.FindComponent("comp1.comp2[1]", nullptr);
620   ASSERT_NE(nullptr, comp);
621   EXPECT_TRUE(HasTrait(*comp, "t3"));
622 
623   comp = manager_.FindComponent("comp1.comp2[1].comp3", nullptr);
624   ASSERT_NE(nullptr, comp);
625   EXPECT_TRUE(HasTrait(*comp, "t4"));
626 
627   comp = manager_.FindComponent("comp1.comp2[1].comp3.comp4", nullptr);
628   ASSERT_NE(nullptr, comp);
629   EXPECT_TRUE(HasTrait(*comp, "t5"));
630 
631   // Some whitespaces don't hurt.
632   comp = manager_.FindComponent(" comp1 . comp2 [  \t 1 ] .   comp3.comp4 ",
633                                 nullptr);
634   EXPECT_NE(nullptr, comp);
635 
636   // Now check some failure cases.
637   ErrorPtr error;
638   EXPECT_EQ(nullptr, manager_.FindComponent("", &error));
639   EXPECT_NE(nullptr, error.get());
640   // 'comp2' doesn't exist:
641   EXPECT_EQ(nullptr, manager_.FindComponent("comp2", nullptr));
642   // 'comp1.comp2' is an array, not a component:
643   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2", nullptr));
644   // 'comp1.comp2[3]' doesn't exist:
645   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[3]", nullptr));
646   // Empty component names:
647   EXPECT_EQ(nullptr, manager_.FindComponent(".comp2[1]", nullptr));
648   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.[1]", nullptr));
649   // Invalid array indices:
650   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[s]", nullptr));
651   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[-2]", nullptr));
652   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[1e1]", nullptr));
653   EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[1", nullptr));
654 }
655 
TEST_F(ComponentManagerTest,ParseCommandInstance)656 TEST_F(ComponentManagerTest, ParseCommandInstance) {
657   const char kTraits[] = R"({
658     "trait1": {
659       "commands": {
660         "command1": { "minimalRole": "user" },
661         "command2": { "minimalRole": "viewer" }
662       }
663     },
664     "trait2": {
665       "commands": {
666         "command1": { "minimalRole": "manager" },
667         "command2": { "minimalRole": "owner" }
668       }
669     },
670     "trait3": {
671       "commands": {
672         "command1": { "minimalRole": "manager" },
673         "command2": { "minimalRole": "owner" }
674       }
675     }
676   })";
677   auto traits = CreateDictionaryValue(kTraits);
678   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
679   ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr));
680   ASSERT_TRUE(manager_.AddComponent("", "comp2", {"trait2"}, nullptr));
681 
682   std::string id;
683   const char kCommand1[] = R"({
684     "name": "trait1.command1",
685     "id": "1234-12345",
686     "component": "comp1",
687     "parameters": {}
688   })";
689   auto command1 = CreateDictionaryValue(kCommand1);
690   EXPECT_NE(nullptr,
691             manager_.ParseCommandInstance(*command1, Command::Origin::kLocal,
692                                           UserRole::kUser, &id, nullptr)
693                 .get());
694   EXPECT_EQ("1234-12345", id);
695   // Not enough access rights
696   EXPECT_EQ(nullptr,
697             manager_.ParseCommandInstance(*command1, Command::Origin::kLocal,
698                                           UserRole::kViewer, &id, nullptr)
699                 .get());
700 
701   const char kCommand2[] = R"({
702     "name": "trait1.command3",
703     "component": "comp1",
704     "parameters": {}
705   })";
706   auto command2 = CreateDictionaryValue(kCommand2);
707   // trait1.command3 doesn't exist
708   EXPECT_EQ(nullptr,
709             manager_.ParseCommandInstance(*command2, Command::Origin::kLocal,
710                                           UserRole::kOwner, &id, nullptr)
711                 .get());
712   EXPECT_TRUE(id.empty());
713 
714   const char kCommand3[] = R"({
715     "name": "trait2.command1",
716     "component": "comp1",
717     "parameters": {}
718   })";
719   auto command3 = CreateDictionaryValue(kCommand3);
720   // Component comp1 doesn't have trait2.
721   EXPECT_EQ(nullptr,
722             manager_.ParseCommandInstance(*command3, Command::Origin::kLocal,
723                                           UserRole::kOwner, &id, nullptr)
724                 .get());
725 
726   // No component specified, find the suitable component
727   const char kCommand4[] = R"({
728     "name": "trait1.command1",
729     "parameters": {}
730   })";
731   auto command4 = CreateDictionaryValue(kCommand4);
732   auto command_instance = manager_.ParseCommandInstance(
733       *command4, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr);
734   EXPECT_NE(nullptr, command_instance.get());
735   EXPECT_EQ("comp1", command_instance->GetComponent());
736 
737   const char kCommand5[] = R"({
738     "name": "trait2.command1",
739     "parameters": {}
740   })";
741   auto command5 = CreateDictionaryValue(kCommand5);
742   command_instance = manager_.ParseCommandInstance(
743       *command5, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr);
744   EXPECT_NE(nullptr, command_instance.get());
745   EXPECT_EQ("comp2", command_instance->GetComponent());
746 
747   // Cannot route the command, no component with 'trait3'.
748   const char kCommand6[] = R"({
749     "name": "trait3.command1",
750     "parameters": {}
751   })";
752   auto command6 = CreateDictionaryValue(kCommand6);
753   EXPECT_EQ(nullptr,
754             manager_.ParseCommandInstance(*command6, Command::Origin::kLocal,
755                                           UserRole::kOwner, &id, nullptr)
756                 .get());
757 }
758 
TEST_F(ComponentManagerTest,AddCommand)759 TEST_F(ComponentManagerTest, AddCommand) {
760   const char kTraits[] = R"({
761     "trait1": {
762       "commands": {
763         "command1": { "minimalRole": "user" }
764       }
765     }
766   })";
767   auto traits = CreateDictionaryValue(kTraits);
768   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
769   ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr));
770 
771   std::string id;
772   const char kCommand[] = R"({
773     "name": "trait1.command1",
774     "id": "1234-12345",
775     "component": "comp1",
776     "parameters": {}
777   })";
778   auto command = CreateDictionaryValue(kCommand);
779   auto command_instance = manager_.ParseCommandInstance(
780       *command, Command::Origin::kLocal, UserRole::kUser, &id, nullptr);
781   ASSERT_NE(nullptr, command_instance.get());
782   manager_.AddCommand(std::move(command_instance));
783   const auto* queued_command = manager_.FindCommand(id);
784   ASSERT_NE(nullptr, queued_command);
785   EXPECT_EQ("trait1.command1", queued_command->GetName());
786 }
787 
TEST_F(ComponentManagerTest,AddCommandHandler)788 TEST_F(ComponentManagerTest, AddCommandHandler) {
789   const char kTraits[] = R"({
790     "trait1": {
791       "commands": {
792         "command1": { "minimalRole": "user" }
793       }
794     },
795     "trait2": {
796       "commands": {
797         "command2": { "minimalRole": "user" }
798       }
799     }
800   })";
801   auto traits = CreateDictionaryValue(kTraits);
802   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
803   ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr));
804   ASSERT_TRUE(
805       manager_.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
806 
807   std::string last_tags;
808   auto handler = [&last_tags](int tag, const std::weak_ptr<Command>& command) {
809     if (!last_tags.empty())
810       last_tags += ',';
811     last_tags += std::to_string(tag);
812   };
813 
814   manager_.AddCommandHandler("comp1", "trait1.command1",
815                              base::Bind(handler, 1));
816   manager_.AddCommandHandler("comp2", "trait1.command1",
817                              base::Bind(handler, 2));
818   manager_.AddCommandHandler("comp2", "trait2.command2",
819                              base::Bind(handler, 3));
820   EXPECT_TRUE(last_tags.empty());
821 
822   const char kCommand1[] = R"({
823     "name": "trait1.command1",
824     "component": "comp1"
825   })";
826   auto command1 = CreateDictionaryValue(kCommand1);
827   auto command_instance = manager_.ParseCommandInstance(
828       *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
829   ASSERT_NE(nullptr, command_instance.get());
830   manager_.AddCommand(std::move(command_instance));
831   EXPECT_EQ("1", last_tags);
832   last_tags.clear();
833 
834   const char kCommand2[] = R"({
835     "name": "trait1.command1",
836     "component": "comp2"
837   })";
838   auto command2 = CreateDictionaryValue(kCommand2);
839   command_instance = manager_.ParseCommandInstance(
840       *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
841   ASSERT_NE(nullptr, command_instance.get());
842   manager_.AddCommand(std::move(command_instance));
843   EXPECT_EQ("2", last_tags);
844   last_tags.clear();
845 
846   const char kCommand3[] = R"({
847     "name": "trait2.command2",
848     "component": "comp2",
849     "parameters": {}
850   })";
851   auto command3 = CreateDictionaryValue(kCommand3);
852   command_instance = manager_.ParseCommandInstance(
853       *command3, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
854   ASSERT_NE(nullptr, command_instance.get());
855   manager_.AddCommand(std::move(command_instance));
856   EXPECT_EQ("3", last_tags);
857   last_tags.clear();
858 }
859 
TEST_F(ComponentManagerTest,AddDefaultCommandHandler)860 TEST_F(ComponentManagerTest, AddDefaultCommandHandler) {
861   const char kTraits[] = R"({
862     "trait1": {
863       "commands": {
864         "command1": { "minimalRole": "user" }
865       }
866     },
867     "trait2": {
868       "commands": {
869         "command2": { "minimalRole": "user" }
870       }
871     }
872   })";
873   auto traits = CreateDictionaryValue(kTraits);
874   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
875   ASSERT_TRUE(manager_.AddComponent("", "comp", {"trait1", "trait2"}, nullptr));
876 
877   int count = 0;
878   auto handler = [&count](int tag, const std::weak_ptr<Command>& command) {
879     count++;
880   };
881 
882   manager_.AddCommandHandler("", "", base::Bind(handler, 1));
883   EXPECT_EQ(0, count);
884 
885   const char kCommand1[] = R"({
886     "name": "trait1.command1",
887     "component": "comp"
888   })";
889   auto command1 = CreateDictionaryValue(kCommand1);
890   auto command_instance = manager_.ParseCommandInstance(
891       *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
892   ASSERT_NE(nullptr, command_instance.get());
893   manager_.AddCommand(std::move(command_instance));
894   EXPECT_EQ(1, count);
895 
896   const char kCommand2[] = R"({
897     "name": "trait2.command2",
898     "component": "comp"
899   })";
900   auto command2 = CreateDictionaryValue(kCommand2);
901   command_instance = manager_.ParseCommandInstance(
902       *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
903   ASSERT_NE(nullptr, command_instance.get());
904   manager_.AddCommand(std::move(command_instance));
905   EXPECT_EQ(2, count);
906 }
907 
TEST_F(ComponentManagerTest,SetStateProperties)908 TEST_F(ComponentManagerTest, SetStateProperties) {
909   CreateTestComponentTree(&manager_);
910 
911   const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})";
912   auto state1 = CreateDictionaryValue(kState1);
913   ASSERT_TRUE(manager_.SetStateProperties("comp1", *state1, nullptr));
914   const char kExpected1[] = R"({
915     "comp1": {
916       "traits": [ "t1" ],
917       "state": {"t1": {"p1": 0, "p2": "foo"}},
918       "components": {
919         "comp2": [
920           {
921             "traits": [ "t2" ]
922           },
923           {
924             "traits": [ "t3" ],
925             "components": {
926               "comp3": {
927                 "traits": [ "t4" ],
928                 "components": {
929                   "comp4": {
930                     "traits": [ "t5", "t6" ]
931                   }
932                 }
933               }
934             }
935           }
936         ]
937       }
938     }
939   })";
940   EXPECT_JSON_EQ(kExpected1, manager_.GetComponents());
941 
942   const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})";
943   auto state2 = CreateDictionaryValue(kState2);
944   ASSERT_TRUE(manager_.SetStateProperties("comp1", *state2, nullptr));
945 
946   const char kExpected2[] = R"({
947     "comp1": {
948       "traits": [ "t1" ],
949       "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
950       "components": {
951         "comp2": [
952           {
953             "traits": [ "t2" ]
954           },
955           {
956             "traits": [ "t3" ],
957             "components": {
958               "comp3": {
959                 "traits": [ "t4" ],
960                 "components": {
961                   "comp4": {
962                     "traits": [ "t5", "t6" ]
963                   }
964                 }
965               }
966             }
967           }
968         ]
969       }
970     }
971   })";
972   EXPECT_JSON_EQ(kExpected2, manager_.GetComponents());
973 
974   const char kState3[] = R"({"t5": {"p1": 1}})";
975   auto state3 = CreateDictionaryValue(kState3);
976   ASSERT_TRUE(manager_.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3,
977                                           nullptr));
978 
979   const char kExpected3[] = R"({
980     "comp1": {
981       "traits": [ "t1" ],
982       "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
983       "components": {
984         "comp2": [
985           {
986             "traits": [ "t2" ]
987           },
988           {
989             "traits": [ "t3" ],
990             "components": {
991               "comp3": {
992                 "traits": [ "t4" ],
993                 "components": {
994                   "comp4": {
995                     "traits": [ "t5", "t6" ],
996                     "state": { "t5": { "p1": 1 } }
997                   }
998                 }
999               }
1000             }
1001           }
1002         ]
1003       }
1004     }
1005   })";
1006   EXPECT_JSON_EQ(kExpected3, manager_.GetComponents());
1007 }
1008 
TEST_F(ComponentManagerTest,SetStatePropertiesFromJson)1009 TEST_F(ComponentManagerTest, SetStatePropertiesFromJson) {
1010   CreateTestComponentTree(&manager_);
1011 
1012   ASSERT_TRUE(manager_.SetStatePropertiesFromJson(
1013       "comp1.comp2[1].comp3.comp4",
1014       R"({"t5": {"p1": 3}, "t6": {"p2": 5}})", nullptr));
1015 
1016   const char kExpected[] = R"({
1017     "comp1": {
1018       "traits": [ "t1" ],
1019       "components": {
1020         "comp2": [
1021           {
1022             "traits": [ "t2" ]
1023           },
1024           {
1025             "traits": [ "t3" ],
1026             "components": {
1027               "comp3": {
1028                 "traits": [ "t4" ],
1029                 "components": {
1030                   "comp4": {
1031                     "traits": [ "t5", "t6" ],
1032                     "state": {
1033                       "t5": { "p1": 3 },
1034                       "t6": { "p2": 5 }
1035                     }
1036                   }
1037                 }
1038               }
1039             }
1040           }
1041         ]
1042       }
1043     }
1044   })";
1045   EXPECT_JSON_EQ(kExpected, manager_.GetComponents());
1046 }
1047 
TEST_F(ComponentManagerTest,SetGetStateProperty)1048 TEST_F(ComponentManagerTest, SetGetStateProperty) {
1049   const char kTraits[] = R"({
1050     "trait1": {
1051       "state": {
1052         "prop1": { "type": "string" },
1053         "prop2": { "type": "integer" }
1054       }
1055     },
1056     "trait2": {
1057       "state": {
1058         "prop3": { "type": "string" },
1059         "prop4": { "type": "string" }
1060       }
1061     }
1062   })";
1063   auto traits = CreateDictionaryValue(kTraits);
1064   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
1065   ASSERT_TRUE(
1066       manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1067 
1068   base::StringValue p1("foo");
1069   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
1070 
1071   const char kExpected1[] = R"({
1072     "comp1": {
1073       "traits": [ "trait1", "trait2" ],
1074       "state": {
1075         "trait1": { "prop1": "foo" }
1076       }
1077     }
1078   })";
1079   EXPECT_JSON_EQ(kExpected1, manager_.GetComponents());
1080 
1081   base::FundamentalValue p2(2);
1082   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
1083 
1084   const char kExpected2[] = R"({
1085     "comp1": {
1086       "traits": [ "trait1", "trait2" ],
1087       "state": {
1088         "trait1": { "prop1": "foo" },
1089         "trait2": { "prop3": 2 }
1090       }
1091     }
1092   })";
1093   EXPECT_JSON_EQ(kExpected2, manager_.GetComponents());
1094   // Just the package name without property:
1095   EXPECT_FALSE(manager_.SetStateProperty("comp1", "trait2", p2, nullptr));
1096 
1097   const base::Value* value =
1098       manager_.GetStateProperty("comp1", "trait1.prop1", nullptr);
1099   ASSERT_NE(nullptr, value);
1100   EXPECT_TRUE(p1.Equals(value));
1101   value = manager_.GetStateProperty("comp1", "trait2.prop3", nullptr);
1102   ASSERT_NE(nullptr, value);
1103   EXPECT_TRUE(p2.Equals(value));
1104 
1105   // Non-existing property:
1106   EXPECT_EQ(nullptr, manager_.GetStateProperty("comp1", "trait2.p", nullptr));
1107   // Non-existing component
1108   EXPECT_EQ(nullptr, manager_.GetStateProperty("comp2", "trait.prop", nullptr));
1109   // Just the package name without property:
1110   EXPECT_EQ(nullptr, manager_.GetStateProperty("comp1", "trait2", nullptr));
1111 }
1112 
TEST_F(ComponentManagerTest,AddStateChangedCallback)1113 TEST_F(ComponentManagerTest, AddStateChangedCallback) {
1114   const char kTraits[] = R"({
1115     "trait1": {
1116       "state": {
1117         "prop1": { "type": "string" },
1118         "prop2": { "type": "string" }
1119       }
1120     }
1121   })";
1122   auto traits = CreateDictionaryValue(kTraits);
1123   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
1124   ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr));
1125 
1126   int count = 0;
1127   int count2 = 0;
1128   manager_.AddStateChangedCallback(base::Bind([&count]() { count++; }));
1129   manager_.AddStateChangedCallback(base::Bind([&count2]() { count2++; }));
1130   EXPECT_EQ(1, count);
1131   EXPECT_EQ(1, count2);
1132   EXPECT_EQ(0u, manager_.GetLastStateChangeId());
1133 
1134   base::StringValue p1("foo");
1135   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
1136   EXPECT_EQ(2, count);
1137   EXPECT_EQ(2, count2);
1138   EXPECT_EQ(1u, manager_.GetLastStateChangeId());
1139 
1140   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop2", p1, nullptr));
1141   EXPECT_EQ(3, count);
1142   EXPECT_EQ(3, count2);
1143   EXPECT_EQ(2u, manager_.GetLastStateChangeId());
1144 
1145   // Fail - no component.
1146   ASSERT_FALSE(manager_.SetStateProperty("comp2", "trait1.prop2", p1, nullptr));
1147   EXPECT_EQ(3, count);
1148   EXPECT_EQ(3, count2);
1149   EXPECT_EQ(2u, manager_.GetLastStateChangeId());
1150 }
1151 
TEST_F(ComponentManagerTest,ComponentStateUpdates)1152 TEST_F(ComponentManagerTest, ComponentStateUpdates) {
1153   const char kTraits[] = R"({
1154     "trait1": {
1155       "state": {
1156         "prop1": { "type": "string" },
1157         "prop2": { "type": "string" }
1158       }
1159     },
1160     "trait2": {
1161       "state": {
1162         "prop3": { "type": "string" },
1163         "prop4": { "type": "string" }
1164       }
1165     }
1166   })";
1167   auto traits = CreateDictionaryValue(kTraits);
1168   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
1169   ASSERT_TRUE(
1170       manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1171   ASSERT_TRUE(
1172       manager_.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
1173 
1174   std::vector<ComponentManager::UpdateID> updates1;
1175   auto callback1 = [&updates1](ComponentManager::UpdateID id) {
1176     updates1.push_back(id);
1177   };
1178   // State change queue is empty, callback should be called immediately.
1179   auto token1 = manager_.AddServerStateUpdatedCallback(base::Bind(callback1));
1180   ASSERT_EQ(1u, updates1.size());
1181   EXPECT_EQ(manager_.GetLastStateChangeId(), updates1.front());
1182   updates1.clear();
1183 
1184   base::StringValue foo("foo");
1185   base::Time time1 = base::Time::Now();
1186   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(time1));
1187   // These three updates should be grouped into two separate state change queue
1188   // items, since they all happen at the same time, but for two different
1189   // components.
1190   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", foo, nullptr));
1191   ASSERT_TRUE(manager_.SetStateProperty("comp2", "trait2.prop3", foo, nullptr));
1192   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop2", foo, nullptr));
1193 
1194   std::vector<ComponentManager::UpdateID> updates2;
1195   auto callback2 = [&updates2](ComponentManager::UpdateID id) {
1196     updates2.push_back(id);
1197   };
1198   // State change queue is not empty, so callback will be called later.
1199   auto token2 = manager_.AddServerStateUpdatedCallback(base::Bind(callback2));
1200   EXPECT_TRUE(updates2.empty());
1201 
1202   base::StringValue bar("bar");
1203   base::Time time2 = time1 + base::TimeDelta::FromSeconds(1);
1204   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(time2));
1205   // Two more update events (as above) but at |time2|.
1206   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", bar, nullptr));
1207   ASSERT_TRUE(manager_.SetStateProperty("comp2", "trait2.prop3", bar, nullptr));
1208   ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop2", bar, nullptr));
1209 
1210   auto snapshot = manager_.GetAndClearRecordedStateChanges();
1211   EXPECT_EQ(manager_.GetLastStateChangeId(), snapshot.update_id);
1212   ASSERT_EQ(4u, snapshot.state_changes.size());
1213 
1214   EXPECT_EQ("comp1", snapshot.state_changes[0].component);
1215   EXPECT_EQ(time1, snapshot.state_changes[0].timestamp);
1216   EXPECT_JSON_EQ(R"({"trait1":{"prop1":"foo","prop2":"foo"}})",
1217                  *snapshot.state_changes[0].changed_properties);
1218 
1219   EXPECT_EQ("comp2", snapshot.state_changes[1].component);
1220   EXPECT_EQ(time1, snapshot.state_changes[1].timestamp);
1221   EXPECT_JSON_EQ(R"({"trait2":{"prop3":"foo"}})",
1222                  *snapshot.state_changes[1].changed_properties);
1223 
1224   EXPECT_EQ("comp1", snapshot.state_changes[2].component);
1225   EXPECT_EQ(time2, snapshot.state_changes[2].timestamp);
1226   EXPECT_JSON_EQ(R"({"trait1":{"prop1":"bar","prop2":"bar"}})",
1227                  *snapshot.state_changes[2].changed_properties);
1228 
1229   EXPECT_EQ("comp2", snapshot.state_changes[3].component);
1230   EXPECT_EQ(time2, snapshot.state_changes[3].timestamp);
1231   EXPECT_JSON_EQ(R"({"trait2":{"prop3":"bar"}})",
1232                  *snapshot.state_changes[3].changed_properties);
1233 
1234   // Make sure previous GetAndClearRecordedStateChanges() clears the queue.
1235   auto snapshot2 = manager_.GetAndClearRecordedStateChanges();
1236   EXPECT_EQ(manager_.GetLastStateChangeId(), snapshot2.update_id);
1237   EXPECT_TRUE(snapshot2.state_changes.empty());
1238 
1239   // Now indicate that we have update the changes on the server.
1240   manager_.NotifyStateUpdatedOnServer(snapshot.update_id);
1241   ASSERT_EQ(1u, updates1.size());
1242   EXPECT_EQ(snapshot.update_id, updates1.front());
1243   ASSERT_EQ(1u, updates2.size());
1244   EXPECT_EQ(snapshot.update_id, updates2.front());
1245 }
1246 
TEST_F(ComponentManagerTest,FindComponentWithTrait)1247 TEST_F(ComponentManagerTest, FindComponentWithTrait) {
1248   const char kTraits[] = R"({
1249     "trait1": {},
1250     "trait2": {},
1251     "trait3": {}
1252   })";
1253   auto traits = CreateDictionaryValue(kTraits);
1254   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
1255   ASSERT_TRUE(
1256       manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1257   ASSERT_TRUE(manager_.AddComponent("", "comp2", {"trait3"}, nullptr));
1258 
1259   EXPECT_EQ("comp1", manager_.FindComponentWithTrait("trait1"));
1260   EXPECT_EQ("comp1", manager_.FindComponentWithTrait("trait2"));
1261   EXPECT_EQ("comp2", manager_.FindComponentWithTrait("trait3"));
1262   EXPECT_EQ("", manager_.FindComponentWithTrait("trait4"));
1263 }
1264 
TEST_F(ComponentManagerTest,AddLegacyCommandAndStateDefinitions)1265 TEST_F(ComponentManagerTest, AddLegacyCommandAndStateDefinitions) {
1266   const char kCommandDefs1[] = R"({
1267     "package1": {
1268       "command1": {
1269         "minimalRole": "user",
1270         "parameters": {"height": {"type": "integer"}}
1271       },
1272       "command2": {
1273         "minimalRole": "owner",
1274         "parameters": {}
1275       }
1276     },
1277     "package2": {
1278       "command1": { "minimalRole": "user" },
1279       "command2": { "minimalRole": "owner" }
1280     }
1281   })";
1282   auto json = CreateDictionaryValue(kCommandDefs1);
1283   EXPECT_TRUE(manager_.AddLegacyCommandDefinitions(*json, nullptr));
1284   const char kExpected1[] = R"({
1285     "package1": {
1286       "commands": {
1287         "command1": {
1288           "minimalRole": "user",
1289           "parameters": {"height": {"type": "integer"}}
1290         },
1291         "command2": {
1292           "minimalRole": "owner",
1293           "parameters": {}
1294         }
1295       }
1296     },
1297     "package2": {
1298       "commands": {
1299         "command1": { "minimalRole": "user" },
1300         "command2": { "minimalRole": "owner" }
1301       }
1302     }
1303   })";
1304   EXPECT_JSON_EQ(kExpected1, manager_.GetTraits());
1305   const char kExpectedComponents1[] = R"({
1306     "__weave__": { "traits": ["package1", "package2"] }
1307   })";
1308   EXPECT_JSON_EQ(kExpectedComponents1, manager_.GetComponents());
1309 
1310   const char kCommandDefs2[] = R"({
1311     "package2": {
1312       "command3": { "minimalRole": "user" }
1313     },
1314     "package3": {
1315       "command1": { "minimalRole": "user" },
1316       "command2": { "minimalRole": "owner" }
1317     }
1318   })";
1319   json = CreateDictionaryValue(kCommandDefs2);
1320   EXPECT_TRUE(manager_.AddLegacyCommandDefinitions(*json, nullptr));
1321   const char kExpected2[] = R"({
1322     "package1": {
1323       "commands": {
1324         "command1": {
1325           "minimalRole": "user",
1326           "parameters": {"height": {"type": "integer"}}
1327         },
1328         "command2": {
1329           "minimalRole": "owner",
1330           "parameters": {}
1331         }
1332       }
1333     },
1334     "package2": {
1335       "commands": {
1336         "command1": { "minimalRole": "user" },
1337         "command2": { "minimalRole": "owner" },
1338         "command3": { "minimalRole": "user" }
1339       }
1340     },
1341     "package3": {
1342       "commands": {
1343         "command1": { "minimalRole": "user" },
1344         "command2": { "minimalRole": "owner" }
1345       }
1346     }
1347   })";
1348   EXPECT_JSON_EQ(kExpected2, manager_.GetTraits());
1349   const char kExpectedComponents2[] = R"({
1350     "__weave__": { "traits": ["package1", "package2", "package3"] }
1351   })";
1352   EXPECT_JSON_EQ(kExpectedComponents2, manager_.GetComponents());
1353 
1354   // Redefining existing commands.
1355   EXPECT_FALSE(manager_.AddLegacyCommandDefinitions(*json, nullptr));
1356 
1357   const char kStateDefs1[] = R"({
1358     "package1": {
1359       "prop1": { "type": "string" },
1360       "prop2": { "type": "string" }
1361     },
1362     "package4": {
1363       "prop3": { "type": "string" },
1364       "prop4": { "type": "string" }
1365     }
1366   })";
1367   json = CreateDictionaryValue(kStateDefs1);
1368   EXPECT_TRUE(manager_.AddLegacyStateDefinitions(*json, nullptr));
1369   const char kExpectedComponents3[] = R"({
1370     "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
1371   })";
1372   EXPECT_JSON_EQ(kExpectedComponents3, manager_.GetComponents());
1373 
1374   const char kExpected3[] = R"({
1375     "package1": {
1376       "commands": {
1377         "command1": {
1378           "minimalRole": "user",
1379           "parameters": {"height": {"type": "integer"}}
1380         },
1381         "command2": {
1382           "minimalRole": "owner",
1383           "parameters": {}
1384         }
1385       },
1386       "state": {
1387         "prop1": { "type": "string" },
1388         "prop2": { "type": "string" }
1389       }
1390     },
1391     "package2": {
1392       "commands": {
1393         "command1": { "minimalRole": "user" },
1394         "command2": { "minimalRole": "owner" },
1395         "command3": { "minimalRole": "user" }
1396       }
1397     },
1398     "package3": {
1399       "commands": {
1400         "command1": { "minimalRole": "user" },
1401         "command2": { "minimalRole": "owner" }
1402       }
1403     },
1404     "package4": {
1405       "state": {
1406         "prop3": { "type": "string" },
1407         "prop4": { "type": "string" }
1408       }
1409     }
1410   })";
1411   EXPECT_JSON_EQ(kExpected3, manager_.GetTraits());
1412   const char kExpectedComponents4[] = R"({
1413     "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
1414   })";
1415   EXPECT_JSON_EQ(kExpectedComponents4, manager_.GetComponents());
1416 
1417   // Redefining existing commands.
1418   EXPECT_FALSE(manager_.AddLegacyStateDefinitions(*json, nullptr));
1419 
1420   const char kExpected4[] = R"({
1421     "package1": {
1422       "command1": {
1423         "minimalRole": "user",
1424         "parameters": {"height": {"type": "integer"}}
1425       },
1426       "command2": {
1427         "minimalRole": "owner",
1428         "parameters": {}
1429       }
1430     },
1431     "package2": {
1432       "command1": { "minimalRole": "user" },
1433       "command2": { "minimalRole": "owner" },
1434       "command3": { "minimalRole": "user" }
1435     },
1436     "package3": {
1437       "command1": { "minimalRole": "user" },
1438       "command2": { "minimalRole": "owner" }
1439     }
1440   })";
1441   EXPECT_JSON_EQ(kExpected4, manager_.GetLegacyCommandDefinitions());
1442 }
1443 
TEST_F(ComponentManagerTest,GetLegacyState)1444 TEST_F(ComponentManagerTest, GetLegacyState) {
1445   const char kTraits[] = R"({
1446     "trait1": {
1447       "state": {
1448         "prop1": { "type": "string" },
1449         "prop2": { "type": "string" }
1450       }
1451     },
1452     "trait2": {
1453       "state": {
1454         "prop3": { "type": "string" },
1455         "prop4": { "type": "string" }
1456       }
1457     }
1458   })";
1459   auto traits = CreateDictionaryValue(kTraits);
1460   ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr));
1461   ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr));
1462   ASSERT_TRUE(manager_.AddComponent("", "comp2", {"trait2"}, nullptr));
1463 
1464   ASSERT_TRUE(manager_.SetStatePropertiesFromJson(
1465       "comp1", R"({"trait1": {"prop1": "foo", "prop2": "bar"}})", nullptr));
1466   ASSERT_TRUE(manager_.SetStatePropertiesFromJson(
1467       "comp2", R"({"trait2": {"prop3": "baz", "prop4": "quux"}})", nullptr));
1468 
1469   const char kExpected[] = R"({
1470     "trait1": {
1471       "prop1": "foo",
1472       "prop2": "bar"
1473     },
1474     "trait2": {
1475       "prop3": "baz",
1476       "prop4": "quux"
1477     }
1478   })";
1479   EXPECT_JSON_EQ(kExpected, manager_.GetLegacyState());
1480 }
1481 
TEST_F(ComponentManagerTest,TestMockComponentManager)1482 TEST_F(ComponentManagerTest, TestMockComponentManager) {
1483   // Check that all the virtual methods are mocked out.
1484   MockComponentManager mock;
1485 }
1486 
1487 }  // namespace weave
1488