diff options
Diffstat (limited to 'cmds/idmap2/tests/IdmapTests.cpp')
-rw-r--r-- | cmds/idmap2/tests/IdmapTests.cpp | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp new file mode 100644 index 000000000000..0379aa491682 --- /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 |