blob: 9f8d92bee2b98baf39cbd66f01f3dd6df2739d95 [file] [log] [blame]
// Copyright 2015 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 "psyche/psyched/cell.h"
#include <string>
#include <vector>
#include <base/logging.h>
#include <base/macros.h>
#include <gtest/gtest.h>
#include <protobinder/binder_manager_stub.h>
#include <protobinder/binder_proxy.h>
#include "psyche/common/binder_test_base.h"
#include "psyche/proto_bindings/germ.pb.h"
#include "psyche/proto_bindings/germ.pb.rpc.h"
#include "psyche/proto_bindings/soma_container_spec.pb.h"
#include "psyche/psyched/germ_connection.h"
#include "psyche/psyched/service_stub.h"
#include "psyche/psyched/stub_factory.h"
using protobinder::BinderProxy;
using soma::ContainerSpec;
namespace psyche {
namespace {
// Stub implementation of the Germ interface.
class GermInterfaceStub : public germ::IGerm {
public:
GermInterfaceStub()
: launch_return_value_(0),
terminate_return_value_(0),
launch_success_(true),
terminate_success_(true) {}
~GermInterfaceStub() override = default;
void set_launch_return_value(int value) { launch_return_value_ = value; }
void set_launch_pid(int pid) {
launch_pid_ = pid;
}
void set_terminate_return_value(int value) {
terminate_return_value_ = value;
}
const std::vector<std::string>& launched_cell_names() const {
return launched_cell_names_;
}
const std::vector<int>& terminated_cell_pids() const {
return terminated_cell_pids_;
}
// IGerm:
int Launch(germ::LaunchRequest* in, germ::LaunchResponse* out) override {
out->set_success(launch_success_);
out->set_pid(launch_pid_);
launched_cell_names_.push_back(in->spec().name());
return launch_return_value_;
}
int Terminate(germ::TerminateRequest* in,
germ::TerminateResponse* out) override {
out->set_success(terminate_success_);
terminated_cell_pids_.push_back(in->pid());
return terminate_return_value_;
}
private:
// binder result returned by Launch().
int launch_return_value_;
// binder result returned by Terminate().
int terminate_return_value_;
// germ success field returned by LaunchResponse.
bool launch_success_;
// germ pid field returned by LaunchResponse.
int launch_pid_;
// germ success field returned by TerminateResponse.
bool terminate_success_;
// Cell names passed to Launch().
std::vector<std::string> launched_cell_names_;
// Cell init PIDs passed to Terminate().
std::vector<int> terminated_cell_pids_;
DISALLOW_COPY_AND_ASSIGN(GermInterfaceStub);
};
class CellTest : public BinderTestBase {
public:
CellTest()
: germ_proxy_(nullptr),
germ_(nullptr) {
InitGerm();
}
~CellTest() override = default;
protected:
// Initializes |germ_proxy_| and |germ_| and registers them with
// |binder_manager_|.
void InitGerm() {
germ_proxy_ = CreateBinderProxy().release();
germ_ = new GermInterfaceStub;
binder_manager_->SetTestInterface(germ_proxy_,
std::unique_ptr<IInterface>(germ_));
germ_connection_.SetProxy(std::unique_ptr<BinderProxy>(germ_proxy_));
}
StubFactory factory_;
GermConnection germ_connection_;
BinderProxy* germ_proxy_; // Owned by this class.
GermInterfaceStub* germ_; // Owned by |binder_manager_|.
private:
DISALLOW_COPY_AND_ASSIGN(CellTest);
};
TEST_F(CellTest, InitializeFromSpec) {
ContainerSpec spec;
const std::string kCellName("/tmp/org.example.cell");
spec.set_name(kCellName);
const std::vector<std::string> kServiceNames = {
"org.example.search.query",
"org.example.search.autocomplete",
};
for (const auto& name : kServiceNames)
spec.add_service_names(name);
Cell cell(spec, &factory_, &germ_connection_);
EXPECT_EQ(kCellName, cell.GetName());
// Check that all of the listed services were created.
const CellInterface::ServiceMap& services = cell.GetServices();
for (const auto& name : kServiceNames) {
SCOPED_TRACE(name);
const auto it = services.find(name);
ASSERT_TRUE(it != services.end());
EXPECT_EQ(factory_.GetService(name), it->second.get());
}
// Modify the spec that was passed in to verify that Cell made its own copy of
// it.
spec.set_name("/some/other/name");
EXPECT_EQ(kCellName, cell.GetName());
}
// Tests various failures when communicating with germd.
TEST_F(CellTest, GermCommunication) {
ContainerSpec spec;
const std::string kCellName("/tmp/org.example.cell");
const int kCellPid(123);
spec.set_name(kCellName);
germ_->set_launch_pid(kCellPid);
Cell cell(spec, &factory_, &germ_connection_);
// Failure should be reported for RPC errors.
germ_->set_launch_return_value(-1);
EXPECT_FALSE(cell.Launch());
// Now report that the germd binder proxy died.
germ_->set_launch_return_value(0);
binder_manager_->ReportBinderDeath(germ_proxy_);
EXPECT_FALSE(cell.Launch());
// Register a new proxy for germd and check that the next service request is
// successful.
InitGerm();
EXPECT_TRUE(cell.Launch());
ASSERT_EQ(1, germ_->launched_cell_names().size());
EXPECT_EQ(kCellName, germ_->launched_cell_names()[0]);
// Make similar calls to Terminate() as Launch(), showing failures for RPC,
// binder death, and restart and success.
germ_->set_terminate_return_value(-1);
EXPECT_FALSE(cell.Terminate());
germ_->set_terminate_return_value(0);
binder_manager_->ReportBinderDeath(germ_proxy_);
EXPECT_FALSE(cell.Terminate());
InitGerm();
EXPECT_TRUE(cell.Terminate());
ASSERT_EQ(1, germ_->terminated_cell_pids().size());
EXPECT_EQ(kCellPid, germ_->terminated_cell_pids()[0]);
}
} // namespace
} // namespace psyche