Add support for state updates on ComponentManager

Implemented state support functionality from StateManager. Now the
ComponentManager object can replace StateManager, StatePackage,
CommandDictionary and CommandManager classes.

Most implementation was borrowed/adapted from existing StateManager
and StatePackage classes. And the interfaces were made compatible
with those two classes so the new ComponentManager can be used as
a drop-in replacement of the old classes.

BUG: 25841719
Change-Id: Ib981de1cf44c47a95594c56d78ac211812b3fa44
Reviewed-on: https://weave-review.googlesource.com/1780
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/src/component_manager_unittest.cc b/src/component_manager_unittest.cc
index ba6bc34..83f9933 100644
--- a/src/component_manager_unittest.cc
+++ b/src/component_manager_unittest.cc
@@ -30,6 +30,71 @@
   return false;
 }
 
+// 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" ]

+//                   }

+//                 }

+//               }

+//             }

+//           }

+//         ],

+//       }

+//     }

+//   }

+// }
+void CreateTestComponentTree(ComponentManager* manager) {
+  const char kTraits[] = R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager->LoadTraits(*json, nullptr));
+  EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr));
+  EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t2"},
+                                             nullptr));
+  EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t3"},
+                                             nullptr));
+  EXPECT_TRUE(manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"},
+                                    nullptr));
+  EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4",
+                                    {"t5", "t6"}, nullptr));
+}
+
+// Test clock class to record predefined time intervals.
+// Implementation from base/test/simple_test_clock.{h|cc}
+class SimpleTestClock : public base::Clock {
+ public:
+  base::Time Now() override { return now_; }
+
+  // Advances the clock by |delta|.
+  void Advance(base::TimeDelta delta) { now_ += delta; }
+
+  // Sets the clock to the given time.
+  void SetNow(base::Time now) { now_ = now; }
+
+ private:
+  base::Time now_;
+};
+
 }  // anonymous namespace
 
 TEST(ComponentManager, Empty) {
@@ -176,6 +241,63 @@
   EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
 }
 
+TEST(ComponentManager, AddTraitDefChangedCallback) {
+  ComponentManager manager;
+  int count = 0;
+  int count2 = 0;
+  manager.AddTraitDefChangedCallback(base::Bind([&count]() { count++; }));
+  manager.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; }));
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(1, count2);
+  // New definitions.
+  const char kTraits1[] = R"({
+    "trait1": {
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits1);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(2, count);
+  // Duplicate definition, shouldn't call the callback.
+  const char kTraits2[] = R"({
+    "trait1": {
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    }
+  })";
+  json = CreateDictionaryValue(kTraits2);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(2, count);
+  // New definition, should call the callback now.
+  const char kTraits3[] = R"({
+    "trait3": {
+      "state": {
+        "property3": {"type": "string"}
+      }
+    }
+  })";
+  json = CreateDictionaryValue(kTraits3);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(3, count);
+  // Wrong definition, shouldn't call the callback.
+  const char kTraits4[] = R"({
+    "trait4": "foo"
+  })";
+  json = CreateDictionaryValue(kTraits4);
+  EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(3, count);
+  // Make sure both callbacks were called the same number of times.
+  EXPECT_EQ(count2, count);
+}
+
 TEST(ComponentManager, LoadTraitsNotAnObject) {
   ComponentManager manager;
   const char kTraits1[] = R"({"trait1": 0})";
@@ -426,18 +548,32 @@
   EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
 }
 
+TEST(ComponentManager, AddComponentTreeChangedCallback) {
+  ComponentManager manager;
+  int count = 0;
+  int count2 = 0;
+  manager.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; }));
+  manager.AddComponentTreeChangedCallback(
+      base::Bind([&count2]() { count2++; }));
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(1, count2);
+  EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
+  EXPECT_EQ(2, count);
+  EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
+  EXPECT_EQ(3, count);
+  EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
+  EXPECT_EQ(4, count);
+  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
+  EXPECT_EQ(5, count);
+  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
+  EXPECT_EQ(6, count);
+  // Make sure both callbacks were called the same number of times.
+  EXPECT_EQ(count2, count);
+}
+
 TEST(ComponentManager, FindComponent) {
   ComponentManager manager;
-  const char kTraits[] = R"({"t1":{}, "t2":{}, "t3":{}, "t4":{}, "t5":{}})";
-  auto json = CreateDictionaryValue(kTraits);
-  ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
-
-  EXPECT_TRUE(manager.AddComponent("", "comp1", {"t1"}, nullptr));
-  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"t2"}, nullptr));
-  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"t3"}, nullptr));
-  EXPECT_TRUE(manager.AddComponent("comp1.comp2[1]", "comp3", {"t4"}, nullptr));
-  EXPECT_TRUE(manager.AddComponent("comp1.comp2[1].comp3", "comp4", {"t5"},
-                                   nullptr));
+  CreateTestComponentTree(&manager);
 
   const base::DictionaryValue* comp = manager.FindComponent("comp1", nullptr);
   ASSERT_NE(nullptr, comp);
