blob: 2143b3c883653795099b5c13acb9879e67623208 [file] [log] [blame]
// Copyright 2018 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 <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_refptr.h>
#include <base/run_loop.h>
#include <base/values.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_exported_object.h>
#include <dbus/values_util.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "vm_tools/notificationd/notification_daemon.h"
using ::testing::_;
using ::testing::Invoke;
namespace {
class MockNotificationDaemon : public vm_tools::notificationd::DBusInterface {
public:
MockNotificationDaemon() : dbus_service() {}
bool GetCapabilities(std::vector<std::string>* out_capabilities) override {
*out_capabilities = capabilities_;
return true;
}
bool Notify(const NotifyArgument& input, uint32_t* out_id) override {
received_notify_arg_ = input;
*out_id = notify_out_id_;
return true;
}
bool GetServerInformation(ServerInformation* output) override {
*output = server_info_;
return true;
}
bool CloseNotification(uint32_t id) override {
received_close_notification_id_ = id;
return true;
}
void SetCapabilities(const std::vector<std::string>& capabilities) {
capabilities_ = capabilities;
}
void SetServerInformation(const ServerInformation& server_info) {
server_info_ = server_info;
}
void SetNotifyOutId(uint32_t out_id) { notify_out_id_ = out_id; }
const NotifyArgument& GetReceivedNotifyArg() const {
return received_notify_arg_;
}
uint32_t received_close_notification_id() const {
return received_close_notification_id_;
}
private:
std::vector<std::string> capabilities_;
ServerInformation server_info_;
NotifyArgument received_notify_arg_;
uint32_t notify_out_id_;
uint32_t received_close_notification_id_;
std::unique_ptr<vm_tools::notificationd::DBusService> dbus_service;
FRIEND_TEST(DBusServiceTest, GetCapabilities);
FRIEND_TEST(DBusServiceTest, Notify);
FRIEND_TEST(DBusServiceTest, GetServerInformation);
FRIEND_TEST(DBusServiceTest, CloseNotification);
};
} // namespace
namespace vm_tools {
namespace notificationd {
class DBusServiceTest : public ::testing::Test {
public:
DBusServiceTest() = default;
DBusServiceTest(const DBusServiceTest&) = delete;
DBusServiceTest& operator=(const DBusServiceTest&) = delete;
// Create dummy method call.
std::unique_ptr<dbus::MethodCall> CreateMockMethodCall(
const std::string& method_name) {
const uint32_t kSerial = 123;
auto method_call = std::make_unique<dbus::MethodCall>(
"org.freedesktop.Notifications", method_name);
method_call->SetSerial(kSerial);
return method_call;
}
void AppendStringArray(dbus::MessageWriter* writer,
const std::vector<std::string>& array) {
dbus::MessageWriter array_writer(nullptr);
writer->OpenArray("s", &array_writer);
for (const auto& str : array) {
array_writer.AppendString(str);
}
writer->CloseContainer(&array_writer);
}
void AppendEmptyVariantDict(dbus::MessageWriter* writer) {
dbus::MessageWriter array_writer(nullptr);
writer->OpenArray("{sv}", &array_writer);
writer->CloseContainer(&array_writer);
}
};
// Test if dbus adaptor can properly call GetCapability method and recieve the
// data from notification daemon.
TEST_F(DBusServiceTest, GetCapabilities) {
MockNotificationDaemon daemon;
DBusService dbus_service(&daemon);
const std::vector<std::string> expected_data = {"body", "actions",
"action-icons"};
daemon.SetCapabilities(expected_data);
auto method_call = CreateMockMethodCall("GetCapabilities");
auto response = dbus_service.CallGetCapabilities(method_call.get());
ASSERT_TRUE(response.get() != nullptr);
// Parse resonse
dbus::MessageReader reader(response.get());
std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
ASSERT_TRUE(value.get() != nullptr);
ASSERT_FALSE(reader.HasMoreData());
ASSERT_TRUE(value->is_list());
std::vector<std::string> received_data;
for (const auto& element : value->GetList()) {
ASSERT_TRUE(element.is_string());
received_data.push_back(element.GetString());
}
EXPECT_EQ(received_data, expected_data);
}
// Test if dbus adaptor can properly call GetServerInformation method and
// recieve the data from notification daemon.
TEST_F(DBusServiceTest, GetServerInformation) {
MockNotificationDaemon daemon;
DBusService dbus_service(&daemon);
const NotificationDaemon::ServerInformation expected_data = {
.name = "NameTest",
.vendor = "VendorTest",
.version = "VersionTest",
.spec_version = "SpecVersionTest"};
daemon.SetServerInformation(expected_data);
auto method_call = CreateMockMethodCall("GetServerInformation");
auto response = dbus_service.CallGetServerInformation(method_call.get());
ASSERT_TRUE(response.get() != nullptr);
// Parse response
dbus::MessageReader reader(response.get());
NotificationDaemon::ServerInformation received_data;
ASSERT_TRUE(reader.PopString(&received_data.name));
ASSERT_TRUE(reader.PopString(&received_data.vendor));
ASSERT_TRUE(reader.PopString(&received_data.version));
ASSERT_TRUE(reader.PopString(&received_data.spec_version));
ASSERT_FALSE(reader.HasMoreData());
EXPECT_EQ(received_data.name, expected_data.name);
EXPECT_EQ(received_data.vendor, expected_data.vendor);
EXPECT_EQ(received_data.version, expected_data.version);
EXPECT_EQ(received_data.spec_version, expected_data.spec_version);
}
// Test if dbus adaptor can properly call Notify method and recieve the data
// from notification daemon.
TEST_F(DBusServiceTest, Notify) {
MockNotificationDaemon daemon;
DBusService dbus_service(&daemon);
const NotificationDaemon::NotifyArgument expected_data = {
.app_name = "AppNameTest",
.replaces_id = 1,
.app_icon = "AppIconTest",
.summary = "SummaryTest",
.body = "BodyTest",
.actions = {"ActionTest1", "ActionTest2", "Actiontest3"},
.hints = {{"KeyTest1", "ValueTest1"}},
.expire_timeout = 2};
const uint32_t expected_out_id = 333;
daemon.SetNotifyOutId(expected_out_id);
auto method_call = CreateMockMethodCall("Notify");
// Prepare args for the method call
dbus::MessageWriter writer(method_call.get());
writer.AppendString(expected_data.app_name);
writer.AppendUint32(expected_data.replaces_id);
writer.AppendString(expected_data.app_icon);
writer.AppendString(expected_data.summary);
writer.AppendString(expected_data.body);
AppendStringArray(&writer, expected_data.actions);
AppendEmptyVariantDict(&writer);
writer.AppendInt32(expected_data.expire_timeout);
auto response = dbus_service.CallNotify(method_call.get());
ASSERT_TRUE(response.get() != nullptr);
// Test args
const auto received_args = daemon.GetReceivedNotifyArg();
EXPECT_EQ(received_args.app_name, expected_data.app_name);
EXPECT_EQ(received_args.replaces_id, expected_data.replaces_id);
EXPECT_EQ(received_args.app_icon, expected_data.app_icon);
EXPECT_EQ(received_args.summary, expected_data.summary);
EXPECT_EQ(received_args.body, expected_data.body);
EXPECT_EQ(received_args.actions, expected_data.actions);
// parsing hints is not implemented yet.
EXPECT_NE(received_args.hints, expected_data.hints);
EXPECT_EQ(received_args.expire_timeout, expected_data.expire_timeout);
// Parse response
dbus::MessageReader reader(response.get());
uint32_t received_out_id = 0;
ASSERT_TRUE(reader.PopUint32(&received_out_id));
ASSERT_FALSE(reader.HasMoreData());
// Test response
EXPECT_EQ(received_out_id, expected_out_id);
}
// Test if dbus adaptor can properly call CloseNotification method.
TEST_F(DBusServiceTest, CloseNotification) {
MockNotificationDaemon daemon;
DBusService dbus_service(&daemon);
const uint32_t expected_data = 777;
auto method_call = CreateMockMethodCall("CloseNotification");
// Prepare args for the method call
dbus::MessageWriter writer(method_call.get());
writer.AppendUint32(expected_data);
auto response = dbus_service.CallCloseNotification(method_call.get());
ASSERT_TRUE(response.get() != nullptr);
// Test received data
EXPECT_EQ(daemon.received_close_notification_id(), expected_data);
// Parse response (expects no data in response)
dbus::MessageReader reader(response.get());
ASSERT_FALSE(reader.HasMoreData());
}
// Test if dbus adaptor can properly send NotificationClosed signal.
TEST_F(DBusServiceTest, NotificationClosedSignal) {
DBusService dbus_service(nullptr);
// Prepare mock exported object
const dbus::ObjectPath kObjectPath("/org/example/TestService");
auto mock_exported_object =
base::MakeRefCounted<dbus::MockExportedObject>(nullptr, kObjectPath);
dbus_service.exported_object_ = mock_exported_object.get();
// Expected data
const uint32_t expected_id = 456;
const auto expected_reason = DBusService::ClosedReason::BY_USER;
// Test if SendSignal is called once with expected args
EXPECT_CALL(*mock_exported_object.get(), SendSignal(_))
.WillOnce(Invoke([&](dbus::Signal* signal) {
dbus::MessageReader reader(signal);
uint32_t id = -1;
uint32_t reason = -1;
ASSERT_TRUE(reader.PopUint32(&id));
ASSERT_TRUE(reader.PopUint32(&reason));
ASSERT_FALSE(reader.HasMoreData());
EXPECT_EQ(id, expected_id);
EXPECT_EQ(reason, static_cast<uint32_t>(expected_reason));
}));
// Send signal
dbus_service.SendNotificationClosedSignal(expected_id, expected_reason);
}
// Test if dbus adaptor can properly send ActionInvoked signal.
TEST_F(DBusServiceTest, ActionInvokedSignal) {
DBusService dbus_service(nullptr);
// Prepare mock exported object
const dbus::ObjectPath kObjectPath("/org/example/TestService");
auto mock_exported_object =
base::MakeRefCounted<dbus::MockExportedObject>(nullptr, kObjectPath);
dbus_service.exported_object_ = mock_exported_object.get();
// Expected data
const uint32_t expected_id = 456;
const std::string expected_action_key = "Test action key";
// Test if SendSignal is called once with expected args
EXPECT_CALL(*mock_exported_object.get(), SendSignal(_))
.WillOnce(Invoke([&](dbus::Signal* signal) {
dbus::MessageReader reader(signal);
uint32_t id = -1;
std::string action_key;
ASSERT_TRUE(reader.PopUint32(&id));
ASSERT_TRUE(reader.PopString(&action_key));
ASSERT_FALSE(reader.HasMoreData());
EXPECT_EQ(id, expected_id);
EXPECT_EQ(action_key, expected_action_key);
}));
// Send signal
dbus_service.SendActionInvokedSignal(expected_id, expected_action_key);
}
} // namespace notificationd
} // namespace vm_tools