blob: c75728697160ece61e827270ddae1faabd58d920 [file] [log] [blame] [edit]
// Copyright 2020 The Fuchsia 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 "build_info.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/vfs/cpp/pseudo_dir.h>
#include <lib/vfs/cpp/pseudo_file.h>
#include <zircon/status.h>
namespace {
const char kFuchsiaBuildInfoDirectoryPath[] = "/config/build-info";
const char kProductFileName[] = "product";
const char kBoardFileName[] = "board";
const char kVersionFileName[] = "version";
const char kLastCommitDateFileName[] = "latest-commit-date";
const char kSnapshotFileName[] = "snapshot";
} // namespace
class BuildInfoServiceInstance {
public:
explicit BuildInfoServiceInstance(std::unique_ptr<sys::ComponentContext> context) {
context_ = std::move(context);
binding_ = std::make_unique<fidl::Binding<fuchsia::buildinfo::Provider>>(&impl_);
fidl::InterfaceRequestHandler<fuchsia::buildinfo::Provider> handler =
[&](fidl::InterfaceRequest<fuchsia::buildinfo::Provider> request) {
binding_->Bind(std::move(request));
};
context_->outgoing()->AddPublicService(std::move(handler));
}
private:
ProviderImpl impl_;
std::unique_ptr<fidl::Binding<fuchsia::buildinfo::Provider>> binding_;
std::unique_ptr<sys::ComponentContext> context_;
};
class BuildInfoServiceTestFixture : public gtest::TestLoopFixture {
public:
BuildInfoServiceTestFixture() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread){};
void SetUp() override {
TestLoopFixture::SetUp();
build_info_service_instance_.reset(new BuildInfoServiceInstance(provider_.TakeContext()));
loop_.StartThread();
// Create a channel.
zx_handle_t endpoint0;
zx_handle_t endpoint1;
zx_status_t status = zx_channel_create(0, &endpoint0, &endpoint1);
ZX_ASSERT_MSG(status == ZX_OK, "Cannot create channel: %s\n", zx_status_get_string(status));
// Get the process's namespace.
fdio_ns_t *ns;
status = fdio_ns_get_installed(&ns);
ZX_ASSERT_MSG(status == ZX_OK, "Cannot get namespace: %s\n", zx_status_get_string(status));
// Create the /config/build-info path in the namespace.
std::string build_info_directory_path(kFuchsiaBuildInfoDirectoryPath);
status = fdio_ns_bind(ns, build_info_directory_path.c_str(), endpoint0);
ZX_ASSERT_MSG(status == ZX_OK, "Cannot bind %s to namespace: %s\n",
build_info_directory_path.c_str(), zx_status_get_string(status));
// Connect the build-info PseudoDir to the /config/build-info path.
zx::channel channel(endpoint1);
build_info_directory_.Serve(fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE,
std::move(channel), loop_.dispatcher());
}
// Creates a PsuedoDir named |build_info_filename| in the PsuedoDir "/config/build-info" in
// the component's namespace. The file contains |build_info_filename| followed optionally by
// a trailing newline.
void CreateBuildInfoFile(std::string build_info_filename, bool with_trailing_newline = true) {
std::string file_contents(build_info_filename);
// Some build info files contain a trailing newline which the build info service strips.
// Optionally add a trailing newline to test the trailing whitespace stripping.
if (with_trailing_newline) {
file_contents.append("\n");
}
vfs::PseudoFile::ReadHandler versionFileReadFn = [file_contents](std::vector<uint8_t> *output,
size_t max_file_size) {
output->resize(file_contents.length());
std::copy(file_contents.begin(), file_contents.end(), output->begin());
return ZX_OK;
};
vfs::PseudoFile::WriteHandler versionFileWriteFn;
// Create a PseudoFile.
std::unique_ptr<vfs::PseudoFile> pseudo_file = std::make_unique<vfs::PseudoFile>(
file_contents.length(), std::move(versionFileReadFn), std::move(versionFileWriteFn));
// Add the file to the build-info PseudoDir.
build_info_directory_.AddEntry(std::move(build_info_filename), std::move(pseudo_file));
}
void TearDown() override {
TestLoopFixture::TearDown();
build_info_service_instance_.reset();
DestroyBuildInfoFile();
}
protected:
fuchsia::buildinfo::ProviderPtr GetProxy() {
fuchsia::buildinfo::ProviderPtr provider;
provider_.ConnectToPublicService(provider.NewRequest());
return provider;
}
private:
void DestroyBuildInfoFile() {
fdio_ns_t *ns;
zx_status_t status = fdio_ns_get_installed(&ns);
std::string build_info_directory_path(kFuchsiaBuildInfoDirectoryPath);
status = fdio_ns_unbind(ns, build_info_directory_path.c_str());
loop_.Quit();
loop_.JoinThreads();
}
std::unique_ptr<BuildInfoServiceInstance> build_info_service_instance_;
sys::testing::ComponentContextProvider provider_;
vfs::PseudoDir build_info_directory_;
async::Loop loop_;
};
TEST_F(BuildInfoServiceTestFixture, BuildInfo) {
CreateBuildInfoFile(kProductFileName);
CreateBuildInfoFile(kBoardFileName);
CreateBuildInfoFile(kVersionFileName);
CreateBuildInfoFile(kLastCommitDateFileName);
fuchsia::buildinfo::ProviderPtr proxy = GetProxy();
proxy->GetBuildInfo([&](const fuchsia::buildinfo::BuildInfo &response) {
EXPECT_TRUE(response.has_product_config());
EXPECT_EQ(response.product_config(), kProductFileName);
EXPECT_TRUE(response.has_board_config());
EXPECT_EQ(response.board_config(), kBoardFileName);
EXPECT_TRUE(response.has_version());
EXPECT_EQ(response.version(), kVersionFileName);
EXPECT_TRUE(response.has_latest_commit_date());
EXPECT_EQ(response.latest_commit_date(), kLastCommitDateFileName);
});
RunLoopUntilIdle();
}
TEST_F(BuildInfoServiceTestFixture, Snapshot) {
CreateBuildInfoFile(kSnapshotFileName, false);
fuchsia::buildinfo::ProviderPtr proxy = GetProxy();
proxy->GetSnapshotInfo([&](zx::vmo response) {
uint64_t size;
response.get_size(&size);
auto buffer = std::make_unique<char[]>(size);
response.read(buffer.get(), 0, size);
std::string response_string{buffer.get()};
EXPECT_EQ(response_string, kSnapshotFileName);
});
RunLoopUntilIdle();
}