1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "extensions/openpower-pels/paths.hpp"
17 #include "extensions/openpower-pels/repository.hpp"
18 #include "pel_utils.hpp"
19 
20 #include <ext/stdio_filebuf.h>
21 
22 #include <filesystem>
23 
24 #include <gtest/gtest.h>
25 
26 using namespace openpower::pels;
27 namespace fs = std::filesystem;
28 
29 /**
30  * Clean the Repo after every testcase.
31  * And because we have PEL object, also clean up
32  * the log ID.
33  */
34 class RepositoryTest : public CleanLogID
35 {
36   protected:
37     void SetUp() override
38     {
39         repoPath = getPELRepoPath();
40     }
41 
42     void TearDown() override
43     {
44         fs::remove_all(repoPath);
45     }
46 
47     fs::path repoPath;
48 };
49 
50 TEST_F(RepositoryTest, FilenameTest)
51 {
52     BCDTime date = {0x20, 0x30, 0x11, 0x28, 0x13, 0x6, 0x7, 0x8};
53 
54     EXPECT_EQ(Repository::getPELFilename(0x12345678, date),
55               "2030112813060708_12345678");
56 
57     EXPECT_EQ(Repository::getPELFilename(0xAABBCCDD, date),
58               "2030112813060708_AABBCCDD");
59 
60     EXPECT_EQ(Repository::getPELFilename(0x3AFF1, date),
61               "2030112813060708_0003AFF1");
62 
63     EXPECT_EQ(Repository::getPELFilename(100, date),
64               "2030112813060708_00000064");
65 
66     EXPECT_EQ(Repository::getPELFilename(0, date), "2030112813060708_00000000");
67 }
68 
69 TEST_F(RepositoryTest, AddTest)
70 {
71     Repository repo{repoPath};
72     auto data = pelDataFactory(TestPELType::pelSimple);
73     auto pel = std::make_unique<PEL>(data);
74 
75     repo.add(pel);
76 
77     // Check that the PEL was stored where it was supposed to be,
78     // and that it wrote the PEL data.
79     const auto ts = pel->privateHeader().commitTimestamp();
80     auto name = Repository::getPELFilename(pel->id(), ts);
81 
82     fs::path file = repoPath / "logs" / name;
83     EXPECT_TRUE(fs::exists(file));
84 
85     auto newData = readPELFile(file);
86     auto pelData = pel->data();
87     EXPECT_EQ(*newData, pelData);
88 }
89 
90 TEST_F(RepositoryTest, RestoreTest)
91 {
92     using pelID = Repository::LogID::Pel;
93     using obmcID = Repository::LogID::Obmc;
94 
95     std::vector<Repository::LogID> ids;
96 
97     {
98         Repository repo{repoPath};
99 
100         // Add some PELs to the repository
101         {
102             auto data = pelDataFactory(TestPELType::pelSimple);
103             auto pel = std::make_unique<PEL>(data, 1);
104             pel->assignID();
105             repo.add(pel);
106             ids.emplace_back(pelID(pel->id()), obmcID(1));
107         }
108         {
109             auto data = pelDataFactory(TestPELType::pelSimple);
110             auto pel = std::make_unique<PEL>(data, 2);
111             pel->assignID();
112             repo.add(pel);
113             ids.emplace_back(pelID(pel->id()), obmcID(2));
114         }
115 
116         // Check they're there
117         EXPECT_TRUE(repo.hasPEL(ids[0]));
118         EXPECT_TRUE(repo.hasPEL(ids[1]));
119 
120         // Do some other search tests while we're here.
121 
122         // Search based on PEL ID
123         Repository::LogID id(pelID(ids[0].pelID));
124         EXPECT_TRUE(repo.hasPEL(id));
125 
126         // Search based on OBMC log ID
127         id.pelID.id = 0;
128         id.obmcID = ids[0].obmcID;
129         EXPECT_TRUE(repo.hasPEL(id));
130 
131         // ... based on the other PEL ID
132         id.pelID = ids[1].pelID;
133         id.obmcID.id = 0;
134         EXPECT_TRUE(repo.hasPEL(id));
135 
136         // Not found
137         id.pelID.id = 99;
138         id.obmcID.id = 100;
139         EXPECT_FALSE(repo.hasPEL(id));
140     }
141 
142     {
143         // Restore and check they're still there, then
144         // remove them.
145         Repository repo{repoPath};
146         EXPECT_TRUE(repo.hasPEL(ids[0]));
147         EXPECT_TRUE(repo.hasPEL(ids[1]));
148 
149         repo.remove(ids[0]);
150         EXPECT_FALSE(repo.hasPEL(ids[0]));
151 
152         repo.remove(ids[1]);
153         EXPECT_FALSE(repo.hasPEL(ids[1]));
154     }
155 }
156 
157 TEST_F(RepositoryTest, TestGetPELData)
158 {
159     using ID = Repository::LogID;
160     Repository repo{repoPath};
161 
162     ID badID{ID::Pel(42)};
163     auto noData = repo.getPELData(badID);
164     EXPECT_FALSE(noData);
165 
166     // Add a PEL to the repo, and get the data back with getPELData.
167     auto data = pelDataFactory(TestPELType::pelSimple);
168     auto dataCopy = data;
169     auto pel = std::make_unique<PEL>(data);
170     auto pelID = pel->id();
171     repo.add(pel);
172 
173     ID id{ID::Pel(pelID)};
174     auto pelData = repo.getPELData(id);
175 
176     ASSERT_TRUE(pelData);
177     EXPECT_EQ(dataCopy, *pelData);
178 }
179 
180 TEST_F(RepositoryTest, TestForEach)
181 {
182     Repository repo{repoPath};
183 
184     // Add 2 PELs
185     auto data = pelDataFactory(TestPELType::pelSimple);
186     auto pel = std::make_unique<PEL>(data);
187     repo.add(pel);
188 
189     pel = std::make_unique<PEL>(data);
190     pel->assignID();
191     pel->setCommitTime();
192     repo.add(pel);
193 
194     // Make a function that saves the IDs
195     std::vector<uint32_t> ids;
196     Repository::ForEachFunc f1 = [&ids](const PEL& pel) {
197         ids.push_back(pel.id());
198         return false;
199     };
200 
201     repo.for_each(f1);
202 
203     EXPECT_EQ(ids.size(), 2);
204 
205     // Stop after the first time in.
206     Repository::ForEachFunc f2 = [&ids](const PEL& pel) {
207         ids.push_back(pel.id());
208         return true;
209     };
210 
211     ids.clear();
212     repo.for_each(f2);
213     EXPECT_EQ(ids.size(), 1);
214 }
215 
216 TEST_F(RepositoryTest, TestSubscriptions)
217 {
218     std::vector<uint32_t> added;
219     std::vector<uint32_t> removed;
220 
221     Repository::AddCallback ac = [&added](const PEL& pel) {
222         added.push_back(pel.id());
223     };
224 
225     Repository::DeleteCallback dc = [&removed](uint32_t id) {
226         removed.push_back(id);
227     };
228 
229     Repository repo{repoPath};
230     repo.subscribeToAdds("test", ac);
231     repo.subscribeToDeletes("test", dc);
232 
233     auto data = pelDataFactory(TestPELType::pelSimple);
234     auto pel = std::make_unique<PEL>(data);
235     auto pelID = pel->id();
236     repo.add(pel);
237 
238     EXPECT_EQ(added.size(), 1);
239 
240     using ID = Repository::LogID;
241     ID id{ID::Pel(pelID)};
242     repo.remove(id);
243 
244     EXPECT_EQ(removed.size(), 1);
245 
246     repo.unsubscribeFromAdds("test");
247     repo.unsubscribeFromDeletes("test");
248 
249     added.clear();
250     removed.clear();
251 
252     repo.add(pel);
253     EXPECT_EQ(added.size(), 0);
254 
255     repo.remove(id);
256     EXPECT_EQ(removed.size(), 0);
257 }
258 
259 TEST_F(RepositoryTest, TestGetAttributes)
260 {
261     uint32_t pelID = 0;
262     std::bitset<16> actionFlags;
263 
264     {
265         Repository repo{repoPath};
266 
267         // Add a PEL to the repo
268         auto data = pelDataFactory(TestPELType::pelSimple);
269         auto pel = std::make_unique<PEL>(data);
270         repo.add(pel);
271 
272         pelID = pel->id();
273         actionFlags = pel->userHeader().actionFlags();
274 
275         using ID = Repository::LogID;
276         ID id{ID::Pel(pelID)};
277 
278         auto a = repo.getPELAttributes(id);
279         EXPECT_TRUE(a);
280         EXPECT_EQ((*a).get().actionFlags, actionFlags);
281 
282         id.pelID.id = 0;
283         a = repo.getPELAttributes(id);
284         EXPECT_FALSE(a);
285     }
286 
287     {
288         // Restore the repository and check again
289         Repository repo{repoPath};
290 
291         using ID = Repository::LogID;
292         ID id{ID::Pel(pelID)};
293 
294         auto a = repo.getPELAttributes(id);
295         EXPECT_TRUE(a);
296         EXPECT_EQ((*a).get().actionFlags, actionFlags);
297 
298         id.pelID.id = 0;
299         a = repo.getPELAttributes(id);
300         EXPECT_FALSE(a);
301     }
302 }
303 
304 TEST_F(RepositoryTest, TestSetHostState)
305 {
306     // Add a PEL to the repo
307     auto data = pelDataFactory(TestPELType::pelSimple);
308     auto pel = std::make_unique<PEL>(data);
309     using ID = Repository::LogID;
310     ID id{ID::Pel(pel->id())};
311 
312     {
313         Repository repo{repoPath};
314 
315         repo.add(pel);
316 
317         auto a = repo.getPELAttributes(id);
318         EXPECT_EQ((*a).get().hostState, TransmissionState::newPEL);
319 
320         repo.setPELHostTransState(pel->id(), TransmissionState::acked);
321 
322         // First, check the attributes
323         a = repo.getPELAttributes(id);
324         EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
325 
326         // Next, check the PEL data itself
327         auto pelData = repo.getPELData(id);
328         PEL newPEL{*pelData};
329         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
330     }
331 
332     {
333         // Now restore, and check again
334         Repository repo{repoPath};
335 
336         // First, check the attributes
337         auto a = repo.getPELAttributes(id);
338         EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
339 
340         // Next, check the PEL data itself
341         auto pelData = repo.getPELData(id);
342         PEL newPEL{*pelData};
343         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
344     }
345 }
346 
347 TEST_F(RepositoryTest, TestSetHMCState)
348 {
349     // Add a PEL to the repo
350     auto data = pelDataFactory(TestPELType::pelSimple);
351     auto pel = std::make_unique<PEL>(data);
352     using ID = Repository::LogID;
353     ID id{ID::Pel(pel->id())};
354 
355     {
356         Repository repo{repoPath};
357 
358         repo.add(pel);
359 
360         auto a = repo.getPELAttributes(id);
361         EXPECT_EQ((*a).get().hmcState, TransmissionState::newPEL);
362 
363         repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
364 
365         // First, check the attributes
366         a = repo.getPELAttributes(id);
367         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
368 
369         // Next, check the PEL data itself
370         auto pelData = repo.getPELData(id);
371         PEL newPEL{*pelData};
372         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
373     }
374 
375     {
376         // Now restore, and check again
377         Repository repo{repoPath};
378 
379         // First, check the attributes
380         auto a = repo.getPELAttributes(id);
381         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
382 
383         // Next, check the PEL data itself
384         auto pelData = repo.getPELData(id);
385         PEL newPEL{*pelData};
386         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
387     }
388 }
389 
390 TEST_F(RepositoryTest, TestGetPELFD)
391 {
392     Repository repo{repoPath};
393 
394     auto data = pelDataFactory(TestPELType::pelSimple);
395     auto pel = std::make_unique<PEL>(data);
396     pel->setCommitTime();
397     pel->assignID();
398 
399     repo.add(pel);
400 
401     using ID = Repository::LogID;
402     ID id{ID::Pel(pel->id())};
403 
404     auto fd = repo.getPELFD(id);
405 
406     EXPECT_TRUE(fd);
407 
408     // Get the size
409     struct stat s;
410     int r = fstat(*fd, &s);
411     ASSERT_EQ(r, 0);
412 
413     auto size = s.st_size;
414 
415     // Read the PEL data out of the FD
416     FILE* fp = fdopen(*fd, "r");
417     ASSERT_NE(fp, nullptr);
418 
419     std::vector<uint8_t> newData;
420     newData.resize(size);
421     r = fread(newData.data(), 1, size, fp);
422     EXPECT_EQ(r, size);
423 
424     PEL newPEL{newData};
425 
426     EXPECT_TRUE(newPEL.valid());
427     EXPECT_EQ(newPEL.id(), pel->id());
428 
429     fclose(fp);
430 
431     // Call getPELFD again, this time with a bad ID
432     id.pelID.id = 42;
433     fd = repo.getPELFD(id);
434 
435     EXPECT_FALSE(fd);
436 }
437