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 <cstdio>
20 
21 #include <sys/stat.h>
22 #include "aconfig_storage/aconfig_storage_read_api.hpp"
23 #include "aconfig_storage/aconfig_storage_write_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_to_rw_temp_file(std::string const & source_file)35   Result<std::string> copy_to_rw_temp_file(std::string const& source_file) {
36     auto temp_file = std::string(std::tmpnam(nullptr));
37     auto content = std::string();
38     if (!ReadFileToString(source_file, &content)) {
39       return Error() << "failed to read file: " << source_file;
40     }
41     if (!WriteStringToFile(content, temp_file)) {
42       return Error() << "failed to copy file: " << source_file;
43     }
44     if (chmod(temp_file.c_str(),
45               S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) == -1) {
46       return Error() << "failed to chmod";
47     }
48     return temp_file;
49   }
50 
SetUp()51   void SetUp() override {
52     auto const test_dir = android::base::GetExecutableDirectory();
53     flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val");
54     flag_info = *copy_to_rw_temp_file(test_dir + "/flag.info");
55   }
56 
TearDown()57   void TearDown() override {
58     std::remove(flag_val.c_str());
59     std::remove(flag_info.c_str());
60   }
61 
62   std::string flag_val;
63   std::string flag_info;
64 };
65 
66 /// Negative test to lock down the error when mapping a non writeable storage file
TEST_F(AconfigStorageTest,test_non_writable_storage_file_mapping)67 TEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) {
68   ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1);
69   auto mapped_file_result = api::map_mutable_storage_file(flag_val);
70   ASSERT_FALSE(mapped_file_result.ok());
71   auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
72   ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
73 }
74 
75 /// Test to lock down storage flag value update api
TEST_F(AconfigStorageTest,test_boolean_flag_value_update)76 TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
77   auto mapped_file_result = api::map_mutable_storage_file(flag_val);
78   ASSERT_TRUE(mapped_file_result.ok());
79   auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);
80 
81   for (int offset = 0; offset < 8; ++offset) {
82     auto update_result = api::set_boolean_flag_value(*mapped_file, offset, true);
83     ASSERT_TRUE(update_result.ok());
84     auto value = api::get_boolean_flag_value(*mapped_file, offset);
85     ASSERT_TRUE(value.ok());
86     ASSERT_TRUE(*value);
87   }
88 }
89 
90 /// Negative test to lock down the error when querying flag value out of range
TEST_F(AconfigStorageTest,test_invalid_boolean_flag_value_update)91 TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {
92   auto mapped_file_result = api::map_mutable_storage_file(flag_val);
93   ASSERT_TRUE(mapped_file_result.ok());
94   auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);
95   auto update_result = api::set_boolean_flag_value(*mapped_file, 8, true);
96   ASSERT_FALSE(update_result.ok());
97   ASSERT_EQ(update_result.error().message(),
98             std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
99 }
100 
101 /// Test to lock down storage flag has server override update api
TEST_F(AconfigStorageTest,test_flag_has_server_override_update)102 TEST_F(AconfigStorageTest, test_flag_has_server_override_update) {
103   auto mapped_file_result = api::map_mutable_storage_file(flag_info);
104   ASSERT_TRUE(mapped_file_result.ok());
105   auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);
106 
107   for (int offset = 0; offset < 8; ++offset) {
108     auto update_result = api::set_flag_has_server_override(
109         *mapped_file, api::FlagValueType::Boolean, offset, true);
110     ASSERT_TRUE(update_result.ok()) << update_result.error();
111     auto attribute = api::get_flag_attribute(
112         *mapped_file, api::FlagValueType::Boolean, offset);
113     ASSERT_TRUE(attribute.ok());
114     ASSERT_TRUE(*attribute & api::FlagInfoBit::HasServerOverride);
115 
116     update_result = api::set_flag_has_server_override(
117         *mapped_file, api::FlagValueType::Boolean, offset, false);
118     ASSERT_TRUE(update_result.ok());
119     attribute = api::get_flag_attribute(
120         *mapped_file, api::FlagValueType::Boolean, offset);
121     ASSERT_TRUE(attribute.ok());
122     ASSERT_FALSE(*attribute & api::FlagInfoBit::HasServerOverride);
123   }
124 }
125 
126 /// Test to lock down storage flag has local override update api
TEST_F(AconfigStorageTest,test_flag_has_local_override_update)127 TEST_F(AconfigStorageTest, test_flag_has_local_override_update) {
128   auto mapped_file_result = api::map_mutable_storage_file(flag_info);
129   ASSERT_TRUE(mapped_file_result.ok());
130   auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);
131 
132   for (int offset = 0; offset < 8; ++offset) {
133     auto update_result = api::set_flag_has_local_override(
134         *mapped_file, api::FlagValueType::Boolean, offset, true);
135     ASSERT_TRUE(update_result.ok());
136     auto attribute = api::get_flag_attribute(
137         *mapped_file, api::FlagValueType::Boolean, offset);
138     ASSERT_TRUE(attribute.ok());
139     ASSERT_TRUE(*attribute & api::FlagInfoBit::HasLocalOverride);
140 
141     update_result = api::set_flag_has_local_override(
142         *mapped_file, api::FlagValueType::Boolean, offset, false);
143     ASSERT_TRUE(update_result.ok());
144     attribute = api::get_flag_attribute(
145         *mapped_file, api::FlagValueType::Boolean, offset);
146     ASSERT_TRUE(attribute.ok());
147     ASSERT_FALSE(*attribute & api::FlagInfoBit::HasLocalOverride);
148   }
149 }
150