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