ValueMetricProducer_test.cpp revision 9369446f0b04945d6674550728ae81196d6fb5c2
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; 47 48/* 49 * Tests pulled atoms with no conditions 50 */ 51TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { 52 ValueMetric metric; 53 metric.set_id(metricId); 54 metric.set_bucket(ONE_MINUTE); 55 metric.mutable_value_field()->set_field(tagId); 56 metric.mutable_value_field()->add_child()->set_field(2); 57 58 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 59 // TODO: pending refactor of StatsPullerManager 60 // For now we still need this so that it doesn't do real pulling. 61 shared_ptr<MockStatsPullerManager> pullerManager = 62 make_shared<StrictMock<MockStatsPullerManager>>(); 63 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); 64 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 65 66 ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 67 tagId, bucketStartTimeNs, pullerManager); 68 69 vector<shared_ptr<LogEvent>> allData; 70 allData.clear(); 71 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 72 event->write(tagId); 73 event->write(11); 74 event->init(); 75 allData.push_back(event); 76 77 valueProducer.onDataPulled(allData); 78 // has one slice 79 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 80 ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 81 // startUpdated:true tainted:0 sum:0 start:11 82 EXPECT_EQ(true, curInterval.startUpdated); 83 EXPECT_EQ(0, curInterval.tainted); 84 EXPECT_EQ(0, curInterval.sum); 85 EXPECT_EQ(11, curInterval.start); 86 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 87 EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue); 88 89 allData.clear(); 90 event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); 91 event->write(tagId); 92 event->write(23); 93 event->init(); 94 allData.push_back(event); 95 valueProducer.onDataPulled(allData); 96 // has one slice 97 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 98 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 99 // tartUpdated:false tainted:0 sum:12 100 EXPECT_EQ(true, curInterval.startUpdated); 101 EXPECT_EQ(0, curInterval.tainted); 102 EXPECT_EQ(0, curInterval.sum); 103 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 104 EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); 105 EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue); 106 107 allData.clear(); 108 event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); 109 event->write(tagId); 110 event->write(36); 111 event->init(); 112 allData.push_back(event); 113 valueProducer.onDataPulled(allData); 114 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 115 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 116 // startUpdated:false tainted:0 sum:12 117 EXPECT_EQ(true, curInterval.startUpdated); 118 EXPECT_EQ(0, curInterval.tainted); 119 EXPECT_EQ(0, curInterval.sum); 120 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 121 EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size()); 122 EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue); 123} 124 125/* 126 * Test pulled event with non sliced condition. 127 */ 128TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { 129 ValueMetric metric; 130 metric.set_id(metricId); 131 metric.set_bucket(ONE_MINUTE); 132 metric.mutable_value_field()->set_field(tagId); 133 metric.mutable_value_field()->add_child()->set_field(2); 134 metric.set_condition(StringToId("SCREEN_ON")); 135 136 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 137 shared_ptr<MockStatsPullerManager> pullerManager = 138 make_shared<StrictMock<MockStatsPullerManager>>(); 139 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); 140 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); 141 142 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 143 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 144 data->clear(); 145 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 146 event->write(tagId); 147 event->write(100); 148 event->init(); 149 data->push_back(event); 150 return true; 151 })) 152 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 153 data->clear(); 154 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); 155 event->write(tagId); 156 event->write(120); 157 event->init(); 158 data->push_back(event); 159 return true; 160 })); 161 162 ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, 163 pullerManager); 164 165 valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); 166 167 // has one slice 168 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 169 ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 170 // startUpdated:false tainted:0 sum:0 start:100 171 EXPECT_EQ(100, curInterval.start); 172 EXPECT_EQ(true, curInterval.startUpdated); 173 EXPECT_EQ(0, curInterval.tainted); 174 EXPECT_EQ(0, curInterval.sum); 175 EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); 176 177 vector<shared_ptr<LogEvent>> allData; 178 allData.clear(); 179 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 180 event->write(1); 181 event->write(110); 182 event->init(); 183 allData.push_back(event); 184 valueProducer.onDataPulled(allData); 185 186 // has one slice 187 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 188 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 189 // startUpdated:false tainted:0 sum:0 start:110 190 EXPECT_EQ(110, curInterval.start); 191 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 192 EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); 193 EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue); 194 195 valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); 196 197 // has one slice 198 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 199 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 200 // startUpdated:false tainted:0 sum:0 start:110 201 EXPECT_EQ(10, curInterval.sum); 202 EXPECT_EQ(false, curInterval.startUpdated); 203} 204 205TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { 206 ValueMetric metric; 207 metric.set_id(metricId); 208 metric.set_bucket(ONE_MINUTE); 209 metric.mutable_value_field()->set_field(tagId); 210 metric.mutable_value_field()->add_child()->set_field(2); 211 212 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 213 shared_ptr<MockStatsPullerManager> pullerManager = 214 make_shared<StrictMock<MockStatsPullerManager>>(); 215 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 shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); 224 event2->write(1); 225 event2->write(20); 226 event2->init(); 227 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); 228 // has one slice 229 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 230 ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 231 EXPECT_EQ(10, curInterval.sum); 232 233 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); 234 235 // has one slice 236 EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); 237 curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; 238 EXPECT_EQ(30, curInterval.sum); 239 240 valueProducer.flushIfNeededLocked(bucket3StartTimeNs); 241 EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); 242 EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); 243 EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue); 244} 245 246TEST(ValueMetricProducerTest, TestAnomalyDetection) { 247 Alert alert; 248 alert.set_id(101); 249 alert.set_metric_id(metricId); 250 alert.set_trigger_if_sum_gt(130); 251 alert.set_num_buckets(2); 252 const int32_t refPeriodSec = 3; 253 alert.set_refractory_period_secs(refPeriodSec); 254 255 ValueMetric metric; 256 metric.set_id(metricId); 257 metric.set_bucket(ONE_MINUTE); 258 metric.mutable_value_field()->set_field(tagId); 259 metric.mutable_value_field()->add_child()->set_field(2); 260 261 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 262 ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 263 -1 /*not pulled*/, bucketStartTimeNs); 264 sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert); 265 266 267 shared_ptr<LogEvent> event1 268 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC); 269 event1->write(161); 270 event1->write(10); // value of interest 271 event1->init(); 272 shared_ptr<LogEvent> event2 273 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC); 274 event2->write(162); 275 event2->write(20); // value of interest 276 event2->init(); 277 shared_ptr<LogEvent> event3 278 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC); 279 event3->write(163); 280 event3->write(130); // value of interest 281 event3->init(); 282 shared_ptr<LogEvent> event4 283 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC); 284 event4->write(35); 285 event4->write(1); // value of interest 286 event4->init(); 287 shared_ptr<LogEvent> event5 288 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC); 289 event5->write(45); 290 event5->write(150); // value of interest 291 event5->init(); 292 shared_ptr<LogEvent> event6 293 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC); 294 event6->write(25); 295 event6->write(160); // value of interest 296 event6->init(); 297 298 // Two events in bucket #0. 299 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); 300 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); 301 // Value sum == 30 <= 130. 302 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); 303 304 // One event in bucket #2. No alarm as bucket #0 is trashed out. 305 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); 306 // Value sum == 130 <= 130. 307 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); 308 309 // Three events in bucket #3. 310 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); 311 // Anomaly at event 4 since Value sum == 131 > 130! 312 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 313 event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); 314 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5); 315 // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. 316 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 317 event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); 318 319 valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6); 320 // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. 321 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 322 event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec); 323} 324 325} // namespace statsd 326} // namespace os 327} // namespace android 328#else 329GTEST_LOG_(INFO) << "This test does nothing.\n"; 330#endif 331