ValueMetricProducer_test.cpp revision 330af58f2b8582b855085655fae553cdfaf44e6c
1// Copyright (C) 2017 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include "src/metrics/ValueMetricProducer.h" 16#include "src/stats_log_util.h" 17#include "metrics_test_helper.h" 18#include "tests/statsd_test_util.h" 19 20#include <gmock/gmock.h> 21#include <gtest/gtest.h> 22#include <stdio.h> 23#include <vector> 24 25using namespace testing; 26using android::sp; 27using std::make_shared; 28using std::set; 29using std::shared_ptr; 30using std::unordered_map; 31using std::vector; 32 33#ifdef __ANDROID__ 34 35namespace android { 36namespace os { 37namespace statsd { 38 39const ConfigKey kConfigKey(0, 12345); 40const int tagId = 1; 41const int64_t metricId = 123; 42const int64_t bucketStartTimeNs = 10000000000; 43const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 44const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; 45const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; 46const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; 47const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 48 49/* 50 * Tests pulled atoms with no conditions 51 */ 52TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { 53 ValueMetric metric; 54 metric.set_id(metricId); 55 metric.set_bucket(ONE_MINUTE); 56 metric.mutable_value_field()->set_field(tagId); 57 metric.mutable_value_field()->add_child()->set_field(2); 58 59 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 60 // TODO: pending refactor of StatsPullerManager 61 // For now we still need this so that it doesn't do real pulling. 62 shared_ptr<MockStatsPullerManager> pullerManager = 63 make_shared<StrictMock<MockStatsPullerManager>>(); 64 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); 65 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 66 67 ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 68 tagId, bucketStartTimeNs, pullerManager); 69 70 vector<shared_ptr<LogEvent>> allData; 71 allData.clear(); 72 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 73 event->write(tagId); 74 event->write(11); 75 event->init(); 76 allData.push_back(event); 77 78 valueProducer.onDataPulled(allData); 79 // has one slice 80 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 81 ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 82 // startUpdated:true tainted:0 sum:0 start:11 83 EXPECT_EQ(true, curInterval.startUpdated); 84 EXPECT_EQ(0, curInterval.tainted); 85 EXPECT_EQ(0, curInterval.sum); 86 EXPECT_EQ(11, curInterval.start); 87 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 88 EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue); 89 90 allData.clear(); 91 event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); 92 event->write(tagId); 93 event->write(23); 94 event->init(); 95 allData.push_back(event); 96 valueProducer.onDataPulled(allData); 97 // has one slice 98 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 99 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 100 // tartUpdated:false tainted:0 sum:12 101 EXPECT_EQ(true, curInterval.startUpdated); 102 EXPECT_EQ(0, curInterval.tainted); 103 EXPECT_EQ(0, curInterval.sum); 104 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 105 EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); 106 EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue); 107 108 allData.clear(); 109 event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); 110 event->write(tagId); 111 event->write(36); 112 event->init(); 113 allData.push_back(event); 114 valueProducer.onDataPulled(allData); 115 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 116 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 117 // startUpdated:false tainted:0 sum:12 118 EXPECT_EQ(true, curInterval.startUpdated); 119 EXPECT_EQ(0, curInterval.tainted); 120 EXPECT_EQ(0, curInterval.sum); 121 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 122 EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size()); 123 EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue); 124} 125 126/* 127 * Test pulled event with non sliced condition. 128 */ 129TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { 130 ValueMetric metric; 131 metric.set_id(metricId); 132 metric.set_bucket(ONE_MINUTE); 133 metric.mutable_value_field()->set_field(tagId); 134 metric.mutable_value_field()->add_child()->set_field(2); 135 metric.set_condition(StringToId("SCREEN_ON")); 136 137 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 138 shared_ptr<MockStatsPullerManager> pullerManager = 139 make_shared<StrictMock<MockStatsPullerManager>>(); 140 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); 141 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); 142 143 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 144 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 145 data->clear(); 146 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 147 event->write(tagId); 148 event->write(100); 149 event->init(); 150 data->push_back(event); 151 return true; 152 })) 153 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 154 data->clear(); 155 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); 156 event->write(tagId); 157 event->write(120); 158 event->init(); 159 data->push_back(event); 160 return true; 161 })); 162 163 ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, 164 pullerManager); 165 166 valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); 167 168 // has one slice 169 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 170 ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 171 // startUpdated:false tainted:0 sum:0 start:100 172 EXPECT_EQ(100, curInterval.start); 173 EXPECT_EQ(true, curInterval.startUpdated); 174 EXPECT_EQ(0, curInterval.tainted); 175 EXPECT_EQ(0, curInterval.sum); 176 EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); 177 178 vector<shared_ptr<LogEvent>> allData; 179 allData.clear(); 180 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 181 event->write(1); 182 event->write(110); 183 event->init(); 184 allData.push_back(event); 185 valueProducer.onDataPulled(allData); 186 187 // has one slice 188 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 189 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 190 // startUpdated:false tainted:0 sum:0 start:110 191 EXPECT_EQ(110, curInterval.start); 192 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 193 EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); 194 EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue); 195 196 valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); 197 198 // has one slice 199 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 200 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 201 // startUpdated:false tainted:0 sum:0 start:110 202 EXPECT_EQ(10, curInterval.sum); 203 EXPECT_EQ(false, curInterval.startUpdated); 204} 205 206TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { 207 ValueMetric metric; 208 metric.set_id(metricId); 209 metric.set_bucket(ONE_MINUTE); 210 metric.mutable_value_field()->set_field(tagId); 211 metric.mutable_value_field()->add_child()->set_field(2); 212 213 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 214 shared_ptr<MockStatsPullerManager> pullerManager = 215 make_shared<StrictMock<MockStatsPullerManager>>(); 216 ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, 217 pullerManager); 218 219 shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 220 event1->write(1); 221 event1->write(10); 222 event1->init(); 223 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); 224 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 225 226 valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 227 EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 228 EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs); 229 230 shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC); 231 event2->write(1); 232 event2->write(10); 233 event2->init(); 234 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); 235 EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 236 EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs); 237 238 // Next value should create a new bucket. 239 shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC); 240 event3->write(1); 241 event3->write(10); 242 event3->init(); 243 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); 244 EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 245 EXPECT_EQ((uint64_t)bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); 246} 247 248TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { 249 ValueMetric metric; 250 metric.set_id(metricId); 251 metric.set_bucket(ONE_MINUTE); 252 metric.mutable_value_field()->set_field(tagId); 253 metric.mutable_value_field()->add_child()->set_field(2); 254 255 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 256 shared_ptr<MockStatsPullerManager> pullerManager = 257 make_shared<StrictMock<MockStatsPullerManager>>(); 258 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); 259 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 260 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 261 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 262 data->clear(); 263 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 264 event->write(tagId); 265 event->write(120); 266 event->init(); 267 data->push_back(event); 268 return true; 269 })); 270 ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs, 271 pullerManager); 272 273 vector<shared_ptr<LogEvent>> allData; 274 allData.clear(); 275 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); 276 event->write(tagId); 277 event->write(100); 278 event->init(); 279 allData.push_back(event); 280 281 valueProducer.onDataPulled(allData); 282 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 283 284 valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 285 EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 286 EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs); 287 EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue); 288 289 allData.clear(); 290 event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 291 event->write(tagId); 292 event->write(150); 293 event->init(); 294 allData.push_back(event); 295 valueProducer.onDataPulled(allData); 296 EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 297 EXPECT_EQ((uint64_t)bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); 298 EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue); 299} 300 301TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { 302 ValueMetric metric; 303 metric.set_id(metricId); 304 metric.set_bucket(ONE_MINUTE); 305 metric.mutable_value_field()->set_field(tagId); 306 metric.mutable_value_field()->add_child()->set_field(2); 307 308 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 309 shared_ptr<MockStatsPullerManager> pullerManager = 310 make_shared<StrictMock<MockStatsPullerManager>>(); 311 312 ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, 313 pullerManager); 314 315 shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 316 event1->write(1); 317 event1->write(10); 318 event1->init(); 319 shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); 320 event2->write(1); 321 event2->write(20); 322 event2->init(); 323 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); 324 // has one slice 325 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 326 ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 327 EXPECT_EQ(10, curInterval.sum); 328 329 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); 330 331 // has one slice 332 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 333 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 334 EXPECT_EQ(30, curInterval.sum); 335 336 valueProducer.flushIfNeededLocked(bucket3StartTimeNs); 337 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 338 EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); 339 EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue); 340} 341 342TEST(ValueMetricProducerTest, TestAnomalyDetection) { 343 Alert alert; 344 alert.set_id(101); 345 alert.set_metric_id(metricId); 346 alert.set_trigger_if_sum_gt(130); 347 alert.set_num_buckets(2); 348 const int32_t refPeriodSec = 3; 349 alert.set_refractory_period_secs(refPeriodSec); 350 351 ValueMetric metric; 352 metric.set_id(metricId); 353 metric.set_bucket(ONE_MINUTE); 354 metric.mutable_value_field()->set_field(tagId); 355 metric.mutable_value_field()->add_child()->set_field(2); 356 357 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 358 ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 359 -1 /*not pulled*/, bucketStartTimeNs); 360 sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert); 361 362 363 shared_ptr<LogEvent> event1 364 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC); 365 event1->write(161); 366 event1->write(10); // value of interest 367 event1->init(); 368 shared_ptr<LogEvent> event2 369 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC); 370 event2->write(162); 371 event2->write(20); // value of interest 372 event2->init(); 373 shared_ptr<LogEvent> event3 374 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC); 375 event3->write(163); 376 event3->write(130); // value of interest 377 event3->init(); 378 shared_ptr<LogEvent> event4 379 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC); 380 event4->write(35); 381 event4->write(1); // value of interest 382 event4->init(); 383 shared_ptr<LogEvent> event5 384 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC); 385 event5->write(45); 386 event5->write(150); // value of interest 387 event5->init(); 388 shared_ptr<LogEvent> event6 389 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC); 390 event6->write(25); 391 event6->write(160); // value of interest 392 event6->init(); 393 394 // Two events in bucket #0. 395 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); 396 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); 397 // Value sum == 30 <= 130. 398 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); 399 400 // One event in bucket #2. No alarm as bucket #0 is trashed out. 401 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); 402 // Value sum == 130 <= 130. 403 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); 404 405 // Three events in bucket #3. 406 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); 407 // Anomaly at event 4 since Value sum == 131 > 130! 408 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 409 event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); 410 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5); 411 // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. 412 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 413 event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); 414 415 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6); 416 // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. 417 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 418 event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); 419} 420 421} // namespace statsd 422} // namespace os 423} // namespace android 424#else 425GTEST_LOG_(INFO) << "This test does nothing.\n"; 426#endif 427