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