xref: /aosp_15_r20/system/core/init/persistent_properties_test.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
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 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <vector>
25 
26 #include <android-base/file.h>
27 #include <gtest/gtest.h>
28 
29 #include "util.h"
30 
31 using namespace std::string_literals;
32 
33 namespace android {
34 namespace init {
35 
VectorToPersistentProperties(const std::vector<std::pair<std::string,std::string>> & input_properties)36 PersistentProperties VectorToPersistentProperties(
37     const std::vector<std::pair<std::string, std::string>>& input_properties) {
38     PersistentProperties persistent_properties;
39 
40     for (const auto& [name, value] : input_properties) {
41         auto persistent_property_record = persistent_properties.add_properties();
42         persistent_property_record->set_name(name);
43         persistent_property_record->set_value(value);
44     }
45 
46     return persistent_properties;
47 }
48 
CheckPropertiesEqual(std::vector<std::pair<std::string,std::string>> expected,const PersistentProperties & persistent_properties)49 void CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,
50                           const PersistentProperties& persistent_properties) {
51     for (const auto& persistent_property_record : persistent_properties.properties()) {
52         auto it = std::find_if(expected.begin(), expected.end(),
53                                [persistent_property_record](const auto& entry) {
54                                    return entry.first == persistent_property_record.name() &&
55                                           entry.second == persistent_property_record.value();
56                                });
57         ASSERT_TRUE(it != expected.end())
58             << "Found unexpected property (" << persistent_property_record.name() << ", "
59             << persistent_property_record.value() << ")";
60         expected.erase(it);
61     }
62     auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {
63         std::string result;
64         for (const auto& [name, value] : vector) {
65             result += " (" + name + ", " + value + ")";
66         }
67         return result;
68     };
69     EXPECT_TRUE(expected.empty()) << "Did not find expected properties:" << joiner(expected);
70 }
71 
TEST(persistent_properties,EndToEnd)72 TEST(persistent_properties, EndToEnd) {
73     TemporaryFile tf;
74     ASSERT_TRUE(tf.fd != -1);
75     persistent_property_filename = tf.path;
76 
77     std::vector<std::pair<std::string, std::string>> persistent_properties = {
78         {"persist.sys.locale", "en-US"},
79         {"persist.sys.timezone", "America/Los_Angeles"},
80         {"persist.test.empty.value", ""},
81         {"persist.test.new.line", "abc\n\n\nabc"},
82         {"persist.test.numbers", "1234567890"},
83         {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
84         // We don't currently allow for non-ascii names for system properties, but this is a policy
85         // decision, not a technical limitation.
86         {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
87     };
88 
89     ASSERT_RESULT_OK(
90             WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
91 
92     auto read_back_properties = LoadPersistentProperties();
93     CheckPropertiesEqual(persistent_properties, read_back_properties);
94 }
95 
TEST(persistent_properties,AddProperty)96 TEST(persistent_properties, AddProperty) {
97     TemporaryFile tf;
98     ASSERT_TRUE(tf.fd != -1);
99     persistent_property_filename = tf.path;
100 
101     std::vector<std::pair<std::string, std::string>> persistent_properties = {
102         {"persist.sys.timezone", "America/Los_Angeles"},
103     };
104     ASSERT_RESULT_OK(
105             WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
106 
107     WritePersistentProperty("persist.sys.locale", "pt-BR");
108 
109     std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
110         {"persist.sys.timezone", "America/Los_Angeles"},
111         {"persist.sys.locale", "pt-BR"},
112     };
113 
114     auto read_back_properties = LoadPersistentProperties();
115     CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
116 }
117 
TEST(persistent_properties,UpdateProperty)118 TEST(persistent_properties, UpdateProperty) {
119     TemporaryFile tf;
120     ASSERT_TRUE(tf.fd != -1);
121     persistent_property_filename = tf.path;
122 
123     std::vector<std::pair<std::string, std::string>> persistent_properties = {
124         {"persist.sys.locale", "en-US"},
125         {"persist.sys.timezone", "America/Los_Angeles"},
126     };
127     ASSERT_RESULT_OK(
128             WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
129 
130     WritePersistentProperty("persist.sys.locale", "pt-BR");
131 
132     std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
133         {"persist.sys.locale", "pt-BR"},
134         {"persist.sys.timezone", "America/Los_Angeles"},
135     };
136 
137     auto read_back_properties = LoadPersistentProperties();
138     CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
139 }
140 
TEST(persistent_properties,UpdatePropertyBadParse)141 TEST(persistent_properties, UpdatePropertyBadParse) {
142     TemporaryFile tf;
143     ASSERT_TRUE(tf.fd != -1);
144     persistent_property_filename = tf.path;
145 
146     ASSERT_RESULT_OK(WriteFile(tf.path, "ab"));
147 
148     WritePersistentProperty("persist.sys.locale", "pt-BR");
149 
150     auto read_back_properties = LoadPersistentProperties();
151     EXPECT_GT(read_back_properties.properties().size(), 0);
152 
153     auto it =
154         std::find_if(read_back_properties.properties().begin(),
155                      read_back_properties.properties().end(), [](const auto& entry) {
156                          return entry.name() == "persist.sys.locale" && entry.value() == "pt-BR";
157                      });
158     EXPECT_FALSE(it == read_back_properties.properties().end());
159 }
160 
TEST(persistent_properties,NopUpdateDoesntWriteFile)161 TEST(persistent_properties, NopUpdateDoesntWriteFile) {
162     TemporaryFile tf;
163     ASSERT_TRUE(tf.fd != -1);
164     persistent_property_filename = tf.path;
165 
166     auto last_modified = [&tf]() -> time_t {
167         struct stat buf;
168         EXPECT_EQ(fstat(tf.fd, &buf), 0);
169         return buf.st_mtime;
170     };
171 
172     std::vector<std::pair<std::string, std::string>> persistent_properties = {
173             {"persist.sys.locale", "en-US"},
174             {"persist.sys.timezone", "America/Los_Angeles"},
175     };
176     ASSERT_RESULT_OK(
177             WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
178 
179     time_t t = last_modified();
180     sleep(2);
181     WritePersistentProperty("persist.sys.locale", "en-US");
182     // Ensure that the file was not modified
183     ASSERT_EQ(last_modified(), t);
184 }
185 
TEST(persistent_properties,RejectNonPersistProperty)186 TEST(persistent_properties, RejectNonPersistProperty) {
187     TemporaryFile tf;
188     ASSERT_TRUE(tf.fd != -1);
189     persistent_property_filename = tf.path;
190 
191     WritePersistentProperty("notpersist.sys.locale", "pt-BR");
192 
193     auto read_back_properties = LoadPersistentProperties();
194     EXPECT_EQ(read_back_properties.properties().size(), 0);
195 
196     WritePersistentProperty("persist.sys.locale", "pt-BR");
197 
198     read_back_properties = LoadPersistentProperties();
199     EXPECT_GT(read_back_properties.properties().size(), 0);
200 
201     auto it = std::find_if(read_back_properties.properties().begin(),
202                            read_back_properties.properties().end(), [](const auto& entry) {
203                                return entry.name() == "persist.sys.locale" &&
204                                       entry.value() == "pt-BR";
205                            });
206     EXPECT_FALSE(it == read_back_properties.properties().end());
207 }
208 
TEST(persistent_properties,StagedPersistProperty)209 TEST(persistent_properties, StagedPersistProperty) {
210     TemporaryFile tf;
211     ASSERT_TRUE(tf.fd != -1);
212     persistent_property_filename = tf.path;
213 
214     std::vector<std::pair<std::string, std::string>> persistent_properties = {
215         {"persist.sys.locale", "en-US"},
216         {"next_boot.persist.test.numbers", "54321"},
217         {"persist.sys.timezone", "America/Los_Angeles"},
218         {"persist.test.numbers", "12345"},
219         {"next_boot.persist.test.extra", "abc"},
220     };
221 
222     ASSERT_RESULT_OK(
223             WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
224 
225     std::vector<std::pair<std::string, std::string>> expected_persistent_properties = {
226         {"persist.sys.locale", "en-US"},
227         {"persist.sys.timezone", "America/Los_Angeles"},
228         {"persist.test.numbers", "54321"},
229         {"persist.test.extra", "abc"},
230     };
231 
232     // lock down that staged props are applied
233     auto first_read_back_properties = LoadPersistentProperties();
234     CheckPropertiesEqual(expected_persistent_properties, first_read_back_properties);
235 
236     // lock down that other props are not overwritten
237     auto second_read_back_properties = LoadPersistentProperties();
238     CheckPropertiesEqual(expected_persistent_properties, second_read_back_properties);
239 }
240 
241 }  // namespace init
242 }  // namespace android
243