1 #include "item_updater.hpp"
2 #include "mocked_utils.hpp"
3 
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 
6 #include <gmock/gmock.h>
7 #include <gtest/gtest.h>
8 
9 using namespace phosphor::software::updater;
10 using ::testing::_;
11 using ::testing::Pointee;
12 using ::testing::Return;
13 using ::testing::ReturnArg;
14 using ::testing::StrEq;
15 
16 using std::experimental::any;
17 
18 class TestItemUpdater : public ::testing::Test
19 {
20   public:
21     using Properties = ItemUpdater::Properties;
22     using PropertyType = utils::UtilsInterface::PropertyType;
23 
24     TestItemUpdater() :
25         mockedUtils(
26             reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
27     {
28         ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>());
29         ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT)))
30             .WillByDefault(Return(any(PropertyType(true))));
31     }
32 
33     ~TestItemUpdater()
34     {
35         utils::freeUtils();
36     }
37 
38     const auto& GetActivations()
39     {
40         return itemUpdater->activations;
41     }
42 
43     std::string getObjPath(const std::string& versionId)
44     {
45         return std::string(dBusPath) + "/" + versionId;
46     }
47 
48     void onPsuInventoryChanged(const std::string& psuPath,
49                                const Properties& properties)
50     {
51         itemUpdater->onPsuInventoryChanged(psuPath, properties);
52     }
53 
54     void scanDirectory(const fs::path& p)
55     {
56         itemUpdater->scanDirectory(p);
57     }
58 
59     static constexpr auto dBusPath = SOFTWARE_OBJPATH;
60     sdbusplus::SdBusMock sdbusMock;
61     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
62     const utils::MockedUtils& mockedUtils;
63     std::unique_ptr<ItemUpdater> itemUpdater;
64 };
65 
66 TEST_F(TestItemUpdater, ctordtor)
67 {
68     EXPECT_CALL(mockedUtils, getLatestVersion(_)).Times(1);
69     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
70 }
71 
72 TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent)
73 {
74     constexpr auto psuPath = "/com/example/inventory/psu0";
75     constexpr auto service = "com.example.Software.Psu";
76     constexpr auto version = "version0";
77     std::string objPath = getObjPath(version);
78     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
79         .WillOnce(Return(std::vector<std::string>({psuPath})));
80     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
81         .WillOnce(Return(service));
82     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
83         .WillOnce(Return(std::string(version)));
84     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
85                                              _, StrEq(PRESENT)))
86         .WillOnce(Return(any(PropertyType(false)))); // not present
87 
88     // The item updater itself
89     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
90         .Times(1);
91 
92     // No activation/version objects are created
93     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
94         .Times(0);
95     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
96 }
97 
98 TEST_F(TestItemUpdater, CreateOnePSUOnPresent)
99 {
100     constexpr auto psuPath = "/com/example/inventory/psu0";
101     constexpr auto service = "com.example.Software.Psu";
102     constexpr auto version = "version0";
103     std::string objPath = getObjPath(version);
104     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
105         .WillOnce(Return(std::vector<std::string>({psuPath})));
106     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
107         .WillOnce(Return(service));
108     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
109         .WillOnce(Return(std::string(version)));
110     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
111                                              _, StrEq(PRESENT)))
112         .WillOnce(Return(any(PropertyType(true)))); // present
113 
114     // The item updater itself
115     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
116         .Times(1);
117 
118     // activation and version object will be added
119     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
120         .Times(2);
121     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
122 }
123 
124 TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion)
125 {
126     constexpr auto psu0 = "/com/example/inventory/psu0";
127     constexpr auto psu1 = "/com/example/inventory/psu1";
128     constexpr auto service = "com.example.Software.Psu";
129     auto version0 = std::string("version0");
130     auto version1 = std::string("version0");
131     auto objPath0 = getObjPath(version0);
132     auto objPath1 = getObjPath(version1);
133 
134     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
135         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
136     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
137         .WillOnce(Return(service));
138     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
139         .WillOnce(Return(service));
140     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
141         .WillOnce(Return(std::string(version0)));
142     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
143                                              StrEq(PRESENT)))
144         .WillOnce(Return(any(PropertyType(true)))); // present
145     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
146         .WillOnce(Return(std::string(version1)));
147     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
148                                              StrEq(PRESENT)))
149         .WillOnce(Return(any(PropertyType(true)))); // present
150 
151     // The item updater itself
152     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
153         .Times(1);
154 
155     // activation and version object will be added
156     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
157         .Times(2);
158     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
159 
160     // Verify there is only one activation and it has two associations
161     const auto& activations = GetActivations();
162     EXPECT_EQ(1u, activations.size());
163     const auto& activation = activations.find(version0)->second;
164     const auto& assocs = activation->associations();
165     EXPECT_EQ(2u, assocs.size());
166     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
167     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
168 }
169 
170 TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion)
171 {
172     constexpr auto psu0 = "/com/example/inventory/psu0";
173     constexpr auto psu1 = "/com/example/inventory/psu1";
174     constexpr auto service = "com.example.Software.Psu";
175     auto version0 = std::string("version0");
176     auto version1 = std::string("version1");
177     auto objPath0 = getObjPath(version0);
178     auto objPath1 = getObjPath(version1);
179 
180     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
181         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
182     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
183         .WillOnce(Return(service));
184     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
185         .WillOnce(Return(service));
186     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
187         .WillOnce(Return(std::string(version0)));
188     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
189                                              StrEq(PRESENT)))
190         .WillOnce(Return(any(PropertyType(true)))); // present
191     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
192         .WillOnce(Return(std::string(version1)));
193     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
194                                              StrEq(PRESENT)))
195         .WillOnce(Return(any(PropertyType(true)))); // present
196 
197     // The item updater itself
198     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
199         .Times(1);
200 
201     // two new activation and version objects will be added
202     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
203         .Times(2);
204     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
205         .Times(2);
206     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
207 
208     // Verify there are two activations and each with one association
209     const auto& activations = GetActivations();
210     EXPECT_EQ(2u, activations.size());
211     const auto& activation0 = activations.find(version0)->second;
212     const auto& assocs0 = activation0->associations();
213     EXPECT_EQ(1u, assocs0.size());
214     EXPECT_EQ(psu0, std::get<2>(assocs0[0]));
215 
216     const auto& activation1 = activations.find(version1)->second;
217     const auto& assocs1 = activation1->associations();
218     EXPECT_EQ(1u, assocs1.size());
219     EXPECT_EQ(psu1, std::get<2>(assocs1[0]));
220 }
221 
222 TEST_F(TestItemUpdater, OnOnePSURemoved)
223 {
224     constexpr auto psuPath = "/com/example/inventory/psu0";
225     constexpr auto service = "com.example.Software.Psu";
226     constexpr auto version = "version0";
227     std::string objPath = getObjPath(version);
228     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
229         .WillOnce(Return(std::vector<std::string>({psuPath})));
230     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
231         .WillOnce(Return(service));
232     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
233         .WillOnce(Return(std::string(version)));
234     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
235                                              _, StrEq(PRESENT)))
236         .WillOnce(Return(any(PropertyType(true)))); // present
237 
238     // The item updater itself
239     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
240         .Times(1);
241 
242     // activation and version object will be added
243     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
244         .Times(2);
245     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
246 
247     // the activation and version object will be removed
248     Properties p{{PRESENT, PropertyType(false)}};
249     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
250         .Times(2);
251     onPsuInventoryChanged(psuPath, p);
252 
253     // on exit, item updater is removed
254     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
255         .Times(1);
256 }
257 
258 TEST_F(TestItemUpdater, OnOnePSUAdded)
259 {
260     constexpr auto psuPath = "/com/example/inventory/psu0";
261     constexpr auto service = "com.example.Software.Psu";
262     constexpr auto version = "version0";
263     std::string objPath = getObjPath(version);
264     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
265         .WillOnce(Return(std::vector<std::string>({psuPath})));
266     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
267         .WillOnce(Return(service));
268     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
269         .WillOnce(Return(std::string(version)));
270     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
271                                              _, StrEq(PRESENT)))
272         .WillOnce(Return(any(PropertyType(false)))); // not present
273 
274     // The item updater itself
275     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
276         .Times(1);
277 
278     // No activation/version objects are created
279     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
280         .Times(0);
281     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
282 
283     // The PSU is present and version is added in a single call
284     Properties propAdded{{PRESENT, PropertyType(true)}};
285     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
286         .WillOnce(Return(std::string(version)));
287     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
288         .Times(2);
289     onPsuInventoryChanged(psuPath, propAdded);
290 }
291 
292 TEST_F(TestItemUpdater, OnOnePSURemovedAndAdded)
293 {
294     constexpr auto psuPath = "/com/example/inventory/psu0";
295     constexpr auto service = "com.example.Software.Psu";
296     constexpr auto version = "version0";
297     std::string objPath = getObjPath(version);
298     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
299         .WillOnce(Return(std::vector<std::string>({psuPath})));
300     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
301         .WillOnce(Return(service));
302     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
303         .WillOnce(Return(std::string(version)));
304     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
305                                              _, StrEq(PRESENT)))
306         .WillOnce(Return(any(PropertyType(true)))); // present
307 
308     // The item updater itself
309     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
310         .Times(1);
311 
312     // activation and version object will be added
313     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
314         .Times(2);
315     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
316 
317     // the activation and version object will be removed
318     Properties propRemoved{{PRESENT, PropertyType(false)}};
319     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
320         .Times(2);
321     onPsuInventoryChanged(psuPath, propRemoved);
322 
323     Properties propAdded{{PRESENT, PropertyType(true)}};
324     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
325         .WillOnce(Return(std::string(version)));
326     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
327         .Times(2);
328     onPsuInventoryChanged(psuPath, propAdded);
329 
330     // on exit, objects are removed
331     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
332         .Times(2);
333     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
334         .Times(1);
335 }
336 
337 TEST_F(TestItemUpdater,
338        TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion)
339 {
340     constexpr auto psu0 = "/com/example/inventory/psu0";
341     constexpr auto psu1 = "/com/example/inventory/psu1";
342     constexpr auto service = "com.example.Software.Psu";
343     auto version0 = std::string("version0");
344     auto version1 = std::string("version0");
345     auto objPath0 = getObjPath(version0);
346     auto objPath1 = getObjPath(version1);
347 
348     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
349         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
350     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
351         .WillOnce(Return(service));
352     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
353         .WillOnce(Return(service));
354     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
355         .WillOnce(Return(std::string(version0)));
356     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
357                                              StrEq(PRESENT)))
358         .WillOnce(Return(any(PropertyType(true)))); // present
359     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
360         .WillOnce(Return(std::string(version1)));
361     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
362                                              StrEq(PRESENT)))
363         .WillOnce(Return(any(PropertyType(true)))); // present
364 
365     // The item updater itself
366     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
367         .Times(1);
368 
369     // activation and version object will be added
370     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
371         .Times(2);
372     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
373 
374     // Verify there is only one activation and it has two associations
375     const auto& activations = GetActivations();
376     EXPECT_EQ(1u, activations.size());
377     const auto& activation = activations.find(version0)->second;
378     auto assocs = activation->associations();
379     EXPECT_EQ(2u, assocs.size());
380     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
381     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
382 
383     // PSU0 is removed, only associations shall be updated
384     Properties propRemoved{{PRESENT, PropertyType(false)}};
385     onPsuInventoryChanged(psu0, propRemoved);
386     assocs = activation->associations();
387     EXPECT_EQ(1u, assocs.size());
388     EXPECT_EQ(psu1, std::get<2>(assocs[0]));
389 
390     // PSU1 is removed, the activation and version object shall be removed
391     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
392         .Times(2);
393     onPsuInventoryChanged(psu1, propRemoved);
394 
395     // Add PSU0 and PSU1 back, but PSU1 with a different version
396     version1 = "version1";
397     objPath1 = getObjPath(version1);
398     Properties propAdded0{{PRESENT, PropertyType(true)}};
399     Properties propAdded1{{PRESENT, PropertyType(true)}};
400     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
401         .WillOnce(Return(std::string(version0)));
402     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
403         .WillOnce(Return(std::string(version1)));
404     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
405         .Times(2);
406     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
407         .Times(2);
408     onPsuInventoryChanged(psu0, propAdded0);
409     onPsuInventoryChanged(psu1, propAdded1);
410 
411     // on exit, objects are removed
412     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
413         .Times(2);
414     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1)))
415         .Times(2);
416     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
417         .Times(1);
418 }
419 
420 TEST_F(TestItemUpdater, scanDirOnNoPSU)
421 {
422     constexpr auto psuPath = "/com/example/inventory/psu0";
423     constexpr auto service = "com.example.Software.Psu";
424     constexpr auto version = "version0";
425     std::string objPath = getObjPath(version);
426     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
427         .WillOnce(Return(std::vector<std::string>({psuPath})));
428     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
429         .WillOnce(Return(service));
430     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
431         .WillOnce(Return(std::string(version)));
432     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
433                                              _, StrEq(PRESENT)))
434         .WillOnce(Return(any(PropertyType(false)))); // not present
435 
436     // The item updater itself
437     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
438         .Times(1);
439 
440     // No activation/version objects are created
441     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
442         .Times(0);
443     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
444 
445     // The valid image in test/psu-images-one-valid-one-invalid/model-1/
446     auto objPathValid = getObjPath("psu-test.v0.4");
447     auto objPathInvalid = getObjPath("psu-test.v0.5");
448     // activation and version object will be added on scan dir
449     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathValid)))
450         .Times(2);
451     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathInvalid)))
452         .Times(0);
453     scanDirectory("./psu-images-one-valid-one-invalid");
454 }
455 
456 TEST_F(TestItemUpdater, scanDirOnSamePSUVersion)
457 {
458     constexpr auto psuPath = "/com/example/inventory/psu0";
459     constexpr auto service = "com.example.Software.Psu";
460     constexpr auto version = "version0";
461     std::string objPath = getObjPath(version);
462     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
463         .WillOnce(Return(std::vector<std::string>({psuPath})));
464     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
465         .WillOnce(Return(service));
466     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
467         .WillOnce(Return(std::string(version)));
468     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
469                                              _, StrEq(PRESENT)))
470         .WillOnce(Return(any(PropertyType(true)))); // present
471 
472     // The item updater itself
473     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
474         .Times(1);
475 
476     // activation and version object will be added
477     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
478         .Times(2);
479     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
480 
481     // The valid image in test/psu-images-valid-version0/model-3/ has the same
482     // version as the running PSU, so no objects will be created, but only the
483     // path will be set to the version object
484     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
485         .Times(0);
486     EXPECT_CALL(sdbusMock, sd_bus_emit_properties_changed_strv(
487                                _, StrEq(objPath),
488                                StrEq("xyz.openbmc_project.Common.FilePath"),
489                                Pointee(StrEq("Path"))))
490         .Times(1);
491     scanDirectory("./psu-images-valid-version0");
492 }
493