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