• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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