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