idmap2: initial code drop
idmap2 is a reboot of the idmap project. The project aims to
- use modern C++
- greatly improve test and debug support
- interface towards AssetManager2 (instead of AssetManager)
- provide a solid foundation to add support for new features
To make it easier to verify correctness, this first version of idmap2 is
feature equivalent to idmap. Later versions will add support for new
features such as <overlayable>.
Bug: 78815803
Test: make idmap2_tests
Change-Id: I1d806dc875a493e730ab55d2fdb027618e586d16
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
new file mode 100644
index 0000000..8b552dc
--- /dev/null
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream raw_stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap1 = Idmap::FromBinaryStream(raw_stream, error);
+ ASSERT_THAT(idmap1, NotNull());
+
+ std::stringstream stream;
+ BinaryStreamVisitor visitor(stream);
+ idmap1->accept(&visitor);
+
+ std::unique_ptr<const Idmap> idmap2 = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap2, NotNull());
+
+ ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
+ ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
+ ASSERT_EQ(idmap1->GetData().size(), 1u);
+ ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
+
+ const auto& data1 = idmap1->GetData()[0];
+ const auto& data2 = idmap2->GetData()[0];
+
+ ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
+ ASSERT_EQ(data1->GetTypeEntries().size(), 2u);
+ ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
+ ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
+ ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
+ ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(2), data2->GetTypeEntries()[0]->GetEntry(2));
+ ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(0), data2->GetTypeEntries()[1]->GetEntry(0));
+ ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(1), data2->GetTypeEntries()[1]->GetEntry(1));
+ ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(2), data2->GetTypeEntries()[1]->GetEntry(2));
+}
+
+TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ BinaryStreamVisitor visitor(stream);
+ idmap->accept(&visitor);
+ const std::string str = stream.str();
+ const StringPiece data(str);
+ std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(data);
+ ASSERT_THAT(loaded_idmap, NotNull());
+ ASSERT_EQ(loaded_idmap->TargetPackageId(), 0x7f);
+
+ const IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01);
+ ASSERT_THAT(header, NotNull());
+
+ EntryId entry;
+ bool success = LoadedIdmap::Lookup(header, 0x0000, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0000);
+
+ header = loaded_idmap->GetEntryMapForType(0x02);
+ ASSERT_THAT(header, NotNull());
+
+ success = LoadedIdmap::Lookup(header, 0x0002, &entry);
+ ASSERT_FALSE(success);
+
+ success = LoadedIdmap::Lookup(header, 0x0003, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0000);
+
+ success = LoadedIdmap::Lookup(header, 0x0004, &entry);
+ ASSERT_FALSE(success);
+
+ success = LoadedIdmap::Lookup(header, 0x0005, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0001);
+
+ success = LoadedIdmap::Lookup(header, 0x0006, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0002);
+
+ success = LoadedIdmap::Lookup(header, 0x0007, &entry);
+ ASSERT_FALSE(success);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
new file mode 100644
index 0000000..b04b256
--- /dev/null
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/file.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(CommandLineOptionsTests, Flag) {
+ bool foo = true, bar = false;
+
+ CommandLineOptions opts =
+ CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar);
+
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "--bar"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(foo);
+ ASSERT_TRUE(bar);
+
+ foo = bar = false;
+ success = opts.Parse({"--foo"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(foo);
+ ASSERT_FALSE(bar);
+}
+
+TEST(CommandLineOptionsTests, MandatoryOption) {
+ std::string foo, bar;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .MandatoryOption("--foo", "", &foo)
+ .MandatoryOption("--bar", "", &bar);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "FOO");
+ ASSERT_EQ(bar, "BAR");
+
+ success = opts.Parse({"--foo"}, fakeStdErr);
+ ASSERT_FALSE(success);
+}
+
+TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsButExpectedOnce) {
+ std::string foo;
+ CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &foo);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FIRST", "--foo", "SECOND"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "SECOND");
+}
+
+TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsAndExpectedOnceOrMore) {
+ std::vector<std::string> args;
+ CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &args);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(args.size(), 2u);
+ ASSERT_EQ(args[0], "FOO");
+ ASSERT_EQ(args[1], "BAR");
+}
+
+TEST(CommandLineOptionsTests, OptionalOption) {
+ std::string foo, bar;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .OptionalOption("--foo", "", &foo)
+ .OptionalOption("--bar", "", &bar);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "FOO");
+ ASSERT_EQ(bar, "BAR");
+
+ success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "BAZ");
+
+ success = opts.Parse({"--foo"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
+ ASSERT_FALSE(success);
+}
+
+TEST(CommandLineOptionsTests, CornerCases) {
+ std::string foo, bar;
+ bool baz = false;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .MandatoryOption("--foo", "", &foo)
+ .OptionalFlag("--baz", "", &baz)
+ .OptionalOption("--bar", "", &bar);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--unexpected"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--bar", "BAR"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--baz", "--foo", "FOO"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(baz);
+ ASSERT_EQ(foo, "FOO");
+}
+
+TEST(CommandLineOptionsTests, ConvertArgvToVector) {
+ const char* argv[] = {
+ "program-name",
+ "--foo",
+ "FOO",
+ nullptr,
+ };
+ std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(3, argv);
+ ASSERT_EQ(v->size(), 2ul);
+ ASSERT_EQ((*v)[0], "--foo");
+ ASSERT_EQ((*v)[1], "FOO");
+}
+
+TEST(CommandLineOptionsTests, ConvertArgvToVectorNoArgs) {
+ const char* argv[] = {
+ "program-name",
+ nullptr,
+ };
+ std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(1, argv);
+ ASSERT_EQ(v->size(), 0ul);
+}
+
+TEST(CommandLineOptionsTests, Usage) {
+ std::string arg1, arg2, arg3, arg4;
+ bool arg5 = false, arg6 = false;
+ std::vector<std::string> arg7;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .MandatoryOption("--aa", "description-aa", &arg1)
+ .OptionalFlag("--bb", "description-bb", &arg5)
+ .OptionalOption("--cc", "description-cc", &arg2)
+ .OptionalOption("--dd", "description-dd", &arg3)
+ .MandatoryOption("--ee", "description-ee", &arg4)
+ .OptionalFlag("--ff", "description-ff", &arg6)
+ .MandatoryOption("--gg", "description-gg", &arg7);
+ std::stringstream stream;
+ opts.Usage(stream);
+ const std::string s = stream.str();
+ ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg "
+ "[--gg arg [..]]"),
+ std::string::npos);
+ ASSERT_NE(s.find("--aa arg description-aa"), std::string::npos);
+ ASSERT_NE(s.find("--ff description-ff"), std::string::npos);
+ ASSERT_NE(s.find("--gg arg description-gg (can be provided multiple times)"),
+ std::string::npos);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
new file mode 100644
index 0000000..0c6439a
--- /dev/null
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 <dirent.h>
+#include <set>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/macros.h"
+
+#include "idmap2/FileUtils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
+ const auto& root = GetTestDataPath();
+ auto v = utils::FindFiles(root, false,
+ [](unsigned char type ATTRIBUTE_UNUSED,
+ const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
+ ASSERT_THAT(v, NotNull());
+ ASSERT_EQ(v->size(), 4u);
+ ASSERT_EQ(
+ std::set<std::string>(v->begin(), v->end()),
+ std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
+}
+
+TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
+ const auto& root = GetTestDataPath();
+ auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
+ return type == DT_REG && path.size() > 4 && !path.compare(path.size() - 4, 4, ".apk");
+ });
+ ASSERT_THAT(v, NotNull());
+ ASSERT_EQ(v->size(), 4u);
+ ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
+ std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
+ root + "/overlay/overlay-static-1.apk",
+ root + "/overlay/overlay-static-2.apk"}));
+}
+
+TEST(FileUtilsTests, ReadFile) {
+ int pipefd[2];
+ ASSERT_EQ(pipe(pipefd), 0);
+
+ ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
+ close(pipefd[1]);
+
+ auto data = ReadFile(pipefd[0]);
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(*data, "foobar");
+ close(pipefd[0]);
+}
+
+} // namespace utils
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
new file mode 100644
index 0000000..5c4e857
--- /dev/null
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+/*
+ * The tests in this file operate on a higher level than the tests in the other
+ * files. Here, all tests execute the idmap2 binary and only depend on
+ * libidmap2 to verify the output of idmap2.
+ */
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring> // strerror
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/PosixUtils.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::android::util::ExecuteBinary;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+class Idmap2BinaryTests : public Idmap2Tests {};
+
+static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
+ const std::string& overlay_apk_path) {
+ // check that the idmap file looks reasonable (IdmapTests is responsible for
+ // more in-depth verification)
+ ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
+ ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
+ ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
+ ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
+ ASSERT_EQ(idmap.GetData().size(), 1u);
+}
+
+#define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path) \
+ do { \
+ ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
+ } while (0)
+
+TEST_F(Idmap2BinaryTests, Create) {
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+ struct stat st;
+ ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
+
+ std::stringstream error;
+ std::ifstream fin(GetIdmapPath());
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(fin, error);
+ fin.close();
+
+ ASSERT_THAT(idmap, NotNull());
+ ASSERT_IDMAP(*idmap, GetTargetApkPath(), GetOverlayApkPath());
+
+ unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, Dump) {
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "dump",
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
+ ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos);
+ ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos);
+ ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "dump",
+ "--verbose",
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("00000000: 504d4449 magic"), std::string::npos);
+ ASSERT_NE(result->stdout.find("00000210: 007f target package id"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "dump",
+ "--verbose",
+ "--idmap-path", GetTestDataPath() + "/DOES-NOT-EXIST"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+
+ unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, Scan) {
+ const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
+ const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
+ const std::string idmap_static_1_path =
+ Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
+ const std::string idmap_static_2_path =
+ Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
+
+ // single input directory, recursive
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTestDataPath(),
+ "--recursive",
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ std::stringstream expected;
+ expected << idmap_static_1_path << std::endl;
+ expected << idmap_static_2_path << std::endl;
+ ASSERT_EQ(result->stdout, expected.str());
+
+ std::stringstream error;
+ auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
+ auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
+ auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream, error);
+ ASSERT_THAT(idmap_static_1, NotNull());
+ ASSERT_IDMAP(*idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
+
+ auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
+ auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
+ auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream, error);
+ ASSERT_THAT(idmap_static_2, NotNull());
+ ASSERT_IDMAP(*idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
+
+ unlink(idmap_static_2_path.c_str());
+ unlink(idmap_static_1_path.c_str());
+
+ // multiple input directories, non-recursive
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTestDataPath() + "/target",
+ "--input-directory", GetTestDataPath() + "/overlay",
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->stdout, expected.str());
+ unlink(idmap_static_2_path.c_str());
+ unlink(idmap_static_1_path.c_str());
+
+ // the same input directory given twice, but no duplicate entries
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTestDataPath(),
+ "--input-directory", GetTestDataPath(),
+ "--recursive",
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->stdout, expected.str());
+ unlink(idmap_static_2_path.c_str());
+ unlink(idmap_static_1_path.c_str());
+
+ // no APKs in input-directory: ok, but no output
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTempDirPath(),
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->stdout, "");
+}
+
+TEST_F(Idmap2BinaryTests, Lookup) {
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "lookup",
+ "--idmap-path", GetIdmapPath(),
+ "--config", "",
+ "--resid", "0x7f020003"}); // string/str1
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
+ ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "lookup",
+ "--idmap-path", GetIdmapPath(),
+ "--config", "",
+ "--resid", "test.target:string/str1"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
+ ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "lookup",
+ "--idmap-path", GetIdmapPath(),
+ "--config", "sv",
+ "--resid", "test.target:string/str1"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+ unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
+ const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
+
+ // missing mandatory options
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+
+ // missing argument to option
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+
+ // invalid target apk path
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", invalid_target_apk_path,
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
new file mode 100644
index 0000000..0379aa4
--- /dev/null
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -0,0 +1,395 @@
+/*
+ * 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 <cstdio> // fclose
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/macros.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(IdmapTests, TestCanonicalIdmapPathFor) {
+ ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
+ "/foo/vendor@overlay@bar.apk@idmap");
+}
+
+TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream stream(raw);
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ ASSERT_THAT(header, NotNull());
+ ASSERT_EQ(header->GetMagic(), 0x504d4449u);
+ ASSERT_EQ(header->GetVersion(), 0x01u);
+ ASSERT_EQ(header->GetTargetCrc(), 0x1234u);
+ ASSERT_EQ(header->GetOverlayCrc(), 0x5678u);
+ ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
+ ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
+}
+
+TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ // overwrite the target path string, including the terminating null, with '.'
+ for (size_t i = 0x10; i < 0x110; i++) {
+ raw[i] = '.';
+ }
+ std::istringstream stream(raw);
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ ASSERT_THAT(header, IsNull());
+}
+
+TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
+ const size_t offset = 0x210;
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+ idmap_raw_data_len - offset);
+ std::istringstream stream(raw);
+
+ std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
+ ASSERT_THAT(header, NotNull());
+ ASSERT_EQ(header->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(header->GetTypeCount(), 2u);
+}
+
+TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
+ const size_t offset = 0x214;
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+ idmap_raw_data_len - offset);
+ std::istringstream stream(raw);
+
+ std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(data->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(data->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(data->GetEntryCount(), 1u);
+ ASSERT_EQ(data->GetEntryOffset(), 0u);
+ ASSERT_EQ(data->GetEntry(0), 0u);
+}
+
+TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
+ const size_t offset = 0x210;
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+ idmap_raw_data_len - offset);
+ std::istringstream stream(raw);
+
+ std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+ ASSERT_EQ(types.size(), 2u);
+
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetEntryCount(), 3u);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+}
+
+TEST(IdmapTests, CreateIdmapFromBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ ASSERT_THAT(idmap->GetHeader(), NotNull());
+ ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234u);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678u);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
+
+ const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+ ASSERT_EQ(dataBlocks.size(), 1u);
+
+ const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+ ASSERT_EQ(types.size(), 2u);
+
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetEntryCount(), 3u);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+}
+
+TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data),
+ 10); // data too small
+ std::istringstream stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap, IsNull());
+}
+
+TEST(IdmapTests, CreateIdmapFromApkAssets) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ ASSERT_THAT(idmap->GetHeader(), NotNull());
+ ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
+
+ const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+ ASSERT_EQ(dataBlocks.size(), 1u);
+
+ const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+
+ const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+ ASSERT_EQ(types.size(), 2u);
+
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01u);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01u);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(types[1]->GetEntryCount(), 4u);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+ ASSERT_EQ(types[1]->GetEntry(3), 0x0002u);
+}
+
+TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
+ std::string target_apk_path(GetTestDataPath());
+ for (int i = 0; i < 32; i++) {
+ target_apk_path += "/target/../";
+ }
+ target_apk_path += "/target/target.apk";
+ ASSERT_GT(target_apk_path.size(), kIdmapStringLength);
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, IsNull());
+}
+
+TEST(IdmapTests, IdmapHeaderIsUpToDate) {
+ fclose(stderr); // silence expected warnings from libandroidfw
+
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ BinaryStreamVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ ASSERT_THAT(header, NotNull());
+ ASSERT_TRUE(header->IsUpToDate(error)) << error.str();
+
+ // magic: bytes (0x0, 0x03)
+ std::string bad_magic_string(stream.str());
+ bad_magic_string[0x0] = '.';
+ bad_magic_string[0x1] = '.';
+ bad_magic_string[0x2] = '.';
+ bad_magic_string[0x3] = '.';
+ std::stringstream bad_magic_stream(bad_magic_string);
+ std::unique_ptr<const IdmapHeader> bad_magic_header =
+ IdmapHeader::FromBinaryStream(bad_magic_stream);
+ ASSERT_THAT(bad_magic_header, NotNull());
+ ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(error));
+
+ // version: bytes (0x4, 0x07)
+ std::string bad_version_string(stream.str());
+ bad_version_string[0x4] = '.';
+ bad_version_string[0x5] = '.';
+ bad_version_string[0x6] = '.';
+ bad_version_string[0x7] = '.';
+ std::stringstream bad_version_stream(bad_version_string);
+ std::unique_ptr<const IdmapHeader> bad_version_header =
+ IdmapHeader::FromBinaryStream(bad_version_stream);
+ ASSERT_THAT(bad_version_header, NotNull());
+ ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
+ ASSERT_FALSE(bad_version_header->IsUpToDate(error));
+
+ // target crc: bytes (0x8, 0xb)
+ std::string bad_target_crc_string(stream.str());
+ bad_target_crc_string[0x8] = '.';
+ bad_target_crc_string[0x9] = '.';
+ bad_target_crc_string[0xa] = '.';
+ bad_target_crc_string[0xb] = '.';
+ std::stringstream bad_target_crc_stream(bad_target_crc_string);
+ std::unique_ptr<const IdmapHeader> bad_target_crc_header =
+ IdmapHeader::FromBinaryStream(bad_target_crc_stream);
+ ASSERT_THAT(bad_target_crc_header, NotNull());
+ ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
+ ASSERT_FALSE(bad_target_crc_header->IsUpToDate(error));
+
+ // overlay crc: bytes (0xc, 0xf)
+ std::string bad_overlay_crc_string(stream.str());
+ bad_overlay_crc_string[0xc] = '.';
+ bad_overlay_crc_string[0xd] = '.';
+ bad_overlay_crc_string[0xe] = '.';
+ bad_overlay_crc_string[0xf] = '.';
+ std::stringstream bad_overlay_crc_stream(bad_overlay_crc_string);
+ std::unique_ptr<const IdmapHeader> bad_overlay_crc_header =
+ IdmapHeader::FromBinaryStream(bad_overlay_crc_stream);
+ ASSERT_THAT(bad_overlay_crc_header, NotNull());
+ ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
+ ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(error));
+
+ // target path: bytes (0x10, 0x10f)
+ std::string bad_target_path_string(stream.str());
+ bad_target_path_string[0x10] = '\0';
+ std::stringstream bad_target_path_stream(bad_target_path_string);
+ std::unique_ptr<const IdmapHeader> bad_target_path_header =
+ IdmapHeader::FromBinaryStream(bad_target_path_stream);
+ ASSERT_THAT(bad_target_path_header, NotNull());
+ ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
+ ASSERT_FALSE(bad_target_path_header->IsUpToDate(error));
+
+ // overlay path: bytes (0x110, 0x20f)
+ std::string bad_overlay_path_string(stream.str());
+ bad_overlay_path_string[0x110] = '\0';
+ std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
+ std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
+ IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
+ ASSERT_THAT(bad_overlay_path_header, NotNull());
+ ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath());
+ ASSERT_FALSE(bad_overlay_path_header->IsUpToDate(error));
+}
+
+class TestVisitor : public Visitor {
+ public:
+ explicit TestVisitor(std::ostream& stream) : stream_(stream) {
+ }
+
+ void visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(Idmap)" << std::endl;
+ }
+
+ void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapHeader)" << std::endl;
+ }
+
+ void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapData)" << std::endl;
+ }
+
+ void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
+ }
+
+ void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
+ }
+
+ private:
+ std::ostream& stream_;
+};
+
+TEST(IdmapTests, TestVisitor) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream test_stream;
+ TestVisitor visitor(test_stream);
+ idmap->accept(&visitor);
+
+ ASSERT_EQ(test_stream.str(),
+ "TestVisitor::visit(Idmap)\n"
+ "TestVisitor::visit(IdmapHeader)\n"
+ "TestVisitor::visit(IdmapData)\n"
+ "TestVisitor::visit(IdmapData::Header)\n"
+ "TestVisitor::visit(IdmapData::TypeEntry)\n"
+ "TestVisitor::visit(IdmapData::TypeEntry)\n");
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp
new file mode 100644
index 0000000..f2469ea
--- /dev/null
+++ b/cmds/idmap2/tests/Main.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <string>
+
+#include "android-base/file.h"
+
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+namespace android {
+namespace idmap2 {
+
+const std::string GetTestDataPath() {
+ return base::GetExecutableDirectory() + "/tests/data";
+}
+
+} // namespace idmap2
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
new file mode 100644
index 0000000..da97792
--- /dev/null
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/PrettyPrintVisitor.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+using android::ApkAssets;
+
+namespace android {
+namespace idmap2 {
+
+TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ PrettyPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+}
+
+TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
+ fclose(stderr); // silence expected warnings from libandroidfw
+
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream raw_stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(raw_stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ PrettyPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
new file mode 100644
index 0000000..c28ce2e
--- /dev/null
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 <cstdio> // fclose
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/RawPrintVisitor.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ RawPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000008: f5ad1d1d target crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: d470336b overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
+ std::string::npos);
+}
+
+TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
+ fclose(stderr); // silence expected warnings from libandroidfw
+
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream raw_stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(raw_stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ RawPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000008: 00001234 target crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f020000 -> 0x7f020000\n"), std::string::npos);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
new file mode 100644
index 0000000..0547fa0
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/ResourceUtils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+class ResourceUtilsTests : public Idmap2Tests {
+ protected:
+ void SetUp() override {
+ Idmap2Tests::SetUp();
+
+ apk_assets_ = ApkAssets::Load(GetTargetApkPath());
+ ASSERT_THAT(apk_assets_, NotNull());
+
+ am_.SetApkAssets({apk_assets_.get()});
+ }
+
+ const AssetManager2& GetAssetManager() {
+ return am_;
+ }
+
+ private:
+ AssetManager2 am_;
+ std::unique_ptr<const ApkAssets> apk_assets_;
+};
+
+TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
+ bool lookup_ok;
+ std::string name;
+ std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+ ASSERT_TRUE(lookup_ok);
+ ASSERT_EQ(name, "integer/int1");
+}
+
+TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
+ bool lookup_ok;
+ std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+ ASSERT_FALSE(lookup_ok);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
new file mode 100644
index 0000000..18dc541
--- /dev/null
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_TESTS_TESTHELPERS_H_
+#define IDMAP2_TESTS_TESTHELPERS_H_
+
+#include <string>
+
+namespace android {
+namespace idmap2 {
+
+const unsigned char idmap_raw_data[] = {
+ // IDMAP HEADER
+ // 0x0: magic
+ 0x49, 0x44, 0x4d, 0x50,
+
+ // 0x4: version
+ 0x01, 0x00, 0x00, 0x00,
+
+ // 0x8: target crc
+ 0x34, 0x12, 0x00, 0x00,
+
+ // 0xc: overlay crc
+ 0x78, 0x56, 0x00, 0x00,
+
+ // 0x10: target path "target.apk"
+ 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ // 0x110: overlay path "overlay.apk"
+ 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ // DATA HEADER
+ // 0x210: target package id
+ 0x7f, 0x00,
+
+ // 0x212: types count
+ 0x02, 0x00,
+
+ // DATA BLOCK
+ // 0x214: target type
+ 0x02, 0x00,
+
+ // 0x216: overlay type
+ 0x02, 0x00,
+
+ // 0x218: entry count
+ 0x01, 0x00,
+
+ // 0x21a: entry offset
+ 0x00, 0x00,
+
+ // 0x21c: entries
+ 0x00, 0x00, 0x00, 0x00,
+
+ // DATA BLOCK
+ // 0x220: target type
+ 0x03, 0x00,
+
+ // 0x222: overlay type
+ 0x03, 0x00,
+
+ // 0x224: entry count
+ 0x03, 0x00,
+
+ // 0x226: entry offset
+ 0x03, 0x00,
+
+ // 0x228, 0x22c, 0x230: entries
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0xff, 0xff, 0xff, 0xff,
+
+ 0x01, 0x00, 0x00, 0x00};
+
+const unsigned int idmap_raw_data_len = 565;
+
+const std::string GetTestDataPath();
+
+class Idmap2Tests : public testing::Test {
+ protected:
+ virtual void SetUp() {
+#ifdef __ANDROID__
+ tmp_dir_path_ = "/data/local/tmp/idmap2-tests-XXXXXX";
+#else
+ tmp_dir_path_ = "/tmp/idmap2-tests-XXXXXX";
+#endif
+ EXPECT_NE(mkdtemp(const_cast<char*>(tmp_dir_path_.c_str())), nullptr)
+ << "Failed to create temporary directory: " << strerror(errno);
+ target_apk_path_ = GetTestDataPath() + "/target/target.apk";
+ overlay_apk_path_ = GetTestDataPath() + "/overlay/overlay.apk";
+ idmap_path_ = tmp_dir_path_ + "/a.idmap";
+ }
+
+ virtual void TearDown() {
+ EXPECT_EQ(rmdir(tmp_dir_path_.c_str()), 0)
+ << "Failed to remove temporary directory " << tmp_dir_path_ << ": " << strerror(errno);
+ }
+
+ const std::string& GetTempDirPath() {
+ return tmp_dir_path_;
+ }
+
+ const std::string& GetTargetApkPath() {
+ return target_apk_path_;
+ }
+
+ const std::string& GetOverlayApkPath() {
+ return overlay_apk_path_;
+ }
+
+ const std::string& GetIdmapPath() {
+ return idmap_path_;
+ }
+
+ private:
+ std::string tmp_dir_path_;
+ std::string target_apk_path_;
+ std::string overlay_apk_path_;
+ std::string idmap_path_;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_TESTS_TESTHELPERS_H_
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
new file mode 100644
index 0000000..97ff03e
--- /dev/null
+++ b/cmds/idmap2/tests/XmlTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <cstdio> // fclose
+
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(XmlTests, Create) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ auto data = zip->Uncompress("AndroidManifest.xml");
+ ASSERT_THAT(data, NotNull());
+
+ auto xml = Xml::Create(data->buf, data->size);
+ ASSERT_THAT(xml, NotNull());
+
+ fclose(stderr); // silence expected warnings from libandroidfw
+ const char* not_xml = "foo";
+ auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+ ASSERT_THAT(fail, IsNull());
+}
+
+TEST(XmlTests, FindTag) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ auto data = zip->Uncompress("res/xml/test.xml");
+ ASSERT_THAT(data, NotNull());
+
+ auto xml = Xml::Create(data->buf, data->size);
+ ASSERT_THAT(xml, NotNull());
+
+ auto attrs = xml->FindTag("c");
+ ASSERT_THAT(attrs, NotNull());
+ ASSERT_EQ(attrs->size(), 4u);
+ ASSERT_EQ(attrs->at("type_string"), "fortytwo");
+ ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
+ ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
+ ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0u);
+
+ auto fail = xml->FindTag("does-not-exist");
+ ASSERT_THAT(fail, IsNull());
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
new file mode 100644
index 0000000..a504d31
--- /dev/null
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <cstdio> // fclose
+#include <string>
+#include <utility>
+
+#include "idmap2/ZipFile.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(ZipFileTests, BasicOpen) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ fclose(stderr); // silence expected warnings from libziparchive
+ auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist");
+ ASSERT_THAT(fail, IsNull());
+}
+
+TEST(ZipFileTests, Crc) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ bool status;
+ uint32_t crc;
+ std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
+ ASSERT_TRUE(status);
+ ASSERT_EQ(crc, 0x762f3d24);
+
+ std::tie(status, std::ignore) = zip->Crc("does-not-exist");
+ ASSERT_FALSE(status);
+}
+
+TEST(ZipFileTests, Uncompress) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ auto data = zip->Uncompress("assets/lorem-ipsum.txt");
+ ASSERT_THAT(data, NotNull());
+ const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n");
+ ASSERT_THAT(data->size, lorem_ipsum.size());
+ ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum);
+
+ auto fail = zip->Uncompress("does-not-exist");
+ ASSERT_THAT(fail, IsNull());
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 0000000..9f89d31
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay">
+ <overlay
+ android:targetPackage="test.target" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
new file mode 100644
index 0000000..39336cc
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay.static1">
+ <overlay
+ android:targetPackage="test.target"
+ android:isStatic="true"
+ android:priority="1" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
new file mode 100644
index 0000000..e1cc175
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay.static2">
+ <overlay
+ android:targetPackage="test.target"
+ android:isStatic="true"
+ android:priority="2" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
new file mode 100644
index 0000000..cba1086
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -0,0 +1,40 @@
+# 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.
+
+FRAMEWORK_RES_APK="$(gettop)/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk"
+
+aapt2 compile --dir res -o compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifest.xml \
+ -o overlay.apk \
+ compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifestStatic1.xml \
+ -o overlay-static-1.apk \
+ compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifestStatic2.xml \
+ -o overlay-static-2.apk \
+ compiled.flata
+
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
new file mode 100644
index 0000000..9a0f487
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
new file mode 100644
index 0000000..3fc31c7
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
new file mode 100644
index 0000000..b4cd7cf
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml b/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml
new file mode 100644
index 0000000..eed0b3d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <string name="str1">overlay-1-sv</string>
+ <string name="str4">overlay-4-sv</string>
+</resources>
diff --git a/cmds/idmap2/tests/data/overlay/res/values/values.xml b/cmds/idmap2/tests/data/overlay/res/values/values.xml
new file mode 100644
index 0000000..815d1a8
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <string name="str1">overlay-1</string>
+ <string name="str3">overlay-3</string>
+ <integer name="int1">-1</integer>
+ <integer name="not_in_target">-1</integer>
+</resources>
diff --git a/cmds/idmap2/tests/data/target/AndroidManifest.xml b/cmds/idmap2/tests/data/target/AndroidManifest.xml
new file mode 100644
index 0000000..3a861b4
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.target">
+</manifest>
diff --git a/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt b/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt
new file mode 100644
index 0000000..d2cf010
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet.
diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build
new file mode 100644
index 0000000..8569c4f
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/build
@@ -0,0 +1,17 @@
+# 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.
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
new file mode 100644
index 0000000..56bf0d6
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <string name="a">a</string>
+ <string name="b">b</string>
+ <string name="c">c</string>
+ <string name="str1">target-1</string>
+ <string name="str2">target-2</string>
+ <string name="str3">target-3</string>
+ <string name="str4">target-4</string>
+ <string name="x">x</string>
+ <string name="y">y</string>
+ <string name="z">z</string>
+ <integer name="int1">1</integer>
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
new file mode 100644
index 0000000..0fe21c6
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<a>
+ <b>
+ <c
+ type_string="fortytwo"
+ type_int_dec="42"
+ type_int_hex="0x2a"
+ type_int_boolean="true"
+ />
+ </b>
+</a>
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
new file mode 100644
index 0000000..18ecc27
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