blob: e905573bde907d1e1fce98a2d152d103e24a2c1b [file] [log] [blame]
// Copyright 2021 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 <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/callback_helpers.h>
#include <base/strings/strcat.h>
#include <base/test/task_environment.h>
#include <brillo/dbus/mock_dbus_method_response.h>
#include <dbus/bus.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "arc/data-snapshotd/upstart_client.h"
#include "arc/data-snapshotd/worker_bridge.h"
#include "arc/data-snapshotd/worker_client.h"
namespace arc {
namespace data_snapshotd {
namespace {
constexpr char kFakeAccountID[] = "fake@account.id";
constexpr char kFakePrivateKey[] = "fake-private-key";
constexpr char kFakePublicKey[] = "fake-public-key";
} // namespace
// Fake implementation of UpstartClient.
class FakeUpstartClient : public UpstartClient {
public:
explicit FakeUpstartClient(const scoped_refptr<dbus::Bus>& bus)
: UpstartClient(bus) {}
void StartWorkerDaemon(const std::vector<std::string>& environment,
base::OnceCallback<void(bool)> callback) override {
if (should_start_)
is_started_ = true;
EXPECT_EQ(environment, environment_);
std::move(callback).Run(should_start_);
}
void StopWorkerDaemon() override { is_started_ = false; }
bool is_started() const { return is_started_; }
void set_should_start(bool should_start) { should_start_ = should_start; }
void set_environment(const std::vector<std::string>& environment) {
environment_ = environment;
}
private:
bool is_started_ = false;
bool should_start_ = false;
std::vector<std::string> environment_;
};
// Fake implementation of WorkerClient.
class FakeWorkerClient : public WorkerClient {
public:
explicit FakeWorkerClient(const scoped_refptr<dbus::Bus>& bus)
: WorkerClient(bus) {}
void WaitForServiceToBeAvailable(
dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback)
override {
std::move(callback).Run(service_is_available_);
}
void TakeSnapshot(const std::string& account_id,
const std::string& private_key,
const std::string& public_key,
base::OnceCallback<void(bool)> callback) override {
EXPECT_EQ(account_id, kFakeAccountID);
EXPECT_EQ(private_key, kFakePrivateKey);
EXPECT_EQ(public_key, kFakePublicKey);
std::move(callback).Run(result_);
}
void LoadSnapshot(const std::string& account_id,
base::OnceCallback<void(bool, bool)> callback) override {
EXPECT_EQ(account_id, kFakeAccountID);
std::move(callback).Run(result_, last_);
}
void set_available(bool available) { service_is_available_ = available; }
void set_result(bool result) { result_ = result; }
void set_last(bool last) { last_ = last; }
private:
bool service_is_available_ = false;
bool result_ = false;
bool last_ = false;
};
class WorkerBridgeTest : public testing::Test {
public:
WorkerBridgeTest() : bus_(new dbus::Bus{dbus::Bus::Options{}}) {}
void SetUp() override {
auto upstart_client = std::make_unique<FakeUpstartClient>(bus_);
upstart_client_ = upstart_client.get();
upstart_client_->set_should_start(true);
upstart_client_->set_environment(
{base::StrCat({"CHROMEOS_USER=", kFakeAccountID})});
auto worker_client = std::make_unique<FakeWorkerClient>(bus_);
worker_client_ = worker_client.get();
worker_bridge_ = WorkerBridge::CreateForTesting(std::move(upstart_client),
std::move(worker_client));
}
void TearDown() override { worker_bridge_.reset(); }
void FastForwardAttempt() {
task_environment_.FastForwardBy(
WorkerBridge::connection_attempt_interval_for_testing());
task_environment_.RunUntilIdle();
}
void RunAll(bool expected_result) {
worker_client()->set_result(expected_result);
RunTakeSnapshot(expected_result);
RunLoadSnapshot(expected_result, expected_result);
}
void RunTakeSnapshot(bool expected_result) {
std::unique_ptr<brillo::dbus_utils::MockDBusMethodResponse<bool>> response(
new brillo::dbus_utils::MockDBusMethodResponse<bool>(nullptr));
response->set_return_callback(base::Bind(
[](bool expected_result, const bool& success) {
EXPECT_EQ(expected_result, success);
},
expected_result));
worker_bridge_->TakeSnapshot(kFakeAccountID, kFakePrivateKey,
kFakePublicKey, std::move(response));
}
void RunLoadSnapshot(bool expected_result, bool expected_last) {
worker_client()->set_last(expected_last);
std::unique_ptr<brillo::dbus_utils::MockDBusMethodResponse<bool, bool>>
response(new brillo::dbus_utils::MockDBusMethodResponse<bool, bool>(
nullptr));
response->set_return_callback(base::Bind(
[](bool expected_result, bool expected_last, const bool& success,
const bool& last) {
EXPECT_EQ(expected_result, success);
EXPECT_EQ(expected_last, last);
},
expected_result, expected_last));
worker_bridge_->LoadSnapshot(kFakeAccountID, std::move(response));
}
WorkerBridge* worker_bridge() { return worker_bridge_.get(); }
FakeWorkerClient* worker_client() { return worker_client_; }
FakeUpstartClient* upstart_client() { return upstart_client_; }
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private:
scoped_refptr<dbus::Bus> bus_;
std::unique_ptr<WorkerBridge> worker_bridge_;
FakeUpstartClient* upstart_client_;
FakeWorkerClient* worker_client_;
};
// Test basic scenario: D-Bus service is available immediately.
TEST_F(WorkerBridgeTest, ServiceAvailable) {
worker_client()->set_available(true);
worker_bridge()->Init(kFakeAccountID, base::DoNothing());
task_environment_.RunUntilIdle();
EXPECT_TRUE(worker_bridge()->is_available_for_testing());
RunAll(true /* expected_result */);
}
// Test basic scenario: D-Bus service is not available.
TEST_F(WorkerBridgeTest, ServiceUnavailable) {
worker_client()->set_available(false);
worker_bridge()->Init(kFakeAccountID, base::DoNothing());
task_environment_.RunUntilIdle();
EXPECT_FALSE(worker_bridge()->is_available_for_testing());
RunAll(false /* expected_result */);
}
// Test that service is available from the max attempt.
TEST_F(WorkerBridgeTest, ServiceAvailableMaxAttempt) {
worker_client()->set_available(false);
worker_bridge()->Init(kFakeAccountID, base::DoNothing());
// Not available from the first attempt.
task_environment_.RunUntilIdle();
EXPECT_FALSE(worker_bridge()->is_available_for_testing());
size_t attempts_number =
WorkerBridge::max_connection_attempt_count_for_testing() - 1;
for (size_t i = 1; i < attempts_number; i++) {
// Not available from the next max - 2 attempts.
FastForwardAttempt();
EXPECT_FALSE(worker_bridge()->is_available_for_testing());
}
// Available from the max attempt.
worker_client()->set_available(true /* is_available */);
FastForwardAttempt();
EXPECT_TRUE(worker_bridge()->is_available_for_testing());
RunAll(true /* expected_result */);
}
// Test that service is available from the max + 1 attempt and is not picked up.
TEST_F(WorkerBridgeTest, ServiceUnavailableMaxAttempts) {
worker_client()->set_available(false);
worker_bridge()->Init(kFakeAccountID, base::DoNothing());
// Not available from the first attempt.
task_environment_.RunUntilIdle();
EXPECT_FALSE(worker_bridge()->is_available_for_testing());
size_t attempts_number =
WorkerBridge::max_connection_attempt_count_for_testing();
for (size_t i = 1; i < attempts_number; i++) {
// Not available from the next max - 1 attempts.
FastForwardAttempt();
EXPECT_FALSE(worker_bridge()->is_available_for_testing());
}
// Available from the max + 1 attempt, but bridge is not listening.
worker_client()->set_available(true);
FastForwardAttempt();
EXPECT_FALSE(worker_bridge()->is_available_for_testing());
RunAll(false /* expected_result */);
}
} // namespace data_snapshotd
} // namespace arc