1 /*
2 * Copyright (C) 2024 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 <string>
18 #include <vector>
19 #include <memory>
20 #include <cstdio>
21
22 #include <sys/stat.h>
23 #include "aconfig_storage/aconfig_storage_read_api.hpp"
24 #include <gtest/gtest.h>
25 #include <android-base/file.h>
26 #include <android-base/result.h>
27
28 using namespace android::base;
29
30 namespace api = aconfig_storage;
31 namespace private_api = aconfig_storage::private_internal_api;
32
33 class AconfigStorageTest : public ::testing::Test {
34 protected:
copy_file(std::string const & src_file,std::string const & dst_file)35 Result<void> copy_file(std::string const& src_file,
36 std::string const& dst_file) {
37 auto content = std::string();
38 if (!ReadFileToString(src_file, &content)) {
39 return Error() << "failed to read file: " << src_file;
40 }
41 if (!WriteStringToFile(content, dst_file)) {
42 return Error() << "failed to copy file: " << dst_file;
43 }
44 return {};
45 }
46
SetUp()47 void SetUp() override {
48 auto const test_base_dir = android::base::GetExecutableDirectory();
49 auto const test_dir = test_base_dir + "/data/v1";
50 storage_dir = std::string(root_dir.path);
51 auto maps_dir = storage_dir + "/maps";
52 auto boot_dir = storage_dir + "/boot";
53 mkdir(maps_dir.c_str(), 0775);
54 mkdir(boot_dir.c_str(), 0775);
55 package_map = std::string(maps_dir) + "/mockup.package.map";
56 flag_map = std::string(maps_dir) + "/mockup.flag.map";
57 flag_val = std::string(boot_dir) + "/mockup.val";
58 flag_info = std::string(boot_dir) + "/mockup.info";
59 copy_file(test_dir + "/package_v1.map", package_map);
60 copy_file(test_dir + "/flag_v1.map", flag_map);
61 copy_file(test_dir + "/flag_v1.val", flag_val);
62 copy_file(test_dir + "/flag_v1.info", flag_info);
63 }
64
TearDown()65 void TearDown() override {
66 std::remove(package_map.c_str());
67 std::remove(flag_map.c_str());
68 std::remove(flag_val.c_str());
69 std::remove(flag_info.c_str());
70 }
71
72 TemporaryDir root_dir;
73 std::string storage_dir;
74 std::string package_map;
75 std::string flag_map;
76 std::string flag_val;
77 std::string flag_info;
78 };
79
80 /// Test to lock down storage file version query api
TEST_F(AconfigStorageTest,test_storage_version_query)81 TEST_F(AconfigStorageTest, test_storage_version_query) {
82 auto version = api::get_storage_file_version(package_map);
83 ASSERT_TRUE(version.ok());
84 ASSERT_EQ(*version, 1);
85 version = api::get_storage_file_version(flag_map);
86 ASSERT_TRUE(version.ok());
87 ASSERT_EQ(*version, 1);
88 version = api::get_storage_file_version(flag_val);
89 ASSERT_TRUE(version.ok());
90 ASSERT_EQ(*version, 1);
91 version = api::get_storage_file_version(flag_info);
92 ASSERT_TRUE(version.ok());
93 ASSERT_EQ(*version, 1);
94 }
95
96 /// Negative test to lock down the error when mapping none exist storage files
TEST_F(AconfigStorageTest,test_none_exist_storage_file_mapping)97 TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
98 auto mapped_file_result = private_api::get_mapped_file_impl(
99 storage_dir, "vendor", api::StorageFileType::package_map);
100 ASSERT_FALSE(mapped_file_result.ok());
101 ASSERT_EQ(mapped_file_result.error(),
102 std::string("failed to open ") + storage_dir
103 + "/maps/vendor.package.map: No such file or directory");
104 }
105
106 /// Test to lock down storage package context query api
TEST_F(AconfigStorageTest,test_package_context_query)107 TEST_F(AconfigStorageTest, test_package_context_query) {
108 auto mapped_file_result = private_api::get_mapped_file_impl(
109 storage_dir, "mockup", api::StorageFileType::package_map);
110 ASSERT_TRUE(mapped_file_result.ok());
111 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
112
113 auto context = api::get_package_read_context(
114 *mapped_file, "com.android.aconfig.storage.test_1");
115 ASSERT_TRUE(context.ok());
116 ASSERT_TRUE(context->package_exists);
117 ASSERT_EQ(context->package_id, 0);
118 ASSERT_EQ(context->boolean_start_index, 0);
119
120 context = api::get_package_read_context(
121 *mapped_file, "com.android.aconfig.storage.test_2");
122 ASSERT_TRUE(context.ok());
123 ASSERT_TRUE(context->package_exists);
124 ASSERT_EQ(context->package_id, 1);
125 ASSERT_EQ(context->boolean_start_index, 3);
126
127 context = api::get_package_read_context(
128 *mapped_file, "com.android.aconfig.storage.test_4");
129 ASSERT_TRUE(context.ok());
130 ASSERT_TRUE(context->package_exists);
131 ASSERT_EQ(context->package_id, 2);
132 ASSERT_EQ(context->boolean_start_index, 6);
133 }
134
135 /// Test to lock down when querying none exist package
TEST_F(AconfigStorageTest,test_none_existent_package_context_query)136 TEST_F(AconfigStorageTest, test_none_existent_package_context_query) {
137 auto mapped_file_result = private_api::get_mapped_file_impl(
138 storage_dir, "mockup", api::StorageFileType::package_map);
139 ASSERT_TRUE(mapped_file_result.ok());
140 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
141
142 auto context = api::get_package_read_context(
143 *mapped_file, "com.android.aconfig.storage.test_3");
144 ASSERT_TRUE(context.ok());
145 ASSERT_FALSE(context->package_exists);
146 }
147
148 /// Test to lock down storage flag context query api
TEST_F(AconfigStorageTest,test_flag_context_query)149 TEST_F(AconfigStorageTest, test_flag_context_query) {
150 auto mapped_file_result = private_api::get_mapped_file_impl(
151 storage_dir, "mockup", api::StorageFileType::flag_map);
152 ASSERT_TRUE(mapped_file_result.ok());
153 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
154
155 auto baseline = std::vector<std::tuple<int, std::string, api::StoredFlagType, int>>{
156 {0, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
157 {0, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 2},
158 {2, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 1},
159 {1, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0},
160 {1, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 1},
161 {1, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 2},
162 {2, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 0},
163 {0, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0},
164 };
165 for (auto const&[package_id, flag_name, flag_type, flag_index] : baseline) {
166 auto context = api::get_flag_read_context(*mapped_file, package_id, flag_name);
167 ASSERT_TRUE(context.ok());
168 ASSERT_TRUE(context->flag_exists);
169 ASSERT_EQ(context->flag_type, flag_type);
170 ASSERT_EQ(context->flag_index, flag_index);
171 }
172 }
173
174 /// Test to lock down when querying none exist flag
TEST_F(AconfigStorageTest,test_none_existent_flag_context_query)175 TEST_F(AconfigStorageTest, test_none_existent_flag_context_query) {
176 auto mapped_file_result = private_api::get_mapped_file_impl(
177 storage_dir, "mockup", api::StorageFileType::flag_map);
178 ASSERT_TRUE(mapped_file_result.ok());
179 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
180
181 auto context = api::get_flag_read_context(*mapped_file, 0, "none_exist");
182 ASSERT_TRUE(context.ok());
183 ASSERT_FALSE(context->flag_exists);
184
185 context = api::get_flag_read_context(*mapped_file, 3, "enabled_ro");
186 ASSERT_TRUE(context.ok());
187 ASSERT_FALSE(context->flag_exists);
188 }
189
190 /// Test to lock down storage flag value query api
TEST_F(AconfigStorageTest,test_boolean_flag_value_query)191 TEST_F(AconfigStorageTest, test_boolean_flag_value_query) {
192 auto mapped_file_result = private_api::get_mapped_file_impl(
193 storage_dir, "mockup", api::StorageFileType::flag_val);
194 ASSERT_TRUE(mapped_file_result.ok());
195 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
196
197 auto expected_value = std::vector<bool>{
198 false, true, true, false, true, true, true, true};
199 for (int index = 0; index < 8; ++index) {
200 auto value = api::get_boolean_flag_value(*mapped_file, index);
201 ASSERT_TRUE(value.ok());
202 ASSERT_EQ(*value, expected_value[index]);
203 }
204 }
205
206 /// Negative test to lock down the error when querying flag value out of range
TEST_F(AconfigStorageTest,test_invalid_boolean_flag_value_query)207 TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
208 auto mapped_file_result = private_api::get_mapped_file_impl(
209 storage_dir, "mockup", api::StorageFileType::flag_val);
210 ASSERT_TRUE(mapped_file_result.ok());
211 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
212
213 auto value = api::get_boolean_flag_value(*mapped_file, 8);
214 ASSERT_FALSE(value.ok());
215 ASSERT_EQ(value.error(),
216 std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
217 }
218
219 /// Test to lock down storage flag info query api
TEST_F(AconfigStorageTest,test_boolean_flag_info_query)220 TEST_F(AconfigStorageTest, test_boolean_flag_info_query) {
221 auto mapped_file_result = private_api::get_mapped_file_impl(
222 storage_dir, "mockup", api::StorageFileType::flag_info);
223 ASSERT_TRUE(mapped_file_result.ok());
224 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
225
226 auto expected_value = std::vector<bool>{
227 true, false, true, true, false, false, false, true};
228 for (int index = 0; index < 8; ++index) {
229 auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);
230 ASSERT_TRUE(attribute.ok());
231 ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::HasServerOverride), 0);
232 ASSERT_EQ((*attribute & static_cast<uint8_t>(api::FlagInfoBit::IsReadWrite)) != 0,
233 expected_value[index]);
234 ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::HasLocalOverride), 0);
235 }
236 }
237
238 /// Negative test to lock down the error when querying flag info out of range
TEST_F(AconfigStorageTest,test_invalid_boolean_flag_info_query)239 TEST_F(AconfigStorageTest, test_invalid_boolean_flag_info_query) {
240 auto mapped_file_result = private_api::get_mapped_file_impl(
241 storage_dir, "mockup", api::StorageFileType::flag_info);
242 ASSERT_TRUE(mapped_file_result.ok());
243 auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);
244
245 auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, 8);
246 ASSERT_FALSE(attribute.ok());
247 ASSERT_EQ(attribute.error(),
248 std::string("InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)"));
249 }
250