@@ -606,4 +742,347 @@
   last_tags.clear();
 }
 
+TEST(ComponentManager, SetStateProperties) {
+  ComponentManager manager;
+  CreateTestComponentTree(&manager);
+
+  const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})";
+  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" ]

+                  }

+                }

+              }

+            }

+          }

+        ]

+      }

+    }

+  })";
+  EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
+
+  const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})";
+  auto state2 = CreateDictionaryValue(kState2);
+  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" ]

+                  }

+                }

+              }

+            }

+          }

+        ]

+      }

+    }

+  })";
+  EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
+
+  const char kState3[] = R"({"t5": {"p1": 1}})";
+  auto state3 = CreateDictionaryValue(kState3);
+  ASSERT_TRUE(manager.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3,
+                                         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 } }

+                  }

+                }

+              }

+            }

+          }

+        ]

+      }

+    }

+  })";
+  EXPECT_JSON_EQ(kExpected3, manager.GetComponents());
+}
+
+TEST(ComponentManager, SetStatePropertiesFromJson) {
+  ComponentManager manager;
+  CreateTestComponentTree(&manager);
+
+  ASSERT_TRUE(manager.SetStatePropertiesFromJson(
+      "comp1.comp2[1].comp3.comp4", R"({"t5": {"p1": 3}, "t6": {"p2": 5}})",
+      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 }

+                    }

+                  }

+                }

+              }

+            }

+          }

+        ]

+      }

+    }

+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetComponents());
+}
+
+TEST(ComponentManager, SetGetStateProperty) {
+  ComponentManager manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "integer" }
+      }
+    },
+    "trait2": {
+      "state": {
+        "prop3": { "type": "string" },
+        "prop4": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+
+  base::StringValue p1("foo");
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
+
+  const char kExpected1[] = R"({
+    "comp1": {

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

+      "state": {

+        "trait1": { "prop1": "foo" }

+      }

+    }

+  })";
+  EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
+
+  base::FundamentalValue p2(2);
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
+
+  const char kExpected2[] = R"({
+    "comp1": {

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

+      "state": {

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

+        "trait2": { "prop3": 2 }

+      }

+    }

