| /** | 
 |  * Copyright © 2019 IBM Corporation | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 | #include "extensions/openpower-pels/paths.hpp" | 
 | #include "extensions/openpower-pels/repository.hpp" | 
 | #include "pel_utils.hpp" | 
 |  | 
 | #include <ext/stdio_filebuf.h> | 
 |  | 
 | #include <filesystem> | 
 |  | 
 | #include <gtest/gtest.h> | 
 |  | 
 | using namespace openpower::pels; | 
 | namespace fs = std::filesystem; | 
 |  | 
 | /** | 
 |  * Clean the Repo after every testcase. | 
 |  * And because we have PEL object, also clean up | 
 |  * the log ID. | 
 |  */ | 
 | class RepositoryTest : public CleanLogID | 
 | { | 
 |   protected: | 
 |     void SetUp() override | 
 |     { | 
 |         repoPath = getPELRepoPath(); | 
 |     } | 
 |  | 
 |     void TearDown() override | 
 |     { | 
 |         fs::remove_all(repoPath); | 
 |     } | 
 |  | 
 |     fs::path repoPath; | 
 | }; | 
 |  | 
 | TEST_F(RepositoryTest, FilenameTest) | 
 | { | 
 |     BCDTime date = {0x20, 0x30, 0x11, 0x28, 0x13, 0x6, 0x7, 0x8}; | 
 |  | 
 |     EXPECT_EQ(Repository::getPELFilename(0x12345678, date), | 
 |               "2030112813060708_12345678"); | 
 |  | 
 |     EXPECT_EQ(Repository::getPELFilename(0xAABBCCDD, date), | 
 |               "2030112813060708_AABBCCDD"); | 
 |  | 
 |     EXPECT_EQ(Repository::getPELFilename(0x3AFF1, date), | 
 |               "2030112813060708_0003AFF1"); | 
 |  | 
 |     EXPECT_EQ(Repository::getPELFilename(100, date), | 
 |               "2030112813060708_00000064"); | 
 |  | 
 |     EXPECT_EQ(Repository::getPELFilename(0, date), "2030112813060708_00000000"); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, AddTest) | 
 | { | 
 |     Repository repo{repoPath}; | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |  | 
 |     repo.add(pel); | 
 |  | 
 |     // Check that the PEL was stored where it was supposed to be, | 
 |     // and that it wrote the PEL data. | 
 |     const auto ts = pel->privateHeader().commitTimestamp(); | 
 |     auto name = Repository::getPELFilename(pel->id(), ts); | 
 |  | 
 |     fs::path file = repoPath / "logs" / name; | 
 |     EXPECT_TRUE(fs::exists(file)); | 
 |  | 
 |     auto newData = readPELFile(file); | 
 |     auto pelData = pel->data(); | 
 |     EXPECT_EQ(*newData, pelData); | 
 |  | 
 |     EXPECT_EQ(repo.lastPelID(), pel->id()); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, RemoveTest) | 
 | { | 
 |     using pelID = Repository::LogID::Pel; | 
 |     using obmcID = Repository::LogID::Obmc; | 
 |  | 
 |     // Add and remove a PEL from the repo | 
 |  | 
 |     Repository repo{repoPath}; | 
 |  | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data, 1); | 
 |  | 
 |     pel->assignID(); | 
 |     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}}; | 
 |  | 
 |     repo.add(pel); | 
 |  | 
 |     auto removedID = repo.remove(id); | 
 |     ASSERT_TRUE(removedID); | 
 |     EXPECT_EQ(*removedID, id); | 
 |  | 
 |     EXPECT_FALSE(repo.hasPEL(id)); | 
 |  | 
 |     // Try to remove it again, not there | 
 |     EXPECT_FALSE(repo.remove(id)); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, RestoreTest) | 
 | { | 
 |     using pelID = Repository::LogID::Pel; | 
 |     using obmcID = Repository::LogID::Obmc; | 
 |  | 
 |     std::vector<Repository::LogID> ids; | 
 |  | 
 |     { | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         // Add some PELs to the repository | 
 |         { | 
 |             auto data = pelDataFactory(TestPELType::pelSimple); | 
 |             auto pel = std::make_unique<PEL>(data, 1); | 
 |             pel->assignID(); | 
 |             repo.add(pel); | 
 |             ids.emplace_back(pelID(pel->id()), obmcID(1)); | 
 |         } | 
 |         { | 
 |             auto data = pelDataFactory(TestPELType::pelSimple); | 
 |             auto pel = std::make_unique<PEL>(data, 2); | 
 |             pel->assignID(); | 
 |             repo.add(pel); | 
 |             ids.emplace_back(pelID(pel->id()), obmcID(2)); | 
 |         } | 
 |  | 
 |         // Check they're there | 
 |         EXPECT_TRUE(repo.hasPEL(ids[0])); | 
 |         EXPECT_TRUE(repo.hasPEL(ids[1])); | 
 |  | 
 |         // Do some other search tests while we're here. | 
 |  | 
 |         // Search based on PEL ID | 
 |         Repository::LogID id(pelID(ids[0].pelID)); | 
 |         EXPECT_TRUE(repo.hasPEL(id)); | 
 |  | 
 |         // Search based on OBMC log ID | 
 |         id.pelID.id = 0; | 
 |         id.obmcID = ids[0].obmcID; | 
 |         EXPECT_TRUE(repo.hasPEL(id)); | 
 |  | 
 |         // ... based on the other PEL ID | 
 |         id.pelID = ids[1].pelID; | 
 |         id.obmcID.id = 0; | 
 |         EXPECT_TRUE(repo.hasPEL(id)); | 
 |  | 
 |         // Not found | 
 |         id.pelID.id = 99; | 
 |         id.obmcID.id = 100; | 
 |         EXPECT_FALSE(repo.hasPEL(id)); | 
 |  | 
 |         // Try to remove it anyway | 
 |         EXPECT_FALSE(repo.remove(id)); | 
 |     } | 
 |  | 
 |     { | 
 |         // Restore and check they're still there, then | 
 |         // remove them. | 
 |         Repository repo{repoPath}; | 
 |         EXPECT_TRUE(repo.hasPEL(ids[0])); | 
 |         EXPECT_TRUE(repo.hasPEL(ids[1])); | 
 |  | 
 |         repo.remove(ids[0]); | 
 |         EXPECT_FALSE(repo.hasPEL(ids[0])); | 
 |  | 
 |         repo.remove(ids[1]); | 
 |         EXPECT_FALSE(repo.hasPEL(ids[1])); | 
 |     } | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestGetPELData) | 
 | { | 
 |     using ID = Repository::LogID; | 
 |     Repository repo{repoPath}; | 
 |  | 
 |     ID badID{ID::Pel(42)}; | 
 |     auto noData = repo.getPELData(badID); | 
 |     EXPECT_FALSE(noData); | 
 |  | 
 |     // Add a PEL to the repo, and get the data back with getPELData. | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto dataCopy = data; | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     auto pelID = pel->id(); | 
 |     repo.add(pel); | 
 |  | 
 |     ID id{ID::Pel(pelID)}; | 
 |     auto pelData = repo.getPELData(id); | 
 |  | 
 |     ASSERT_TRUE(pelData); | 
 |     EXPECT_EQ(dataCopy, *pelData); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestForEach) | 
 | { | 
 |     Repository repo{repoPath}; | 
 |  | 
 |     // Add 2 PELs | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     repo.add(pel); | 
 |  | 
 |     pel = std::make_unique<PEL>(data); | 
 |     pel->assignID(); | 
 |     pel->setCommitTime(); | 
 |     repo.add(pel); | 
 |  | 
 |     // Make a function that saves the IDs | 
 |     std::vector<uint32_t> ids; | 
 |     Repository::ForEachFunc f1 = [&ids](const PEL& pel) { | 
 |         ids.push_back(pel.id()); | 
 |         return false; | 
 |     }; | 
 |  | 
 |     repo.for_each(f1); | 
 |  | 
 |     EXPECT_EQ(ids.size(), 2); | 
 |  | 
 |     // Stop after the first time in. | 
 |     Repository::ForEachFunc f2 = [&ids](const PEL& pel) { | 
 |         ids.push_back(pel.id()); | 
 |         return true; | 
 |     }; | 
 |  | 
 |     ids.clear(); | 
 |     repo.for_each(f2); | 
 |     EXPECT_EQ(ids.size(), 1); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestSubscriptions) | 
 | { | 
 |     std::vector<uint32_t> added; | 
 |     std::vector<uint32_t> removed; | 
 |  | 
 |     Repository::AddCallback ac = [&added](const PEL& pel) { | 
 |         added.push_back(pel.id()); | 
 |     }; | 
 |  | 
 |     Repository::DeleteCallback dc = [&removed](uint32_t id) { | 
 |         removed.push_back(id); | 
 |     }; | 
 |  | 
 |     Repository repo{repoPath}; | 
 |     repo.subscribeToAdds("test", ac); | 
 |     repo.subscribeToDeletes("test", dc); | 
 |  | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     auto pelID = pel->id(); | 
 |     repo.add(pel); | 
 |  | 
 |     EXPECT_EQ(added.size(), 1); | 
 |  | 
 |     using ID = Repository::LogID; | 
 |     ID id{ID::Pel(pelID)}; | 
 |     repo.remove(id); | 
 |  | 
 |     EXPECT_EQ(removed.size(), 1); | 
 |  | 
 |     repo.unsubscribeFromAdds("test"); | 
 |     repo.unsubscribeFromDeletes("test"); | 
 |  | 
 |     added.clear(); | 
 |     removed.clear(); | 
 |  | 
 |     repo.add(pel); | 
 |     EXPECT_EQ(added.size(), 0); | 
 |  | 
 |     repo.remove(id); | 
 |     EXPECT_EQ(removed.size(), 0); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestGetAttributes) | 
 | { | 
 |     uint32_t pelID = 0; | 
 |     std::bitset<16> actionFlags; | 
 |  | 
 |     { | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         // Add a PEL to the repo | 
 |         auto data = pelDataFactory(TestPELType::pelSimple); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |  | 
 |         pelID = pel->id(); | 
 |         actionFlags = pel->userHeader().actionFlags(); | 
 |  | 
 |         using ID = Repository::LogID; | 
 |         ID id{ID::Pel(pelID)}; | 
 |  | 
 |         auto a = repo.getPELAttributes(id); | 
 |         EXPECT_TRUE(a); | 
 |         EXPECT_EQ((*a).get().actionFlags, actionFlags); | 
 |  | 
 |         id.pelID.id = 0; | 
 |         a = repo.getPELAttributes(id); | 
 |         EXPECT_FALSE(a); | 
 |     } | 
 |  | 
 |     { | 
 |         // Restore the repository and check again | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         using ID = Repository::LogID; | 
 |         ID id{ID::Pel(pelID)}; | 
 |  | 
 |         auto a = repo.getPELAttributes(id); | 
 |         EXPECT_TRUE(a); | 
 |         EXPECT_EQ((*a).get().actionFlags, actionFlags); | 
 |  | 
 |         id.pelID.id = 0; | 
 |         a = repo.getPELAttributes(id); | 
 |         EXPECT_FALSE(a); | 
 |     } | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestSetHostState) | 
 | { | 
 |     // Add a PEL to the repo | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     using ID = Repository::LogID; | 
 |     ID id{ID::Pel(pel->id())}; | 
 |  | 
 |     { | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         repo.add(pel); | 
 |  | 
 |         auto a = repo.getPELAttributes(id); | 
 |         EXPECT_EQ((*a).get().hostState, TransmissionState::newPEL); | 
 |  | 
 |         repo.setPELHostTransState(pel->id(), TransmissionState::acked); | 
 |  | 
 |         // First, check the attributes | 
 |         a = repo.getPELAttributes(id); | 
 |         EXPECT_EQ((*a).get().hostState, TransmissionState::acked); | 
 |  | 
 |         // Next, check the PEL data itself | 
 |         auto pelData = repo.getPELData(id); | 
 |         PEL newPEL{*pelData}; | 
 |         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked); | 
 |     } | 
 |  | 
 |     { | 
 |         // Now restore, and check again | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         // First, check the attributes | 
 |         auto a = repo.getPELAttributes(id); | 
 |         EXPECT_EQ((*a).get().hostState, TransmissionState::acked); | 
 |  | 
 |         // Next, check the PEL data itself | 
 |         auto pelData = repo.getPELData(id); | 
 |         PEL newPEL{*pelData}; | 
 |         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked); | 
 |     } | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestSetHMCState) | 
 | { | 
 |     // Add a PEL to the repo | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     using ID = Repository::LogID; | 
 |     ID id{ID::Pel(pel->id())}; | 
 |  | 
 |     { | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         repo.add(pel); | 
 |  | 
 |         auto a = repo.getPELAttributes(id); | 
 |         EXPECT_EQ((*a).get().hmcState, TransmissionState::newPEL); | 
 |  | 
 |         repo.setPELHMCTransState(pel->id(), TransmissionState::acked); | 
 |  | 
 |         // First, check the attributes | 
 |         a = repo.getPELAttributes(id); | 
 |         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked); | 
 |  | 
 |         // Next, check the PEL data itself | 
 |         auto pelData = repo.getPELData(id); | 
 |         PEL newPEL{*pelData}; | 
 |         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked); | 
 |     } | 
 |  | 
 |     { | 
 |         // Now restore, and check again | 
 |         Repository repo{repoPath}; | 
 |  | 
 |         // First, check the attributes | 
 |         auto a = repo.getPELAttributes(id); | 
 |         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked); | 
 |  | 
 |         // Next, check the PEL data itself | 
 |         auto pelData = repo.getPELData(id); | 
 |         PEL newPEL{*pelData}; | 
 |         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked); | 
 |     } | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, TestGetPELFD) | 
 | { | 
 |     Repository repo{repoPath}; | 
 |  | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     pel->setCommitTime(); | 
 |     pel->assignID(); | 
 |  | 
 |     repo.add(pel); | 
 |  | 
 |     using ID = Repository::LogID; | 
 |     ID id{ID::Pel(pel->id())}; | 
 |  | 
 |     auto fd = repo.getPELFD(id); | 
 |  | 
 |     EXPECT_TRUE(fd); | 
 |  | 
 |     // Get the size | 
 |     struct stat s; | 
 |     int r = fstat(*fd, &s); | 
 |     ASSERT_EQ(r, 0); | 
 |  | 
 |     auto size = s.st_size; | 
 |  | 
 |     // Read the PEL data out of the FD | 
 |     FILE* fp = fdopen(*fd, "r"); | 
 |     ASSERT_NE(fp, nullptr); | 
 |  | 
 |     std::vector<uint8_t> newData; | 
 |     newData.resize(size); | 
 |     r = fread(newData.data(), 1, size, fp); | 
 |     EXPECT_EQ(r, size); | 
 |  | 
 |     PEL newPEL{newData}; | 
 |  | 
 |     EXPECT_TRUE(newPEL.valid()); | 
 |     EXPECT_EQ(newPEL.id(), pel->id()); | 
 |  | 
 |     fclose(fp); | 
 |  | 
 |     // Call getPELFD again, this time with a bad ID | 
 |     id.pelID.id = 42; | 
 |     fd = repo.getPELFD(id); | 
 |  | 
 |     EXPECT_FALSE(fd); | 
 | } | 
 |  | 
 | // Test the repo size statistics | 
 | TEST_F(RepositoryTest, TestRepoSizes) | 
 | { | 
 |     uint32_t id = 1; | 
 |  | 
 |     Repository repo{repoPath, 10000, 500}; | 
 |  | 
 |     // All of the size stats are the sizes on disk a PEL takes up, | 
 |     // which is different than the file size.  Disk usage seems | 
 |     // to have a granularity of 4096 bytes.  This probably shouldn't | 
 |     // be hardcoded, but I don't know how to look it up dynamically. | 
 |  | 
 |     // All sizes are zero | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 0); | 
 |         EXPECT_EQ(stats.bmc, 0); | 
 |         EXPECT_EQ(stats.nonBMC, 0); | 
 |         EXPECT_EQ(stats.bmcServiceable, 0); | 
 |         EXPECT_EQ(stats.bmcInfo, 0); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 0); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 |  | 
 |     // Add a 2000B BMC predictive error | 
 |     auto data = pelFactory(id++, 'O', 0x20, 0x8800, 2000); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     auto pelID1 = pel->id(); | 
 |     repo.add(pel); | 
 |  | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 4096); | 
 |         EXPECT_EQ(stats.bmc, 4096); | 
 |         EXPECT_EQ(stats.nonBMC, 0); | 
 |         EXPECT_EQ(stats.bmcServiceable, 4096); | 
 |         EXPECT_EQ(stats.bmcInfo, 0); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 0); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 |  | 
 |     // Add a 5000B BMC informational error | 
 |     data = pelFactory(id++, 'O', 0x00, 0x8800, 5000); | 
 |     pel = std::make_unique<PEL>(data); | 
 |     auto pelID2 = pel->id(); | 
 |     repo.add(pel); | 
 |  | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 4096 + 8192); | 
 |         EXPECT_EQ(stats.bmc, 4096 + 8192); | 
 |         EXPECT_EQ(stats.nonBMC, 0); | 
 |         EXPECT_EQ(stats.bmcServiceable, 4096); | 
 |         EXPECT_EQ(stats.bmcInfo, 8192); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 0); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 |  | 
 |     // Add a 4000B Hostboot unrecoverable error | 
 |     data = pelFactory(id++, 'B', 0x40, 0x8800, 4000); | 
 |     pel = std::make_unique<PEL>(data); | 
 |     auto pelID3 = pel->id(); | 
 |     repo.add(pel); | 
 |  | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 4096 + 8192 + 4096); | 
 |         EXPECT_EQ(stats.bmc, 4096 + 8192); | 
 |         EXPECT_EQ(stats.nonBMC, 4096); | 
 |         EXPECT_EQ(stats.bmcServiceable, 4096); | 
 |         EXPECT_EQ(stats.bmcInfo, 8192); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 4096); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 |  | 
 |     // Add a 5000B Hostboot informational error | 
 |     data = pelFactory(id++, 'B', 0x00, 0x8800, 5000); | 
 |     pel = std::make_unique<PEL>(data); | 
 |     auto pelID4 = pel->id(); | 
 |     repo.add(pel); | 
 |  | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 4096 + 8192 + 4096 + 8192); | 
 |         EXPECT_EQ(stats.bmc, 4096 + 8192); | 
 |         EXPECT_EQ(stats.nonBMC, 4096 + 8192); | 
 |         EXPECT_EQ(stats.bmcServiceable, 4096); | 
 |         EXPECT_EQ(stats.bmcInfo, 8192); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 4096); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 8192); | 
 |     } | 
 |  | 
 |     // Remove the BMC serviceable error | 
 |     using ID = Repository::LogID; | 
 |     ID id1{ID::Pel(pelID1)}; | 
 |  | 
 |     repo.remove(id1); | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 8192 + 4096 + 8192); | 
 |         EXPECT_EQ(stats.bmc, 8192); | 
 |         EXPECT_EQ(stats.nonBMC, 4096 + 8192); | 
 |         EXPECT_EQ(stats.bmcServiceable, 0); | 
 |         EXPECT_EQ(stats.bmcInfo, 8192); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 4096); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 8192); | 
 |     } | 
 |  | 
 |     // Remove the Hostboot informational error | 
 |     ID id4{ID::Pel(pelID4)}; | 
 |  | 
 |     repo.remove(id4); | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 8192 + 4096); | 
 |         EXPECT_EQ(stats.bmc, 8192); | 
 |         EXPECT_EQ(stats.nonBMC, 4096); | 
 |         EXPECT_EQ(stats.bmcServiceable, 0); | 
 |         EXPECT_EQ(stats.bmcInfo, 8192); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 4096); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 |  | 
 |     // Remove the BMC informational error | 
 |     ID id2{ID::Pel(pelID2)}; | 
 |  | 
 |     repo.remove(id2); | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 4096); | 
 |         EXPECT_EQ(stats.bmc, 0); | 
 |         EXPECT_EQ(stats.nonBMC, 4096); | 
 |         EXPECT_EQ(stats.bmcServiceable, 0); | 
 |         EXPECT_EQ(stats.bmcInfo, 0); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 4096); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 |  | 
 |     // Remove the hostboot unrecoverable error | 
 |     ID id3{ID::Pel(pelID3)}; | 
 |  | 
 |     repo.remove(id3); | 
 |     { | 
 |         const auto& stats = repo.getSizeStats(); | 
 |         EXPECT_EQ(stats.total, 0); | 
 |         EXPECT_EQ(stats.bmc, 0); | 
 |         EXPECT_EQ(stats.nonBMC, 0); | 
 |         EXPECT_EQ(stats.bmcServiceable, 0); | 
 |         EXPECT_EQ(stats.bmcInfo, 0); | 
 |         EXPECT_EQ(stats.nonBMCServiceable, 0); | 
 |         EXPECT_EQ(stats.nonBMCInfo, 0); | 
 |     } | 
 | } | 
 |  | 
 | // Prune PELs, when no HMC/OS/PHYP acks | 
 | TEST_F(RepositoryTest, TestPruneNoAcks) | 
 | { | 
 |     std::vector<uint32_t> id; | 
 |     Repository repo{repoPath, 4096 * 20, 100}; | 
 |  | 
 |     // Add 10 4096B (on disk) PELs of BMC nonInfo, Info and nonBMC info, | 
 |     // nonInfo errors. None of them acked by PHYP, host, or HMC. | 
 |     for (uint32_t i = 1; i <= 10; i++) | 
 |     { | 
 |         // BMC predictive | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |  | 
 |         // BMC info | 
 |         data = pelFactory(i + 100, 'O', 0x0, 0x8800, 500); | 
 |         pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |  | 
 |         // Hostboot predictive | 
 |         data = pelFactory(i + 200, 'B', 0x20, 0x8800, 500); | 
 |         pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |  | 
 |         // Hostboot info | 
 |         data = pelFactory(i + 300, 'B', 0x0, 0x8800, 500); | 
 |         pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     const auto& sizes = repo.getSizeStats(); | 
 |     EXPECT_EQ(sizes.total, 4096 * 40); | 
 |  | 
 |     // Sanity check the very first PELs with IDs 1 to 4 are | 
 |     // there so we can check they are removed after the prune. | 
 |     for (uint32_t i = 1; i < 5; i++) | 
 |     { | 
 |         Repository::LogID logID{Repository::LogID::Pel{i}}; | 
 |         EXPECT_TRUE(repo.getPELAttributes(logID)); | 
 |     } | 
 |  | 
 |     // Prune down to 15%/30%/15%/30% = 90% total | 
 |     auto IDs = repo.prune(id); | 
 |  | 
 |     // Check the final sizes | 
 |     EXPECT_EQ(sizes.total, 4096 * 18);            // 90% of 20 PELs | 
 |     EXPECT_EQ(sizes.bmcInfo, 4096 * 3);           // 15% of 20 PELs | 
 |     EXPECT_EQ(sizes.bmcServiceable, 4096 * 6);    // 30% of 20 PELs | 
 |     EXPECT_EQ(sizes.nonBMCInfo, 4096 * 3);        // 15% of 20 PELs | 
 |     EXPECT_EQ(sizes.nonBMCServiceable, 4096 * 6); // 30% of 20 PELs | 
 |  | 
 |     // Check that at least the 4 oldest, which are the oldest of | 
 |     // each type, were removed. | 
 |     for (uint32_t i = 1; i < 5; i++) | 
 |     { | 
 |         Repository::LogID logID{Repository::LogID::Pel{i}}; | 
 |         EXPECT_FALSE(repo.getPELAttributes(logID)); | 
 |  | 
 |         // Make sure the corresponding OpenBMC event log ID which is | 
 |         // 500 + the PEL ID is in the list. | 
 |         EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end()); | 
 |     } | 
 | } | 
 |  | 
 | // Test that if filled completely with 1 type of PEL, that | 
 | // pruning still works properly | 
 | TEST_F(RepositoryTest, TestPruneInfoOnly) | 
 | { | 
 |     std::vector<uint32_t> id; | 
 |     Repository repo{repoPath, 4096 * 22, 100}; | 
 |  | 
 |     // Fill 4096*23 bytes on disk of BMC info PELs | 
 |     for (uint32_t i = 1; i <= 23; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0, 0x8800, 1000); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     const auto& sizes = repo.getSizeStats(); | 
 |     EXPECT_EQ(sizes.total, 4096 * 23); | 
 |  | 
 |     // Pruning to 15% of 4096 * 22 will leave 3 4096B PELs. | 
 |  | 
 |     // Sanity check the oldest 20 are there so when they | 
 |     // get pruned below we'll know they were removed. | 
 |     for (uint32_t i = 1; i <= 20; i++) | 
 |     { | 
 |         Repository::LogID logID{Repository::LogID::Pel{i}}; | 
 |         EXPECT_TRUE(repo.getPELAttributes(logID)); | 
 |     } | 
 |  | 
 |     auto IDs = repo.prune(id); | 
 |  | 
 |     // Check the final sizes | 
 |     EXPECT_EQ(sizes.total, 4096 * 3); | 
 |     EXPECT_EQ(sizes.bmcInfo, 4096 * 3); | 
 |     EXPECT_EQ(sizes.bmcServiceable, 0); | 
 |     EXPECT_EQ(sizes.nonBMCInfo, 0); | 
 |     EXPECT_EQ(sizes.nonBMCServiceable, 0); | 
 |  | 
 |     EXPECT_EQ(IDs.size(), 20); | 
 |  | 
 |     // Can no longer find the oldest 20 PELs. | 
 |     for (uint32_t i = 1; i <= 20; i++) | 
 |     { | 
 |         Repository::LogID logID{Repository::LogID::Pel{i}}; | 
 |         EXPECT_FALSE(repo.getPELAttributes(logID)); | 
 |         EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end()); | 
 |     } | 
 | } | 
 |  | 
 | // Test that the HMC/OS/PHYP ack values affect the | 
 | // pruning order. | 
 | TEST_F(RepositoryTest, TestPruneWithAcks) | 
 | { | 
 |     std::vector<uint32_t> id; | 
 |     Repository repo{repoPath, 4096 * 20, 100}; | 
 |  | 
 |     // Fill 30% worth of BMC non-info non-acked PELs | 
 |     for (uint32_t i = 1; i <= 6; i++) | 
 |     { | 
 |         // BMC predictive | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     // Add another PEL to push it over the 30%, each time adding | 
 |     // a different type that should be pruned before the above ones | 
 |     // even though those are older. | 
 |     for (uint32_t i = 1; i <= 3; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         auto idToDelete = pel->obmcLogID(); | 
 |         repo.add(pel); | 
 |  | 
 |         if (1 == i) | 
 |         { | 
 |             repo.setPELHMCTransState(pel->id(), TransmissionState::acked); | 
 |         } | 
 |         else if (2 == i) | 
 |         { | 
 |             repo.setPELHostTransState(pel->id(), TransmissionState::acked); | 
 |         } | 
 |         else | 
 |         { | 
 |             repo.setPELHostTransState(pel->id(), TransmissionState::sent); | 
 |         } | 
 |  | 
 |         auto IDs = repo.prune(id); | 
 |         EXPECT_EQ(repo.getSizeStats().total, 4096 * 6); | 
 |  | 
 |         // The newest PEL should be the one deleted | 
 |         ASSERT_EQ(IDs.size(), 1); | 
 |         EXPECT_EQ(IDs[0], idToDelete); | 
 |     } | 
 | } | 
 |  | 
 | // Test that the total number of PELs limit is enforced. | 
 | TEST_F(RepositoryTest, TestPruneTooManyPELs) | 
 | { | 
 |     std::vector<uint32_t> id; | 
 |     Repository repo{repoPath, 4096 * 100, 10}; | 
 |  | 
 |     // Add 10, which is the limit and is still OK | 
 |     for (uint32_t i = 1; i <= 10; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     auto IDs = repo.prune(id); | 
 |  | 
 |     // Nothing pruned yet | 
 |     EXPECT_TRUE(IDs.empty()); | 
 |  | 
 |     // Add 1 more PEL which will be too many. | 
 |     { | 
 |         auto data = pelFactory(11, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     // Now that's it's over the limit of 10, it will bring it down | 
 |     // to 80%, which is 8 after it removes 3. | 
 |     IDs = repo.prune(id); | 
 |     EXPECT_EQ(repo.getSizeStats().total, 4096 * 8); | 
 |     ASSERT_EQ(IDs.size(), 3); | 
 |  | 
 |     // Check that it deleted the oldest ones. | 
 |     // The OpenBMC log ID is the PEL ID + 500. | 
 |     EXPECT_EQ(IDs[0], 500 + 1); | 
 |     EXPECT_EQ(IDs[1], 500 + 2); | 
 |     EXPECT_EQ(IDs[2], 500 + 3); | 
 | } | 
 |  | 
 | // Test the sizeWarning function | 
 | TEST_F(RepositoryTest, TestSizeWarning) | 
 | { | 
 |     uint32_t id = 1; | 
 |     Repository repo{repoPath, 100 * 4096, 500}; | 
 |  | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |  | 
 |     // 95% is still OK (disk size for these is 4096) | 
 |     for (uint32_t i = 1; i <= 95; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |  | 
 |     // Now at 96% | 
 |     auto data = pelFactory(id++, 'B', 0x20, 0x8800, 500); | 
 |     auto pel = std::make_unique<PEL>(data); | 
 |     repo.add(pel); | 
 |  | 
 |     EXPECT_TRUE(repo.sizeWarning()); | 
 | } | 
 |  | 
 | // Test sizeWarning when there are too many PEls | 
 | TEST_F(RepositoryTest, TestSizeWarningNumPELs) | 
 | { | 
 |     Repository repo{repoPath, 4096 * 100, 5}; | 
 |  | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |  | 
 |     for (uint32_t i = 1; i <= 5; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |  | 
 |     // Add 1 more for a total of 6, now over the limit | 
 |     { | 
 |         auto data = pelFactory(6, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     EXPECT_TRUE(repo.sizeWarning()); | 
 | } | 
 |  | 
 | // Test existense of archive file | 
 | TEST_F(RepositoryTest, TestArchiveFile) | 
 | { | 
 |     using pelID = Repository::LogID::Pel; | 
 |     using obmcID = Repository::LogID::Obmc; | 
 |  | 
 |     // Add and remove a PEL from the repo | 
 |  | 
 |     Repository repo{repoPath}; | 
 |  | 
 |     fs::path archivePath = repoPath / "logs" / "archive"; | 
 |     EXPECT_TRUE(fs::exists(archivePath)); | 
 |  | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data, 1); | 
 |  | 
 |     pel->assignID(); | 
 |     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}}; | 
 |  | 
 |     repo.add(pel); | 
 |  | 
 |     auto path = repoPath / "logs" / | 
 |                 Repository::getPELFilename(pel->id(), pel->commitTime()); | 
 |     EXPECT_TRUE(fs::exists(path)); | 
 |  | 
 |     auto removedID = repo.remove(id); | 
 |     ASSERT_TRUE(removedID); | 
 |     EXPECT_EQ(*removedID, id); | 
 |  | 
 |     archivePath /= Repository::getPELFilename(pel->id(), pel->commitTime()); | 
 |     EXPECT_TRUE(fs::exists(archivePath)); | 
 |  | 
 |     EXPECT_FALSE(repo.hasPEL(id)); | 
 | } | 
 |  | 
 | // Test archive folder size with sizeWarning function | 
 | TEST_F(RepositoryTest, TestArchiveSize) | 
 | { | 
 |     using pelID = Repository::LogID::Pel; | 
 |     using obmcID = Repository::LogID::Obmc; | 
 |  | 
 |     // Create repo with max PEL=500 and space=4096*100 | 
 |     Repository repo{repoPath, 100 * 4096, 500}; | 
 |  | 
 |     // Fill 94% (disk size for these is 4096) | 
 |     for (uint32_t i = 1; i <= 94; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     // Add another PEL which makes 95% still ok | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data, 1); | 
 |     pel->assignID(); | 
 |     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}}; | 
 |     repo.add(pel); | 
 |  | 
 |     // With 95% full expect no size warning | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |  | 
 |     // Remove last created PEL | 
 |     repo.remove(id); | 
 |  | 
 |     // Repo is 94% full with one PEL in archive log | 
 |     // Total repo size 95% full (including archive) still ok | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |  | 
 |     // Confirm the repo size 94% full | 
 |     const auto& sizes = repo.getSizeStats(); | 
 |     EXPECT_EQ(sizes.total, 4096 * 94); | 
 |  | 
 |     // Make sure archive contain the one deleted file | 
 |     fs::path archivePath = repoPath / "logs" / "archive"; | 
 |     archivePath /= Repository::getPELFilename(pel->id(), pel->commitTime()); | 
 |     EXPECT_TRUE(fs::exists(archivePath)); | 
 |  | 
 |     // Add another PEL which makes repo 95% full | 
 |     data = pelDataFactory(TestPELType::pelSimple); | 
 |     pel = std::make_unique<PEL>(data, 1); | 
 |     pel->assignID(); | 
 |     Repository::LogID idx{pelID{pel->id()}, obmcID{pel->obmcLogID()}}; | 
 |     repo.add(pel); | 
 |  | 
 |     // Repo with 95% full + one archive file becomes 96% | 
 |     // which is greater than the warning | 
 |     // expect archive file to be deleted to get repo size back to 95% | 
 |     EXPECT_FALSE(repo.sizeWarning()); | 
 |     EXPECT_FALSE(fs::exists(archivePath)); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, GetLogIDFoundTC) | 
 | { | 
 |     // Add and Check the created LogId | 
 |  | 
 |     Repository repo{repoPath}; | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data, 1); | 
 |  | 
 |     pel->assignID(); | 
 |  | 
 |     repo.add(pel); | 
 |  | 
 |     // Getting by PEL Id | 
 |     Repository::LogID idWithPelId{Repository::LogID::Pel(pel->id())}; | 
 |     auto logID = repo.getLogID(idWithPelId); | 
 |     ASSERT_TRUE(logID.has_value()); | 
 |     EXPECT_EQ(logID->obmcID.id, pel->obmcLogID()); | 
 |     EXPECT_EQ(logID->pelID.id, pel->id()); | 
 |  | 
 |     // Getting by OBMC Event Log Id | 
 |     Repository::LogID idWithObmcLogId{ | 
 |         Repository::LogID::Obmc(pel->obmcLogID())}; | 
 |     logID = repo.getLogID(idWithObmcLogId); | 
 |     ASSERT_TRUE(logID.has_value()); | 
 |     EXPECT_EQ(logID->obmcID.id, pel->obmcLogID()); | 
 |     EXPECT_EQ(logID->pelID.id, pel->id()); | 
 | } | 
 |  | 
 | TEST_F(RepositoryTest, GetLogIDNotFoundTC) | 
 | { | 
 |     // Add and Check the created LogId | 
 |  | 
 |     Repository repo{repoPath}; | 
 |     auto data = pelDataFactory(TestPELType::pelSimple); | 
 |     auto pel = std::make_unique<PEL>(data, 1); | 
 |  | 
 |     pel->assignID(); | 
 |  | 
 |     repo.add(pel); | 
 |  | 
 |     // Getting by invalid PEL Id | 
 |     Repository::LogID idWithPelId{Repository::LogID::Pel(0xFFFFFFFF)}; | 
 |     auto logID = repo.getLogID(idWithPelId); | 
 |     ASSERT_TRUE(!logID.has_value()); | 
 |  | 
 |     // Getting by invalid OBMC Event Log ID | 
 |     Repository::LogID idWithObmcLogId{Repository::LogID::Obmc(0xFFFFFFFF)}; | 
 |     logID = repo.getLogID(idWithObmcLogId); | 
 |     ASSERT_TRUE(!logID.has_value()); | 
 | } | 
 |  | 
 | // Test that OpenBMC log Id with hardware isolation entry is not removed. | 
 | TEST_F(RepositoryTest, TestPruneWithIdHwIsoEntry) | 
 | { | 
 |     std::vector<uint32_t> id{502}; | 
 |     Repository repo{repoPath, 4096 * 100, 10}; | 
 |  | 
 |     // Add 10, which is the limit and is still OK | 
 |     for (uint32_t i = 1; i <= 10; i++) | 
 |     { | 
 |         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     auto IDs = repo.prune(id); | 
 |  | 
 |     // Nothing pruned yet | 
 |     EXPECT_TRUE(IDs.empty()); | 
 |  | 
 |     // Add 1 more PEL which will be too many. | 
 |     { | 
 |         auto data = pelFactory(11, 'O', 0x20, 0x8800, 500); | 
 |         auto pel = std::make_unique<PEL>(data); | 
 |         repo.add(pel); | 
 |     } | 
 |  | 
 |     // Now that's it's over the limit of 10, it will bring it down | 
 |     // to 80%, which is 8 after it removes 3. | 
 |     IDs = repo.prune(id); | 
 |     EXPECT_EQ(repo.getSizeStats().total, 4096 * 8); | 
 |     ASSERT_EQ(IDs.size(), 3); | 
 |  | 
 |     // Check that it deleted the oldest ones. | 
 |     // And the Id with hw isolation entry is NOT removed. | 
 |     // The OpenBMC log ID is the PEL ID + 500. | 
 |     EXPECT_EQ(IDs[0], 500 + 1); | 
 |     EXPECT_EQ(IDs[1], 500 + 3); | 
 |     EXPECT_EQ(IDs[2], 500 + 4); | 
 | } |