| // Copyright 2014 The Chromium OS 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 "buffet/commands/command_queue.h" | 
 |  | 
 | #include <set> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include <base/bind.h> | 
 | #include <base/memory/weak_ptr.h> | 
 | #include <chromeos/strings/string_utils.h> | 
 | #include <gtest/gtest.h> | 
 |  | 
 | #include "buffet/commands/command_definition.h" | 
 | #include "buffet/commands/object_schema.h" | 
 |  | 
 | namespace buffet { | 
 |  | 
 | class CommandQueueTest : public testing::Test { | 
 |  public: | 
 |   std::unique_ptr<CommandInstance> CreateDummyCommandInstance( | 
 |       const std::string& name, | 
 |       const std::string& id) { | 
 |     std::unique_ptr<CommandInstance> cmd{ | 
 |         new CommandInstance{name, "local", &command_definition_, {}}}; | 
 |     cmd->SetID(id); | 
 |     return cmd; | 
 |   } | 
 |  | 
 |   bool Remove(const std::string& id) { return queue_.Remove(id); } | 
 |  | 
 |   void Cleanup(const base::TimeDelta& interval) { | 
 |     queue_.SetNowForTest(base::Time::Now() + interval); | 
 |     return queue_.Cleanup(); | 
 |   } | 
 |  | 
 |   CommandQueue queue_; | 
 |  | 
 |  private: | 
 |   CommandDefinition command_definition_{"powerd", | 
 |                                         ObjectSchema::Create(), | 
 |                                         ObjectSchema::Create(), | 
 |                                         ObjectSchema::Create()}; | 
 | }; | 
 |  | 
 | // Keeps track of commands being added to and removed from the queue_. | 
 | // Aborts if duplicate commands are added or non-existent commands are removed. | 
 | class FakeDispatcher { | 
 |  public: | 
 |   explicit FakeDispatcher(CommandQueue* queue) { | 
 |     queue->AddOnCommandAddedCallback( | 
 |         base::Bind(&FakeDispatcher::OnCommandAdded, | 
 |                    weak_ptr_factory_.GetWeakPtr())); | 
 |     queue->AddOnCommandRemovedCallback( | 
 |         base::Bind(&FakeDispatcher::OnCommandRemoved, | 
 |                    weak_ptr_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   void OnCommandAdded(CommandInstance* command_instance) { | 
 |     CHECK(ids_.insert(command_instance->GetID()).second) | 
 |         << "Command ID already exists: " << command_instance->GetID(); | 
 |     CHECK(commands_.insert(command_instance).second) | 
 |         << "Command instance already exists"; | 
 |   } | 
 |  | 
 |   void OnCommandRemoved(CommandInstance* command_instance) { | 
 |     CHECK_EQ(1u, ids_.erase(command_instance->GetID())) | 
 |         << "Command ID not found: " << command_instance->GetID(); | 
 |     CHECK_EQ(1u, commands_.erase(command_instance)) | 
 |         << "Command instance not found"; | 
 |   } | 
 |  | 
 |   // Get the comma-separated list of command IDs currently accumulated in the | 
 |   // command queue_. | 
 |   std::string GetIDs() const { | 
 |     using chromeos::string_utils::Join; | 
 |     return Join(",", std::vector<std::string>(ids_.begin(), ids_.end())); | 
 |   } | 
 |  | 
 |  private: | 
 |   std::set<std::string> ids_; | 
 |   std::set<CommandInstance*> commands_; | 
 |   base::WeakPtrFactory<FakeDispatcher> weak_ptr_factory_{this}; | 
 | }; | 
 |  | 
 | TEST_F(CommandQueueTest, Empty) { | 
 |   EXPECT_TRUE(queue_.IsEmpty()); | 
 |   EXPECT_EQ(0, queue_.GetCount()); | 
 | } | 
 |  | 
 | TEST_F(CommandQueueTest, Add) { | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", "id1")); | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", "id2")); | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", "id3")); | 
 |   EXPECT_EQ(3, queue_.GetCount()); | 
 |   EXPECT_FALSE(queue_.IsEmpty()); | 
 | } | 
 |  | 
 | TEST_F(CommandQueueTest, Remove) { | 
 |   const std::string id1 = "id1"; | 
 |   const std::string id2 = "id2"; | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", id2)); | 
 |   EXPECT_FALSE(queue_.IsEmpty()); | 
 |   EXPECT_FALSE(Remove("dummy")); | 
 |   EXPECT_EQ(2, queue_.GetCount()); | 
 |   EXPECT_TRUE(Remove(id1)); | 
 |   EXPECT_EQ(1, queue_.GetCount()); | 
 |   EXPECT_FALSE(Remove(id1)); | 
 |   EXPECT_EQ(1, queue_.GetCount()); | 
 |   EXPECT_TRUE(Remove(id2)); | 
 |   EXPECT_EQ(0, queue_.GetCount()); | 
 |   EXPECT_FALSE(Remove(id2)); | 
 |   EXPECT_EQ(0, queue_.GetCount()); | 
 |   EXPECT_TRUE(queue_.IsEmpty()); | 
 | } | 
 |  | 
 | TEST_F(CommandQueueTest, DelayedRemove) { | 
 |   const std::string id1 = "id1"; | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); | 
 |   EXPECT_EQ(1, queue_.GetCount()); | 
 |  | 
 |   queue_.DelayedRemove(id1); | 
 |   EXPECT_EQ(1, queue_.GetCount()); | 
 |  | 
 |   Cleanup(base::TimeDelta::FromMinutes(1)); | 
 |   EXPECT_EQ(1, queue_.GetCount()); | 
 |  | 
 |   Cleanup(base::TimeDelta::FromMinutes(15)); | 
 |   EXPECT_EQ(0, queue_.GetCount()); | 
 | } | 
 |  | 
 | TEST_F(CommandQueueTest, Dispatch) { | 
 |   FakeDispatcher dispatch(&queue_); | 
 |   const std::string id1 = "id1"; | 
 |   const std::string id2 = "id2"; | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", id2)); | 
 |   std::set<std::string> ids{id1, id2};  // Make sure they are sorted properly. | 
 |   std::string expected_set = chromeos::string_utils::Join( | 
 |       ",", std::vector<std::string>(ids.begin(), ids.end())); | 
 |   EXPECT_EQ(expected_set, dispatch.GetIDs()); | 
 |   Remove(id1); | 
 |   EXPECT_EQ(id2, dispatch.GetIDs()); | 
 |   Remove(id2); | 
 |   EXPECT_EQ("", dispatch.GetIDs()); | 
 | } | 
 |  | 
 | TEST_F(CommandQueueTest, Find) { | 
 |   const std::string id1 = "id1"; | 
 |   const std::string id2 = "id2"; | 
 |   queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); | 
 |   queue_.Add(CreateDummyCommandInstance("base.shutdown", id2)); | 
 |   EXPECT_EQ(nullptr, queue_.Find("dummy")); | 
 |   auto cmd1 = queue_.Find(id1); | 
 |   EXPECT_NE(nullptr, cmd1); | 
 |   EXPECT_EQ("base.reboot", cmd1->GetName()); | 
 |   EXPECT_EQ(id1, cmd1->GetID()); | 
 |   auto cmd2 = queue_.Find(id2); | 
 |   EXPECT_NE(nullptr, cmd2); | 
 |   EXPECT_EQ("base.shutdown", cmd2->GetName()); | 
 |   EXPECT_EQ(id2, cmd2->GetID()); | 
 | } | 
 |  | 
 | }  // namespace buffet |