Home | History | Annotate | Download | only in commands
      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/commands/command_queue.h"
      6 
      7 #include <set>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include <base/bind.h>
     12 #include <base/memory/weak_ptr.h>
     13 #include <gmock/gmock.h>
     14 #include <gtest/gtest.h>
     15 #include <weave/provider/test/fake_task_runner.h>
     16 
     17 #include "src/bind_lambda.h"
     18 #include "src/string_utils.h"
     19 
     20 namespace weave {
     21 
     22 using testing::Return;
     23 using testing::StrictMock;
     24 
     25 class CommandQueueTest : public testing::Test {
     26  public:
     27   std::unique_ptr<CommandInstance> CreateDummyCommandInstance(
     28       const std::string& name,
     29       const std::string& id) {
     30     std::unique_ptr<CommandInstance> cmd{
     31         new CommandInstance{name, Command::Origin::kLocal, {}}};
     32     cmd->SetID(id);
     33     return cmd;
     34   }
     35 
     36   bool Remove(const std::string& id) { return queue_.Remove(id); }
     37 
     38   void Cleanup(const base::TimeDelta& interval) {
     39     return queue_.Cleanup(task_runner_.GetClock()->Now() + interval);
     40   }
     41 
     42   std::string GetFirstCommandToBeRemoved() const {
     43     return queue_.remove_queue_.top().second;
     44   }
     45 
     46   StrictMock<provider::test::FakeTaskRunner> task_runner_;
     47   CommandQueue queue_{&task_runner_, task_runner_.GetClock()};
     48 };
     49 
     50 // Keeps track of commands being added to and removed from the queue_.
     51 // Aborts if duplicate commands are added or non-existent commands are removed.
     52 class FakeDispatcher {
     53  public:
     54   explicit FakeDispatcher(CommandQueue* queue) {
     55     queue->AddCommandAddedCallback(base::Bind(&FakeDispatcher::OnCommandAdded,
     56                                               weak_ptr_factory_.GetWeakPtr()));
     57     queue->AddCommandRemovedCallback(base::Bind(
     58         &FakeDispatcher::OnCommandRemoved, weak_ptr_factory_.GetWeakPtr()));
     59   }
     60 
     61   void OnCommandAdded(Command* command) {
     62     CHECK(ids_.insert(command->GetID()).second) << "Command ID already exists: "
     63                                                 << command->GetID();
     64     CHECK(commands_.insert(command).second)
     65         << "Command instance already exists";
     66   }
     67 
     68   void OnCommandRemoved(Command* command) {
     69     CHECK_EQ(1u, ids_.erase(command->GetID())) << "Command ID not found: "
     70                                                << command->GetID();
     71     CHECK_EQ(1u, commands_.erase(command)) << "Command instance not found";
     72   }
     73 
     74   // Get the comma-separated list of command IDs currently accumulated in the
     75   // command queue_.
     76   std::string GetIDs() const {
     77     return Join(",", std::vector<std::string>(ids_.begin(), ids_.end()));
     78   }
     79 
     80  private:
     81   std::set<std::string> ids_;
     82   std::set<Command*> commands_;
     83   base::WeakPtrFactory<FakeDispatcher> weak_ptr_factory_{this};
     84 };
     85 
     86 TEST_F(CommandQueueTest, Empty) {
     87   EXPECT_TRUE(queue_.IsEmpty());
     88   EXPECT_EQ(0u, queue_.GetCount());
     89 }
     90 
     91 TEST_F(CommandQueueTest, Add) {
     92   queue_.Add(CreateDummyCommandInstance("base.reboot", "id1"));
     93   queue_.Add(CreateDummyCommandInstance("base.reboot", "id2"));
     94   queue_.Add(CreateDummyCommandInstance("base.reboot", "id3"));
     95   EXPECT_EQ(3u, queue_.GetCount());
     96   EXPECT_FALSE(queue_.IsEmpty());
     97 }
     98 
     99 TEST_F(CommandQueueTest, Remove) {
    100   const std::string id1 = "id1";
    101   const std::string id2 = "id2";
    102   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
    103   queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
    104   EXPECT_FALSE(queue_.IsEmpty());
    105   EXPECT_FALSE(Remove("dummy"));
    106   EXPECT_EQ(2u, queue_.GetCount());
    107   EXPECT_TRUE(Remove(id1));
    108   EXPECT_EQ(1u, queue_.GetCount());
    109   EXPECT_FALSE(Remove(id1));
    110   EXPECT_EQ(1u, queue_.GetCount());
    111   EXPECT_TRUE(Remove(id2));
    112   EXPECT_EQ(0u, queue_.GetCount());
    113   EXPECT_FALSE(Remove(id2));
    114   EXPECT_EQ(0u, queue_.GetCount());
    115   EXPECT_TRUE(queue_.IsEmpty());
    116 }
    117 
    118 TEST_F(CommandQueueTest, RemoveLater) {
    119   const std::string id1 = "id1";
    120   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
    121   EXPECT_EQ(1u, queue_.GetCount());
    122 
    123   queue_.RemoveLater(id1);
    124   EXPECT_EQ(1u, queue_.GetCount());
    125 
    126   Cleanup(base::TimeDelta::FromMinutes(1));
    127   EXPECT_EQ(1u, queue_.GetCount());
    128 
    129   Cleanup(base::TimeDelta::FromMinutes(15));
    130   EXPECT_EQ(0u, queue_.GetCount());
    131 }
    132 
    133 TEST_F(CommandQueueTest, RemoveLaterOnCleanupTask) {
    134   const std::string id1 = "id1";
    135   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
    136   EXPECT_EQ(1u, queue_.GetCount());
    137 
    138   queue_.RemoveLater(id1);
    139   EXPECT_EQ(1u, queue_.GetCount());
    140   ASSERT_EQ(1u, task_runner_.GetTaskQueueSize());
    141 
    142   task_runner_.RunOnce();
    143 
    144   EXPECT_EQ(0u, queue_.GetCount());
    145   EXPECT_EQ(0u, task_runner_.GetTaskQueueSize());
    146 }
    147 
    148 TEST_F(CommandQueueTest, CleanupMultipleCommands) {
    149   const std::string id1 = "id1";
    150   const std::string id2 = "id2";
    151 
    152   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
    153   queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
    154   auto remove_task = [this](const std::string& id) { queue_.RemoveLater(id); };
    155   remove_task(id1);
    156   task_runner_.PostDelayedTask(FROM_HERE, base::Bind(remove_task, id2),
    157                                base::TimeDelta::FromSeconds(10));
    158   EXPECT_EQ(2u, queue_.GetCount());
    159   ASSERT_EQ(2u, task_runner_.GetTaskQueueSize());
    160   task_runner_.RunOnce();  // Executes "remove_task(id2) @ T+10s".
    161   ASSERT_EQ(2u, queue_.GetCount());
    162   ASSERT_EQ(1u, task_runner_.GetTaskQueueSize());
    163   EXPECT_EQ(id1, GetFirstCommandToBeRemoved());
    164   task_runner_.RunOnce();  // Should remove task "id1" from queue.
    165   ASSERT_EQ(1u, queue_.GetCount());
    166   ASSERT_EQ(1u, task_runner_.GetTaskQueueSize());
    167   EXPECT_EQ(id2, GetFirstCommandToBeRemoved());
    168   task_runner_.RunOnce();  // Should remove task "id2" from queue.
    169   EXPECT_EQ(0u, queue_.GetCount());
    170   EXPECT_EQ(0u, task_runner_.GetTaskQueueSize());
    171 }
    172 
    173 TEST_F(CommandQueueTest, Dispatch) {
    174   FakeDispatcher dispatch(&queue_);
    175   const std::string id1 = "id1";
    176   const std::string id2 = "id2";
    177   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
    178   queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
    179   std::set<std::string> ids{id1, id2};  // Make sure they are sorted properly.
    180   std::string expected_set =
    181       Join(",", std::vector<std::string>(ids.begin(), ids.end()));
    182   EXPECT_EQ(expected_set, dispatch.GetIDs());
    183   Remove(id1);
    184   EXPECT_EQ(id2, dispatch.GetIDs());
    185   Remove(id2);
    186   EXPECT_EQ("", dispatch.GetIDs());
    187 }
    188 
    189 TEST_F(CommandQueueTest, Find) {
    190   const std::string id1 = "id1";
    191   const std::string id2 = "id2";
    192   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
    193   queue_.Add(CreateDummyCommandInstance("base.shutdown", id2));
    194   EXPECT_EQ(nullptr, queue_.Find("dummy"));
    195   auto cmd1 = queue_.Find(id1);
    196   EXPECT_NE(nullptr, cmd1);
    197   EXPECT_EQ("base.reboot", cmd1->GetName());
    198   EXPECT_EQ(id1, cmd1->GetID());
    199   auto cmd2 = queue_.Find(id2);
    200   EXPECT_NE(nullptr, cmd2);
    201   EXPECT_EQ("base.shutdown", cmd2->GetName());
    202   EXPECT_EQ(id2, cmd2->GetID());
    203 }
    204 
    205 }  // namespace weave
    206