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 
onDumpReport(sp<KllMetricProducer> & producer,int64_t dumpTimeNs,bool includeCurrentBucket)100 StatsLogReport onDumpReport(sp<KllMetricProducer>& producer, int64_t dumpTimeNs,
101                             bool includeCurrentBucket) {
102     ProtoOutputStream output;
103     set<int32_t> usedUids;
104     producer->onDumpReport(dumpTimeNs, includeCurrentBucket, true /*erase data*/, FAST, nullptr,
105                            usedUids, &output);
106     return outputStreamToProto(&output);
107 }
108 
109 }  // anonymous namespace
110 
111 class KllMetricProducerTestHelper {
112 public:
createKllProducerNoConditions(const KllMetric & metric)113     static sp<KllMetricProducer> createKllProducerNoConditions(const KllMetric& metric) {
114         return createKllProducer(metric);
115     }
116 
createKllProducerWithCondition(const KllMetric & metric,const ConditionState & initialCondition)117     static sp<KllMetricProducer> createKllProducerWithCondition(
118             const KllMetric& metric, const ConditionState& initialCondition) {
119         return createKllProducer(metric, initialCondition);
120     }
121 
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)122     static sp<KllMetricProducer> createKllProducer(
123             const KllMetric& metric, optional<ConditionState> initialCondition = nullopt,
124             vector<int32_t> slicedStateAtoms = {},
125             unordered_map<int, unordered_map<int, int64_t>> stateGroupMap = {},
126             const int64_t timeBaseNs = bucketStartTimeNs,
127             const int64_t startTimeNs = bucketStartTimeNs) {
128         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
129         const int64_t bucketSizeNs = MillisToNano(
130                 TimeUnitToBucketSizeInMillisGuardrailed(kConfigKey.GetUid(), metric.bucket()));
131         const bool containsAnyPositionInDimensionsInWhat =
132                 HasPositionANY(metric.dimensions_in_what());
133         const bool shouldUseNestedDimensions =
134                 ShouldUseNestedDimensions(metric.dimensions_in_what());
135 
136         vector<Matcher> fieldMatchers;
137         translateFieldMatcher(metric.kll_field(), &fieldMatchers);
138 
139         const auto [dimensionSoftLimit, dimensionHardLimit] =
140                 StatsdStats::getAtomDimensionKeySizeLimits(
141                         atomId, StatsdStats::kDimensionKeySizeHardLimitMin);
142 
143         int conditionIndex = initialCondition ? 0 : -1;
144         vector<ConditionState> initialConditionCache;
145         if (initialCondition) {
146             initialConditionCache.push_back(initialCondition.value());
147         }
148 
149         sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
150         return new KllMetricProducer(
151                 kConfigKey, metric, protoHash, {/*pullAtomId=*/-1, /*pullerManager=*/nullptr},
152                 {timeBaseNs, startTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
153                  /*conditionCorrectionThresholdNs=*/nullopt, metric.split_bucket_for_app_upgrade()},
154                 {containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions,
155                  logEventMatcherIndex,
156                  /*eventMatcherWizard=*/nullptr, metric.dimensions_in_what(), fieldMatchers},
157                 {conditionIndex, metric.links(), initialConditionCache, wizard},
158                 {metric.state_link(), slicedStateAtoms, stateGroupMap},
159                 {/*eventActivationMap=*/{}, /*eventDeactivationMap=*/{}},
160                 {dimensionSoftLimit, dimensionHardLimit}, provider);
161     }
162 
createMetric()163     static KllMetric createMetric() {
164         KllMetric metric;
165         metric.set_id(metricId);
166         metric.set_bucket(ONE_MINUTE);
167         metric.mutable_kll_field()->set_field(atomId);
168         metric.mutable_kll_field()->add_child()->set_field(2);
169         metric.set_split_bucket_for_app_upgrade(true);
170         return metric;
171     }
172 
createMetricWithCondition()173     static KllMetric createMetricWithCondition() {
174         KllMetric metric = KllMetricProducerTestHelper::createMetric();
175         metric.set_condition(StringToId("SCREEN_ON"));
176         return metric;
177     }
178 };
179 
180 // Setup for parameterized tests.
181 class KllMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
182 
183 INSTANTIATE_TEST_SUITE_P(KllMetricProducerTest_PartialBucket, KllMetricProducerTest_PartialBucket,
184                          testing::Values(APP_UPGRADE, BOOT_COMPLETE));
185 
TEST_P(KllMetricProducerTest_PartialBucket,TestPushedEventsMultipleBuckets)186 TEST_P(KllMetricProducerTest_PartialBucket, TestPushedEventsMultipleBuckets) {
187     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
188     sp<KllMetricProducer> kllProducer =
189             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
190 
191     LogEvent event1(/*uid=*/0, /*pid=*/0);
192     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
193     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
194     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
195 
196     const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
197     switch (GetParam()) {
198         case APP_UPGRADE:
199             kllProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
200             break;
201         case BOOT_COMPLETE:
202             kllProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
203             break;
204     }
205     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1},
206                {partialBucketSplitTimeNs - bucketStartTimeNs}, {bucketStartTimeNs},
207                {partialBucketSplitTimeNs});
208     EXPECT_EQ(partialBucketSplitTimeNs, kllProducer->mCurrentBucketStartTimeNs);
209     EXPECT_EQ(0, kllProducer->getCurrentBucketNum());
210 
211     // Event arrives after the bucket split.
212     LogEvent event2(/*uid=*/0, /*pid=*/0);
213     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
214     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
215 
216     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1},
217                {partialBucketSplitTimeNs - bucketStartTimeNs}, {bucketStartTimeNs},
218                {partialBucketSplitTimeNs});
219     EXPECT_EQ(partialBucketSplitTimeNs, kllProducer->mCurrentBucketStartTimeNs);
220     EXPECT_EQ(0, kllProducer->getCurrentBucketNum());
221 
222     // Next value should create a new bucket.
223     LogEvent event3(/*uid=*/0, /*pid=*/0);
224     CreateRepeatedValueLogEvent(&event3, atomId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
225     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
226     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1, 1},
227                {partialBucketSplitTimeNs - bucketStartTimeNs,
228                 bucket2StartTimeNs - partialBucketSplitTimeNs},
229                {bucketStartTimeNs, partialBucketSplitTimeNs},
230                {partialBucketSplitTimeNs, bucket2StartTimeNs});
231     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, kllProducer->mCurrentBucketStartTimeNs);
232     EXPECT_EQ(1, kllProducer->getCurrentBucketNum());
233 }
234 
TEST(KllMetricProducerTest,TestPushedEventsWithoutCondition)235 TEST(KllMetricProducerTest, TestPushedEventsWithoutCondition) {
236     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
237     sp<KllMetricProducer> kllProducer =
238             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
239 
240     LogEvent event1(/*uid=*/0, /*pid=*/0);
241     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
242 
243     LogEvent event2(/*uid=*/0, /*pid=*/0);
244     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
245 
246     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
247     // has one slice
248     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
249     const KllMetricProducer::Interval& curInterval0 =
250             kllProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
251     EXPECT_EQ(1, curInterval0.aggregate->num_values());
252     EXPECT_GT(curInterval0.sampleSize, 0);
253 
254     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
255 
256     // has one slice
257     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
258     EXPECT_EQ(2, curInterval0.aggregate->num_values());
259 
260     kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
261     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {2}, {bucketSizeNs},
262                {bucketStartTimeNs}, {bucket2StartTimeNs});
263 }
264 
TEST(KllMetricProducerTest,TestPushedEventsWithCondition)265 TEST(KllMetricProducerTest, TestPushedEventsWithCondition) {
266     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
267     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
268             metric, ConditionState::kFalse);
269 
270     LogEvent event1(/*uid=*/0, /*pid=*/0);
271     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
272     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
273     // Has 0 slices.
274     ASSERT_EQ(0UL, kllProducer->mCurrentSlicedBucket.size());
275 
276     kllProducer->onConditionChangedLocked(true, bucketStartTimeNs + 15);
277 
278     LogEvent event2(/*uid=*/0, /*pid=*/0);
279     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
280     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
281 
282     // has one slice
283     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
284     const KllMetricProducer::Interval& curInterval0 =
285             kllProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
286     EXPECT_EQ(1, curInterval0.aggregate->num_values());
287 
288     LogEvent event3(/*uid=*/0, /*pid=*/0);
289     CreateRepeatedValueLogEvent(&event3, atomId, bucketStartTimeNs + 30, 30);
290     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
291 
292     // has one slice
293     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
294     EXPECT_EQ(2, curInterval0.aggregate->num_values());
295 
296     kllProducer->onConditionChangedLocked(false, bucketStartTimeNs + 35);
297 
298     LogEvent event4(/*uid=*/0, /*pid=*/0);
299     CreateRepeatedValueLogEvent(&event4, atomId, bucketStartTimeNs + 40, 40);
300     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
301 
302     // has one slice
303     ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
304     EXPECT_EQ(2, curInterval0.aggregate->num_values());
305 
306     kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
307     TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {2}, {20},
308                {bucketStartTimeNs}, {bucket2StartTimeNs});
309 }
310 
311 /*
312  * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
313  * when a metric is initialized.
314  */
TEST(KllMetricProducerTest_BucketDrop,TestInvalidBucketWhenConditionUnknown)315 TEST(KllMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
316     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
317     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
318             metric, ConditionState::kUnknown);
319 
320     // Condition change event.
321     kllProducer->onConditionChanged(true, bucketStartTimeNs + 50);
322 
323     // Check dump report.
324     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
325     StatsLogReport report =
326             onDumpReport(kllProducer, dumpReportTimeNs, true /* include current bucket */);
327     EXPECT_TRUE(report.has_kll_metrics());
328     ASSERT_EQ(0, report.kll_metrics().data_size());
329     ASSERT_EQ(1, report.kll_metrics().skipped_size());
330 
331     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
332               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
333     EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
334               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
335     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
336 
337     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
338     EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
339     EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
340 }
341 
342 /*
343  * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
344  * is smaller than the "min_bucket_size_nanos" specified in the metric config.
345  */
TEST(KllMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)346 TEST(KllMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
347     KllMetric metric = KllMetricProducerTestHelper::createMetric();
348     metric.set_min_bucket_size_nanos(10 * NS_PER_SEC);  // 10 seconds
349 
350     sp<KllMetricProducer> kllProducer =
351             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
352 
353     LogEvent event1(/*uid=*/0, /*pid=*/0);
354     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
355     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
356 
357     // Check dump report.
358     int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
359     StatsLogReport report =
360             onDumpReport(kllProducer, dumpReportTimeNs, true /* include current bucket */);
361     EXPECT_TRUE(report.has_kll_metrics());
362     ASSERT_EQ(0, report.kll_metrics().data_size());
363     ASSERT_EQ(1, report.kll_metrics().skipped_size());
364 
365     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
366               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
367     EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
368               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
369     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
370 
371     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
372     EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
373     EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
374 }
375 
376 /*
377  * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
378  */
TEST(KllMetricProducerTest_BucketDrop,TestBucketDropWhenDataUnavailable)379 TEST(KllMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
380     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
381 
382     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
383             metric, ConditionState::kFalse);
384 
385     // Check dump report.
386     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000;  // 10 seconds
387     StatsLogReport report =
388             onDumpReport(kllProducer, dumpReportTimeNs, true /* include current bucket */);
389     EXPECT_TRUE(report.has_kll_metrics());
390     ASSERT_EQ(0, report.kll_metrics().data_size());
391     ASSERT_EQ(1, report.kll_metrics().skipped_size());
392 
393     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
394               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
395     EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
396               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
397     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
398 
399     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
400     EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
401     EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
402 }
403 
404 /*
405  * Test bucket splits when condition is unknown.
406  */
TEST(KllMetricProducerTest,TestForcedBucketSplitWhenConditionUnknownSkipsBucket)407 TEST(KllMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
408     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
409 
410     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
411             metric, ConditionState::kUnknown);
412 
413     // App update event.
414     int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
415     kllProducer->notifyAppUpgrade(appUpdateTimeNs);
416 
417     // Check dump report.
418     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000;  // 10 seconds
419     StatsLogReport report =
420             onDumpReport(kllProducer, dumpReportTimeNs, false /* include current bucket */);
421     EXPECT_TRUE(report.has_kll_metrics());
422     ASSERT_EQ(0, report.kll_metrics().data_size());
423     ASSERT_EQ(1, report.kll_metrics().skipped_size());
424 
425     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
426               report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
427     EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
428               report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
429     ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
430 
431     auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
432     EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
433     EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
434 }
435 
TEST(KllMetricProducerTest,TestByteSize)436 TEST(KllMetricProducerTest, TestByteSize) {
437     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
438     sp<KllMetricProducer> kllProducer =
439             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
440 
441     LogEvent event1(/*uid=*/0, /*pid=*/0);
442     CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
443 
444     LogEvent event2(/*uid=*/0, /*pid=*/0);
445     CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
446 
447     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
448     kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
449     kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
450 
451     const size_t expectedSize = kllProducer->kBucketSize + 4 /* one int aggIndex entry */ +
452                                 16 /* two int64_t entries in KllQuantile object */;
453 
454     EXPECT_EQ(expectedSize, kllProducer->byteSize());
455 }
456 
TEST(KllMetricProducerTest,TestCorruptedDataReason_WhatLoss)457 TEST(KllMetricProducerTest, TestCorruptedDataReason_WhatLoss) {
458     const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
459     sp<KllMetricProducer> kllProducer =
460             KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
461 
462     kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_SOCKET_LOSS,
463                                        MetricProducer::LostAtomType::kWhat);
464     {
465         // Check dump report content.
466         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 50,
467                                              true /* include current bucket */);
468         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
469     }
470 
471     kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
472                                        MetricProducer::LostAtomType::kWhat);
473     {
474         // Check dump report content.
475         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 150,
476                                              true /* include current bucket */);
477         EXPECT_THAT(report.data_corrupted_reason(),
478                     ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
479     }
480 
481     kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_SOCKET_LOSS,
482                                        MetricProducer::LostAtomType::kWhat);
483     kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
484                                        MetricProducer::LostAtomType::kWhat);
485     {
486         // Check dump report content.
487         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 250,
488                                              true /* include current bucket */);
489         EXPECT_THAT(report.data_corrupted_reason(),
490                     ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW, DATA_CORRUPTED_SOCKET_LOSS));
491     }
492 }
493 
TEST(KllMetricProducerTest,TestCorruptedDataReason_ConditionLoss)494 TEST(KllMetricProducerTest, TestCorruptedDataReason_ConditionLoss) {
495     const int conditionId = 10;
496 
497     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
498 
499     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
500             metric, ConditionState::kFalse);
501 
502     kllProducer->onMatchedLogEventLost(conditionId, DATA_CORRUPTED_SOCKET_LOSS,
503                                        MetricProducer::LostAtomType::kCondition);
504     {
505         // Check dump report content.
506         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 50,
507                                              true /* include current bucket */);
508         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
509     }
510 
511     kllProducer->onMatchedLogEventLost(conditionId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
512                                        MetricProducer::LostAtomType::kCondition);
513     {
514         // Check dump report content.
515         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 150,
516                                              true /* include current bucket */);
517         EXPECT_THAT(report.data_corrupted_reason(),
518                     ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW, DATA_CORRUPTED_SOCKET_LOSS));
519     }
520 }
521 
TEST(KllMetricProducerTest,TestCorruptedDataReason_StateLoss)522 TEST(KllMetricProducerTest, TestCorruptedDataReason_StateLoss) {
523     const int stateAtomId = 10;
524 
525     const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
526 
527     sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
528             metric, ConditionState::kFalse);
529 
530     kllProducer->onStateEventLost(stateAtomId, DATA_CORRUPTED_SOCKET_LOSS);
531     {
532         // Check dump report content.
533         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 50,
534                                              true /* include current bucket */);
535         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
536     }
537 
538     // validation that data corruption signal remains accurate after another dump
539     {
540         // Check dump report content.
541         StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 150,
542                                              true /* include current bucket */);
543         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
544     }
545 }
546 
547 }  // namespace statsd
548 }  // namespace os
549 }  // namespace android
550 #else
551 GTEST_LOG_(INFO) << "This test does nothing.\n";
552 #endif
553