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)93 bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents, 94 android::StringPiece out_dir, android::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,android::IDiagnostics * diag)100 bool CommandTestFixture::Link(const std::vector<std::string>& args, android::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 = 108 file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests", 109 "android-33.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,android::StringPiece flat_dir,android::IDiagnostics * diag)115 bool CommandTestFixture::Link(const std::vector<std::string>& args, android::StringPiece flat_dir, 116 android::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 = 124 file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests", 125 "android-33.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 std::optional<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 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 android::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 240