1 /*
2 * Copyright (C) 2017 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 "persistent_properties.h"
18
19 #include <errno.h>
20
21 #include <vector>
22
23 #include <android-base/file.h>
24 #include <gtest/gtest.h>
25
26 #include "util.h"
27
28 using namespace std::string_literals;
29
30 namespace android {
31 namespace init {
32
VectorToPersistentProperties(const std::vector<std::pair<std::string,std::string>> & input_properties)33 PersistentProperties VectorToPersistentProperties(
34 const std::vector<std::pair<std::string, std::string>>& input_properties) {
35 PersistentProperties persistent_properties;
36
37 for (const auto& [name, value] : input_properties) {
38 auto persistent_property_record = persistent_properties.add_properties();
39 persistent_property_record->set_name(name);
40 persistent_property_record->set_value(value);
41 }
42
43 return persistent_properties;
44 }
45
CheckPropertiesEqual(std::vector<std::pair<std::string,std::string>> expected,const PersistentProperties & persistent_properties)46 void CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,
47 const PersistentProperties& persistent_properties) {
48 for (const auto& persistent_property_record : persistent_properties.properties()) {
49 auto it = std::find_if(expected.begin(), expected.end(),
50 [persistent_property_record](const auto& entry) {
51 return entry.first == persistent_property_record.name() &&
52 entry.second == persistent_property_record.value();
53 });
54 ASSERT_TRUE(it != expected.end())
55 << "Found unexpected property (" << persistent_property_record.name() << ", "
56 << persistent_property_record.value() << ")";
57 expected.erase(it);
58 }
59 auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {
60 std::string result;
61 for (const auto& [name, value] : vector) {
62 result += " (" + name + ", " + value + ")";
63 }
64 return result;
65 };
66 EXPECT_TRUE(expected.empty()) << "Did not find expected properties:" << joiner(expected);
67 }
68
TEST(persistent_properties,EndToEnd)69 TEST(persistent_properties, EndToEnd) {
70 TemporaryFile tf;
71 ASSERT_TRUE(tf.fd != -1);
72 persistent_property_filename = tf.path;
73
74 std::vector<std::pair<std::string, std::string>> persistent_properties = {
75 {"persist.sys.locale", "en-US"},
76 {"persist.sys.timezone", "America/Los_Angeles"},
77 {"persist.test.empty.value", ""},
78 {"persist.test.new.line", "abc\n\n\nabc"},
79 {"persist.test.numbers", "1234567890"},
80 {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
81 // We don't currently allow for non-ascii names for system properties, but this is a policy
82 // decision, not a technical limitation.
83 {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
84 };
85
86 ASSERT_RESULT_OK(
87 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
88
89 auto read_back_properties = LoadPersistentProperties();
90 CheckPropertiesEqual(persistent_properties, read_back_properties);
91 }
92
TEST(persistent_properties,AddProperty)93 TEST(persistent_properties, AddProperty) {
94 TemporaryFile tf;
95 ASSERT_TRUE(tf.fd != -1);
96 persistent_property_filename = tf.path;
97
98 std::vector<std::pair<std::string, std::string>> persistent_properties = {
99 {"persist.sys.timezone", "America/Los_Angeles"},
100 };
101 ASSERT_RESULT_OK(
102 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
103
104 WritePersistentProperty("persist.sys.locale", "pt-BR");
105
106 std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
107 {"persist.sys.timezone", "America/Los_Angeles"},
108 {"persist.sys.locale", "pt-BR"},
109 };
110
111 auto read_back_properties = LoadPersistentProperties();
112 CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
113 }
114
TEST(persistent_properties,UpdateProperty)115 TEST(persistent_properties, UpdateProperty) {
116 TemporaryFile tf;
117 ASSERT_TRUE(tf.fd != -1);
118 persistent_property_filename = tf.path;
119
120 std::vector<std::pair<std::string, std::string>> persistent_properties = {
121 {"persist.sys.locale", "en-US"},
122 {"persist.sys.timezone", "America/Los_Angeles"},
123 };
124 ASSERT_RESULT_OK(
125 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
126
127 WritePersistentProperty("persist.sys.locale", "pt-BR");
128
129 std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
130 {"persist.sys.locale", "pt-BR"},
131 {"persist.sys.timezone", "America/Los_Angeles"},
132 };
133
134 auto read_back_properties = LoadPersistentProperties();
135 CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
136 }
137
TEST(persistent_properties,UpdatePropertyBadParse)138 TEST(persistent_properties, UpdatePropertyBadParse) {
139 TemporaryFile tf;
140 ASSERT_TRUE(tf.fd != -1);
141 persistent_property_filename = tf.path;
142
143 ASSERT_RESULT_OK(WriteFile(tf.path, "ab"));
144
145 WritePersistentProperty("persist.sys.locale", "pt-BR");
146
147 auto read_back_properties = LoadPersistentProperties();
148 EXPECT_GT(read_back_properties.properties().size(), 0);
149
150 auto it =
151 std::find_if(read_back_properties.properties().begin(),
152 read_back_properties.properties().end(), [](const auto& entry) {
153 return entry.name() == "persist.sys.locale" && entry.value() == "pt-BR";
154 });
155 EXPECT_FALSE(it == read_back_properties.properties().end());
156 }
157
TEST(persistent_properties,RejectNonPersistProperty)158 TEST(persistent_properties, RejectNonPersistProperty) {
159 TemporaryFile tf;
160 ASSERT_TRUE(tf.fd != -1);
161 persistent_property_filename = tf.path;
162
163 WritePersistentProperty("notpersist.sys.locale", "pt-BR");
164
165 auto read_back_properties = LoadPersistentProperties();
166 EXPECT_EQ(read_back_properties.properties().size(), 0);
167
168 WritePersistentProperty("persist.sys.locale", "pt-BR");
169
170 read_back_properties = LoadPersistentProperties();
171 EXPECT_GT(read_back_properties.properties().size(), 0);
172
173 auto it = std::find_if(read_back_properties.properties().begin(),
174 read_back_properties.properties().end(), [](const auto& entry) {
175 return entry.name() == "persist.sys.locale" &&
176 entry.value() == "pt-BR";
177 });
178 EXPECT_FALSE(it == read_back_properties.properties().end());
179 }
180
181 } // namespace init
182 } // namespace android
183