| /* | |
| * 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 "Convert.h" | |
| #include "LoadedApk.h" | |
| #include "test/Common.h" | |
| #include "test/Test.h" | |
| #include "ziparchive/zip_archive.h" | |
| using testing::AnyOfArray; | |
| using testing::Eq; | |
| using testing::Ne; | |
| using testing::Not; | |
| using testing::SizeIs; | |
| namespace aapt { | |
| using namespace aapt::test; | |
| using ConvertTest = CommandTestFixture; | |
| TEST_F(ConvertTest, RemoveRawXmlStrings) { | |
| StdErrDiagnostics diag; | |
| const std::string compiled_files_dir = GetTestPath("compiled"); | |
| ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)", | |
| compiled_files_dir, &diag)); | |
| const std::string out_apk = GetTestPath("out.apk"); | |
| std::vector<std::string> link_args = { | |
| "--manifest", GetDefaultManifest(), | |
| "-o", out_apk, | |
| "--keep-raw-values", | |
| "--proto-format" | |
| }; | |
| ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | |
| const std::string out_convert_apk = GetTestPath("out_convert.apk"); | |
| std::vector<android::StringPiece> convert_args = { | |
| "-o", out_convert_apk, | |
| "--output-format", "binary", | |
| out_apk, | |
| }; | |
| ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0)); | |
| // Load the binary xml tree | |
| android::ResXMLTree tree; | |
| std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag); | |
| std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml"); | |
| ASSERT_THAT(data, Ne(nullptr)); | |
| AssertLoadXml(apk.get(), data.get(), &tree); | |
| // Check that the raw string index has not been assigned | |
| EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1)); | |
| } | |
| TEST_F(ConvertTest, KeepRawXmlStrings) { | |
| StdErrDiagnostics diag; | |
| const std::string compiled_files_dir = GetTestPath("compiled"); | |
| ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)", | |
| compiled_files_dir, &diag)); | |
| const std::string out_apk = GetTestPath("out.apk"); | |
| std::vector<std::string> link_args = { | |
| "--manifest", GetDefaultManifest(), | |
| "-o", out_apk, | |
| "--keep-raw-values", | |
| "--proto-format" | |
| }; | |
| ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | |
| const std::string out_convert_apk = GetTestPath("out_convert.apk"); | |
| std::vector<android::StringPiece> convert_args = { | |
| "-o", out_convert_apk, | |
| "--output-format", "binary", | |
| "--keep-raw-values", | |
| out_apk, | |
| }; | |
| ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0)); | |
| // Load the binary xml tree | |
| android::ResXMLTree tree; | |
| std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag); | |
| std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml"); | |
| ASSERT_THAT(data, Ne(nullptr)); | |
| AssertLoadXml(apk.get(), data.get(), &tree); | |
| // Check that the raw string index has been set to the correct string pool entry | |
| int32_t raw_index = tree.getAttributeValueStringID(0); | |
| ASSERT_THAT(raw_index, Ne(-1)); | |
| EXPECT_THAT(android::util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), | |
| Eq("007")); | |
| } | |
| TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) { | |
| StdErrDiagnostics diag; | |
| const std::string apk_path = | |
| file::BuildPath({android::base::GetExecutableDirectory(), | |
| "integration-tests", "ConvertTest", "duplicate_entries.apk"}); | |
| const std::string out_convert_apk = GetTestPath("out_convert.apk"); | |
| std::vector<android::StringPiece> convert_args = { | |
| "-o", out_convert_apk, | |
| "--output-format", "proto", | |
| apk_path | |
| }; | |
| ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0)); | |
| ZipArchiveHandle handle; | |
| ASSERT_THAT(OpenArchive(out_convert_apk.c_str(), &handle), Eq(0)); | |
| void* cookie = nullptr; | |
| int32_t result = StartIteration(handle, &cookie, "res/theme/10", ""); | |
| // If this is -5, that means we've found a duplicate entry and this test has failed | |
| EXPECT_THAT(result, Eq(0)); | |
| // But if read succeeds, verify only one res/theme/10 entry | |
| int count = 0; | |
| // Can't pass nullptrs into Next() | |
| std::string zip_name; | |
| ZipEntry zip_data; | |
| while ((result = Next(cookie, &zip_data, &zip_name)) == 0) { | |
| count++; | |
| } | |
| EndIteration(cookie); | |
| EXPECT_THAT(count, Eq(1)); | |
| } | |
| TEST_F(ConvertTest, ConvertWithResourceNameCollapsing) { | |
| StdErrDiagnostics diag; | |
| const std::string compiled_files_dir = GetTestPath("compiled"); | |
| ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), | |
| R"(<resources> | |
| <string name="first">string</string> | |
| <string name="second">string</string> | |
| <string name="third">another string</string> | |
| <bool name="bool1">true</bool> | |
| <bool name="bool2">true</bool> | |
| <bool name="bool3">true</bool> | |
| <integer name="int1">10</integer> | |
| <integer name="int2">10</integer> | |
| </resources>)", | |
| compiled_files_dir, &diag)); | |
| std::string resource_config_path = GetTestPath("resource-config"); | |
| WriteFile(resource_config_path, "integer/int1#no_collapse\ninteger/int2#no_collapse"); | |
| const std::string proto_apk = GetTestPath("proto.apk"); | |
| std::vector<std::string> link_args = { | |
| "--proto-format", "--manifest", GetDefaultManifest(kDefaultPackageName), "-o", proto_apk, | |
| }; | |
| ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | |
| const std::string binary_apk = GetTestPath("binary.apk"); | |
| std::vector<android::StringPiece> convert_args = {"-o", | |
| binary_apk, | |
| "--output-format", | |
| "binary", | |
| "--collapse-resource-names", | |
| "--deduplicate-entry-values", | |
| "--resources-config-path", | |
| resource_config_path, | |
| proto_apk}; | |
| ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0)); | |
| std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(binary_apk, &diag); | |
| for (const auto& package : apk->GetResourceTable()->packages) { | |
| for (const auto& type : package->types) { | |
| switch (type->named_type.type) { | |
| case ResourceType::kBool: | |
| EXPECT_THAT(type->entries, SizeIs(3)); | |
| for (const auto& entry : type->entries) { | |
| auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value; | |
| EXPECT_THAT(value.data, Eq(0xffffffffu)); | |
| } | |
| break; | |
| case ResourceType::kString: | |
| EXPECT_THAT(type->entries, SizeIs(3)); | |
| for (const auto& entry : type->entries) { | |
| auto value = ValueCast<String>(entry->FindValue({})->value.get())->value; | |
| EXPECT_THAT(entry->name, Not(AnyOfArray({"first", "second", "third"}))); | |
| EXPECT_THAT(*value, AnyOfArray({"string", "another string"})); | |
| } | |
| break; | |
| case ResourceType::kInteger: | |
| EXPECT_THAT(type->entries, SizeIs(2)); | |
| for (const auto& entry : type->entries) { | |
| auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value; | |
| EXPECT_THAT(entry->name, AnyOfArray({"int1", "int2"})); | |
| EXPECT_THAT(value.data, Eq(10)); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } // namespace aapt |