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 | // 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 | |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 90 | CHECK(android::base::WriteStringToFile(contents, path)); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents, |
| 94 | const android::StringPiece& out_dir, IDiagnostics* diag) { |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 95 | WriteFile(path, contents); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 96 | CHECK(file::mkdirs(out_dir.data())); |
| 97 | return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0; |
| 98 | } |
| 99 | |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 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 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 |
Ryan Mitchell | 4382e44 | 2021-07-14 12:53:01 -0700 | [diff] [blame^] | 129 | std::optional<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 | |
Ryan Mitchell | a55dc2e | 2019-01-24 10:58:23 -0800 | [diff] [blame] | 140 | std::string CommandTestFixture::GetDefaultManifest(const char* package_name) { |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 141 | const std::string manifest_file = GetTestPath("AndroidManifest.xml"); |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 142 | WriteFile(manifest_file, android::base::StringPrintf(R"( |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 143 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
Ryan Mitchell | a55dc2e | 2019-01-24 10:58:23 -0800 | [diff] [blame] | 144 | package="%s"> |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 145 | </manifest>)", package_name)); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 146 | return manifest_file; |
| 147 | } |
| 148 | |
Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 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, |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 158 | android::ResXMLTree *out_tree) { |
| 159 | ASSERT_THAT(apk, Ne(nullptr)); |
| 160 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 | |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 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 | |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 239 | } // namespace aapt |