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 
TEST(DurationMetricTrackerTest,TestFirstBucket)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 
TEST(DurationMetricTrackerTest,TestNoCondition)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 
TEST(DurationMetricTrackerTest,TestNonSlicedCondition)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 
TEST(DurationMetricTrackerTest,TestNonSlicedConditionUnknownState)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 
TEST(DurationMetricTrackerTest,TestSumDurationWithUpgrade)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 
TEST(DurationMetricTrackerTest,TestSumDurationWithUpgradeInFollowingBucket)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 
TEST(DurationMetricTrackerTest,TestSumDurationAnomalyWithUpgrade)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 
TEST(DurationMetricTrackerTest,TestMaxDurationWithUpgrade)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 
TEST(DurationMetricTrackerTest,TestMaxDurationWithUpgradeInNextBucket)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