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/DurationMetricProducer.h" 16 #include "src/stats_log_util.h" 17 #include "metrics_test_helper.h" 18 #include "src/condition/ConditionWizard.h" 19 20 #include <gmock/gmock.h> 21 #include <gtest/gtest.h> 22 #include <stdio.h> 23 #include <set> 24 #include <unordered_map> 25 #include <vector> 26 27 using namespace android::os::statsd; 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 36 namespace android { 37 namespace os { 38 namespace statsd { 39 40 const ConfigKey kConfigKey(0, 12345); 41 42 TEST(DurationMetricTrackerTest, TestFirstBucket) { 43 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 44 DurationMetric metric; 45 metric.set_id(1); 46 metric.set_bucket(ONE_MINUTE); 47 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 48 49 FieldMatcher dimensions; 50 DurationMetricProducer durationProducer( 51 kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, 52 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC/2); 53 54 EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); 55 EXPECT_EQ(10, durationProducer.mCurrentBucketNum); 56 EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs()); 57 } 58 59 TEST(DurationMetricTrackerTest, TestNoCondition) { 60 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 61 int64_t bucketStartTimeNs = 10000000000; 62 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 63 64 DurationMetric metric; 65 metric.set_id(1); 66 metric.set_bucket(ONE_MINUTE); 67 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 68 69 int tagId = 1; 70 LogEvent event1(tagId, bucketStartTimeNs + 1); 71 event1.init(); 72 LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); 73 event2.init(); 74 75 FieldMatcher dimensions; 76 DurationMetricProducer durationProducer( 77 kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, 78 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 79 80 durationProducer.onMatchedLogEvent(1 /* start index*/, event1); 81 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); 82 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 83 EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); 84 EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != 85 durationProducer.mPastBuckets.end()); 86 const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 87 EXPECT_EQ(2UL, buckets.size()); 88 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); 89 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); 90 EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); 91 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); 92 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); 93 EXPECT_EQ(2LL, buckets[1].mDuration); 94 } 95 96 TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { 97 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 98 int64_t bucketStartTimeNs = 10000000000; 99 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 100 101 DurationMetric metric; 102 metric.set_id(1); 103 metric.set_bucket(ONE_MINUTE); 104 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 105 106 int tagId = 1; 107 LogEvent event1(tagId, bucketStartTimeNs + 1); 108 event1.init(); 109 LogEvent event2(tagId, bucketStartTimeNs + 2); 110 event2.init(); 111 LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); 112 event3.init(); 113 LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); 114 event4.init(); 115 116 FieldMatcher dimensions; 117 DurationMetricProducer durationProducer( 118 kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 119 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 120 durationProducer.mCondition = ConditionState::kFalse; 121 122 EXPECT_FALSE(durationProducer.mCondition); 123 EXPECT_FALSE(durationProducer.isConditionSliced()); 124 125 durationProducer.onMatchedLogEvent(1 /* start index*/, event1); 126 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); 127 durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); 128 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 129 130 durationProducer.onMatchedLogEvent(1 /* start index*/, event3); 131 durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); 132 durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); 133 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 134 EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); 135 EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != 136 durationProducer.mPastBuckets.end()); 137 const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 138 EXPECT_EQ(1UL, buckets2.size()); 139 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); 140 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); 141 EXPECT_EQ(1LL, buckets2[0].mDuration); 142 } 143 144 TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { 145 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 146 int64_t bucketStartTimeNs = 10000000000; 147 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 148 149 DurationMetric metric; 150 metric.set_id(1); 151 metric.set_bucket(ONE_MINUTE); 152 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 153 154 int tagId = 1; 155 LogEvent event1(tagId, bucketStartTimeNs + 1); 156 event1.init(); 157 LogEvent event2(tagId, bucketStartTimeNs + 2); 158 event2.init(); 159 LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); 160 event3.init(); 161 LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); 162 event4.init(); 163 164 FieldMatcher dimensions; 165 DurationMetricProducer durationProducer( 166 kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 167 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 168 169 EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); 170 EXPECT_FALSE(durationProducer.isConditionSliced()); 171 172 durationProducer.onMatchedLogEvent(1 /* start index*/, event1); 173 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); 174 durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); 175 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 176 177 durationProducer.onMatchedLogEvent(1 /* start index*/, event3); 178 durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); 179 durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); 180 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 181 EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); 182 const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 183 EXPECT_EQ(1UL, buckets2.size()); 184 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); 185 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); 186 EXPECT_EQ(1LL, buckets2[0].mDuration); 187 } 188 189 TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { 190 /** 191 * The duration starts from the first bucket, through the two partial buckets (10-70sec), 192 * another bucket, and ends at the beginning of the next full bucket. 193 * Expected buckets: 194 * - [10,25]: 14 secs 195 * - [25,70]: All 45 secs 196 * - [70,130]: All 60 secs 197 * - [130, 210]: Only 5 secs (event ended at 135sec) 198 */ 199 int64_t bucketStartTimeNs = 10000000000; 200 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 201 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 202 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; 203 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; 204 205 int tagId = 1; 206 207 DurationMetric metric; 208 metric.set_id(1); 209 metric.set_bucket(ONE_MINUTE); 210 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 211 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 212 FieldMatcher dimensions; 213 DurationMetricProducer durationProducer( 214 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 215 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 216 217 LogEvent start_event(tagId, startTimeNs); 218 start_event.init(); 219 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 220 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 221 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 222 223 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 224 EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 225 std::vector<DurationBucket> buckets = 226 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 227 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); 228 EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); 229 EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); 230 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 231 232 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 233 LogEvent end_event(tagId, endTimeNs); 234 end_event.init(); 235 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 236 buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 237 EXPECT_EQ(3UL, buckets.size()); 238 EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); 239 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); 240 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); 241 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); 242 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); 243 EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); 244 } 245 246 TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { 247 /** 248 * Expected buckets (start at 11s, upgrade at 75s, end at 135s): 249 * - [10,70]: 59 secs 250 * - [70,75]: 5 sec 251 * - [75,130]: 55 secs 252 */ 253 int64_t bucketStartTimeNs = 10000000000; 254 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 255 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; 256 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; 257 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; 258 259 int tagId = 1; 260 261 DurationMetric metric; 262 metric.set_id(1); 263 metric.set_bucket(ONE_MINUTE); 264 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 265 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 266 FieldMatcher dimensions; 267 DurationMetricProducer durationProducer( 268 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 269 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 270 271 LogEvent start_event(tagId, startTimeNs); 272 start_event.init(); 273 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 274 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 275 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 276 277 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 278 EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 279 std::vector<DurationBucket> buckets = 280 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 281 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); 282 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); 283 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); 284 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); 285 EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); 286 EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); 287 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 288 289 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 290 LogEvent end_event(tagId, endTimeNs); 291 end_event.init(); 292 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 293 buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 294 EXPECT_EQ(3UL, buckets.size()); 295 EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); 296 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); 297 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); 298 } 299 300 TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { 301 sp<AlarmMonitor> alarmMonitor; 302 int64_t bucketStartTimeNs = 10000000000; 303 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 304 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 305 int64_t startTimeNs = bucketStartTimeNs + 1; 306 int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; 307 308 int tagId = 1; 309 310 // Setup metric with alert. 311 DurationMetric metric; 312 metric.set_id(1); 313 metric.set_bucket(ONE_MINUTE); 314 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 315 Alert alert; 316 alert.set_num_buckets(3); 317 alert.set_trigger_if_sum_gt(2); 318 319 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 320 FieldMatcher dimensions; 321 DurationMetricProducer durationProducer( 322 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 323 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 324 325 sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); 326 EXPECT_TRUE(anomalyTracker != nullptr); 327 328 LogEvent start_event(tagId, startTimeNs); 329 start_event.init(); 330 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 331 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 332 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 333 LogEvent end_event(tagId, endTimeNs); 334 end_event.init(); 335 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 336 337 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, 338 anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); 339 } 340 341 TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { 342 int64_t bucketStartTimeNs = 10000000000; 343 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 344 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 345 int64_t startTimeNs = bucketStartTimeNs + 1; 346 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; 347 348 int tagId = 1; 349 350 DurationMetric metric; 351 metric.set_id(1); 352 metric.set_bucket(ONE_MINUTE); 353 metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); 354 LogEvent event1(tagId, startTimeNs); 355 event1.write("111"); // uid 356 event1.init(); 357 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 358 FieldMatcher dimensions; 359 DurationMetricProducer durationProducer( 360 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 361 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 362 363 LogEvent start_event(tagId, startTimeNs); 364 start_event.init(); 365 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 366 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 367 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 368 369 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 370 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 371 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 372 373 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 374 LogEvent end_event(tagId, endTimeNs); 375 end_event.init(); 376 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 377 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 378 379 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); 380 std::vector<DurationBucket> buckets = 381 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 382 EXPECT_EQ(1UL, buckets.size()); 383 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); 384 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); 385 EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); 386 } 387 388 TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { 389 int64_t bucketStartTimeNs = 10000000000; 390 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 391 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; 392 int64_t startTimeNs = bucketStartTimeNs + 1; 393 int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; 394 395 int tagId = 1; 396 397 DurationMetric metric; 398 metric.set_id(1); 399 metric.set_bucket(ONE_MINUTE); 400 metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); 401 LogEvent event1(tagId, startTimeNs); 402 event1.write("111"); // uid 403 event1.init(); 404 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 405 FieldMatcher dimensions; 406 DurationMetricProducer durationProducer( 407 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 408 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); 409 410 LogEvent start_event(tagId, startTimeNs); 411 start_event.init(); 412 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 413 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 414 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 415 416 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 417 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 418 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 419 420 // Stop occurs in the same partial bucket as created for the app upgrade. 421 LogEvent end_event(tagId, endTimeNs); 422 end_event.init(); 423 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 424 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 425 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 426 427 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 428 std::vector<DurationBucket> buckets = 429 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 430 EXPECT_EQ(1UL, buckets.size()); 431 EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); 432 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); 433 EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); 434 } 435 436 } // namespace statsd 437 } // namespace os 438 } // namespace android 439 #else 440 GTEST_LOG_(INFO) << "This test does nothing.\n"; 441 #endif 442