• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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