| // Copyright 2015 The Weave Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "src/states/state_change_queue.h" | 
 |  | 
 | #include <gtest/gtest.h> | 
 |  | 
 | #include "src/bind_lambda.h" | 
 | #include "src/commands/unittest_utils.h" | 
 |  | 
 | namespace weave { | 
 |  | 
 | class StateChangeQueueTest : public ::testing::Test { | 
 |  public: | 
 |   void SetUp() override { queue_.reset(new StateChangeQueue(100)); } | 
 |  | 
 |   void TearDown() override { queue_.reset(); } | 
 |  | 
 |   std::unique_ptr<StateChangeQueue> queue_; | 
 | }; | 
 |  | 
 | TEST_F(StateChangeQueueTest, Empty) { | 
 |   EXPECT_TRUE(queue_->IsEmpty()); | 
 |   EXPECT_EQ(0, queue_->GetLastStateChangeId()); | 
 |   EXPECT_TRUE(queue_->GetAndClearRecordedStateChanges().empty()); | 
 | } | 
 |  | 
 | TEST_F(StateChangeQueueTest, UpdateOne) { | 
 |   StateChange change{base::Time::Now(), | 
 |                      ValueMap{{"prop.name", test::make_int_prop_value(23)}}}; | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(change.timestamp, | 
 |                                               change.changed_properties)); | 
 |   EXPECT_FALSE(queue_->IsEmpty()); | 
 |   EXPECT_EQ(1, queue_->GetLastStateChangeId()); | 
 |   auto changes = queue_->GetAndClearRecordedStateChanges(); | 
 |   EXPECT_EQ(1, queue_->GetLastStateChangeId()); | 
 |   ASSERT_EQ(1, changes.size()); | 
 |   EXPECT_EQ(change.timestamp, changes.front().timestamp); | 
 |   EXPECT_EQ(change.changed_properties, changes.front().changed_properties); | 
 |   EXPECT_TRUE(queue_->IsEmpty()); | 
 |   EXPECT_TRUE(queue_->GetAndClearRecordedStateChanges().empty()); | 
 | } | 
 |  | 
 | // TODO(vitalybuka): Fix flakiness. | 
 | TEST_F(StateChangeQueueTest, DISABLED_UpdateMany) { | 
 |   StateChange change1{base::Time::Now(), | 
 |                       ValueMap{{"prop.name1", test::make_int_prop_value(23)}}}; | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(change1.timestamp, | 
 |                                               change1.changed_properties)); | 
 |   StateChange change2{base::Time::Now(), | 
 |                       ValueMap{ | 
 |                           {"prop.name1", test::make_int_prop_value(17)}, | 
 |                           {"prop.name2", test::make_double_prop_value(1.0)}, | 
 |                           {"prop.name3", test::make_bool_prop_value(false)}, | 
 |                       }}; | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(change2.timestamp, | 
 |                                               change2.changed_properties)); | 
 |   EXPECT_EQ(2, queue_->GetLastStateChangeId()); | 
 |   EXPECT_FALSE(queue_->IsEmpty()); | 
 |   auto changes = queue_->GetAndClearRecordedStateChanges(); | 
 |   ASSERT_EQ(2, changes.size()); | 
 |   EXPECT_EQ(change1.timestamp, changes[0].timestamp); | 
 |   EXPECT_EQ(change1.changed_properties, changes[0].changed_properties); | 
 |   EXPECT_EQ(change2.timestamp, changes[1].timestamp); | 
 |   EXPECT_EQ(change2.changed_properties, changes[1].changed_properties); | 
 |   EXPECT_TRUE(queue_->IsEmpty()); | 
 |   EXPECT_TRUE(queue_->GetAndClearRecordedStateChanges().empty()); | 
 | } | 
 |  | 
 | TEST_F(StateChangeQueueTest, GroupByTimestamp) { | 
 |   base::Time timestamp = base::Time::Now(); | 
 |   base::TimeDelta time_delta = base::TimeDelta::FromMinutes(1); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       timestamp, ValueMap{{"prop.name1", test::make_int_prop_value(1)}})); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       timestamp, ValueMap{{"prop.name2", test::make_int_prop_value(2)}})); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       timestamp, ValueMap{{"prop.name1", test::make_int_prop_value(3)}})); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       timestamp + time_delta, | 
 |       ValueMap{{"prop.name1", test::make_int_prop_value(4)}})); | 
 |  | 
 |   auto changes = queue_->GetAndClearRecordedStateChanges(); | 
 |   EXPECT_EQ(4, queue_->GetLastStateChangeId()); | 
 |   ASSERT_EQ(2, changes.size()); | 
 |  | 
 |   ValueMap expected1{ | 
 |       {"prop.name1", test::make_int_prop_value(3)}, | 
 |       {"prop.name2", test::make_int_prop_value(2)}, | 
 |   }; | 
 |   ValueMap expected2{ | 
 |       {"prop.name1", test::make_int_prop_value(4)}, | 
 |   }; | 
 |   EXPECT_EQ(timestamp, changes[0].timestamp); | 
 |   EXPECT_EQ(expected1, changes[0].changed_properties); | 
 |   EXPECT_EQ(timestamp + time_delta, changes[1].timestamp); | 
 |   EXPECT_EQ(expected2, changes[1].changed_properties); | 
 | } | 
 |  | 
 | TEST_F(StateChangeQueueTest, MaxQueueSize) { | 
 |   queue_.reset(new StateChangeQueue(2)); | 
 |   base::Time start_time = base::Time::Now(); | 
 |   base::TimeDelta time_delta1 = base::TimeDelta::FromMinutes(1); | 
 |   base::TimeDelta time_delta2 = base::TimeDelta::FromMinutes(3); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       start_time, ValueMap{ | 
 |                       {"prop.name1", test::make_int_prop_value(1)}, | 
 |                       {"prop.name2", test::make_int_prop_value(2)}, | 
 |                   })); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       start_time + time_delta1, | 
 |       ValueMap{ | 
 |           {"prop.name1", test::make_int_prop_value(3)}, | 
 |           {"prop.name3", test::make_int_prop_value(4)}, | 
 |       })); | 
 |  | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       start_time + time_delta2, | 
 |       ValueMap{ | 
 |           {"prop.name10", test::make_int_prop_value(10)}, | 
 |           {"prop.name11", test::make_int_prop_value(11)}, | 
 |       })); | 
 |  | 
 |   EXPECT_EQ(3, queue_->GetLastStateChangeId()); | 
 |   auto changes = queue_->GetAndClearRecordedStateChanges(); | 
 |   ASSERT_EQ(2, changes.size()); | 
 |  | 
 |   ValueMap expected1{ | 
 |       {"prop.name1", test::make_int_prop_value(3)}, | 
 |       {"prop.name2", test::make_int_prop_value(2)}, | 
 |       {"prop.name3", test::make_int_prop_value(4)}, | 
 |   }; | 
 |   EXPECT_EQ(start_time + time_delta1, changes[0].timestamp); | 
 |   EXPECT_EQ(expected1, changes[0].changed_properties); | 
 |  | 
 |   ValueMap expected2{ | 
 |       {"prop.name10", test::make_int_prop_value(10)}, | 
 |       {"prop.name11", test::make_int_prop_value(11)}, | 
 |   }; | 
 |   EXPECT_EQ(start_time + time_delta2, changes[1].timestamp); | 
 |   EXPECT_EQ(expected2, changes[1].changed_properties); | 
 | } | 
 |  | 
 | TEST_F(StateChangeQueueTest, ImmediateStateChangeNotification) { | 
 |   // When queue is empty, registering a new callback will trigger it. | 
 |   bool called = false; | 
 |   auto callback = [&called](StateChangeQueueInterface::UpdateID id) { | 
 |     called = true; | 
 |   }; | 
 |   queue_->AddOnStateUpdatedCallback(base::Bind(callback)); | 
 |   EXPECT_TRUE(called); | 
 | } | 
 |  | 
 | TEST_F(StateChangeQueueTest, DelayedStateChangeNotification) { | 
 |   // When queue is not empty, registering a new callback will not trigger it. | 
 |   ASSERT_TRUE(queue_->NotifyPropertiesUpdated( | 
 |       base::Time::Now(), ValueMap{ | 
 |                              {"prop.name1", test::make_int_prop_value(1)}, | 
 |                              {"prop.name2", test::make_int_prop_value(2)}, | 
 |                          })); | 
 |  | 
 |   auto callback = [](StateChangeQueueInterface::UpdateID id) { | 
 |     FAIL() << "This should not be called"; | 
 |   }; | 
 |   queue_->AddOnStateUpdatedCallback(base::Bind(callback)); | 
 | } | 
 |  | 
 | }  // namespace weave |