blob: 4e6b120f373097830d1bd66a2d010a801f87aa90 [file] [log] [blame]
// 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>
#include <unistd.h>
#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);
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<InputJar> 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_