blob: 6b8893e5973f9c3b7df34c88b0dbb4b05efeaf09 [file] [log] [blame]
Yao Chen5154a372017-10-30 22:57:06 -07001// 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
Yao Chend5aa01b32017-12-19 16:46:36 -080015#include "src/metrics/duration_helper/OringDurationTracker.h"
Yao Chen5154a372017-10-30 22:57:06 -070016#include "src/condition/ConditionWizard.h"
Yangster-mac94e197c2018-01-02 16:03:03 -080017#include "metrics_test_helper.h"
18#include "tests/statsd_test_util.h"
Yao Chen5154a372017-10-30 22:57:06 -070019
Yao Chen93fe3a32017-11-02 13:52:59 -070020#include <gmock/gmock.h>
21#include <gtest/gtest.h>
Yao Chen5154a372017-10-30 22:57:06 -070022#include <stdio.h>
23#include <set>
24#include <unordered_map>
25#include <vector>
26
Yao Chen5154a372017-10-30 22:57:06 -070027using namespace testing;
28using android::sp;
29using std::set;
30using std::unordered_map;
31using std::vector;
32
33#ifdef __ANDROID__
Yao Chen93fe3a32017-11-02 13:52:59 -070034namespace android {
35namespace os {
36namespace statsd {
Yao Chen5154a372017-10-30 22:57:06 -070037
Yangster-mac94e197c2018-01-02 16:03:03 -080038const ConfigKey kConfigKey(0, 12345);
Yangster-mac20877162017-12-22 17:19:39 -080039const int TagId = 1;
Yangster-mac94e197c2018-01-02 16:03:03 -080040const int64_t metricId = 123;
Yangster-mac20877162017-12-22 17:19:39 -080041const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
Yao Chend5aa01b32017-12-19 16:46:36 -080042
Yangster-mac20877162017-12-22 17:19:39 -080043const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
44const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
45const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
Yao Chenb3561512017-11-21 18:07:17 -080046
Yao Chen5154a372017-10-30 22:57:06 -070047TEST(OringDurationTrackerTest, TestDurationOverlap) {
48 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
49
Yao Chenf60e0ba2017-11-29 15:06:41 -080050 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yao Chen5154a372017-10-30 22:57:06 -070051
52 uint64_t bucketStartTimeNs = 10000000000;
53 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
54 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
Yangster-mace2cd6d52017-11-09 20:38:30 -080055 uint64_t durationTimeNs = 2 * 1000;
Yao Chen5154a372017-10-30 22:57:06 -070056
Yangster-mac94e197c2018-01-02 16:03:03 -080057 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
Yao Chend59a6582018-01-08 11:17:11 -080058 bucketStartTimeNs, bucketSizeNs, false, {});
Yao Chen5154a372017-10-30 22:57:06 -070059
Yao Chend59a6582018-01-08 11:17:11 -080060 tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -080061 EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
Yao Chend59a6582018-01-08 11:17:11 -080062 tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
Yangster-mace2cd6d52017-11-09 20:38:30 -080063 EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
Yao Chen5154a372017-10-30 22:57:06 -070064
Yao Chend5aa01b32017-12-19 16:46:36 -080065 tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
Yao Chenf60e0ba2017-11-29 15:06:41 -080066 tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
67 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
68
69 EXPECT_EQ(1u, buckets[eventKey].size());
70 EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration);
Yao Chen5154a372017-10-30 22:57:06 -070071}
72
Yao Chen0ea19902017-11-15 15:44:45 -080073TEST(OringDurationTrackerTest, TestDurationNested) {
74 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
75
Yao Chenf60e0ba2017-11-29 15:06:41 -080076 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yao Chen0ea19902017-11-15 15:44:45 -080077
78 uint64_t bucketStartTimeNs = 10000000000;
79 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
80 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
81
Yangster-mac94e197c2018-01-02 16:03:03 -080082 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
Yao Chend59a6582018-01-08 11:17:11 -080083 bucketSizeNs, false, {});
Yao Chen0ea19902017-11-15 15:44:45 -080084
Yao Chend59a6582018-01-08 11:17:11 -080085 tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
86 tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
Yao Chen0ea19902017-11-15 15:44:45 -080087
Yao Chend5aa01b32017-12-19 16:46:36 -080088 tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false);
89 tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
Yao Chen0ea19902017-11-15 15:44:45 -080090
Yao Chenf60e0ba2017-11-29 15:06:41 -080091 tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
92 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
93 EXPECT_EQ(1u, buckets[eventKey].size());
94 EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration);
Yangster-mace2cd6d52017-11-09 20:38:30 -080095}
96
97TEST(OringDurationTrackerTest, TestStopAll) {
98 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
99
Yao Chenf60e0ba2017-11-29 15:06:41 -0800100 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800101
102 uint64_t bucketStartTimeNs = 10000000000;
103 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
104 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
105
Yangster-mac94e197c2018-01-02 16:03:03 -0800106 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
Yao Chend59a6582018-01-08 11:17:11 -0800107 bucketSizeNs, false, {});
Yangster-mace2cd6d52017-11-09 20:38:30 -0800108
Yao Chend59a6582018-01-08 11:17:11 -0800109 tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
110 tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
Yangster-mace2cd6d52017-11-09 20:38:30 -0800111
112 tracker.noteStopAll(eventStartTimeNs + 2003);
113
Yao Chenf60e0ba2017-11-29 15:06:41 -0800114 tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
115 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
116 EXPECT_EQ(1u, buckets[eventKey].size());
117 EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800118}
119
120TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
121 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
122
Yao Chenf60e0ba2017-11-29 15:06:41 -0800123 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800124
125 uint64_t bucketStartTimeNs = 10000000000;
126 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
127 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
128 uint64_t durationTimeNs = 2 * 1000;
129
Yangster-mac94e197c2018-01-02 16:03:03 -0800130 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
Yao Chend59a6582018-01-08 11:17:11 -0800131 bucketSizeNs, false, {});
Yangster-mace2cd6d52017-11-09 20:38:30 -0800132
Yao Chend59a6582018-01-08 11:17:11 -0800133 tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800134 EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
Yao Chenf60e0ba2017-11-29 15:06:41 -0800135 tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
Yao Chend59a6582018-01-08 11:17:11 -0800136 tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800137 EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
138
Yao Chenf60e0ba2017-11-29 15:06:41 -0800139 EXPECT_EQ(2u, buckets[eventKey].size());
140 EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
141 EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800142
Yao Chend5aa01b32017-12-19 16:46:36 -0800143 tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 10, false);
144 tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false);
Yao Chenf60e0ba2017-11-29 15:06:41 -0800145 tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
146 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
147 EXPECT_EQ(2u, buckets[eventKey].size());
148 EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
149 EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
Yao Chen0ea19902017-11-15 15:44:45 -0800150}
151
Yao Chen5154a372017-10-30 22:57:06 -0700152TEST(OringDurationTrackerTest, TestDurationConditionChange) {
153 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
154
155 ConditionKey key1;
Yangster-mac94e197c2018-01-02 16:03:03 -0800156 key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
Yao Chen5154a372017-10-30 22:57:06 -0700157
158 EXPECT_CALL(*wizard, query(_, key1)) // #4
159 .WillOnce(Return(ConditionState::kFalse));
160
Yao Chenf60e0ba2017-11-29 15:06:41 -0800161 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yao Chen5154a372017-10-30 22:57:06 -0700162
163 uint64_t bucketStartTimeNs = 10000000000;
164 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
165 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800166 uint64_t durationTimeNs = 2 * 1000;
Yao Chen5154a372017-10-30 22:57:06 -0700167
Yangster-mac94e197c2018-01-02 16:03:03 -0800168 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
Yao Chend59a6582018-01-08 11:17:11 -0800169 bucketStartTimeNs, bucketSizeNs, true, {});
Yao Chen5154a372017-10-30 22:57:06 -0700170
Yao Chend5aa01b32017-12-19 16:46:36 -0800171 tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
Yao Chen5154a372017-10-30 22:57:06 -0700172
Yao Chen09294ef2017-11-25 19:54:01 -0800173 tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
174
Yao Chend5aa01b32017-12-19 16:46:36 -0800175 tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
Yao Chen09294ef2017-11-25 19:54:01 -0800176
Yao Chenf60e0ba2017-11-29 15:06:41 -0800177 tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
178 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
179 EXPECT_EQ(1u, buckets[eventKey].size());
180 EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration);
Yao Chen09294ef2017-11-25 19:54:01 -0800181}
182
183TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
184 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
185
186 ConditionKey key1;
Yangster-mac94e197c2018-01-02 16:03:03 -0800187 key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
Yao Chen09294ef2017-11-25 19:54:01 -0800188
189 EXPECT_CALL(*wizard, query(_, key1))
190 .Times(2)
191 .WillOnce(Return(ConditionState::kFalse))
192 .WillOnce(Return(ConditionState::kTrue));
193
Yao Chenf60e0ba2017-11-29 15:06:41 -0800194 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yao Chen09294ef2017-11-25 19:54:01 -0800195
196 uint64_t bucketStartTimeNs = 10000000000;
197 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
198 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
199 uint64_t durationTimeNs = 2 * 1000;
200
Yangster-mac94e197c2018-01-02 16:03:03 -0800201 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
Yao Chend59a6582018-01-08 11:17:11 -0800202 bucketStartTimeNs, bucketSizeNs, true, {});
Yao Chen09294ef2017-11-25 19:54:01 -0800203
Yao Chend5aa01b32017-12-19 16:46:36 -0800204 tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
Yao Chen09294ef2017-11-25 19:54:01 -0800205 // condition to false; record duration 5n
206 tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
207 // condition to true.
208 tracker.onSlicedConditionMayChange(eventStartTimeNs + 1000);
209 // 2nd duration: 1000ns
Yao Chend5aa01b32017-12-19 16:46:36 -0800210 tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
Yao Chen09294ef2017-11-25 19:54:01 -0800211
Yao Chenf60e0ba2017-11-29 15:06:41 -0800212 tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
213 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
214 EXPECT_EQ(1u, buckets[eventKey].size());
215 EXPECT_EQ(1005ULL, buckets[eventKey][0].mDuration);
Yao Chen5154a372017-10-30 22:57:06 -0700216}
Yao Chen0ea19902017-11-15 15:44:45 -0800217
218TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
219 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
220
221 ConditionKey key1;
Yangster-mac94e197c2018-01-02 16:03:03 -0800222 key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
Yao Chen0ea19902017-11-15 15:44:45 -0800223
224 EXPECT_CALL(*wizard, query(_, key1)) // #4
225 .WillOnce(Return(ConditionState::kFalse));
226
Yao Chenf60e0ba2017-11-29 15:06:41 -0800227 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yao Chen0ea19902017-11-15 15:44:45 -0800228
229 uint64_t bucketStartTimeNs = 10000000000;
230 uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
231 uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
232
Yangster-mac94e197c2018-01-02 16:03:03 -0800233 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
Yao Chend59a6582018-01-08 11:17:11 -0800234 bucketSizeNs, true, {});
Yao Chen0ea19902017-11-15 15:44:45 -0800235
Yao Chend5aa01b32017-12-19 16:46:36 -0800236 tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
237 tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
Yao Chen0ea19902017-11-15 15:44:45 -0800238
Yao Chend5aa01b32017-12-19 16:46:36 -0800239 tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false);
Yao Chen0ea19902017-11-15 15:44:45 -0800240
241 tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
242
Yao Chend5aa01b32017-12-19 16:46:36 -0800243 tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
Yao Chen0ea19902017-11-15 15:44:45 -0800244
Yao Chenf60e0ba2017-11-29 15:06:41 -0800245 tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
246 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
247 EXPECT_EQ(1u, buckets[eventKey].size());
248 EXPECT_EQ(15ULL, buckets[eventKey][0].mDuration);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800249}
250
251TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
252 Alert alert;
Yangster-mac94e197c2018-01-02 16:03:03 -0800253 alert.set_id(101);
254 alert.set_metric_id(1);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800255 alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
Yangster-maca7fb12d2018-01-03 17:17:20 -0800256 alert.set_num_buckets(2);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800257 alert.set_refractory_period_secs(1);
258
Yao Chenf60e0ba2017-11-29 15:06:41 -0800259 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800260 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
Yao Chend59a6582018-01-08 11:17:11 -0800261
Yangster-mace2cd6d52017-11-09 20:38:30 -0800262 uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
263 uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
264 uint64_t bucketSizeNs = 30 * NS_PER_SEC;
265
Bookatz857aaa52017-12-19 15:29:06 -0800266 sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
Yangster-mac94e197c2018-01-02 16:03:03 -0800267 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
Yao Chend59a6582018-01-08 11:17:11 -0800268 bucketSizeNs, true, {anomalyTracker});
Yangster-mace2cd6d52017-11-09 20:38:30 -0800269
270 // Nothing in the past bucket.
Yao Chend59a6582018-01-08 11:17:11 -0800271 tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800272 EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
273 tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
274
Yao Chend5aa01b32017-12-19 16:46:36 -0800275 tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false);
Yao Chenf60e0ba2017-11-29 15:06:41 -0800276 EXPECT_EQ(0u, buckets[eventKey].size());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800277
278 uint64_t event1StartTimeNs = eventStartTimeNs + 10;
Yao Chend59a6582018-01-08 11:17:11 -0800279 tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800280 // No past buckets. The anomaly will happen in bucket #0.
281 EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
282 tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
283
284 uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
Yao Chenf60e0ba2017-11-29 15:06:41 -0800285 tracker.flushIfNeeded(event1StopTimeNs, &buckets);
Yao Chend5aa01b32017-12-19 16:46:36 -0800286 tracker.noteStop(kEventKey1, event1StopTimeNs, false);
Yao Chenf60e0ba2017-11-29 15:06:41 -0800287
288 EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
289 EXPECT_EQ(1u, buckets[eventKey].size());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800290 EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
Yao Chenf60e0ba2017-11-29 15:06:41 -0800291 buckets[eventKey][0].mDuration);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800292
293 const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
294 const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
295
296 // One past buckets. The anomaly will happen in bucket #1.
297 uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
Yao Chend59a6582018-01-08 11:17:11 -0800298 tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800299 EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
300 bucket1Duration),
301 tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
Yao Chend5aa01b32017-12-19 16:46:36 -0800302 tracker.noteStop(kEventKey1, event2StartTimeNs + 1, false);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800303
304 // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
305 // bucket #2.
306 uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
Yao Chend59a6582018-01-08 11:17:11 -0800307 tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800308 EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
309 tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
310}
311
Bookatz1bf94382018-01-04 11:43:20 -0800312TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
Yangster-mace2cd6d52017-11-09 20:38:30 -0800313 Alert alert;
Yangster-mac94e197c2018-01-02 16:03:03 -0800314 alert.set_id(101);
315 alert.set_metric_id(1);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800316 alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
Yangster-maca7fb12d2018-01-03 17:17:20 -0800317 alert.set_num_buckets(2);
Bookatz1bf94382018-01-04 11:43:20 -0800318 const int32_t refPeriodSec = 45;
319 alert.set_refractory_period_secs(refPeriodSec);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800320
Yao Chenf60e0ba2017-11-29 15:06:41 -0800321 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800322 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
Yao Chend59a6582018-01-08 11:17:11 -0800323
Yangster-mace2cd6d52017-11-09 20:38:30 -0800324 uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
325 uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
326 uint64_t bucketSizeNs = 30 * NS_PER_SEC;
327
Bookatz857aaa52017-12-19 15:29:06 -0800328 sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
Yangster-mac94e197c2018-01-02 16:03:03 -0800329 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
Yao Chend59a6582018-01-08 11:17:11 -0800330 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
Yangster-mace2cd6d52017-11-09 20:38:30 -0800331
Bookatz1bf94382018-01-04 11:43:20 -0800332 tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
333 tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
334 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800335 EXPECT_TRUE(tracker.mStarted.empty());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800336 EXPECT_EQ(10LL, tracker.mDuration);
337
338 EXPECT_EQ(0u, tracker.mStarted.size());
339
Bookatz1bf94382018-01-04 11:43:20 -0800340 tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800341 EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
342 EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
343 (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
Bookatz1bf94382018-01-04 11:43:20 -0800344 // The alarm is set to fire at 51s, and when it does, an anomaly would be declared. However,
345 // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails
346 // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s.
Yao Chenf60e0ba2017-11-29 15:06:41 -0800347 tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
Bookatz1bf94382018-01-04 11:43:20 -0800348 tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 25, false);
Yao Chend5aa01b32017-12-19 16:46:36 -0800349 EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs));
Bookatz1bf94382018-01-04 11:43:20 -0800350 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey),
351 (eventStartTimeNs + 2 * bucketSizeNs + 25) / NS_PER_SEC + refPeriodSec);
352}
353
354TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
355 Alert alert;
356 alert.set_id(101);
357 alert.set_metric_id(1);
358 alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
359 alert.set_num_buckets(2);
360 const int32_t refPeriodSec = 45;
361 alert.set_refractory_period_secs(refPeriodSec);
362
363 unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
364 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
365 ConditionKey conkey;
366 conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
367 uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
368 uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
369 uint64_t bucketSizeNs = 30 * NS_PER_SEC;
370
371 sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
372 OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
373 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
374
375 tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
376 EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
377 sp<const AnomalyAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
378 EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
379 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
380
381 tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later)
382 EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
383 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
384
385 tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
386 EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
387 alarm = anomalyTracker->mAlarms.begin()->second;
388 EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
389 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
390
391 tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
392 EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
393 alarm = anomalyTracker->mAlarms.begin()->second;
394 EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
395 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
396
397 tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1
398 EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
399 alarm = anomalyTracker->mAlarms.begin()->second;
400 EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
401 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
402
403 // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
404 std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> firedAlarms({alarm});
405 anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
406 EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
407 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
408
409 tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2
410 EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
411 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
Yao Chen0ea19902017-11-15 15:44:45 -0800412}
413
Yao Chen93fe3a32017-11-02 13:52:59 -0700414} // namespace statsd
415} // namespace os
416} // namespace android
Yao Chen5154a372017-10-30 22:57:06 -0700417#else
418GTEST_LOG_(INFO) << "This test does nothing.\n";
419#endif