xref: /aosp_15_r20/frameworks/base/tools/aapt2/test/Fixture.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2018 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 "test/Fixture.h"
18 
19 #include <android-base/errors.h>
20 #include <android-base/file.h>
21 #include <android-base/stringprintf.h>
22 #include <android-base/utf8.h>
23 #include <androidfw/FileStream.h>
24 #include <androidfw/StringPiece.h>
25 #include <dirent.h>
26 #include <gmock/gmock.h>
27 #include <gtest/gtest.h>
28 
29 #include "Diagnostics.h"
30 #include "cmd/Compile.h"
31 #include "cmd/Link.h"
32 #include "util/Files.h"
33 
34 using testing::Eq;
35 using testing::Ne;
36 
37 namespace aapt {
38 
39 const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test";
40 
ClearDirectory(android::StringPiece path)41 void ClearDirectory(android::StringPiece path) {
42   const std::string root_dir(path);
43   std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
44   if (!dir) {
45     StdErrDiagnostics().Error(android::DiagMessage()
46                               << android::base::SystemErrorCodeToString(errno));
47     return;
48   }
49 
50   while (struct dirent* entry = readdir(dir.get())) {
51     // Do not delete hidden files and do not recurse to the parent of this directory
52     if (util::StartsWith(entry->d_name, ".")) {
53       continue;
54     }
55 
56     std::string full_path = file::BuildPath({root_dir, entry->d_name});
57     if (file::GetFileType(full_path) == file::FileType::kDirectory) {
58       ClearDirectory(full_path);
59 #ifdef _WIN32
60       _rmdir(full_path.c_str());
61 #else
62       rmdir(full_path.c_str());
63 #endif
64     } else {
65       android::base::utf8::unlink(full_path.c_str());
66     }
67   }
68 }
69 
SetUp()70 void TestDirectoryFixture::SetUp() {
71   temp_dir_ = file::BuildPath({testing::TempDir(), "_temp",
72                                testing::UnitTest::GetInstance()->current_test_case()->name(),
73                                testing::UnitTest::GetInstance()->current_test_info()->name()});
74   ASSERT_TRUE(file::mkdirs(temp_dir_));
75   ClearDirectory(temp_dir_);
76 }
77 
TearDown()78 void TestDirectoryFixture::TearDown() {
79   ClearDirectory(temp_dir_);
80 }
81 
WriteFile(const std::string & path,const std::string & contents)82 void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
83   // Create any intermediate directories specified in the path
84   auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
85   if (pos != path.rend()) {
86     std::string dirs = path.substr(0, (&*pos - path.data()));
87     file::mkdirs(dirs);
88   }
89 
90   CHECK(android::base::WriteStringToFile(contents, path));
91 }
92 
CompileFile(const std::string & path,const std::string & contents,android::StringPiece out_dir,android::IDiagnostics * diag,const std::vector<android::StringPiece> & additional_args)93 bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
94                                      android::StringPiece out_dir, android::IDiagnostics* diag,
95                                      const std::vector<android::StringPiece>& additional_args) {
96   WriteFile(path, contents);
97   CHECK(file::mkdirs(out_dir.data()));
98   std::vector<android::StringPiece> args = {path, "-o", out_dir, "-v"};
99   args.insert(args.end(), additional_args.begin(), additional_args.end());
100   return CompileCommand(diag).Execute(args, &std::cerr) == 0;
101 }
102 
Link(const std::vector<std::string> & args,android::IDiagnostics * diag)103 bool CommandTestFixture::Link(const std::vector<std::string>& args, android::IDiagnostics* diag) {
104   std::vector<android::StringPiece> link_args;
105   for(const std::string& arg : args) {
106     link_args.emplace_back(arg);
107   }
108 
109   // Link against the android SDK
110   std::string android_sdk =
111       file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests",
112                        "android-33.jar"});
113   link_args.insert(link_args.end(), {"-I", android_sdk});
114 
115   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
116 }
117 
Link(const std::vector<std::string> & args,android::StringPiece flat_dir,android::IDiagnostics * diag)118 bool CommandTestFixture::Link(const std::vector<std::string>& args, android::StringPiece flat_dir,
119                               android::IDiagnostics* diag) {
120   std::vector<android::StringPiece> link_args;
121   for(const std::string& arg : args) {
122     link_args.emplace_back(arg);
123   }
124 
125   // Link against the android SDK
126   std::string android_sdk =
127       file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests",
128                        "android-33.jar"});
129   link_args.insert(link_args.end(), {"-I", android_sdk});
130 
131   // Add the files from the compiled resources directory to the link file arguments
132   std::optional<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
133   if (compiled_files) {
134     for (std::string& compile_file : compiled_files.value()) {
135       compile_file = file::BuildPath({flat_dir, compile_file});
136       link_args.emplace_back(std::move(compile_file));
137     }
138   }
139 
140   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
141 }
142 
GetDefaultManifest(const char * package_name)143 std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
144   const std::string manifest_file = GetTestPath("AndroidManifest.xml");
145   WriteFile(manifest_file, android::base::StringPrintf(R"(
146       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
147           package="%s">
148       </manifest>)", package_name));
149   return manifest_file;
150 }
151 
152 std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk,
153                                                               android::StringPiece path) {
154   return apk
155       ->GetFileCollection()
156       ->FindFile(path)
157       ->OpenAsData();
158 }
159 
160 void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
161                                        android::ResXMLTree *out_tree) {
162   ASSERT_THAT(apk, Ne(nullptr));
163 
164   out_tree->setTo(data->data(), data->size());
165   ASSERT_THAT(out_tree->getError(), Eq(android::OK));
166   while (out_tree->next() != android::ResXMLTree::START_TAG) {
167     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
168     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
169   }
170 }
171 
172 ManifestBuilder::ManifestBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
173 }
174 
175 ManifestBuilder& ManifestBuilder::SetPackageName(const std::string& package_name) {
176   package_name_ = package_name;
177   return *this;
178 }
179 
180 ManifestBuilder& ManifestBuilder::AddContents(const std::string& contents) {
181   contents_ += contents + "\n";
182   return *this;
183 }
184 
185 std::string ManifestBuilder::Build(const std::string& file_path) {
186   const char* manifest_template = R"(
187       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
188           package="%s">
189           %s
190       </manifest>)";
191 
192   fixture_->WriteFile(file_path, android::base::StringPrintf(
193                                      manifest_template, package_name_.c_str(), contents_.c_str()));
194   return file_path;
195 }
196 
197 std::string ManifestBuilder::Build() {
198   return Build(fixture_->GetTestPath("AndroidManifest.xml"));
199 }
200 
201 LinkCommandBuilder::LinkCommandBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
202 }
203 
204 LinkCommandBuilder& LinkCommandBuilder::SetManifestFile(const std::string& file) {
205   manifest_supplied_ = true;
206   args_.emplace_back("--manifest");
207   args_.emplace_back(file);
208   return *this;
209 }
210 
211 LinkCommandBuilder& LinkCommandBuilder::AddFlag(const std::string& flag) {
212   args_.emplace_back(flag);
213   return *this;
214 }
215 
216 LinkCommandBuilder& LinkCommandBuilder::AddCompiledResDir(const std::string& dir,
217                                                           android::IDiagnostics* diag) {
218   if (auto files = file::FindFiles(dir, diag)) {
219     for (std::string& compile_file : files.value()) {
220       args_.emplace_back(file::BuildPath({dir, compile_file}));
221     }
222   }
223   return *this;
224 }
225 
226 LinkCommandBuilder& LinkCommandBuilder::AddParameter(const std::string& param,
227                                                      const std::string& value) {
228   args_.emplace_back(param);
229   args_.emplace_back(value);
230   return *this;
231 }
232 
233 std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) {
234   if (!manifest_supplied_) {
235     SetManifestFile(ManifestBuilder(fixture_).Build());
236   }
237   args_.emplace_back("-o");
238   args_.emplace_back(out_apk);
239   return args_;
240 }
241 
242 }  // namespace aapt
243