1 /*
2  * Copyright (C) 2024, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <aidl/android/os/BnPullAtomCallback.h>
18 #include <aidl/android/os/IPullAtomCallback.h>
19 #include <aidl/android/os/IPullAtomResultReceiver.h>
20 #include <aidl/android/util/StatsEventParcel.h>
21 #include <gtest/gtest.h>
22 
23 #include <memory>
24 #include <optional>
25 
26 #include "src/StatsLogProcessor.h"
27 #include "src/logd/LogEvent.h"
28 #include "src/stats_log.pb.h"
29 #include "src/stats_log_util.h"
30 #include "src/statsd_config.pb.h"
31 #include "tests/metrics/parsing_utils/parsing_test_utils.h"
32 #include "tests/statsd_test_util.h"
33 
34 #ifdef __ANDROID__
35 
36 using aidl::android::util::StatsEventParcel;
37 using namespace std;
38 using namespace testing;
39 
40 namespace android {
41 namespace os {
42 namespace statsd {
43 namespace {
44 
45 class ValueMetricHistogramE2eTest : public Test {
46 protected:
createProcessor(const StatsdConfig & config,const shared_ptr<IPullAtomCallback> & puller=nullptr,int32_t pullAtomId=0)47     void createProcessor(const StatsdConfig& config,
48                          const shared_ptr<IPullAtomCallback>& puller = nullptr,
49                          int32_t pullAtomId = 0) {
50         processor = CreateStatsLogProcessor(baseTimeNs, bucketStartTimeNs, config, cfgKey, puller,
51                                             pullAtomId);
52     }
53 
getReports(int64_t dumpTimeNs)54     optional<ConfigMetricsReportList> getReports(int64_t dumpTimeNs) {
55         ConfigMetricsReportList reports;
56         vector<uint8_t> buffer;
57         processor->onDumpReport(cfgKey, dumpTimeNs,
58                                 /*include_current_bucket*/ false, true, ADB_DUMP, FAST, &buffer);
59         if (reports.ParseFromArray(&buffer[0], buffer.size())) {
60             backfillDimensionPath(&reports);
61             backfillStringInReport(&reports);
62             backfillStartEndTimestamp(&reports);
63             return reports;
64         }
65         return nullopt;
66     }
67 
getReports()68     optional<ConfigMetricsReportList> getReports() {
69         return getReports(bucketStartTimeNs + bucketSizeNs);
70     }
71 
logEvents(const vector<shared_ptr<LogEvent>> & events)72     void logEvents(const vector<shared_ptr<LogEvent>>& events) {
73         for (const shared_ptr<LogEvent> event : events) {
74             processor->OnLogEvent(event.get());
75         }
76     }
77 
validateHistogram(const ConfigMetricsReportList & reports,int valueIndex,const vector<int> & binCounts)78     void validateHistogram(const ConfigMetricsReportList& reports, int valueIndex,
79                            const vector<int>& binCounts) {
80         ASSERT_EQ(reports.reports_size(), 1);
81         ConfigMetricsReport report = reports.reports(0);
82         ASSERT_EQ(report.metrics_size(), 1);
83         StatsLogReport metricReport = report.metrics(0);
84         ASSERT_TRUE(metricReport.has_value_metrics());
85         ASSERT_EQ(metricReport.value_metrics().skipped_size(), 0);
86         ValueMetricData data = metricReport.value_metrics().data(0);
87         ASSERT_EQ(data.bucket_info_size(), 1);
88         ValueBucketInfo bucket = data.bucket_info(0);
89         ASSERT_GE(bucket.values_size(), valueIndex + 1);
90         ASSERT_THAT(bucket.values(valueIndex),
91                     Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
92         ASSERT_THAT(bucket.values(valueIndex).histogram().count(), ElementsAreArray(binCounts));
93     }
94 
95     const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL;
96     const uint64_t baseTimeNs = getElapsedRealtimeNs();
97     const uint64_t bucketStartTimeNs = baseTimeNs + bucketSizeNs;  // 0:10
98     ConfigKey cfgKey;
99     sp<StatsLogProcessor> processor;
100 };
101 
102 class ValueMetricHistogramE2eTestPushedExplicitBins : public ValueMetricHistogramE2eTest {
103 protected:
SetUp()104     void SetUp() override {
105         StatsdConfig config = createExplicitHistogramStatsdConfig(/* bins */ {1, 7, 10, 20});
106         createProcessor(config);
107     }
108 };
109 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestNoEvents)110 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestNoEvents) {
111     optional<ConfigMetricsReportList> reports = getReports();
112     ASSERT_NE(reports, nullopt);
113 
114     ASSERT_EQ(reports->reports_size(), 1);
115     ConfigMetricsReport report = reports->reports(0);
116     ASSERT_EQ(report.metrics_size(), 1);
117     StatsLogReport metricReport = report.metrics(0);
118     EXPECT_TRUE(metricReport.has_value_metrics());
119     ASSERT_EQ(metricReport.value_metrics().skipped_size(), 1);
120 }
121 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestOneEventInFirstBinAfterUnderflow)122 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestOneEventInFirstBinAfterUnderflow) {
123     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 5,
124                                       /* value2 */ 0)});
125 
126     optional<ConfigMetricsReportList> reports = getReports();
127     ASSERT_NE(reports, nullopt);
128 
129     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {0, 1, -3});
130 }
131 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestOneEventInOverflowAndUnderflow)132 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestOneEventInOverflowAndUnderflow) {
133     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 0,
134                                       /* value2 */ 0),
135                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* value1 */ 20,
136                                       /* value2 */ 0)});
137 
138     optional<ConfigMetricsReportList> reports = getReports();
139     ASSERT_NE(reports, nullopt);
140 
141     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {1, -3, 1});
142 }
143 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestOneEventInUnderflow)144 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestOneEventInUnderflow) {
145     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ -1,
146                                       /* value2 */ 0)});
147 
148     optional<ConfigMetricsReportList> reports = getReports();
149     ASSERT_NE(reports, nullopt);
150 
151     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {1, -4});
152 }
153 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestOneEventInOverflow)154 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestOneEventInOverflow) {
155     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 100,
156                                       /* value2 */ 0)});
157 
158     optional<ConfigMetricsReportList> reports = getReports();
159     ASSERT_NE(reports, nullopt);
160 
161     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {-4, 1});
162 }
163 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestOneEventInFirstBinBeforeOverflow)164 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestOneEventInFirstBinBeforeOverflow) {
165     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 15,
166                                       /* value2 */ 0)});
167 
168     optional<ConfigMetricsReportList> reports = getReports();
169     ASSERT_NE(reports, nullopt);
170 
171     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {-3, 1, 0});
172 }
173 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestOneEventInMiddleBin)174 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestOneEventInMiddleBin) {
175     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 7,
176                                       /* value2 */ 0)});
177 
178     optional<ConfigMetricsReportList> reports = getReports();
179     ASSERT_NE(reports, nullopt);
180 
181     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {-2, 1, -2});
182 }
183 
TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins,TestMultipleEvents)184 TEST_F(ValueMetricHistogramE2eTestPushedExplicitBins, TestMultipleEvents) {
185     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 8,
186                                       /* value2 */ 0),
187                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* value1 */ 15,
188                                       /* value2 */ 0),
189                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 30, /* value1 */ 19,
190                                       /* value2 */ 0),
191                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 40, /* value1 */ 3,
192                                       /* value2 */ 0),
193                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 50, /* value1 */ 9,
194                                       /* value2 */ 0),
195                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 60, /* value1 */ 3,
196                                       /* value2 */ 0)});
197 
198     optional<ConfigMetricsReportList> reports = getReports();
199     ASSERT_NE(reports, nullopt);
200 
201     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {0, 2, 2, 2, 0});
202 }
203 
204 class ValueMetricHistogramE2eTestPushedLinearBins : public ValueMetricHistogramE2eTest {
205 protected:
SetUp()206     void SetUp() override {
207         // Bin starts: [UNDERFLOW, -10, -6, -2, 2, 6, 10]
208         StatsdConfig config =
209                 createGeneratedHistogramStatsdConfig(/* min */ -10, /* max */ 10, /* count */ 5,
210                                                      HistogramBinConfig::GeneratedBins::LINEAR);
211         createProcessor(config);
212     }
213 };
214 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestNoEvents)215 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestNoEvents) {
216     optional<ConfigMetricsReportList> reports = getReports();
217     ASSERT_NE(reports, nullopt);
218 
219     ASSERT_EQ(reports->reports_size(), 1);
220     ConfigMetricsReport report = reports->reports(0);
221     ASSERT_EQ(report.metrics_size(), 1);
222     StatsLogReport metricReport = report.metrics(0);
223     EXPECT_TRUE(metricReport.has_value_metrics());
224     ASSERT_EQ(metricReport.value_metrics().skipped_size(), 1);
225 }
226 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestOneEventInFirstBinAfterUnderflow)227 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestOneEventInFirstBinAfterUnderflow) {
228     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ -10,
229                                       /* value2 */ 0)});
230 
231     optional<ConfigMetricsReportList> reports = getReports();
232     ASSERT_NE(reports, nullopt);
233 
234     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {0, 1, -5});
235 }
236 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestOneEventInOverflowAndUnderflow)237 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestOneEventInOverflowAndUnderflow) {
238     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ -11,
239                                       /* value2 */ 0),
240                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* value1 */ 10,
241                                       /* value2 */ 0)});
242 
243     optional<ConfigMetricsReportList> reports = getReports();
244     ASSERT_NE(reports, nullopt);
245 
246     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {1, -5, 1});
247 }
248 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestOneEventInUnderflow)249 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestOneEventInUnderflow) {
250     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ -15,
251                                       /* value2 */ 0)});
252 
253     optional<ConfigMetricsReportList> reports = getReports();
254     ASSERT_NE(reports, nullopt);
255 
256     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {1, -6});
257 }
258 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestOneEventInOverflow)259 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestOneEventInOverflow) {
260     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 100,
261                                       /* value2 */ 0)});
262 
263     optional<ConfigMetricsReportList> reports = getReports();
264     ASSERT_NE(reports, nullopt);
265 
266     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {-6, 1});
267 }
268 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestOneEventInFirstBinBeforeOverflow)269 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestOneEventInFirstBinBeforeOverflow) {
270     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 6,
271                                       /* value2 */ 0)});
272 
273     optional<ConfigMetricsReportList> reports = getReports();
274     ASSERT_NE(reports, nullopt);
275 
276     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {-5, 1, 0});
277 }
278 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestOneEventInMiddleBin)279 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestOneEventInMiddleBin) {
280     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 0,
281                                       /* value2 */ 0)});
282 
283     optional<ConfigMetricsReportList> reports = getReports();
284     ASSERT_NE(reports, nullopt);
285 
286     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {-3, 1, -3});
287 }
288 
TEST_F(ValueMetricHistogramE2eTestPushedLinearBins,TestMultipleEvents)289 TEST_F(ValueMetricHistogramE2eTestPushedLinearBins, TestMultipleEvents) {
290     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 1,
291                                       /* value2 */ 0),
292                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* value1 */ 4,
293                                       /* value2 */ 0),
294                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 30, /* value1 */ -9,
295                                       /* value2 */ 0),
296                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 40, /* value1 */ 3,
297                                       /* value2 */ 0),
298                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 50, /* value1 */ 8,
299                                       /* value2 */ 0),
300                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 60, /* value1 */ -11,
301                                       /* value2 */ 0)});
302 
303     optional<ConfigMetricsReportList> reports = getReports();
304     ASSERT_NE(reports, nullopt);
305 
306     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {1, 1, 0, 1, 2, 1, 0});
307 }
308 
309 class ValueMetricHistogramE2eTestMultiplePushedHistograms : public ValueMetricHistogramE2eTest {
310 protected:
SetUp()311     void SetUp() override {
312         StatsdConfig config;
313         *config.add_atom_matcher() = CreateSimpleAtomMatcher("matcher", /* atomId */ 1);
314         *config.add_value_metric() =
315                 createValueMetric("ValueMetric", config.atom_matcher(0), /* valueFields */ {1, 2},
316                                   {ValueMetric::HISTOGRAM, ValueMetric::HISTOGRAM},
317                                   /* condition */ nullopt, /* states */ {});
318 
319         // Bin starts: [UNDERFLOW, 5, 10, 20, 40, 80, 160]
320         *config.mutable_value_metric(0)->add_histogram_bin_configs() =
321                 createGeneratedBinConfig(/* id */ 1, /* min */ 5, /* max */ 160, /* count */ 5,
322                                          HistogramBinConfig::GeneratedBins::EXPONENTIAL);
323 
324         // Bin starts: [UNDERFLOW, -10, -6, -2, 2, 6, 10]
325         *config.mutable_value_metric(0)->add_histogram_bin_configs() =
326                 createGeneratedBinConfig(/* id */ 2, /* min */ -10, /* max */ 10, /* count */ 5,
327                                          HistogramBinConfig::GeneratedBins::LINEAR);
328 
329         createProcessor(config);
330     }
331 };
332 
TEST_F(ValueMetricHistogramE2eTestMultiplePushedHistograms,TestNoEvents)333 TEST_F(ValueMetricHistogramE2eTestMultiplePushedHistograms, TestNoEvents) {
334     optional<ConfigMetricsReportList> reports = getReports();
335     ASSERT_NE(reports, nullopt);
336 
337     ASSERT_EQ(reports->reports_size(), 1);
338     ConfigMetricsReport report = reports->reports(0);
339     ASSERT_EQ(report.metrics_size(), 1);
340     StatsLogReport metricReport = report.metrics(0);
341     EXPECT_TRUE(metricReport.has_value_metrics());
342     ASSERT_EQ(metricReport.value_metrics().skipped_size(), 1);
343 }
344 
TEST_F(ValueMetricHistogramE2eTestMultiplePushedHistograms,TestMultipleEvents)345 TEST_F(ValueMetricHistogramE2eTestMultiplePushedHistograms, TestMultipleEvents) {
346     logEvents({CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 90,
347                                       /* value2 */ 0),
348                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* value1 */ 6,
349                                       /* value2 */ 12),
350                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 30, /* value1 */ 50,
351                                       /* value2 */ -1),
352                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 40, /* value1 */ 30,
353                                       /* value2 */ 5),
354                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 50, /* value1 */ 15,
355                                       /* value2 */ 2),
356                CreateTwoValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 60, /* value1 */ 160,
357                                       /* value2 */ 9)});
358 
359     optional<ConfigMetricsReportList> reports = getReports();
360     ASSERT_NE(reports, nullopt);
361 
362     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 0, {0, 1, 1, 1, 1, 1, 1});
363     TRACE_CALL(validateHistogram, *reports, /* valueIndex */ 1, {-3, 2, 2, 1, 1});
364 }
365 
TEST_F(ValueMetricHistogramE2eTest,TestDimensionConditionAndMultipleAggregationTypes)366 TEST_F(ValueMetricHistogramE2eTest, TestDimensionConditionAndMultipleAggregationTypes) {
367     StatsdConfig config;
368     *config.add_atom_matcher() = CreateSimpleAtomMatcher("matcher", /* atomId */ 1);
369     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
370     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
371     *config.add_predicate() = CreateScreenIsOnPredicate();
372     config.mutable_predicate(0)->mutable_simple_predicate()->set_initial_value(
373             SimplePredicate::FALSE);
374     *config.add_value_metric() =
375             createValueMetric("ValueMetric", config.atom_matcher(0), /* valueFields */ {1, 2, 2},
376                               {ValueMetric::HISTOGRAM, ValueMetric::SUM, ValueMetric::MIN},
377                               /* condition */ config.predicate(0).id(), /* states */ {});
378     *config.mutable_value_metric(0)->mutable_dimensions_in_what() =
379             CreateDimensions(/* atomId */ 1, {3 /* value3 */});
380 
381     // Bin starts: [UNDERFLOW, 5, 10, 20, 40, 80, 160]
382     *config.mutable_value_metric(0)->add_histogram_bin_configs() =
383             createGeneratedBinConfig(/* id */ 1, /* min */ 5, /* max */ 160, /* count */ 5,
384                                      HistogramBinConfig::GeneratedBins::EXPONENTIAL);
385 
386     createProcessor(config);
387 
388     logEvents(
389             {CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* value1 */ 90,
390                                       /* value2 */ 0, /* value3 */ 1),
391              CreateScreenStateChangedEvent(bucketStartTimeNs + 15, android::view::DISPLAY_STATE_ON),
392              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* value1 */ 6,
393                                       /* value2 */ 12, /* value3 */ 1),
394              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 30, /* value1 */ 50,
395                                       /* value2 */ -1, /* value3 */ 1),
396              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 40, /* value1 */ 30,
397                                       /* value2 */ 5, /* value3 */ 2),
398              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 50, /* value1 */ 15,
399                                       /* value2 */ 2, /* value3 */ 1),
400              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 60, /* value1 */ 5,
401                                       /* value2 */ 3, /* value3 */ 2),
402              CreateScreenStateChangedEvent(bucketStartTimeNs + 65,
403                                            android::view::DISPLAY_STATE_OFF),
404              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 70, /* value1 */ 160,
405                                       /* value2 */ 9, /* value3 */ 1),
406              CreateThreeValueLogEvent(/* atomId */ 1, bucketStartTimeNs + 80, /* value1 */ 70,
407                                       /* value2 */ 20, /* value3 */ 2)});
408 
409     optional<ConfigMetricsReportList> reports = getReports();
410 
411     ASSERT_NE(reports, nullopt);
412     ASSERT_EQ(reports->reports_size(), 1);
413     ConfigMetricsReport report = reports->reports(0);
414     ASSERT_EQ(report.metrics_size(), 1);
415     StatsLogReport metricReport = report.metrics(0);
416     ASSERT_TRUE(metricReport.has_value_metrics());
417     ASSERT_EQ(metricReport.value_metrics().skipped_size(), 0);
418     ASSERT_EQ(metricReport.value_metrics().data_size(), 2);
419 
420     // Dimension 1
421     {
422         ValueMetricData data = metricReport.value_metrics().data(0);
423         ASSERT_EQ(data.bucket_info_size(), 1);
424         ValueBucketInfo bucket = data.bucket_info(0);
425         ASSERT_EQ(bucket.values_size(), 3);
426         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
427         EXPECT_THAT(bucket.values(0).histogram().count(), ElementsAreArray({0, 1, 1, 0, 1, -2}));
428         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
429         EXPECT_EQ(bucket.values(1).value_long(), 13);
430         ASSERT_THAT(bucket.values(2), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
431         EXPECT_EQ(bucket.values(2).value_long(), -1);
432     }
433 
434     // Dimension 2
435     {
436         ValueMetricData data = metricReport.value_metrics().data(1);
437         ASSERT_EQ(data.bucket_info_size(), 1);
438         ValueBucketInfo bucket = data.bucket_info(0);
439         ASSERT_EQ(bucket.values_size(), 3);
440         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
441         EXPECT_THAT(bucket.values(0).histogram().count(), ElementsAreArray({0, 1, 0, 1, -3}));
442         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
443         EXPECT_EQ(bucket.values(1).value_long(), 8);
444         ASSERT_THAT(bucket.values(2), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
445         EXPECT_EQ(bucket.values(2).value_long(), 3);
446     }
447 }
448 
449 // Test fixture which uses a ValueMetric on a pushed atom with a statsd-aggregated histogram as well
450 // as a client-aggregated histogram.
451 class ValueMetricHistogramE2eTestClientAggregatedPushedHistogram
452     : public ValueMetricHistogramE2eTest {
453 protected:
SetUp()454     void SetUp() override {
455         StatsdConfig config;
456         *config.add_atom_matcher() = CreateSimpleAtomMatcher("matcher", /* atomId */ 1);
457         *config.add_value_metric() =
458                 createValueMetric("ValueMetric", config.atom_matcher(0), /* valueFields */ {2, 3},
459                                   {ValueMetric::HISTOGRAM, ValueMetric::HISTOGRAM},
460                                   /* condition */ nullopt, /* states */ {});
461         config.mutable_value_metric(0)->mutable_value_field()->mutable_child(1)->set_position(ALL);
462         *config.mutable_value_metric(0)->mutable_dimensions_in_what() =
463                 CreateRepeatedDimensions(/* atomId */ 1, {1 /* uid */}, {Position::FIRST});
464 
465         // Bin starts: [UNDERFLOW, -10, -6, -2, 2, 6, 10]
466         *config.mutable_value_metric(0)->add_histogram_bin_configs() =
467                 createGeneratedBinConfig(/* id */ 1, /* min */ -10, /* max */ 10, /* count */ 5,
468                                          HistogramBinConfig::GeneratedBins::LINEAR);
469 
470         config.mutable_value_metric(0)->add_histogram_bin_configs()->set_id(2);
471         config.mutable_value_metric(0)
472                 ->mutable_histogram_bin_configs(1)
473                 ->mutable_client_aggregated_bins();
474 
475         createProcessor(config);
476 
477         StatsdStats::getInstance().reset();
478     }
479 
480 public:
481     void doTestMultipleEvents() __INTRODUCED_IN(__ANDROID_API_T__);
482     void doTestBadHistograms() __INTRODUCED_IN(__ANDROID_API_T__);
483 };
484 
TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPushedHistogram,TestMultipleEvents,__ANDROID_API_T__)485 TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPushedHistogram, TestMultipleEvents,
486                __ANDROID_API_T__) {
487     logEvents({makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* uids */ {1},
488                                        /* value1 */ 0, /* value2 */ {0, 0, 1, 1}),
489                makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* uids */ {1},
490                                        /* value1 */ 12, /* value2 */ {0, 2, 0, 1}),
491                makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 30, /* uids */ {2},
492                                        /* value1 */ -1, /* value2 */ {1, 0, 0, 0}),
493                makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 40, /* uids */ {1},
494                                        /* value1 */ 5, /* value2 */ {0, 0, 0, 0}),
495                makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 50, /* uids */ {2},
496                                        /* value1 */ 2, /* value2 */ {0, 2, 0, 1}),
497                makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 60, /* uids */ {2},
498                                        /* value1 */ 9, /* value2 */ {10, 5, 2, 2})});
499 
500     optional<ConfigMetricsReportList> reports = getReports();
501     ASSERT_NE(reports, nullopt);
502 
503     ASSERT_EQ(reports->reports_size(), 1);
504     ConfigMetricsReport report = reports->reports(0);
505     ASSERT_EQ(report.metrics_size(), 1);
506     StatsLogReport metricReport = report.metrics(0);
507     ASSERT_TRUE(metricReport.has_value_metrics());
508     ASSERT_EQ(metricReport.value_metrics().skipped_size(), 0);
509     ASSERT_EQ(metricReport.value_metrics().data_size(), 2);
510 
511     // Dimension 1
512     {
513         ValueMetricData data = metricReport.value_metrics().data(0);
514         ASSERT_EQ(data.bucket_info_size(), 1);
515         ValueBucketInfo bucket = data.bucket_info(0);
516         ASSERT_EQ(bucket.values_size(), 2);
517         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
518         EXPECT_THAT(bucket.values(0).histogram().count(), ElementsAreArray({-3, 1, 1, 0, 1}));
519         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
520         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({0, 2, 1, 2}));
521     }
522 
523     // Dimension 2
524     {
525         ValueMetricData data = metricReport.value_metrics().data(1);
526         ASSERT_EQ(data.bucket_info_size(), 1);
527         ValueBucketInfo bucket = data.bucket_info(0);
528         ASSERT_EQ(bucket.values_size(), 2);
529         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
530         EXPECT_THAT(bucket.values(0).histogram().count(), ElementsAreArray({-3, 1, 1, 1, 0}));
531         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
532         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({11, 7, 2, 3}));
533     }
534 }
535 
TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPushedHistogram,TestBadHistograms,__ANDROID_API_T__)536 TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPushedHistogram, TestBadHistograms,
537                __ANDROID_API_T__) {
538     logEvents(
539             {// Histogram has negative bin count.
540              makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 10, /* uids */ {1},
541                                      /* value1 */ 0, /* value2 */ {0, 0, -1, 1}),
542 
543              // Good histogram, recorded in interval.
544              makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 20, /* uids */ {1},
545                                      /* value1 */ 12, /* value2 */ {0, 2, 0, 1}),
546 
547              // Histogram has more bins than what's already aggregated. Aggregation is not updated.
548              makeRepeatedUidLogEvent(/* atomId */ 1, bucketStartTimeNs + 30, /* uids */ {1},
549                                      /* value1 */ -1, /* value2 */ {1, 0, 0, 0, 0})});
550 
551     optional<ConfigMetricsReportList> reports = getReports();
552     ASSERT_NE(reports, nullopt);
553 
554     ASSERT_EQ(reports->reports_size(), 1);
555     ConfigMetricsReport report = reports->reports(0);
556     ASSERT_EQ(report.metrics_size(), 1);
557     StatsLogReport metricReport = report.metrics(0);
558     ASSERT_TRUE(metricReport.has_value_metrics());
559     EXPECT_EQ(metricReport.value_metrics().skipped_size(), 0);
560     ASSERT_EQ(metricReport.value_metrics().data_size(), 1);
561     ValueMetricData data = metricReport.value_metrics().data(0);
562     ASSERT_EQ(data.bucket_info_size(), 1);
563     ValueBucketInfo bucket = data.bucket_info(0);
564     ASSERT_EQ(bucket.values_size(), 2);
565     ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
566     EXPECT_THAT(bucket.values(0).histogram().count(), ElementsAreArray({-3, 2, -2, 1}));
567     ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
568     EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({0, 2, 0, 1}));
569 
570     StatsdStatsReport statsdStatsReport = getStatsdStatsReport();
571     ASSERT_EQ(statsdStatsReport.atom_metric_stats_size(), 1);
572     EXPECT_EQ(statsdStatsReport.atom_metric_stats(0).bad_value_type(), 2);
573 }
574 
575 class Puller : public BnPullAtomCallback {
576 public:
577     int curPullNum = 0;
578 
579     // Mapping of uid to histograms for each pull
580     const map<int, vector<vector<int>>> histMap;
581 
Puller(const map<int,vector<vector<int>>> & histMap)582     Puller(const map<int, vector<vector<int>>>& histMap) : histMap(histMap) {
583     }
584 
onPullAtom(int atomId,const shared_ptr<IPullAtomResultReceiver> & resultReceiver)585     Status onPullAtom(int atomId,
586                       const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
587         if (__builtin_available(android __ANDROID_API_T__, *)) {
588             vector<StatsEventParcel> parcels;
589             for (auto const& [uid, histograms] : histMap) {
590                 const vector<int>& histogram = histograms[curPullNum];
591                 AStatsEvent* statsEvent = AStatsEvent_obtain();
592                 AStatsEvent_setAtomId(statsEvent, atomId);
593                 AStatsEvent_writeInt32(statsEvent, uid);
594                 AStatsEvent_writeInt32(statsEvent, curPullNum);
595                 AStatsEvent_writeInt32Array(statsEvent, histogram.data(), histogram.size());
596                 AStatsEvent_build(statsEvent);
597                 size_t size;
598                 uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &size);
599 
600                 StatsEventParcel p;
601                 // vector.assign() creates a copy, but this is inevitable unless
602                 // stats_event.h/c uses a vector as opposed to a buffer.
603                 p.buffer.assign(buffer, buffer + size);
604                 parcels.push_back(std::move(p));
605                 AStatsEvent_release(statsEvent);
606             }
607             curPullNum++;
608             resultReceiver->pullFinished(atomId, /*success=*/true, parcels);
609         }
610         return Status::ok();
611     }
612 };
613 
614 }  // anonymous namespace
615 
616 // Test fixture which uses a ValueMetric on a pulled atom with a client-aggregated histogram.
617 class ValueMetricHistogramE2eTestClientAggregatedPulledHistogram
618     : public ValueMetricHistogramE2eTest {
619 protected:
620     const int atomId = 10'000;
621     StatsdConfig config;
622 
SetUp()623     void SetUp() override {
624         *config.add_atom_matcher() = CreateSimpleAtomMatcher("matcher", atomId);
625 
626         *config.add_value_metric() =
627                 createValueMetric("ValueMetric", config.atom_matcher(0), /* valueFields */ {2, 3},
628                                   {ValueMetric::SUM, ValueMetric::HISTOGRAM},
629                                   nullopt /* condition */, /* states */ {});
630 
631         config.mutable_value_metric(0)->mutable_value_field()->mutable_child(1)->set_position(ALL);
632         *config.mutable_value_metric(0)->mutable_dimensions_in_what() =
633                 CreateDimensions(atomId, {1 /* uid */});
634 
635         config.mutable_value_metric(0)->add_histogram_bin_configs()->set_id(1);
636         config.mutable_value_metric(0)
637                 ->mutable_histogram_bin_configs(0)
638                 ->mutable_client_aggregated_bins();
639 
640         config.mutable_value_metric(0)->set_skip_zero_diff_output(false);
641 
642         config.add_default_pull_packages("AID_ROOT");
643 
644         StatsdStats::getInstance().reset();
645     }
646 
createProcessorWithHistData(const map<int,vector<vector<int>>> & histData)647     void createProcessorWithHistData(const map<int, vector<vector<int>>>& histData) {
648         createProcessor(config, SharedRefBase::make<Puller>(histData), atomId);
649     }
650 
651 public:
652     void doTestPulledAtom() __INTRODUCED_IN(__ANDROID_API_T__);
653     void doTestBadHistograms() __INTRODUCED_IN(__ANDROID_API_T__);
654     void doTestZeroDefaultBase() __INTRODUCED_IN(__ANDROID_API_T__);
655 };
656 
TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPulledHistogram,TestPulledAtom,__ANDROID_API_T__)657 TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPulledHistogram, TestPulledAtom,
658                __ANDROID_API_T__) {
659     map<int, vector<vector<int>>> histData;
660     histData[1].push_back({0, 0, 0, 0});
661     histData[1].push_back({1, 0, 2, 0});
662     histData[1].push_back({1, 1, 3, 5});
663     histData[1].push_back({1, 1, 3, 5});
664     histData[1].push_back({3, 1, 3, 5});
665     histData[2].push_back({0, 1, 0, 0});
666     histData[2].push_back({1, 3, 0, 2});
667     histData[2].push_back({1, 3, 0, 2});
668     histData[2].push_back({2, 9, 3, 5});
669     histData[2].push_back({3, 9, 3, 5});
670     createProcessorWithHistData(histData);
671 
672     processor->mPullerManager->ForceClearPullerCache();
673     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 2 + 1);
674 
675     processor->mPullerManager->ForceClearPullerCache();
676     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 3 + 2);
677 
678     processor->mPullerManager->ForceClearPullerCache();
679     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 4 + 3);
680 
681     processor->mPullerManager->ForceClearPullerCache();
682     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 5 + 4);
683 
684     optional<ConfigMetricsReportList> reports = getReports(baseTimeNs + bucketSizeNs * 6 + 100);
685     ASSERT_NE(reports, nullopt);
686 
687     ASSERT_NE(reports, nullopt);
688     ASSERT_EQ(reports->reports_size(), 1);
689     ConfigMetricsReport report = reports->reports(0);
690     ASSERT_EQ(report.metrics_size(), 1);
691 
692     StatsLogReport metricReport = report.metrics(0);
693     ASSERT_TRUE(metricReport.has_value_metrics());
694     EXPECT_EQ(metricReport.value_metrics().skipped_size(), 0);
695     ASSERT_EQ(metricReport.value_metrics().data_size(), 2);
696     StatsLogReport::ValueMetricDataWrapper valueMetrics;
697     sortMetricDataByDimensionsValue(metricReport.value_metrics(), &valueMetrics);
698 
699     // Dimension uid = 1
700     {
701         ValueMetricData data = valueMetrics.data(0);
702         ASSERT_EQ(data.bucket_info_size(), 4);
703 
704         ValueBucketInfo bucket = data.bucket_info(0);
705         ASSERT_EQ(bucket.values_size(), 2);
706         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
707         EXPECT_EQ(bucket.values(0).value_long(), 1);
708         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
709         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, 0, 2, 0}));
710 
711         bucket = data.bucket_info(1);
712         ASSERT_EQ(bucket.values_size(), 2);
713         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
714         EXPECT_EQ(bucket.values(0).value_long(), 1);
715         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
716         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({0, 1, 1, 5}));
717 
718         bucket = data.bucket_info(2);
719         ASSERT_EQ(bucket.values_size(), 2);
720         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
721         EXPECT_EQ(bucket.values(0).value_long(), 1);
722         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
723         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({-4}));
724 
725         bucket = data.bucket_info(3);
726         ASSERT_EQ(bucket.values_size(), 2);
727         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
728         EXPECT_EQ(bucket.values(0).value_long(), 1);
729         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
730         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({2, -3}));
731     }
732 
733     // Dimension uid = 2
734     {
735         ValueMetricData data = valueMetrics.data(1);
736         ASSERT_EQ(data.bucket_info_size(), 4);
737 
738         ValueBucketInfo bucket = data.bucket_info(0);
739         ASSERT_EQ(bucket.values_size(), 2);
740         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
741         EXPECT_EQ(bucket.values(0).value_long(), 1);
742         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
743         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, 2, 0, 2}));
744 
745         bucket = data.bucket_info(1);
746         ASSERT_EQ(bucket.values_size(), 2);
747         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
748         EXPECT_EQ(bucket.values(0).value_long(), 1);
749         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
750         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({-4}));
751 
752         bucket = data.bucket_info(2);
753         ASSERT_EQ(bucket.values_size(), 2);
754         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
755         EXPECT_EQ(bucket.values(0).value_long(), 1);
756         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
757         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, 6, 3, 3}));
758 
759         bucket = data.bucket_info(3);
760         ASSERT_EQ(bucket.values_size(), 2);
761         ASSERT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
762         EXPECT_EQ(bucket.values(0).value_long(), 1);
763         ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
764         EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, -3}));
765     }
766 }
767 
TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPulledHistogram,TestBadHistograms,__ANDROID_API_T__)768 TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPulledHistogram, TestBadHistograms,
769                __ANDROID_API_T__) {
770     map<int, vector<vector<int>>> histData;
771     histData[1].push_back({0, 0, 0, 0});  // base updated.
772 
773     histData[1].push_back({1, 0, 2});  // base updated, no aggregate recorded due to
774                                        // ERROR_BINS_MISMATCH
775 
776     histData[1].push_back({1, -1, 3});  // base is reset, no aggregate recorded due to
777                                         // negative bin count
778 
779     histData[1].push_back({1, 2, 3});  // base updated, no aggregate recorded
780 
781     histData[1].push_back({2, 6, 3});  // base updated, aggregate updated
782 
783     histData[1].push_back({3, 9, 4});  // base updated, aggregate updated
784 
785     histData[1].push_back({4, 8, 5});  // base updated, no aggregate recorded because 2nd bin
786                                        // decreased
787 
788     createProcessorWithHistData(histData);
789 
790     processor->mPullerManager->ForceClearPullerCache();
791     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 2 + 1);
792 
793     processor->mPullerManager->ForceClearPullerCache();
794     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 3 + 2);
795 
796     processor->mPullerManager->ForceClearPullerCache();
797     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 4 + 3);
798 
799     processor->mPullerManager->ForceClearPullerCache();
800     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 5 + 4);
801 
802     processor->mPullerManager->ForceClearPullerCache();
803     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 6 + 5);
804 
805     processor->mPullerManager->ForceClearPullerCache();
806     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 7 + 6);
807 
808     optional<ConfigMetricsReportList> reports = getReports(baseTimeNs + bucketSizeNs * 8 + 100);
809 
810     ASSERT_NE(reports, nullopt);
811     ASSERT_EQ(reports->reports_size(), 1);
812     ConfigMetricsReport report = reports->reports(0);
813     ASSERT_EQ(report.metrics_size(), 1);
814 
815     StatsLogReport metricReport = report.metrics(0);
816     ASSERT_TRUE(metricReport.has_value_metrics());
817     EXPECT_EQ(metricReport.value_metrics().skipped_size(), 0);
818 
819     EXPECT_EQ(metricReport.value_metrics().data_size(), 1);
820     ValueMetricData data = metricReport.value_metrics().data(0);
821     EXPECT_EQ(data.bucket_info_size(), 6);
822 
823     ValueBucketInfo bucket = data.bucket_info(0);
824     ASSERT_EQ(bucket.values_size(), 1);
825     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
826 
827     bucket = data.bucket_info(1);
828     ASSERT_EQ(bucket.values_size(), 1);
829     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
830 
831     bucket = data.bucket_info(2);
832     ASSERT_EQ(bucket.values_size(), 1);
833     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
834 
835     bucket = data.bucket_info(3);
836     ASSERT_EQ(bucket.values_size(), 2);
837     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
838     ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
839     EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, 4, 0}));
840 
841     bucket = data.bucket_info(4);
842     ASSERT_EQ(bucket.values_size(), 2);
843     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
844     ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
845     EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, 3, 1}));
846 
847     bucket = data.bucket_info(5);
848     ASSERT_EQ(bucket.values_size(), 1);
849     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
850 
851     StatsdStatsReport statsdStatsReport = getStatsdStatsReport();
852     ASSERT_EQ(statsdStatsReport.atom_metric_stats_size(), 1);
853     EXPECT_EQ(statsdStatsReport.atom_metric_stats(0).bad_value_type(), 3);
854 }
855 
TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPulledHistogram,TestZeroDefaultBase,__ANDROID_API_T__)856 TEST_F_GUARDED(ValueMetricHistogramE2eTestClientAggregatedPulledHistogram, TestZeroDefaultBase,
857                __ANDROID_API_T__) {
858     config.mutable_value_metric(0)->set_use_zero_default_base(true);
859 
860     map<int, vector<vector<int>>> histData;
861     histData[1].push_back({-1, 0, 2});  // base not updated
862     histData[1].push_back({1, 0, 2});   // base updated, aggregate also recorded.
863     histData[1].push_back({2, 0, 2});   // base updated, aggregate also recorded.
864 
865     createProcessorWithHistData(histData);
866 
867     processor->mPullerManager->ForceClearPullerCache();
868     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 2 + 1);
869 
870     processor->mPullerManager->ForceClearPullerCache();
871     processor->informPullAlarmFired(baseTimeNs + bucketSizeNs * 3 + 1);
872 
873     optional<ConfigMetricsReportList> reports = getReports(baseTimeNs + bucketSizeNs * 4 + 100);
874 
875     ASSERT_NE(reports, nullopt);
876     ASSERT_EQ(reports->reports_size(), 1);
877     ConfigMetricsReport report = reports->reports(0);
878     ASSERT_EQ(report.metrics_size(), 1);
879 
880     StatsLogReport metricReport = report.metrics(0);
881     ASSERT_TRUE(metricReport.has_value_metrics());
882     EXPECT_EQ(metricReport.value_metrics().skipped_size(), 0);
883 
884     EXPECT_EQ(metricReport.value_metrics().data_size(), 1);
885     ValueMetricData data = metricReport.value_metrics().data(0);
886     EXPECT_EQ(data.bucket_info_size(), 2);
887 
888     ValueBucketInfo bucket = data.bucket_info(0);
889     ASSERT_EQ(bucket.values_size(), 2);
890     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
891     EXPECT_EQ(bucket.values(0).value_long(), 1);
892     ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
893     EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, 0, 2}));
894 
895     bucket = data.bucket_info(1);
896     ASSERT_EQ(bucket.values_size(), 2);
897     EXPECT_THAT(bucket.values(0), Property(&ValueBucketInfo::Value::has_value_long, IsTrue()));
898     EXPECT_EQ(bucket.values(0).value_long(), 1);
899     ASSERT_THAT(bucket.values(1), Property(&ValueBucketInfo::Value::has_histogram, IsTrue()));
900     EXPECT_THAT(bucket.values(1).histogram().count(), ElementsAreArray({1, -2}));
901 }
902 
903 }  // namespace statsd
904 }  // namespace os
905 }  // namespace android
906 #else
907 GTEST_LOG_(INFO) << "This test does nothing.\n";
908 #endif
909