blob: 444ba8df558ec618d4c3fecf6fb78b9711326372 [file] [log] [blame] [edit]
#include <chrono>
#include <filesystem> // NOLINT
#include <fstream>
#include <memory>
#include <sstream>
#include <string>
#include <system_error> // NOLINT
#include <thread> // NOLINT
#include <unordered_map>
#include "bios_settings.grpc.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
namespace blobs {
namespace {
using ::google::protobuf::Empty;
using ::grpc::Channel;
using ::grpc::ClientContext;
using ::phosphor::bios::BiosSettings;
using ::phosphor::bios::OpaqueSettings;
using phosphor::bios::grpc_gen::BiosSettingsService;
class BiosSettingsTest : public ::testing::Test {
protected:
static void SetUpTestSuite() {
server_ = std::make_unique<GrpcBlobServer>("/tmp");
server_->start();
std::shared_ptr<Channel> channel = grpc::CreateChannel(
"localhost:10166", grpc::InsecureChannelCredentials());
biossettings_stub_ = BiosSettingsService::NewStub(channel);
}
static void TearDownTestSuite() {
absl::SleepFor(absl::Seconds(5));
server_->stop();
server_.reset();
biossettings_stub_.reset();
}
static void removeFile(const std::filesystem::path& path) {
std::error_code ec; // For error handling during removal
if (std::filesystem::exists(path)) { // Only try to remove if it exists
std::filesystem::remove(path, ec);
ASSERT_FALSE(std::filesystem::exists(path))
<< "Failed to remove file: " << path << " Error: " << ec.message();
}
}
static std::unique_ptr<GrpcBlobServer> server_;
static std::unique_ptr<BiosSettingsService::Stub> biossettings_stub_;
const std::string kConfiguredFilepath =
"/tmp/var/google/bios/configured-settings.txt";
const std::string kReportedFilepath =
"/tmp/var/google/bios/reported-settings.txt";
};
std::unique_ptr<GrpcBlobServer> BiosSettingsTest::server_;
std::unique_ptr<BiosSettingsService::Stub> BiosSettingsTest::biossettings_stub_;
TEST_F(BiosSettingsTest, SetBiosSettings) {
BiosSettings request;
request.set_grace_egm_enabled(true);
request.set_grace_egm_hypervisor_reserved_memory_mb(1024);
Empty response;
ClientContext context;
std::error_code ec;
std::filesystem::path dirPath("/tmp/var/google");
bool created = std::filesystem::create_directories(dirPath, ec);
ASSERT_TRUE(created || !ec) << "Failed to create directory: " << dirPath
<< " Error: " << ec.message();
EXPECT_OK(biossettings_stub_->SetBiosSettings(&context, request, &response));
BiosSettings get_response;
ClientContext get_context;
EXPECT_OK(
biossettings_stub_->GetBiosSettings(&get_context, {}, &get_response));
EXPECT_EQ(get_response.grace_egm_enabled(), request.grace_egm_enabled());
EXPECT_EQ(get_response.grace_egm_hypervisor_reserved_memory_mb(),
request.grace_egm_hypervisor_reserved_memory_mb());
removeFile("/tmp/var/google/bios-settings.txtpb");
}
TEST_F(BiosSettingsTest, GetSettingsReturnsFileNotFound) {
Empty request;
OpaqueSettings response;
ClientContext context;
removeFile(kConfiguredFilepath);
grpc::Status status =
biossettings_stub_->GetSettings(&context, request, &response);
EXPECT_EQ(status.error_code(), grpc::StatusCode::NOT_FOUND);
}
TEST_F(BiosSettingsTest, GetSettingsReturnsValidSettings) {
std::error_code ec;
// Create the directories recursively
std::filesystem::create_directories("/tmp/var/google/bios");
std::ofstream out(kConfiguredFilepath);
out << "test_content";
out.close();
Empty request;
OpaqueSettings response;
ClientContext context;
grpc::Status status =
biossettings_stub_->GetSettings(&context, request, &response);
EXPECT_OK(status);
EXPECT_EQ(response.content(), "test_content");
removeFile(kConfiguredFilepath);
}
TEST_F(BiosSettingsTest, ReportSettings) {
std::filesystem::remove_all("/tmp/var/google/bios");
OpaqueSettings request;
Empty response;
ClientContext context;
request.set_content("test_status");
grpc::Status status =
biossettings_stub_->ReportSettings(&context, request, &response);
EXPECT_OK(status);
std::ifstream ifs(kReportedFilepath);
std::string errorMessage;
ASSERT_TRUE(ifs.is_open()) << "Failed to open " << kReportedFilepath;
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string content = buffer.str();
EXPECT_EQ(content, "test_status");
std::filesystem::file_time_type last_write_time =
std::filesystem::last_write_time(kReportedFilepath);
// Sleep for 1 second so the last write time is measurable.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Report the same settings again
ClientContext context2;
request.set_content("test_status");
status = biossettings_stub_->ReportSettings(&context2, request, &response);
EXPECT_OK(status);
EXPECT_EQ(last_write_time,
std::filesystem::last_write_time(kReportedFilepath))
<< "File should not be updated if the same settings are reported.";
removeFile(kReportedFilepath);
}
TEST_F(BiosSettingsTest, GetSettingsOnMultiHost) {
std::filesystem::create_directories("/tmp/var/google/bios");
// Port 10167: (insecure gRPC instance 0), CN 0 of multi-CN system
// Port 10168: (insecure gRPC instance 1), CN 1 of multi-CN system
// Port 10166: (insecure gRPC instance 2), single CN system, covered in the
// default case
std::unordered_map<int, int> instance_to_stub_map{{0, 10167}, {1, 10168}};
for (const auto& [instance, port] : instance_to_stub_map) {
std::shared_ptr<Channel> channel =
grpc::CreateChannel("localhost:" + std::to_string(port),
grpc::InsecureChannelCredentials());
std::unique_ptr<BiosSettingsService::Stub> cn_stub =
BiosSettingsService::NewStub(channel);
std::ofstream out(kConfiguredFilepath + "_" + std::to_string(instance));
std::string cn_content = "settings from CN " + std::to_string(instance);
out << cn_content;
out.close();
Empty request;
OpaqueSettings response;
ClientContext context;
grpc::Status status = cn_stub->GetSettings(&context, request, &response);
EXPECT_OK(status);
EXPECT_EQ(response.content(), cn_content);
}
std::filesystem::remove_all("/tmp/var/google/bios");
}
} // namespace
} // namespace blobs