+  })";
+  EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
+  // Just the package name without property:
+  EXPECT_FALSE(manager.SetStateProperty("comp1", "trait2", p2, nullptr));
+
+  const base::Value* value = manager.GetStateProperty("comp1", "trait1.prop1",
+                                                      nullptr);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(p1.Equals(value));
+  value = manager.GetStateProperty("comp1", "trait2.prop3", nullptr);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(p2.Equals(value));
+
+  // Non-existing property:
+  EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2.p", nullptr));
+  // Non-existing component
+  EXPECT_EQ(nullptr, manager.GetStateProperty("comp2", "trait.prop", nullptr));
+  // Just the package name without property:
+  EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2", nullptr));
+}
+
+TEST(ComponentManager, AddStateChangedCallback) {
+  SimpleTestClock clock;
+  ComponentManager manager{&clock};
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
+
+  int count = 0;
+  int count2 = 0;
+  manager.AddStateChangedCallback(base::Bind([&count]() { count++; }));
+  manager.AddStateChangedCallback(base::Bind([&count2]() { count2++; }));
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(1, count2);
+  EXPECT_EQ(0, manager.GetLastStateChangeId());
+
+  base::StringValue p1("foo");
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
+  EXPECT_EQ(2, count);
+  EXPECT_EQ(2, count2);
+  EXPECT_EQ(1, manager.GetLastStateChangeId());
+
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", p1, nullptr));
+  EXPECT_EQ(3, count);
+  EXPECT_EQ(3, count2);
+  EXPECT_EQ(2, manager.GetLastStateChangeId());
+
+  // Fail - no component.
+  ASSERT_FALSE(manager.SetStateProperty("comp2", "trait1.prop2", p1, nullptr));
+  EXPECT_EQ(3, count);
+  EXPECT_EQ(3, count2);
+  EXPECT_EQ(2, manager.GetLastStateChangeId());
+}
+
+TEST(ComponentManager, ComponentStateUpdates) {
+  SimpleTestClock clock;
+  ComponentManager manager{&clock};
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "string" }
+      }
+    },
+    "trait2": {
+      "state": {
+        "prop3": { "type": "string" },
+        "prop4": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
+
+  std::vector<ComponentManager::UpdateID> updates1;
+  auto callback1 = [&updates1](ComponentManager::UpdateID id) {
+    updates1.push_back(id);
+  };
+  // State change queue is empty, callback should be called immediately.
+  auto token1 = manager.AddServerStateUpdatedCallback(base::Bind(callback1));
+  ASSERT_EQ(1u, updates1.size());
+  EXPECT_EQ(manager.GetLastStateChangeId(), updates1.front());
+  updates1.clear();
+
+  base::StringValue foo("foo");
+  base::Time time1 = base::Time::Now();
+  clock.SetNow(time1);
+  // These three updates should be grouped into two separate state change queue
+  // items, since they all happen at the same time, but for two different
+  // components.
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", foo, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", foo, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", foo, nullptr));
+
+  std::vector<ComponentManager::UpdateID> updates2;
+  auto callback2 = [&updates2](ComponentManager::UpdateID id) {
+    updates2.push_back(id);
+  };
+  // State change queue is not empty, so callback will be called later.
+  auto token2 = manager.AddServerStateUpdatedCallback(base::Bind(callback2));
+  EXPECT_TRUE(updates2.empty());
+
+  base::StringValue bar("bar");
+  base::Time time2 = time1 + base::TimeDelta::FromSeconds(1);
+  clock.SetNow(time2);
+  // Two more update events (as above) but at |time2|.
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", bar, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", bar, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", bar, nullptr));
+
+  auto snapshot = manager.GetAndClearRecordedStateChanges();
+  EXPECT_EQ(manager.GetLastStateChangeId(), snapshot.update_id);
+  ASSERT_EQ(4u, snapshot.state_changes.size());
+
+  EXPECT_EQ("comp1", snapshot.state_changes[0].component);
+  EXPECT_EQ(time1, snapshot.state_changes[0].timestamp);
+  EXPECT_JSON_EQ(R"({"trait1":{"prop1":"foo","prop2":"foo"}})",
+                 *snapshot.state_changes[0].changed_properties);
+
+  EXPECT_EQ("comp2", snapshot.state_changes[1].component);
+  EXPECT_EQ(time1, snapshot.state_changes[1].timestamp);
+  EXPECT_JSON_EQ(R"({"trait2":{"prop3":"foo"}})",
+                 *snapshot.state_changes[1].changed_properties);
+
+  EXPECT_EQ("comp1", snapshot.state_changes[2].component);
+  EXPECT_EQ(time2, snapshot.state_changes[2].timestamp);
+  EXPECT_JSON_EQ(R"({"trait1":{"prop1":"bar","prop2":"bar"}})",
+                 *snapshot.state_changes[2].changed_properties);
+
+  EXPECT_EQ("comp2", snapshot.state_changes[3].component);
+  EXPECT_EQ(time2, snapshot.state_changes[3].timestamp);
+  EXPECT_JSON_EQ(R"({"trait2":{"prop3":"bar"}})",
+                 *snapshot.state_changes[3].changed_properties);
+
+  // Make sure previous GetAndClearRecordedStateChanges() clears the queue.
+  auto snapshot2 = manager.GetAndClearRecordedStateChanges();
+  EXPECT_EQ(manager.GetLastStateChangeId(), snapshot2.update_id);
+  EXPECT_TRUE(snapshot2.state_changes.empty());
+
+  // Now indicate that we have update the changes on the server.
+  manager.NotifyStateUpdatedOnServer(snapshot.update_id);
+  ASSERT_EQ(1u, updates1.size());
+  EXPECT_EQ(snapshot.update_id, updates1.front());
+  ASSERT_EQ(1u, updates2.size());
+  EXPECT_EQ(snapshot.update_id, updates2.front());
+}
+
 }  // namespace weave