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/CountMetricProducer.h"
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <math.h>
20 #include <stdio.h>
21
22 #include <vector>
23
24 #include "metrics_test_helper.h"
25 #include "src/stats_log_util.h"
26 #include "stats_event.h"
27 #include "tests/statsd_test_util.h"
28
29 using namespace testing;
30 using android::sp;
31 using std::set;
32 using std::unordered_map;
33 using std::vector;
34
35 #ifdef __ANDROID__
36
37 namespace android {
38 namespace os {
39 namespace statsd {
40
41
42 namespace {
43 const ConfigKey kConfigKey(0, 12345);
44 const uint64_t protoHash = 0x1234567890;
45
makeLogEvent(LogEvent * logEvent,int64_t timestampNs,int atomId)46 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
47 AStatsEvent* statsEvent = AStatsEvent_obtain();
48 AStatsEvent_setAtomId(statsEvent, atomId);
49 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
50
51 parseStatsEventToLogEvent(statsEvent, logEvent);
52 }
53
makeLogEvent(LogEvent * logEvent,int64_t timestampNs,int atomId,string uid)54 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
55 AStatsEvent* statsEvent = AStatsEvent_obtain();
56 AStatsEvent_setAtomId(statsEvent, atomId);
57 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
58 AStatsEvent_writeString(statsEvent, uid.c_str());
59
60 parseStatsEventToLogEvent(statsEvent, logEvent);
61 }
62
63 } // namespace
64
65 // Setup for parameterized tests.
66 class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
67
68 INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket,
69 CountMetricProducerTest_PartialBucket,
70 testing::Values(APP_UPGRADE, BOOT_COMPLETE));
71
TEST(CountMetricProducerTest,TestFirstBucket)72 TEST(CountMetricProducerTest, TestFirstBucket) {
73 CountMetric metric;
74 metric.set_id(1);
75 metric.set_bucket(ONE_MINUTE);
76 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
77
78 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
79 wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
80 EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
81 EXPECT_EQ(10, countProducer.mCurrentBucketNum);
82 EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
83 }
84
TEST(CountMetricProducerTest,TestNonDimensionalEvents)85 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
86 int64_t bucketStartTimeNs = 10000000000;
87 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
88 int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
89 int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
90 int tagId = 1;
91
92 CountMetric metric;
93 metric.set_id(1);
94 metric.set_bucket(ONE_MINUTE);
95
96 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
97
98 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
99 wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
100
101 // 2 events in bucket 1.
102 LogEvent event1(/*uid=*/0, /*pid=*/0);
103 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
104 LogEvent event2(/*uid=*/0, /*pid=*/0);
105 makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
106
107 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
108 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
109
110 // Flushes at event #2.
111 countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
112 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
113
114 // Flushes.
115 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
116 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
117 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
118 countProducer.mPastBuckets.end());
119 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
120 ASSERT_EQ(1UL, buckets.size());
121 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
122 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
123 EXPECT_EQ(2LL, buckets[0].mCount);
124
125 // 1 matched event happens in bucket 2.
126 LogEvent event3(/*uid=*/0, /*pid=*/0);
127 makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId);
128
129 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
130
131 countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
132 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
133 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
134 countProducer.mPastBuckets.end());
135 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
136 const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
137 EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
138 EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
139 EXPECT_EQ(1LL, bucketInfo2.mCount);
140
141 // nothing happens in bucket 3. we should not record anything for bucket 3.
142 countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
143 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
144 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
145 countProducer.mPastBuckets.end());
146 const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
147 ASSERT_EQ(2UL, buckets3.size());
148 }
149
TEST(CountMetricProducerTest,TestEventsWithNonSlicedCondition)150 TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
151 int64_t bucketStartTimeNs = 10000000000;
152 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
153
154 CountMetric metric;
155 metric.set_id(1);
156 metric.set_bucket(ONE_MINUTE);
157 metric.set_condition(StringToId("SCREEN_ON"));
158
159 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
160
161 CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
162 protoHash, bucketStartTimeNs, bucketStartTimeNs);
163 assertConditionTimer(countProducer.mConditionTimer, false, 0, 0);
164
165 countProducer.onConditionChanged(true, bucketStartTimeNs);
166 assertConditionTimer(countProducer.mConditionTimer, true, 0, bucketStartTimeNs);
167
168 LogEvent event1(/*uid=*/0, /*pid=*/0);
169 makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1);
170 countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
171
172 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
173
174 countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
175 assertConditionTimer(countProducer.mConditionTimer, false, 2, bucketStartTimeNs + 2);
176
177 // Upon this match event, the matched event1 is flushed.
178 LogEvent event2(/*uid=*/0, /*pid=*/0);
179 makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1);
180 countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
181 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
182
183 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
184 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
185 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
186 countProducer.mPastBuckets.end());
187
188 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
189 ASSERT_EQ(1UL, buckets.size());
190 const auto& bucketInfo = buckets[0];
191 EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
192 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
193 EXPECT_EQ(1LL, bucketInfo.mCount);
194 }
195
TEST(CountMetricProducerTest,TestEventsWithSlicedCondition)196 TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
197 int64_t bucketStartTimeNs = 10000000000;
198 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
199
200 int tagId = 1;
201 int conditionTagId = 2;
202
203 CountMetric metric;
204 metric.set_id(1);
205 metric.set_bucket(ONE_MINUTE);
206 metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
207 MetricConditionLink* link = metric.add_links();
208 link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
209 buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
210 buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
211
212 LogEvent event1(/*uid=*/0, /*pid=*/0);
213 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
214
215 LogEvent event2(/*uid=*/0, /*pid=*/0);
216 makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222");
217
218 ConditionKey key1;
219 key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
220 getMockedDimensionKey(conditionTagId, 2, "111")};
221
222 ConditionKey key2;
223 key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
224 getMockedDimensionKey(conditionTagId, 2, "222")};
225
226 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
227
228 EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
229
230 EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
231
232 CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
233 {ConditionState::kUnknown}, wizard, protoHash,
234 bucketStartTimeNs, bucketStartTimeNs);
235
236 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
237 countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
238 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
239
240 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
241 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
242 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
243 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
244 countProducer.mPastBuckets.end());
245 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
246 ASSERT_EQ(1UL, buckets.size());
247 const auto& bucketInfo = buckets[0];
248 EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
249 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
250 EXPECT_EQ(1LL, bucketInfo.mCount);
251 }
252
TEST_P(CountMetricProducerTest_PartialBucket,TestSplitInCurrentBucket)253 TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
254 sp<AlarmMonitor> alarmMonitor;
255 int64_t bucketStartTimeNs = 10000000000;
256 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
257 int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
258
259 int tagId = 1;
260 int conditionTagId = 2;
261
262 CountMetric metric;
263 metric.set_id(1);
264 metric.set_bucket(ONE_MINUTE);
265 metric.set_split_bucket_for_app_upgrade(true);
266 Alert alert;
267 alert.set_num_buckets(3);
268 alert.set_trigger_if_sum_gt(2);
269
270 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
271
272 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
273 protoHash, bucketStartTimeNs, bucketStartTimeNs);
274
275 sp<AnomalyTracker> anomalyTracker =
276 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
277 EXPECT_TRUE(anomalyTracker != nullptr);
278
279 // Bucket is not flushed yet.
280 LogEvent event1(/*uid=*/0, /*pid=*/0);
281 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
282 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
283 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
284 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
285
286 // App upgrade or boot complete forces bucket flush.
287 // Check that there's a past bucket and the bucket end is not adjusted.
288 switch (GetParam()) {
289 case APP_UPGRADE:
290 countProducer.notifyAppUpgrade(eventTimeNs);
291 break;
292 case BOOT_COMPLETE:
293 countProducer.onStatsdInitCompleted(eventTimeNs);
294 break;
295 }
296 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
297 EXPECT_EQ(bucketStartTimeNs,
298 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
299 EXPECT_EQ(eventTimeNs,
300 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
301 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
302 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
303 // Anomaly tracker only contains full buckets.
304 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
305
306 int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
307 // Next event occurs in same bucket as partial bucket created.
308 LogEvent event2(/*uid=*/0, /*pid=*/0);
309 makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
310 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
311 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
312 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
313 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
314 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
315
316 // Third event in following bucket.
317 LogEvent event3(/*uid=*/0, /*pid=*/0);
318 makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
319 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
320 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
321 EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
322 EXPECT_EQ(1, countProducer.getCurrentBucketNum());
323 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
324 }
325
TEST_P(CountMetricProducerTest_PartialBucket,TestSplitInNextBucket)326 TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
327 int64_t bucketStartTimeNs = 10000000000;
328 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
329 int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
330
331 int tagId = 1;
332 int conditionTagId = 2;
333
334 CountMetric metric;
335 metric.set_id(1);
336 metric.set_bucket(ONE_MINUTE);
337 metric.set_split_bucket_for_app_upgrade(true);
338
339 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
340
341 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
342 protoHash, bucketStartTimeNs, bucketStartTimeNs);
343
344 // Bucket is flushed yet.
345 LogEvent event1(/*uid=*/0, /*pid=*/0);
346 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
347 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
348 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
349
350 // App upgrade or boot complete forces bucket flush.
351 // Check that there's a past bucket and the bucket end is not adjusted since the upgrade
352 // occurred after the bucket end time.
353 switch (GetParam()) {
354 case APP_UPGRADE:
355 countProducer.notifyAppUpgrade(eventTimeNs);
356 break;
357 case BOOT_COMPLETE:
358 countProducer.onStatsdInitCompleted(eventTimeNs);
359 break;
360 }
361 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
362 EXPECT_EQ(bucketStartTimeNs,
363 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
364 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
365 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
366 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
367
368 // Next event occurs in same bucket as partial bucket created.
369 LogEvent event2(/*uid=*/0, /*pid=*/0);
370 makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
371 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
372 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
373
374 // Third event in following bucket.
375 LogEvent event3(/*uid=*/0, /*pid=*/0);
376 makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
377 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
378 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
379 EXPECT_EQ((int64_t)eventTimeNs,
380 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
381 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
382 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
383 }
384
TEST(CountMetricProducerTest,TestSplitOnAppUpgradeDisabled)385 TEST(CountMetricProducerTest, TestSplitOnAppUpgradeDisabled) {
386 sp<AlarmMonitor> alarmMonitor;
387 int64_t bucketStartTimeNs = 10000000000;
388 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
389 int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
390
391 int tagId = 1;
392 int conditionTagId = 2;
393 CountMetric metric;
394 metric.set_id(1);
395 metric.set_bucket(ONE_MINUTE);
396 metric.set_split_bucket_for_app_upgrade(false);
397 Alert alert;
398 alert.set_num_buckets(3);
399 alert.set_trigger_if_sum_gt(2);
400
401 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
402 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
403 protoHash, bucketStartTimeNs, bucketStartTimeNs);
404
405 sp<AnomalyTracker> anomalyTracker =
406 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
407 EXPECT_TRUE(anomalyTracker != nullptr);
408
409 LogEvent event1(/*uid=*/0, /*pid=*/0);
410 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
411 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
412 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
413 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
414
415 // App upgrade event occurs. Make sure no bucket is split.
416 // Check that there's a past bucket and the bucket end is not adjusted.
417 countProducer.notifyAppUpgrade(eventTimeNs);
418
419 ASSERT_EQ(0UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
420 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
421 EXPECT_EQ(bucketStartTimeNs, countProducer.mCurrentBucketStartTimeNs);
422 // Anomaly tracker only contains full buckets.
423 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
424
425 int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
426 // Next event occurs in the first bucket.
427 LogEvent event2(/*uid=*/0, /*pid=*/0);
428 makeLogEvent(&event2, eventTimeNs + 10 * NS_PER_SEC, tagId, /*uid=*/"222");
429 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
430 ASSERT_EQ(0UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
431 EXPECT_EQ(bucketStartTimeNs, countProducer.mCurrentBucketStartTimeNs);
432 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
433 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
434
435 // Third event in following bucket.
436 LogEvent event3(/*uid=*/0, /*pid=*/0);
437 makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
438 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
439 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
440 EXPECT_EQ(bucketStartTimeNs,
441 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
442 EXPECT_EQ(bucketStartTimeNs + 60 * NS_PER_SEC,
443 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
444 EXPECT_EQ(2, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mCount);
445 EXPECT_EQ(bucketStartTimeNs + 60 * NS_PER_SEC, countProducer.mCurrentBucketStartTimeNs);
446 EXPECT_EQ(1, countProducer.getCurrentBucketNum());
447 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
448 }
449
TEST(CountMetricProducerTest,TestAnomalyDetectionUnSliced)450 TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
451 sp<AlarmMonitor> alarmMonitor;
452 Alert alert;
453 alert.set_id(11);
454 alert.set_metric_id(1);
455 alert.set_trigger_if_sum_gt(2);
456 alert.set_num_buckets(2);
457 const int32_t refPeriodSec = 1;
458 alert.set_refractory_period_secs(refPeriodSec);
459
460 int64_t bucketStartTimeNs = 10000000000;
461 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
462 int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
463 int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
464
465 CountMetric metric;
466 metric.set_id(1);
467 metric.set_bucket(ONE_MINUTE);
468
469 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
470
471 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
472 wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
473
474 sp<AnomalyTracker> anomalyTracker =
475 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
476
477 int tagId = 1;
478 LogEvent event1(/*uid=*/0, /*pid=*/0);
479 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
480 LogEvent event2(/*uid=*/0, /*pid=*/0);
481 makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
482 LogEvent event3(/*uid=*/0, /*pid=*/0);
483 makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId);
484 LogEvent event4(/*uid=*/0, /*pid=*/0);
485 makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId);
486 LogEvent event5(/*uid=*/0, /*pid=*/0);
487 makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId);
488 LogEvent event6(/*uid=*/0, /*pid=*/0);
489 makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId);
490 LogEvent event7(/*uid=*/0, /*pid=*/0);
491 makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId);
492
493 // Two events in bucket #0.
494 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
495 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
496
497 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
498 EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
499 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
500
501 // One event in bucket #2. No alarm as bucket #0 is trashed out.
502 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
503 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
504 EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
505 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
506
507 // Two events in bucket #3.
508 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
509 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
510 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
511 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
512 EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
513 // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
514 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
515 std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
516
517 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
518 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
519 EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
520 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
521 std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
522 }
523
TEST(CountMetricProducerTest,TestOneWeekTimeUnit)524 TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
525 CountMetric metric;
526 metric.set_id(1);
527 metric.set_bucket(ONE_WEEK);
528
529 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
530
531 int64_t oneDayNs = 24 * 60 * 60 * 1e9;
532 int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
533
534 CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
535 protoHash, oneDayNs, fiveWeeksNs);
536
537 int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
538
539 EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
540 EXPECT_EQ(4, countProducer.mCurrentBucketNum);
541 EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
542 }
543
544 } // namespace statsd
545 } // namespace os
546 } // namespace android
547 #else
548 GTEST_LOG_(INFO) << "This test does nothing.\n";
549 #endif
550