1 #include "activation.hpp" 2 #include "mocked_association_interface.hpp" 3 #include "mocked_utils.hpp" 4 5 #include <sdbusplus/test/sdbus_mock.hpp> 6 7 #include <gmock/gmock.h> 8 #include <gtest/gtest.h> 9 10 using namespace phosphor::software::updater; 11 12 using ::testing::_; 13 using ::testing::Return; 14 using ::testing::StrEq; 15 16 using std::experimental::any; 17 18 class TestActivation : public ::testing::Test 19 { 20 public: 21 using PropertyType = utils::UtilsInterface::PropertyType; 22 using Status = Activation::Status; 23 using RequestedStatus = Activation::RequestedActivations; 24 TestActivation() : 25 mockedUtils( 26 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils())) 27 { 28 // By default make it compatible with the test software 29 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) 30 .WillByDefault(Return(any(PropertyType(std::string("TestManu"))))); 31 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL))) 32 .WillByDefault(Return(any(PropertyType(std::string("TestModel"))))); 33 } 34 ~TestActivation() 35 { 36 } 37 38 void onUpdateDone() 39 { 40 activation->onUpdateDone(); 41 } 42 void onUpdateFailed() 43 { 44 activation->onUpdateFailed(); 45 } 46 int getProgress() 47 { 48 return activation->activationProgress->progress(); 49 } 50 const auto& getPsuQueue() 51 { 52 return activation->psuQueue; 53 } 54 55 sdbusplus::SdBusMock sdbusMock; 56 sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock); 57 const utils::MockedUtils& mockedUtils; 58 MockedAssociationInterface mockedAssociationInterface; 59 std::unique_ptr<Activation> activation; 60 std::string versionId = "abcdefgh"; 61 std::string extVersion = "manufacturer=TestManu,model=TestModel"; 62 std::string filePath = ""; 63 std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId; 64 Status status = Status::Ready; 65 AssociationList associations; 66 }; 67 68 TEST_F(TestActivation, ctordtor) 69 { 70 activation = std::make_unique<Activation>( 71 mockedBus, dBusPath, versionId, extVersion, status, associations, 72 &mockedAssociationInterface, filePath); 73 } 74 75 TEST_F(TestActivation, ctorWithInvalidExtVersion) 76 { 77 extVersion = "invalid text"; 78 activation = std::make_unique<Activation>( 79 mockedBus, dBusPath, versionId, extVersion, status, associations, 80 &mockedAssociationInterface, filePath); 81 } 82 83 namespace phosphor::software::updater::internal 84 { 85 extern std::string getUpdateService(const std::string& psuInventoryPath, 86 const std::string& versionId); 87 } 88 89 TEST_F(TestActivation, getUpdateService) 90 { 91 std::string psuInventoryPath = "/com/example/inventory/powersupply1"; 92 std::string versionId = "12345678"; 93 std::string toCompare = "psu-update@-com-example-inventory-" 94 "powersupply1\\x20-tmp-images-12345678.service"; 95 96 auto service = phosphor::software::updater::internal::getUpdateService( 97 psuInventoryPath, versionId); 98 EXPECT_EQ(toCompare, service); 99 } 100 101 TEST_F(TestActivation, doUpdateWhenNoPSU) 102 { 103 activation = std::make_unique<Activation>( 104 mockedBus, dBusPath, versionId, extVersion, status, associations, 105 &mockedAssociationInterface, filePath); 106 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 107 .WillByDefault( 108 Return(std::vector<std::string>({}))); // No PSU inventory 109 activation->requestedActivation(RequestedStatus::Active); 110 111 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 112 .Times(0); 113 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 114 .Times(0); 115 EXPECT_EQ(Status::Failed, activation->activation()); 116 } 117 118 TEST_F(TestActivation, doUpdateOnePSUOK) 119 { 120 constexpr auto psu0 = "/com/example/inventory/psu0"; 121 activation = std::make_unique<Activation>( 122 mockedBus, dBusPath, versionId, extVersion, status, associations, 123 &mockedAssociationInterface, filePath); 124 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 125 .WillByDefault( 126 Return(std::vector<std::string>({psu0}))); // One PSU inventory 127 activation->requestedActivation(RequestedStatus::Active); 128 129 EXPECT_EQ(Status::Activating, activation->activation()); 130 131 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 132 .Times(1); 133 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 134 .Times(1); 135 onUpdateDone(); 136 EXPECT_EQ(Status::Active, activation->activation()); 137 } 138 139 TEST_F(TestActivation, doUpdateFourPSUsOK) 140 { 141 constexpr auto psu0 = "/com/example/inventory/psu0"; 142 constexpr auto psu1 = "/com/example/inventory/psu1"; 143 constexpr auto psu2 = "/com/example/inventory/psu2"; 144 constexpr auto psu3 = "/com/example/inventory/psu3"; 145 activation = std::make_unique<Activation>( 146 mockedBus, dBusPath, versionId, extVersion, status, associations, 147 &mockedAssociationInterface, filePath); 148 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 149 .WillByDefault(Return( 150 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 151 activation->requestedActivation(RequestedStatus::Active); 152 153 EXPECT_EQ(Status::Activating, activation->activation()); 154 EXPECT_EQ(10, getProgress()); 155 156 onUpdateDone(); 157 EXPECT_EQ(Status::Activating, activation->activation()); 158 EXPECT_EQ(30, getProgress()); 159 160 onUpdateDone(); 161 EXPECT_EQ(Status::Activating, activation->activation()); 162 EXPECT_EQ(50, getProgress()); 163 164 onUpdateDone(); 165 EXPECT_EQ(Status::Activating, activation->activation()); 166 EXPECT_EQ(70, getProgress()); 167 168 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 169 .Times(1); 170 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 171 .Times(1); 172 173 onUpdateDone(); 174 EXPECT_EQ(Status::Active, activation->activation()); 175 } 176 177 TEST_F(TestActivation, doUpdateFourPSUsFailonSecond) 178 { 179 constexpr auto psu0 = "/com/example/inventory/psu0"; 180 constexpr auto psu1 = "/com/example/inventory/psu1"; 181 constexpr auto psu2 = "/com/example/inventory/psu2"; 182 constexpr auto psu3 = "/com/example/inventory/psu3"; 183 activation = std::make_unique<Activation>( 184 mockedBus, dBusPath, versionId, extVersion, status, associations, 185 &mockedAssociationInterface, filePath); 186 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 187 .WillByDefault(Return( 188 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 189 activation->requestedActivation(RequestedStatus::Active); 190 191 EXPECT_EQ(Status::Activating, activation->activation()); 192 EXPECT_EQ(10, getProgress()); 193 194 onUpdateDone(); 195 EXPECT_EQ(Status::Activating, activation->activation()); 196 EXPECT_EQ(30, getProgress()); 197 198 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 199 .Times(0); 200 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 201 .Times(0); 202 onUpdateFailed(); 203 EXPECT_EQ(Status::Failed, activation->activation()); 204 } 205 206 TEST_F(TestActivation, doUpdateOnExceptionFromDbus) 207 { 208 constexpr auto psu0 = "/com/example/inventory/psu0"; 209 activation = std::make_unique<Activation>( 210 mockedBus, dBusPath, versionId, extVersion, status, associations, 211 &mockedAssociationInterface, filePath); 212 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 213 .WillByDefault( 214 Return(std::vector<std::string>({psu0}))); // One PSU inventory 215 ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr)) 216 .WillByDefault(Return(-1)); // Make sdbus call failure 217 activation->requestedActivation(RequestedStatus::Active); 218 219 EXPECT_EQ(Status::Failed, activation->activation()); 220 } 221 222 TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible) 223 { 224 constexpr auto psu0 = "/com/example/inventory/psu0"; 225 extVersion = "manufacturer=TestManu,model=DifferentModel"; 226 activation = std::make_unique<Activation>( 227 mockedBus, dBusPath, versionId, extVersion, status, associations, 228 &mockedAssociationInterface, filePath); 229 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 230 .WillByDefault(Return(std::vector<std::string>({psu0}))); 231 activation->requestedActivation(RequestedStatus::Active); 232 233 EXPECT_EQ(Status::Failed, activation->activation()); 234 } 235 236 TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible) 237 { 238 constexpr auto psu0 = "/com/example/inventory/psu0"; 239 extVersion = "manufacturer=DifferentManu,model=TestModel"; 240 activation = std::make_unique<Activation>( 241 mockedBus, dBusPath, versionId, extVersion, status, associations, 242 &mockedAssociationInterface, filePath); 243 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 244 .WillByDefault(Return(std::vector<std::string>({psu0}))); 245 activation->requestedActivation(RequestedStatus::Active); 246 247 EXPECT_EQ(Status::Failed, activation->activation()); 248 } 249 250 TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty) 251 { 252 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) 253 .WillByDefault(Return(any(PropertyType(std::string(""))))); 254 extVersion = "manufacturer=AnyManu,model=TestModel"; 255 // Below is the same as doUpdateOnePSUOK case 256 constexpr auto psu0 = "/com/example/inventory/psu0"; 257 activation = std::make_unique<Activation>( 258 mockedBus, dBusPath, versionId, extVersion, status, associations, 259 &mockedAssociationInterface, filePath); 260 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 261 .WillByDefault( 262 Return(std::vector<std::string>({psu0}))); // One PSU inventory 263 activation->requestedActivation(RequestedStatus::Active); 264 265 EXPECT_EQ(Status::Activating, activation->activation()); 266 267 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 268 .Times(1); 269 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 270 .Times(1); 271 onUpdateDone(); 272 EXPECT_EQ(Status::Active, activation->activation()); 273 } 274 275 TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible) 276 { 277 constexpr auto psu0 = "/com/example/inventory/psu0"; 278 constexpr auto psu1 = "/com/example/inventory/psu1"; 279 constexpr auto psu2 = "/com/example/inventory/psu2"; 280 constexpr auto psu3 = "/com/example/inventory/psu3"; 281 ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL))) 282 .WillByDefault( 283 Return(any(PropertyType(std::string("DifferentModel"))))); 284 activation = std::make_unique<Activation>( 285 mockedBus, dBusPath, versionId, extVersion, status, associations, 286 &mockedAssociationInterface, filePath); 287 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 288 .WillByDefault(Return( 289 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 290 activation->requestedActivation(RequestedStatus::Active); 291 292 const auto& psuQueue = getPsuQueue(); 293 EXPECT_EQ(3u, psuQueue.size()); 294 295 // Only 3 PSUs shall be updated, and psu1 shall be skipped 296 EXPECT_EQ(Status::Activating, activation->activation()); 297 EXPECT_EQ(10, getProgress()); 298 299 onUpdateDone(); 300 EXPECT_EQ(Status::Activating, activation->activation()); 301 EXPECT_EQ(36, getProgress()); 302 303 onUpdateDone(); 304 EXPECT_EQ(Status::Activating, activation->activation()); 305 EXPECT_EQ(62, getProgress()); 306 307 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 308 .Times(1); 309 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 310 .Times(1); 311 312 onUpdateDone(); 313 EXPECT_EQ(Status::Active, activation->activation()); 314 } 315