| /** |
| * 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); |
| } |
| |
| 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)); |
| } |
| |
| { |
| // 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); |
| } |