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