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/anomaly/AnomalyTracker.h"
16 
17 #include <gtest/gtest.h>
18 #include <math.h>
19 #include <stdio.h>
20 
21 #include <vector>
22 
23 #include "tests/statsd_test_util.h"
24 
25 using namespace testing;
26 using android::sp;
27 using std::set;
28 using std::unordered_map;
29 using std::vector;
30 
31 #ifdef __ANDROID__
32 
33 namespace android {
34 namespace os {
35 namespace statsd {
36 
37 const ConfigKey kConfigKey(0, 12345);
38 
getMockMetricDimensionKey(int key,string value)39 MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
40     int pos[] = {key, 0, 0};
41     HashableDimensionKey dim;
42     dim.addValue(FieldValue(Field(1, pos, 0), Value(value)));
43     return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY);
44 }
45 
AddValueToBucket(const std::vector<std::pair<MetricDimensionKey,long>> & key_value_pair_list,std::shared_ptr<DimToValMap> bucket)46 void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
47                       std::shared_ptr<DimToValMap> bucket) {
48     for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
49         (*bucket)[itr->first] += itr->second;
50     }
51 }
52 
MockBucket(const std::vector<std::pair<MetricDimensionKey,long>> & key_value_pair_list)53 std::shared_ptr<DimToValMap> MockBucket(
54         const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
55     std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
56     AddValueToBucket(key_value_pair_list, bucket);
57     return bucket;
58 }
59 
60 // Returns the value, for the given key, in that bucket, or 0 if not present.
getBucketValue(const std::shared_ptr<DimToValMap> & bucket,const MetricDimensionKey & key)61 int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
62                        const MetricDimensionKey& key) {
63     const auto& itr = bucket->find(key);
64     if (itr != bucket->end()) {
65         return itr->second;
66     }
67     return 0;
68 }
69 
70 // Returns true if keys in trueList are detected as anomalies and keys in falseList are not.
detectAnomaliesPass(AnomalyTracker & tracker,const int64_t & bucketNum,const std::shared_ptr<DimToValMap> & currentBucket,const std::set<const MetricDimensionKey> & trueList,const std::set<const MetricDimensionKey> & falseList)71 bool detectAnomaliesPass(AnomalyTracker& tracker,
72                          const int64_t& bucketNum,
73                          const std::shared_ptr<DimToValMap>& currentBucket,
74                          const std::set<const MetricDimensionKey>& trueList,
75                          const std::set<const MetricDimensionKey>& falseList) {
76     for (const MetricDimensionKey& key : trueList) {
77         if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
78             return false;
79         }
80     }
81     for (const MetricDimensionKey& key : falseList) {
82         if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
83             return false;
84         }
85     }
86     return true;
87 }
88 
89 // Calls tracker.detectAndDeclareAnomaly on each key in the bucket.
detectAndDeclareAnomalies(AnomalyTracker & tracker,const int64_t & bucketNum,const std::shared_ptr<DimToValMap> & bucket,const int64_t & eventTimestamp)90 void detectAndDeclareAnomalies(AnomalyTracker& tracker,
91                                const int64_t& bucketNum,
92                                const std::shared_ptr<DimToValMap>& bucket,
93                                const int64_t& eventTimestamp) {
94     for (const auto& kv : *bucket) {
95         tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
96                                         kv.second);
97     }
98 }
99 
100 // Asserts that the refractory time for each key in timestamps is the corresponding
101 // timestamp (in ns) + refractoryPeriodSec.
102 // If a timestamp value is negative, instead asserts that the refractory period is inapplicable
103 // (either non-existant or already past).
checkRefractoryTimes(AnomalyTracker & tracker,const int64_t & currTimestampNs,const int32_t & refractoryPeriodSec,const std::unordered_map<MetricDimensionKey,int64_t> & timestamps)104 void checkRefractoryTimes(AnomalyTracker& tracker,
105                           const int64_t& currTimestampNs,
106                           const int32_t& refractoryPeriodSec,
107                           const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
108     for (const auto& kv : timestamps) {
109         if (kv.second < 0) {
110             // Make sure that, if there is a refractory period, it is already past.
111             EXPECT_LT(tracker.getRefractoryPeriodEndsSec(kv.first) * NS_PER_SEC,
112                     (uint64_t)currTimestampNs)
113                     << "Failure was at currTimestampNs " << currTimestampNs;
114         } else {
115             EXPECT_EQ(tracker.getRefractoryPeriodEndsSec(kv.first),
116                       std::ceil(1.0 * kv.second / NS_PER_SEC) + refractoryPeriodSec)
117                       << "Failure was at currTimestampNs " << currTimestampNs;
118         }
119     }
120 }
121 
TEST(AnomalyTrackerTest,TestConsecutiveBuckets)122 TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
123     const int64_t bucketSizeNs = 30 * NS_PER_SEC;
124     const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
125     Alert alert;
126     alert.set_num_buckets(3);
127     alert.set_refractory_period_secs(refractoryPeriodSec);
128     alert.set_trigger_if_sum_gt(2);
129 
130     AnomalyTracker anomalyTracker(alert, kConfigKey);
131     MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
132     MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
133     MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
134 
135     int64_t eventTimestamp0 = 10 * NS_PER_SEC;
136     int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
137     int64_t eventTimestamp2 = 2 * bucketSizeNs + 12 * NS_PER_SEC;
138     int64_t eventTimestamp3 = 3 * bucketSizeNs + 13 * NS_PER_SEC;
139     int64_t eventTimestamp4 = 4 * bucketSizeNs + 14 * NS_PER_SEC;
140     int64_t eventTimestamp5 = 5 * bucketSizeNs + 5 * NS_PER_SEC;
141     int64_t eventTimestamp6 = 6 * bucketSizeNs + 16 * NS_PER_SEC;
142 
143     std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
144     std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}});
145     std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}});
146     std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}});
147     std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 5}});
148     std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}});
149     std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
150 
151     // Start time with no events.
152     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
153     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
154 
155     // Event from bucket #0 occurs.
156     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 0, bucket0, {}, {keyA, keyB, keyC}));
157     detectAndDeclareAnomalies(anomalyTracker, 0, bucket0, eventTimestamp1);
158     checkRefractoryTimes(anomalyTracker, eventTimestamp0, refractoryPeriodSec,
159             {{keyA, -1}, {keyB, -1}, {keyC, -1}});
160 
161     // Adds past bucket #0
162     anomalyTracker.addPastBucket(bucket0, 0);
163     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
164     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
165     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
166     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
167     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
168 
169     // Event from bucket #1 occurs.
170     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
171     detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1);
172     checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
173             {{keyA, -1}, {keyB, -1}, {keyC, -1}});
174 
175     // Adds past bucket #0 again. The sum does not change.
176     anomalyTracker.addPastBucket(bucket0, 0);
177     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
178     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
179     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
180     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
181     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
182     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
183     detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1 + 1);
184     checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
185             {{keyA, -1}, {keyB, -1}, {keyC, -1}});
186 
187     // Adds past bucket #1.
188     anomalyTracker.addPastBucket(bucket1, 1);
189     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
190     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
191     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
192     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
193     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
194 
195     // Event from bucket #2 occurs. New anomaly on keyB.
196     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
197     detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2);
198     checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
199             {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
200 
201     // Adds past bucket #1 again. Nothing changes.
202     anomalyTracker.addPastBucket(bucket1, 1);
203     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
204     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
205     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
206     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
207     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
208     // Event from bucket #2 occurs (again).
209     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
210     detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2 + 1);
211     checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
212             {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
213 
214     // Adds past bucket #2.
215     anomalyTracker.addPastBucket(bucket2, 2);
216     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
217     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
218     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
219     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
220 
221     // Event from bucket #3 occurs. New anomaly on keyA.
222     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 3, bucket3, {keyA}, {keyB, keyC}));
223     detectAndDeclareAnomalies(anomalyTracker, 3, bucket3, eventTimestamp3);
224     checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
225             {{keyA, eventTimestamp3}, {keyB, eventTimestamp2}, {keyC, -1}});
226 
227     // Adds bucket #3.
228     anomalyTracker.addPastBucket(bucket3, 3L);
229     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
230     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
231     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
232     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
233 
234     // Event from bucket #4 occurs. New anomaly on keyB.
235     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 4, bucket4, {keyB}, {keyA, keyC}));
236     detectAndDeclareAnomalies(anomalyTracker, 4, bucket4, eventTimestamp4);
237     checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
238             {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
239 
240     // Adds bucket #4.
241     anomalyTracker.addPastBucket(bucket4, 4);
242     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
243     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
244     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
245     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
246 
247     // Event from bucket #5 occurs. New anomaly on keyA, which is still in refractory.
248     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 5, bucket5, {keyA, keyB}, {keyC}));
249     detectAndDeclareAnomalies(anomalyTracker, 5, bucket5, eventTimestamp5);
250     checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
251             {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
252 
253     // Adds bucket #5.
254     anomalyTracker.addPastBucket(bucket5, 5);
255     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
256     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
257     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
258     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
259 
260     // Event from bucket #6 occurs. New anomaly on keyA, which is now out of refractory.
261     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 6, bucket6, {keyA, keyB}, {keyC}));
262     detectAndDeclareAnomalies(anomalyTracker, 6, bucket6, eventTimestamp6);
263     checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
264             {{keyA, eventTimestamp6}, {keyB, eventTimestamp4}, {keyC, -1}});
265 }
266 
TEST(AnomalyTrackerTest,TestSparseBuckets)267 TEST(AnomalyTrackerTest, TestSparseBuckets) {
268     const int64_t bucketSizeNs = 30 * NS_PER_SEC;
269     const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
270     Alert alert;
271     alert.set_num_buckets(3);
272     alert.set_refractory_period_secs(refractoryPeriodSec);
273     alert.set_trigger_if_sum_gt(2);
274 
275     AnomalyTracker anomalyTracker(alert, kConfigKey);
276     MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
277     MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
278     MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
279     MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
280     MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
281 
282     std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
283     std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
284     std::shared_ptr<DimToValMap> bucket18 = MockBucket({{keyB, 1}, {keyC, 1}});
285     std::shared_ptr<DimToValMap> bucket20 = MockBucket({{keyB, 3}, {keyC, 1}});
286     std::shared_ptr<DimToValMap> bucket25 = MockBucket({{keyD, 1}});
287     std::shared_ptr<DimToValMap> bucket28 = MockBucket({{keyE, 2}});
288 
289     int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
290     int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
291     int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
292     int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
293     int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
294     int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
295 
296     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
297     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
298     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD}));
299     detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1);
300     checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
301             {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
302 
303     // Add past bucket #9
304     anomalyTracker.addPastBucket(bucket9, 9);
305     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
306     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
307     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
308     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
309     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
310     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD}));
311     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
312     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
313     detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2);
314     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
315     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
316     checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
317             {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
318 
319     // Add past bucket #16
320     anomalyTracker.addPastBucket(bucket16, 16);
321     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
322     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
323     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
324     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD}));
325     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
326     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
327     // Within refractory period.
328     detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3);
329     checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
330             {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
331     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
332     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
333 
334     // Add past bucket #18
335     anomalyTracker.addPastBucket(bucket18, 18);
336     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
337     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
338     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
339     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
340     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
341     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
342     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
343     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
344     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
345     detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4);
346     checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
347             {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
348 
349     // Add bucket #18 again. Nothing changes.
350     anomalyTracker.addPastBucket(bucket18, 18);
351     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
352     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
353     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
354     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
355     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
356     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
357     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
358     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
359     detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1);
360     // Within refractory period.
361     checkRefractoryTimes(anomalyTracker, eventTimestamp4 + 1, refractoryPeriodSec,
362             {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
363 
364     // Add past bucket #20
365     anomalyTracker.addPastBucket(bucket20, 20);
366     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
367     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
368     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL);
369     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
370     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD}));
371     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
372     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
373     detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5);
374     checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
375             {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
376 
377     // Add past bucket #25
378     anomalyTracker.addPastBucket(bucket25, 25);
379     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
380     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
381     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL);
382     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {},
383             {keyA, keyB, keyC, keyD, keyE}));
384     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
385     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
386     detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6);
387     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
388     checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
389             {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
390 
391     // Updates current bucket #28.
392     (*bucket28)[keyE] = 5;
393     EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE},
394             {keyA, keyB, keyC, keyD}));
395     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
396     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
397     detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7);
398     ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
399     checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
400             {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}});
401 }
402 
403 }  // namespace statsd
404 }  // namespace os
405 }  // namespace android
406 #else
407 GTEST_LOG_(INFO) << "This test does nothing.\n";
408 #endif
409