blob: bbeb459c860452e2d6897f546ceabf185184b5d1 [file] [log] [blame]
Shawn McCarney472101c2024-04-17 16:31:09 -05001/**
2 * Copyright © 2024 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
17#include "mock_services.hpp"
18#include "rail.hpp"
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050019#include "services.hpp"
Shawn McCarney472101c2024-04-17 16:31:09 -050020#include "standard_device.hpp"
21
22#include <cstdint>
23#include <map>
24#include <memory>
25#include <optional>
26#include <string>
27#include <utility>
28#include <vector>
29
30#include <gmock/gmock.h>
31#include <gtest/gtest.h>
32
33using namespace phosphor::power::sequencer;
34
35using ::testing::Return;
36using ::testing::Throw;
37
38/**
39 * @class StandardDeviceImpl
40 *
41 * Concrete subclass of the StandardDevice abstract class.
42 *
43 * This subclass is required for two reasons:
44 * - StandardDevice has some pure virtual methods so it cannot be instantiated.
45 * - The pure virtual methods provide the PMBus and GPIO information. Mocking
46 * these makes it possible to test the pgood fault detection algorithm.
47 *
48 * This class is not intended to be used outside of this file. It is
49 * implementation detail for testing the the StandardDevice class.
50 */
51class StandardDeviceImpl : public StandardDevice
52{
53 public:
54 // Specify which compiler-generated methods we want
55 StandardDeviceImpl() = delete;
56 StandardDeviceImpl(const StandardDeviceImpl&) = delete;
57 StandardDeviceImpl(StandardDeviceImpl&&) = delete;
58 StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete;
59 StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete;
60 virtual ~StandardDeviceImpl() = default;
61
62 // Constructor just calls StandardDevice constructor
Shawn McCarney31234452025-10-28 12:32:05 -050063 explicit StandardDeviceImpl(const std::string& name, uint8_t bus,
64 uint16_t address,
Shawn McCarneyfe536672025-10-29 12:44:08 -050065 const std::string& powerControlGPIOName,
66 const std::string& powerGoodGPIOName,
Shawn McCarney472101c2024-04-17 16:31:09 -050067 std::vector<std::unique_ptr<Rail>> rails) :
Shawn McCarneyfe536672025-10-29 12:44:08 -050068 StandardDevice(name, bus, address, powerControlGPIOName,
69 powerGoodGPIOName, std::move(rails))
Shawn McCarney472101c2024-04-17 16:31:09 -050070 {}
71
72 // Mock pure virtual methods
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050073 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
74 (override));
Shawn McCarney472101c2024-04-17 16:31:09 -050075 MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
76 MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
77 MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
78 MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
Shawn McCarney16275832024-06-27 10:14:11 -050079
80 // Override empty implementation with mock so we can verify it is called
81 MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
82 (override));
Shawn McCarney472101c2024-04-17 16:31:09 -050083};
84
85/**
86 * Creates a Rail object that checks for a pgood fault using STATUS_VOUT.
87 *
88 * @param name Unique name for the rail
89 * @param isPowerSupplyRail Specifies whether the rail is produced by a
90 power supply
91 * @param pageNum PMBus PAGE number of the rail
92 * @return Rail object
93 */
Patrick Williamsf5402192024-08-16 15:20:53 -040094std::unique_ptr<Rail> createRailStatusVout(
95 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
Shawn McCarney472101c2024-04-17 16:31:09 -050096{
97 std::optional<std::string> presence{};
98 std::optional<uint8_t> page{pageNum};
99 bool checkStatusVout{true};
100 bool compareVoltageToLimit{false};
101 std::optional<GPIO> gpio{};
102 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
103 checkStatusVout, compareVoltageToLimit, gpio);
104}
105
106/**
107 * Creates a Rail object that checks for a pgood fault using a GPIO.
108 *
109 * @param name Unique name for the rail
110 * @param isPowerSupplyRail Specifies whether the rail is produced by a
111 power supply
112 * @param gpio GPIO line to read to determine the pgood status of the rail
113 * @return Rail object
114 */
Patrick Williamsf5402192024-08-16 15:20:53 -0400115std::unique_ptr<Rail> createRailGPIO(
116 const std::string& name, bool isPowerSupplyRail, unsigned int gpioLine)
Shawn McCarney472101c2024-04-17 16:31:09 -0500117{
118 std::optional<std::string> presence{};
119 std::optional<uint8_t> page{};
120 bool checkStatusVout{false};
121 bool compareVoltageToLimit{false};
122 bool activeLow{false};
123 std::optional<GPIO> gpio{GPIO{gpioLine, activeLow}};
124 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
125 checkStatusVout, compareVoltageToLimit, gpio);
126}
127
Shawn McCarney16275832024-06-27 10:14:11 -0500128/**
129 * Creates a Rail object that checks for a pgood fault using output voltage.
130 *
131 * @param name Unique name for the rail
132 * @param isPowerSupplyRail Specifies whether the rail is produced by a
133 power supply
134 * @param pageNum PMBus PAGE number of the rail
135 * @return Rail object
136 */
Patrick Williamsf5402192024-08-16 15:20:53 -0400137std::unique_ptr<Rail> createRailOutputVoltage(
138 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
Shawn McCarney16275832024-06-27 10:14:11 -0500139{
140 std::optional<std::string> presence{};
141 std::optional<uint8_t> page{pageNum};
142 bool checkStatusVout{false};
143 bool compareVoltageToLimit{true};
144 std::optional<GPIO> gpio{};
145 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
146 checkStatusVout, compareVoltageToLimit, gpio);
147}
148
Shawn McCarney472101c2024-04-17 16:31:09 -0500149TEST(StandardDeviceTests, Constructor)
150{
151 // Empty vector of rails
152 {
Shawn McCarney31234452025-10-28 12:32:05 -0500153 std::string name{"xyz_pseq"};
154 uint8_t bus{3};
155 uint16_t address{0x72};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500156 std::string powerControlGPIOName{"power-chassis-control"};
157 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500158 std::vector<std::unique_ptr<Rail>> rails{};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500159 StandardDeviceImpl device{
160 name,
161 bus,
162 address,
163 powerControlGPIOName,
164 powerGoodGPIOName,
165 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500166
Shawn McCarney31234452025-10-28 12:32:05 -0500167 EXPECT_EQ(device.getName(), name);
168 EXPECT_EQ(device.getBus(), bus);
169 EXPECT_EQ(device.getAddress(), address);
Shawn McCarneyfe536672025-10-29 12:44:08 -0500170 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
171 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
Shawn McCarney472101c2024-04-17 16:31:09 -0500172 EXPECT_TRUE(device.getRails().empty());
173 }
174
175 // Non-empty vector of rails
176 {
Shawn McCarney31234452025-10-28 12:32:05 -0500177 std::string name{"abc_pseq"};
178 uint8_t bus{0};
179 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500180 std::string powerControlGPIOName{"power-chassis-control"};
181 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500182 std::vector<std::unique_ptr<Rail>> rails{};
183 rails.emplace_back(createRailGPIO("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500184 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500185 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500186 StandardDeviceImpl device{
187 name,
188 bus,
189 address,
190 powerControlGPIOName,
191 powerGoodGPIOName,
192 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500193
Shawn McCarney31234452025-10-28 12:32:05 -0500194 EXPECT_EQ(device.getName(), name);
195 EXPECT_EQ(device.getBus(), bus);
196 EXPECT_EQ(device.getAddress(), address);
Shawn McCarneyfe536672025-10-29 12:44:08 -0500197 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
198 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
Shawn McCarney472101c2024-04-17 16:31:09 -0500199 EXPECT_EQ(device.getRails().size(), 3);
200 EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
201 EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
202 EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
203 }
204}
205
206TEST(StandardDeviceTests, GetName)
207{
Shawn McCarney31234452025-10-28 12:32:05 -0500208 std::string name{"xyz_pseq"};
209 uint8_t bus{0};
210 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500211 std::string powerControlGPIOName{"power-chassis-control"};
212 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500213 std::vector<std::unique_ptr<Rail>> rails{};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500214 StandardDeviceImpl device{
215 name,
216 bus,
217 address,
218 powerControlGPIOName,
219 powerGoodGPIOName,
220 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500221
Shawn McCarney31234452025-10-28 12:32:05 -0500222 EXPECT_EQ(device.getName(), name);
223}
224
225TEST(StandardDeviceTests, GetBus)
226{
227 std::string name{"abc_pseq"};
228 uint8_t bus{1};
229 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500230 std::string powerControlGPIOName{"power-chassis-control"};
231 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney31234452025-10-28 12:32:05 -0500232 std::vector<std::unique_ptr<Rail>> rails{};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500233 StandardDeviceImpl device{
234 name,
235 bus,
236 address,
237 powerControlGPIOName,
238 powerGoodGPIOName,
239 std::move(rails)};
Shawn McCarney31234452025-10-28 12:32:05 -0500240
241 EXPECT_EQ(device.getBus(), bus);
242}
243
244TEST(StandardDeviceTests, GetAddress)
245{
246 std::string name{"abc_pseq"};
247 uint8_t bus{1};
248 uint16_t address{0x24};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500249 std::string powerControlGPIOName{"power-chassis-control"};
250 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney31234452025-10-28 12:32:05 -0500251 std::vector<std::unique_ptr<Rail>> rails{};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500252 StandardDeviceImpl device{
253 name,
254 bus,
255 address,
256 powerControlGPIOName,
257 powerGoodGPIOName,
258 std::move(rails)};
Shawn McCarney31234452025-10-28 12:32:05 -0500259
260 EXPECT_EQ(device.getAddress(), address);
Shawn McCarney472101c2024-04-17 16:31:09 -0500261}
262
Shawn McCarneyfe536672025-10-29 12:44:08 -0500263TEST(StandardDeviceTests, GetPowerControlGPIOName)
264{
265 std::string name{"xyz_pseq"};
266 uint8_t bus{0};
267 uint16_t address{0x23};
268 std::string powerControlGPIOName{"power-on"};
269 std::string powerGoodGPIOName{"chassis-pgood"};
270 std::vector<std::unique_ptr<Rail>> rails{};
271 StandardDeviceImpl device{
272 name,
273 bus,
274 address,
275 powerControlGPIOName,
276 powerGoodGPIOName,
277 std::move(rails)};
278
279 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
280}
281
282TEST(StandardDeviceTests, GetPowerGoodGPIOName)
283{
284 std::string name{"xyz_pseq"};
285 uint8_t bus{0};
286 uint16_t address{0x23};
287 std::string powerControlGPIOName{"power-on"};
288 std::string powerGoodGPIOName{"chassis-pgood"};
289 std::vector<std::unique_ptr<Rail>> rails{};
290 StandardDeviceImpl device{
291 name,
292 bus,
293 address,
294 powerControlGPIOName,
295 powerGoodGPIOName,
296 std::move(rails)};
297
298 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
299}
300
Shawn McCarney472101c2024-04-17 16:31:09 -0500301TEST(StandardDeviceTests, GetRails)
302{
303 // Empty vector of rails
304 {
Shawn McCarney31234452025-10-28 12:32:05 -0500305 std::string name{"xyz_pseq"};
306 uint8_t bus{0};
307 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500308 std::string powerControlGPIOName{"power-chassis-control"};
309 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500310 std::vector<std::unique_ptr<Rail>> rails{};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500311 StandardDeviceImpl device{
312 name,
313 bus,
314 address,
315 powerControlGPIOName,
316 powerGoodGPIOName,
317 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500318
319 EXPECT_TRUE(device.getRails().empty());
320 }
321
322 // Non-empty vector of rails
323 {
Shawn McCarney31234452025-10-28 12:32:05 -0500324 std::string name{"abc_pseq"};
325 uint8_t bus{0};
326 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500327 std::string powerControlGPIOName{"power-chassis-control"};
328 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500329 std::vector<std::unique_ptr<Rail>> rails{};
330 rails.emplace_back(createRailGPIO("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500331 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500332 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500333 StandardDeviceImpl device{
334 name,
335 bus,
336 address,
337 powerControlGPIOName,
338 powerGoodGPIOName,
339 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500340
341 EXPECT_EQ(device.getRails().size(), 3);
342 EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
343 EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
344 EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
345 }
346}
347
348TEST(StandardDeviceTests, FindPgoodFault)
349{
350 // No rail has a pgood fault
351 {
Shawn McCarney31234452025-10-28 12:32:05 -0500352 std::string name{"abc_pseq"};
353 uint8_t bus{0};
354 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500355 std::string powerControlGPIOName{"power-chassis-control"};
356 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500357 std::vector<std::unique_ptr<Rail>> rails{};
358 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500359 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500360 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500361 StandardDeviceImpl device{
362 name,
363 bus,
364 address,
365 powerControlGPIOName,
366 powerGoodGPIOName,
367 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500368
Shawn McCarney16275832024-06-27 10:14:11 -0500369 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500370 std::vector<int> gpioValues{1, 1, 1};
371 EXPECT_CALL(device, getGPIOValues)
372 .Times(1)
373 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500374 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2));
375 EXPECT_CALL(device, getVoutUVFaultLimit(5))
376 .Times(1)
377 .WillOnce(Return(1.1));
Shawn McCarney472101c2024-04-17 16:31:09 -0500378 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
379
380 MockServices services{};
381
382 std::string powerSupplyError{};
383 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400384 std::string error =
385 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500386 EXPECT_TRUE(error.empty());
387 EXPECT_EQ(additionalData.size(), 0);
388 }
389
Shawn McCarney16275832024-06-27 10:14:11 -0500390 // First rail has a pgood fault detected via GPIO
Shawn McCarney472101c2024-04-17 16:31:09 -0500391 // Is a PSU rail: No PSU error specified
392 {
Shawn McCarney31234452025-10-28 12:32:05 -0500393 std::string name{"abc_pseq"};
394 uint8_t bus{0};
395 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500396 std::string powerControlGPIOName{"power-chassis-control"};
397 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500398 std::vector<std::unique_ptr<Rail>> rails{};
399 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500400 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500401 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500402 StandardDeviceImpl device{
403 name,
404 bus,
405 address,
406 powerControlGPIOName,
407 powerGoodGPIOName,
408 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500409
Shawn McCarney16275832024-06-27 10:14:11 -0500410 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500411 std::vector<int> gpioValues{1, 1, 0};
412 EXPECT_CALL(device, getGPIOValues)
413 .Times(1)
414 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500415 EXPECT_CALL(device, getReadVout(5)).Times(0);
416 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
417 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
Shawn McCarney472101c2024-04-17 16:31:09 -0500418
419 MockServices services{};
420 EXPECT_CALL(services,
421 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
422 .Times(1);
423 EXPECT_CALL(
424 services,
425 logErrorMsg(
426 "Pgood fault found in rail monitored by device abc_pseq"))
427 .Times(1);
428 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
429 .Times(1);
430 EXPECT_CALL(
431 services,
432 logErrorMsg(
433 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
434 .Times(1);
435
436 std::string powerSupplyError{};
437 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400438 std::string error =
439 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500440 EXPECT_EQ(error,
441 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
442 EXPECT_EQ(additionalData.size(), 5);
443 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
444 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
445 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
446 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
447 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
448 }
449
Shawn McCarney16275832024-06-27 10:14:11 -0500450 // First rail has a pgood fault detected via GPIO
Shawn McCarney472101c2024-04-17 16:31:09 -0500451 // Is a PSU rail: PSU error specified
452 {
Shawn McCarney31234452025-10-28 12:32:05 -0500453 std::string name{"abc_pseq"};
454 uint8_t bus{0};
455 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500456 std::string powerControlGPIOName{"power-chassis-control"};
457 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500458 std::vector<std::unique_ptr<Rail>> rails{};
459 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500460 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500461 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500462 StandardDeviceImpl device{
463 name,
464 bus,
465 address,
466 powerControlGPIOName,
467 powerGoodGPIOName,
468 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500469
Shawn McCarney16275832024-06-27 10:14:11 -0500470 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500471 std::vector<int> gpioValues{1, 1, 0};
472 EXPECT_CALL(device, getGPIOValues)
473 .Times(1)
474 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500475 EXPECT_CALL(device, getReadVout(5)).Times(0);
476 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
477 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
Shawn McCarney472101c2024-04-17 16:31:09 -0500478
479 MockServices services{};
480 EXPECT_CALL(services,
481 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
482 .Times(1);
483 EXPECT_CALL(
484 services,
485 logErrorMsg(
486 "Pgood fault found in rail monitored by device abc_pseq"))
487 .Times(1);
488 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
489 .Times(1);
490 EXPECT_CALL(
491 services,
492 logErrorMsg(
493 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
494 .Times(1);
495
496 std::string powerSupplyError{"Undervoltage fault: PSU1"};
497 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400498 std::string error =
499 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500500 EXPECT_EQ(error, powerSupplyError);
501 EXPECT_EQ(additionalData.size(), 5);
502 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
503 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
504 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
505 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
506 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
507 }
508
Shawn McCarney16275832024-06-27 10:14:11 -0500509 // Second rail has a pgood fault detected via output voltage
Shawn McCarney472101c2024-04-17 16:31:09 -0500510 // Not a PSU rail: PSU error specified
511 {
Shawn McCarney31234452025-10-28 12:32:05 -0500512 std::string name{"abc_pseq"};
513 uint8_t bus{0};
514 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500515 std::string powerControlGPIOName{"power-chassis-control"};
516 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500517 std::vector<std::unique_ptr<Rail>> rails{};
518 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500519 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500520 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500521 StandardDeviceImpl device{
522 name,
523 bus,
524 address,
525 powerControlGPIOName,
526 powerGoodGPIOName,
527 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500528
Shawn McCarney16275832024-06-27 10:14:11 -0500529 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500530 std::vector<int> gpioValues{1, 1, 1};
531 EXPECT_CALL(device, getGPIOValues)
532 .Times(1)
533 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500534 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
535 EXPECT_CALL(device, getVoutUVFaultLimit(5))
536 .Times(1)
537 .WillOnce(Return(1.2));
538 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
Shawn McCarney472101c2024-04-17 16:31:09 -0500539 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
540
541 MockServices services{};
542 EXPECT_CALL(services,
543 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]"))
544 .Times(1);
545 EXPECT_CALL(
546 services,
547 logErrorMsg(
548 "Pgood fault found in rail monitored by device abc_pseq"))
549 .Times(1);
550 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
551 .Times(1);
552 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
553 .Times(1);
554 EXPECT_CALL(
555 services,
Shawn McCarney16275832024-06-27 10:14:11 -0500556 logErrorMsg(
557 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
Shawn McCarney472101c2024-04-17 16:31:09 -0500558 .Times(1);
559
560 std::string powerSupplyError{"Undervoltage fault: PSU1"};
561 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400562 std::string error =
563 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500564 EXPECT_EQ(error,
565 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
Shawn McCarney16275832024-06-27 10:14:11 -0500566 EXPECT_EQ(additionalData.size(), 6);
Shawn McCarney472101c2024-04-17 16:31:09 -0500567 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
568 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
569 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
Shawn McCarney16275832024-06-27 10:14:11 -0500570 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
571 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
Shawn McCarney472101c2024-04-17 16:31:09 -0500572 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
573 }
574
Shawn McCarney16275832024-06-27 10:14:11 -0500575 // Third rail has a pgood fault detected via STATUS_VOUT
Shawn McCarney472101c2024-04-17 16:31:09 -0500576 // Device returns 0 GPIO values
577 // Does not halt pgood fault detection because GPIO values not used by rails
578 {
Shawn McCarney31234452025-10-28 12:32:05 -0500579 std::string name{"abc_pseq"};
580 uint8_t bus{0};
581 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500582 std::string powerControlGPIOName{"power-chassis-control"};
583 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500584 std::vector<std::unique_ptr<Rail>> rails{};
585 rails.emplace_back(createRailStatusVout("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500586 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500587 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500588 StandardDeviceImpl device{
589 name,
590 bus,
591 address,
592 powerControlGPIOName,
593 powerGoodGPIOName,
594 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500595
Shawn McCarney16275832024-06-27 10:14:11 -0500596 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500597 std::vector<int> gpioValues{};
598 EXPECT_CALL(device, getGPIOValues)
599 .Times(1)
600 .WillOnce(Return(gpioValues));
601 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
Shawn McCarney16275832024-06-27 10:14:11 -0500602 EXPECT_CALL(device, getReadVout(5)).Times(0);
603 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
Shawn McCarney472101c2024-04-17 16:31:09 -0500604 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
605 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
606
607 MockServices services{};
608 EXPECT_CALL(
609 services,
610 logErrorMsg(
611 "Pgood fault found in rail monitored by device abc_pseq"))
612 .Times(1);
613 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
614 .Times(1);
615 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
616 .Times(1);
617 EXPECT_CALL(
618 services,
619 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
620 .Times(1);
621
622 std::string powerSupplyError{};
623 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400624 std::string error =
625 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500626 EXPECT_EQ(error,
627 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
628 EXPECT_EQ(additionalData.size(), 4);
629 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
630 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
631 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
632 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
633 }
634
Shawn McCarney16275832024-06-27 10:14:11 -0500635 // Third rail has a pgood fault detected via STATUS_VOUT
Shawn McCarney472101c2024-04-17 16:31:09 -0500636 // Exception occurs trying to obtain GPIO values from device
637 // Does not halt pgood fault detection because GPIO values not used by rails
638 {
Shawn McCarney31234452025-10-28 12:32:05 -0500639 std::string name{"abc_pseq"};
640 uint8_t bus{0};
641 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500642 std::string powerControlGPIOName{"power-chassis-control"};
643 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500644 std::vector<std::unique_ptr<Rail>> rails{};
645 rails.emplace_back(createRailStatusVout("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500646 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500647 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500648 StandardDeviceImpl device{
649 name,
650 bus,
651 address,
652 powerControlGPIOName,
653 powerGoodGPIOName,
654 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500655
Shawn McCarney16275832024-06-27 10:14:11 -0500656 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500657 EXPECT_CALL(device, getGPIOValues)
658 .Times(1)
659 .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"}));
660 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
Shawn McCarney16275832024-06-27 10:14:11 -0500661 EXPECT_CALL(device, getReadVout(5)).Times(0);
662 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
Shawn McCarney472101c2024-04-17 16:31:09 -0500663 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
664 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
665
666 MockServices services{};
667 EXPECT_CALL(
668 services,
669 logErrorMsg(
670 "Pgood fault found in rail monitored by device abc_pseq"))
671 .Times(1);
672 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
673 .Times(1);
674 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
675 .Times(1);
676 EXPECT_CALL(
677 services,
678 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
679 .Times(1);
680
681 std::string powerSupplyError{};
682 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400683 std::string error =
684 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500685 EXPECT_EQ(error,
686 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
687 EXPECT_EQ(additionalData.size(), 4);
688 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
689 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
690 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
691 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
692 }
693
Shawn McCarney16275832024-06-27 10:14:11 -0500694 // All three rails appear to have a pgood fault. Verify third rail is
695 // selected, even though it is last in the power on sequence, because it is
696 // checked using STATUS_VOUT. That check happens before the other checks.
697 {
Shawn McCarney31234452025-10-28 12:32:05 -0500698 std::string name{"abc_pseq"};
699 uint8_t bus{0};
700 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500701 std::string powerControlGPIOName{"power-chassis-control"};
702 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney16275832024-06-27 10:14:11 -0500703 std::vector<std::unique_ptr<Rail>> rails{};
704 rails.emplace_back(createRailGPIO("PSU", true, 2));
705 rails.emplace_back(createRailGPIO("VDD", false, 1));
706 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500707 StandardDeviceImpl device{
708 name,
709 bus,
710 address,
711 powerControlGPIOName,
712 powerGoodGPIOName,
713 std::move(rails)};
Shawn McCarney16275832024-06-27 10:14:11 -0500714
715 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
716 std::vector<int> gpioValues{0, 0, 0};
717 EXPECT_CALL(device, getGPIOValues)
718 .Times(1)
719 .WillOnce(Return(gpioValues));
720 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
721 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
722
723 MockServices services{};
724 EXPECT_CALL(services,
725 logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]"))
726 .Times(1);
727 EXPECT_CALL(
728 services,
729 logErrorMsg(
730 "Pgood fault found in rail monitored by device abc_pseq"))
731 .Times(1);
732 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
733 .Times(1);
734 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
735 .Times(1);
736 EXPECT_CALL(
737 services,
738 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
739 .Times(1);
740
741 std::string powerSupplyError{};
742 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400743 std::string error =
744 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney16275832024-06-27 10:14:11 -0500745 EXPECT_EQ(error,
746 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
747 EXPECT_EQ(additionalData.size(), 5);
748 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
749 EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]");
750 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
751 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
752 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
753 }
754
755 // Two rails appear to have a pgood fault. One is found via output voltage
756 // and one is found via a GPIO. Verify the first rail in the sequence with
757 // a fault is selected.
758 {
Shawn McCarney31234452025-10-28 12:32:05 -0500759 std::string name{"abc_pseq"};
760 uint8_t bus{0};
761 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500762 std::string powerControlGPIOName{"power-chassis-control"};
763 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney16275832024-06-27 10:14:11 -0500764 std::vector<std::unique_ptr<Rail>> rails{};
765 rails.emplace_back(createRailStatusVout("VIO", false, 7));
766 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
767 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500768 StandardDeviceImpl device{
769 name,
770 bus,
771 address,
772 powerControlGPIOName,
773 powerGoodGPIOName,
774 std::move(rails)};
Shawn McCarney16275832024-06-27 10:14:11 -0500775
776 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
777 std::vector<int> gpioValues{1, 1, 0};
778 EXPECT_CALL(device, getGPIOValues)
779 .Times(1)
780 .WillOnce(Return(gpioValues));
781 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
782 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
783 EXPECT_CALL(device, getVoutUVFaultLimit(5))
784 .Times(1)
785 .WillOnce(Return(1.2));
786 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
787
788 MockServices services{};
789 EXPECT_CALL(services,
790 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
791 .Times(1);
792 EXPECT_CALL(
793 services,
794 logErrorMsg(
795 "Pgood fault found in rail monitored by device abc_pseq"))
796 .Times(1);
797 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
798 .Times(1);
799 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
800 .Times(1);
801 EXPECT_CALL(
802 services,
803 logErrorMsg(
804 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
805 .Times(1);
806
807 std::string powerSupplyError{};
808 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400809 std::string error =
810 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney16275832024-06-27 10:14:11 -0500811 EXPECT_EQ(error,
812 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
813 EXPECT_EQ(additionalData.size(), 6);
814 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
815 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
816 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
817 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
818 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
819 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
820 }
821
Shawn McCarney472101c2024-04-17 16:31:09 -0500822 // Exception is thrown during pgood fault detection
823 {
Shawn McCarney31234452025-10-28 12:32:05 -0500824 std::string name{"abc_pseq"};
825 uint8_t bus{0};
826 uint16_t address{0x23};
Shawn McCarneyfe536672025-10-29 12:44:08 -0500827 std::string powerControlGPIOName{"power-chassis-control"};
828 std::string powerGoodGPIOName{"power-chassis-good"};
Shawn McCarney472101c2024-04-17 16:31:09 -0500829 std::vector<std::unique_ptr<Rail>> rails{};
830 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500831 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500832 rails.emplace_back(createRailStatusVout("VIO", false, 7));
Shawn McCarneyfe536672025-10-29 12:44:08 -0500833 StandardDeviceImpl device{
834 name,
835 bus,
836 address,
837 powerControlGPIOName,
838 powerGoodGPIOName,
839 std::move(rails)};
Shawn McCarney472101c2024-04-17 16:31:09 -0500840
Shawn McCarney16275832024-06-27 10:14:11 -0500841 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500842 std::vector<int> gpioValues{1, 1, 1};
843 EXPECT_CALL(device, getGPIOValues)
844 .Times(1)
845 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500846 EXPECT_CALL(device, getReadVout(5)).Times(0);
847 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
848 EXPECT_CALL(device, getStatusVout(7))
Shawn McCarney472101c2024-04-17 16:31:09 -0500849 .Times(1)
850 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
Shawn McCarney472101c2024-04-17 16:31:09 -0500851
852 MockServices services{};
853
854 std::string powerSupplyError{};
855 std::map<std::string, std::string> additionalData{};
856 try
857 {
858 device.findPgoodFault(services, powerSupplyError, additionalData);
859 ADD_FAILURE() << "Should not have reached this line.";
860 }
861 catch (const std::exception& e)
862 {
863 EXPECT_STREQ(
864 e.what(),
865 "Unable to determine if a pgood fault occurred in device abc_pseq: "
Shawn McCarney16275832024-06-27 10:14:11 -0500866 "Unable to read STATUS_VOUT value for rail VIO: "
Shawn McCarney472101c2024-04-17 16:31:09 -0500867 "File does not exist");
868 }
869 }
870}