/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "CppGen.h" namespace { constexpr const char* kTestSyspropFile = R"(owner: Platform module: "android.sysprop.PlatformProperties" prop { api_name: "test_double" type: Double prop_name: "android.test_double" scope: Internal access: ReadWrite } prop { api_name: "test_int" type: Integer prop_name: "android.test_int" scope: Public access: ReadWrite } prop { api_name: "test_string" type: String prop_name: "android.test.string" scope: Public access: Readonly legacy_prop_name: "legacy.android.test.string" } prop { api_name: "test_enum" type: Enum prop_name: "android.test.enum" enum_values: "a|b|c|D|e|f|G" scope: Internal access: ReadWrite } prop { api_name: "test_BOOLeaN" type: Boolean prop_name: "ro.android.test.b" scope: Public access: Writeonce } prop { api_name: "android_os_test-long" type: Long scope: Public access: ReadWrite } prop { api_name: "test_double_list" type: DoubleList scope: Internal access: ReadWrite } prop { api_name: "test_list_int" type: IntegerList scope: Public access: ReadWrite } prop { api_name: "test_strlist" type: StringList scope: Public access: ReadWrite deprecated: true } prop { api_name: "el" type: EnumList enum_values: "enu|mva|lue" scope: Internal access: ReadWrite deprecated: true } )"; constexpr const char* kExpectedHeaderOutput = R"(// Generated by the sysprop generator. DO NOT EDIT! #pragma once #include #include #include #include namespace android::sysprop::PlatformProperties { std::optional test_double(); bool test_double(const std::optional& value); std::optional test_int(); bool test_int(const std::optional& value); std::optional test_string(); enum class test_enum_values { A, B, C, D, E, F, G, }; std::optional test_enum(); bool test_enum(const std::optional& value); std::optional test_BOOLeaN(); bool test_BOOLeaN(const std::optional& value); std::optional android_os_test_long(); bool android_os_test_long(const std::optional& value); std::vector> test_double_list(); bool test_double_list(const std::vector>& value); std::vector> test_list_int(); bool test_list_int(const std::vector>& value); [[deprecated]] std::vector> test_strlist(); [[deprecated]] bool test_strlist(const std::vector>& value); enum class el_values { ENU, MVA, LUE, }; [[deprecated]] std::vector> el(); [[deprecated]] bool el(const std::vector>& value); } // namespace android::sysprop::PlatformProperties )"; constexpr const char* kExpectedPublicHeaderOutput = R"(// Generated by the sysprop generator. DO NOT EDIT! #pragma once #include #include #include #include namespace android::sysprop::PlatformProperties { std::optional test_int(); bool test_int(const std::optional& value); std::optional test_string(); std::optional test_BOOLeaN(); bool test_BOOLeaN(const std::optional& value); std::optional android_os_test_long(); bool android_os_test_long(const std::optional& value); std::vector> test_list_int(); bool test_list_int(const std::vector>& value); [[deprecated]] std::vector> test_strlist(); [[deprecated]] bool test_strlist(const std::vector>& value); } // namespace android::sysprop::PlatformProperties )"; constexpr const char* kExpectedSourceOutput = R"(// Generated by the sysprop generator. DO NOT EDIT! #include #include #include #include #include #include #include #include #ifdef __BIONIC__ #include [[maybe_unused]] static bool SetProp(const char* key, const char* value) { return __system_property_set(key, value) == 0; } #else #include [[maybe_unused]] static bool SetProp(const char* key, const char* value) { android::base::SetProperty(key, value); return true; } #endif #include #include namespace { using namespace android::sysprop::PlatformProperties; template T DoParse(const char* str); constexpr const std::pair test_enum_list[] = { {"a", test_enum_values::A}, {"b", test_enum_values::B}, {"c", test_enum_values::C}, {"D", test_enum_values::D}, {"e", test_enum_values::E}, {"f", test_enum_values::F}, {"G", test_enum_values::G}, }; template <> std::optional DoParse(const char* str) { for (auto [name, val] : test_enum_list) { if (strcmp(str, name) == 0) { return val; } } return std::nullopt; } std::string FormatValue(std::optional value) { if (!value) return ""; for (auto [name, val] : test_enum_list) { if (val == *value) { return name; } } LOG_ALWAYS_FATAL("Invalid value %d for property android.test.enum", static_cast(*value)); __builtin_unreachable(); } constexpr const std::pair el_list[] = { {"enu", el_values::ENU}, {"mva", el_values::MVA}, {"lue", el_values::LUE}, }; template <> std::optional DoParse(const char* str) { for (auto [name, val] : el_list) { if (strcmp(str, name) == 0) { return val; } } return std::nullopt; } std::string FormatValue(std::optional value) { if (!value) return ""; for (auto [name, val] : el_list) { if (val == *value) { return name; } } LOG_ALWAYS_FATAL("Invalid value %d for property el", static_cast(*value)); __builtin_unreachable(); } template constexpr bool is_vector = false; template constexpr bool is_vector> = true; template <> [[maybe_unused]] std::optional DoParse(const char* str) { static constexpr const char* kYes[] = {"1", "true"}; static constexpr const char* kNo[] = {"0", "false"}; for (const char* yes : kYes) { if (strcasecmp(yes, str) == 0) return std::make_optional(true); } for (const char* no : kNo) { if (strcasecmp(no, str) == 0) return std::make_optional(false); } return std::nullopt; } template <> [[maybe_unused]] std::optional DoParse(const char* str) { std::int32_t ret; return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt; } template <> [[maybe_unused]] std::optional DoParse(const char* str) { std::uint32_t ret; return android::base::ParseUint(str, &ret) ? std::make_optional(ret) : std::nullopt; } template <> [[maybe_unused]] std::optional DoParse(const char* str) { std::int64_t ret; return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt; } template <> [[maybe_unused]] std::optional DoParse(const char* str) { std::uint64_t ret; return android::base::ParseUint(str, &ret) ? std::make_optional(ret) : std::nullopt; } template <> [[maybe_unused]] std::optional DoParse(const char* str) { int old_errno = errno; errno = 0; char* end; double ret = std::strtod(str, &end); if (errno != 0) { return std::nullopt; } if (str == end || *end != '\0') { errno = EINVAL; return std::nullopt; } errno = old_errno; return std::make_optional(ret); } template <> [[maybe_unused]] std::optional DoParse(const char* str) { return *str == '\0' ? std::nullopt : std::make_optional(str); } template [[maybe_unused]] Vec DoParseList(const char* str) { Vec ret; if (*str == '\0') return ret; const char* p = str; for (;;) { const char* r = p; std::string value; while (*r != ',') { if (*r == '\\') ++r; if (*r == '\0') break; value += *r++; } ret.emplace_back(DoParse(value.c_str())); if (*r == '\0') break; p = r + 1; } return ret; } template inline T TryParse(const char* str) { if constexpr(is_vector) { return DoParseList(str); } else { return DoParse(str); } } [[maybe_unused]] std::string FormatValue(const std::optional& value) { return value ? std::to_string(*value) : ""; } [[maybe_unused]] std::string FormatValue(const std::optional& value) { return value ? std::to_string(*value) : ""; } [[maybe_unused]] std::string FormatValue(const std::optional& value) { return value ? std::to_string(*value) : ""; } [[maybe_unused]] std::string FormatValue(const std::optional& value) { return value ? std::to_string(*value) : ""; } [[maybe_unused]] std::string FormatValue(const std::optional& value) { if (!value) return ""; char buf[1024]; std::sprintf(buf, "%.*g", std::numeric_limits::max_digits10, *value); return buf; } [[maybe_unused]] std::string FormatValue(const std::optional& value) { return value ? (*value ? "true" : "false") : ""; } template [[maybe_unused]] std::string FormatValue(const std::vector& value) { if (value.empty()) return ""; std::string ret; bool first = true; for (auto&& element : value) { if (!first) ret += ','; else first = false; if constexpr(std::is_same_v>) { if (element) { for (char c : *element) { if (c == '\\' || c == ',') ret += '\\'; ret += c; } } } else { ret += FormatValue(element); } } return ret; } template T GetProp(const char* key, const char* legacy = nullptr) { std::string value; #ifdef __BIONIC__ auto pi = __system_property_find(key); if (pi != nullptr) { __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) { *static_cast(cookie) = value; }, &value); } #else value = android::base::GetProperty(key, ""); #endif if (value.empty() && legacy) { ALOGV("prop %s doesn't exist; fallback to legacy prop %s", key, legacy); return GetProp(legacy); } return TryParse(value.c_str()); } } // namespace namespace android::sysprop::PlatformProperties { std::optional test_double() { return GetProp>("android.test_double"); } bool test_double(const std::optional& value) { return SetProp("android.test_double", FormatValue(value).c_str()); } std::optional test_int() { return GetProp>("android.test_int"); } bool test_int(const std::optional& value) { return SetProp("android.test_int", FormatValue(value).c_str()); } std::optional test_string() { return GetProp>("android.test.string", "legacy.android.test.string"); } std::optional test_enum() { return GetProp>("android.test.enum"); } bool test_enum(const std::optional& value) { return SetProp("android.test.enum", FormatValue(value).c_str()); } std::optional test_BOOLeaN() { return GetProp>("ro.android.test.b"); } bool test_BOOLeaN(const std::optional& value) { return SetProp("ro.android.test.b", FormatValue(value).c_str()); } std::optional android_os_test_long() { return GetProp>("android_os_test-long"); } bool android_os_test_long(const std::optional& value) { return SetProp("android_os_test-long", FormatValue(value).c_str()); } std::vector> test_double_list() { return GetProp>>("test_double_list"); } bool test_double_list(const std::vector>& value) { return SetProp("test_double_list", FormatValue(value).c_str()); } std::vector> test_list_int() { return GetProp>>("test_list_int"); } bool test_list_int(const std::vector>& value) { return SetProp("test_list_int", FormatValue(value).c_str()); } std::vector> test_strlist() { return GetProp>>("test_strlist"); } bool test_strlist(const std::vector>& value) { return SetProp("test_strlist", FormatValue(value).c_str()); } std::vector> el() { return GetProp>>("el"); } bool el(const std::vector>& value) { return SetProp("el", FormatValue(value).c_str()); } } // namespace android::sysprop::PlatformProperties )"; } // namespace using namespace std::string_literals; TEST(SyspropTest, CppGenTest) { TemporaryDir temp_dir; std::string temp_sysprop_path = temp_dir.path + "/PlatformProperties.sysprop"s; ASSERT_TRUE( android::base::WriteStringToFile(kTestSyspropFile, temp_sysprop_path)); auto sysprop_deleter = android::base::make_scope_guard( [&] { unlink(temp_sysprop_path.c_str()); }); ASSERT_RESULT_OK(GenerateCppFiles(temp_sysprop_path, temp_dir.path, temp_dir.path + "/public"s, temp_dir.path, "properties/PlatformProperties.sysprop.h")); std::string header_output_path = temp_dir.path + "/PlatformProperties.sysprop.h"s; std::string public_header_output_path = temp_dir.path + "/public/PlatformProperties.sysprop.h"s; std::string source_output_path = temp_dir.path + "/PlatformProperties.sysprop.cpp"s; auto generated_file_deleter = android::base::make_scope_guard([&] { unlink(header_output_path.c_str()); unlink(public_header_output_path.c_str()); unlink(source_output_path.c_str()); }); std::string header_output; ASSERT_TRUE(android::base::ReadFileToString(header_output_path, &header_output, true)); EXPECT_EQ(header_output, kExpectedHeaderOutput); std::string public_header_output; ASSERT_TRUE(android::base::ReadFileToString(public_header_output_path, &public_header_output, true)); EXPECT_EQ(public_header_output, kExpectedPublicHeaderOutput); std::string source_output; ASSERT_TRUE(android::base::ReadFileToString(source_output_path, &source_output, true)); EXPECT_EQ(source_output, kExpectedSourceOutput); }