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