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