1 /** 2 * Copyright © 2020 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 "action.hpp" 17 #include "chassis.hpp" 18 #include "compare_presence_action.hpp" 19 #include "device.hpp" 20 #include "i2c_interface.hpp" 21 #include "mock_action.hpp" 22 #include "mock_error_logging.hpp" 23 #include "mock_journal.hpp" 24 #include "mock_presence_service.hpp" 25 #include "mock_services.hpp" 26 #include "mocked_i2c_interface.hpp" 27 #include "presence_detection.hpp" 28 #include "rule.hpp" 29 #include "system.hpp" 30 31 #include <sdbusplus/exception.hpp> 32 33 #include <memory> 34 #include <stdexcept> 35 #include <string> 36 #include <tuple> 37 #include <utility> 38 #include <vector> 39 40 #include <gmock/gmock.h> 41 #include <gtest/gtest.h> 42 43 using namespace phosphor::power::regulators; 44 45 using ::testing::Ref; 46 using ::testing::Return; 47 using ::testing::Throw; 48 49 /** 50 * Concrete subclass of sdbusplus::exception_t abstract base class. 51 */ 52 class TestSDBusError : public sdbusplus::exception_t 53 { 54 public: 55 TestSDBusError(const std::string& error) : error{error} 56 { 57 } 58 59 const char* what() const noexcept override 60 { 61 return error.c_str(); 62 } 63 64 const char* name() const noexcept override 65 { 66 return ""; 67 } 68 69 const char* description() const noexcept override 70 { 71 return ""; 72 } 73 74 private: 75 const std::string error{}; 76 }; 77 78 /** 79 * Creates the parent objects that normally contain a PresenceDetection object. 80 * 81 * A PresenceDetection object is normally contained within a hierarchy of 82 * System, Chassis, and Device objects. These objects are required in order to 83 * call the execute() method. 84 * 85 * Creates the System, Chassis, and Device objects. The PresenceDetection 86 * object is moved into the Device object. 87 * 88 * @param detection PresenceDetection object to move into object hierarchy 89 * @return Pointers to the System, Chassis, and Device objects. The Chassis and 90 * Device objects are contained within the System object and will be 91 * automatically destructed. 92 */ 93 std::tuple<std::unique_ptr<System>, Chassis*, Device*> 94 createParentObjects(std::unique_ptr<PresenceDetection> detection) 95 { 96 // Create mock I2CInterface 97 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 98 std::make_unique<i2c::MockedI2CInterface>(); 99 100 // Create Device that contains PresenceDetection 101 std::unique_ptr<Device> device = std::make_unique<Device>( 102 "vdd_reg", true, 103 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 104 std::move(i2cInterface), std::move(detection)); 105 Device* devicePtr = device.get(); 106 107 // Create Chassis that contains Device 108 std::vector<std::unique_ptr<Device>> devices{}; 109 devices.emplace_back(std::move(device)); 110 std::unique_ptr<Chassis> chassis = 111 std::make_unique<Chassis>(1, std::move(devices)); 112 Chassis* chassisPtr = chassis.get(); 113 114 // Create System that contains Chassis 115 std::vector<std::unique_ptr<Rule>> rules{}; 116 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 117 chassisVec.emplace_back(std::move(chassis)); 118 std::unique_ptr<System> system = 119 std::make_unique<System>(std::move(rules), std::move(chassisVec)); 120 121 return std::make_tuple(std::move(system), chassisPtr, devicePtr); 122 } 123 124 TEST(PresenceDetectionTests, Constructor) 125 { 126 std::vector<std::unique_ptr<Action>> actions{}; 127 actions.emplace_back(std::make_unique<MockAction>()); 128 129 PresenceDetection detection{std::move(actions)}; 130 EXPECT_EQ(detection.getActions().size(), 1); 131 EXPECT_FALSE(detection.getCachedPresence().has_value()); 132 } 133 134 TEST(PresenceDetectionTests, ClearCache) 135 { 136 // Create MockAction that will return true once 137 std::unique_ptr<MockAction> action = std::make_unique<MockAction>(); 138 EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true)); 139 140 // Create PresenceDetection 141 std::vector<std::unique_ptr<Action>> actions{}; 142 actions.emplace_back(std::move(action)); 143 PresenceDetection* detection = new PresenceDetection(std::move(actions)); 144 145 // Create parent System, Chassis, and Device objects 146 auto [system, chassis, device] = 147 createParentObjects(std::unique_ptr<PresenceDetection>{detection}); 148 149 // Verify that initially no presence value is cached 150 EXPECT_FALSE(detection->getCachedPresence().has_value()); 151 152 // Call execute() which should obtain and cache presence value 153 MockServices services{}; 154 EXPECT_TRUE(detection->execute(services, *system, *chassis, *device)); 155 156 // Verify true presence value was cached 157 EXPECT_TRUE(detection->getCachedPresence().has_value()); 158 EXPECT_TRUE(detection->getCachedPresence().value()); 159 160 // Clear cached presence value 161 detection->clearCache(); 162 163 // Verify that no presence value is cached 164 EXPECT_FALSE(detection->getCachedPresence().has_value()); 165 } 166 167 TEST(PresenceDetectionTests, Execute) 168 { 169 // Create ComparePresenceAction 170 std::unique_ptr<ComparePresenceAction> action = 171 std::make_unique<ComparePresenceAction>( 172 "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2", 173 true); 174 175 // Create PresenceDetection 176 std::vector<std::unique_ptr<Action>> actions{}; 177 actions.emplace_back(std::move(action)); 178 PresenceDetection* detection = new PresenceDetection(std::move(actions)); 179 180 // Create parent System, Chassis, and Device objects 181 auto [system, chassis, device] = 182 createParentObjects(std::unique_ptr<PresenceDetection>{detection}); 183 184 // Test where works: Present: Value is not cached 185 { 186 EXPECT_FALSE(detection->getCachedPresence().has_value()); 187 188 // Create MockServices. MockPresenceService::isPresent() should return 189 // true. 190 MockServices services{}; 191 MockPresenceService& presenceService = 192 services.getMockPresenceService(); 193 EXPECT_CALL(presenceService, 194 isPresent("/xyz/openbmc_project/inventory/system/chassis/" 195 "motherboard/cpu2")) 196 .Times(1) 197 .WillOnce(Return(true)); 198 199 // Execute PresenceDetection 200 EXPECT_TRUE(detection->execute(services, *system, *chassis, *device)); 201 202 EXPECT_TRUE(detection->getCachedPresence().has_value()); 203 EXPECT_TRUE(detection->getCachedPresence().value()); 204 } 205 206 // Test where works: Present: Value is cached 207 { 208 EXPECT_TRUE(detection->getCachedPresence().has_value()); 209 210 // Create MockServices. MockPresenceService::isPresent() should not be 211 // called. 212 MockServices services{}; 213 MockPresenceService& presenceService = 214 services.getMockPresenceService(); 215 EXPECT_CALL(presenceService, isPresent).Times(0); 216 217 // Execute PresenceDetection 218 EXPECT_TRUE(detection->execute(services, *system, *chassis, *device)); 219 } 220 221 // Test where works: Not present: Value is not cached 222 { 223 // Clear cached presence value 224 detection->clearCache(); 225 EXPECT_FALSE(detection->getCachedPresence().has_value()); 226 227 // Create MockServices. MockPresenceService::isPresent() should return 228 // false. 229 MockServices services{}; 230 MockPresenceService& presenceService = 231 services.getMockPresenceService(); 232 EXPECT_CALL(presenceService, 233 isPresent("/xyz/openbmc_project/inventory/system/chassis/" 234 "motherboard/cpu2")) 235 .Times(1) 236 .WillOnce(Return(false)); 237 238 // Execute PresenceDetection 239 EXPECT_FALSE(detection->execute(services, *system, *chassis, *device)); 240 241 EXPECT_TRUE(detection->getCachedPresence().has_value()); 242 EXPECT_FALSE(detection->getCachedPresence().value()); 243 } 244 245 // Test where works: Not present: Value is cached 246 { 247 EXPECT_TRUE(detection->getCachedPresence().has_value()); 248 249 // Create MockServices. MockPresenceService::isPresent() should not be 250 // called. 251 MockServices services{}; 252 MockPresenceService& presenceService = 253 services.getMockPresenceService(); 254 EXPECT_CALL(presenceService, isPresent).Times(0); 255 256 // Execute PresenceDetection 257 EXPECT_FALSE(detection->execute(services, *system, *chassis, *device)); 258 } 259 260 // Test where fails 261 { 262 // Clear cached presence value 263 detection->clearCache(); 264 EXPECT_FALSE(detection->getCachedPresence().has_value()); 265 266 // Create MockServices. MockPresenceService::isPresent() should throw 267 // an exception. 268 MockServices services{}; 269 MockPresenceService& presenceService = 270 services.getMockPresenceService(); 271 EXPECT_CALL(presenceService, 272 isPresent("/xyz/openbmc_project/inventory/system/chassis/" 273 "motherboard/cpu2")) 274 .Times(1) 275 .WillOnce(Throw(TestSDBusError{"DBusError: Invalid object path."})); 276 277 // Define expected journal messages that should be passed to MockJournal 278 MockJournal& journal = services.getMockJournal(); 279 std::vector<std::string> exceptionMessages{ 280 "DBusError: Invalid object path.", 281 "ActionError: compare_presence: { fru: " 282 "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2, " 283 "value: true }"}; 284 EXPECT_CALL(journal, logError(exceptionMessages)).Times(1); 285 EXPECT_CALL(journal, 286 logError("Unable to determine presence of vdd_reg")) 287 .Times(1); 288 289 // Expect logDBusError() to be called 290 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 291 EXPECT_CALL(errorLogging, 292 logDBusError(Entry::Level::Warning, Ref(journal))) 293 .Times(1); 294 295 // Execute PresenceDetection. Should return true when an error occurs. 296 EXPECT_TRUE(detection->execute(services, *system, *chassis, *device)); 297 298 EXPECT_TRUE(detection->getCachedPresence().has_value()); 299 EXPECT_TRUE(detection->getCachedPresence().value()); 300 } 301 } 302 303 TEST(PresenceDetectionTests, GetActions) 304 { 305 std::vector<std::unique_ptr<Action>> actions{}; 306 307 MockAction* action1 = new MockAction{}; 308 actions.emplace_back(std::unique_ptr<MockAction>{action1}); 309 310 MockAction* action2 = new MockAction{}; 311 actions.emplace_back(std::unique_ptr<MockAction>{action2}); 312 313 PresenceDetection detection{std::move(actions)}; 314 EXPECT_EQ(detection.getActions().size(), 2); 315 EXPECT_EQ(detection.getActions()[0].get(), action1); 316 EXPECT_EQ(detection.getActions()[1].get(), action2); 317 } 318 319 TEST(PresenceDetectionTests, GetCachedPresence) 320 { 321 // Create MockAction that will return false once 322 std::unique_ptr<MockAction> action = std::make_unique<MockAction>(); 323 EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(false)); 324 325 // Create PresenceDetection 326 std::vector<std::unique_ptr<Action>> actions{}; 327 actions.emplace_back(std::move(action)); 328 PresenceDetection* detection = new PresenceDetection(std::move(actions)); 329 330 // Create parent System, Chassis, and Device objects 331 auto [system, chassis, device] = 332 createParentObjects(std::unique_ptr<PresenceDetection>{detection}); 333 334 // Verify that initially no presence value is cached 335 EXPECT_FALSE(detection->getCachedPresence().has_value()); 336 337 // Call execute() which should obtain and cache presence value 338 MockServices services{}; 339 EXPECT_FALSE(detection->execute(services, *system, *chassis, *device)); 340 341 // Verify false presence value was cached 342 EXPECT_TRUE(detection->getCachedPresence().has_value()); 343 EXPECT_FALSE(detection->getCachedPresence().value()); 344 345 // Clear cached presence value 346 detection->clearCache(); 347 348 // Verify that no presence value is cached 349 EXPECT_FALSE(detection->getCachedPresence().has_value()); 350 } 351