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 "src/metrics/parsing_utils/histogram_parsing_utils.h"
18
19 #include <gtest/gtest.h>
20
21 #include <algorithm>
22 #include <numeric>
23 #include <variant>
24 #include <vector>
25
26 #include "src/guardrail/StatsdStats.h"
27 #include "src/stats_util.h"
28 #include "src/statsd_config.pb.h"
29 #include "tests/metrics/parsing_utils/parsing_test_utils.h"
30 #include "tests/statsd_test_util.h"
31
32 #ifdef __ANDROID__
33
34 using namespace std;
35 using namespace testing;
36
37 namespace android {
38 namespace os {
39 namespace statsd {
40 namespace {
41
42 using HistogramParsingUtilsTest = InitConfigTest;
43
44 constexpr auto LINEAR = HistogramBinConfig::GeneratedBins::LINEAR;
45 constexpr auto EXPONENTIAL = HistogramBinConfig::GeneratedBins::EXPONENTIAL;
46
TEST_F(HistogramParsingUtilsTest,TestMissingHistogramBinConfigId)47 TEST_F(HistogramParsingUtilsTest, TestMissingHistogramBinConfigId) {
48 StatsdConfig config = createExplicitHistogramStatsdConfig(/* bins */ {5});
49 config.mutable_value_metric(0)->mutable_histogram_bin_configs()->Mutable(0)->clear_id();
50
51 EXPECT_EQ(initConfig(config),
52 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_MISSING_BIN_CONFIG_ID,
53 config.value_metric(0).id()));
54 }
55
TEST_F(HistogramParsingUtilsTest,TestMissingHistogramBinConfigBinningStrategy)56 TEST_F(HistogramParsingUtilsTest, TestMissingHistogramBinConfigBinningStrategy) {
57 StatsdConfig config = createHistogramStatsdConfig();
58 config.mutable_value_metric(0)->add_histogram_bin_configs()->set_id(1);
59
60 EXPECT_EQ(initConfig(config),
61 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_UNKNOWN_BINNING_STRATEGY,
62 config.value_metric(0).id()));
63 }
64
TEST_F(HistogramParsingUtilsTest,TestGeneratedBinsMissingMin)65 TEST_F(HistogramParsingUtilsTest, TestGeneratedBinsMissingMin) {
66 StatsdConfig config =
67 createGeneratedHistogramStatsdConfig(/* min */ 1, /* max */ 10, /* count */ 5, LINEAR);
68 config.mutable_value_metric(0)
69 ->mutable_histogram_bin_configs(0)
70 ->mutable_generated_bins()
71 ->clear_min();
72
73 EXPECT_EQ(
74 initConfig(config),
75 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_MISSING_GENERATED_BINS_ARGS,
76 config.value_metric(0).id()));
77 }
78
TEST_F(HistogramParsingUtilsTest,TestGeneratedBinsMissingMax)79 TEST_F(HistogramParsingUtilsTest, TestGeneratedBinsMissingMax) {
80 StatsdConfig config =
81 createGeneratedHistogramStatsdConfig(/* min */ 1, /* max */ 10, /* count */ 5, LINEAR);
82 config.mutable_value_metric(0)
83 ->mutable_histogram_bin_configs(0)
84 ->mutable_generated_bins()
85 ->clear_max();
86
87 EXPECT_EQ(
88 initConfig(config),
89 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_MISSING_GENERATED_BINS_ARGS,
90 config.value_metric(0).id()));
91 }
92
TEST_F(HistogramParsingUtilsTest,TestGeneratedBinsMissingCount)93 TEST_F(HistogramParsingUtilsTest, TestGeneratedBinsMissingCount) {
94 StatsdConfig config =
95 createGeneratedHistogramStatsdConfig(/* min */ 1, /* max */ 10, /* count */ 5, LINEAR);
96 config.mutable_value_metric(0)
97 ->mutable_histogram_bin_configs(0)
98 ->mutable_generated_bins()
99 ->clear_count();
100
101 EXPECT_EQ(
102 initConfig(config),
103 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_MISSING_GENERATED_BINS_ARGS,
104 config.value_metric(0).id()));
105 }
106
TEST_F(HistogramParsingUtilsTest,TestGeneratedBinsMissingStrategy)107 TEST_F(HistogramParsingUtilsTest, TestGeneratedBinsMissingStrategy) {
108 StatsdConfig config = createHistogramStatsdConfig();
109 *config.mutable_value_metric(0)->add_histogram_bin_configs() =
110 createGeneratedBinConfig(/* id */ 1, /* min */ 1, /* max */ 10, /* count */ 5,
111 HistogramBinConfig::GeneratedBins::UNKNOWN);
112
113 EXPECT_EQ(
114 initConfig(config),
115 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_MISSING_GENERATED_BINS_ARGS,
116 config.value_metric(0).id()));
117
118 config.mutable_value_metric(0)
119 ->mutable_histogram_bin_configs(0)
120 ->mutable_generated_bins()
121 ->clear_strategy();
122
123 clearData();
124 EXPECT_EQ(
125 initConfig(config),
126 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_MISSING_GENERATED_BINS_ARGS,
127 config.value_metric(0).id()));
128 }
129
TEST_F(HistogramParsingUtilsTest,TestGeneratedBinsMinNotLessThanMax)130 TEST_F(HistogramParsingUtilsTest, TestGeneratedBinsMinNotLessThanMax) {
131 StatsdConfig config =
132 createGeneratedHistogramStatsdConfig(/* min */ 10, /* max */ 10, /* count */ 5, LINEAR);
133
134 EXPECT_EQ(initConfig(config),
135 InvalidConfigReason(
136 INVALID_CONFIG_REASON_VALUE_METRIC_HIST_GENERATED_BINS_INVALID_MIN_MAX,
137 config.value_metric(0).id()));
138 }
139
TEST_F(HistogramParsingUtilsTest,TestExponentialBinsMinNotLessThanMax)140 TEST_F(HistogramParsingUtilsTest, TestExponentialBinsMinNotLessThanMax) {
141 StatsdConfig config = createGeneratedHistogramStatsdConfig(/* min */ 10, /* max */ 10,
142 /* count */ 5, EXPONENTIAL);
143
144 EXPECT_EQ(initConfig(config),
145 InvalidConfigReason(
146 INVALID_CONFIG_REASON_VALUE_METRIC_HIST_GENERATED_BINS_INVALID_MIN_MAX,
147 config.value_metric(0).id()));
148 }
149
TEST_F(HistogramParsingUtilsTest,TestExponentialBinsZeroMin)150 TEST_F(HistogramParsingUtilsTest, TestExponentialBinsZeroMin) {
151 StatsdConfig config = createGeneratedHistogramStatsdConfig(/* min */ 0, /* max */ 10,
152 /* count */ 5, EXPONENTIAL);
153
154 EXPECT_EQ(initConfig(config),
155 InvalidConfigReason(
156 INVALID_CONFIG_REASON_VALUE_METRIC_HIST_GENERATED_BINS_INVALID_MIN_MAX,
157 config.value_metric(0).id()));
158 }
159
TEST_F(HistogramParsingUtilsTest,TestTooFewGeneratedBins)160 TEST_F(HistogramParsingUtilsTest, TestTooFewGeneratedBins) {
161 StatsdConfig config =
162 createGeneratedHistogramStatsdConfig(/* min */ 10, /* max */ 50, /* count */ 2, LINEAR);
163
164 EXPECT_EQ(initConfig(config), nullopt);
165
166 config.mutable_value_metric(0)
167 ->mutable_histogram_bin_configs(0)
168 ->mutable_generated_bins()
169 ->set_count(1);
170
171 clearData();
172 EXPECT_EQ(initConfig(config),
173 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_TOO_FEW_BINS,
174 config.value_metric(0).id()));
175 }
176
TEST_F(HistogramParsingUtilsTest,TestTooManyGeneratedBins)177 TEST_F(HistogramParsingUtilsTest, TestTooManyGeneratedBins) {
178 StatsdConfig config = createGeneratedHistogramStatsdConfig(/* min */ 10, /* max */ 50,
179 /* count */ 100, LINEAR);
180
181 EXPECT_EQ(initConfig(config), nullopt);
182
183 config.mutable_value_metric(0)
184 ->mutable_histogram_bin_configs(0)
185 ->mutable_generated_bins()
186 ->set_count(101);
187
188 clearData();
189 EXPECT_EQ(initConfig(config),
190 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_TOO_MANY_BINS,
191 config.value_metric(0).id()));
192 }
193
TEST_F(HistogramParsingUtilsTest,TestTooFewExplicitBins)194 TEST_F(HistogramParsingUtilsTest, TestTooFewExplicitBins) {
195 StatsdConfig config = createExplicitHistogramStatsdConfig(/* bins */ {1});
196
197 EXPECT_EQ(initConfig(config),
198 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_TOO_FEW_BINS,
199 config.value_metric(0).id()));
200
201 config.mutable_value_metric(0)
202 ->mutable_histogram_bin_configs(0)
203 ->mutable_explicit_bins()
204 ->add_bin(2);
205
206 clearData();
207 EXPECT_EQ(initConfig(config), nullopt);
208 }
209
TEST_F(HistogramParsingUtilsTest,TestTooManyExplicitBins)210 TEST_F(HistogramParsingUtilsTest, TestTooManyExplicitBins) {
211 BinStarts bins(100);
212 // Fill bins with values 1, 2, ..., 100.
213 std::iota(std::begin(bins), std::end(bins), 1);
214 StatsdConfig config = createExplicitHistogramStatsdConfig(bins);
215
216 EXPECT_EQ(initConfig(config), nullopt);
217
218 config.mutable_value_metric(0)
219 ->mutable_histogram_bin_configs(0)
220 ->mutable_explicit_bins()
221 ->add_bin(101);
222
223 clearData();
224 EXPECT_EQ(initConfig(config),
225 InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HIST_TOO_MANY_BINS,
226 config.value_metric(0).id()));
227 }
228
TEST_F(HistogramParsingUtilsTest,TestExplicitBinsDuplicateValues)229 TEST_F(HistogramParsingUtilsTest, TestExplicitBinsDuplicateValues) {
230 BinStarts bins(50);
231 // Fill bins with values 1, 2, ..., 50.
232 std::iota(std::begin(bins), std::end(bins), 1);
233 StatsdConfig config = createExplicitHistogramStatsdConfig(bins);
234
235 config.mutable_value_metric(0)
236 ->mutable_histogram_bin_configs(0)
237 ->mutable_explicit_bins()
238 ->add_bin(50);
239
240 EXPECT_EQ(initConfig(config),
241 InvalidConfigReason(
242 INVALID_CONFIG_REASON_VALUE_METRIC_HIST_EXPLICIT_BINS_NOT_STRICTLY_ORDERED,
243 config.value_metric(0).id()));
244 }
245
TEST_F(HistogramParsingUtilsTest,TestExplicitBinsUnsortedValues)246 TEST_F(HistogramParsingUtilsTest, TestExplicitBinsUnsortedValues) {
247 BinStarts bins(50);
248 // Fill bins with values 1, 2, ..., 50.
249 std::iota(std::begin(bins), std::end(bins), 1);
250
251 // Swap values at indices 10 and 40.
252 std::swap(bins[10], bins[40]);
253
254 StatsdConfig config = createExplicitHistogramStatsdConfig(bins);
255
256 EXPECT_EQ(initConfig(config),
257 InvalidConfigReason(
258 INVALID_CONFIG_REASON_VALUE_METRIC_HIST_EXPLICIT_BINS_NOT_STRICTLY_ORDERED,
259 config.value_metric(0).id()));
260 }
261
getParsedBins(const ValueMetric & metric)262 const BinStarts getParsedBins(const ValueMetric& metric) {
263 ParseHistogramBinConfigsResult result =
264 parseHistogramBinConfigs(metric, /* aggregationTypes */ {ValueMetric::HISTOGRAM});
265 return holds_alternative<vector<optional<const BinStarts>>>(result)
266 ? *get<vector<optional<const BinStarts>>>(result).front()
267 : BinStarts();
268 }
269
getParsedGeneratedBins(float min,float max,int count,HistogramBinConfig::GeneratedBins::Strategy strategy)270 const BinStarts getParsedGeneratedBins(float min, float max, int count,
271 HistogramBinConfig::GeneratedBins::Strategy strategy) {
272 StatsdConfig config = createGeneratedHistogramStatsdConfig(min, max, count, strategy);
273
274 return getParsedBins(config.value_metric(0));
275 }
276
getParsedLinearBins(float min,float max,int count)277 const BinStarts getParsedLinearBins(float min, float max, int count) {
278 return getParsedGeneratedBins(min, max, count, LINEAR);
279 }
280
TEST_F(HistogramParsingUtilsTest,TestValidLinearBins)281 TEST_F(HistogramParsingUtilsTest, TestValidLinearBins) {
282 EXPECT_THAT(getParsedLinearBins(-10, 10, 5),
283 ElementsAre(UNDERFLOW_BIN_START, -10, -6, -2, 2, 6, 10));
284 EXPECT_THAT(getParsedLinearBins(-10, 10, 2), ElementsAre(UNDERFLOW_BIN_START, -10, 0, 10));
285 EXPECT_THAT(getParsedLinearBins(-100, -50, 3),
286 ElementsAre(UNDERFLOW_BIN_START, -100, FloatNear(-83.33, 0.01),
287 FloatNear(-66.67, 0.01), -50));
288 EXPECT_THAT(getParsedLinearBins(2.5, 11.3, 7),
289 ElementsAre(UNDERFLOW_BIN_START, 2.5, FloatNear(3.76, 0.01), FloatNear(5.01, 0.01),
290 FloatNear(6.27, 0.01), FloatNear(7.53, 0.01), FloatNear(8.79, 0.01),
291 FloatNear(10.04, 0.01), 11.3));
292 }
293
getParsedExponentialBins(float min,float max,int count)294 BinStarts getParsedExponentialBins(float min, float max, int count) {
295 return getParsedGeneratedBins(min, max, count, EXPONENTIAL);
296 }
297
TEST_F(HistogramParsingUtilsTest,TestValidExponentialBins)298 TEST_F(HistogramParsingUtilsTest, TestValidExponentialBins) {
299 EXPECT_THAT(getParsedExponentialBins(5, 160, 5),
300 ElementsAre(UNDERFLOW_BIN_START, 5, 10, 20, 40, 80, 160));
301 EXPECT_THAT(getParsedExponentialBins(3, 1875, 4),
302 ElementsAre(UNDERFLOW_BIN_START, 3, FloatEq(15), FloatEq(75), FloatEq(375), 1875));
303 EXPECT_THAT(getParsedExponentialBins(1, 1000, 3),
304 ElementsAre(UNDERFLOW_BIN_START, 1, 10, 100, 1000));
305 }
306
getParsedExplicitBins(BinStarts bins)307 BinStarts getParsedExplicitBins(BinStarts bins) {
308 StatsdConfig config = createExplicitHistogramStatsdConfig(bins);
309
310 return getParsedBins(config.value_metric(0));
311 }
312
TEST_F(HistogramParsingUtilsTest,TestValidExplicitBins)313 TEST_F(HistogramParsingUtilsTest, TestValidExplicitBins) {
314 EXPECT_THAT(getParsedExplicitBins({0, 1, 2}), ElementsAre(UNDERFLOW_BIN_START, 0, 1, 2));
315 EXPECT_THAT(getParsedExplicitBins({-1, 5, 200}), ElementsAre(UNDERFLOW_BIN_START, -1, 5, 200));
316 }
317
TEST_F(HistogramParsingUtilsTest,TestMultipleHistogramBinConfigs)318 TEST_F(HistogramParsingUtilsTest, TestMultipleHistogramBinConfigs) {
319 StatsdConfig config = createGeneratedHistogramStatsdConfig(/* min */ -100, /* max */ 0,
320 /* count */ 5, LINEAR);
321 config.mutable_value_metric(0)->clear_aggregation_type();
322 config.mutable_value_metric(0)->add_aggregation_types(ValueMetric::HISTOGRAM);
323 config.mutable_value_metric(0)->add_aggregation_types(ValueMetric::HISTOGRAM);
324 config.mutable_value_metric(0)->mutable_value_field()->add_child()->set_field(2);
325 *config.mutable_value_metric(0)->add_histogram_bin_configs() =
326 createExplicitBinConfig(/* id */ 2, {1, 9, 30});
327
328 ParseHistogramBinConfigsResult result = parseHistogramBinConfigs(
329 config.value_metric(0),
330 /* aggregationTypes */ {ValueMetric::HISTOGRAM, ValueMetric::HISTOGRAM});
331 ASSERT_TRUE(holds_alternative<vector<optional<const BinStarts>>>(result));
332 const vector<optional<const BinStarts>>& histograms =
333 get<vector<optional<const BinStarts>>>(result);
334 ASSERT_EQ(histograms.size(), 2);
335
336 EXPECT_THAT(*(histograms[0]), ElementsAre(UNDERFLOW_BIN_START, -100, -80, -60, -40, -20, 0));
337 EXPECT_THAT(*(histograms[1]), ElementsAre(UNDERFLOW_BIN_START, 1, 9, 30));
338 }
339
340 } // anonymous namespace
341
342 } // namespace statsd
343 } // namespace os
344 } // namespace android
345 #else
346 GTEST_LOG_(INFO) << "This test does nothing.\n";
347 #endif
348