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