blob: 098055be33336af0a296958cae58b4876c1a98e5 [file] [edit]
#include <chrono> // NOLINT(build/c++11)
#include <cstdint>
#include <filesystem> // NOLINT(build/c++17)
#include <fstream>
#include <memory>
#include <string>
#include <string_view>
#include "boottime.grpc.pb.h"
#include "boottime.pb.h"
#include <gtest/gtest.h>
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "grpcblob_server.h"
#include "grpcpp/client_context.h"
#include "grpcpp/create_channel.h"
#include "grpcpp/security/credentials.h"
#include "grpcpp/server_context.h"
#include "grpcpp/support/status.h"
#ifndef EXPECT_OK
#define EXPECT_OK(x) EXPECT_TRUE(x.ok())
#endif
#ifndef TEMP_CLIENT_CONTEXT
#define TEMP_CLIENT_CONTEXT(x, y) \
{ \
ClientContext x; \
y; \
}
#endif
namespace blobs {
namespace {
namespace fs = std::filesystem;
using ::grpc::Channel;
using ::grpc::ClientContext;
using ::phosphor::boottime::GetBootTimeRequest;
using ::phosphor::boottime::GetBootTimeResponse;
using ::phosphor::boottime::RebootCompleteRequest;
using ::phosphor::boottime::RebootCompleteResponse;
using ::phosphor::boottime::SetCheckpointRequest;
using ::phosphor::boottime::SetCheckpointResponse;
using ::phosphor::boottime::SetDurationRequest;
using ::phosphor::boottime::SetDurationResponse;
using ::phosphor::boottime::grpc_gen::BootTimeService;
class BootTimeTest : public ::testing::Test {
protected:
BootTimeTest() {
if (fs::exists(test_dir_)) {
fs::remove_all(test_dir_);
}
fs::create_directories(test_dir_);
LockFilePermissions(fs::perm_options::add);
if (server_ == nullptr) {
SetUpServer();
}
}
~BootTimeTest() {
if (fs::exists(test_dir_)) {
fs::remove_all(test_dir_);
}
}
static int64_t GetMonoTimeMs() {
double monotime_tmp;
std::ifstream fin("/proc/uptime");
fin >> monotime_tmp;
return static_cast<int64_t>(monotime_tmp * 1000);
}
static void LockFilePermissions(fs::perm_options op) {
if (!fs::exists(test_lock_file_)) {
fs::create_directories(fs::path(test_lock_file_).remove_filename());
std::ofstream{std::string(test_lock_file_)};
}
fs::permissions(test_lock_file_,
fs::perms::owner_write | fs::perms::group_write |
fs::perms::others_write,
op);
}
static void SetUpServer() {
server_ = std::make_unique<GrpcBlobServer>(std::string(test_dir_));
server_->start();
std::shared_ptr<Channel> channel = grpc::CreateChannel(
"localhost:10166", grpc::InsecureChannelCredentials());
boottime_stub_ = BootTimeService::NewStub(channel);
}
static void CloseServer() {
absl::SleepFor(absl::Seconds(1));
server_->stop();
server_.reset();
boottime_stub_.reset();
}
static void SetUpTestSuite() { SetUpServer(); }
static void TearDownTestSuite() { CloseServer(); }
static constexpr std::string_view test_dir_ = "/tmp/boottime_grpc_test/";
static constexpr std::string_view test_lock_file_ =
"/tmp/boottime_grpc_test/var/lock/boottime_api.lock";
static std::unique_ptr<GrpcBlobServer> server_;
static std::unique_ptr<BootTimeService::Stub> boottime_stub_;
};
std::unique_ptr<GrpcBlobServer> BootTimeTest::server_;
std::unique_ptr<BootTimeService::Stub> BootTimeTest::boottime_stub_;
TEST_F(BootTimeTest, BootTimeSetInvalidNameFormat) {
SetCheckpointRequest req_c;
SetCheckpointResponse resp_c;
SetDurationRequest req_d;
SetDurationResponse resp_d;
req_c.set_name("_**#-aa");
req_c.set_timestamp_ms(20000);
req_d.set_name("??_@@0002");
req_d.set_duration_ms(1500);
TEMP_CLIENT_CONTEXT(
c,
EXPECT_EQ(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c).error_code(),
grpc::StatusCode::INVALID_ARGUMENT));
TEMP_CLIENT_CONTEXT(
c, EXPECT_EQ(boottime_stub_->SetDuration(&c, req_d, &resp_d).error_code(),
grpc::StatusCode::INVALID_ARGUMENT));
}
TEST_F(BootTimeTest, BootTimeSetInvalidNameLength) {
SetCheckpointRequest req_c;
SetCheckpointResponse resp_c;
SetDurationRequest req_d;
SetDurationResponse resp_d;
req_c.mutable_name()->clear();
req_d.mutable_name()->clear();
TEMP_CLIENT_CONTEXT(
c, EXPECT_EQ(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c),
grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid name length")));
TEMP_CLIENT_CONTEXT(c,
EXPECT_EQ(boottime_stub_->SetDuration(&c, req_d, &resp_d),
grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid name length")));
req_c.mutable_name()->reserve(1024 + 32);
req_d.mutable_name()->reserve(1024 + 32);
for (int i = 0; i <= (1024 / 16); ++i) {
req_c.mutable_name()->append("aaaaaaaaaaaaaaaa");
req_d.mutable_name()->append("aaaaaaaaaaaaaaaa");
}
TEMP_CLIENT_CONTEXT(
c, EXPECT_EQ(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c),
grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid name length")));
TEMP_CLIENT_CONTEXT(c,
EXPECT_EQ(boottime_stub_->SetDuration(&c, req_d, &resp_d),
grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid name length")));
}
TEST_F(BootTimeTest, BootTimeSetAndGetData) {
SetCheckpointRequest req_c;
SetCheckpointResponse resp_c;
SetDurationRequest req_d;
SetDurationResponse resp_d;
RebootCompleteRequest req_r;
RebootCompleteResponse resp_r;
GetBootTimeRequest req_g;
GetBootTimeResponse resp_g;
int64_t monotime_ms_begin = 0;
int64_t monotime_ms_end = 0;
int64_t walltime_ms_begin = 0;
monotime_ms_begin = GetMonoTimeMs();
walltime_ms_begin = std::chrono::system_clock::now().time_since_epoch() /
std::chrono::milliseconds(1);
req_c.set_name("tc1");
req_c.set_timestamp_ms(1000);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c)));
req_c.set_name("tc2");
req_c.set_timestamp_ms(2000);
req_c.set_duration_ms(300);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c)));
// Use internal walltime
req_c.set_name("tc3");
req_c.set_timestamp_ms(0);
req_c.set_duration_ms(0);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c)));
req_d.set_name("td1");
req_d.set_duration_ms(1600);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetDuration(&c, req_d, &resp_d)));
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->GetBootTime(&c, req_g, &resp_g)));
monotime_ms_end = GetMonoTimeMs();
// We can't check everything via text proto as reboot_flow.timestamp_mono_ms
// is not determined by the caller.
EXPECT_EQ(resp_g.stat().is_rebooting(), true);
EXPECT_EQ(resp_g.reboot_flow(0).stage(), "tc1");
EXPECT_EQ(resp_g.reboot_flow(0).timestamp_wall_ms(), 1000);
EXPECT_GE(resp_g.reboot_flow(0).timestamp_mono_ms(), monotime_ms_begin);
EXPECT_EQ(resp_g.reboot_flow(1).stage(), "tc2:BEGIN");
EXPECT_EQ(resp_g.reboot_flow(1).timestamp_wall_ms(), 2000 - 300);
EXPECT_EQ(resp_g.reboot_flow(2).stage(), "tc2");
EXPECT_EQ(resp_g.reboot_flow(2).timestamp_wall_ms(), 2000);
EXPECT_EQ(resp_g.reboot_flow(2).timestamp_mono_ms(),
resp_g.reboot_flow(1).timestamp_mono_ms() + 300);
EXPECT_GE(resp_g.reboot_flow(2).timestamp_mono_ms(),
resp_g.reboot_flow(0).timestamp_mono_ms());
EXPECT_EQ(resp_g.reboot_flow(3).stage(), "tc3");
EXPECT_GE(resp_g.reboot_flow(3).timestamp_mono_ms(),
resp_g.reboot_flow(2).timestamp_mono_ms());
EXPECT_LE(resp_g.reboot_flow(3).timestamp_mono_ms(), monotime_ms_end);
EXPECT_GE(resp_g.reboot_flow(3).timestamp_wall_ms(), walltime_ms_begin);
EXPECT_LE(resp_g.reboot_flow(3).timestamp_wall_ms(),
std::chrono::system_clock::now().time_since_epoch() /
std::chrono::milliseconds(1));
EXPECT_EQ(resp_g.durations(0).name(), "td1");
EXPECT_EQ(resp_g.durations(0).duration_ms(), 1600);
// Verify that single-host port 10166 (instance 2) writes to host0, matching
// instance 0 (port 10167).
std::shared_ptr<Channel> host0_channel = grpc::CreateChannel(
"localhost:10167", grpc::InsecureChannelCredentials());
std::unique_ptr<BootTimeService::Stub> host0_stub =
BootTimeService::NewStub(host0_channel);
GetBootTimeResponse resp_host0;
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(host0_stub->GetBootTime(&c, req_g, &resp_host0)));
EXPECT_EQ(resp_host0.reboot_flow_size(), resp_g.reboot_flow_size());
EXPECT_EQ(resp_host0.durations_size(), resp_g.durations_size());
}
TEST_F(BootTimeTest, BootTimeRebootBehavior) {
SetCheckpointRequest req_c;
SetCheckpointResponse resp_c;
SetDurationRequest req_d;
SetDurationResponse resp_d;
RebootCompleteRequest req_r;
RebootCompleteResponse resp_r;
GetBootTimeRequest req_g;
GetBootTimeResponse resp_g;
int64_t monotime_ms_begin = 0;
int64_t walltime_ms_begin = 0;
monotime_ms_begin = GetMonoTimeMs();
walltime_ms_begin = std::chrono::system_clock::now().time_since_epoch() /
std::chrono::milliseconds(1);
req_c.set_name("tc3");
req_c.set_timestamp_ms(20000);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c)));
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->RebootComplete(&c, req_r, &resp_r)));
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->GetBootTime(&c, req_g, &resp_g)));
EXPECT_EQ(resp_g.stat().is_rebooting(), false);
EXPECT_EQ(resp_g.reboot_flow(0).stage(), "tc3");
EXPECT_EQ(resp_g.reboot_flow(0).timestamp_wall_ms(), 20000);
EXPECT_EQ(resp_g.reboot_flow(1).stage(), "RebootEnd");
EXPECT_GE(resp_g.reboot_flow(1).timestamp_mono_ms(), monotime_ms_begin);
EXPECT_LE(resp_g.reboot_flow(1).timestamp_mono_ms(), GetMonoTimeMs());
EXPECT_GE(resp_g.reboot_flow(1).timestamp_wall_ms(), walltime_ms_begin);
EXPECT_LE(resp_g.reboot_flow(1).timestamp_wall_ms(),
std::chrono::system_clock::now().time_since_epoch() /
std::chrono::milliseconds(1));
req_d.set_name("td2");
req_d.set_duration_ms(1000);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetDuration(&c, req_d, &resp_d)));
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->GetBootTime(&c, req_g, &resp_g)));
EXPECT_EQ(resp_g.stat().is_rebooting(), true);
EXPECT_EQ(resp_g.reboot_flow_size(), 0);
EXPECT_EQ(resp_g.durations(0).name(), "td2");
EXPECT_EQ(resp_g.durations(0).duration_ms(), 1000);
}
TEST_F(BootTimeTest, BootTimeApiRegisterFailure) {
SetCheckpointRequest req_c;
SetCheckpointResponse resp_c;
SetDurationRequest req_d;
SetDurationResponse resp_d;
RebootCompleteRequest req_r;
RebootCompleteResponse resp_r;
CloseServer();
LockFilePermissions(fs::perm_options::remove);
SetUpServer();
req_c.set_name("tc1");
req_c.set_timestamp_ms(1000);
req_d.set_name("td1");
req_d.set_duration_ms(1000);
TEMP_CLIENT_CONTEXT(
c,
EXPECT_EQ(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c).error_code(),
grpc::StatusCode::INTERNAL));
TEMP_CLIENT_CONTEXT(
c, EXPECT_EQ(boottime_stub_->SetDuration(&c, req_d, &resp_d).error_code(),
grpc::StatusCode::INTERNAL));
TEMP_CLIENT_CONTEXT(
c,
EXPECT_EQ(boottime_stub_->RebootComplete(&c, req_r, &resp_r).error_code(),
grpc::StatusCode::INTERNAL));
LockFilePermissions(fs::perm_options::add);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetCheckpoint(&c, req_c, &resp_c)));
CloseServer();
LockFilePermissions(fs::perm_options::remove);
SetUpServer();
LockFilePermissions(fs::perm_options::add);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->SetDuration(&c, req_d, &resp_d)));
CloseServer();
LockFilePermissions(fs::perm_options::remove);
SetUpServer();
LockFilePermissions(fs::perm_options::add);
TEMP_CLIENT_CONTEXT(
c, EXPECT_OK(boottime_stub_->RebootComplete(&c, req_r, &resp_r)));
}
} // namespace
} // namespace blobs