blob: 1e2e0ace630fa0ae81cc0fc7279c9248237cee07 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/commands/command_queue.h"
Alex Deymof6cbe322014-11-10 19:55:35 -08006
Alex Vakulenko515b42b2014-08-07 15:46:31 -07007#include <set>
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -07008#include <string>
Alex Vakulenko515b42b2014-08-07 15:46:31 -07009#include <vector>
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070010
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070011#include <base/bind.h>
12#include <base/memory/weak_ptr.h>
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080013#include <gmock/gmock.h>
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070014#include <gtest/gtest.h>
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080015#include <weave/provider/test/fake_task_runner.h>
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070016
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080017#include "src/bind_lambda.h"
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020018#include "src/string_utils.h"
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070019
Vitaly Bukab6f015a2015-07-09 14:59:23 -070020namespace weave {
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070021
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080022using testing::Return;
23using testing::StrictMock;
24
Alex Vakulenko5ef75792015-03-19 15:50:44 -070025class CommandQueueTest : public testing::Test {
26 public:
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070027 std::unique_ptr<CommandInstance> CreateDummyCommandInstance(
28 const std::string& name,
29 const std::string& id) {
Vitaly Buka34668e72015-12-15 14:46:47 -080030 std::unique_ptr<CommandInstance> cmd{
31 new CommandInstance{name, Command::Origin::kLocal, {}}};
Alex Vakulenko5ef75792015-03-19 15:50:44 -070032 cmd->SetID(id);
33 return cmd;
34 }
35
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070036 bool Remove(const std::string& id) { return queue_.Remove(id); }
37
38 void Cleanup(const base::TimeDelta& interval) {
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080039 return queue_.Cleanup(task_runner_.GetClock()->Now() + interval);
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070040 }
41
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080042 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()};
Alex Vakulenko5ef75792015-03-19 15:50:44 -070048};
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070049
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070050// Keeps track of commands being added to and removed from the queue_.
Alex Vakulenko515b42b2014-08-07 15:46:31 -070051// Aborts if duplicate commands are added or non-existent commands are removed.
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070052class FakeDispatcher {
Alex Vakulenko515b42b2014-08-07 15:46:31 -070053 public:
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070054 explicit FakeDispatcher(CommandQueue* queue) {
Vitaly Buka553a7622015-10-05 13:53:20 -070055 queue->AddCommandAddedCallback(base::Bind(&FakeDispatcher::OnCommandAdded,
56 weak_ptr_factory_.GetWeakPtr()));
57 queue->AddCommandRemovedCallback(base::Bind(
Vitaly Bukaa647c852015-07-06 14:51:01 -070058 &FakeDispatcher::OnCommandRemoved, weak_ptr_factory_.GetWeakPtr()));
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070059 }
60
Vitaly Buka12affd82015-07-23 18:45:35 -070061 void OnCommandAdded(Command* command) {
62 CHECK(ids_.insert(command->GetID()).second) << "Command ID already exists: "
63 << command->GetID();
64 CHECK(commands_.insert(command).second)
Alex Vakulenko515b42b2014-08-07 15:46:31 -070065 << "Command instance already exists";
66 }
67
Vitaly Buka12affd82015-07-23 18:45:35 -070068 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";
Alex Vakulenko515b42b2014-08-07 15:46:31 -070072 }
73
74 // Get the comma-separated list of command IDs currently accumulated in the
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070075 // command queue_.
Alex Vakulenko515b42b2014-08-07 15:46:31 -070076 std::string GetIDs() const {
Vitaly Bukadb770e72015-03-10 19:33:33 -070077 return Join(",", std::vector<std::string>(ids_.begin(), ids_.end()));
Alex Vakulenko515b42b2014-08-07 15:46:31 -070078 }
79
80 private:
81 std::set<std::string> ids_;
Vitaly Buka12affd82015-07-23 18:45:35 -070082 std::set<Command*> commands_;
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070083 base::WeakPtrFactory<FakeDispatcher> weak_ptr_factory_{this};
Alex Vakulenko515b42b2014-08-07 15:46:31 -070084};
85
Alex Vakulenko5ef75792015-03-19 15:50:44 -070086TEST_F(CommandQueueTest, Empty) {
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070087 EXPECT_TRUE(queue_.IsEmpty());
Vitaly Buka52d006a2015-11-21 17:14:51 -080088 EXPECT_EQ(0u, queue_.GetCount());
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070089}
90
Alex Vakulenko5ef75792015-03-19 15:50:44 -070091TEST_F(CommandQueueTest, Add) {
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070092 queue_.Add(CreateDummyCommandInstance("base.reboot", "id1"));
93 queue_.Add(CreateDummyCommandInstance("base.reboot", "id2"));
94 queue_.Add(CreateDummyCommandInstance("base.reboot", "id3"));
Vitaly Buka52d006a2015-11-21 17:14:51 -080095 EXPECT_EQ(3u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070096 EXPECT_FALSE(queue_.IsEmpty());
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070097}
98
Alex Vakulenko5ef75792015-03-19 15:50:44 -070099TEST_F(CommandQueueTest, Remove) {
Anton Muhin5191e812014-10-30 17:49:48 +0400100 const std::string id1 = "id1";
101 const std::string id2 = "id2";
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700102 queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
103 queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
104 EXPECT_FALSE(queue_.IsEmpty());
105 EXPECT_FALSE(Remove("dummy"));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800106 EXPECT_EQ(2u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700107 EXPECT_TRUE(Remove(id1));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800108 EXPECT_EQ(1u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700109 EXPECT_FALSE(Remove(id1));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800110 EXPECT_EQ(1u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700111 EXPECT_TRUE(Remove(id2));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800112 EXPECT_EQ(0u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700113 EXPECT_FALSE(Remove(id2));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800114 EXPECT_EQ(0u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700115 EXPECT_TRUE(queue_.IsEmpty());
116}
117
Alex Vakulenko329ad802016-02-01 12:11:30 -0800118TEST_F(CommandQueueTest, RemoveLater) {
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700119 const std::string id1 = "id1";
120 queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800121 EXPECT_EQ(1u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700122
Alex Vakulenko329ad802016-02-01 12:11:30 -0800123 queue_.RemoveLater(id1);
Vitaly Buka52d006a2015-11-21 17:14:51 -0800124 EXPECT_EQ(1u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700125
126 Cleanup(base::TimeDelta::FromMinutes(1));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800127 EXPECT_EQ(1u, queue_.GetCount());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700128
129 Cleanup(base::TimeDelta::FromMinutes(15));
Vitaly Buka52d006a2015-11-21 17:14:51 -0800130 EXPECT_EQ(0u, queue_.GetCount());
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700131}
132
Alex Vakulenko98d1fee2016-02-01 12:25:21 -0800133TEST_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
148TEST_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
Alex Vakulenko5ef75792015-03-19 15:50:44 -0700173TEST_F(CommandQueueTest, Dispatch) {
Vitaly Bukaae0f3a12015-05-11 16:27:30 -0700174 FakeDispatcher dispatch(&queue_);
Anton Muhin5191e812014-10-30 17:49:48 +0400175 const std::string id1 = "id1";
176 const std::string id2 = "id2";
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700177 queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
178 queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
Alex Vakulenko515b42b2014-08-07 15:46:31 -0700179 std::set<std::string> ids{id1, id2}; // Make sure they are sorted properly.
Vitaly Buka24d6fd52015-08-13 23:22:48 -0700180 std::string expected_set =
181 Join(",", std::vector<std::string>(ids.begin(), ids.end()));
Alex Vakulenko515b42b2014-08-07 15:46:31 -0700182 EXPECT_EQ(expected_set, dispatch.GetIDs());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700183 Remove(id1);
Alex Vakulenko515b42b2014-08-07 15:46:31 -0700184 EXPECT_EQ(id2, dispatch.GetIDs());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700185 Remove(id2);
Alex Vakulenko515b42b2014-08-07 15:46:31 -0700186 EXPECT_EQ("", dispatch.GetIDs());
187}
188
Alex Vakulenko5ef75792015-03-19 15:50:44 -0700189TEST_F(CommandQueueTest, Find) {
Anton Muhin5191e812014-10-30 17:49:48 +0400190 const std::string id1 = "id1";
191 const std::string id2 = "id2";
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700192 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);
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700196 EXPECT_NE(nullptr, cmd1);
197 EXPECT_EQ("base.reboot", cmd1->GetName());
Alex Vakulenkofedc4872014-08-20 12:38:43 -0700198 EXPECT_EQ(id1, cmd1->GetID());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700199 auto cmd2 = queue_.Find(id2);
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700200 EXPECT_NE(nullptr, cmd2);
201 EXPECT_EQ("base.shutdown", cmd2->GetName());
Alex Vakulenkofedc4872014-08-20 12:38:43 -0700202 EXPECT_EQ(id2, cmd2->GetID());
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700203}
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700204
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700205} // namespace weave