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::int64_t> DoParse(const char* str) {
314 std::int64_t ret;
315 return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
316 }
317
318 template <> [[maybe_unused]] std::optional<double> DoParse(const char* str) {
319 int old_errno = errno;
320 errno = 0;
321 char* end;
322 double ret = std::strtod(str, &end);
323 if (errno != 0) {
324 return std::nullopt;
325 }
326 if (str == end || *end != '\0') {
327 errno = EINVAL;
328 return std::nullopt;
329 }
330 errno = old_errno;
331 return std::make_optional(ret);
332 }
333
334 template <> [[maybe_unused]] std::optional<std::string> DoParse(const char* str) {
335 return *str == '\0' ? std::nullopt : std::make_optional(str);
336 }
337
338 template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) {
339 Vec ret;
340 if (*str == '\0') return ret;
341 const char* p = str;
342 for (;;) {
343 const char* r = p;
344 std::string value;
345 while (*r != ',') {
346 if (*r == '\\') ++r;
347 if (*r == '\0') break;
348 value += *r++;
349 }
350 ret.emplace_back(DoParse<typename Vec::value_type>(value.c_str()));
351 if (*r == '\0') break;
352 p = r + 1;
353 }
354 return ret;
355 }
356
357 template <typename T> inline T TryParse(const char* str) {
358 if constexpr(is_vector<T>) {
359 return DoParseList<T>(str);
360 } else {
361 return DoParse<T>(str);
362 }
363 }
364
365 [[maybe_unused]] std::string FormatValue(const std::optional<std::int32_t>& value) {
366 return value ? std::to_string(*value) : "";
367 }
368
369 [[maybe_unused]] std::string FormatValue(const std::optional<std::int64_t>& value) {
370 return value ? std::to_string(*value) : "";
371 }
372
373 [[maybe_unused]] std::string FormatValue(const std::optional<double>& value) {
374 if (!value) return "";
375 char buf[1024];
376 std::sprintf(buf, "%.*g", std::numeric_limits<double>::max_digits10, *value);
377 return buf;
378 }
379
380 [[maybe_unused]] std::string FormatValue(const std::optional<bool>& value) {
381 return value ? (*value ? "true" : "false") : "";
382 }
383
384 template <typename T>
385 [[maybe_unused]] std::string FormatValue(const std::vector<T>& value) {
386 if (value.empty()) return "";
387
388 std::string ret;
389 bool first = true;
390
391 for (auto&& element : value) {
392 if (!first) ret += ',';
393 else first = false;
394 if constexpr(std::is_same_v<T, std::optional<std::string>>) {
395 if (element) {
396 for (char c : *element) {
397 if (c == '\\' || c == ',') ret += '\\';
398 ret += c;
399 }
400 }
401 } else {
402 ret += FormatValue(element);
403 }
404 }
405
406 return ret;
407 }
408
409 template <typename T>
410 T GetProp(const char* key, const char* legacy = nullptr) {
411 std::string value;
412 #ifdef __BIONIC__
413 auto pi = __system_property_find(key);
414 if (pi != nullptr) {
415 __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
416 *static_cast<std::string*>(cookie) = value;
417 }, &value);
418 }
419 #else
420 value = android::base::GetProperty(key, "");
421 #endif
422 if (value.empty() && legacy) {
423 ALOGD("prop %s doesn't exist; fallback to legacy prop %s", key, legacy);
424 return GetProp<T>(legacy);
425 }
426 return TryParse<T>(value.c_str());
427 }
428
429 } // namespace
430
431 namespace android::sysprop::PlatformProperties {
432
433 std::optional<double> test_double() {
434 return GetProp<std::optional<double>>("android.test_double");
435 }
436
437 bool test_double(const std::optional<double>& value) {
438 return SetProp("android.test_double", FormatValue(value).c_str()) == 0;
439 }
440
441 std::optional<std::int32_t> test_int() {
442 return GetProp<std::optional<std::int32_t>>("android.test_int");
443 }
444
445 bool test_int(const std::optional<std::int32_t>& value) {
446 return SetProp("android.test_int", FormatValue(value).c_str()) == 0;
447 }
448
449 std::optional<std::string> test_string() {
450 return GetProp<std::optional<std::string>>("android.test.string", "legacy.android.test.string");
451 }
452
453 std::optional<test_enum_values> test_enum() {
454 return GetProp<std::optional<test_enum_values>>("android.test.enum");
455 }
456
457 bool test_enum(const std::optional<test_enum_values>& value) {
458 return SetProp("android.test.enum", FormatValue(value).c_str()) == 0;
459 }
460
461 std::optional<bool> test_BOOLeaN() {
462 return GetProp<std::optional<bool>>("ro.android.test.b");
463 }
464
465 bool test_BOOLeaN(const std::optional<bool>& value) {
466 return SetProp("ro.android.test.b", FormatValue(value).c_str()) == 0;
467 }
468
469 std::optional<std::int64_t> android_os_test_long() {
470 return GetProp<std::optional<std::int64_t>>("android_os_test-long");
471 }
472
473 bool android_os_test_long(const std::optional<std::int64_t>& value) {
474 return SetProp("android_os_test-long", FormatValue(value).c_str()) == 0;
475 }
476
477 std::vector<std::optional<double>> test_double_list() {
478 return GetProp<std::vector<std::optional<double>>>("test_double_list");
479 }
480
481 bool test_double_list(const std::vector<std::optional<double>>& value) {
482 return SetProp("test_double_list", FormatValue(value).c_str()) == 0;
483 }
484
485 std::vector<std::optional<std::int32_t>> test_list_int() {
486 return GetProp<std::vector<std::optional<std::int32_t>>>("test_list_int");
487 }
488
489 bool test_list_int(const std::vector<std::optional<std::int32_t>>& value) {
490 return SetProp("test_list_int", FormatValue(value).c_str()) == 0;
491 }
492
493 std::vector<std::optional<std::string>> test_strlist() {
494 return GetProp<std::vector<std::optional<std::string>>>("test_strlist");
495 }
496
497 bool test_strlist(const std::vector<std::optional<std::string>>& value) {
498 return SetProp("test_strlist", FormatValue(value).c_str()) == 0;
499 }
500
501 std::vector<std::optional<el_values>> el() {
502 return GetProp<std::vector<std::optional<el_values>>>("el");
503 }
504
505 bool el(const std::vector<std::optional<el_values>>& value) {
506 return SetProp("el", FormatValue(value).c_str()) == 0;
507 }
508
509 } // namespace android::sysprop::PlatformProperties
510 )";
511
512 } // namespace
513
514 using namespace std::string_literals;
515
TEST(SyspropTest,CppGenTest)516 TEST(SyspropTest, CppGenTest) {
517 TemporaryDir temp_dir;
518
519 std::string temp_sysprop_path = temp_dir.path + "/PlatformProperties.sysprop"s;
520 ASSERT_TRUE(
521 android::base::WriteStringToFile(kTestSyspropFile, temp_sysprop_path));
522
523 auto sysprop_deleter = android::base::make_scope_guard(
524 [&] { unlink(temp_sysprop_path.c_str()); });
525
526 ASSERT_RESULT_OK(GenerateCppFiles(temp_sysprop_path, temp_dir.path,
527 temp_dir.path + "/public"s, temp_dir.path,
528 "properties/PlatformProperties.sysprop.h"));
529
530 std::string header_output_path =
531 temp_dir.path + "/PlatformProperties.sysprop.h"s;
532 std::string public_header_output_path =
533 temp_dir.path + "/public/PlatformProperties.sysprop.h"s;
534 std::string source_output_path =
535 temp_dir.path + "/PlatformProperties.sysprop.cpp"s;
536
537 auto generated_file_deleter = android::base::make_scope_guard([&] {
538 unlink(header_output_path.c_str());
539 unlink(public_header_output_path.c_str());
540 unlink(source_output_path.c_str());
541 });
542
543 std::string header_output;
544 ASSERT_TRUE(android::base::ReadFileToString(header_output_path,
545 &header_output, true));
546 EXPECT_EQ(header_output, kExpectedHeaderOutput);
547
548 std::string public_header_output;
549 ASSERT_TRUE(android::base::ReadFileToString(public_header_output_path,
550 &public_header_output, true));
551 EXPECT_EQ(public_header_output, kExpectedPublicHeaderOutput);
552
553 std::string source_output;
554 ASSERT_TRUE(android::base::ReadFileToString(source_output_path,
555 &source_output, true));
556 EXPECT_EQ(source_output, kExpectedSourceOutput);
557 }
558