You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
409 lines
20 KiB
409 lines
20 KiB
4 months ago
|
// Copyright (C) 2017 The Android Open Source Project
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
#include "src/anomaly/AnomalyTracker.h"
|
||
|
|
||
|
#include <gtest/gtest.h>
|
||
|
#include <math.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include <vector>
|
||
|
|
||
|
#include "tests/statsd_test_util.h"
|
||
|
|
||
|
using namespace testing;
|
||
|
using android::sp;
|
||
|
using std::set;
|
||
|
using std::unordered_map;
|
||
|
using std::vector;
|
||
|
|
||
|
#ifdef __ANDROID__
|
||
|
|
||
|
namespace android {
|
||
|
namespace os {
|
||
|
namespace statsd {
|
||
|
|
||
|
const ConfigKey kConfigKey(0, 12345);
|
||
|
|
||
|
MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
|
||
|
int pos[] = {key, 0, 0};
|
||
|
HashableDimensionKey dim;
|
||
|
dim.addValue(FieldValue(Field(1, pos, 0), Value(value)));
|
||
|
return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY);
|
||
|
}
|
||
|
|
||
|
void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
|
||
|
std::shared_ptr<DimToValMap> bucket) {
|
||
|
for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
|
||
|
(*bucket)[itr->first] += itr->second;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<DimToValMap> MockBucket(
|
||
|
const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
|
||
|
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
|
||
|
AddValueToBucket(key_value_pair_list, bucket);
|
||
|
return bucket;
|
||
|
}
|
||
|
|
||
|
// Returns the value, for the given key, in that bucket, or 0 if not present.
|
||
|
int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
|
||
|
const MetricDimensionKey& key) {
|
||
|
const auto& itr = bucket->find(key);
|
||
|
if (itr != bucket->end()) {
|
||
|
return itr->second;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Returns true if keys in trueList are detected as anomalies and keys in falseList are not.
|
||
|
bool 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) {
|
||
|
for (const MetricDimensionKey& key : trueList) {
|
||
|
if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
for (const MetricDimensionKey& key : falseList) {
|
||
|
if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Calls tracker.detectAndDeclareAnomaly on each key in the bucket.
|
||
|
void detectAndDeclareAnomalies(AnomalyTracker& tracker,
|
||
|
const int64_t& bucketNum,
|
||
|
const std::shared_ptr<DimToValMap>& bucket,
|
||
|
const int64_t& eventTimestamp) {
|
||
|
for (const auto& kv : *bucket) {
|
||
|
tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
|
||
|
kv.second);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Asserts that the refractory time for each key in timestamps is the corresponding
|
||
|
// timestamp (in ns) + refractoryPeriodSec.
|
||
|
// If a timestamp value is negative, instead asserts that the refractory period is inapplicable
|
||
|
// (either non-existant or already past).
|
||
|
void checkRefractoryTimes(AnomalyTracker& tracker,
|
||
|
const int64_t& currTimestampNs,
|
||
|
const int32_t& refractoryPeriodSec,
|
||
|
const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
|
||
|
for (const auto& kv : timestamps) {
|
||
|
if (kv.second < 0) {
|
||
|
// Make sure that, if there is a refractory period, it is already past.
|
||
|
EXPECT_LT(tracker.getRefractoryPeriodEndsSec(kv.first) * NS_PER_SEC,
|
||
|
(uint64_t)currTimestampNs)
|
||
|
<< "Failure was at currTimestampNs " << currTimestampNs;
|
||
|
} else {
|
||
|
EXPECT_EQ(tracker.getRefractoryPeriodEndsSec(kv.first),
|
||
|
std::ceil(1.0 * kv.second / NS_PER_SEC) + refractoryPeriodSec)
|
||
|
<< "Failure was at currTimestampNs " << currTimestampNs;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
|
||
|
const int64_t bucketSizeNs = 30 * NS_PER_SEC;
|
||
|
const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
|
||
|
Alert alert;
|
||
|
alert.set_num_buckets(3);
|
||
|
alert.set_refractory_period_secs(refractoryPeriodSec);
|
||
|
alert.set_trigger_if_sum_gt(2);
|
||
|
|
||
|
AnomalyTracker anomalyTracker(alert, kConfigKey);
|
||
|
MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
|
||
|
MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
|
||
|
MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
|
||
|
|
||
|
int64_t eventTimestamp0 = 10 * NS_PER_SEC;
|
||
|
int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
|
||
|
int64_t eventTimestamp2 = 2 * bucketSizeNs + 12 * NS_PER_SEC;
|
||
|
int64_t eventTimestamp3 = 3 * bucketSizeNs + 13 * NS_PER_SEC;
|
||
|
int64_t eventTimestamp4 = 4 * bucketSizeNs + 14 * NS_PER_SEC;
|
||
|
int64_t eventTimestamp5 = 5 * bucketSizeNs + 5 * NS_PER_SEC;
|
||
|
int64_t eventTimestamp6 = 6 * bucketSizeNs + 16 * NS_PER_SEC;
|
||
|
|
||
|
std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}});
|
||
|
std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 5}});
|
||
|
std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}});
|
||
|
std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
|
||
|
|
||
|
// Start time with no events.
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
|
||
|
|
||
|
// Event from bucket #0 occurs.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 0, bucket0, {}, {keyA, keyB, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 0, bucket0, eventTimestamp1);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp0, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, -1}, {keyC, -1}});
|
||
|
|
||
|
// Adds past bucket #0
|
||
|
anomalyTracker.addPastBucket(bucket0, 0);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
|
||
|
|
||
|
// Event from bucket #1 occurs.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, -1}, {keyC, -1}});
|
||
|
|
||
|
// Adds past bucket #0 again. The sum does not change.
|
||
|
anomalyTracker.addPastBucket(bucket0, 0);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1 + 1);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, -1}, {keyC, -1}});
|
||
|
|
||
|
// Adds past bucket #1.
|
||
|
anomalyTracker.addPastBucket(bucket1, 1);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
|
||
|
// Event from bucket #2 occurs. New anomaly on keyB.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
|
||
|
|
||
|
// Adds past bucket #1 again. Nothing changes.
|
||
|
anomalyTracker.addPastBucket(bucket1, 1);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
// Event from bucket #2 occurs (again).
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2 + 1);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
|
||
|
|
||
|
// Adds past bucket #2.
|
||
|
anomalyTracker.addPastBucket(bucket2, 2);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
|
||
|
|
||
|
// Event from bucket #3 occurs. New anomaly on keyA.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 3, bucket3, {keyA}, {keyB, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 3, bucket3, eventTimestamp3);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
|
||
|
{{keyA, eventTimestamp3}, {keyB, eventTimestamp2}, {keyC, -1}});
|
||
|
|
||
|
// Adds bucket #3.
|
||
|
anomalyTracker.addPastBucket(bucket3, 3L);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
|
||
|
|
||
|
// Event from bucket #4 occurs. New anomaly on keyB.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 4, bucket4, {keyB}, {keyA, keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 4, bucket4, eventTimestamp4);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
|
||
|
{{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
|
||
|
|
||
|
// Adds bucket #4.
|
||
|
anomalyTracker.addPastBucket(bucket4, 4);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
|
||
|
|
||
|
// Event from bucket #5 occurs. New anomaly on keyA, which is still in refractory.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 5, bucket5, {keyA, keyB}, {keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 5, bucket5, eventTimestamp5);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
|
||
|
{{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
|
||
|
|
||
|
// Adds bucket #5.
|
||
|
anomalyTracker.addPastBucket(bucket5, 5);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
|
||
|
|
||
|
// Event from bucket #6 occurs. New anomaly on keyA, which is now out of refractory.
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 6, bucket6, {keyA, keyB}, {keyC}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 6, bucket6, eventTimestamp6);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
|
||
|
{{keyA, eventTimestamp6}, {keyB, eventTimestamp4}, {keyC, -1}});
|
||
|
}
|
||
|
|
||
|
TEST(AnomalyTrackerTest, TestSparseBuckets) {
|
||
|
const int64_t bucketSizeNs = 30 * NS_PER_SEC;
|
||
|
const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
|
||
|
Alert alert;
|
||
|
alert.set_num_buckets(3);
|
||
|
alert.set_refractory_period_secs(refractoryPeriodSec);
|
||
|
alert.set_trigger_if_sum_gt(2);
|
||
|
|
||
|
AnomalyTracker anomalyTracker(alert, kConfigKey);
|
||
|
MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
|
||
|
MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
|
||
|
MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
|
||
|
MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
|
||
|
MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
|
||
|
|
||
|
std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
|
||
|
std::shared_ptr<DimToValMap> bucket18 = MockBucket({{keyB, 1}, {keyC, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket20 = MockBucket({{keyB, 3}, {keyC, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket25 = MockBucket({{keyD, 1}});
|
||
|
std::shared_ptr<DimToValMap> bucket28 = MockBucket({{keyE, 2}});
|
||
|
|
||
|
int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
|
||
|
int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
|
||
|
int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
|
||
|
int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
|
||
|
int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
|
||
|
int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
|
||
|
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD}));
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
|
||
|
// Add past bucket #9
|
||
|
anomalyTracker.addPastBucket(bucket9, 9);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD}));
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
|
||
|
// Add past bucket #16
|
||
|
anomalyTracker.addPastBucket(bucket16, 16);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD}));
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
|
||
|
// Within refractory period.
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
|
||
|
|
||
|
// Add past bucket #18
|
||
|
anomalyTracker.addPastBucket(bucket18, 18);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
|
||
|
// Add bucket #18 again. Nothing changes.
|
||
|
anomalyTracker.addPastBucket(bucket18, 18);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1);
|
||
|
// Within refractory period.
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp4 + 1, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
|
||
|
// Add past bucket #20
|
||
|
anomalyTracker.addPastBucket(bucket20, 20);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD}));
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
|
||
|
// Add past bucket #25
|
||
|
anomalyTracker.addPastBucket(bucket25, 25);
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
|
||
|
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL);
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {},
|
||
|
{keyA, keyB, keyC, keyD, keyE}));
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
|
||
|
|
||
|
// Updates current bucket #28.
|
||
|
(*bucket28)[keyE] = 5;
|
||
|
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE},
|
||
|
{keyA, keyB, keyC, keyD}));
|
||
|
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7);
|
||
|
ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
|
||
|
checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
|
||
|
{{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}});
|
||
|
}
|
||
|
|
||
|
} // namespace statsd
|
||
|
} // namespace os
|
||
|
} // namespace android
|
||
|
#else
|
||
|
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||
|
#endif
|