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