• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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