1 /*
2 * Copyright (C) 2018 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 <unistd.h>
18 #include <string>
19
20 #include <android-base/file.h>
21 #include <android-base/scopeguard.h>
22 #include <android-base/test_utils.h>
23 #include <gtest/gtest.h>
24
25 #include "CppGen.h"
26
27 namespace {
28
29 constexpr const char* kTestSyspropFile =
30 R"(owner: Platform
31 module: "android.sysprop.PlatformProperties"
32 prop {
33 api_name: "test_double"
34 type: Double
35 prop_name: "android.test_double"
36 scope: Internal
37 access: ReadWrite
38 }
39 prop {
40 api_name: "test_int"
41 type: Integer
42 prop_name: "android.test_int"
43 scope: Public
44 access: ReadWrite
45 }
46 prop {
47 api_name: "test_string"
48 type: String
49 prop_name: "android.test.string"
50 scope: Public
51 access: Readonly
52 legacy_prop_name: "legacy.android.test.string"
53 }
54 prop {
55 api_name: "test_enum"
56 type: Enum
57 prop_name: "android.test.enum"
58 enum_values: "a|b|c|D|e|f|G"
59 scope: Internal
60 access: ReadWrite
61 }
62 prop {
63 api_name: "test_BOOLeaN"
64 type: Boolean
65 prop_name: "ro.android.test.b"
66 scope: Public
67 access: Writeonce
68 }
69 prop {
70 api_name: "android_os_test-long"
71 type: Long
72 scope: Public
73 access: ReadWrite
74 }
75 prop {
76 api_name: "test_double_list"
77 type: DoubleList
78 scope: Internal
79 access: ReadWrite
80 }
81 prop {
82 api_name: "test_list_int"
83 type: IntegerList
84 scope: Public
85 access: ReadWrite
86 }
87 prop {
88 api_name: "test_strlist"
89 type: StringList
90 scope: Public
91 access: ReadWrite
92 deprecated: true
93 }
94 prop {
95 api_name: "el"
96 type: EnumList
97 enum_values: "enu|mva|lue"
98 scope: Internal
99 access: ReadWrite
100 deprecated: true
101 }
102 )";
103
104 constexpr const char* kExpectedHeaderOutput =
105 R"(// Generated by the sysprop generator. DO NOT EDIT!
106
107 #pragma once
108
109 #include <cstdint>
110 #include <optional>
111 #include <string>
112 #include <vector>
113
114 namespace android::sysprop::PlatformProperties {
115
116 std::optional<double> test_double();
117 bool test_double(const std::optional<double>& value);
118
119 std::optional<std::int32_t> test_int();
120 bool test_int(const std::optional<std::int32_t>& value);
121
122 std::optional<std::string> test_string();
123
124 enum class test_enum_values {
125 A,
126 B,
127 C,
128 D,
129 E,
130 F,
131 G,
132 };
133
134 std::optional<test_enum_values> test_enum();
135 bool test_enum(const std::optional<test_enum_values>& value);
136
137 std::optional<bool> test_BOOLeaN();
138 bool test_BOOLeaN(const std::optional<bool>& value);
139
140 std::optional<std::int64_t> android_os_test_long();
141 bool android_os_test_long(const std::optional<std::int64_t>& value);
142
143 std::vector<std::optional<double>> test_double_list();
144 bool test_double_list(const std::vector<std::optional<double>>& value);
145
146 std::vector<std::optional<std::int32_t>> test_list_int();
147 bool test_list_int(const std::vector<std::optional<std::int32_t>>& value);
148
149 [[deprecated]] std::vector<std::optional<std::string>> test_strlist();
150 [[deprecated]] bool test_strlist(const std::vector<std::optional<std::string>>& value);
151
152 enum class el_values {
153 ENU,
154 MVA,
155 LUE,
156 };
157
158 [[deprecated]] std::vector<std::optional<el_values>> el();
159 [[deprecated]] bool el(const std::vector<std::optional<el_values>>& value);
160
161 } // namespace android::sysprop::PlatformProperties
162 )";
163
164 constexpr const char* kExpectedPublicHeaderOutput =
165 R"(// Generated by the sysprop generator. DO NOT EDIT!
166
167 #pragma once
168
169 #include <cstdint>
170 #include <optional>
171 #include <string>
172 #include <vector>
173
174 namespace android::sysprop::PlatformProperties {
175
176 std::optional<std::int32_t> test_int();
177 bool test_int(const std::optional<std::int32_t>& value);
178
179 std::optional<std::string> test_string();
180
181 std::optional<bool> test_BOOLeaN();
182 bool test_BOOLeaN(const std::optional<bool>& value);
183
184 std::optional<std::int64_t> android_os_test_long();
185 bool android_os_test_long(const std::optional<std::int64_t>& value);
186
187 std::vector<std::optional<std::int32_t>> test_list_int();
188 bool test_list_int(const std::vector<std::optional<std::int32_t>>& value);
189
190 [[deprecated]] std::vector<std::optional<std::string>> test_strlist();
191 [[deprecated]] bool test_strlist(const std::vector<std::optional<std::string>>& value);
192
193 } // namespace android::sysprop::PlatformProperties
194 )";
195
196 constexpr const char* kExpectedSourceOutput =
197 R"(// Generated by the sysprop generator. DO NOT EDIT!
198
199 #include <properties/PlatformProperties.sysprop.h>
200
201 #include <cctype>
202 #include <cerrno>
203 #include <cstdio>
204 #include <cstring>
205 #include <limits>
206 #include <utility>
207
208 #include <strings.h>
209 #ifdef __BIONIC__
210 #include <sys/system_properties.h>
211 [[maybe_unused]] static bool SetProp(const char* key, const char* value) {
212 return __system_property_set(key, value) == 0;
213 }
214 #else
215 #include <android-base/properties.h>
216 [[maybe_unused]] static bool SetProp(const char* key, const char* value) {
217 android::base::SetProperty(key, value);
218 return true;
219 }
220 #endif
221
222 #include <android-base/parseint.h>
223 #include <log/log.h>
224
225 namespace {
226
227 using namespace android::sysprop::PlatformProperties;
228
229 template <typename T> T DoParse(const char* str);
230
231 constexpr const std::pair<const char*, test_enum_values> test_enum_list[] = {
232 {"a", test_enum_values::A},
233 {"b", test_enum_values::B},
234 {"c", test_enum_values::C},
235 {"D", test_enum_values::D},
236 {"e", test_enum_values::E},
237 {"f", test_enum_values::F},
238 {"G", test_enum_values::G},
239 };
240
241 template <>
242 std::optional<test_enum_values> DoParse(const char* str) {
243 for (auto [name, val] : test_enum_list) {
244 if (strcmp(str, name) == 0) {
245 return val;
246 }
247 }
248 return std::nullopt;
249 }
250
251 std::string FormatValue(std::optional<test_enum_values> value) {
252 if (!value) return "";
253 for (auto [name, val] : test_enum_list) {
254 if (val == *value) {
255 return name;
256 }
257 }
258 LOG_ALWAYS_FATAL("Invalid value %d for property android.test.enum", static_cast<std::int32_t>(*value));
259 __builtin_unreachable();
260 }
261
262 constexpr const std::pair<const char*, el_values> el_list[] = {
263 {"enu", el_values::ENU},
264 {"mva", el_values::MVA},
265 {"lue", el_values::LUE},
266 };
267
268 template <>
269 std::optional<el_values> DoParse(const char* str) {
270 for (auto [name, val] : el_list) {
271 if (strcmp(str, name) == 0) {
272 return val;
273 }
274 }
275 return std::nullopt;
276 }
277
278 std::string FormatValue(std::optional<el_values> value) {
279 if (!value) return "";
280 for (auto [name, val] : el_list) {
281 if (val == *value) {
282 return name;
283 }
284 }
285 LOG_ALWAYS_FATAL("Invalid value %d for property el", static_cast<std::int32_t>(*value));
286 __builtin_unreachable();
287 }
288
289 template <typename T> constexpr bool is_vector = false;
290
291 template <typename T> constexpr bool is_vector<std::vector<T>> = true;
292
293 template <> [[maybe_unused]] std::optional<bool> DoParse(const char* str) {
294 static constexpr const char* kYes[] = {"1", "true"};
295 static constexpr const char* kNo[] = {"0", "false"};
296
297 for (const char* yes : kYes) {
298 if (strcasecmp(yes, str) == 0) return std::make_optional(true);
299 }
300
301 for (const char* no : kNo) {
302 if (strcasecmp(no, str) == 0) return std::make_optional(false);
303 }
304
305 return std::nullopt;
306 }
307
308 template <> [[maybe_unused]] std::optional<std::int32_t> DoParse(const char* str) {
309 std::int32_t ret;
310 return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
311 }
312
313 template <> [[maybe_unused]] std::optional<std::uint32_t> DoParse(const char* str) {
314 std::uint32_t ret;
315 return android::base::ParseUint(str, &ret) ? std::make_optional(ret) : std::nullopt;
316 }
317
318 template <> [[maybe_unused]] std::optional<std::int64_t> DoParse(const char* str) {
319 std::int64_t ret;
320 return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
321 }
322
323 template <> [[maybe_unused]] std::optional<std::uint64_t> DoParse(const char* str) {
324 std::uint64_t ret;
325 return android::base::ParseUint(str, &ret) ? std::make_optional(ret) : std::nullopt;
326 }
327
328 template <> [[maybe_unused]] std::optional<double> DoParse(const char* str) {
329 int old_errno = errno;
330 errno = 0;
331 char* end;
332 double ret = std::strtod(str, &end);
333 if (errno != 0) {
334 return std::nullopt;
335 }
336 if (str == end || *end != '\0') {
337 errno = EINVAL;
338 return std::nullopt;
339 }
340 errno = old_errno;
341 return std::make_optional(ret);
342 }
343
344 template <> [[maybe_unused]] std::optional<std::string> DoParse(const char* str) {
345 return *str == '\0' ? std::nullopt : std::make_optional(str);
346 }
347
348 template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) {
349 Vec ret;
350 if (*str == '\0') return ret;
351 const char* p = str;
352 for (;;) {
353 const char* r = p;
354 std::string value;
355 while (*r != ',') {
356 if (*r == '\\') ++r;
357 if (*r == '\0') break;
358 value += *r++;
359 }
360 ret.emplace_back(DoParse<typename Vec::value_type>(value.c_str()));
361 if (*r == '\0') break;
362 p = r + 1;
363 }
364 return ret;
365 }
366
367 template <typename T> inline T TryParse(const char* str) {
368 if constexpr(is_vector<T>) {
369 return DoParseList<T>(str);
370 } else {
371 return DoParse<T>(str);
372 }
373 }
374
375 [[maybe_unused]] std::string FormatValue(const std::optional<std::int32_t>& value) {
376 return value ? std::to_string(*value) : "";
377 }
378
379 [[maybe_unused]] std::string FormatValue(const std::optional<std::uint32_t>& value) {
380 return value ? std::to_string(*value) : "";
381 }
382
383 [[maybe_unused]] std::string FormatValue(const std::optional<std::int64_t>& value) {
384 return value ? std::to_string(*value) : "";
385 }
386
387 [[maybe_unused]] std::string FormatValue(const std::optional<std::uint64_t>& value) {
388 return value ? std::to_string(*value) : "";
389 }
390
391 [[maybe_unused]] std::string FormatValue(const std::optional<double>& value) {
392 if (!value) return "";
393 char buf[1024];
394 std::sprintf(buf, "%.*g", std::numeric_limits<double>::max_digits10, *value);
395 return buf;
396 }
397
398 [[maybe_unused]] std::string FormatValue(const std::optional<bool>& value) {
399 return value ? (*value ? "true" : "false") : "";
400 }
401
402 template <typename T>
403 [[maybe_unused]] std::string FormatValue(const std::vector<T>& value) {
404 if (value.empty()) return "";
405
406 std::string ret;
407 bool first = true;
408
409 for (auto&& element : value) {
410 if (!first) ret += ',';
411 else first = false;
412 if constexpr(std::is_same_v<T, std::optional<std::string>>) {
413 if (element) {
414 for (char c : *element) {
415 if (c == '\\' || c == ',') ret += '\\';
416 ret += c;
417 }
418 }
419 } else {
420 ret += FormatValue(element);
421 }
422 }
423
424 return ret;
425 }
426
427 template <typename T>
428 T GetProp(const char* key, const char* legacy = nullptr) {
429 std::string value;
430 #ifdef __BIONIC__
431 auto pi = __system_property_find(key);
432 if (pi != nullptr) {
433 __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
434 *static_cast<std::string*>(cookie) = value;
435 }, &value);
436 }
437 #else
438 value = android::base::GetProperty(key, "");
439 #endif
440 if (value.empty() && legacy) {
441 ALOGV("prop %s doesn't exist; fallback to legacy prop %s", key, legacy);
442 return GetProp<T>(legacy);
443 }
444 return TryParse<T>(value.c_str());
445 }
446
447 } // namespace
448
449 namespace android::sysprop::PlatformProperties {
450
451 std::optional<double> test_double() {
452 return GetProp<std::optional<double>>("android.test_double");
453 }
454
455 bool test_double(const std::optional<double>& value) {
456 return SetProp("android.test_double", FormatValue(value).c_str());
457 }
458
459 std::optional<std::int32_t> test_int() {
460 return GetProp<std::optional<std::int32_t>>("android.test_int");
461 }
462
463 bool test_int(const std::optional<std::int32_t>& value) {
464 return SetProp("android.test_int", FormatValue(value).c_str());
465 }
466
467 std::optional<std::string> test_string() {
468 return GetProp<std::optional<std::string>>("android.test.string", "legacy.android.test.string");
469 }
470
471 std::optional<test_enum_values> test_enum() {
472 return GetProp<std::optional<test_enum_values>>("android.test.enum");
473 }
474
475 bool test_enum(const std::optional<test_enum_values>& value) {
476 return SetProp("android.test.enum", FormatValue(value).c_str());
477 }
478
479 std::optional<bool> test_BOOLeaN() {
480 return GetProp<std::optional<bool>>("ro.android.test.b");
481 }
482
483 bool test_BOOLeaN(const std::optional<bool>& value) {
484 return SetProp("ro.android.test.b", FormatValue(value).c_str());
485 }
486
487 std::optional<std::int64_t> android_os_test_long() {
488 return GetProp<std::optional<std::int64_t>>("android_os_test-long");
489 }
490
491 bool android_os_test_long(const std::optional<std::int64_t>& value) {
492 return SetProp("android_os_test-long", FormatValue(value).c_str());
493 }
494
495 std::vector<std::optional<double>> test_double_list() {
496 return GetProp<std::vector<std::optional<double>>>("test_double_list");
497 }
498
499 bool test_double_list(const std::vector<std::optional<double>>& value) {
500 return SetProp("test_double_list", FormatValue(value).c_str());
501 }
502
503 std::vector<std::optional<std::int32_t>> test_list_int() {
504 return GetProp<std::vector<std::optional<std::int32_t>>>("test_list_int");
505 }
506
507 bool test_list_int(const std::vector<std::optional<std::int32_t>>& value) {
508 return SetProp("test_list_int", FormatValue(value).c_str());
509 }
510
511 std::vector<std::optional<std::string>> test_strlist() {
512 return GetProp<std::vector<std::optional<std::string>>>("test_strlist");
513 }
514
515 bool test_strlist(const std::vector<std::optional<std::string>>& value) {
516 return SetProp("test_strlist", FormatValue(value).c_str());
517 }
518
519 std::vector<std::optional<el_values>> el() {
520 return GetProp<std::vector<std::optional<el_values>>>("el");
521 }
522
523 bool el(const std::vector<std::optional<el_values>>& value) {
524 return SetProp("el", FormatValue(value).c_str());
525 }
526
527 } // namespace android::sysprop::PlatformProperties
528 )";
529
530 } // namespace
531
532 using namespace std::string_literals;
533
TEST(SyspropTest,CppGenTest)534 TEST(SyspropTest, CppGenTest) {
535 TemporaryDir temp_dir;
536
537 std::string temp_sysprop_path = temp_dir.path + "/PlatformProperties.sysprop"s;
538 ASSERT_TRUE(
539 android::base::WriteStringToFile(kTestSyspropFile, temp_sysprop_path));
540
541 auto sysprop_deleter = android::base::make_scope_guard(
542 [&] { unlink(temp_sysprop_path.c_str()); });
543
544 ASSERT_RESULT_OK(GenerateCppFiles(temp_sysprop_path, temp_dir.path,
545 temp_dir.path + "/public"s, temp_dir.path,
546 "properties/PlatformProperties.sysprop.h"));
547
548 std::string header_output_path =
549 temp_dir.path + "/PlatformProperties.sysprop.h"s;
550 std::string public_header_output_path =
551 temp_dir.path + "/public/PlatformProperties.sysprop.h"s;
552 std::string source_output_path =
553 temp_dir.path + "/PlatformProperties.sysprop.cpp"s;
554
555 auto generated_file_deleter = android::base::make_scope_guard([&] {
556 unlink(header_output_path.c_str());
557 unlink(public_header_output_path.c_str());
558 unlink(source_output_path.c_str());
559 });
560
561 std::string header_output;
562 ASSERT_TRUE(android::base::ReadFileToString(header_output_path,
563 &header_output, true));
564 EXPECT_EQ(header_output, kExpectedHeaderOutput);
565
566 std::string public_header_output;
567 ASSERT_TRUE(android::base::ReadFileToString(public_header_output_path,
568 &public_header_output, true));
569 EXPECT_EQ(public_header_output, kExpectedPublicHeaderOutput);
570
571 std::string source_output;
572 ASSERT_TRUE(android::base::ReadFileToString(source_output_path,
573 &source_output, true));
574 EXPECT_EQ(source_output, kExpectedSourceOutput);
575 }
576