blob: 3cb907d9fed58ea87bb08fce9b10195e766201db [file] [log] [blame]
Alex Vakulenko4866ac92014-08-20 12:53:33 -07001// Copyright 2014 The Chromium OS 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
Alex Deymof6cbe322014-11-10 19:55:35 -08005#include "buffet/commands/dbus_command_proxy.h"
6
Alex Vakulenko4866ac92014-08-20 12:53:33 -07007#include <functional>
8#include <memory>
9
10#include <dbus/mock_bus.h>
11#include <dbus/mock_exported_object.h>
12#include <dbus/property.h>
13#include <chromeos/dbus/dbus_object.h>
Alex Vakulenko5c7bf012014-10-30 16:28:38 -070014#include <chromeos/dbus/dbus_object_test_helpers.h>
Alex Vakulenko4866ac92014-08-20 12:53:33 -070015#include <gtest/gtest.h>
16
17#include "buffet/commands/command_dictionary.h"
18#include "buffet/commands/command_instance.h"
Alex Vakulenko4866ac92014-08-20 12:53:33 -070019#include "buffet/commands/unittest_utils.h"
Alex Vakulenko89d9d5e2014-09-12 10:27:23 -070020#include "buffet/libbuffet/dbus_constants.h"
Alex Vakulenko4866ac92014-08-20 12:53:33 -070021
22using ::testing::AnyNumber;
23using ::testing::Return;
24using ::testing::Invoke;
25using ::testing::_;
26
Alex Vakulenko4866ac92014-08-20 12:53:33 -070027using buffet::unittests::CreateDictionaryValue;
Alex Vakulenkof6b38712014-09-03 16:23:38 -070028using chromeos::dbus_utils::AsyncEventSequencer;
Alex Vakulenkof6b38712014-09-03 16:23:38 -070029using chromeos::dbus_utils::ExportedObjectManager;
Alex Vakulenko576c9792014-09-22 16:49:45 -070030using chromeos::VariantDictionary;
Alex Vakulenko4866ac92014-08-20 12:53:33 -070031
32namespace buffet {
33
34namespace {
35
36const char kTestCommandCategoty[] = "test_command_category";
37const char kTestCommandId[] = "cmd_1";
38
Alex Vakulenko4866ac92014-08-20 12:53:33 -070039} // namespace
40
41class DBusCommandProxyTest : public ::testing::Test {
42 public:
43 void SetUp() override {
44 // Set up a mock DBus bus object.
45 dbus::Bus::Options options;
46 options.bus_type = dbus::Bus::SYSTEM;
47 bus_ = new dbus::MockBus(options);
48 // By default, don't worry about threading assertions.
49 EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
50 EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
51
52 // Command instance.
53 auto json = CreateDictionaryValue(R"({
54 'robot': {
55 'jump': {
56 'parameters': {
57 'height': {
58 'type': 'integer',
59 'minimum': 0,
60 'maximum': 100
61 },
62 '_jumpType': {
63 'type': 'string',
64 'enum': ['_withAirFlip', '_withSpin', '_withKick']
65 }
66 }
67 }
68 }
69 })");
70 CHECK(dict_.LoadCommands(*json, kTestCommandCategoty, nullptr, nullptr))
71 << "Failed to parse test command dictionary";
72
73 json = CreateDictionaryValue(R"({
74 'name': 'robot.jump',
75 'parameters': {
76 'height': 53,
77 '_jumpType': '_withKick'
78 }
79 })");
80 command_instance_ = CommandInstance::FromJson(json.get(), dict_, nullptr);
81 command_instance_->SetID(kTestCommandId);
82
83 // Set up a mock ExportedObject to be used with the DBus command proxy.
84 std::string cmd_path = dbus_constants::kCommandServicePathPrefix;
85 cmd_path += kTestCommandId;
86 const dbus::ObjectPath kCmdObjPath(cmd_path);
87 // Use a mock exported object for the exported object manager.
88 mock_exported_object_command_ =
89 new dbus::MockExportedObject(bus_.get(), kCmdObjPath);
90 EXPECT_CALL(*bus_, GetExportedObject(kCmdObjPath)).Times(AnyNumber())
91 .WillRepeatedly(Return(mock_exported_object_command_.get()));
92 EXPECT_CALL(*mock_exported_object_command_,
93 ExportMethod(_, _, _, _)).Times(AnyNumber());
94
Anton Muhinb66a9302014-11-10 22:15:22 +040095 std::unique_ptr<CommandProxyInterface> command_proxy(
96 new DBusCommandProxy(nullptr, bus_, command_instance_.get(), cmd_path));
97 command_instance_->AddProxy(std::move(command_proxy));
98 GetCommandProxy()->RegisterAsync(
Alex Vakulenkof6b38712014-09-03 16:23:38 -070099 AsyncEventSequencer::GetDefaultCompletionAction());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700100 }
101
102 void TearDown() override {
103 EXPECT_CALL(*mock_exported_object_command_, Unregister()).Times(1);
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700104 command_instance_.reset();
105 dict_.Clear();
106 bus_ = nullptr;
107 }
108
Anton Muhinb66a9302014-11-10 22:15:22 +0400109 DBusCommandProxy* GetCommandProxy() const {
Mike Frysinger42e3a722014-11-15 06:48:08 -0500110 CHECK_EQ(command_instance_->proxies_.size(), 1U);
Anton Muhinb66a9302014-11-10 22:15:22 +0400111 return static_cast<DBusCommandProxy*>(command_instance_->proxies_[0].get());
112 }
113
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700114 chromeos::dbus_utils::DBusObject* GetProxyDBusObject() {
Anton Muhinb66a9302014-11-10 22:15:22 +0400115 return &GetCommandProxy()->dbus_object_;
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700116 }
117
118 std::string GetStatus() const {
Anton Muhinb66a9302014-11-10 22:15:22 +0400119 return GetCommandProxy()->status_.value();
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700120 }
121
122 int32_t GetProgress() const {
Anton Muhinb66a9302014-11-10 22:15:22 +0400123 return GetCommandProxy()->progress_.value();
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700124 }
125
Alex Vakulenko576c9792014-09-22 16:49:45 -0700126 VariantDictionary GetParameters() const {
Anton Muhinb66a9302014-11-10 22:15:22 +0400127 return GetCommandProxy()->parameters_.value();
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700128 }
129
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700130 std::unique_ptr<dbus::Response> CallMethod(
131 const std::string& method_name,
132 const std::function<void(dbus::MessageWriter*)>& param_callback) {
133 dbus::MethodCall method_call(dbus_constants::kCommandInterface,
134 method_name);
135 method_call.SetSerial(1234);
136 dbus::MessageWriter writer(&method_call);
137 if (param_callback)
138 param_callback(&writer);
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700139 return chromeos::dbus_utils::testing::CallMethod(*GetProxyDBusObject(),
140 &method_call);
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700141 }
142
143 static bool IsResponseError(const std::unique_ptr<dbus::Response>& response) {
144 return (response->GetMessageType() == dbus::Message::MESSAGE_ERROR);
145 }
146
147 static void VerifyResponse(
148 const std::unique_ptr<dbus::Response>& response,
149 const std::function<void(dbus::MessageReader*)>& result_callback) {
150 EXPECT_FALSE(IsResponseError(response));
151 dbus::MessageReader reader(response.get());
152 if (result_callback)
153 result_callback(&reader);
154 EXPECT_FALSE(reader.HasMoreData());
155 }
156
157 template<typename T>
158 T GetPropertyValue(const std::string& property_name) {
159 dbus::MethodCall method_call(dbus::kPropertiesInterface,
160 dbus::kPropertiesGet);
161 method_call.SetSerial(1234);
162 dbus::MessageWriter writer(&method_call);
163 writer.AppendString(dbus_constants::kCommandInterface);
164 writer.AppendString(property_name);
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700165 auto response = chromeos::dbus_utils::testing::CallMethod(
166 *GetProxyDBusObject(), &method_call);
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700167 T value{};
168 VerifyResponse(response, [&value](dbus::MessageReader* reader) {
169 EXPECT_TRUE(chromeos::dbus_utils::PopValueFromReader(reader, &value));
170 });
171 return value;
172 }
173
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700174 std::unique_ptr<CommandInstance> command_instance_;
175 CommandDictionary dict_;
176
177 scoped_refptr<dbus::MockExportedObject> mock_exported_object_command_;
178 scoped_refptr<dbus::MockBus> bus_;
179};
180
181TEST_F(DBusCommandProxyTest, Init) {
Alex Vakulenko576c9792014-09-22 16:49:45 -0700182 VariantDictionary params = {
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700183 {"height", int32_t{53}},
184 {"_jumpType", std::string{"_withKick"}},
185 };
186 EXPECT_EQ(CommandInstance::kStatusQueued, GetStatus());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700187 EXPECT_EQ(0, GetProgress());
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700188 EXPECT_EQ(params, GetParameters());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700189 EXPECT_EQ("robot.jump",
190 GetPropertyValue<std::string>(dbus_constants::kCommandName));
191 EXPECT_EQ(kTestCommandCategoty,
192 GetPropertyValue<std::string>(dbus_constants::kCommandCategory));
193 EXPECT_EQ(kTestCommandId,
194 GetPropertyValue<std::string>(dbus_constants::kCommandId));
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700195 EXPECT_EQ(CommandInstance::kStatusQueued,
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700196 GetPropertyValue<std::string>(dbus_constants::kCommandStatus));
197 EXPECT_EQ(0, GetPropertyValue<int32_t>(dbus_constants::kCommandProgress));
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700198 EXPECT_EQ(params,
Alex Vakulenko576c9792014-09-22 16:49:45 -0700199 GetPropertyValue<VariantDictionary>(
200 dbus_constants::kCommandParameters));
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700201}
202
203TEST_F(DBusCommandProxyTest, SetProgress) {
204 EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(2);
205 auto response = CallMethod(dbus_constants::kCommandSetProgress,
206 [](dbus::MessageWriter* writer) {
207 writer->AppendInt32(10);
208 });
209 VerifyResponse(response, {});
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700210 EXPECT_EQ(CommandInstance::kStatusInProgress, GetStatus());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700211 EXPECT_EQ(10, GetProgress());
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700212 EXPECT_EQ(CommandInstance::kStatusInProgress,
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700213 GetPropertyValue<std::string>(dbus_constants::kCommandStatus));
214 EXPECT_EQ(10, GetPropertyValue<int32_t>(dbus_constants::kCommandProgress));
215}
216
217TEST_F(DBusCommandProxyTest, SetProgress_OutOfRange) {
218 auto response = CallMethod(dbus_constants::kCommandSetProgress,
219 [](dbus::MessageWriter* writer) {
220 writer->AppendInt32(110);
221 });
222 EXPECT_TRUE(IsResponseError(response));
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700223 EXPECT_EQ(CommandInstance::kStatusQueued, GetStatus());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700224 EXPECT_EQ(0, GetProgress());
225}
226
227TEST_F(DBusCommandProxyTest, Abort) {
228 EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
229 auto response = CallMethod(dbus_constants::kCommandAbort, {});
230 VerifyResponse(response, {});
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700231 EXPECT_EQ(CommandInstance::kStatusAborted, GetStatus());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700232}
233
234TEST_F(DBusCommandProxyTest, Cancel) {
235 EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
236 auto response = CallMethod(dbus_constants::kCommandCancel, {});
237 VerifyResponse(response, {});
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700238 EXPECT_EQ(CommandInstance::kStatusCanceled, GetStatus());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700239}
240
241TEST_F(DBusCommandProxyTest, Done) {
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700242 // 3 property updates:
243 // status: queued -> inProgress
244 // progress: 0 -> 100
245 // status: inProgress -> done
246 EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(3);
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700247 auto response = CallMethod(dbus_constants::kCommandDone, {});
248 VerifyResponse(response, {});
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700249 EXPECT_EQ(CommandInstance::kStatusDone, GetStatus());
Alex Vakulenko4866ac92014-08-20 12:53:33 -0700250 EXPECT_EQ(100, GetProgress());
251}
252
253} // namespace buffet