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