| // Copyright 2010 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "bootstat/bootstat.h" |
| |
| #include <fcntl.h> |
| #include <inttypes.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <time.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/memory/ptr_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/time/time.h> |
| #include <brillo/scoped_umask.h> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace bootstat { |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::ByMove; |
| using ::testing::InSequence; |
| using ::testing::Mock; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| |
| namespace { |
| |
| constexpr char kProcPath[] = "proc"; |
| constexpr char kProcUptimePath[] = "proc/uptime"; |
| |
| void RemoveFile(const base::FilePath& file_path) { |
| // Either this is a link, or the path exists (PathExists would resolve |
| // symlink). |
| EXPECT_TRUE(base::IsLink(file_path) || base::PathExists(file_path)) |
| << "Path does not exist " << file_path; |
| EXPECT_TRUE(base::DeleteFile(file_path)) << "Cannot delete " << file_path; |
| } |
| |
| // Basic helper function to test whether the contents of the |
| // specified file exactly match the given contents string. |
| void ValidateEventFileContents(const base::FilePath& file_path, |
| const base::StringPiece expected_content) { |
| EXPECT_TRUE(base::PathIsWritable(file_path)) |
| << "ValidateEventFileContents access(): " << file_path |
| << " is not writable: " << strerror(errno) << "."; |
| ASSERT_TRUE(base::PathIsReadable(file_path)) |
| << "ValidateEventFileContents access(): " << file_path |
| << " is not readable: " << strerror(errno) << "."; |
| |
| std::string actual_contents; |
| ASSERT_TRUE(base::ReadFileToString(file_path, &actual_contents)) |
| << "ValidateEventFileContents cannot read " << file_path; |
| EXPECT_EQ(expected_content, actual_contents) |
| << "ValidateEventFileContents content mismatch."; |
| } |
| } // namespace |
| |
| // Mock class to interact with the system. |
| class MockBootStatSystem : public BootStatSystem { |
| public: |
| explicit MockBootStatSystem(const base::FilePath& disk_statistics_file_path, |
| const base::FilePath& root_path) |
| : BootStatSystem(root_path), |
| disk_statistics_file_path_(disk_statistics_file_path) {} |
| |
| base::FilePath GetDiskStatisticsFilePath() const override { |
| return disk_statistics_file_path_; |
| } |
| |
| MOCK_METHOD(std::optional<struct timespec>, GetUpTime, (), (const, override)); |
| MOCK_METHOD(base::ScopedFD, OpenRtc, (), (const, override)); |
| MOCK_METHOD(std::optional<struct rtc_time>, |
| GetRtcTime, |
| (base::ScopedFD*), |
| (const, override)); |
| |
| private: |
| base::FilePath disk_statistics_file_path_; |
| }; |
| |
| // Test environment for Bootstat class. |
| // The class uses test-specific interfaces that change the default |
| // paths from the kernel statistics pseudo-files to temporary paths |
| // selected by this test. This class also redirects the location for |
| // the event files created by BootStat.LogEvent() to a temporary directory. |
| class BootstatTest : public ::testing::Test { |
| protected: |
| virtual void SetUp(); |
| |
| // Writes disk stats to mock file. |
| bool WriteMockDiskStats(const std::string& content); |
| |
| // Writes uptime to mock file. |
| bool WriteUptime(const std::string& content); |
| |
| bool WriteUptime(const struct timespec& uptime, const struct timespec& idle); |
| |
| // Checks that the stats directory only contains the expected files. |
| void ValidateStatsDirectoryContent(const std::set<base::FilePath>& expected); |
| |
| base::ScopedTempDir temp_dir_; |
| base::FilePath stats_output_dir_; |
| std::unique_ptr<BootStat> boot_stat_; |
| // Raw pointer, owned by boot_stat_. |
| MockBootStatSystem* boot_stat_system_; |
| |
| private: |
| base::FilePath mock_disk_file_path_; |
| base::FilePath mock_root_path_; |
| }; |
| |
| void BootstatTest::SetUp() { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| stats_output_dir_ = temp_dir_.GetPath().Append("stats"); |
| ASSERT_TRUE(base::CreateDirectory(stats_output_dir_)); |
| mock_disk_file_path_ = temp_dir_.GetPath().Append("block_stats"); |
| mock_root_path_ = temp_dir_.GetPath(); |
| boot_stat_system_ = |
| new MockBootStatSystem(mock_disk_file_path_, mock_root_path_); |
| boot_stat_ = std::make_unique<BootStat>(stats_output_dir_, |
| base::WrapUnique(boot_stat_system_)); |
| } |
| |
| bool BootstatTest::WriteMockDiskStats(const std::string& content) { |
| return base::WriteFile(mock_disk_file_path_, content); |
| } |
| |
| bool BootstatTest::WriteUptime(const std::string& content) { |
| base::FilePath dir = mock_root_path_.Append(kProcPath); |
| if (!base::CreateDirectoryAndGetError(dir, nullptr)) |
| return false; |
| return base::WriteFile(mock_root_path_.Append(kProcUptimePath), content); |
| } |
| |
| bool BootstatTest::WriteUptime(const struct timespec& uptime, |
| const struct timespec& idle) { |
| static constexpr int kNsecsPerSec = 1e9; |
| |
| std::string content = base::StringPrintf( |
| "%" PRId64 ".%02ld %" PRId64 ".%02ld", |
| static_cast<int64_t>(uptime.tv_sec), |
| uptime.tv_nsec / (kNsecsPerSec / 100), static_cast<int64_t>(idle.tv_sec), |
| idle.tv_nsec / (kNsecsPerSec / 100)); |
| |
| return WriteUptime(content); |
| } |
| |
| void BootstatTest::ValidateStatsDirectoryContent( |
| const std::set<base::FilePath>& expected) { |
| std::set<base::FilePath> seen; |
| |
| base::FileEnumerator enumerator( |
| stats_output_dir_, false, |
| base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); |
| for (base::FilePath name = enumerator.Next(); !name.empty(); |
| name = enumerator.Next()) |
| seen.insert(name); |
| |
| EXPECT_EQ(expected, seen); |
| } |
| |
| // Holds LogEvent test data and expected results. |
| struct LogEventTestData { |
| const struct timespec uptime; |
| const struct timespec idle; |
| const char* expected_uptime; |
| const char* mock_disk_content; |
| const char* expected_disk_content; |
| }; |
| |
| constexpr struct LogEventTestData kDefaultTestData = { |
| // uptime (tv_sec, tv_nsec) |
| {691448, 123456789}, |
| // idle (tv_sec, tv_nsec) |
| {600000, 870000000}, |
| // expected_uptime |
| "691448.123456789 600000.870000000\n", |
| // mock_disk_content |
| " 1417116 14896 55561564 10935990 4267850 78379879" |
| " 661568738 1635920520 158 17856450 1649520570\n", |
| // expected_disk_content |
| " 1417116 14896 55561564 10935990 4267850 78379879" |
| " 661568738 1635920520 158 17856450 1649520570\n", |
| }; |
| |
| // Tests that event file content matches expectations when an |
| // event is logged multiple times. |
| TEST_F(BootstatTest, ContentGeneration) { |
| constexpr struct LogEventTestData kTestData[] = { |
| { |
| // uptime (tv_sec, tv_nsec) |
| {691448, 123456789}, |
| // idle (tv_sec, tv_nsec) |
| {600000, 870000000}, |
| // expected_uptime |
| "691448.123456789 600000.870000000\n", |
| // mock_disk_content |
| " 1417116 14896 55561564 10935990 4267850 78379879" |
| " 661568738 1635920520 158 17856450 1649520570\n", |
| // expected_disk_content |
| " 1417116 14896 55561564 10935990 4267850 78379879" |
| " 661568738 1635920520 158 17856450 1649520570\n", |
| }, |
| { |
| // uptime (tv_sec, tv_nsec) |
| {691623, 12}, // Tests zero padding |
| // expected_uptime |
| {600200, 0}, |
| "691448.123456789 600000.870000000\n" |
| "691623.000000012 600200.000000000\n", |
| // mock_disk_content |
| " 1420714 14918 55689988 11006390 4287385 78594261" |
| " 663441564 1651579200 152 17974280 1665255160\n", |
| // expected_disk_content |
| " 1417116 14896 55561564 10935990 4267850 78379879" |
| " 661568738 1635920520 158 17856450 1649520570\n" // No |
| // comma! |
| " 1420714 14918 55689988 11006390 4287385 78594261" |
| " 663441564 1651579200 152 17974280 1665255160\n", |
| }, |
| }; |
| |
| constexpr char kEventName[] = "test_event"; |
| base::FilePath uptime_file_path = |
| stats_output_dir_.Append(std::string("uptime-") + kEventName); |
| base::FilePath diskstats_file_path = |
| stats_output_dir_.Append(std::string("disk-") + kEventName); |
| |
| for (int i = 0; i < std::size(kTestData); i++) { |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillOnce(Return(std::make_optional(kTestData[i].uptime))); |
| ASSERT_TRUE(WriteUptime(kTestData[i].uptime, kTestData[i].idle)); |
| ASSERT_TRUE(WriteMockDiskStats(kTestData[i].mock_disk_content)); |
| |
| ASSERT_TRUE(boot_stat_->LogEvent(kEventName)); |
| |
| Mock::VerifyAndClear(boot_stat_system_); |
| |
| ValidateEventFileContents(uptime_file_path, kTestData[i].expected_uptime); |
| ValidateEventFileContents(diskstats_file_path, |
| kTestData[i].expected_disk_content); |
| ValidateStatsDirectoryContent( |
| std::set{uptime_file_path, diskstats_file_path}); |
| } |
| } |
| |
| // Tests that name truncation of logged events works as advertised. |
| TEST_F(BootstatTest, EventNameTruncation) { |
| constexpr struct { |
| const char* event_name; |
| const char* expected_event_name; |
| } kTestData[] = { |
| // clang-format off |
| { |
| // 16 32 48 64 |
| // kEventName: 256 chars |
| "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" |
| "=064+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" |
| "=128+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" |
| "=191+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef", |
| // expected_kEventName: 256 chars |
| "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcde", |
| }, |
| { |
| "ev", // kEventName: 2 chars |
| "ev", // expected_kEventName: 2 chars (not truncated) |
| }, |
| { |
| // kEventName: 64 chars |
| "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef", |
| // expected_kEventName: 63 chars |
| "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcde", |
| }, |
| { |
| // kEventName: 63 chars |
| "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcde", |
| // expected_kEventName: 63 chars (not truncated) |
| "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcde", |
| }, |
| // clang-format on |
| }; |
| |
| for (int i = 0; i < std::size(kTestData); i++) { |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillOnce(Return(std::make_optional(kDefaultTestData.uptime))); |
| ASSERT_TRUE(WriteUptime(kDefaultTestData.uptime, kDefaultTestData.idle)); |
| ASSERT_TRUE(WriteMockDiskStats(kDefaultTestData.mock_disk_content)); |
| |
| ASSERT_TRUE(boot_stat_->LogEvent(kTestData[i].event_name)); |
| |
| Mock::VerifyAndClear(boot_stat_system_); |
| |
| base::FilePath uptime_file_path = stats_output_dir_.Append( |
| std::string("uptime-") + kTestData[i].expected_event_name); |
| base::FilePath diskstats_file_path = stats_output_dir_.Append( |
| std::string("disk-") + kTestData[i].expected_event_name); |
| ValidateEventFileContents(uptime_file_path, |
| kDefaultTestData.expected_uptime); |
| ValidateEventFileContents(diskstats_file_path, |
| kDefaultTestData.mock_disk_content); |
| ValidateStatsDirectoryContent( |
| std::set{uptime_file_path, diskstats_file_path}); |
| RemoveFile(diskstats_file_path); |
| RemoveFile(uptime_file_path); |
| } |
| } |
| |
| // Test that event logging does not follow symbolic links (even if the target |
| // exists). |
| TEST_F(BootstatTest, SymlinkFollowTarget) { |
| constexpr char kEventName[] = "symlink-no-follow"; |
| base::FilePath uptime_file_path = |
| stats_output_dir_.Append(std::string("uptime-") + kEventName); |
| base::FilePath diskstats_file_path = |
| stats_output_dir_.Append(std::string("disk-") + kEventName); |
| |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillRepeatedly(Return(std::make_optional(kDefaultTestData.uptime))); |
| ASSERT_TRUE(WriteUptime(kDefaultTestData.uptime, kDefaultTestData.idle)); |
| ASSERT_TRUE(WriteMockDiskStats(kDefaultTestData.mock_disk_content)); |
| |
| // Relative targets for the symbolic links. |
| base::FilePath uptime_link_path("uptime.symlink"); |
| base::FilePath diskstats_link_path("disk.symlink"); |
| |
| ASSERT_TRUE(base::CreateSymbolicLink(uptime_link_path, uptime_file_path)); |
| ASSERT_TRUE( |
| base::CreateSymbolicLink(diskstats_link_path, diskstats_file_path)); |
| |
| // Create the symlink targets |
| constexpr char kDefaultContent[] = "DEFAULT"; |
| ASSERT_TRUE(base::WriteFile(uptime_file_path, kDefaultContent)); |
| ASSERT_TRUE(base::WriteFile(diskstats_file_path, kDefaultContent)); |
| |
| EXPECT_FALSE(boot_stat_->LogEvent(kEventName)); |
| |
| // Expect no additional content in the files. |
| std::string data; |
| EXPECT_TRUE(base::ReadFileToString(uptime_file_path, &data)); |
| EXPECT_EQ(data, kDefaultContent); |
| EXPECT_TRUE(base::ReadFileToString(diskstats_file_path, &data)); |
| EXPECT_EQ(data, kDefaultContent); |
| } |
| |
| // Test that event logging does not follow symbolic links (when the target does |
| // not exists). |
| TEST_F(BootstatTest, SymlinkFollowNoTarget) { |
| constexpr char kEventName[] = "symlink-no-follow"; |
| base::FilePath uptime_file_path = |
| stats_output_dir_.Append(std::string("uptime-") + kEventName); |
| base::FilePath diskstats_file_path = |
| stats_output_dir_.Append(std::string("disk-") + kEventName); |
| |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillRepeatedly(Return(std::make_optional(kDefaultTestData.uptime))); |
| ASSERT_TRUE(WriteUptime(kDefaultTestData.uptime, kDefaultTestData.idle)); |
| ASSERT_TRUE(WriteMockDiskStats(kDefaultTestData.mock_disk_content)); |
| |
| // Relative targets for the symbolic links. |
| base::FilePath uptime_link_path("uptime.symlink"); |
| base::FilePath diskstats_link_path("disk.symlink"); |
| |
| ASSERT_TRUE(base::CreateSymbolicLink(uptime_link_path, uptime_file_path)); |
| ASSERT_TRUE( |
| base::CreateSymbolicLink(diskstats_link_path, diskstats_file_path)); |
| |
| EXPECT_FALSE(boot_stat_->LogEvent(kEventName)); |
| |
| // Expect to be unable to read content |
| std::string data; |
| EXPECT_FALSE(base::ReadFileToString(uptime_file_path, &data)); |
| EXPECT_FALSE(base::ReadFileToString(diskstats_file_path, &data)); |
| |
| // ... and the targets must not exist |
| EXPECT_FALSE(base::PathExists(stats_output_dir_.Append(uptime_link_path))); |
| EXPECT_FALSE(base::PathExists(stats_output_dir_.Append(diskstats_link_path))); |
| } |
| |
| // Nanoseconds in a millisecond. |
| constexpr int kmSec = 1000 * 1000; |
| |
| // Tests that rtc sync can be generated successfully |
| TEST_F(BootstatTest, RtcGeneration) { |
| // Test a worst case where it takes ~1s to get a tick. |
| constexpr struct timespec kUptimeTestData[5] = { |
| // tv_sec, tv_nsec |
| {30, 0 * kmSec}, {30, 333 * kmSec}, {30, 666 * kmSec}, |
| {30, 999 * kmSec}, {31, 1 * kmSec}, |
| }; |
| // struct rtc_time: tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year |
| constexpr struct rtc_time kRtcTestData[2] = { |
| {33, 1, 12, 3, 8, 121}, |
| {34, 1, 12, 3, 8, 121}, |
| }; |
| constexpr char kExpectedRtcSyncData[] = |
| "30.999000000 31.001000000 2021-09-03 12:01:34\n"; |
| |
| constexpr char kEventName[] = "test_event"; |
| base::FilePath sync_rtc_file_path = |
| stats_output_dir_.Append(std::string("sync-rtc-") + kEventName); |
| |
| int rtc_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY | O_CLOEXEC)); |
| |
| { |
| InSequence seq; // All these calls must be in sequence. |
| |
| EXPECT_CALL(*boot_stat_system_, OpenRtc()) |
| .WillOnce(Return(ByMove(base::ScopedFD(rtc_fd)))); |
| |
| for (int i = 0; i < 3; i++) { |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillRepeatedly(Return(std::make_optional(kUptimeTestData[i]))); |
| EXPECT_CALL(*boot_stat_system_, GetRtcTime(_)) |
| .Times(1) |
| .WillOnce(Return(std::make_optional(kRtcTestData[0]))); |
| } |
| |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillRepeatedly(Return(std::make_optional(kUptimeTestData[3]))); |
| EXPECT_CALL(*boot_stat_system_, GetRtcTime(_)) |
| .Times(1) |
| .WillOnce(Return(std::make_optional(kRtcTestData[1]))); |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillRepeatedly(Return(std::make_optional(kUptimeTestData[4]))); |
| |
| boot_stat_->LogRtcSync(kEventName); |
| |
| ValidateEventFileContents(sync_rtc_file_path, kExpectedRtcSyncData); |
| } |
| |
| RemoveFile(sync_rtc_file_path); |
| } |
| |
| // Tests that rtc sync times out if it does not tick. |
| TEST_F(BootstatTest, RtcGenerationTimeout) { |
| // The code times out after 1.5s, but we let it run for 2.0s at most. |
| constexpr struct timespec kUptimeTestData[] = { |
| // tv_sec, tv_nsec |
| {30, 0 * kmSec}, {30, 300 * kmSec}, {31, 400 * kmSec}, |
| {31, 600 * kmSec}, {32, 0 * kmSec}, |
| }; |
| // struct rtc_time: tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year |
| constexpr struct rtc_time kRtcTestData = {33, 1, 12, 3, 9, 121}; |
| |
| constexpr char kEventName[] = "test_event"; |
| base::FilePath sync_rtc_file_path = |
| stats_output_dir_.Append(std::string("sync-rtc-") + kEventName); |
| |
| int rtc_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY)); |
| |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .Times(AnyNumber()) |
| .WillOnce(Return(std::make_optional(kUptimeTestData[0]))) |
| .WillOnce(Return(std::make_optional(kUptimeTestData[1]))) |
| .WillOnce(Return(std::make_optional(kUptimeTestData[2]))) |
| .WillOnce(Return(std::make_optional(kUptimeTestData[3]))) |
| .WillOnce(Return(std::make_optional(kUptimeTestData[4]))); |
| EXPECT_CALL(*boot_stat_system_, OpenRtc()) |
| .WillOnce(Return(ByMove(base::ScopedFD(rtc_fd)))); |
| EXPECT_CALL(*boot_stat_system_, GetRtcTime(_)) |
| .WillRepeatedly(Return(std::make_optional(kRtcTestData))); |
| |
| boot_stat_->LogRtcSync(kEventName); |
| } |
| |
| TEST_F(BootstatTest, GetIdleTime) { |
| { |
| ASSERT_TRUE(WriteUptime("3.00 2.50")); |
| auto ts = boot_stat_system_->GetIdleTime(); |
| ASSERT_TRUE(ts); |
| EXPECT_EQ(ts->InMilliseconds(), 2500); |
| } |
| { |
| ASSERT_TRUE(WriteUptime("5.43 0.00")); |
| auto ts = boot_stat_system_->GetIdleTime(); |
| ASSERT_TRUE(ts); |
| EXPECT_EQ(ts->InMilliseconds(), 0); |
| } |
| } |
| |
| TEST_F(BootstatTest, GetEventTimings) { |
| constexpr struct LogEventTestData kTestData[] = { |
| { |
| .uptime = |
| { |
| .tv_sec = 1234, |
| .tv_nsec = 56789, |
| }, |
| .idle = |
| { |
| .tv_sec = 60000, |
| .tv_nsec = 120000000, |
| }, |
| .mock_disk_content = |
| " 1417116 14896 55561564 10935990 4267850 78379879" |
| " 661568738 1635920520 158 17856450 1649520570\n", |
| }, |
| { |
| .uptime = |
| { |
| .tv_sec = 20000, |
| .tv_nsec = 0, |
| }, |
| .idle = |
| { |
| .tv_sec = 90017, |
| .tv_nsec = 0, |
| }, |
| .mock_disk_content = |
| " 1420714 14918 55689988 11006390 4287385 78594261" |
| " 663441564 1651579200 152 17974280 1665255160\n", |
| }, |
| }; |
| |
| for (int i = 0; i < std::size(kTestData); i++) { |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillOnce(Return(std::make_optional(kTestData[i].uptime))); |
| ASSERT_TRUE(WriteUptime(kTestData[i].uptime, kTestData[i].idle)); |
| ASSERT_TRUE(WriteMockDiskStats(kTestData[i].mock_disk_content)); |
| |
| ASSERT_TRUE(boot_stat_->LogEvent("ev")); |
| |
| Mock::VerifyAndClear(boot_stat_system_); |
| } |
| |
| auto events = boot_stat_->GetEventTimings("ev"); |
| ASSERT_TRUE(events); |
| |
| ASSERT_EQ(events->size(), std::size(kTestData)); |
| for (int i = 0; i < std::size(kTestData); i++) { |
| auto& event = (*events)[i]; |
| EXPECT_EQ(event.uptime, base::Seconds(kTestData[i].uptime.tv_sec) + |
| base::Nanoseconds(kTestData[i].uptime.tv_nsec)); |
| EXPECT_EQ(event.idle_time, |
| base::Seconds(kTestData[i].idle.tv_sec) + |
| base::Nanoseconds(kTestData[i].idle.tv_nsec)); |
| } |
| } |
| |
| TEST_F(BootstatTest, Umask) { |
| constexpr char kEventName[] = "umasking"; |
| |
| EXPECT_CALL(*boot_stat_system_, GetUpTime()) |
| .WillRepeatedly(Return(std::make_optional(kDefaultTestData.uptime))); |
| ASSERT_TRUE(WriteUptime(kDefaultTestData.uptime, kDefaultTestData.idle)); |
| ASSERT_TRUE(WriteMockDiskStats(kDefaultTestData.mock_disk_content)); |
| |
| // By default (umask), create files without group/other read/write |
| // permissions. Bootstat should still force group/other read permissions. |
| brillo::ScopedUmask scoped_mask(S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); |
| |
| ASSERT_TRUE(boot_stat_->LogEvent(kEventName)); |
| |
| base::FilePath uptime_file_path = |
| stats_output_dir_.Append(std::string("uptime-") + kEventName); |
| base::FilePath diskstats_file_path = |
| stats_output_dir_.Append(std::string("disk-") + kEventName); |
| |
| int mode; |
| ASSERT_TRUE(GetPosixFilePermissions(uptime_file_path, &mode)); |
| // Honor write mask: |
| EXPECT_EQ(mode & (S_IWGRP | S_IWOTH), 0) << "Unexpected write permissions"; |
| // But don't honor read mask: |
| EXPECT_EQ(mode & (S_IRGRP | S_IROTH), S_IRGRP | S_IROTH) |
| << "Unexpected read permissions"; |
| |
| ASSERT_TRUE(GetPosixFilePermissions(diskstats_file_path, &mode)); |
| // Honor write mask: |
| EXPECT_EQ(mode & (S_IWGRP | S_IWOTH), 0) << "Unexpected write permissions"; |
| // But don't honor read mask: |
| EXPECT_EQ(mode & (S_IRGRP | S_IROTH), S_IRGRP | S_IROTH) |
| << "Unexpected read permissions"; |
| } |
| |
| } // namespace bootstat |