Home | History | Annotate | Download | only in metrics
      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/duration_helper/OringDurationTracker.h"
     16 #include "src/condition/ConditionWizard.h"
     17 #include "metrics_test_helper.h"
     18 #include "tests/statsd_test_util.h"
     19 
     20 #include <gmock/gmock.h>
     21 #include <gtest/gtest.h>
     22 #include <math.h>
     23 #include <stdio.h>
     24 #include <set>
     25 #include <unordered_map>
     26 #include <vector>
     27 
     28 using namespace testing;
     29 using android::sp;
     30 using std::set;
     31 using std::unordered_map;
     32 using std::vector;
     33 
     34 #ifdef __ANDROID__
     35 namespace android {
     36 namespace os {
     37 namespace statsd {
     38 
     39 const ConfigKey kConfigKey(0, 12345);
     40 const int TagId = 1;
     41 const int64_t metricId = 123;
     42 const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
     43 
     44 const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps");
     45 const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     46 const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
     47 const int64_t bucketSizeNs = 30 * NS_PER_SEC;
     48 
     49 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     50     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
     51 
     52     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     53     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
     54     vector<Matcher> dimensionInCondition;
     55     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     56 
     57     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     58 
     59     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     60     int64_t bucketStartTimeNs = 10000000000;
     61     int64_t bucketNum = 0;
     62     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
     63     int64_t durationTimeNs = 2 * 1000;
     64 
     65     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
     66                                  false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
     67                                  bucketSizeNs, false, false, {});
     68 
     69     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     70     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
     71     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
     72     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
     73 
     74     tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
     75     tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
     76     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
     77 
     78     EXPECT_EQ(1u, buckets[eventKey].size());
     79     EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration);
     80 }
     81 
     82 TEST(OringDurationTrackerTest, TestDurationNested) {
     83     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
     84 
     85     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     86     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
     87     vector<Matcher> dimensionInCondition;
     88     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     89 
     90     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     91 
     92     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     93     int64_t bucketStartTimeNs = 10000000000;
     94     int64_t bucketNum = 0;
     95     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
     96 
     97     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
     98                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
     99                                  bucketSizeNs, false, false, {});
    100 
    101     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
    102     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
    103 
    104     tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false);
    105     tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
    106 
    107     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
    108     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    109     EXPECT_EQ(1u, buckets[eventKey].size());
    110     EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration);
    111 }
    112 
    113 TEST(OringDurationTrackerTest, TestStopAll) {
    114     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    115 
    116     const std::vector<HashableDimensionKey> kConditionKey1 =
    117         {getMockedDimensionKey(TagId, 1, "maps")};
    118     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    119     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    120     vector<Matcher> dimensionInCondition;
    121     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    122 
    123     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    124 
    125     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
    126     int64_t bucketStartTimeNs = 10000000000;
    127     int64_t bucketNum = 0;
    128     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
    129 
    130     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    131                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    132                                  bucketSizeNs, false, false, {});
    133 
    134     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
    135     tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
    136 
    137     tracker.noteStopAll(eventStartTimeNs + 2003);
    138 
    139     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
    140     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    141     EXPECT_EQ(1u, buckets[eventKey].size());
    142     EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration);
    143 }
    144 
    145 TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
    146     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    147 
    148     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    149     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    150     vector<Matcher> dimensionInCondition;
    151     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    152 
    153     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    154 
    155     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
    156     int64_t bucketStartTimeNs = 10000000000;
    157     int64_t bucketNum = 0;
    158     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
    159     int64_t durationTimeNs = 2 * 1000;
    160 
    161     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    162                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    163                                  bucketSizeNs, false, false, {});
    164 
    165     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
    166     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
    167     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
    168     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey());
    169     EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
    170 
    171     EXPECT_EQ(2u, buckets[eventKey].size());
    172     EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
    173     EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
    174 
    175     tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 10, false);
    176     tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false);
    177     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
    178     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    179     EXPECT_EQ(2u, buckets[eventKey].size());
    180     EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
    181     EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
    182 }
    183 
    184 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
    185     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    186 
    187     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    188     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    189     vector<Matcher> dimensionInCondition;
    190     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    191 
    192     ConditionKey key1;
    193     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
    194 
    195     EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))  // #4
    196             .WillOnce(Return(ConditionState::kFalse));
    197 
    198     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    199 
    200     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
    201     int64_t bucketStartTimeNs = 10000000000;
    202     int64_t bucketNum = 0;
    203     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
    204     int64_t durationTimeNs = 2 * 1000;
    205 
    206     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    207                                  false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    208                                  bucketSizeNs, true, false, {});
    209 
    210     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
    211 
    212     tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5);
    213 
    214     tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
    215 
    216     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
    217     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    218     EXPECT_EQ(1u, buckets[eventKey].size());
    219     EXPECT_EQ(5LL, buckets[eventKey][0].mDuration);
    220 }
    221 
    222 TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
    223     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    224 
    225     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    226     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    227     vector<Matcher> dimensionInCondition;
    228     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    229 
    230     ConditionKey key1;
    231     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
    232 
    233     EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))
    234             .Times(2)
    235             .WillOnce(Return(ConditionState::kFalse))
    236             .WillOnce(Return(ConditionState::kTrue));
    237 
    238     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    239 
    240     int64_t bucketStartTimeNs = 10000000000;
    241     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
    242     int64_t bucketNum = 0;
    243     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
    244     int64_t durationTimeNs = 2 * 1000;
    245 
    246     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    247                                  false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    248                                  bucketSizeNs, true, false, {});
    249 
    250     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
    251     // condition to false; record duration 5n
    252     tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5);
    253     // condition to true.
    254     tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 1000);
    255     // 2nd duration: 1000ns
    256     tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
    257 
    258     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
    259     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    260     EXPECT_EQ(1u, buckets[eventKey].size());
    261     EXPECT_EQ(1005LL, buckets[eventKey][0].mDuration);
    262 }
    263 
    264 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
    265     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    266 
    267     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    268     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    269     vector<Matcher> dimensionInCondition;
    270     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    271 
    272     ConditionKey key1;
    273     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
    274 
    275     EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))  // #4
    276             .WillOnce(Return(ConditionState::kFalse));
    277 
    278     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    279 
    280     int64_t bucketStartTimeNs = 10000000000;
    281     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
    282     int64_t bucketNum = 0;
    283     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
    284 
    285     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    286                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    287                                  bucketSizeNs, true, false, {});
    288 
    289     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
    290     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
    291 
    292     tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false);
    293 
    294     tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 15);
    295 
    296     tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
    297 
    298     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
    299     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    300     EXPECT_EQ(1u, buckets[eventKey].size());
    301     EXPECT_EQ(15LL, buckets[eventKey][0].mDuration);
    302 }
    303 
    304 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
    305     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    306 
    307     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    308     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    309     vector<Matcher> dimensionInCondition;
    310     Alert alert;
    311     alert.set_id(101);
    312     alert.set_metric_id(1);
    313     alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
    314     alert.set_num_buckets(2);
    315     alert.set_refractory_period_secs(1);
    316 
    317     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    318     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    319 
    320     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
    321     int64_t bucketNum = 0;
    322     int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
    323 
    324     sp<AlarmMonitor> alarmMonitor;
    325     sp<DurationAnomalyTracker> anomalyTracker =
    326         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
    327     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    328                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    329                                  bucketSizeNs, true, false, {anomalyTracker});
    330 
    331     // Nothing in the past bucket.
    332     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
    333     EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
    334               tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
    335 
    336     tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false);
    337     EXPECT_EQ(0u, buckets[eventKey].size());
    338 
    339     int64_t event1StartTimeNs = eventStartTimeNs + 10;
    340     tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey());
    341     // No past buckets. The anomaly will happen in bucket #0.
    342     EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
    343               tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
    344 
    345     int64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
    346     tracker.flushIfNeeded(event1StopTimeNs, &buckets);
    347     tracker.noteStop(kEventKey1, event1StopTimeNs, false);
    348 
    349     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
    350     EXPECT_EQ(1u, buckets[eventKey].size());
    351     EXPECT_EQ(3LL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
    352               buckets[eventKey][0].mDuration);
    353 
    354     const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
    355     const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
    356 
    357     // One past buckets. The anomaly will happen in bucket #1.
    358     int64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
    359     tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey());
    360     EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
    361                           bucket1Duration),
    362               tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
    363     tracker.noteStop(kEventKey1, event2StartTimeNs + 1, false);
    364 
    365     // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
    366     // bucket #2.
    367     int64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
    368     tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey());
    369     EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
    370               tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
    371 }
    372 
    373 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) {
    374     vector<Matcher> dimensionInCondition;
    375     Alert alert;
    376     alert.set_id(101);
    377     alert.set_metric_id(1);
    378     alert.set_trigger_if_sum_gt(5 * NS_PER_SEC);
    379     alert.set_num_buckets(1);
    380     alert.set_refractory_period_secs(20);
    381 
    382     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
    383     int64_t bucketNum = 0;
    384 
    385     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    386     sp<AlarmMonitor> alarmMonitor;
    387     sp<DurationAnomalyTracker> anomalyTracker =
    388         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
    389     OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1,
    390                                  dimensionInCondition,
    391                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    392                                  bucketSizeNs, true, false, {anomalyTracker});
    393 
    394     int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
    395     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
    396     // Anomaly happens in the bucket #1.
    397     EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC),
    398               tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
    399 
    400     tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 14 * NS_PER_SEC, false);
    401 
    402     EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
    403               anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
    404 
    405     int64_t event2StartTimeNs = bucketStartTimeNs + 22 * NS_PER_SEC;
    406     EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
    407               anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
    408     EXPECT_EQ((long long)(bucketStartTimeNs + 35 * NS_PER_SEC),
    409               tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
    410 }
    411 
    412 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) {
    413     // Test the cases where the refractory period is smaller than the bucket size, longer than
    414     // the bucket size, and longer than 2x of the anomaly detection window.
    415     for (int j = 0; j < 3; j++) {
    416         int64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC;
    417         for (int i = 0; i <= 7; ++i) {
    418             vector<Matcher> dimensionInCondition;
    419             Alert alert;
    420             alert.set_id(101);
    421             alert.set_metric_id(1);
    422             alert.set_trigger_if_sum_gt(thresholdNs);
    423             alert.set_num_buckets(3);
    424             alert.set_refractory_period_secs(
    425                 bucketSizeNs / NS_PER_SEC / 2 + i * bucketSizeNs / NS_PER_SEC);
    426 
    427             int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
    428             int64_t bucketNum = 101;
    429 
    430             sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    431             sp<AlarmMonitor> alarmMonitor;
    432             sp<DurationAnomalyTracker> anomalyTracker =
    433                 new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
    434             OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY,
    435                                          wizard, 1, dimensionInCondition,
    436                                          true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    437                                          bucketSizeNs, true, false, {anomalyTracker});
    438 
    439             int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
    440             tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
    441             EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs),
    442                       tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
    443             int64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC;
    444             tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStopTimeNs, false);
    445 
    446             int64_t refractoryPeriodEndSec =
    447                 anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY);
    448             EXPECT_EQ(eventStopTimeNs / (int64_t)NS_PER_SEC + alert.refractory_period_secs(),
    449                        refractoryPeriodEndSec);
    450 
    451             // Acquire and release a wakelock in the next bucket.
    452             int64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs;
    453             tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey());
    454             int64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC;
    455             tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false);
    456 
    457             // Test the alarm prediction works well when seeing another wakelock start event.
    458             for (int k = 0; k <= 2; ++k) {
    459                 int64_t event3StartTimeNs = event2StopTimeNs + NS_PER_SEC + k * bucketSizeNs;
    460                 int64_t alarmTimestampNs =
    461                     tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs);
    462                 EXPECT_GT(alarmTimestampNs, 0u);
    463                 EXPECT_GE(alarmTimestampNs, event3StartTimeNs);
    464                 EXPECT_GE(alarmTimestampNs, refractoryPeriodEndSec *(int64_t) NS_PER_SEC);
    465             }
    466         }
    467     }
    468 }
    469 
    470 TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
    471     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    472 
    473     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    474     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    475     vector<Matcher> dimensionInCondition;
    476     Alert alert;
    477     alert.set_id(101);
    478     alert.set_metric_id(1);
    479     alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
    480     alert.set_num_buckets(2);
    481     const int32_t refPeriodSec = 45;
    482     alert.set_refractory_period_secs(refPeriodSec);
    483 
    484     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    485     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    486 
    487     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
    488     int64_t bucketNum = 0;
    489     int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
    490 
    491     sp<AlarmMonitor> alarmMonitor;
    492     sp<DurationAnomalyTracker> anomalyTracker =
    493         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
    494     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    495                                  true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
    496                                  bucketSizeNs, false, false, {anomalyTracker});
    497 
    498     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
    499     tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
    500     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
    501     EXPECT_TRUE(tracker.mStarted.empty());
    502     EXPECT_EQ(10LL, tracker.mDuration); // 10ns
    503 
    504     EXPECT_EQ(0u, tracker.mStarted.size());
    505 
    506     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
    507     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
    508     EXPECT_EQ((long long)(52ULL * NS_PER_SEC),  // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up
    509               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
    510     // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However,
    511     // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails
    512     // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s.
    513     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
    514     tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 25, false);
    515     EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs));
    516     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey),
    517               std::ceil((eventStartTimeNs + 2 * bucketSizeNs + 25.0) / NS_PER_SEC + refPeriodSec));
    518 }
    519 
    520 TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
    521     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
    522 
    523     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
    524     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
    525     vector<Matcher> dimensionInCondition;
    526     Alert alert;
    527     alert.set_id(101);
    528     alert.set_metric_id(1);
    529     alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
    530     alert.set_num_buckets(2);
    531     const int32_t refPeriodSec = 45;
    532     alert.set_refractory_period_secs(refPeriodSec);
    533 
    534     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
    535     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
    536     ConditionKey conkey;
    537     conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
    538     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
    539     int64_t bucketSizeNs = 30 * NS_PER_SEC;
    540 
    541     sp<AlarmMonitor> alarmMonitor;
    542     sp<DurationAnomalyTracker> anomalyTracker =
    543         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
    544     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
    545                                  true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
    546                                  bucketSizeNs, false, false, {anomalyTracker});
    547 
    548     tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
    549     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
    550     sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
    551     EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
    552     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
    553 
    554     tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later)
    555     EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
    556     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
    557 
    558     tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
    559     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
    560     alarm = anomalyTracker->mAlarms.begin()->second;
    561     EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
    562     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
    563 
    564     tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
    565     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
    566     alarm = anomalyTracker->mAlarms.begin()->second;
    567     EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
    568     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
    569 
    570     tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1
    571     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
    572     alarm = anomalyTracker->mAlarms.begin()->second;
    573     EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
    574     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
    575 
    576     // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
    577     std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
    578     anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
    579     EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
    580     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
    581 
    582     tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2
    583     EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
    584     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
    585 }
    586 
    587 }  // namespace statsd
    588 }  // namespace os
    589 }  // namespace android
    590 #else
    591 GTEST_LOG_(INFO) << "This test does nothing.\n";
    592 #endif