|  | // Copyright 2016 The Bazel Authors. All rights reserved. | 
|  | // | 
|  | // 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 BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_SCAN_ENTRIES_TEST_H_ | 
|  | #define BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_SCAN_ENTRIES_TEST_H_ 1 | 
|  |  | 
|  | #include <errno.h> | 
|  | #ifndef _WIN32 | 
|  | #include <unistd.h> | 
|  | #endif | 
|  | #include <memory> | 
|  | #include <string> | 
|  |  | 
|  | #include "src/tools/singlejar/input_jar.h" | 
|  | #include "src/tools/singlejar/test_util.h" | 
|  |  | 
|  | #include "googletest/include/gtest/gtest.h" | 
|  |  | 
|  | static const char kJar[] = "jar.jar"; | 
|  | static const char kXXXX[] = "4GB-1file"; | 
|  | static const char kEmpty[] = "empty"; | 
|  | static const char kRes1[] = "res1"; | 
|  | static const char kRes2[] = "res2"; | 
|  | static const char kHuge[] = "4GB+1file"; | 
|  | static const int32_t res1_size = 123; | 
|  | static const int32_t res2_size = 456; | 
|  | static const int64_t huge_size = 0x100000001L; | 
|  | static const int64_t kHugeOffset = 0x100000001L; | 
|  |  | 
|  | /* Verifies that InputJar can handle zip/jar files created by a given creator. | 
|  | * This includes basic directory scan, handling huge (>4GB) zip files and huge | 
|  | * entries in them, and handling zip files with "huge" (>64K) number of entries. | 
|  | * A creator is passed as a typed parameter. | 
|  | */ | 
|  | template <class ZipCreator> | 
|  | class InputJarScanEntries : public testing::Test { | 
|  | public: | 
|  | static void SetUpTestCase() { ZipCreator::SetUpTestCase(); } | 
|  |  | 
|  | static void TearDownTestCase() { ZipCreator::TearDownTestCase(); } | 
|  |  | 
|  | static void CreateBasicJar() { | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kRes1, res1_size)); | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kRes2, res2_size)); | 
|  | unlink(kJar); | 
|  | ASSERT_EQ(0, ZipCreator::Jar(true, kJar, kRes1, kRes2, nullptr)); | 
|  | unlink(kRes1); | 
|  | unlink(kRes2); | 
|  | } | 
|  |  | 
|  | static void CreateJarWithHugeUncompressed() { | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kHuge, huge_size)); | 
|  | unlink(kJar); | 
|  | ASSERT_EQ(0, ZipCreator::Jar(true, kJar, kHuge, nullptr)); | 
|  | unlink(kHuge); | 
|  | } | 
|  |  | 
|  | static void CreateJarWithZip64Entries() { | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kXXXX, 0xFFFFFFFF)); | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kHuge, huge_size)); | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kEmpty, 0)); | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(kRes1, res1_size)); | 
|  | ASSERT_EQ( | 
|  | 0, ZipCreator::Jar(false, kJar, kXXXX, kHuge, kEmpty, kRes1, nullptr)); | 
|  | unlink(kXXXX); | 
|  | unlink(kHuge); | 
|  | unlink(kEmpty); | 
|  | unlink(kRes1); | 
|  | } | 
|  |  | 
|  | static void CreateJarWithLotsOfEntries() { | 
|  | unlink(kJar); | 
|  | // Create 256 directories with 256 files in each one, | 
|  | // make an archive from them | 
|  | for (int dir = 0; dir < 256; ++dir) { | 
|  | char dirname[10]; | 
|  | snprintf(dirname, sizeof(dirname), "dir%d", dir); | 
|  | #ifdef _WIN32 | 
|  | ASSERT_EQ(0, mkdir(dirname)); | 
|  | #else | 
|  | ASSERT_EQ(0, mkdir(dirname, 0777)); | 
|  | #endif | 
|  | for (int file = 0; file < 256; ++file) { | 
|  | char filepath[20]; | 
|  | snprintf(filepath, sizeof(filepath), "%s/%d", dirname, file); | 
|  | ASSERT_TRUE(singlejar_test_util::AllocateFile(filepath, 1)); | 
|  | } | 
|  | } | 
|  | ASSERT_EQ(0, ZipCreator::Jar(false, kJar, "dir*", nullptr)); | 
|  | for (int dir = 0; dir < 256; ++dir) { | 
|  | char rmdircmd[100]; | 
|  | #ifdef _WIN32 | 
|  | snprintf(rmdircmd, sizeof(rmdircmd), "rmdir /S /Q dir%d", dir); | 
|  | #else | 
|  | snprintf(rmdircmd, sizeof(rmdircmd), "rm dir%d/* && rmdir dir%d", dir, | 
|  | dir); | 
|  | #endif | 
|  | ASSERT_EQ(0, system(rmdircmd)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetUp() override { input_jar_.reset(new InputJar); } | 
|  |  | 
|  | static void SmogCheck(const CDH *cdh, const LH *lh) { | 
|  | ASSERT_TRUE(cdh->is()) << "No expected tag in the Central Directory Entry."; | 
|  | ASSERT_NE(nullptr, lh) << "No local header."; | 
|  | ASSERT_TRUE(lh->is()) << "No expected tag in the Local Header."; | 
|  | EXPECT_EQ(lh->file_name_string(), cdh->file_name_string()); | 
|  | if (!cdh->no_size_in_local_header()) { | 
|  | EXPECT_EQ(lh->compressed_file_size(), cdh->compressed_file_size()) | 
|  | << "Entry: " << lh->file_name_string(); | 
|  | EXPECT_EQ(lh->uncompressed_file_size(), cdh->uncompressed_file_size()) | 
|  | << "Entry: " << cdh->file_name_string(); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<InputJar> input_jar_; | 
|  | }; | 
|  |  | 
|  | TYPED_TEST_SUITE_P(InputJarScanEntries); | 
|  |  | 
|  | TYPED_TEST_P(InputJarScanEntries, OpenClose) { | 
|  | ASSERT_EQ(0, chdir(getenv("TEST_TMPDIR"))); | 
|  | this->CreateBasicJar(); | 
|  | singlejar_test_util::LsZip(kJar); | 
|  | ASSERT_TRUE(this->input_jar_->Open(kJar)); | 
|  | EXPECT_GE(this->input_jar_->fd(), 0); | 
|  | this->input_jar_->Close(); | 
|  | EXPECT_LT(this->input_jar_->fd(), 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check that the jar has the expected entries, they have expected | 
|  | * sizes, and that we can access both central directory entries and | 
|  | * local headers. | 
|  | */ | 
|  | TYPED_TEST_P(InputJarScanEntries, Basic) { | 
|  | ASSERT_EQ(0, chdir(getenv("TEST_TMPDIR"))); | 
|  | this->CreateBasicJar(); | 
|  | ASSERT_TRUE(this->input_jar_->Open(kJar)); | 
|  | const LH *lh; | 
|  | const CDH *cdh; | 
|  | int file_count = 0; | 
|  | bool res1_present = false; | 
|  | bool res2_present = false; | 
|  | for (int entry_count = 0; (cdh = this->input_jar_->NextEntry(&lh)); | 
|  | ++entry_count) { | 
|  | this->SmogCheck(cdh, lh); | 
|  | if ('/' != lh->file_name()[lh->file_name_length() - 1]) { | 
|  | ++file_count; | 
|  | if (cdh->file_name_is(kRes1)) { | 
|  | EXPECT_EQ(res1_size, cdh->uncompressed_file_size()); | 
|  | res1_present = true; | 
|  | } else if (cdh->file_name_is(kRes2)) { | 
|  | EXPECT_EQ(res2_size, cdh->uncompressed_file_size()); | 
|  | res2_present = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | this->input_jar_->Close(); | 
|  | unlink(kJar); | 
|  | EXPECT_TRUE(res1_present) << "Jar file " << kJar << " lacks expected '" | 
|  | << kRes1 << "' file."; | 
|  | EXPECT_TRUE(res2_present) << "Jar file " << kJar << " lacks expected '" | 
|  | << kRes2 << "' file."; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check we can handle >4GB jar with >4GB entry in it. | 
|  | */ | 
|  | TYPED_TEST_P(InputJarScanEntries, HugeUncompressed) { | 
|  | ASSERT_EQ(0, chdir(getenv("TEST_TMPDIR"))); | 
|  | this->CreateJarWithHugeUncompressed(); | 
|  | singlejar_test_util::LsZip(kJar); | 
|  | ASSERT_TRUE(this->input_jar_->Open(kJar)); | 
|  | const LH *lh; | 
|  | const CDH *cdh; | 
|  | bool huge_file_present = false; | 
|  |  | 
|  | while ((cdh = this->input_jar_->NextEntry(&lh))) { | 
|  | this->SmogCheck(cdh, lh); | 
|  | if (cdh->file_name_is(kHuge)) { | 
|  | EXPECT_EQ(huge_size, cdh->uncompressed_file_size()) | 
|  | << "Entry: " << cdh->file_name_string(); | 
|  | huge_file_present = true; | 
|  | } | 
|  | } | 
|  | this->input_jar_->Close(); | 
|  | unlink(kJar); | 
|  | EXPECT_TRUE(huge_file_present) << "Jar file " << kJar << " lacks expected '" | 
|  | << kHuge << "' file."; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check we can handle >4GB jar with huge and small entries and huge and | 
|  | * small offsets in the central directory. | 
|  | */ | 
|  | TYPED_TEST_P(InputJarScanEntries, TestZip64) { | 
|  | ASSERT_EQ(0, chdir(getenv("TEST_TMPDIR"))); | 
|  | this->CreateJarWithZip64Entries(); | 
|  | singlejar_test_util::LsZip(kJar); | 
|  | ASSERT_TRUE(this->input_jar_->Open(kJar)); | 
|  | const LH *lh; | 
|  | const CDH *cdh; | 
|  | while ((cdh = this->input_jar_->NextEntry(&lh))) { | 
|  | this->SmogCheck(cdh, lh); | 
|  |  | 
|  | if (cdh->file_name_is(kXXXX)) { | 
|  | EXPECT_EQ(0xFFFFFFFF, cdh->uncompressed_file_size()); | 
|  | EXPECT_EQ(0xFFFFFFFF, cdh->compressed_file_size()); | 
|  | } else if (cdh->file_name_is(kHuge)) { | 
|  | EXPECT_EQ(huge_size, cdh->uncompressed_file_size()); | 
|  | EXPECT_EQ(huge_size, cdh->compressed_file_size()); | 
|  | EXPECT_LT(kHugeOffset, cdh->local_header_offset()); | 
|  | } else if (cdh->file_name_is(kEmpty)) { | 
|  | EXPECT_EQ(0, cdh->uncompressed_file_size()); | 
|  | EXPECT_EQ(0, cdh->compressed_file_size()); | 
|  | EXPECT_EQ(0, lh->compressed_file_size()); | 
|  | EXPECT_LT(kHugeOffset, cdh->local_header_offset()); | 
|  | } else if (cdh->file_name_is(kRes1)) { | 
|  | EXPECT_EQ(res1_size, cdh->uncompressed_file_size()); | 
|  | EXPECT_LT(kHugeOffset, cdh->local_header_offset()); | 
|  | } | 
|  | } | 
|  | this->input_jar_->Close(); | 
|  | unlink(kJar); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check we can handle >64K entries. | 
|  | */ | 
|  | TYPED_TEST_P(InputJarScanEntries, LotsOfEntries) { | 
|  | ASSERT_EQ(0, chdir(getenv("TEST_TMPDIR"))); | 
|  | this->CreateJarWithLotsOfEntries(); | 
|  | #if !defined(__APPLE__) && !defined(_WIN32) | 
|  | const char kTailUnzip[] = "unzip -v jar.jar | tail"; | 
|  | ASSERT_EQ(0, system(kTailUnzip)) << "Failed command: " << kTailUnzip; | 
|  | #endif | 
|  | ASSERT_TRUE(this->input_jar_->Open(kJar)); | 
|  | const LH *lh; | 
|  | const CDH *cdh; | 
|  | int entry_count = 0; | 
|  | int file_count = 0; | 
|  | int dir_count = 0; | 
|  | while ((cdh = this->input_jar_->NextEntry(&lh))) { | 
|  | this->SmogCheck(cdh, lh); | 
|  | ++entry_count; | 
|  | if (cdh->file_name()[cdh->file_name_length() - 1] == '/') { | 
|  | ++dir_count; | 
|  | } else { | 
|  | ++file_count; | 
|  | } | 
|  | } | 
|  | this->input_jar_->Close(); | 
|  | unlink(kJar); | 
|  |  | 
|  | /* We cannot compare to the exact number because JDK's jar | 
|  | * adds META-INF/ and META-INF/MANIFEST.MF. | 
|  | */ | 
|  | EXPECT_LE(256 * 257, entry_count); | 
|  | EXPECT_LE(256, dir_count); | 
|  | EXPECT_LE(256 * 256, file_count); | 
|  | } | 
|  |  | 
|  | REGISTER_TYPED_TEST_SUITE_P(InputJarScanEntries, OpenClose, Basic, | 
|  | HugeUncompressed, TestZip64, LotsOfEntries); | 
|  |  | 
|  | #endif  // BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_SCAN_ENTRIES_TEST_H_ |