Yao Chen | 93fe3a3 | 2017-11-02 13:52:59 -0700 | [diff] [blame] | 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 | |
Yao Chen | 93fe3a3 | 2017-11-02 13:52:59 -0700 | [diff] [blame] | 15 | #include "src/metrics/CountMetricProducer.h" |
| 16 | |
| 17 | #include <gmock/gmock.h> |
| 18 | #include <gtest/gtest.h> |
Bookatz | 6bf9825 | 2018-03-14 10:44:24 -0700 | [diff] [blame] | 19 | #include <math.h> |
Yao Chen | 93fe3a3 | 2017-11-02 13:52:59 -0700 | [diff] [blame] | 20 | #include <stdio.h> |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 21 | |
Yao Chen | 93fe3a3 | 2017-11-02 13:52:59 -0700 | [diff] [blame] | 22 | #include <vector> |
| 23 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 24 | #include "metrics_test_helper.h" |
| 25 | #include "src/stats_log_util.h" |
| 26 | #include "stats_event.h" |
| 27 | #include "tests/statsd_test_util.h" |
| 28 | |
Yao Chen | 93fe3a3 | 2017-11-02 13:52:59 -0700 | [diff] [blame] | 29 | using namespace testing; |
| 30 | using android::sp; |
| 31 | using std::set; |
| 32 | using std::unordered_map; |
| 33 | using std::vector; |
| 34 | |
| 35 | #ifdef __ANDROID__ |
| 36 | |
| 37 | namespace android { |
| 38 | namespace os { |
| 39 | namespace statsd { |
| 40 | |
Yao Chen | b356151 | 2017-11-21 18:07:17 -0800 | [diff] [blame] | 41 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 42 | namespace { |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 43 | const ConfigKey kConfigKey(0, 12345); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 44 | |
| 45 | void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { |
| 46 | AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| 47 | AStatsEvent_setAtomId(statsEvent, atomId); |
| 48 | AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 49 | |
tsaichristine | 8dca82e | 2020-04-07 09:40:03 -0700 | [diff] [blame] | 50 | parseStatsEventToLogEvent(statsEvent, logEvent); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 51 | } |
| 52 | |
| 53 | void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) { |
| 54 | AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| 55 | AStatsEvent_setAtomId(statsEvent, atomId); |
| 56 | AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 57 | AStatsEvent_writeString(statsEvent, uid.c_str()); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 58 | |
tsaichristine | 8dca82e | 2020-04-07 09:40:03 -0700 | [diff] [blame] | 59 | parseStatsEventToLogEvent(statsEvent, logEvent); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | } // namespace |
| 63 | |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 64 | // Setup for parameterized tests. |
| 65 | class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; |
| 66 | |
| 67 | INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket, |
| 68 | CountMetricProducerTest_PartialBucket, |
| 69 | testing::Values(APP_UPGRADE, BOOT_COMPLETE)); |
| 70 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 71 | TEST(CountMetricProducerTest, TestFirstBucket) { |
| 72 | CountMetric metric; |
| 73 | metric.set_id(1); |
| 74 | metric.set_bucket(ONE_MINUTE); |
| 75 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 76 | |
| 77 | CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, |
| 78 | 600 * NS_PER_SEC + NS_PER_SEC / 2); |
| 79 | EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); |
| 80 | EXPECT_EQ(10, countProducer.mCurrentBucketNum); |
| 81 | EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); |
| 82 | } |
| 83 | |
| 84 | TEST(CountMetricProducerTest, TestNonDimensionalEvents) { |
| 85 | int64_t bucketStartTimeNs = 10000000000; |
| 86 | int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; |
| 87 | int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; |
| 88 | int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; |
| 89 | int tagId = 1; |
| 90 | |
| 91 | CountMetric metric; |
| 92 | metric.set_id(1); |
| 93 | metric.set_bucket(ONE_MINUTE); |
| 94 | |
| 95 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 96 | |
| 97 | CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, |
| 98 | bucketStartTimeNs, bucketStartTimeNs); |
| 99 | |
| 100 | // 2 events in bucket 1. |
| 101 | LogEvent event1(/*uid=*/0, /*pid=*/0); |
| 102 | makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); |
| 103 | LogEvent event2(/*uid=*/0, /*pid=*/0); |
| 104 | makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); |
| 105 | |
| 106 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); |
| 107 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); |
| 108 | |
| 109 | // Flushes at event #2. |
| 110 | countProducer.flushIfNeededLocked(bucketStartTimeNs + 2); |
| 111 | EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); |
| 112 | |
| 113 | // Flushes. |
| 114 | countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); |
| 115 | EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); |
| 116 | EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != |
| 117 | countProducer.mPastBuckets.end()); |
| 118 | const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; |
| 119 | EXPECT_EQ(1UL, buckets.size()); |
| 120 | EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); |
| 121 | EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); |
| 122 | EXPECT_EQ(2LL, buckets[0].mCount); |
| 123 | |
| 124 | // 1 matched event happens in bucket 2. |
| 125 | LogEvent event3(/*uid=*/0, /*pid=*/0); |
| 126 | makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId); |
| 127 | |
| 128 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); |
| 129 | |
| 130 | countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); |
| 131 | EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); |
| 132 | EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != |
| 133 | countProducer.mPastBuckets.end()); |
| 134 | EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
| 135 | const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; |
| 136 | EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); |
| 137 | EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); |
| 138 | EXPECT_EQ(1LL, bucketInfo2.mCount); |
| 139 | |
| 140 | // nothing happens in bucket 3. we should not record anything for bucket 3. |
| 141 | countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); |
| 142 | EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); |
| 143 | EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != |
| 144 | countProducer.mPastBuckets.end()); |
| 145 | const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; |
| 146 | EXPECT_EQ(2UL, buckets3.size()); |
| 147 | } |
| 148 | |
| 149 | TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { |
| 150 | int64_t bucketStartTimeNs = 10000000000; |
| 151 | int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; |
| 152 | |
| 153 | CountMetric metric; |
| 154 | metric.set_id(1); |
| 155 | metric.set_bucket(ONE_MINUTE); |
| 156 | metric.set_condition(StringToId("SCREEN_ON")); |
| 157 | |
| 158 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 159 | |
| 160 | CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, |
| 161 | bucketStartTimeNs); |
| 162 | |
| 163 | countProducer.onConditionChanged(true, bucketStartTimeNs); |
| 164 | |
| 165 | LogEvent event1(/*uid=*/0, /*pid=*/0); |
| 166 | makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1); |
| 167 | countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); |
| 168 | |
| 169 | EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); |
| 170 | |
| 171 | countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2); |
| 172 | |
| 173 | // Upon this match event, the matched event1 is flushed. |
| 174 | LogEvent event2(/*uid=*/0, /*pid=*/0); |
| 175 | makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1); |
| 176 | countProducer.onMatchedLogEvent(1 /*matcher index*/, event2); |
| 177 | EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); |
| 178 | |
| 179 | countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); |
| 180 | EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); |
| 181 | EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != |
| 182 | countProducer.mPastBuckets.end()); |
| 183 | |
| 184 | const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; |
| 185 | EXPECT_EQ(1UL, buckets.size()); |
| 186 | const auto& bucketInfo = buckets[0]; |
| 187 | EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); |
| 188 | EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); |
| 189 | EXPECT_EQ(1LL, bucketInfo.mCount); |
| 190 | } |
| 191 | |
| 192 | TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { |
| 193 | int64_t bucketStartTimeNs = 10000000000; |
| 194 | int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; |
| 195 | |
| 196 | int tagId = 1; |
| 197 | int conditionTagId = 2; |
| 198 | |
| 199 | CountMetric metric; |
| 200 | metric.set_id(1); |
| 201 | metric.set_bucket(ONE_MINUTE); |
| 202 | metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); |
| 203 | MetricConditionLink* link = metric.add_links(); |
| 204 | link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); |
| 205 | buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); |
| 206 | buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); |
| 207 | |
| 208 | LogEvent event1(/*uid=*/0, /*pid=*/0); |
| 209 | makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); |
| 210 | |
| 211 | LogEvent event2(/*uid=*/0, /*pid=*/0); |
| 212 | makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222"); |
| 213 | |
| 214 | ConditionKey key1; |
| 215 | key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { |
| 216 | getMockedDimensionKey(conditionTagId, 2, "111")}; |
| 217 | |
| 218 | ConditionKey key2; |
| 219 | key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { |
| 220 | getMockedDimensionKey(conditionTagId, 2, "222")}; |
| 221 | |
| 222 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 223 | EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); |
| 224 | |
| 225 | EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); |
| 226 | |
| 227 | CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, |
| 228 | bucketStartTimeNs, bucketStartTimeNs); |
| 229 | |
| 230 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); |
| 231 | countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); |
| 232 | EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); |
| 233 | |
| 234 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); |
| 235 | countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); |
| 236 | EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); |
| 237 | EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != |
| 238 | countProducer.mPastBuckets.end()); |
| 239 | const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; |
| 240 | EXPECT_EQ(1UL, buckets.size()); |
| 241 | const auto& bucketInfo = buckets[0]; |
| 242 | EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); |
| 243 | EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); |
| 244 | EXPECT_EQ(1LL, bucketInfo.mCount); |
| 245 | } |
| 246 | |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 247 | TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) { |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 248 | sp<AlarmMonitor> alarmMonitor; |
| 249 | int64_t bucketStartTimeNs = 10000000000; |
| 250 | int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 251 | int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 252 | |
| 253 | int tagId = 1; |
| 254 | int conditionTagId = 2; |
| 255 | |
| 256 | CountMetric metric; |
| 257 | metric.set_id(1); |
| 258 | metric.set_bucket(ONE_MINUTE); |
| 259 | Alert alert; |
| 260 | alert.set_num_buckets(3); |
| 261 | alert.set_trigger_if_sum_gt(2); |
| 262 | |
| 263 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 264 | CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, |
| 265 | bucketStartTimeNs, bucketStartTimeNs); |
| 266 | |
| 267 | sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); |
| 268 | EXPECT_TRUE(anomalyTracker != nullptr); |
| 269 | |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 270 | // Bucket is not flushed yet. |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 271 | LogEvent event1(/*uid=*/0, /*pid=*/0); |
| 272 | makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); |
| 273 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); |
| 274 | EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); |
| 275 | EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); |
| 276 | |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 277 | // App upgrade or boot complete forces bucket flush. |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 278 | // Check that there's a past bucket and the bucket end is not adjusted. |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 279 | switch (GetParam()) { |
| 280 | case APP_UPGRADE: |
| 281 | countProducer.notifyAppUpgrade(eventTimeNs); |
| 282 | break; |
| 283 | case BOOT_COMPLETE: |
| 284 | countProducer.onStatsdInitCompleted(eventTimeNs); |
| 285 | break; |
| 286 | } |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 287 | EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 288 | EXPECT_EQ(bucketStartTimeNs, |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 289 | countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 290 | EXPECT_EQ(eventTimeNs, |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 291 | countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 292 | EXPECT_EQ(0, countProducer.getCurrentBucketNum()); |
| 293 | EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 294 | // Anomaly tracker only contains full buckets. |
| 295 | EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); |
| 296 | |
| 297 | int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); |
| 298 | // Next event occurs in same bucket as partial bucket created. |
| 299 | LogEvent event2(/*uid=*/0, /*pid=*/0); |
| 300 | makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); |
| 301 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); |
| 302 | EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 303 | EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); |
| 304 | EXPECT_EQ(0, countProducer.getCurrentBucketNum()); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 305 | EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); |
| 306 | |
| 307 | // Third event in following bucket. |
| 308 | LogEvent event3(/*uid=*/0, /*pid=*/0); |
| 309 | makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); |
| 310 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); |
| 311 | EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
| 312 | EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 313 | EXPECT_EQ(1, countProducer.getCurrentBucketNum()); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 314 | EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); |
| 315 | } |
| 316 | |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 317 | TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) { |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 318 | int64_t bucketStartTimeNs = 10000000000; |
| 319 | int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 320 | int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 321 | |
| 322 | int tagId = 1; |
| 323 | int conditionTagId = 2; |
| 324 | |
| 325 | CountMetric metric; |
| 326 | metric.set_id(1); |
| 327 | metric.set_bucket(ONE_MINUTE); |
| 328 | |
| 329 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 330 | CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, |
| 331 | bucketStartTimeNs, bucketStartTimeNs); |
| 332 | |
| 333 | // Bucket is flushed yet. |
| 334 | LogEvent event1(/*uid=*/0, /*pid=*/0); |
| 335 | makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); |
| 336 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); |
| 337 | EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); |
| 338 | |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 339 | // App upgrade or boot complete forces bucket flush. |
| 340 | // Check that there's a past bucket and the bucket end is not adjusted since the upgrade |
| 341 | // occurred after the bucket end time. |
| 342 | switch (GetParam()) { |
| 343 | case APP_UPGRADE: |
| 344 | countProducer.notifyAppUpgrade(eventTimeNs); |
| 345 | break; |
| 346 | case BOOT_COMPLETE: |
| 347 | countProducer.onStatsdInitCompleted(eventTimeNs); |
| 348 | break; |
| 349 | } |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 350 | EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 351 | EXPECT_EQ(bucketStartTimeNs, |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 352 | countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); |
| 353 | EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, |
| 354 | countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 355 | EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 356 | |
| 357 | // Next event occurs in same bucket as partial bucket created. |
| 358 | LogEvent event2(/*uid=*/0, /*pid=*/0); |
| 359 | makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); |
| 360 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); |
| 361 | EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
| 362 | |
| 363 | // Third event in following bucket. |
| 364 | LogEvent event3(/*uid=*/0, /*pid=*/0); |
| 365 | makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); |
| 366 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); |
| 367 | EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); |
Tej Singh | e678cb7 | 2020-04-14 16:23:30 -0700 | [diff] [blame] | 368 | EXPECT_EQ((int64_t)eventTimeNs, |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 369 | countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); |
| 370 | EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, |
| 371 | countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); |
| 372 | } |
| 373 | |
| 374 | TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { |
| 375 | sp<AlarmMonitor> alarmMonitor; |
| 376 | Alert alert; |
| 377 | alert.set_id(11); |
| 378 | alert.set_metric_id(1); |
| 379 | alert.set_trigger_if_sum_gt(2); |
| 380 | alert.set_num_buckets(2); |
| 381 | const int32_t refPeriodSec = 1; |
| 382 | alert.set_refractory_period_secs(refPeriodSec); |
| 383 | |
| 384 | int64_t bucketStartTimeNs = 10000000000; |
| 385 | int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; |
| 386 | int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; |
| 387 | int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; |
| 388 | |
| 389 | CountMetric metric; |
| 390 | metric.set_id(1); |
| 391 | metric.set_bucket(ONE_MINUTE); |
| 392 | |
| 393 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 394 | CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, |
| 395 | bucketStartTimeNs, bucketStartTimeNs); |
| 396 | |
| 397 | sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); |
| 398 | |
| 399 | int tagId = 1; |
| 400 | LogEvent event1(/*uid=*/0, /*pid=*/0); |
| 401 | makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); |
| 402 | LogEvent event2(/*uid=*/0, /*pid=*/0); |
| 403 | makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); |
| 404 | LogEvent event3(/*uid=*/0, /*pid=*/0); |
| 405 | makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId); |
| 406 | LogEvent event4(/*uid=*/0, /*pid=*/0); |
| 407 | makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId); |
| 408 | LogEvent event5(/*uid=*/0, /*pid=*/0); |
| 409 | makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId); |
| 410 | LogEvent event6(/*uid=*/0, /*pid=*/0); |
| 411 | makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId); |
| 412 | LogEvent event7(/*uid=*/0, /*pid=*/0); |
| 413 | makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId); |
| 414 | |
| 415 | // Two events in bucket #0. |
| 416 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); |
| 417 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); |
| 418 | |
| 419 | EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); |
| 420 | EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); |
| 421 | EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); |
| 422 | |
| 423 | // One event in bucket #2. No alarm as bucket #0 is trashed out. |
| 424 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); |
| 425 | EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); |
| 426 | EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); |
| 427 | EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); |
| 428 | |
| 429 | // Two events in bucket #3. |
| 430 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); |
| 431 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); |
| 432 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); |
| 433 | EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); |
| 434 | EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); |
| 435 | // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 |
| 436 | EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), |
| 437 | std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); |
| 438 | |
| 439 | countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); |
| 440 | EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); |
| 441 | EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); |
| 442 | EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), |
| 443 | std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); |
| 444 | } |
Yangster-mac | e2cd6d5 | 2017-11-09 20:38:30 -0800 | [diff] [blame] | 445 | |
Ruchir Rastogi | 060fbfc | 2019-10-11 13:16:51 -0700 | [diff] [blame] | 446 | TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { |
| 447 | CountMetric metric; |
| 448 | metric.set_id(1); |
| 449 | metric.set_bucket(ONE_WEEK); |
| 450 | |
| 451 | sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); |
| 452 | |
| 453 | int64_t oneDayNs = 24 * 60 * 60 * 1e9; |
| 454 | int64_t fiveWeeksNs = 5 * 7 * oneDayNs; |
| 455 | |
| 456 | CountMetricProducer countProducer( |
| 457 | kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs); |
| 458 | |
| 459 | int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs; |
| 460 | |
| 461 | EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs); |
| 462 | EXPECT_EQ(4, countProducer.mCurrentBucketNum); |
| 463 | EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs()); |
| 464 | } |
| 465 | |
Yao Chen | 93fe3a3 | 2017-11-02 13:52:59 -0700 | [diff] [blame] | 466 | } // namespace statsd |
| 467 | } // namespace os |
| 468 | } // namespace android |
| 469 | #else |
| 470 | GTEST_LOG_(INFO) << "This test does nothing.\n"; |
| 471 | #endif |