Files
frameworks_base/tools/aapt2/test/Fixture.cpp
Ryan Mitchell 5855de7ed6 Use R field directly for shared lib styleable attrs
If a shared library exposes an attribute and a client uses the
attribute in its own styleable, the value of the lib attribute
resource id in the client styleable must be fixed with the correct
package id at runtime. Since the client will not have an
onResourcesLoaded to call, the client should directly reference the
attribute resource field in its styleable.

Bug: 147674078
Test: aapt2_tests
Change-Id: I8e64bb2d3165a7072e2604fe1730b248545978f4
2021-02-24 15:57:34 -08:00

242 lines
8.1 KiB
C++

/*
* 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) {
CHECK(util::StartsWith(path, temp_dir_))
<< "Attempting to create a file outside of test temporary directory.";
// 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