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