• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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({android::base::GetExecutableDirectory(),
71                                "_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,const android::StringPiece & out_dir,IDiagnostics * diag)93 bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
94                                      const android::StringPiece& out_dir, IDiagnostics* diag) {
95   WriteFile(path, contents);
96   CHECK(file::mkdirs(out_dir.data()));
97   return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
98 }
99 
Link(const std::vector<std::string> & args,IDiagnostics * diag)100 bool CommandTestFixture::Link(const std::vector<std::string>& args, IDiagnostics* diag) {
101   std::vector<android::StringPiece> link_args;
102   for(const std::string& arg : args) {
103     link_args.emplace_back(arg);
104   }
105 
106   // Link against the android SDK
107   std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
108                                              "integration-tests", "CommandTests",
109                                              "android-28.jar"});
110   link_args.insert(link_args.end(), {"-I", android_sdk});
111 
112   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
113 }
114 
Link(const std::vector<std::string> & args,const android::StringPiece & flat_dir,IDiagnostics * diag)115 bool CommandTestFixture::Link(const std::vector<std::string>& args,
116                               const android::StringPiece& flat_dir, IDiagnostics* diag) {
117   std::vector<android::StringPiece> link_args;
118   for(const std::string& arg : args) {
119     link_args.emplace_back(arg);
120   }
121 
122   // Link against the android SDK
123   std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
124                                              "integration-tests", "CommandTests",
125                                              "android-28.jar"});
126   link_args.insert(link_args.end(), {"-I", android_sdk});
127 
128   // Add the files from the compiled resources directory to the link file arguments
129   Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
130   if (compiled_files) {
131     for (std::string& compile_file : compiled_files.value()) {
132       compile_file = file::BuildPath({flat_dir, compile_file});
133       link_args.emplace_back(std::move(compile_file));
134     }
135   }
136 
137   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
138 }
139 
GetDefaultManifest(const char * package_name)140 std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
141   const std::string manifest_file = GetTestPath("AndroidManifest.xml");
142   WriteFile(manifest_file, android::base::StringPrintf(R"(
143       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
144           package="%s">
145       </manifest>)", package_name));
146   return manifest_file;
147 }
148 
149 std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk,
150                                                               const android::StringPiece& path) {
151   return apk
152       ->GetFileCollection()
153       ->FindFile(path)
154       ->OpenAsData();
155 }
156 
157 void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
158                                        android::ResXMLTree *out_tree) {
159   ASSERT_THAT(apk, Ne(nullptr));
160 
161   out_tree->setTo(data->data(), data->size());
162   ASSERT_THAT(out_tree->getError(), Eq(android::OK));
163   while (out_tree->next() != android::ResXMLTree::START_TAG) {
164     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
165     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
166   }
167 }
168 
169 ManifestBuilder::ManifestBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
170 }
171 
172 ManifestBuilder& ManifestBuilder::SetPackageName(const std::string& package_name) {
173   package_name_ = package_name;
174   return *this;
175 }
176 
177 ManifestBuilder& ManifestBuilder::AddContents(const std::string& contents) {
178   contents_ += contents + "\n";
179   return *this;
180 }
181 
182 std::string ManifestBuilder::Build(const std::string& file_path) {
183   const char* manifest_template = R"(
184       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
185           package="%s">
186           %s
187       </manifest>)";
188 
189   fixture_->WriteFile(file_path, android::base::StringPrintf(
190                                      manifest_template, package_name_.c_str(), contents_.c_str()));
191   return file_path;
192 }
193 
194 std::string ManifestBuilder::Build() {
195   return Build(fixture_->GetTestPath("AndroidManifest.xml"));
196 }
197 
198 LinkCommandBuilder::LinkCommandBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
199 }
200 
201 LinkCommandBuilder& LinkCommandBuilder::SetManifestFile(const std::string& file) {
202   manifest_supplied_ = true;
203   args_.emplace_back("--manifest");
204   args_.emplace_back(file);
205   return *this;
206 }
207 
208 LinkCommandBuilder& LinkCommandBuilder::AddFlag(const std::string& flag) {
209   args_.emplace_back(flag);
210   return *this;
211 }
212 
213 LinkCommandBuilder& LinkCommandBuilder::AddCompiledResDir(const std::string& dir,
214                                                           IDiagnostics* diag) {
215   if (auto files = file::FindFiles(dir, diag)) {
216     for (std::string& compile_file : files.value()) {
217       args_.emplace_back(file::BuildPath({dir, compile_file}));
218     }
219   }
220   return *this;
221 }
222 
223 LinkCommandBuilder& LinkCommandBuilder::AddParameter(const std::string& param,
224                                                      const std::string& value) {
225   args_.emplace_back(param);
226   args_.emplace_back(value);
227   return *this;
228 }
229 
230 std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) {
231   if (!manifest_supplied_) {
232     SetManifestFile(ManifestBuilder(fixture_).Build());
233   }
234   args_.emplace_back("-o");
235   args_.emplace_back(out_apk);
236   return args_;
237 }
238 
239 } // namespace aapt