// 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 #include #include #include #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 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); ASSERT_EQ(0, mkdir(dirname, 0777)); 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]; snprintf(rmdircmd, sizeof(rmdircmd), "rm dir%d/* && rmdir dir%d", dir, dir); 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 input_jar_; }; TYPED_TEST_CASE_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__) 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_CASE_P(InputJarScanEntries, OpenClose, Basic, HugeUncompressed, TestZip64, LotsOfEntries); #endif // BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_SCAN_ENTRIES_TEST_H_