1 // SPDX-License-Identifier: Apache-2.0
2 
3 #include "../functions.hpp"
4 
5 #include <stdlib.h>
6 
7 #include <array>
8 #include <cerrno>
9 #include <filesystem>
10 #include <fstream>
11 #include <map>
12 #include <string>
13 #include <vector>
14 
15 #include <gtest/gtest.h>
16 
17 using namespace std::string_literals;
18 
TEST(GetExtensionsForIbmCompatibleSystem,testSingleMatch)19 TEST(GetExtensionsForIbmCompatibleSystem, testSingleMatch)
20 {
21     std::map<std::string, std::vector<std::string>> extensionMap{{
22         {"system-foo"s, {".EXT"s}},
23     }};
24     std::vector<std::string> compatibleSystem{"system-foo"s}, extensions;
25     auto found =
26         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
27             extensionMap, compatibleSystem, extensions);
28     EXPECT_TRUE(found);
29     EXPECT_EQ(extensions, std::vector<std::string>{".EXT"s});
30 }
31 
TEST(GetExtensionsForIbmCompatibleSystem,testSingleNoMatchNoModify)32 TEST(GetExtensionsForIbmCompatibleSystem, testSingleNoMatchNoModify)
33 {
34     std::map<std::string, std::vector<std::string>> extensionMap{{
35         {"system-bar"s, {".EXT"s}},
36     }};
37     std::vector<std::string> compatibleSystem{"system-foo"s},
38         extensions{"foo"s};
39     auto found =
40         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
41             extensionMap, compatibleSystem, extensions);
42     EXPECT_FALSE(found);
43     EXPECT_EQ(extensions, std::vector<std::string>{"foo"s});
44 }
45 
TEST(GetExtensionsForIbmCompatibleSystem,testMatchModify)46 TEST(GetExtensionsForIbmCompatibleSystem, testMatchModify)
47 {
48     std::map<std::string, std::vector<std::string>> extensionMap{{
49         {"system-bar"s, {".BAR"s}},
50         {"system-foo"s, {".FOO"s}},
51     }};
52     std::vector<std::string> compatibleSystem{"system-foo"s},
53         extensions{"foo"s};
54     auto found =
55         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
56             extensionMap, compatibleSystem, extensions);
57     EXPECT_TRUE(found);
58     EXPECT_EQ(extensions, std::vector<std::string>{".FOO"s});
59 }
60 
TEST(GetExtensionsForIbmCompatibleSystem,testEmpty)61 TEST(GetExtensionsForIbmCompatibleSystem, testEmpty)
62 {
63     std::map<std::string, std::vector<std::string>> extensionMap;
64     std::vector<std::string> compatibleSystem{"system-foo"s},
65         extensions{"foo"s};
66     auto found =
67         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
68             extensionMap, compatibleSystem, extensions);
69     EXPECT_FALSE(found);
70     EXPECT_EQ(extensions, std::vector<std::string>{"foo"s});
71 }
72 
TEST(GetExtensionsForIbmCompatibleSystem,testEmptyEmpty)73 TEST(GetExtensionsForIbmCompatibleSystem, testEmptyEmpty)
74 {
75     std::map<std::string, std::vector<std::string>> extensionMap;
76     std::vector<std::string> compatibleSystem, extensions{"foo"s};
77     auto found =
78         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
79             extensionMap, compatibleSystem, extensions);
80     EXPECT_FALSE(found);
81     EXPECT_EQ(extensions, std::vector<std::string>{"foo"s});
82 }
83 
TEST(GetExtensionsForIbmCompatibleSystem,testMatchMultiCompat)84 TEST(GetExtensionsForIbmCompatibleSystem, testMatchMultiCompat)
85 {
86     std::map<std::string, std::vector<std::string>> extensionMap{{
87         {"system-bar"s, {".BAR"s}},
88         {"system-foo"s, {".FOO"s}},
89     }};
90     std::vector<std::string> compatibleSystem{"system-foo"s, "system"},
91         extensions;
92     auto found =
93         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
94             extensionMap, compatibleSystem, extensions);
95     EXPECT_TRUE(found);
96     EXPECT_EQ(extensions, std::vector<std::string>{".FOO"s});
97 }
98 
TEST(GetExtensionsForIbmCompatibleSystem,testMultiMatchMultiCompat)99 TEST(GetExtensionsForIbmCompatibleSystem, testMultiMatchMultiCompat)
100 {
101     std::map<std::string, std::vector<std::string>> extensionMap{{
102         {"system-bar"s, {".BAR"s}},
103         {"system-foo"s, {".FOO"s}},
104     }};
105     std::vector<std::string> compatibleSystem{"system-foo"s, "system-bar"},
106         extensions;
107     auto found =
108         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
109             extensionMap, compatibleSystem, extensions);
110     EXPECT_TRUE(found);
111     EXPECT_EQ(extensions, std::vector<std::string>{".FOO"s});
112 }
113 
TEST(GetExtensionsForIbmCompatibleSystem,testMultiMatchMultiCompat2)114 TEST(GetExtensionsForIbmCompatibleSystem, testMultiMatchMultiCompat2)
115 {
116     std::map<std::string, std::vector<std::string>> extensionMap{{
117         {"system-foo"s, {".FOO"s}},
118         {"system-bar"s, {".BAR"s}},
119     }};
120     std::vector<std::string> compatibleSystem{"system-foo"s, "system-bar"},
121         extensions;
122     auto found =
123         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
124             extensionMap, compatibleSystem, extensions);
125     EXPECT_TRUE(found);
126     EXPECT_EQ(extensions, std::vector<std::string>{".FOO"s});
127 }
128 
TEST(GetExtensionsForIbmCompatibleSystem,testMultiMatchMultiCompat3)129 TEST(GetExtensionsForIbmCompatibleSystem, testMultiMatchMultiCompat3)
130 {
131     std::map<std::string, std::vector<std::string>> extensionMap{{
132         {"system-bar"s, {".BAR"s}},
133         {"system-foo"s, {".FOO"s}},
134     }};
135     std::vector<std::string> compatibleSystem{"system-bar", "system-foo"s},
136         extensions;
137     auto found =
138         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
139             extensionMap, compatibleSystem, extensions);
140     EXPECT_TRUE(found);
141     EXPECT_EQ(extensions, std::vector<std::string>{".BAR"s});
142 }
143 
TEST(GetExtensionsForIbmCompatibleSystem,testMultiMatchMultiCompat4)144 TEST(GetExtensionsForIbmCompatibleSystem, testMultiMatchMultiCompat4)
145 {
146     std::map<std::string, std::vector<std::string>> extensionMap{{
147         {"system-foo"s, {".FOO"s}},
148         {"system-bar"s, {".BAR"s}},
149     }};
150     std::vector<std::string> compatibleSystem{"system-bar", "system-foo"s},
151         extensions;
152     auto found =
153         functions::process_hostfirmware::getExtensionsForIbmCompatibleSystem(
154             extensionMap, compatibleSystem, extensions);
155     EXPECT_TRUE(found);
156     EXPECT_EQ(extensions, std::vector<std::string>{".BAR"s});
157 }
158 
TEST(MaybeCall,noMatch)159 TEST(MaybeCall, noMatch)
160 {
161     bool called = false;
162     auto callback = [&called](const auto&) { called = true; };
163     std::map<std::string,
164              std::map<std::string, std::variant<std::vector<std::string>>>>
165         interfaces{{
166             {"foo"s, {{"bar"s, std::vector<std::string>{"foo"s}}}},
167         }};
168     auto found = functions::process_hostfirmware::maybeCall(
169         interfaces, std::move(callback));
170     EXPECT_FALSE(found);
171     EXPECT_FALSE(called);
172 }
173 
TEST(MaybeCall,match)174 TEST(MaybeCall, match)
175 {
176     bool called = false;
177     std::vector<std::string> sys;
178     auto callback = [&called, &sys](const auto& system) {
179         sys = system;
180         called = true;
181     };
182     std::map<std::string,
183              std::map<std::string, std::variant<std::vector<std::string>>>>
184         interfaces{{
185             {"xyz.openbmc_project.Inventory.Decorator.Compatible"s,
186              {{"Names"s, std::vector<std::string>{"foo"s}}}},
187         }};
188     auto found = functions::process_hostfirmware::maybeCall(
189         interfaces, std::move(callback));
190     EXPECT_TRUE(found);
191     EXPECT_TRUE(called);
192     EXPECT_EQ(sys, std::vector<std::string>{"foo"s});
193 }
194 
TEST(MaybeCall,missingNames)195 TEST(MaybeCall, missingNames)
196 {
197     bool called = false;
198     auto callback = [&called](const auto&) { called = true; };
199     std::map<std::string,
200              std::map<std::string, std::variant<std::vector<std::string>>>>
201         interfaces{{
202             {"xyz.openbmc_project.Inventory.Decorator.Compatible"s, {}},
203         }};
204     auto found = functions::process_hostfirmware::maybeCall(
205         interfaces, std::move(callback));
206     EXPECT_TRUE(found);
207     EXPECT_FALSE(called);
208 }
209 
TEST(MaybeCall,emptyCallbackFound)210 TEST(MaybeCall, emptyCallbackFound)
211 {
212     std::map<std::string,
213              std::map<std::string, std::variant<std::vector<std::string>>>>
214         interfaces{{
215             {"xyz.openbmc_project.Inventory.Decorator.Compatible"s,
216              {{"Names"s, std::vector<std::string>{"foo"s}}}},
217         }};
218     auto found = functions::process_hostfirmware::maybeCall(
219         interfaces, std::function<void(std::vector<std::string>)>());
220     EXPECT_TRUE(found);
221 }
222 
TEST(MaybeCall,emptyCallbackNotFound)223 TEST(MaybeCall, emptyCallbackNotFound)
224 {
225     std::map<std::string,
226              std::map<std::string, std::variant<std::vector<std::string>>>>
227         interfaces{{
228             {"foo"s, {{"Names"s, std::vector<std::string>{"foo"s}}}},
229         }};
230     auto found = functions::process_hostfirmware::maybeCall(
231         interfaces, std::function<void(std::vector<std::string>)>());
232     EXPECT_FALSE(found);
233 }
234 
TEST(MaybeCall,emptyInterfaces)235 TEST(MaybeCall, emptyInterfaces)
236 {
237     bool called = false;
238     auto callback = [&called](const auto&) { called = true; };
239     std::map<std::string,
240              std::map<std::string, std::variant<std::vector<std::string>>>>
241         interfaces;
242     auto found = functions::process_hostfirmware::maybeCall(
243         interfaces, std::move(callback));
244     EXPECT_FALSE(found);
245     EXPECT_FALSE(called);
246 }
247 
TEST(MaybeCall,emptyInterfacesEmptyCallback)248 TEST(MaybeCall, emptyInterfacesEmptyCallback)
249 {
250     std::map<std::string,
251              std::map<std::string, std::variant<std::vector<std::string>>>>
252         interfaces;
253     auto found = functions::process_hostfirmware::maybeCall(
254         interfaces, std::function<void(std::vector<std::string>)>());
255     EXPECT_FALSE(found);
256 }
257 
TEST(WriteLink,testLinkNoDelete)258 TEST(WriteLink, testLinkNoDelete)
259 {
260     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
261     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
262     bool called = false;
263     auto callback = [&called](const auto&, auto&) { called = true; };
264     std::filesystem::path linkPath = workdir / "link";
265     std::filesystem::path targetPath = workdir / "target";
266     std::ofstream link{linkPath};
267     functions::process_hostfirmware::writeLink(linkPath.filename(), targetPath,
268                                                callback);
269     std::filesystem::remove_all(workdir);
270     EXPECT_FALSE(called);
271 }
272 
TEST(WriteLink,testLinkDelete)273 TEST(WriteLink, testLinkDelete)
274 {
275     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
276     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
277     bool called = false;
278     auto callback = [&called](const auto&, auto&) { called = true; };
279     auto linkPath = workdir / "link";
280     auto targetPath = workdir / "target";
281     std::ofstream link{linkPath}, target{targetPath};
282     functions::process_hostfirmware::writeLink(linkPath.filename(), targetPath,
283                                                callback);
284     std::filesystem::remove_all(workdir);
285     EXPECT_FALSE(called);
286 }
287 
TEST(WriteLink,testLinkFailDeleteDir)288 TEST(WriteLink, testLinkFailDeleteDir)
289 {
290     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
291     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
292     std::error_code ec;
293     std::filesystem::path callbackPath;
294     auto callback = [&ec, &callbackPath](const auto& p, auto& _ec) {
295         ec = _ec;
296         callbackPath = p;
297     };
298     auto targetPath = workdir / "target";
299     std::filesystem::create_directory(targetPath);
300     auto linkPath = workdir / "link";
301     auto filePath = targetPath / "file";
302     std::ofstream link{linkPath}, file{filePath};
303     functions::process_hostfirmware::writeLink(linkPath.filename(), targetPath,
304                                                callback);
305     std::filesystem::remove_all(workdir);
306     EXPECT_EQ(ec.value(), ENOTEMPTY);
307     EXPECT_EQ(callbackPath, targetPath);
308 }
309 
TEST(WriteLink,testLinkPathNotExist)310 TEST(WriteLink, testLinkPathNotExist)
311 {
312     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
313     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
314     std::error_code ec;
315     std::filesystem::path callbackPath;
316     auto callback = [&ec, &callbackPath](const auto& p, auto& _ec) {
317         ec = _ec;
318         callbackPath = p;
319     };
320     auto linkPath = workdir / "baz";
321     auto targetPath = workdir / "foo/bar/foo";
322     functions::process_hostfirmware::writeLink(linkPath.filename(), targetPath,
323                                                callback);
324     std::filesystem::remove_all(workdir);
325     EXPECT_EQ(ec.value(), ENOENT);
326     EXPECT_EQ(callbackPath, targetPath);
327 }
328 
TEST(FindLinks,testNoLinks)329 TEST(FindLinks, testNoLinks)
330 {
331     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
332     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
333 
334     bool callbackCalled = false, errorCallbackCalled = false;
335     auto callback = [&callbackCalled](const auto&, const auto&, const auto&) {
336         callbackCalled = true;
337     };
338     auto errorCallback = [&errorCallbackCalled](const auto&, auto&) {
339         errorCallbackCalled = true;
340     };
341 
342     std::vector<std::string> extensions;
343     functions::process_hostfirmware::findLinks(workdir, extensions,
344                                                errorCallback, callback);
345     std::filesystem::remove_all(workdir);
346     EXPECT_FALSE(errorCallbackCalled);
347     EXPECT_FALSE(callbackCalled);
348 }
349 
TEST(FindLinks,testOneFound)350 TEST(FindLinks, testOneFound)
351 {
352     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
353     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
354     std::filesystem::path callbackPath, callbackLink;
355 
356     bool errorCallbackCalled = false;
357     auto callback = [&callbackPath, &callbackLink](
358                         const auto& p1, const auto& p2, const auto&) {
359         callbackPath = p1;
360         callbackLink = p2;
361     };
362     auto errorCallback = [&errorCallbackCalled](const auto&, auto&) {
363         errorCallbackCalled = true;
364     };
365 
366     auto filePath = workdir / "foo.foo";
367     std::ofstream file{filePath};
368     std::vector<std::string> extensions{".foo"s};
369     functions::process_hostfirmware::findLinks(workdir, extensions,
370                                                errorCallback, callback);
371     std::filesystem::remove_all(workdir);
372     EXPECT_FALSE(errorCallbackCalled);
373     EXPECT_EQ(callbackLink, workdir / "foo");
374     EXPECT_EQ(callbackPath, filePath.filename());
375 }
376 
TEST(FindLinks,testNoExtensions)377 TEST(FindLinks, testNoExtensions)
378 {
379     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
380     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
381     std::filesystem::path callbackPath, callbackLink;
382 
383     bool errorCallbackCalled = false, callbackCalled = false;
384     auto callback = [&callbackCalled](const auto&, const auto&, const auto&) {
385         callbackCalled = true;
386     };
387     auto errorCallback = [&errorCallbackCalled](const auto&, auto&) {
388         errorCallbackCalled = true;
389     };
390 
391     auto filePath = workdir / "foo.foo";
392     std::ofstream file{filePath};
393     std::vector<std::string> extensions;
394     functions::process_hostfirmware::findLinks(workdir, extensions,
395                                                errorCallback, callback);
396     std::filesystem::remove_all(workdir);
397     EXPECT_FALSE(errorCallbackCalled);
398     EXPECT_FALSE(callbackCalled);
399 }
400 
TEST(FindLinks,testEnoent)401 TEST(FindLinks, testEnoent)
402 {
403     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
404     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
405 
406     std::error_code ec;
407     bool called = false;
408     std::filesystem::path callbackPath;
409     auto callback = [&called](const auto&, const auto&, const auto&) {
410         called = true;
411     };
412     auto errorCallback = [&ec, &callbackPath](const auto& p, auto& _ec) {
413         ec = _ec;
414         callbackPath = p;
415     };
416 
417     std::vector<std::string> extensions;
418     auto dir = workdir / "baz";
419     functions::process_hostfirmware::findLinks(dir, extensions, errorCallback,
420                                                callback);
421     std::filesystem::remove_all(workdir);
422     EXPECT_EQ(ec.value(), ENOENT);
423     EXPECT_EQ(callbackPath, dir);
424     EXPECT_FALSE(called);
425 }
426 
TEST(FindLinks,testEmptyCallback)427 TEST(FindLinks, testEmptyCallback)
428 {
429     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
430     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
431 
432     bool called = false;
433     std::filesystem::path callbackPath;
434     auto errorCallback = [&called](const auto&, auto&) { called = true; };
435 
436     auto filePath = workdir / "foo.foo";
437     std::ofstream file{filePath};
438 
439     std::vector<std::string> extensions{".foo"s};
440     functions::process_hostfirmware::findLinks(
441         workdir, extensions, errorCallback,
442         functions::process_hostfirmware::LinkCallbackType());
443     std::filesystem::remove_all(workdir);
444     EXPECT_FALSE(called);
445     EXPECT_NO_THROW();
446 }
447 
TEST(FindLinks,testEmptyErrorCallback)448 TEST(FindLinks, testEmptyErrorCallback)
449 {
450     std::array<char, 15> tmpl{"/tmp/tmpXXXXXX"};
451     std::filesystem::path workdir = mkdtemp(&tmpl[0]);
452 
453     bool called = false;
454     auto callback = [&called](const auto&, const auto&, const auto&) {
455         called = true;
456     };
457 
458     std::vector<std::string> extensions;
459     auto dir = workdir / "baz";
460     functions::process_hostfirmware::findLinks(
461         dir, extensions, functions::process_hostfirmware::ErrorCallbackType(),
462         callback);
463     std::filesystem::remove_all(workdir);
464     EXPECT_FALSE(called);
465     EXPECT_NO_THROW();
466 }
467