blob: cbd753cca41d8c243725453cba0de9a01ce66044 [file] [log] [blame]
// Copyright 2021 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 "src/developer/forensics/feedback/namespace_init.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/syslog/logger.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/developer/forensics/feedback_data/system_log_recorder/encoding/production_encoding.h"
#include "src/developer/forensics/feedback_data/system_log_recorder/encoding/version.h"
#include "src/developer/forensics/feedback_data/system_log_recorder/reader.h"
#include "src/developer/forensics/testing/log_message.h"
#include "src/developer/forensics/testing/stubs/cobalt_logger_factory.h"
#include "src/developer/forensics/testing/unit_test_fixture.h"
#include "src/developer/forensics/utils/log_format.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/files/scoped_temp_dir.h"
#include "src/lib/timekeeper/test_clock.h"
namespace forensics::feedback {
namespace {
using testing::BuildLogMessage;
using ::testing::UnorderedElementsAreArray;
MATCHER_P2(MatchesCobaltEvent, expected_type, expected_metric_id, "") {
return arg.type == expected_type && arg.metric_id == expected_metric_id;
}
std::string MakeFilepath(const std::string& dir, const size_t file_num) {
return files::JoinPath(dir, std::to_string(file_num));
}
std::string MakeFilepath(const std::string& dir, const std::string& file) {
return files::JoinPath(dir, file);
}
const std::vector<std::string> CurrentLogFilePaths(const std::string& dir) {
return {MakeFilepath(dir, 0), MakeFilepath(dir, 1), MakeFilepath(dir, 2), MakeFilepath(dir, 3),
MakeFilepath(dir, 4), MakeFilepath(dir, 5), MakeFilepath(dir, 6), MakeFilepath(dir, 7)};
}
class NamespaceInitTest : public UnitTestFixture {
protected:
std::string RootdDir() { return temp_dir_.path(); }
void WriteFile(const std::string& path, const std::string& content) {
FX_CHECK(files::WriteFile(path, content)) << "Failed to write to " << path;
}
void DeleteFile(const std::string& path) {
FX_CHECK(files::DeletePath(path, /*recursive=*/true)) << "Failed to delete to " << path;
}
std::string ReadFile(const std::string& path) {
std::string content;
FX_CHECK(files::ReadFileToString(path, &content)) << "Failed to read from " << path;
return content;
}
files::ScopedTempDir temp_dir_;
};
TEST_F(NamespaceInitTest, TestAndSetNotAFdr) {
std::string path = MakeFilepath(RootdDir(), "not_a_fdr.txt");
EXPECT_FALSE(TestAndSetNotAFdr(path));
EXPECT_TRUE(TestAndSetNotAFdr(path));
EXPECT_TRUE(TestAndSetNotAFdr(path));
path = "/bad_path/not_a_fdr.txt";
EXPECT_FALSE(TestAndSetNotAFdr(path));
EXPECT_FALSE(TestAndSetNotAFdr(path));
EXPECT_FALSE(TestAndSetNotAFdr(path));
}
TEST_F(NamespaceInitTest, MovePreviousRebootReason) {
const std::string to = MakeFilepath(RootdDir(), "to.txt");
const std::string from = MakeFilepath(RootdDir(), "from.txt");
const std::string legacy_from = MakeFilepath(RootdDir(), "legacy_from.txt");
// Neither |from| not |legacy_from| exists.
MovePreviousRebootReason(from, legacy_from, to);
EXPECT_FALSE(files::IsFile(to));
// |to| can't be written to.
WriteFile(from, "reboot_reason");
MovePreviousRebootReason(from, legacy_from, "/bad_path/to.txt");
EXPECT_FALSE(files::IsFile("/bad_path/to.txt"));
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "reboot_reason");
// |from| works!
WriteFile(from, "reboot_reason");
MovePreviousRebootReason(from, legacy_from, to);
EXPECT_FALSE(files::IsFile(from));
EXPECT_TRUE(files::IsFile(to));
EXPECT_EQ(ReadFile(to), "reboot_reason");
// |legacy_from| works!
DeleteFile(from);
WriteFile(legacy_from, "reboot_reason");
MovePreviousRebootReason(from, legacy_from, to);
EXPECT_FALSE(files::IsFile(legacy_from));
EXPECT_TRUE(files::IsFile(to));
EXPECT_EQ(ReadFile(to), "reboot_reason");
}
TEST_F(NamespaceInitTest, MoveAndRecordBootId) {
const std::string to = MakeFilepath(RootdDir(), "to.txt");
const std::string from = MakeFilepath(RootdDir(), "from.txt");
// |from| doesn't exist.
MoveAndRecordBootId("boot-id-1", to, from);
EXPECT_FALSE(files::IsFile(to));
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "boot-id-1");
// |to| can't be written to.
MoveAndRecordBootId("boot-id-2", "/bad-path/to.txt", from);
EXPECT_FALSE(files::IsFile("/bad-path/to.txt"));
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "boot-id-2");
// Everything works!
WriteFile(from, "boot-id-3");
MoveAndRecordBootId("boot-id-4", to, from);
EXPECT_TRUE(files::IsFile(to));
EXPECT_EQ(ReadFile(to), "boot-id-3");
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "boot-id-4");
}
TEST_F(NamespaceInitTest, MoveAndRecordBuildVersion) {
const std::string to = MakeFilepath(RootdDir(), "to.txt");
const std::string from = MakeFilepath(RootdDir(), "from.txt");
// |from| doesn't exist.
MoveAndRecordBuildVersion("build-version-1", to, from);
EXPECT_FALSE(files::IsFile(to));
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "build-version-1");
// |to| can't be written to.
MoveAndRecordBuildVersion("build-version-2", "/bad-path/to.txt", from);
EXPECT_FALSE(files::IsFile("/bad-path/to.txt"));
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "build-version-2");
// Everything works!
WriteFile(from, "build-version-3");
MoveAndRecordBuildVersion("build-version-4", to, from);
EXPECT_TRUE(files::IsFile(to));
EXPECT_EQ(ReadFile(to), "build-version-3");
EXPECT_TRUE(files::IsFile(from));
EXPECT_EQ(ReadFile(from), "build-version-4");
}
TEST_F(NamespaceInitTest, CreatePreviousLogsFile) {
timekeeper::TestClock clock;
cobalt::Logger cobalt(dispatcher(), services(), &clock);
SetUpCobaltServer(std::make_unique<stubs::CobaltLoggerFactory>());
std::string logs_dir;
ASSERT_TRUE(temp_dir_.NewTempDir(&logs_dir));
std::string previous_log_contents = "";
for (const auto& filepath : CurrentLogFilePaths(logs_dir)) {
auto encoder = feedback_data::system_log_recorder::ProductionEncoder();
const std::string str = Format(BuildLogMessage(FX_LOG_INFO, "Log for file: " + filepath));
previous_log_contents = previous_log_contents + str;
WriteFile(filepath, encoder.Encode(str));
}
std::string log_file = MakeFilepath(RootdDir(), "log.system.previous_boot.txt");
CreatePreviousLogsFile(&cobalt, logs_dir, log_file);
RunLoopUntilIdle();
EXPECT_FALSE(files::IsDirectory(logs_dir));
EXPECT_EQ(previous_log_contents, ReadFile(log_file));
// Verify the event type and metric_id.
EXPECT_THAT(
ReceivedCobaltEvents(),
UnorderedElementsAreArray({
MatchesCobaltEvent(cobalt::EventType::kInteger,
cobalt_registry::kPreviousBootLogCompressionRatioMigratedMetricId),
}));
}
} // namespace
} // namespace forensics::feedback