1 // Copyright (C) 2021 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/KllMetricProducer.h"
16 
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <math.h>
20 #include <stdio.h>
21 
22 #include <vector>
23 
24 #include "metrics_test_helper.h"
25 #include "src/FieldValue.h"
26 #include "src/matchers/SimpleAtomMatchingTracker.h"
27 #include "src/metrics/MetricProducer.h"
28 #include "src/stats_log_util.h"
29 #include "tests/statsd_test_util.h"
30 
31 using namespace testing;
32 using android::sp;
33 using dist_proc::aggregation::KllQuantile;
34 using std::make_shared;
35 using std::optional;
36 using std::set;
37 using std::shared_ptr;
38 using std::unique_ptr;
39 using std::unordered_map;
40 using std::vector;
41 
42 #ifdef __ANDROID__
43 
44 namespace android {
45 namespace os {
46 namespace statsd {
47 
48 namespace {
49 
50 const ConfigKey kConfigKey(0, 12345);
51 const int atomId = 1;
52 const int64_t metricId = 123;
53 const uint64_t protoHash = 0x1234567890;
54 const int logEventMatcherIndex = 0;
55 const int64_t bucketStartTimeNs = 10000000000;
56 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
57 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
58 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
59 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
60 const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
61 const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
62 
assertPastBucketsSingleKey(const std::unordered_map<MetricDimensionKey,std::vector<PastBucket<unique_ptr<KllQuantile>>>> & mPastBuckets,const std::initializer_list<int> & expectedKllCountsList,const std::initializer_list<int64_t> & expectedDurationNsList,const std::initializer_list<int64_t> & expectedStartTimeNsList,const std::initializer_list<int64_t> & expectedEndTimeNsList)63 static void assertPastBucketsSingleKey(
64         const std::unordered_map<MetricDimensionKey,
65                                  std::vector<PastBucket<unique_ptr<KllQuantile>>>>& mPastBuckets,
66         const std::initializer_list<int>& expectedKllCountsList,
67         const std::initializer_list<int64_t>& expectedDurationNsList,
68         const std::initializer_list<int64_t>& expectedStartTimeNsList,
69         const std::initializer_list<int64_t>& expectedEndTimeNsList) {
70     vector<int> expectedKllCounts(expectedKllCountsList);
71     vector<int64_t> expectedDurationNs(expectedDurationNsList);
72     vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList);
73     vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList);
74 
75     ASSERT_EQ(expectedKllCounts.size(), expectedDurationNs.size());
76     ASSERT_EQ(expectedKllCounts.size(), expectedStartTimeNs.size());
77     ASSERT_EQ(expectedKllCounts.size(), expectedEndTimeNs.size());
78 
79     if (expectedKllCounts.size() == 0) {
80         ASSERT_EQ(0, mPastBuckets.size());
81         return;
82     }
83 
84     ASSERT_EQ(1, mPastBuckets.size());
85     const vector<PastBucket<unique_ptr<KllQuantile>>>& buckets = mPastBuckets.begin()->second;
86     ASSERT_EQ(expectedKllCounts.size(), buckets.size());
87 
88     for (int i = 0; i < expectedKllCounts.size(); i++) {
89         EXPECT_EQ(expectedKllCounts[i], buckets[i].aggregates[0]->num_values())
90                 << "Number of entries in KLL sketch differ at index " << i;
91         EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
92                 << "Condition duration value differ at index " << i;
93         EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs)
94                 << "Start time differs at index " << i;
95         EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs)
96                 << "End time differs at index " << i;
97     }
98 }
99 
100 }  // anonymous namespace
101 
102 class KllMetricProducerTestHelper {
103 public:
createKllProducerNoConditions(const KllMetric & metric)104     static sp<KllMetricProducer> createKllProducerNoConditions(const KllMetric& metric) {
105         return createKllProducer(metric);
106     }
107 
createKllProducerWithCondition(const KllMetric & metric,const ConditionState & initialCondition)108     static sp<KllMetricProducer> createKllProducerWithCondition(
109             const KllMetric& metric, const ConditionState& initialCondition) {
110         return createKllProducer(metric, initialCondition);
111     }
112 
createKllProducer(const KllMetric & metric,optional<ConditionState> initialCondition=nullopt,vector<int32_t> slicedStateAtoms={},unordered_map<int,unordered_map<int,int64_t>> stateGroupMap={},const int64_t timeBaseNs=bucketStartTimeNs,const int64_t startTimeNs=bucketStartTimeNs)113     static sp<KllMetricProducer> createKllProducer(
114             const KllMetric& metric, optional<ConditionState> initialCondition = nullopt,
115             vector<int32_t> slicedStateAtoms = {},
116             unordered_map<int, unordered_map<int, int64_t>> stateGroupMap = {},
117             const int64_t timeBaseNs = bucketStartTimeNs,
118             const int64_t startTimeNs = bucketStartTimeNs) {
119         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
120         const int64_t bucketSizeNs = MillisToNano(
121                 TimeUnitToBucketSizeInMillisGuardrailed(kConfigKey.GetUid(), metric.bucket()));
122         const bool containsAnyPositionInDimensionsInWhat =
123                 HasPositionANY(metric.dimensions_in_what());
124         const bool shouldUseNestedDimensions =
125                 ShouldUseNestedDimensions(metric.dimensions_in_what());
126 
127         vector<Matcher> fieldMatchers;
128         translateFieldMatcher(metric.kll_field(), &fieldMatchers);
129 
130         const auto [dimensionSoftLimit, dimensionHardLimit] =
131                 StatsdStats::getAtomDimensionKeySizeLimits(
132                         atomId, StatsdStats::kDimensionKeySizeHardLimitMin);
133 
134         int conditionIndex = initialCondition ? 0 : -1;
135         vector<ConditionState> initialConditionCache;
136         if (initialCondition) {
137             initialConditionCache.push_back(initialCondition.value());
138         }
139 
140         sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
141         return new KllMetricProducer(
142                 kConfigKey, metric, protoHash, {/*pullAtomId=*/-1, /*pullerManager=*/nullptr},
143                 {timeBaseNs, startTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
144                  /*conditionCorrectionThresholdNs=*/nullopt, metric.split_bucket_for_app_upgrade()},
145                 {containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions,
146                  logEventMatcherIndex,
147                  /*eventMatcherWizard=*/nullptr, metric.dimensions_in_what(), fieldMatchers},
148                 {conditionIndex, metric.links(), initialConditionCache, wizard},
149                 {metric.state_link(), slicedStateAtoms, stateGroupMap},
150                 {/*eventActivationMap=*/{}, /*eventDeactivationMap=*/{}},
151                 {dimensionSoftLimit, dimensionHardLimit}, provider);
152     }
153 
createMetric()154     static KllMetric createMetric() {
155         KllMetric metric;
156         metric.set_id(metricId);
157         metric.set_bucket(ONE_MINUTE);
158         metric.mutable_kll_field()->set_field(atomId);
159         metric.mutable_kll_field()->add_child()->set_field(2);
160         metric.set_split_bucket_for_app_upgrade(true);
161         return metric;
162     }
163 
createMetricWithCondition()164     static KllMetric createMetricWithCondition() {
165         KllMetric metric = KllMetricProducerTestHelper::createMetric();
166         metric.set_condition(StringToId("SCREEN_ON"));
167         return metric;
168     }
169 };
170 
171 // Setup for parameterized tests.
172 class KllMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
173 
174 INSTANTIATE_TEST_SUITE_P(KllMetricProducerTest_PartialBucket, KllMetricProducerTest_PartialBucket,
175                          testing::Values(APP_UPGRADE, BOOT_COMPLETE));
176 
TEST_P(KllMetricProducerTest_PartialBucket,TestPushedEventsMultipleBuckets)177 TEST_P(KllMetricProducerTest_PartialBucket, TestPushedEventsMultipleBuckets) {
178     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
179     sp<KllMetricProducer> kllProducer =
180             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
181 
182     LogEvent event1(/*uid=*/0, /*pid=*/0);
183     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
184     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
185     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
186 
187     const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
188     switch (GetParam()) {
189         case APP_UPGRADE:
190             kllProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
191             break;
192         case BOOT_COMPLETE:
193             kllProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
194             break;
195     }
196     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1},
197                {partialBucketSplitTimeNs - bucketStartTimeNs}, {bucketStartTimeNs},
198                {partialBucketSplitTimeNs});
199     EXPECT_EQ(partialBucketSplitTimeNs, kllProducer->mCurrentBucketStartTimeNs);
200     EXPECT_EQ(0, kllProducer->getCurrentBucketNum());
201 
202     // Event arrives after the bucket split.
203     LogEvent event2(/*uid=*/0, /*pid=*/0);
204     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
205     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
206 
207     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1},
208                {partialBucketSplitTimeNs - bucketStartTimeNs}, {bucketStartTimeNs},
209                {partialBucketSplitTimeNs});
210     EXPECT_EQ(partialBucketSplitTimeNs, kllProducer->mCurrentBucketStartTimeNs);
211     EXPECT_EQ(0, kllProducer->getCurrentBucketNum());
212 
213     // Next value should create a new bucket.
214     LogEvent event3(/*uid=*/0, /*pid=*/0);
215     CreateRepeatedValueLogEvent(&event3, atomId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
216     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
217     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1, 1},
218                {partialBucketSplitTimeNs - bucketStartTimeNs,
219                 bucket2StartTimeNs - partialBucketSplitTimeNs},
220                {bucketStartTimeNs, partialBucketSplitTimeNs},
221                {partialBucketSplitTimeNs, bucket2StartTimeNs});
222     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, kllProducer->mCurrentBucketStartTimeNs);
223     EXPECT_EQ(1, kllProducer->getCurrentBucketNum());
224 }
225 
TEST(KllMetricProducerTest,TestPushedEventsWithoutCondition)226 TEST(KllMetricProducerTest, TestPushedEventsWithoutCondition) {
227     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
228     sp<KllMetricProducer> kllProducer =
229             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
230 
231     LogEvent event1(/*uid=*/0, /*pid=*/0);
232     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
233 
234     LogEvent event2(/*uid=*/0, /*pid=*/0);
235     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
236 
237     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
238     // has one slice
239     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
240     const KllMetricProducer::Interval& curInterval0 =
241             kllProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
242     EXPECT_EQ(1, curInterval0.aggregate->num_values());
243     EXPECT_GT(curInterval0.sampleSize, 0);
244 
245     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
246 
247     // has one slice
248     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
249     EXPECT_EQ(2, curInterval0.aggregate->num_values());
250 
251     kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
252     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {2}, {bucketSizeNs},
253                {bucketStartTimeNs}, {bucket2StartTimeNs});
254 }
255 
TEST(KllMetricProducerTest,TestPushedEventsWithCondition)256 TEST(KllMetricProducerTest, TestPushedEventsWithCondition) {
257     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
258     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
259             metric, ConditionState::kFalse);
260 
261     LogEvent event1(/*uid=*/0, /*pid=*/0);
262     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
263     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
264     // Has 0 slices.
265     ASSERT_EQ(0UL, kllProducer->mCurrentSlicedBucket.size());
266 
267     kllProducer->onConditionChangedLocked(true, bucketStartTimeNs + 15);
268 
269     LogEvent event2(/*uid=*/0, /*pid=*/0);
270     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
271     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
272 
273     // has one slice
274     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
275     const KllMetricProducer::Interval& curInterval0 =
276             kllProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
277     EXPECT_EQ(1, curInterval0.aggregate->num_values());
278 
279     LogEvent event3(/*uid=*/0, /*pid=*/0);
280     CreateRepeatedValueLogEvent(&event3, atomId, bucketStartTimeNs + 30, 30);
281     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
282 
283     // has one slice
284     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
285     EXPECT_EQ(2, curInterval0.aggregate->num_values());
286 
287     kllProducer->onConditionChangedLocked(false, bucketStartTimeNs + 35);
288 
289     LogEvent event4(/*uid=*/0, /*pid=*/0);
290     CreateRepeatedValueLogEvent(&event4, atomId, bucketStartTimeNs + 40, 40);
291     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
292 
293     // has one slice
294     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
295     EXPECT_EQ(2, curInterval0.aggregate->num_values());
296 
297     kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
298     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {2}, {20},
299                {bucketStartTimeNs}, {bucket2StartTimeNs});
300 }
301 
302 /*
303  * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
304  * when a metric is initialized.
305  */
TEST(KllMetricProducerTest_BucketDrop,TestInvalidBucketWhenConditionUnknown)306 TEST(KllMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
307     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
308     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
309             metric, ConditionState::kUnknown);
310 
311     // Condition change event.
312     kllProducer->onConditionChanged(true, bucketStartTimeNs + 50);
313 
314     // Check dump report.
315     ProtoOutputStream output;
316     std::set<string> strSet;
317     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
318     kllProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
319                               NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
320 
321     StatsLogReport report = outputStreamToProto(&output);
322     EXPECT_TRUE(report.has_kll_metrics());
323     ASSERT_EQ(0, report.kll_metrics().data_size());
324     ASSERT_EQ(1, report.kll_metrics().skipped_size());
325 
326     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
327               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
328     EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
329               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
330     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
331 
332     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
333     EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
334     EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
335 }
336 
337 /*
338  * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
339  * is smaller than the "min_bucket_size_nanos" specified in the metric config.
340  */
TEST(KllMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)341 TEST(KllMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
342     KllMetric metric = KllMetricProducerTestHelper::createMetric();
343     metric.set_min_bucket_size_nanos(10 * NS_PER_SEC);  // 10 seconds
344 
345     sp<KllMetricProducer> kllProducer =
346             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
347 
348     LogEvent event1(/*uid=*/0, /*pid=*/0);
349     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
350     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
351 
352     // Check dump report.
353     ProtoOutputStream output;
354     std::set<string> strSet;
355     int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
356     kllProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
357                               NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
358 
359     StatsLogReport report = outputStreamToProto(&output);
360     EXPECT_TRUE(report.has_kll_metrics());
361     ASSERT_EQ(0, report.kll_metrics().data_size());
362     ASSERT_EQ(1, report.kll_metrics().skipped_size());
363 
364     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
365               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
366     EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
367               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
368     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
369 
370     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
371     EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
372     EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
373 }
374 
375 /*
376  * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
377  */
TEST(KllMetricProducerTest_BucketDrop,TestBucketDropWhenDataUnavailable)378 TEST(KllMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
379     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
380 
381     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
382             metric, ConditionState::kFalse);
383 
384     // Check dump report.
385     ProtoOutputStream output;
386     std::set<string> strSet;
387     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000;  // 10 seconds
388     kllProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
389                               NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
390 
391     StatsLogReport report = outputStreamToProto(&output);
392     EXPECT_TRUE(report.has_kll_metrics());
393     ASSERT_EQ(0, report.kll_metrics().data_size());
394     ASSERT_EQ(1, report.kll_metrics().skipped_size());
395 
396     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
397               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
398     EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
399               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
400     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
401 
402     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
403     EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
404     EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
405 }
406 
407 /*
408  * Test bucket splits when condition is unknown.
409  */
TEST(KllMetricProducerTest,TestForcedBucketSplitWhenConditionUnknownSkipsBucket)410 TEST(KllMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
411     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
412 
413     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
414             metric, ConditionState::kUnknown);
415 
416     // App update event.
417     int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
418     kllProducer->notifyAppUpgrade(appUpdateTimeNs);
419 
420     // Check dump report.
421     ProtoOutputStream output;
422     std::set<string> strSet;
423     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000;  // 10 seconds
424     kllProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
425                               NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
426 
427     StatsLogReport report = outputStreamToProto(&output);
428     EXPECT_TRUE(report.has_kll_metrics());
429     ASSERT_EQ(0, report.kll_metrics().data_size());
430     ASSERT_EQ(1, report.kll_metrics().skipped_size());
431 
432     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
433               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
434     EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
435               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
436     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
437 
438     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
439     EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
440     EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
441 }
442 
TEST(KllMetricProducerTest,TestByteSize)443 TEST(KllMetricProducerTest, TestByteSize) {
444     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
445     sp<KllMetricProducer> kllProducer =
446             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
447 
448     LogEvent event1(/*uid=*/0, /*pid=*/0);
449     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
450 
451     LogEvent event2(/*uid=*/0, /*pid=*/0);
452     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
453 
454     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
455     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
456     kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
457 
458     const size_t expectedSize = kllProducer->kBucketSize + 4 /* one int aggIndex entry */ +
459                                 16 /* two int64_t entries in KllQuantile object */;
460 
461     EXPECT_EQ(expectedSize, kllProducer->byteSize());
462 }
463 
464 }  // namespace statsd
465 }  // namespace os
466 }  // namespace android
467 #else
468 GTEST_LOG_(INFO) << "This test does nothing.\n";
469 #endif
470