You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
8.0 KiB

/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "test/Fixture.h"
#include <dirent.h>
#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/utf8.h>
#include <androidfw/StringPiece.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cmd/Compile.h"
#include "cmd/Link.h"
#include "io/FileStream.h"
#include "util/Files.h"
using testing::Eq;
using testing::Ne;
namespace aapt {
const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test";
void ClearDirectory(const android::StringPiece& path) {
const std::string root_dir = path.to_string();
std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
if (!dir) {
StdErrDiagnostics().Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
return;
}
while (struct dirent* entry = readdir(dir.get())) {
// Do not delete hidden files and do not recurse to the parent of this directory
if (util::StartsWith(entry->d_name, ".")) {
continue;
}
std::string full_path = file::BuildPath({root_dir, entry->d_name});
if (file::GetFileType(full_path) == file::FileType::kDirectory) {
ClearDirectory(full_path);
#ifdef _WIN32
_rmdir(full_path.c_str());
#else
rmdir(full_path.c_str());
#endif
} else {
android::base::utf8::unlink(full_path.c_str());
}
}
}
void TestDirectoryFixture::SetUp() {
temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
"_temp",
testing::UnitTest::GetInstance()->current_test_case()->name(),
testing::UnitTest::GetInstance()->current_test_info()->name()});
ASSERT_TRUE(file::mkdirs(temp_dir_));
ClearDirectory(temp_dir_);
}
void TestDirectoryFixture::TearDown() {
ClearDirectory(temp_dir_);
}
void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
// Create any intermediate directories specified in the path
auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
if (pos != path.rend()) {
std::string dirs = path.substr(0, (&*pos - path.data()));
file::mkdirs(dirs);
}
CHECK(android::base::WriteStringToFile(contents, path));
}
bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
const android::StringPiece& out_dir, IDiagnostics* diag) {
WriteFile(path, contents);
CHECK(file::mkdirs(out_dir.data()));
return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
}
bool CommandTestFixture::Link(const std::vector<std::string>& args, IDiagnostics* diag) {
std::vector<android::StringPiece> link_args;
for(const std::string& arg : args) {
link_args.emplace_back(arg);
}
// Link against the android SDK
std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
"integration-tests", "CommandTests",
"android-28.jar"});
link_args.insert(link_args.end(), {"-I", android_sdk});
return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
}
bool CommandTestFixture::Link(const std::vector<std::string>& args,
const android::StringPiece& flat_dir, IDiagnostics* diag) {
std::vector<android::StringPiece> link_args;
for(const std::string& arg : args) {
link_args.emplace_back(arg);
}
// Link against the android SDK
std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
"integration-tests", "CommandTests",
"android-28.jar"});
link_args.insert(link_args.end(), {"-I", android_sdk});
// Add the files from the compiled resources directory to the link file arguments
Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
if (compiled_files) {
for (std::string& compile_file : compiled_files.value()) {
compile_file = file::BuildPath({flat_dir, compile_file});
link_args.emplace_back(std::move(compile_file));
}
}
return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
}
std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
const std::string manifest_file = GetTestPath("AndroidManifest.xml");
WriteFile(manifest_file, android::base::StringPrintf(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="%s">
</manifest>)", package_name));
return manifest_file;
}
std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk,
const android::StringPiece& path) {
return apk
->GetFileCollection()
->FindFile(path)
->OpenAsData();
}
void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
android::ResXMLTree *out_tree) {
ASSERT_THAT(apk, Ne(nullptr));
out_tree->setTo(data->data(), data->size());
ASSERT_THAT(out_tree->getError(), Eq(android::OK));
while (out_tree->next() != android::ResXMLTree::START_TAG) {
ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
}
}
ManifestBuilder::ManifestBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
}
ManifestBuilder& ManifestBuilder::SetPackageName(const std::string& package_name) {
package_name_ = package_name;
return *this;
}
ManifestBuilder& ManifestBuilder::AddContents(const std::string& contents) {
contents_ += contents + "\n";
return *this;
}
std::string ManifestBuilder::Build(const std::string& file_path) {
const char* manifest_template = R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="%s">
%s
</manifest>)";
fixture_->WriteFile(file_path, android::base::StringPrintf(
manifest_template, package_name_.c_str(), contents_.c_str()));
return file_path;
}
std::string ManifestBuilder::Build() {
return Build(fixture_->GetTestPath("AndroidManifest.xml"));
}
LinkCommandBuilder::LinkCommandBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
}
LinkCommandBuilder& LinkCommandBuilder::SetManifestFile(const std::string& file) {
manifest_supplied_ = true;
args_.emplace_back("--manifest");
args_.emplace_back(file);
return *this;
}
LinkCommandBuilder& LinkCommandBuilder::AddFlag(const std::string& flag) {
args_.emplace_back(flag);
return *this;
}
LinkCommandBuilder& LinkCommandBuilder::AddCompiledResDir(const std::string& dir,
IDiagnostics* diag) {
if (auto files = file::FindFiles(dir, diag)) {
for (std::string& compile_file : files.value()) {
args_.emplace_back(file::BuildPath({dir, compile_file}));
}
}
return *this;
}
LinkCommandBuilder& LinkCommandBuilder::AddParameter(const std::string& param,
const std::string& value) {
args_.emplace_back(param);
args_.emplace_back(value);
return *this;
}
std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) {
if (!manifest_supplied_) {
SetManifestFile(ManifestBuilder(fixture_).Build());
}
args_.emplace_back("-o");
args_.emplace_back(out_apk);
return args_;
}
} // namespace aapt