buffet: Fix issues in StateChangeQueue

Addressed additional code review issues brought after the original
code has been published.

- NotifyPropertiesUpdated() now takes timestamp and property set
  as separate parameters to simplify implementation of StateManager.
- StateChangeQueue::max_queue_size_ is made 'const'
- StateChangeQueue now maintains a time-to-event map instead of
  a vector of StateChange. This allows to keep the events sorted
  by time stamp. Also adding discrete events with the same time stamp
  coalesces the property changes.
- added a unit test for coalescing property set changes based on
  the timestamp.

BUG=None
TEST=FEATURES=test emerge-link buffet

Change-Id: I309816d1f040558620fa68a844b05251d0e4319b
Reviewed-on: https://chromium-review.googlesource.com/226300
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Anton Muhin <antonm@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/states/state_manager_unittest.cc b/buffet/states/state_manager_unittest.cc
index b4c3094..df2260a 100644
--- a/buffet/states/state_manager_unittest.cc
+++ b/buffet/states/state_manager_unittest.cc
@@ -44,11 +44,6 @@
   })");
 }
 
-MATCHER_P(IsStateChange, prop_set, "") {
-  return arg.property_set == prop_set &&
-         std::abs((arg.timestamp - base::Time::Now()).InSeconds()) < 2;
-}
-
 }  // anonymous namespace
 
 class StateManagerTest : public ::testing::Test {
@@ -56,7 +51,8 @@
   void SetUp() override {
     // Initial expectations.
     EXPECT_CALL(mock_state_change_queue_, IsEmpty()).Times(0);
-    EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(_)).Times(0);
+    EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(_, _))
+      .Times(0);
     EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges())
       .Times(0);
     mgr_.reset(new StateManager(&mock_state_change_queue_));
@@ -109,33 +105,22 @@
   chromeos::VariantDictionary expected_prop_set{
     {"terminator.target", std::string{"John Connor"}},
   };
+  base::Time timestamp = base::Time::Now();
   EXPECT_CALL(mock_state_change_queue_,
-              NotifyPropertiesUpdated(IsStateChange(expected_prop_set)))
+              NotifyPropertiesUpdated(timestamp, expected_prop_set))
       .WillOnce(Return(true));
   ASSERT_TRUE(mgr_->SetPropertyValue("terminator.target",
-                                     std::string{"John Connor"}, nullptr));
+                                     std::string{"John Connor"},
+                                     timestamp,
+                                     nullptr));
   EXPECT_EQ("{'base':{'manufacturer':'Skynet','serialNumber':'T1000'},"
             "'terminator':{'target':'John Connor'}}",
             ValueToString(mgr_->GetStateValuesAsJson(nullptr).get()));
 }
 
-TEST_F(StateManagerTest, UpdateProperties) {
-  chromeos::VariantDictionary prop_set{
-    {"base.serialNumber", std::string{"T1000.1"}},
-    {"terminator.target", std::string{"Sarah Connor"}},
-  };
-  EXPECT_CALL(mock_state_change_queue_,
-              NotifyPropertiesUpdated(IsStateChange(prop_set)))
-      .WillOnce(Return(true));
-  ASSERT_TRUE(mgr_->UpdateProperties(prop_set, nullptr));
-  EXPECT_EQ("{'base':{'manufacturer':'Skynet','serialNumber':'T1000.1'},"
-            "'terminator':{'target':'Sarah Connor'}}",
-            ValueToString(mgr_->GetStateValuesAsJson(nullptr).get()));
-}
-
 TEST_F(StateManagerTest, SetPropertyValue_Error_NoName) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("", int{0}, &error));
+  ASSERT_FALSE(mgr_->SetPropertyValue("", int{0}, base::Time::Now(), &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPropertyNameMissing, error->GetCode());
   EXPECT_EQ("Property name is missing", error->GetMessage());
@@ -143,7 +128,8 @@
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_NoPackage) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("target", int{0}, &error));
+  ASSERT_FALSE(mgr_->SetPropertyValue("target", int{0}, base::Time::Now(),
+                                      &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPackageNameMissing, error->GetCode());
   EXPECT_EQ("Package name is missing in the property name",
@@ -152,7 +138,8 @@
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownPackage) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("power.level", int{0}, &error));
+  ASSERT_FALSE(mgr_->SetPropertyValue("power.level", int{0}, base::Time::Now(),
+                                      &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
   EXPECT_EQ("Unknown state property package 'power'", error->GetMessage());
@@ -160,28 +147,33 @@
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownProperty) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("base.level", int{0}, &error));
+  ASSERT_FALSE(mgr_->SetPropertyValue("base.level", int{0}, base::Time::Now(),
+                                      &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
   EXPECT_EQ("State property 'base.level' is not defined", error->GetMessage());
 }
 
-TEST_F(StateManagerTest, RetrievePropertyChanges) {
-  EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(_))
+TEST_F(StateManagerTest, GetAndClearRecordedStateChanges) {
+  base::Time timestamp = base::Time::Now();
+  EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(timestamp, _))
       .WillOnce(Return(true));
   ASSERT_TRUE(mgr_->SetPropertyValue("terminator.target",
-                                     std::string{"John Connor"}, nullptr));
+                                     std::string{"John Connor"},
+                                     timestamp,
+                                     nullptr));
   std::vector<StateChange> expected_val;
-  expected_val.emplace_back();
-  expected_val.back().timestamp = base::Time::Now();
-  expected_val.back().property_set.emplace("terminator.target",
-                                           std::string{"John Connor"});
+  expected_val.emplace_back(
+      timestamp,
+      chromeos::VariantDictionary{{"terminator.target",
+                                   std::string{"John Connor"}}});
   EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges())
       .WillOnce(Return(expected_val));
   auto changes = mgr_->GetAndClearRecordedStateChanges();
   ASSERT_EQ(1, changes.size());
   EXPECT_EQ(expected_val.back().timestamp, changes.back().timestamp);
-  EXPECT_EQ(expected_val.back().property_set, changes.back().property_set);
+  EXPECT_EQ(expected_val.back().changed_properties,
+            changes.back().changed_properties);
 }