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 <gtest/gtest.h>
18 #include <pixelstats/MmMetricsReporter.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 #include "MmMetricsGoldenAtomFieldTypes.h"
23 #include "MmMetricsGoldenResults.h"
24 #include "MockMmMetricsReporter.h"
25 #include "VendorAtomIntValueUtil.h"
26
27 #ifndef ARRAY_SIZE
28 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
29 #endif
30
31 namespace android {
32 namespace hardware {
33 namespace google {
34 namespace pixel {
35
36 using mm_metrics_atom_field_test_golden_results::MmMetricsGcmaPerDayHistogram_field_types;
37 using mm_metrics_atom_field_test_golden_results::MmMetricsGcmaPerDaySimple_field_types;
38 using mm_metrics_atom_field_test_golden_results::MmMetricsGcmaPerHour_field_types;
39 using mm_metrics_atom_field_test_golden_results::MmMetricsOomGroupMemUsage_field_types;
40 using mm_metrics_atom_field_test_golden_results::PixelMmMetricsPerDay_field_types;
41 using mm_metrics_atom_field_test_golden_results::PixelMmMetricsPerHour_field_types;
42
43 using mm_metrics_reporter_test_golden_result::MmMetricsGcmaPerDayHistogram_golden;
44 using mm_metrics_reporter_test_golden_result::MmMetricsGcmaPerDaySimple_golden;
45 using mm_metrics_reporter_test_golden_result::MmMetricsGcmaPerHour_golden;
46 using mm_metrics_reporter_test_golden_result::MmMetricsOomGroupMemUsage_golden;
47 using mm_metrics_reporter_test_golden_result::PixelMmMetricsPerDay_golden;
48 using mm_metrics_reporter_test_golden_result::PixelMmMetricsPerHour_golden;
49
50 const char *data_base_path = "/data/local/tmp/test/pixelstats_mm_test/data";
51
TEST(MmMetricsReporterTest,MmMetricsPerHourAtomFieldOffsetTypeTest)52 TEST(MmMetricsReporterTest, MmMetricsPerHourAtomFieldOffsetTypeTest) {
53 int i = -1;
54 uint64_t golden_result;
55 int field_type;
56 std::vector<VendorAtomValue> values;
57 MockMmMetricsReporter mreport;
58 const std::string data_path0 = std::string(data_base_path) + "/test_data_0";
59 const std::string data_path1 = std::string(data_base_path) + "/test_data_1";
60
61 // Assert failure means the test case itself has a bug.
62 ASSERT_EQ(ARRAY_SIZE(PixelMmMetricsPerHour_golden),
63 ARRAY_SIZE(PixelMmMetricsPerHour_field_types));
64
65 /**
66 * In test code we use setBasePath() to read different data sets for simulating
67 * different timing reads of a sysfs node.
68 */
69
70 /**
71 * aggregatePixelMmMetricsPer5Min() aggregates PSI into max, min, and avg.
72 * For the regular code, it will be called 12 times per hour (i.e. once per 5min)
73 * For test code we do 6 times: enough for testing.
74 * e.g. here average = (3 x data0 + 3 x data1) / 6 == avg of data 0, 1
75 * The following sequence simulate regular code obtaining sysfs nodes into
76 * values[] array (i.e. atom), ready to be sent to the server
77 */
78 mreport.setBasePath(data_path0);
79 mreport.aggregatePixelMmMetricsPer5Min();
80 mreport.aggregatePixelMmMetricsPer5Min();
81 mreport.aggregatePixelMmMetricsPer5Min();
82 mreport.setBasePath(data_path1);
83 mreport.aggregatePixelMmMetricsPer5Min();
84 mreport.aggregatePixelMmMetricsPer5Min();
85 mreport.aggregatePixelMmMetricsPer5Min();
86
87 // other fields from data set #0
88 mreport.setBasePath(data_path0);
89 values = mreport.genPixelMmMetricsPerHour();
90
91 // Validate the atom: compare with golden results
92 EXPECT_EQ(values.size(), ARRAY_SIZE(PixelMmMetricsPerHour_field_types));
93 for (auto const &v : values) {
94 i++;
95 golden_result = PixelMmMetricsPerHour_golden[i];
96 field_type = PixelMmMetricsPerHour_field_types[i];
97 if (golden_result == -1)
98 continue; // no need to test (e.g. deprecated field)
99
100 EXPECT_EQ(static_cast<int>(v.getTag()), field_type) << "type mismatch at offset " << i;
101 EXPECT_EQ(getVendorAtomIntValue(v), golden_result) << "value mismatch at offset " << i;
102 }
103 }
104
TEST(MmMetricsReporterTest,MmMetricsPerDayAtomFieldOffsetTypeTest)105 TEST(MmMetricsReporterTest, MmMetricsPerDayAtomFieldOffsetTypeTest) {
106 int i = -1;
107 uint64_t golden_result;
108 int field_type;
109 std::vector<VendorAtomValue> values;
110 MockMmMetricsReporter mreport;
111 const std::string data_path0 = std::string(data_base_path) + "/test_data_0";
112 const std::string data_path1 = std::string(data_base_path) + "/test_data_1";
113
114 // Assert failure means the test case itself has a bug.
115 ASSERT_EQ(ARRAY_SIZE(PixelMmMetricsPerDay_golden),
116 ARRAY_SIZE(PixelMmMetricsPerDay_field_types));
117
118 mreport.setBasePath(data_path0);
119 values = mreport.genPixelMmMetricsPerDay();
120
121 // PixelMmMetricsPerDay calculatd the difference of consecutive readings.
122 // So, it will not send values[] at the 1st read. (i.e. empty for the 1st read)
123 EXPECT_EQ(values.size(), 0);
124 values.clear();
125
126 mreport.setBasePath(data_path1);
127 values = mreport.genPixelMmMetricsPerDay();
128
129 // Per Day metrics (diffs) should be calculated, values[] will be non-empty now.
130 // number of data should be the same as the number of fields in the atom.
131 EXPECT_EQ(values.size(), ARRAY_SIZE(PixelMmMetricsPerDay_field_types));
132 for (auto const &v : values) {
133 i++;
134 EXPECT_LT(i, ARRAY_SIZE(PixelMmMetricsPerDay_field_types));
135
136 golden_result = PixelMmMetricsPerDay_golden[i];
137 field_type = PixelMmMetricsPerDay_field_types[i];
138 if (golden_result == -1)
139 continue; // no need to test (e.g. deprecated field)
140
141 EXPECT_EQ(static_cast<int>(v.getTag()), field_type) << "type mismatch at offset " << i;
142 EXPECT_EQ(getVendorAtomIntValue(v), golden_result) << "value mismatch at offset " << i;
143 }
144 }
145
TEST(MmMetricsReporterTest,MmMetricsOomGroupMemUsageSuccess)146 TEST(MmMetricsReporterTest, MmMetricsOomGroupMemUsageSuccess) {
147 constexpr int kNumTests = 2;
148 MockMmMetricsReporter mreport;
149 const std::string data_path[kNumTests] = {
150 std::string(data_base_path) + "/test_data_0",
151 std::string(data_base_path) + "/test_data_1",
152 };
153 std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
154 int32_t og_metric_uid[kNumTests];
155 auto &golden = MmMetricsOomGroupMemUsage_golden;
156 auto &gold_ftype = MmMetricsOomGroupMemUsage_field_types;
157
158 constexpr int kNumFields = ARRAY_SIZE(MmMetricsOomGroupMemUsage_field_types);
159 constexpr int kNumLines = ARRAY_SIZE(golden[0]);
160
161 ASSERT_LT(kNumLines, 100);
162
163 // Check testcase consistency (if fail, the test case itself has some bug)
164 ASSERT_EQ(ARRAY_SIZE(golden), kNumTests);
165 ASSERT_EQ(ARRAY_SIZE(golden[1]), kNumLines);
166 ASSERT_EQ(ARRAY_SIZE(MmMetricsOomGroupMemUsage_field_types), kNumFields);
167
168 for (int i = 0; i < kNumTests; i++) {
169 for (int j = 0; j < kNumLines; j++) {
170 // golden result does not have UID field, which is date/time based unique ID.
171 ASSERT_EQ(ARRAY_SIZE(golden[i][j]), kNumFields - 1);
172 }
173 }
174
175 for (int test_iteration = 0; test_iteration < kNumTests; ++test_iteration) {
176 // setup
177 mreport.setBasePath(data_path[test_iteration]);
178
179 // --- start test ---
180 ASSERT_TRUE(mreport.readMmProcessUsageByOomGroup(&ogusage));
181 ASSERT_EQ(ogusage.size(), kNumLines);
182
183 int line = 0;
184 for (const auto &u : ogusage) {
185 std::vector<VendorAtomValue> values =
186 mreport.genMmProcessUsageByOomGroupSnapshotAtom(u);
187 int32_t &uid = og_metric_uid[test_iteration];
188
189 // check size
190 ASSERT_EQ(values.size(), kNumFields)
191 << "Size mismatch: test# " << test_iteration << " line " << line;
192
193 if (line == 0) {
194 uid = getVendorAtomIntValue(values[0]);
195 } else {
196 // check UID
197 EXPECT_EQ(getVendorAtomIntValue(values[0]), uid)
198 << "value mismatch: test# " << test_iteration << " line " << line
199 << " field 0";
200 }
201
202 for (int field = 1; field < kNumFields; ++field) {
203 // check types
204 EXPECT_EQ(static_cast<int>(values[field].getTag()), gold_ftype[field])
205 << "type mismatch: test# " << test_iteration << " line " << line
206 << " field " << field;
207
208 if (static_cast<int>(values[field].getTag()) != gold_ftype[field])
209 continue; // no checking values when the type is already wrong.
210
211 // check values
212 EXPECT_EQ(getVendorAtomIntValue(values[field]),
213 golden[test_iteration][line][field - 1])
214 << "value mismatch: test# " << test_iteration << " line " << line
215 << " field " << field;
216 }
217 line++;
218 }
219 // --- end test ---
220 }
221
222 // metric_uid must be unique
223 EXPECT_NE(og_metric_uid[0], og_metric_uid[1]);
224 }
225
TEST(MmMetricsReporterTest,MmMetricsOomGroupMemUsageFailFileNotFound)226 TEST(MmMetricsReporterTest, MmMetricsOomGroupMemUsageFailFileNotFound) {
227 constexpr int kNumTests = 2;
228 MockMmMetricsReporter mreport;
229 const std::string data_path = std::string(data_base_path) + "/nonexisting_dir";
230 std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
231 int32_t uid;
232
233 // setup
234 mreport.setBasePath(data_path);
235
236 // --- start test ---
237 ASSERT_FALSE(mreport.readMmProcessUsageByOomGroup(&ogusage));
238 ASSERT_EQ(ogusage.size(), 0);
239 }
240
file_exists(const char * const path)241 static bool file_exists(const char *const path) {
242 struct stat sbuf;
243
244 return (stat(path, &sbuf) == 0);
245 }
246
TEST(MmMetricsReporterTest,MmMetricsOomGroupMemUsageMultipleFailCases)247 TEST(MmMetricsReporterTest, MmMetricsOomGroupMemUsageMultipleFailCases) {
248 constexpr int kNumTests = 8;
249 MockMmMetricsReporter mreport;
250 const std::string data_path[kNumTests] = {
251 std::string(data_base_path) + "/test_data_oom_usage_fail/1",
252 std::string(data_base_path) + "/test_data_oom_usage_fail/2",
253 std::string(data_base_path) + "/test_data_oom_usage_fail/3",
254 std::string(data_base_path) + "/test_data_oom_usage_fail/4",
255 std::string(data_base_path) + "/test_data_oom_usage_fail/5",
256 std::string(data_base_path) + "/test_data_oom_usage_fail/6",
257 std::string(data_base_path) + "/test_data_oom_usage_fail/7",
258 std::string(data_base_path) + "/test_data_oom_usage_fail/8",
259 };
260 const char *file = "oom_mm_usage";
261 std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
262
263 for (int test_iteration = 0; test_iteration < kNumTests; ++test_iteration) {
264 // setup
265 mreport.setBasePath(data_path[test_iteration]);
266
267 // check file exist, otherwise it is testing "file not found" rather than the desired test
268 ASSERT_TRUE(file_exists((data_path[test_iteration] + "/" + file).c_str()));
269
270 // --- start test ---
271 ASSERT_FALSE(mreport.readMmProcessUsageByOomGroup(&ogusage))
272 << "Iteration " << test_iteration << ": test fail.";
273 ASSERT_EQ(ogusage.size(), 0) << "Iteration " << test_iteration << ": test fail.";
274 }
275 }
276
TEST(MmMetricsReporterTest,MmMetricsGcmaPerHourSuccess)277 TEST(MmMetricsReporterTest, MmMetricsGcmaPerHourSuccess) {
278 MockMmMetricsReporter mreport;
279 const std::string data_path = std::string(data_base_path) + "/test_data_0";
280 auto &golden = MmMetricsGcmaPerHour_golden;
281 auto &gold_ftype = MmMetricsGcmaPerHour_field_types;
282
283 constexpr int kNumFields = ARRAY_SIZE(gold_ftype);
284 constexpr int kNumLines = ARRAY_SIZE(golden);
285
286 // Check testcase consistency (if fail, the test case itself has some bug)
287 ASSERT_EQ(kNumFields, kNumLines);
288
289 // setup
290 mreport.setBasePath(data_path);
291
292 // --- start test ---
293 std::vector<VendorAtomValue> values = mreport.readAndGenGcmaPerHour();
294
295 // check size
296 ASSERT_EQ(values.size(), kNumLines);
297
298 for (int field = 0; field < kNumFields; ++field) {
299 // check type
300 EXPECT_EQ(static_cast<int>(values[field].getTag()), gold_ftype[field])
301 << "type mismatch @ field #" << field;
302
303 if (static_cast<int>(values[field].getTag()) != gold_ftype[field])
304 continue; // no checking the value when the type is wrong.
305
306 // check value
307 EXPECT_EQ(getVendorAtomIntValue(values[field]), golden[field])
308 << "value mismatch @ field #" << field;
309 }
310 }
311
TEST(MmMetricsReporterTest,MmMetricsGcmaPerDaySuccess)312 TEST(MmMetricsReporterTest, MmMetricsGcmaPerDaySuccess) {
313 MockMmMetricsReporter mreport;
314 const std::string data_path = std::string(data_base_path) + "/test_data_0";
315 auto &golden_simple = MmMetricsGcmaPerDaySimple_golden;
316 auto &golden_histogram = MmMetricsGcmaPerDayHistogram_golden;
317
318 auto &gold_simple_ftype = MmMetricsGcmaPerDaySimple_field_types;
319 auto &gold_histogram_ftype = MmMetricsGcmaPerDayHistogram_field_types;
320
321 constexpr int kNumSimpleValues = 4;
322 constexpr int kNumHistogramValues = 4;
323 // total field num in atom values need to count the histogram array as one.
324 constexpr int kNumAtomValues = kNumSimpleValues + 1;
325
326 // Check testcase consistency (if fail, the test case itself has some bug)
327 ASSERT_EQ(ARRAY_SIZE(golden_simple), kNumSimpleValues);
328 ASSERT_EQ(ARRAY_SIZE(golden_histogram), kNumHistogramValues);
329 ASSERT_EQ(ARRAY_SIZE(gold_simple_ftype), kNumSimpleValues + 1); // count the last array type
330 ASSERT_EQ(ARRAY_SIZE(gold_histogram_ftype), kNumHistogramValues);
331
332 // setup
333 mreport.setBasePath(data_path);
334
335 // --- start test ---
336 std::vector<VendorAtomValue> values = mreport.readAndGenGcmaPerDay();
337
338 /*
339 * check size +1:
340 * Histogram in the form of a vector in the last element of 'Simple' value array.
341 */
342 ASSERT_EQ(values.size(), kNumAtomValues);
343
344 // check 'simple' values
345 for (int field = 0; field < kNumSimpleValues; ++field) {
346 // check type
347 EXPECT_EQ(static_cast<int>(values[field].getTag()), gold_simple_ftype[field])
348 << "type mismatch @ field #" << field;
349
350 if (static_cast<int>(values[field].getTag()) != gold_simple_ftype[field])
351 continue; // no checking the value when the type is wrong.
352
353 if (field == kNumAtomValues - 1)
354 continue; // same as break. The last one is an array, compare type only here.
355
356 EXPECT_EQ(getVendorAtomIntValue(values[field]), golden_simple[field])
357 << "value mismatch @ field #" << field;
358 }
359
360 // check array validity
361 auto &arrAtomValue = values[kNumAtomValues - 1];
362 const std::optional<std::vector<int64_t>> &repeatedLongValue =
363 arrAtomValue.get<VendorAtomValue::repeatedLongValue>();
364 ASSERT_TRUE(repeatedLongValue.has_value());
365
366 // check array size
367 ASSERT_EQ(repeatedLongValue.value().size(), kNumHistogramValues);
368
369 // check array values
370 for (int field = 0; field < kNumHistogramValues; ++field) {
371 EXPECT_EQ(repeatedLongValue.value()[field], golden_histogram[field]);
372 }
373 }
374
375 } // namespace pixel
376 } // namespace google
377 } // namespace hardware
378 } // namespace android
379