• 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/test_utils.h>
22 #include <gtest/gtest.h>
23 
24 #include "JavaGen.h"
25 
26 namespace {
27 
28 constexpr const char* kTestSyspropFile =
29     R"(owner: Vendor
30 module: "com.somecompany.TestProperties"
31 
32 prop {
33     api_name: "test_double"
34     type: Double
35     prop_name: "vendor.test_double"
36     scope: Internal
37     access: ReadWrite
38 }
39 prop {
40     api_name: "test_int"
41     type: Integer
42     prop_name: "vendor.test_int"
43     scope: Public
44     access: ReadWrite
45 }
46 prop {
47     api_name: "test_string"
48     type: String
49     prop_name: "vendor.test.string"
50     scope: Public
51     access: Readonly
52     legacy_prop_name: "vendor.old.string"
53 }
54 prop {
55     api_name: "test_enum"
56     type: Enum
57     prop_name: "vendor.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.vendor.test.b"
66     scope: Public
67     access: Writeonce
68 }
69 prop {
70     api_name: "vendor_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* kExpectedPublicOutput =
105     R"s(// Generated by the sysprop generator. DO NOT EDIT!
106 
107 package com.somecompany;
108 
109 import android.os.SystemProperties;
110 import android.util.Log;
111 
112 import java.lang.StringBuilder;
113 import java.util.ArrayList;
114 import java.util.function.Function;
115 import java.util.List;
116 import java.util.Locale;
117 import java.util.Optional;
118 import java.util.StringJoiner;
119 import java.util.stream.Collectors;
120 
121 public final class TestProperties {
122     private TestProperties () {}
123 
124     private static Boolean tryParseBoolean(String str) {
125         switch (str.toLowerCase(Locale.US)) {
126             case "1":
127             case "true":
128                 return Boolean.TRUE;
129             case "0":
130             case "false":
131                 return Boolean.FALSE;
132             default:
133                 return null;
134         }
135     }
136 
137     private static Integer tryParseInteger(String str) {
138         try {
139             return Integer.valueOf(str);
140         } catch (NumberFormatException e) {
141             return null;
142         }
143     }
144 
145     private static Integer tryParseUInt(String str) {
146         try {
147             return Integer.parseUnsignedInt(str);
148         } catch (NumberFormatException e) {
149             return null;
150         }
151     }
152 
153     private static Long tryParseLong(String str) {
154         try {
155             return Long.valueOf(str);
156         } catch (NumberFormatException e) {
157             return null;
158         }
159     }
160 
161     private static Long tryParseULong(String str) {
162         try {
163             return Long.parseUnsignedLong(str);
164         } catch (NumberFormatException e) {
165             return null;
166         }
167     }
168 
169     private static Double tryParseDouble(String str) {
170         try {
171             return Double.valueOf(str);
172         } catch (NumberFormatException e) {
173             return null;
174         }
175     }
176 
177     private static String tryParseString(String str) {
178         return "".equals(str) ? null : str;
179     }
180 
181     private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) {
182         try {
183             return Enum.valueOf(enumType, str.toUpperCase(Locale.US));
184         } catch (IllegalArgumentException e) {
185             return null;
186         }
187     }
188 
189     private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) {
190         if ("".equals(str)) return new ArrayList<>();
191 
192         List<T> ret = new ArrayList<>();
193 
194         int p = 0;
195         for (;;) {
196             StringBuilder sb = new StringBuilder();
197             while (p < str.length() && str.charAt(p) != ',') {
198                 if (str.charAt(p) == '\\') ++p;
199                 if (p == str.length()) break;
200                 sb.append(str.charAt(p++));
201             }
202             ret.add(elementParser.apply(sb.toString()));
203             if (p == str.length()) break;
204             ++p;
205         }
206 
207         return ret;
208     }
209 
210     private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) {
211         if ("".equals(str)) return new ArrayList<>();
212 
213         List<T> ret = new ArrayList<>();
214 
215         for (String element : str.split(",")) {
216             ret.add(tryParseEnum(enumType, element));
217         }
218 
219         return ret;
220     }
221 
222     private static String escape(String str) {
223         return str.replaceAll("([\\\\,])", "\\\\$1");
224     }
225 
226     private static <T> String formatList(List<T> list) {
227         StringJoiner joiner = new StringJoiner(",");
228 
229         for (T element : list) {
230             joiner.add(element == null ? "" : escape(element.toString()));
231         }
232 
233         return joiner.toString();
234     }
235 
236     private static String formatUIntList(List<Integer> list) {
237         StringJoiner joiner = new StringJoiner(",");
238 
239         for (Integer element : list) {
240             joiner.add(element == null ? "" : escape(Integer.toUnsignedString(element)));
241         }
242 
243         return joiner.toString();
244     }
245 
246     private static String formatULongList(List<Long> list) {
247         StringJoiner joiner = new StringJoiner(",");
248 
249         for (Long element : list) {
250             joiner.add(element == null ? "" : escape(Long.toUnsignedString(element)));
251         }
252 
253         return joiner.toString();
254     }
255 
256     private static <T extends Enum<T>> String formatEnumList(List<T> list, Function<T, String> elementFormatter) {
257         StringJoiner joiner = new StringJoiner(",");
258 
259         for (T element : list) {
260             joiner.add(element == null ? "" : elementFormatter.apply(element));
261         }
262 
263         return joiner.toString();
264     }
265 
266     public static Optional<Integer> test_int() {
267         String value = SystemProperties.get("vendor.test_int");
268         return Optional.ofNullable(tryParseInteger(value));
269     }
270 
271     public static void test_int(Integer value) {
272         SystemProperties.set("vendor.test_int", value == null ? "" : value.toString());
273     }
274 
275     public static Optional<String> test_string() {
276         String value = SystemProperties.get("vendor.test.string");
277         if ("".equals(value)) {
278             Log.v("TestProperties", "prop vendor.test.string doesn't exist; fallback to legacy prop vendor.old.string");
279             value = SystemProperties.get("vendor.old.string");
280         }
281         return Optional.ofNullable(tryParseString(value));
282     }
283 
284     public static Optional<Boolean> test_BOOLeaN() {
285         String value = SystemProperties.get("ro.vendor.test.b");
286         return Optional.ofNullable(tryParseBoolean(value));
287     }
288 
289     public static void test_BOOLeaN(Boolean value) {
290         SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString());
291     }
292 
293     public static Optional<Long> vendor_os_test_long() {
294         String value = SystemProperties.get("vendor.vendor_os_test-long");
295         return Optional.ofNullable(tryParseLong(value));
296     }
297 
298     public static void vendor_os_test_long(Long value) {
299         SystemProperties.set("vendor.vendor_os_test-long", value == null ? "" : value.toString());
300     }
301 
302     public static List<Integer> test_list_int() {
303         String value = SystemProperties.get("vendor.test_list_int");
304         return tryParseList(v -> tryParseInteger(v), value);
305     }
306 
307     public static void test_list_int(List<Integer> value) {
308         SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value));
309     }
310 
311     @Deprecated
312     public static List<String> test_strlist() {
313         String value = SystemProperties.get("vendor.test_strlist");
314         return tryParseList(v -> tryParseString(v), value);
315     }
316 
317     @Deprecated
318     public static void test_strlist(List<String> value) {
319         SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value));
320     }
321 }
322 )s";
323 
324 constexpr const char* kExpectedInternalOutput =
325     R"s(// Generated by the sysprop generator. DO NOT EDIT!
326 
327 package com.somecompany;
328 
329 import android.os.SystemProperties;
330 import android.util.Log;
331 
332 import java.lang.StringBuilder;
333 import java.util.ArrayList;
334 import java.util.function.Function;
335 import java.util.List;
336 import java.util.Locale;
337 import java.util.Optional;
338 import java.util.StringJoiner;
339 import java.util.stream.Collectors;
340 
341 public final class TestProperties {
342     private TestProperties () {}
343 
344     private static Boolean tryParseBoolean(String str) {
345         switch (str.toLowerCase(Locale.US)) {
346             case "1":
347             case "true":
348                 return Boolean.TRUE;
349             case "0":
350             case "false":
351                 return Boolean.FALSE;
352             default:
353                 return null;
354         }
355     }
356 
357     private static Integer tryParseInteger(String str) {
358         try {
359             return Integer.valueOf(str);
360         } catch (NumberFormatException e) {
361             return null;
362         }
363     }
364 
365     private static Integer tryParseUInt(String str) {
366         try {
367             return Integer.parseUnsignedInt(str);
368         } catch (NumberFormatException e) {
369             return null;
370         }
371     }
372 
373     private static Long tryParseLong(String str) {
374         try {
375             return Long.valueOf(str);
376         } catch (NumberFormatException e) {
377             return null;
378         }
379     }
380 
381     private static Long tryParseULong(String str) {
382         try {
383             return Long.parseUnsignedLong(str);
384         } catch (NumberFormatException e) {
385             return null;
386         }
387     }
388 
389     private static Double tryParseDouble(String str) {
390         try {
391             return Double.valueOf(str);
392         } catch (NumberFormatException e) {
393             return null;
394         }
395     }
396 
397     private static String tryParseString(String str) {
398         return "".equals(str) ? null : str;
399     }
400 
401     private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) {
402         try {
403             return Enum.valueOf(enumType, str.toUpperCase(Locale.US));
404         } catch (IllegalArgumentException e) {
405             return null;
406         }
407     }
408 
409     private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) {
410         if ("".equals(str)) return new ArrayList<>();
411 
412         List<T> ret = new ArrayList<>();
413 
414         int p = 0;
415         for (;;) {
416             StringBuilder sb = new StringBuilder();
417             while (p < str.length() && str.charAt(p) != ',') {
418                 if (str.charAt(p) == '\\') ++p;
419                 if (p == str.length()) break;
420                 sb.append(str.charAt(p++));
421             }
422             ret.add(elementParser.apply(sb.toString()));
423             if (p == str.length()) break;
424             ++p;
425         }
426 
427         return ret;
428     }
429 
430     private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) {
431         if ("".equals(str)) return new ArrayList<>();
432 
433         List<T> ret = new ArrayList<>();
434 
435         for (String element : str.split(",")) {
436             ret.add(tryParseEnum(enumType, element));
437         }
438 
439         return ret;
440     }
441 
442     private static String escape(String str) {
443         return str.replaceAll("([\\\\,])", "\\\\$1");
444     }
445 
446     private static <T> String formatList(List<T> list) {
447         StringJoiner joiner = new StringJoiner(",");
448 
449         for (T element : list) {
450             joiner.add(element == null ? "" : escape(element.toString()));
451         }
452 
453         return joiner.toString();
454     }
455 
456     private static String formatUIntList(List<Integer> list) {
457         StringJoiner joiner = new StringJoiner(",");
458 
459         for (Integer element : list) {
460             joiner.add(element == null ? "" : escape(Integer.toUnsignedString(element)));
461         }
462 
463         return joiner.toString();
464     }
465 
466     private static String formatULongList(List<Long> list) {
467         StringJoiner joiner = new StringJoiner(",");
468 
469         for (Long element : list) {
470             joiner.add(element == null ? "" : escape(Long.toUnsignedString(element)));
471         }
472 
473         return joiner.toString();
474     }
475 
476     private static <T extends Enum<T>> String formatEnumList(List<T> list, Function<T, String> elementFormatter) {
477         StringJoiner joiner = new StringJoiner(",");
478 
479         for (T element : list) {
480             joiner.add(element == null ? "" : elementFormatter.apply(element));
481         }
482 
483         return joiner.toString();
484     }
485 
486     public static Optional<Double> test_double() {
487         String value = SystemProperties.get("vendor.test_double");
488         return Optional.ofNullable(tryParseDouble(value));
489     }
490 
491     public static void test_double(Double value) {
492         SystemProperties.set("vendor.test_double", value == null ? "" : value.toString());
493     }
494 
495     public static Optional<Integer> test_int() {
496         String value = SystemProperties.get("vendor.test_int");
497         return Optional.ofNullable(tryParseInteger(value));
498     }
499 
500     public static void test_int(Integer value) {
501         SystemProperties.set("vendor.test_int", value == null ? "" : value.toString());
502     }
503 
504     public static Optional<String> test_string() {
505         String value = SystemProperties.get("vendor.test.string");
506         if ("".equals(value)) {
507             Log.v("TestProperties", "prop vendor.test.string doesn't exist; fallback to legacy prop vendor.old.string");
508             value = SystemProperties.get("vendor.old.string");
509         }
510         return Optional.ofNullable(tryParseString(value));
511     }
512 
513     public static enum test_enum_values {
514         A("a"),
515         B("b"),
516         C("c"),
517         D("D"),
518         E("e"),
519         F("f"),
520         G("G");
521         private final String propValue;
522         private test_enum_values(String propValue) {
523             this.propValue = propValue;
524         }
525         public String getPropValue() {
526             return propValue;
527         }
528     }
529 
530     public static Optional<test_enum_values> test_enum() {
531         String value = SystemProperties.get("vendor.test.enum");
532         return Optional.ofNullable(tryParseEnum(test_enum_values.class, value));
533     }
534 
535     public static void test_enum(test_enum_values value) {
536         SystemProperties.set("vendor.test.enum", value == null ? "" : value.getPropValue());
537     }
538 
539     public static Optional<Boolean> test_BOOLeaN() {
540         String value = SystemProperties.get("ro.vendor.test.b");
541         return Optional.ofNullable(tryParseBoolean(value));
542     }
543 
544     public static void test_BOOLeaN(Boolean value) {
545         SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString());
546     }
547 
548     public static Optional<Long> vendor_os_test_long() {
549         String value = SystemProperties.get("vendor.vendor_os_test-long");
550         return Optional.ofNullable(tryParseLong(value));
551     }
552 
553     public static void vendor_os_test_long(Long value) {
554         SystemProperties.set("vendor.vendor_os_test-long", value == null ? "" : value.toString());
555     }
556 
557     public static List<Double> test_double_list() {
558         String value = SystemProperties.get("vendor.test_double_list");
559         return tryParseList(v -> tryParseDouble(v), value);
560     }
561 
562     public static void test_double_list(List<Double> value) {
563         SystemProperties.set("vendor.test_double_list", value == null ? "" : formatList(value));
564     }
565 
566     public static List<Integer> test_list_int() {
567         String value = SystemProperties.get("vendor.test_list_int");
568         return tryParseList(v -> tryParseInteger(v), value);
569     }
570 
571     public static void test_list_int(List<Integer> value) {
572         SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value));
573     }
574 
575     @Deprecated
576     public static List<String> test_strlist() {
577         String value = SystemProperties.get("vendor.test_strlist");
578         return tryParseList(v -> tryParseString(v), value);
579     }
580 
581     @Deprecated
582     public static void test_strlist(List<String> value) {
583         SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value));
584     }
585 
586     public static enum el_values {
587         ENU("enu"),
588         MVA("mva"),
589         LUE("lue");
590         private final String propValue;
591         private el_values(String propValue) {
592             this.propValue = propValue;
593         }
594         public String getPropValue() {
595             return propValue;
596         }
597     }
598 
599     @Deprecated
600     public static List<el_values> el() {
601         String value = SystemProperties.get("vendor.el");
602         return tryParseEnumList(el_values.class, value);
603     }
604 
605     @Deprecated
606     public static void el(List<el_values> value) {
607         SystemProperties.set("vendor.el", value == null ? "" : formatEnumList(value, el_values::getPropValue));
608     }
609 }
610 )s";
611 
612 }  // namespace
613 
614 using namespace std::string_literals;
615 
TEST(SyspropTest,JavaGenTest)616 TEST(SyspropTest, JavaGenTest) {
617   TemporaryFile temp_file;
618 
619   // strlen is optimized for constants, so don't worry about it.
620   ASSERT_EQ(write(temp_file.fd, kTestSyspropFile, strlen(kTestSyspropFile)),
621             strlen(kTestSyspropFile));
622   close(temp_file.fd);
623   temp_file.fd = -1;
624 
625   TemporaryDir temp_dir;
626 
627   std::pair<sysprop::Scope, const char*> tests[] = {
628       {sysprop::Scope::Internal, kExpectedInternalOutput},
629       {sysprop::Scope::Public, kExpectedPublicOutput},
630   };
631 
632   for (auto [scope, expected_output] : tests) {
633     ASSERT_RESULT_OK(GenerateJavaLibrary(temp_file.path, scope, temp_dir.path));
634 
635     std::string java_output_path =
636         temp_dir.path + "/com/somecompany/TestProperties.java"s;
637 
638     std::string java_output;
639     ASSERT_TRUE(
640         android::base::ReadFileToString(java_output_path, &java_output, true));
641     EXPECT_EQ(java_output, expected_output);
642 
643     unlink(java_output_path.c_str());
644     rmdir((temp_dir.path + "/com/somecompany"s).c_str());
645     rmdir((temp_dir.path + "/com"s).c_str());
646   }
647 }
648