// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chromeos/ash/components/dbus/shill/shill_client_unittest_base.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/values_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
namespace ash {
namespace {
// Pops a string-to-string dictionary from the reader.
std::unique_ptr<base::Value> PopStringToStringDictionary(
dbus::MessageReader* reader) {
dbus::MessageReader array_reader(nullptr);
if (!reader->PopArray(&array_reader))
return nullptr;
base::Value::Dict result;
while (array_reader.HasMoreData()) {
dbus::MessageReader entry_reader(nullptr);
std::string key;
std::string value;
if (!array_reader.PopDictEntry(&entry_reader) ||
!entry_reader.PopString(&key) || !entry_reader.PopString(&value)) {
return nullptr;
}
result.Set(key, value);
}
return std::make_unique<base::Value>(std::move(result));
}
} // namespace
ValueMatcher::ValueMatcher(const base::Value& value)
: expected_value_(base::Value::ToUniquePtrValue(value.Clone())) {}
ValueMatcher::~ValueMatcher() = default;
bool ValueMatcher::MatchAndExplain(const base::Value& value,
MatchResultListener* listener) const {
return *expected_value_ == value;
}
void ValueMatcher::DescribeTo(::std::ostream* os) const {
std::string expected_value_str;
base::JSONWriter::WriteWithOptions(*expected_value_,
base::JSONWriter::OPTIONS_PRETTY_PRINT,
&expected_value_str);
*os << "value equals " << expected_value_str;
}
void ValueMatcher::DescribeNegationTo(::std::ostream* os) const {
std::string expected_value_str;
base::JSONWriter::WriteWithOptions(*expected_value_,
base::JSONWriter::OPTIONS_PRETTY_PRINT,
&expected_value_str);
*os << "value does not equal " << expected_value_str;
}
ShillClientUnittestBase::MockPropertyChangeObserver::
MockPropertyChangeObserver() = default;
ShillClientUnittestBase::MockPropertyChangeObserver::
~MockPropertyChangeObserver() = default;
ShillClientUnittestBase::ShillClientUnittestBase(
const std::string& interface_name,
const dbus::ObjectPath& object_path)
: interface_name_(interface_name),
object_path_(object_path),
response_(nullptr) {}
ShillClientUnittestBase::~ShillClientUnittestBase() = default;
void ShillClientUnittestBase::SetUp() {
// Create a mock bus.
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
mock_bus_ = new dbus::MockBus(options);
// Create a mock proxy.
mock_proxy_ = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, object_path_);
// Set expectations so that mock_proxy's Call methods will return responses.
EXPECT_CALL(*mock_proxy_.get(), DoCallMethod(_, _, _))
.WillRepeatedly(Invoke(this, &ShillClientUnittestBase::OnCallMethod));
EXPECT_CALL(*mock_proxy_.get(), DoCallMethodWithErrorResponse(_, _, _))
.WillRepeatedly(Invoke(
this, &ShillClientUnittestBase::OnCallMethodWithErrorResponse));
EXPECT_CALL(*mock_proxy_.get(), DoCallMethodWithErrorCallback(_, _, _, _))
.WillRepeatedly(Invoke(
this, &ShillClientUnittestBase::OnCallMethodWithErrorCallback));
// Set an expectation so mock_proxy's ConnectToSignal() will use
// OnConnectToPropertyChanged() to run the callback.
EXPECT_CALL(
*mock_proxy_.get(),
DoConnectToSignal(interface_name_, shill::kMonitorPropertyChanged, _, _))
.WillRepeatedly(
Invoke(this, &ShillClientUnittestBase::OnConnectToPropertyChanged));
EXPECT_CALL(*mock_proxy_.get(),
DoConnectToSignal(interface_name_,
shill::kOnPlatformMessageFunction, _, _))
.WillRepeatedly(
Invoke(this, &ShillClientUnittestBase::OnConnectToPlatformMessage));
EXPECT_CALL(*mock_proxy_.get(),
DoConnectToSignal(interface_name_,
shill::kOnPacketReceivedFunction, _, _))
.WillRepeatedly(
Invoke(this, &ShillClientUnittestBase::OnConnectToPacketReceived));
EXPECT_CALL(*mock_bus_.get(),
GetObjectProxy(shill::kFlimflamServiceName, object_path_))
.WillRepeatedly(Return(mock_proxy_.get()));
// Set an expectation so mock_bus's GetDBusTaskRunner will return the current
// task runner.
EXPECT_CALL(*mock_bus_.get(), GetDBusTaskRunner())
.WillRepeatedly(
Return(task_environment_.GetMainThreadTaskRunner().get()));
// ShutdownAndBlock() will be called in TearDown().
EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
}
void ShillClientUnittestBase::TearDown() {
mock_bus_->ShutdownAndBlock();
}
void ShillClientUnittestBase::PrepareForMethodCall(
const std::string& method_name,
const ArgumentCheckCallback& argument_checker,
dbus::Response* response) {
expected_method_name_ = method_name;
argument_checker_ = argument_checker;
response_ = response;
}
void ShillClientUnittestBase::SendPlatformMessageSignal(dbus::Signal* signal) {
ASSERT_FALSE(platform_message_handler_.is_null());
platform_message_handler_.Run(signal);
}
void ShillClientUnittestBase::SendPacketReceivedSignal(dbus::Signal* signal) {
ASSERT_FALSE(packet_receieved__handler_.is_null());
packet_receieved__handler_.Run(signal);
}
void ShillClientUnittestBase::SendPropertyChangedSignal(dbus::Signal* signal) {
ASSERT_FALSE(property_changed_handler_.is_null());
property_changed_handler_.Run(signal);
}
// static
void ShillClientUnittestBase::ExpectPropertyChanged(
const std::string& expected_name,
const base::Value* expected_value,
const std::string& name,
const base::Value& value) {
EXPECT_EQ(expected_name, name);
EXPECT_EQ(*expected_value, value);
}
// static
void ShillClientUnittestBase::ExpectNoArgument(dbus::MessageReader* reader) {
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectUint32Argument(
uint32_t expected_value,
dbus::MessageReader* reader) {
uint32_t value;
ASSERT_TRUE(reader->PopUint32(&value));
EXPECT_EQ(expected_value, value);
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectIntArgument(
int expected_value,
dbus::MessageReader* reader) {
int value;
ASSERT_TRUE(reader->PopInt32(&value));
EXPECT_EQ(expected_value, value);
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectArrayOfBytesArgument(
const std::string& expected_bytes,
dbus::MessageReader* reader) {
const uint8_t* bytes = nullptr;
size_t size = 0;
ASSERT_TRUE(reader->PopArrayOfBytes(&bytes, &size));
EXPECT_EQ(expected_bytes.size(), size);
for (size_t i = 0; i < size; ++i) {
EXPECT_EQ(expected_bytes[i], bytes[i]);
}
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectStringArgument(
const std::string& expected_string,
dbus::MessageReader* reader) {
std::string str;
ASSERT_TRUE(reader->PopString(&str));
EXPECT_EQ(expected_string, str);
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectBoolArgument(bool expected_value,
dbus::MessageReader* reader) {
bool value;
ASSERT_TRUE(reader->PopBool(&value));
EXPECT_EQ(expected_value, value);
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectArrayOfStringsArgument(
const std::vector<std::string>& expected_strings,
dbus::MessageReader* reader) {
std::vector<std::string> strs;
ASSERT_TRUE(reader->PopArrayOfStrings(&strs));
EXPECT_EQ(expected_strings, strs);
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectStringAndValueArguments(
const std::string& expected_string,
const base::Value* expected_value,
dbus::MessageReader* reader) {
std::string str;
ASSERT_TRUE(reader->PopString(&str));
EXPECT_EQ(expected_string, str);
base::Value value(dbus::PopDataAsValue(reader));
ASSERT_TRUE(!value.is_none());
EXPECT_EQ(value, *expected_value);
EXPECT_FALSE(reader->HasMoreData());
}
// static
void ShillClientUnittestBase::ExpectValueDictionaryArgument(
const base::Value::Dict* expected_dictionary,
bool string_valued,
dbus::MessageReader* reader) {
dbus::MessageReader array_reader(nullptr);
ASSERT_TRUE(reader->PopArray(&array_reader));
while (array_reader.HasMoreData()) {
dbus::MessageReader entry_reader(nullptr);
ASSERT_TRUE(array_reader.PopDictEntry(&entry_reader));
std::string key;
ASSERT_TRUE(entry_reader.PopString(&key));
if (string_valued) {
std::string value;
ASSERT_TRUE(entry_reader.PopString(&value));
const std::string* expected_value = expected_dictionary->FindString(key);
ASSERT_TRUE(expected_value);
EXPECT_EQ(*expected_value, value);
continue;
}
dbus::MessageReader variant_reader(nullptr);
ASSERT_TRUE(entry_reader.PopVariant(&variant_reader));
std::unique_ptr<base::Value> value;
// Variants in the dictionary can be basic types or string-to-string
// dictionary.
switch (variant_reader.GetDataType()) {
case dbus::Message::ARRAY:
value = PopStringToStringDictionary(&variant_reader);
break;
case dbus::Message::BOOL:
case dbus::Message::INT32:
case dbus::Message::STRING:
value = base::Value::ToUniquePtrValue(
dbus::PopDataAsValue(&variant_reader));
ASSERT_FALSE(value->is_none());
break;
default:
NOTREACHED_IN_MIGRATION();
}
ASSERT_TRUE(value.get());
const base::Value* expected_value = expected_dictionary->Find(key);
ASSERT_TRUE(expected_value);
EXPECT_EQ(*value, *expected_value);
}
}
// static
base::Value::Dict ShillClientUnittestBase::CreateExampleServiceProperties() {
base::Value::Dict properties;
properties.Set(shill::kGuidProperty,
base::Value("00000000-0000-0000-0000-000000000000"));
properties.Set(shill::kModeProperty, base::Value(shill::kModeManaged));
properties.Set(shill::kTypeProperty, base::Value(shill::kTypeWifi));
const std::string ssid = "testssid";
properties.Set(shill::kWifiHexSsid, base::Value(base::HexEncode(ssid)));
properties.Set(shill::kSecurityClassProperty,
base::Value(shill::kSecurityClassPsk));
return properties;
}
void ShillClientUnittestBase::OnConnectToPlatformMessage(
const std::string& interface_name,
const std::string& signal_name,
const dbus::ObjectProxy::SignalCallback& signal_callback,
dbus::ObjectProxy::OnConnectedCallback* on_connected_callback) {
platform_message_handler_ = signal_callback;
const bool success = true;
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(std::move(*on_connected_callback),
interface_name, signal_name, success));
}
void ShillClientUnittestBase::OnConnectToPacketReceived(
const std::string& interface_name,
const std::string& signal_name,
const dbus::ObjectProxy::SignalCallback& signal_callback,
dbus::ObjectProxy::OnConnectedCallback* on_connected_callback) {
packet_receieved__handler_ = signal_callback;
const bool success = true;
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(std::move(*on_connected_callback),
interface_name, signal_name, success));
}
void ShillClientUnittestBase::OnConnectToPropertyChanged(
const std::string& interface_name,
const std::string& signal_name,
const dbus::ObjectProxy::SignalCallback& signal_callback,
dbus::ObjectProxy::OnConnectedCallback* on_connected_callback) {
property_changed_handler_ = signal_callback;
const bool success = true;
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(std::move(*on_connected_callback),
interface_name, signal_name, success));
}
void ShillClientUnittestBase::OnCallMethod(
dbus::MethodCall* method_call,
int timeout_ms,
dbus::ObjectProxy::ResponseCallback* response_callback) {
EXPECT_EQ(interface_name_, method_call->GetInterface());
EXPECT_EQ(expected_method_name_, method_call->GetMember());
dbus::MessageReader reader(method_call);
argument_checker_.Run(&reader);
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(std::move(*response_callback), response_));
}
void ShillClientUnittestBase::OnCallMethodWithErrorResponse(
dbus::MethodCall* method_call,
int timeout_ms,
dbus::ObjectProxy::ResponseOrErrorCallback* response_callback) {
EXPECT_EQ(interface_name_, method_call->GetInterface());
EXPECT_EQ(expected_method_name_, method_call->GetMember());
dbus::MessageReader reader(method_call);
argument_checker_.Run(&reader);
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(std::move(*response_callback), response_, nullptr));
}
void ShillClientUnittestBase::OnCallMethodWithErrorCallback(
dbus::MethodCall* method_call,
int timeout_ms,
dbus::ObjectProxy::ResponseCallback* response_callback,
dbus::ObjectProxy::ErrorCallback* error_callback) {
OnCallMethod(method_call, timeout_ms, response_callback);
}
} // namespace ash