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