• 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_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