Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 | |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame^] | 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> |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 28 | |
| 29 | #include "cmd/Compile.h" |
| 30 | #include "cmd/Link.h" |
| 31 | #include "io/FileStream.h" |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 32 | #include "util/Files.h" |
| 33 | |
| 34 | using testing::Eq; |
| 35 | using testing::Ne; |
| 36 | |
| 37 | namespace aapt { |
| 38 | |
Ryan Mitchell | a55dc2e | 2019-01-24 10:58:23 -0800 | [diff] [blame] | 39 | const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test"; |
| 40 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 | |
| 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 | |
| 78 | void TestDirectoryFixture::TearDown() { |
| 79 | ClearDirectory(temp_dir_); |
| 80 | } |
| 81 | |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 82 | void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) { |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 83 | CHECK(util::StartsWith(path, temp_dir_)) |
| 84 | << "Attempting to create a file outside of test temporary directory."; |
| 85 | |
| 86 | // Create any intermediate directories specified in the path |
| 87 | auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep); |
| 88 | if (pos != path.rend()) { |
| 89 | std::string dirs = path.substr(0, (&*pos - path.data())); |
| 90 | file::mkdirs(dirs); |
| 91 | } |
| 92 | |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 93 | CHECK(android::base::WriteStringToFile(contents, path)); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents, |
| 97 | const android::StringPiece& out_dir, IDiagnostics* diag) { |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 98 | WriteFile(path, contents); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 99 | CHECK(file::mkdirs(out_dir.data())); |
| 100 | return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0; |
| 101 | } |
| 102 | |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 103 | bool CommandTestFixture::Link(const std::vector<std::string>& args, 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 = file::BuildPath({android::base::GetExecutableDirectory(), |
| 111 | "integration-tests", "CommandTests", |
| 112 | "android-28.jar"}); |
| 113 | link_args.insert(link_args.end(), {"-I", android_sdk}); |
| 114 | |
| 115 | return LinkCommand(diag).Execute(link_args, &std::cerr) == 0; |
| 116 | } |
| 117 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 118 | bool CommandTestFixture::Link(const std::vector<std::string>& args, |
| 119 | const android::StringPiece& flat_dir, 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 = file::BuildPath({android::base::GetExecutableDirectory(), |
| 127 | "integration-tests", "CommandTests", |
| 128 | "android-28.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 | Maybe<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 | |
Ryan Mitchell | a55dc2e | 2019-01-24 10:58:23 -0800 | [diff] [blame] | 143 | std::string CommandTestFixture::GetDefaultManifest(const char* package_name) { |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 144 | const std::string manifest_file = GetTestPath("AndroidManifest.xml"); |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 145 | WriteFile(manifest_file, android::base::StringPrintf(R"( |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 146 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
Ryan Mitchell | a55dc2e | 2019-01-24 10:58:23 -0800 | [diff] [blame] | 147 | package="%s"> |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 148 | </manifest>)", package_name)); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 149 | return manifest_file; |
| 150 | } |
| 151 | |
Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 152 | std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk, |
| 153 | const 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, |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 161 | android::ResXMLTree *out_tree) { |
| 162 | ASSERT_THAT(apk, Ne(nullptr)); |
| 163 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 | |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame^] | 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 | 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 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 242 | } // namespace aapt |