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
164 countProducer.onConditionChanged(true, bucketStartTimeNs);
165
166 LogEvent event1(/*uid=*/0, /*pid=*/0);
167 makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1);
168 countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
169
170 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
171
172 countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
173
174 // Upon this match event, the matched event1 is flushed.
175 LogEvent event2(/*uid=*/0, /*pid=*/0);
176 makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1);
177 countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
178 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
179
180 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
181 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
182 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
183 countProducer.mPastBuckets.end());
184
185 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
186 ASSERT_EQ(1UL, buckets.size());
187 const auto& bucketInfo = buckets[0];
188 EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
189 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
190 EXPECT_EQ(1LL, bucketInfo.mCount);
191 }
192
TEST(CountMetricProducerTest,TestEventsWithSlicedCondition)193 TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
194 int64_t bucketStartTimeNs = 10000000000;
195 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
196
197 int tagId = 1;
198 int conditionTagId = 2;
199
200 CountMetric metric;
201 metric.set_id(1);
202 metric.set_bucket(ONE_MINUTE);
203 metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
204 MetricConditionLink* link = metric.add_links();
205 link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
206 buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
207 buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
208
209 LogEvent event1(/*uid=*/0, /*pid=*/0);
210 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
211
212 LogEvent event2(/*uid=*/0, /*pid=*/0);
213 makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222");
214
215 ConditionKey key1;
216 key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
217 getMockedDimensionKey(conditionTagId, 2, "111")};
218
219 ConditionKey key2;
220 key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
221 getMockedDimensionKey(conditionTagId, 2, "222")};
222
223 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
224
225 EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
226
227 EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
228
229 CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
230 {ConditionState::kUnknown}, wizard, protoHash,
231 bucketStartTimeNs, bucketStartTimeNs);
232
233 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
234 countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
235 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
236
237 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
238 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
239 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
240 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
241 countProducer.mPastBuckets.end());
242 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
243 ASSERT_EQ(1UL, buckets.size());
244 const auto& bucketInfo = buckets[0];
245 EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
246 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
247 EXPECT_EQ(1LL, bucketInfo.mCount);
248 }
249
TEST_P(CountMetricProducerTest_PartialBucket,TestSplitInCurrentBucket)250 TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
251 sp<AlarmMonitor> alarmMonitor;
252 int64_t bucketStartTimeNs = 10000000000;
253 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
254 int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
255
256 int tagId = 1;
257 int conditionTagId = 2;
258
259 CountMetric metric;
260 metric.set_id(1);
261 metric.set_bucket(ONE_MINUTE);
262 Alert alert;
263 alert.set_num_buckets(3);
264 alert.set_trigger_if_sum_gt(2);
265
266 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
267
268 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
269 protoHash, bucketStartTimeNs, bucketStartTimeNs);
270
271 sp<AnomalyTracker> anomalyTracker =
272 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
273 EXPECT_TRUE(anomalyTracker != nullptr);
274
275 // Bucket is not flushed yet.
276 LogEvent event1(/*uid=*/0, /*pid=*/0);
277 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
278 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
279 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
280 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
281
282 // App upgrade or boot complete forces bucket flush.
283 // Check that there's a past bucket and the bucket end is not adjusted.
284 switch (GetParam()) {
285 case APP_UPGRADE:
286 countProducer.notifyAppUpgrade(eventTimeNs);
287 break;
288 case BOOT_COMPLETE:
289 countProducer.onStatsdInitCompleted(eventTimeNs);
290 break;
291 }
292 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
293 EXPECT_EQ(bucketStartTimeNs,
294 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
295 EXPECT_EQ(eventTimeNs,
296 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
297 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
298 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
299 // Anomaly tracker only contains full buckets.
300 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
301
302 int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
303 // Next event occurs in same bucket as partial bucket created.
304 LogEvent event2(/*uid=*/0, /*pid=*/0);
305 makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
306 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
307 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
308 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
309 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
310 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
311
312 // Third event in following bucket.
313 LogEvent event3(/*uid=*/0, /*pid=*/0);
314 makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
315 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
316 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
317 EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
318 EXPECT_EQ(1, countProducer.getCurrentBucketNum());
319 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
320 }
321
TEST_P(CountMetricProducerTest_PartialBucket,TestSplitInNextBucket)322 TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
323 int64_t bucketStartTimeNs = 10000000000;
324 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
325 int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
326
327 int tagId = 1;
328 int conditionTagId = 2;
329
330 CountMetric metric;
331 metric.set_id(1);
332 metric.set_bucket(ONE_MINUTE);
333
334 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
335
336 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
337 protoHash, bucketStartTimeNs, bucketStartTimeNs);
338
339 // Bucket is flushed yet.
340 LogEvent event1(/*uid=*/0, /*pid=*/0);
341 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
342 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
343 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
344
345 // App upgrade or boot complete forces bucket flush.
346 // Check that there's a past bucket and the bucket end is not adjusted since the upgrade
347 // occurred after the bucket end time.
348 switch (GetParam()) {
349 case APP_UPGRADE:
350 countProducer.notifyAppUpgrade(eventTimeNs);
351 break;
352 case BOOT_COMPLETE:
353 countProducer.onStatsdInitCompleted(eventTimeNs);
354 break;
355 }
356 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
357 EXPECT_EQ(bucketStartTimeNs,
358 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
359 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
360 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
361 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
362
363 // Next event occurs in same bucket as partial bucket created.
364 LogEvent event2(/*uid=*/0, /*pid=*/0);
365 makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
366 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
367 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
368
369 // Third event in following bucket.
370 LogEvent event3(/*uid=*/0, /*pid=*/0);
371 makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
372 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
373 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
374 EXPECT_EQ((int64_t)eventTimeNs,
375 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
376 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
377 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
378 }
379
TEST(CountMetricProducerTest,TestAnomalyDetectionUnSliced)380 TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
381 sp<AlarmMonitor> alarmMonitor;
382 Alert alert;
383 alert.set_id(11);
384 alert.set_metric_id(1);
385 alert.set_trigger_if_sum_gt(2);
386 alert.set_num_buckets(2);
387 const int32_t refPeriodSec = 1;
388 alert.set_refractory_period_secs(refPeriodSec);
389
390 int64_t bucketStartTimeNs = 10000000000;
391 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
392 int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
393 int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
394
395 CountMetric metric;
396 metric.set_id(1);
397 metric.set_bucket(ONE_MINUTE);
398
399 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
400
401 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
402 wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
403
404 sp<AnomalyTracker> anomalyTracker =
405 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
406
407 int tagId = 1;
408 LogEvent event1(/*uid=*/0, /*pid=*/0);
409 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
410 LogEvent event2(/*uid=*/0, /*pid=*/0);
411 makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
412 LogEvent event3(/*uid=*/0, /*pid=*/0);
413 makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId);
414 LogEvent event4(/*uid=*/0, /*pid=*/0);
415 makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId);
416 LogEvent event5(/*uid=*/0, /*pid=*/0);
417 makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId);
418 LogEvent event6(/*uid=*/0, /*pid=*/0);
419 makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId);
420 LogEvent event7(/*uid=*/0, /*pid=*/0);
421 makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId);
422
423 // Two events in bucket #0.
424 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
425 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
426
427 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
428 EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
429 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
430
431 // One event in bucket #2. No alarm as bucket #0 is trashed out.
432 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
433 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
434 EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
435 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
436
437 // Two events in bucket #3.
438 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
439 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
440 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
441 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
442 EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
443 // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
444 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
445 std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
446
447 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
448 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
449 EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
450 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
451 std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
452 }
453
TEST(CountMetricProducerTest,TestOneWeekTimeUnit)454 TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
455 CountMetric metric;
456 metric.set_id(1);
457 metric.set_bucket(ONE_WEEK);
458
459 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
460
461 int64_t oneDayNs = 24 * 60 * 60 * 1e9;
462 int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
463
464 CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
465 protoHash, oneDayNs, fiveWeeksNs);
466
467 int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
468
469 EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
470 EXPECT_EQ(4, countProducer.mCurrentBucketNum);
471 EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
472 }
473
474 } // namespace statsd
475 } // namespace os
476 } // namespace android
477 #else
478 GTEST_LOG_(INFO) << "This test does nothing.\n";
479 #endif
480