1 #include "dbus_environment.hpp" 2 #include "helpers.hpp" 3 #include "mocks/json_storage_mock.hpp" 4 #include "mocks/trigger_factory_mock.hpp" 5 #include "mocks/trigger_mock.hpp" 6 #include "params/trigger_params.hpp" 7 #include "trigger.hpp" 8 #include "trigger_manager.hpp" 9 #include "utils/conversion_trigger.hpp" 10 #include "utils/transform.hpp" 11 12 using namespace testing; 13 14 class TestTriggerManager : public Test 15 { 16 public: 17 std::pair<boost::system::error_code, std::string> 18 addTrigger(const TriggerParams& params) 19 { 20 const auto sensorInfos = 21 utils::fromLabeledSensorsInfo(params.sensors()); 22 23 std::promise<std::pair<boost::system::error_code, std::string>> 24 addTriggerPromise; 25 DbusEnvironment::getBus()->async_method_call( 26 [&addTriggerPromise](boost::system::error_code ec, 27 const std::string& path) { 28 addTriggerPromise.set_value({ec, path}); 29 }, 30 DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath, 31 TriggerManager::triggerManagerIfaceName, "AddTrigger", params.id(), 32 params.name(), 33 utils::transform( 34 params.triggerActions(), 35 [](const auto& action) { return actionToString(action); }), 36 sensorInfos, params.reportIds(), 37 std::visit(utils::FromLabeledThresholdParamConversion(), 38 params.thresholdParams())); 39 return DbusEnvironment::waitForFuture(addTriggerPromise.get_future()); 40 } 41 42 std::unique_ptr<TriggerManager> makeTriggerManager() 43 { 44 return std::make_unique<TriggerManager>( 45 std::move(triggerFactoryMockPtr), std::move(storageMockPtr), 46 DbusEnvironment::getObjServer()); 47 } 48 49 void SetUp() override 50 { 51 sut = makeTriggerManager(); 52 } 53 54 std::unique_ptr<StorageMock> storageMockPtr = 55 std::make_unique<NiceMock<StorageMock>>(); 56 StorageMock& storageMock = *storageMockPtr; 57 std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr = 58 std::make_unique<NiceMock<TriggerFactoryMock>>(); 59 TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr; 60 std::unique_ptr<TriggerMock> triggerMockPtr = 61 std::make_unique<NiceMock<TriggerMock>>(TriggerParams().id()); 62 TriggerMock& triggerMock = *triggerMockPtr; 63 std::unique_ptr<TriggerManager> sut; 64 MockFunction<void(std::string)> checkPoint; 65 }; 66 67 TEST_F(TestTriggerManager, addTrigger) 68 { 69 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 70 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 71 72 auto [ec, path] = addTrigger(TriggerParams()); 73 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 74 EXPECT_THAT(path, Eq(triggerMock.getPath())); 75 } 76 77 TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds) 78 { 79 TriggerParams triggerParamsDiscrete; 80 auto thresholds = std::vector<discrete::LabeledThresholdParam>{ 81 {"discrete_threshold1", discrete::Severity::ok, 10, "11.0"}, 82 {"discrete_threshold2", discrete::Severity::warning, 10, "12.0"}, 83 {"discrete_threshold3", discrete::Severity::critical, 10, "13.0"}}; 84 85 triggerParamsDiscrete.thresholdParams(thresholds); 86 87 auto [ec, path] = addTrigger(triggerParamsDiscrete); 88 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 89 EXPECT_THAT(path, Eq(triggerMock.getPath())); 90 } 91 92 TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds) 93 { 94 TriggerParams triggerParamsDiscrete; 95 auto thresholds = std::vector<discrete::LabeledThresholdParam>(); 96 97 triggerParamsDiscrete.thresholdParams(thresholds); 98 99 auto [ec, path] = addTrigger(triggerParamsDiscrete); 100 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 101 EXPECT_THAT(path, Eq(triggerMock.getPath())); 102 } 103 104 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice) 105 { 106 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 107 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 108 109 addTrigger(TriggerParams()); 110 111 auto [ec, path] = addTrigger(TriggerParams()); 112 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 113 EXPECT_THAT(path, Eq(std::string())); 114 } 115 116 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithInvalidId) 117 { 118 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 119 .Times(0); 120 121 auto [ec, path] = addTrigger(TriggerParams().id("not valid?")); 122 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 123 EXPECT_THAT(path, Eq(std::string())); 124 } 125 126 TEST_F(TestTriggerManager, addTriggerWithoutIdAndName) 127 { 128 triggerFactoryMock 129 .expectMake(TriggerParams() 130 .id(TriggerManager::triggerNameDefault) 131 .name(TriggerManager::triggerNameDefault), 132 Ref(*sut), Ref(storageMock)) 133 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 134 135 auto [ec, path] = addTrigger(TriggerParams().id("").name("")); 136 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 137 EXPECT_THAT(path, Not(Eq(""))); 138 } 139 140 TEST_F(TestTriggerManager, addTriggerWithPrefixId) 141 { 142 triggerFactoryMock 143 .expectMake(TriggerParams() 144 .id("TelemetryService/HackyName") 145 .name("Hacky/Name!@#$"), 146 Ref(*sut), Ref(storageMock)) 147 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 148 149 auto [ec, path] = addTrigger( 150 TriggerParams().id("TelemetryService/").name("Hacky/Name!@#$")); 151 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 152 EXPECT_THAT(path, Not(Eq(""))); 153 } 154 155 TEST_F(TestTriggerManager, addTriggerWithoutIdTwice) 156 { 157 addTrigger(TriggerParams().id("")); 158 159 auto [ec, path] = addTrigger(TriggerParams().id("")); 160 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 161 EXPECT_THAT(path, Not(Eq(""))); 162 } 163 164 TEST_F(TestTriggerManager, addTriggerWithoutIdAndWithLongNameTwice) 165 { 166 addTrigger(TriggerParams().id("").name( 167 std::string(2 * TriggerManager::maxTriggerIdLength, 'z'))); 168 169 auto [ec, path] = addTrigger(TriggerParams().id("").name( 170 std::string(2 * TriggerManager::maxTriggerIdLength, 'z'))); 171 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 172 EXPECT_THAT(path, Not(Eq(""))); 173 } 174 175 TEST_F(TestTriggerManager, addTriggerWithMaxLengthId) 176 { 177 auto triggerId = std::string(TriggerManager::maxTriggerIdLength, 'z'); 178 auto triggerParams = TriggerParams().id(triggerId); 179 180 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)) 181 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 182 183 auto [ec, path] = addTrigger(triggerParams); 184 185 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 186 EXPECT_THAT(path, Eq(triggerMock.getPath())); 187 } 188 189 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongId) 190 { 191 auto triggerId = std::string(TriggerManager::maxTriggerIdLength + 1, 'z'); 192 auto triggerParams = TriggerParams().id(triggerId); 193 194 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 195 .Times(0); 196 197 auto [ec, path] = addTrigger(triggerParams); 198 199 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 200 EXPECT_THAT(path, Eq(std::string())); 201 } 202 203 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached) 204 { 205 auto triggerParams = TriggerParams(); 206 207 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 208 .Times(TriggerManager::maxTriggers); 209 210 for (size_t i = 0; i < TriggerManager::maxTriggers; i++) 211 { 212 triggerParams.id(TriggerParams().id() + std::to_string(i)); 213 214 auto [ec, path] = addTrigger(triggerParams); 215 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 216 } 217 218 triggerParams.id(TriggerParams().id() + 219 std::to_string(TriggerManager::maxTriggers)); 220 auto [ec, path] = addTrigger(triggerParams); 221 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 222 EXPECT_THAT(path, Eq(std::string())); 223 } 224 225 TEST_F(TestTriggerManager, removeTrigger) 226 { 227 { 228 InSequence seq; 229 triggerFactoryMock 230 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 231 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 232 EXPECT_CALL(triggerMock, Die()); 233 EXPECT_CALL(checkPoint, Call("end")); 234 } 235 236 addTrigger(TriggerParams()); 237 sut->removeTrigger(&triggerMock); 238 checkPoint.Call("end"); 239 } 240 241 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect) 242 { 243 { 244 InSequence seq; 245 EXPECT_CALL(checkPoint, Call("end")); 246 EXPECT_CALL(triggerMock, Die()); 247 } 248 249 sut->removeTrigger(&triggerMock); 250 checkPoint.Call("end"); 251 } 252 253 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect) 254 { 255 { 256 InSequence seq; 257 triggerFactoryMock 258 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 259 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 260 EXPECT_CALL(triggerMock, Die()); 261 EXPECT_CALL(checkPoint, Call("end")); 262 } 263 264 addTrigger(TriggerParams()); 265 sut->removeTrigger(&triggerMock); 266 sut->removeTrigger(&triggerMock); 267 checkPoint.Call("end"); 268 } 269 270 class TestTriggerManagerStorage : public TestTriggerManager 271 { 272 public: 273 using FilePath = interfaces::JsonStorage::FilePath; 274 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 275 276 void SetUp() override 277 { 278 ON_CALL(storageMock, list()) 279 .WillByDefault(Return(std::vector<FilePath>{ 280 {FilePath("trigger1")}, {FilePath("trigger2")}})); 281 282 ON_CALL(storageMock, load(FilePath("trigger1"))) 283 .WillByDefault(InvokeWithoutArgs([this] { return data1; })); 284 285 data2["Id"] = "Trigger2"; 286 data2["Name"] = "Second Trigger"; 287 ON_CALL(storageMock, load(FilePath("trigger2"))) 288 .WillByDefault(InvokeWithoutArgs([this] { return data2; })); 289 } 290 291 nlohmann::json data1 = nlohmann::json{ 292 {"Version", Trigger::triggerVersion}, 293 {"Id", TriggerParams().id()}, 294 {"Name", TriggerParams().name()}, 295 {"ThresholdParamsDiscriminator", 296 TriggerParams().thresholdParams().index()}, 297 {"TriggerActions", utils::transform(TriggerParams().triggerActions(), 298 [](const auto& action) { 299 return actionToString(action); 300 })}, 301 {"ThresholdParams", utils::labeledThresholdParamsToJson( 302 TriggerParams().thresholdParams())}, 303 {"ReportIds", TriggerParams().reportIds()}, 304 {"Sensors", TriggerParams().sensors()}}; 305 306 nlohmann::json data2 = data1; 307 }; 308 309 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage) 310 { 311 triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock)); 312 triggerFactoryMock.expectMake( 313 TriggerParams().id("Trigger2").name("Second Trigger"), _, 314 Ref(storageMock)); 315 EXPECT_CALL(storageMock, remove(_)).Times(0); 316 317 sut = makeTriggerManager(); 318 } 319 320 TEST_F(TestTriggerManagerStorage, 321 triggerManagerCtorRemoveDiscreteTriggerFromStorage) 322 { 323 LabeledTriggerThresholdParams thresholdParams = 324 std::vector<discrete::LabeledThresholdParam>{ 325 {"userId1", discrete::Severity::warning, 15, "10.0"}, 326 {"userId2", discrete::Severity::critical, 5, "20.0"}}; 327 328 data1["ThresholdParamsDiscriminator"] = thresholdParams.index(); 329 330 data1["ThresholdParams"] = 331 utils::labeledThresholdParamsToJson(thresholdParams); 332 333 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 334 335 sut = makeTriggerManager(); 336 } 337 338 TEST_F(TestTriggerManagerStorage, 339 triggerManagerCtorRemoveDiscreteTriggerFromStorage2) 340 { 341 data1["IsDiscrete"] = true; 342 343 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 344 345 sut = makeTriggerManager(); 346 } 347 348 TEST_F(TestTriggerManagerStorage, 349 triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage) 350 { 351 data1["Version"] = Trigger::triggerVersion - 1; 352 353 triggerFactoryMock.expectMake( 354 TriggerParams().id("Trigger2").name("Second Trigger"), _, 355 Ref(storageMock)); 356 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))); 357 358 sut = makeTriggerManager(); 359 } 360