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 | |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 19 | #include <android-base/errors.h> |
| 20 | #include <android-base/file.h> |
| 21 | #include <android-base/stringprintf.h> |
| 22 | #include <android-base/utf8.h> |
Jeremy Meyer | b4f83ff | 2023-11-30 19:29:50 +0000 | [diff] [blame] | 23 | #include <androidfw/FileStream.h> |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 24 | #include <androidfw/StringPiece.h> |
Jeremy Meyer | 56f36e8 | 2022-05-20 20:35:42 +0000 | [diff] [blame] | 25 | #include <dirent.h> |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 26 | #include <gmock/gmock.h> |
| 27 | #include <gtest/gtest.h> |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 28 | |
Jeremy Meyer | 56f36e8 | 2022-05-20 20:35:42 +0000 | [diff] [blame] | 29 | #include "Diagnostics.h" |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 30 | #include "cmd/Compile.h" |
| 31 | #include "cmd/Link.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 | |
Yurii Zubrytskyi | a577514 | 2022-11-02 17:49:49 -0700 | [diff] [blame] | 41 | void ClearDirectory(android::StringPiece path) { |
| 42 | const std::string root_dir(path); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 43 | std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir); |
| 44 | if (!dir) { |
Jeremy Meyer | 56f36e8 | 2022-05-20 20:35:42 +0000 | [diff] [blame] | 45 | StdErrDiagnostics().Error(android::DiagMessage() |
| 46 | << android::base::SystemErrorCodeToString(errno)); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 | |
| 70 | void TestDirectoryFixture::SetUp() { |
weisu | 90d3fb6 | 2021-12-12 22:24:52 +0000 | [diff] [blame] | 71 | temp_dir_ = file::BuildPath({testing::TempDir(), "_temp", |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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, |
Jeremy Meyer | 3d8d4a1 | 2024-08-23 17:29:03 -0700 | [diff] [blame] | 94 | android::StringPiece out_dir, android::IDiagnostics* diag, |
| 95 | const std::vector<android::StringPiece>& additional_args) { |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 96 | WriteFile(path, contents); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 97 | CHECK(file::mkdirs(out_dir.data())); |
Jeremy Meyer | 3d8d4a1 | 2024-08-23 17:29:03 -0700 | [diff] [blame] | 98 | std::vector<android::StringPiece> args = {path, "-o", out_dir, "-v"}; |
| 99 | args.insert(args.end(), additional_args.begin(), additional_args.end()); |
| 100 | return CompileCommand(diag).Execute(args, &std::cerr) == 0; |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 101 | } |
| 102 | |
Jeremy Meyer | 56f36e8 | 2022-05-20 20:35:42 +0000 | [diff] [blame] | 103 | bool CommandTestFixture::Link(const std::vector<std::string>& args, android::IDiagnostics* diag) { |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 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 |
Josh Hou | ec67cb4 | 2022-06-09 19:19:21 +0800 | [diff] [blame] | 110 | std::string android_sdk = |
| 111 | file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests", |
| 112 | "android-33.jar"}); |
Ryan Mitchell | 81dfca0 | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 113 | link_args.insert(link_args.end(), {"-I", android_sdk}); |
| 114 | |
| 115 | return LinkCommand(diag).Execute(link_args, &std::cerr) == 0; |
| 116 | } |
| 117 | |
Yurii Zubrytskyi | a577514 | 2022-11-02 17:49:49 -0700 | [diff] [blame] | 118 | bool CommandTestFixture::Link(const std::vector<std::string>& args, android::StringPiece flat_dir, |
| 119 | android::IDiagnostics* diag) { |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 |
Josh Hou | ec67cb4 | 2022-06-09 19:19:21 +0800 | [diff] [blame] | 126 | std::string android_sdk = |
| 127 | file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests", |
| 128 | "android-33.jar"}); |
Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 |
Ryan Mitchell | 4382e44 | 2021-07-14 12:53:01 -0700 | [diff] [blame] | 132 | 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] | 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, |
Yurii Zubrytskyi | a577514 | 2022-11-02 17:49:49 -0700 | [diff] [blame] | 153 | android::StringPiece path) { |
Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 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, |
Jeremy Meyer | 56f36e8 | 2022-05-20 20:35:42 +0000 | [diff] [blame] | 217 | android::IDiagnostics* diag) { |
Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 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 | |
weisu | 90d3fb6 | 2021-12-12 22:24:52 +0000 | [diff] [blame] | 242 | } // namespace aapt |