| /* |
| * 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 <unistd.h> |
| #include <string> |
| |
| #include <android-base/file.h> |
| #include <android-base/test_utils.h> |
| #include <gtest/gtest.h> |
| |
| #include "JavaGen.h" |
| |
| namespace { |
| |
| constexpr const char* kTestSyspropFile = |
| R"(owner: Vendor |
| module: "com.somecompany.TestProperties" |
| |
| prop { |
| api_name: "test_double" |
| type: Double |
| prop_name: "vendor.test_double" |
| scope: Internal |
| access: ReadWrite |
| } |
| prop { |
| api_name: "test_int" |
| type: Integer |
| prop_name: "vendor.test_int" |
| scope: Public |
| access: ReadWrite |
| } |
| prop { |
| api_name: "test_string" |
| type: String |
| prop_name: "vendor.test.string" |
| scope: Public |
| access: ReadWrite |
| } |
| prop { |
| api_name: "test_enum" |
| type: Enum |
| prop_name: "vendor.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.vendor.test.b" |
| scope: Public |
| access: Writeonce |
| } |
| prop { |
| api_name: "vendor_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* kExpectedPublicOutput = |
| R"s(// Generated by the sysprop generator. DO NOT EDIT! |
| |
| package com.somecompany; |
| |
| import android.os.SystemProperties; |
| |
| import java.lang.StringBuilder; |
| import java.util.ArrayList; |
| import java.util.function.Function; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Optional; |
| import java.util.StringJoiner; |
| import java.util.stream.Collectors; |
| |
| public final class TestProperties { |
| private TestProperties () {} |
| |
| private static Boolean tryParseBoolean(String str) { |
| switch (str.toLowerCase(Locale.US)) { |
| case "1": |
| case "true": |
| return Boolean.TRUE; |
| case "0": |
| case "false": |
| return Boolean.FALSE; |
| default: |
| return null; |
| } |
| } |
| |
| private static Integer tryParseInteger(String str) { |
| try { |
| return Integer.valueOf(str); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| private static Long tryParseLong(String str) { |
| try { |
| return Long.valueOf(str); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| private static Double tryParseDouble(String str) { |
| try { |
| return Double.valueOf(str); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| private static String tryParseString(String str) { |
| return "".equals(str) ? null : str; |
| } |
| |
| private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) { |
| try { |
| return Enum.valueOf(enumType, str.toUpperCase(Locale.US)); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } |
| } |
| |
| private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) { |
| if ("".equals(str)) return new ArrayList<>(); |
| |
| List<T> ret = new ArrayList<>(); |
| |
| int p = 0; |
| for (;;) { |
| StringBuilder sb = new StringBuilder(); |
| while (p < str.length() && str.charAt(p) != ',') { |
| if (str.charAt(p) == '\\') ++p; |
| if (p == str.length()) break; |
| sb.append(str.charAt(p++)); |
| } |
| ret.add(elementParser.apply(sb.toString())); |
| if (p == str.length()) break; |
| ++p; |
| } |
| |
| return ret; |
| } |
| |
| private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) { |
| if ("".equals(str)) return new ArrayList<>(); |
| |
| List<T> ret = new ArrayList<>(); |
| |
| for (String element : str.split(",")) { |
| ret.add(tryParseEnum(enumType, element)); |
| } |
| |
| return ret; |
| } |
| |
| private static String escape(String str) { |
| return str.replaceAll("([\\\\,])", "\\\\$1"); |
| } |
| |
| private static <T> String formatList(List<T> list) { |
| StringJoiner joiner = new StringJoiner(","); |
| |
| for (T element : list) { |
| joiner.add(element == null ? "" : escape(element.toString())); |
| } |
| |
| return joiner.toString(); |
| } |
| |
| private static <T extends Enum<T>> String formatEnumList(List<T> list, Function<T, String> elementFormatter) { |
| StringJoiner joiner = new StringJoiner(","); |
| |
| for (T element : list) { |
| joiner.add(element == null ? "" : elementFormatter.apply(element)); |
| } |
| |
| return joiner.toString(); |
| } |
| |
| public static Optional<Integer> test_int() { |
| String value = SystemProperties.get("vendor.test_int"); |
| return Optional.ofNullable(tryParseInteger(value)); |
| } |
| |
| public static void test_int(Integer value) { |
| SystemProperties.set("vendor.test_int", value == null ? "" : value.toString()); |
| } |
| |
| public static Optional<String> test_string() { |
| String value = SystemProperties.get("vendor.test.string"); |
| return Optional.ofNullable(tryParseString(value)); |
| } |
| |
| public static void test_string(String value) { |
| SystemProperties.set("vendor.test.string", value == null ? "" : value.toString()); |
| } |
| |
| public static Optional<Boolean> test_BOOLeaN() { |
| String value = SystemProperties.get("ro.vendor.test.b"); |
| return Optional.ofNullable(tryParseBoolean(value)); |
| } |
| |
| public static void test_BOOLeaN(Boolean value) { |
| SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString()); |
| } |
| |
| public static Optional<Long> vendor_os_test_long() { |
| String value = SystemProperties.get("vendor.vendor_os_test-long"); |
| return Optional.ofNullable(tryParseLong(value)); |
| } |
| |
| public static void vendor_os_test_long(Long value) { |
| SystemProperties.set("vendor.vendor_os_test-long", value == null ? "" : value.toString()); |
| } |
| |
| public static List<Integer> test_list_int() { |
| String value = SystemProperties.get("vendor.test_list_int"); |
| return tryParseList(v -> tryParseInteger(v), value); |
| } |
| |
| public static void test_list_int(List<Integer> value) { |
| SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value)); |
| } |
| |
| @Deprecated |
| public static List<String> test_strlist() { |
| String value = SystemProperties.get("vendor.test_strlist"); |
| return tryParseList(v -> tryParseString(v), value); |
| } |
| |
| @Deprecated |
| public static void test_strlist(List<String> value) { |
| SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value)); |
| } |
| } |
| )s"; |
| |
| constexpr const char* kExpectedInternalOutput = |
| R"s(// Generated by the sysprop generator. DO NOT EDIT! |
| |
| package com.somecompany; |
| |
| import android.os.SystemProperties; |
| |
| import java.lang.StringBuilder; |
| import java.util.ArrayList; |
| import java.util.function.Function; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Optional; |
| import java.util.StringJoiner; |
| import java.util.stream.Collectors; |
| |
| public final class TestProperties { |
| private TestProperties () {} |
| |
| private static Boolean tryParseBoolean(String str) { |
| switch (str.toLowerCase(Locale.US)) { |
| case "1": |
| case "true": |
| return Boolean.TRUE; |
| case "0": |
| case "false": |
| return Boolean.FALSE; |
| default: |
| return null; |
| } |
| } |
| |
| private static Integer tryParseInteger(String str) { |
| try { |
| return Integer.valueOf(str); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| private static Long tryParseLong(String str) { |
| try { |
| return Long.valueOf(str); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| private static Double tryParseDouble(String str) { |
| try { |
| return Double.valueOf(str); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| private static String tryParseString(String str) { |
| return "".equals(str) ? null : str; |
| } |
| |
| private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) { |
| try { |
| return Enum.valueOf(enumType, str.toUpperCase(Locale.US)); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } |
| } |
| |
| private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) { |
| if ("".equals(str)) return new ArrayList<>(); |
| |
| List<T> ret = new ArrayList<>(); |
| |
| int p = 0; |
| for (;;) { |
| StringBuilder sb = new StringBuilder(); |
| while (p < str.length() && str.charAt(p) != ',') { |
| if (str.charAt(p) == '\\') ++p; |
| if (p == str.length()) break; |
| sb.append(str.charAt(p++)); |
| } |
| ret.add(elementParser.apply(sb.toString())); |
| if (p == str.length()) break; |
| ++p; |
| } |
| |
| return ret; |
| } |
| |
| private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) { |
| if ("".equals(str)) return new ArrayList<>(); |
| |
| List<T> ret = new ArrayList<>(); |
| |
| for (String element : str.split(",")) { |
| ret.add(tryParseEnum(enumType, element)); |
| } |
| |
| return ret; |
| } |
| |
| private static String escape(String str) { |
| return str.replaceAll("([\\\\,])", "\\\\$1"); |
| } |
| |
| private static <T> String formatList(List<T> list) { |
| StringJoiner joiner = new StringJoiner(","); |
| |
| for (T element : list) { |
| joiner.add(element == null ? "" : escape(element.toString())); |
| } |
| |
| return joiner.toString(); |
| } |
| |
| private static <T extends Enum<T>> String formatEnumList(List<T> list, Function<T, String> elementFormatter) { |
| StringJoiner joiner = new StringJoiner(","); |
| |
| for (T element : list) { |
| joiner.add(element == null ? "" : elementFormatter.apply(element)); |
| } |
| |
| return joiner.toString(); |
| } |
| |
| public static Optional<Double> test_double() { |
| String value = SystemProperties.get("vendor.test_double"); |
| return Optional.ofNullable(tryParseDouble(value)); |
| } |
| |
| public static void test_double(Double value) { |
| SystemProperties.set("vendor.test_double", value == null ? "" : value.toString()); |
| } |
| |
| public static Optional<Integer> test_int() { |
| String value = SystemProperties.get("vendor.test_int"); |
| return Optional.ofNullable(tryParseInteger(value)); |
| } |
| |
| public static void test_int(Integer value) { |
| SystemProperties.set("vendor.test_int", value == null ? "" : value.toString()); |
| } |
| |
| public static Optional<String> test_string() { |
| String value = SystemProperties.get("vendor.test.string"); |
| return Optional.ofNullable(tryParseString(value)); |
| } |
| |
| public static void test_string(String value) { |
| SystemProperties.set("vendor.test.string", value == null ? "" : value.toString()); |
| } |
| |
| public static enum test_enum_values { |
| A("a"), |
| B("b"), |
| C("c"), |
| D("D"), |
| E("e"), |
| F("f"), |
| G("G"); |
| private final String propValue; |
| private test_enum_values(String propValue) { |
| this.propValue = propValue; |
| } |
| public String getPropValue() { |
| return propValue; |
| } |
| } |
| |
| public static Optional<test_enum_values> test_enum() { |
| String value = SystemProperties.get("vendor.test.enum"); |
| return Optional.ofNullable(tryParseEnum(test_enum_values.class, value)); |
| } |
| |
| public static void test_enum(test_enum_values value) { |
| SystemProperties.set("vendor.test.enum", value == null ? "" : value.getPropValue()); |
| } |
| |
| public static Optional<Boolean> test_BOOLeaN() { |
| String value = SystemProperties.get("ro.vendor.test.b"); |
| return Optional.ofNullable(tryParseBoolean(value)); |
| } |
| |
| public static void test_BOOLeaN(Boolean value) { |
| SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString()); |
| } |
| |
| public static Optional<Long> vendor_os_test_long() { |
| String value = SystemProperties.get("vendor.vendor_os_test-long"); |
| return Optional.ofNullable(tryParseLong(value)); |
| } |
| |
| public static void vendor_os_test_long(Long value) { |
| SystemProperties.set("vendor.vendor_os_test-long", value == null ? "" : value.toString()); |
| } |
| |
| public static List<Double> test_double_list() { |
| String value = SystemProperties.get("vendor.test_double_list"); |
| return tryParseList(v -> tryParseDouble(v), value); |
| } |
| |
| public static void test_double_list(List<Double> value) { |
| SystemProperties.set("vendor.test_double_list", value == null ? "" : formatList(value)); |
| } |
| |
| public static List<Integer> test_list_int() { |
| String value = SystemProperties.get("vendor.test_list_int"); |
| return tryParseList(v -> tryParseInteger(v), value); |
| } |
| |
| public static void test_list_int(List<Integer> value) { |
| SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value)); |
| } |
| |
| @Deprecated |
| public static List<String> test_strlist() { |
| String value = SystemProperties.get("vendor.test_strlist"); |
| return tryParseList(v -> tryParseString(v), value); |
| } |
| |
| @Deprecated |
| public static void test_strlist(List<String> value) { |
| SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value)); |
| } |
| |
| public static enum el_values { |
| ENU("enu"), |
| MVA("mva"), |
| LUE("lue"); |
| private final String propValue; |
| private el_values(String propValue) { |
| this.propValue = propValue; |
| } |
| public String getPropValue() { |
| return propValue; |
| } |
| } |
| |
| @Deprecated |
| public static List<el_values> el() { |
| String value = SystemProperties.get("vendor.el"); |
| return tryParseEnumList(el_values.class, value); |
| } |
| |
| @Deprecated |
| public static void el(List<el_values> value) { |
| SystemProperties.set("vendor.el", value == null ? "" : formatEnumList(value, el_values::getPropValue)); |
| } |
| } |
| )s"; |
| |
| } // namespace |
| |
| using namespace std::string_literals; |
| |
| TEST(SyspropTest, JavaGenTest) { |
| TemporaryFile temp_file; |
| |
| // strlen is optimized for constants, so don't worry about it. |
| ASSERT_EQ(write(temp_file.fd, kTestSyspropFile, strlen(kTestSyspropFile)), |
| strlen(kTestSyspropFile)); |
| close(temp_file.fd); |
| temp_file.fd = -1; |
| |
| TemporaryDir temp_dir; |
| |
| std::pair<sysprop::Scope, const char*> tests[] = { |
| {sysprop::Scope::Internal, kExpectedInternalOutput}, |
| {sysprop::Scope::Public, kExpectedPublicOutput}, |
| }; |
| |
| for (auto [scope, expected_output] : tests) { |
| ASSERT_RESULT_OK(GenerateJavaLibrary(temp_file.path, scope, temp_dir.path)); |
| |
| std::string java_output_path = |
| temp_dir.path + "/com/somecompany/TestProperties.java"s; |
| |
| std::string java_output; |
| ASSERT_TRUE( |
| android::base::ReadFileToString(java_output_path, &java_output, true)); |
| EXPECT_EQ(java_output, expected_output); |
| |
| unlink(java_output_path.c_str()); |
| rmdir((temp_dir.path + "/com/somecompany"s).c_str()); |
| rmdir((temp_dir.path + "/com"s).c_str()); |
| } |
| } |