zip_archive_test.cc revision 4f6b499ead3de87888d37a74adceaec92c584c0a
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "ziparchive/zip_archive.h" 18 19#include <errno.h> 20#include <getopt.h> 21#include <stdio.h> 22#include <unistd.h> 23#include <vector> 24 25#include <gtest/gtest.h> 26 27static std::string test_data_dir; 28 29static const std::string kValidZip = "valid.zip"; 30 31static const uint8_t kATxtContents[] = { 32 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 33 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 34 '\n' 35}; 36 37static const uint8_t kBTxtContents[] = { 38 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 39 '\n' 40}; 41 42static int32_t OpenArchiveWrapper(const std::string& name, 43 ZipArchiveHandle* handle) { 44 const std::string abs_path = test_data_dir + "/" + name; 45 return OpenArchive(abs_path.c_str(), handle); 46} 47 48static void AssertNameEquals(const std::string& name_str, 49 const ZipEntryName& name) { 50 ASSERT_EQ(name_str.size(), name.name_length); 51 ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length)); 52} 53 54TEST(ziparchive, Open) { 55 ZipArchiveHandle handle; 56 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); 57 58 CloseArchive(handle); 59} 60 61TEST(ziparchive, Iteration) { 62 ZipArchiveHandle handle; 63 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); 64 65 void* iteration_cookie; 66 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL)); 67 68 ZipEntry data; 69 ZipEntryName name; 70 71 // b/c.txt 72 ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); 73 AssertNameEquals("b/c.txt", name); 74 75 // b/d.txt 76 ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); 77 AssertNameEquals("b/d.txt", name); 78 79 // a.txt 80 ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); 81 AssertNameEquals("a.txt", name); 82 83 // b.txt 84 ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); 85 AssertNameEquals("b.txt", name); 86 87 // b/ 88 ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); 89 AssertNameEquals("b/", name); 90 91 // End of iteration. 92 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); 93 94 CloseArchive(handle); 95} 96 97TEST(ziparchive, FindEntry) { 98 ZipArchiveHandle handle; 99 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); 100 101 ZipEntry data; 102 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); 103 104 // Known facts about a.txt, from zipinfo -v. 105 ASSERT_EQ(63, data.offset); 106 ASSERT_EQ(kCompressDeflated, data.method); 107 ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length); 108 ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length); 109 ASSERT_EQ(0x950821c5, data.crc32); 110 111 // An entry that doesn't exist. Should be a negative return code. 112 ASSERT_LT(FindEntry(handle, "nonexistent.txt", &data), 0); 113 114 CloseArchive(handle); 115} 116 117TEST(ziparchive, ExtractToMemory) { 118 ZipArchiveHandle handle; 119 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); 120 121 // An entry that's deflated. 122 ZipEntry data; 123 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); 124 const uint32_t a_size = data.uncompressed_length; 125 ASSERT_EQ(a_size, sizeof(kATxtContents)); 126 uint8_t* buffer = new uint8_t[a_size]; 127 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size)); 128 ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size)); 129 delete[] buffer; 130 131 // An entry that's stored. 132 ASSERT_EQ(0, FindEntry(handle, "b.txt", &data)); 133 const uint32_t b_size = data.uncompressed_length; 134 ASSERT_EQ(b_size, sizeof(kBTxtContents)); 135 buffer = new uint8_t[b_size]; 136 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size)); 137 ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size)); 138 delete[] buffer; 139 140 CloseArchive(handle); 141} 142 143static const uint32_t kEmptyEntriesZip[] = { 144 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 145 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 146 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 147 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000, 148 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974, 149 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400, 150 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 }; 151 152TEST(ziparchive, EmptyEntries) { 153 char temp_file_pattern[] = "empty_entries_test_XXXXXX"; 154 int fd = mkstemp(temp_file_pattern); 155 ASSERT_NE(-1, fd); 156 const ssize_t file_size = sizeof(kEmptyEntriesZip); 157 ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size))); 158 159 ZipArchiveHandle handle; 160 ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle)); 161 162 ZipEntry entry; 163 ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry)); 164 ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length); 165 uint8_t buffer[1]; 166 ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1)); 167 168 char output_file_pattern[] = "empty_entries_output_XXXXXX"; 169 int output_fd = mkstemp(output_file_pattern); 170 ASSERT_NE(-1, output_fd); 171 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd)); 172 173 struct stat stat_buf; 174 ASSERT_EQ(0, fstat(output_fd, &stat_buf)); 175 ASSERT_EQ(0, stat_buf.st_size); 176 177 close(fd); 178 close(output_fd); 179} 180 181TEST(ziparchive, TrailerAfterEOCD) { 182 char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX"; 183 int fd = mkstemp(temp_file_pattern); 184 ASSERT_NE(-1, fd); 185 186 // Create a file with 8 bytes of random garbage. 187 static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' }; 188 const ssize_t file_size = sizeof(kEmptyEntriesZip); 189 const ssize_t trailer_size = sizeof(trailer); 190 ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size))); 191 ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size))); 192 193 ZipArchiveHandle handle; 194 ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle)); 195} 196 197TEST(ziparchive, ExtractToFile) { 198 char kTempFilePattern[] = "zip_archive_input_XXXXXX"; 199 int fd = mkstemp(kTempFilePattern); 200 ASSERT_NE(-1, fd); 201 const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' }; 202 const ssize_t data_size = sizeof(data); 203 204 ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size))); 205 206 ZipArchiveHandle handle; 207 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); 208 209 ZipEntry entry; 210 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry)); 211 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd)); 212 213 214 // Assert that the first 8 bytes of the file haven't been clobbered. 215 uint8_t read_buffer[data_size]; 216 ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET)); 217 ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size))); 218 ASSERT_EQ(0, memcmp(read_buffer, data, data_size)); 219 220 // Assert that the remainder of the file contains the incompressed data. 221 std::vector<uint8_t> uncompressed_data(entry.uncompressed_length); 222 ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length), 223 TEMP_FAILURE_RETRY( 224 read(fd, &uncompressed_data[0], entry.uncompressed_length))); 225 ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents, 226 sizeof(kATxtContents))); 227 228 // Assert that the total length of the file is sane 229 ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)), 230 lseek64(fd, 0, SEEK_END)); 231 232 close(fd); 233} 234 235int main(int argc, char** argv) { 236 ::testing::InitGoogleTest(&argc, argv); 237 238 static struct option options[] = { 239 { "test_data_dir", required_argument, NULL, 't' }, 240 { NULL, 0, NULL, 0 } 241 }; 242 243 while (true) { 244 int option_index; 245 const int c = getopt_long_only(argc, argv, "", options, &option_index); 246 if (c == -1) { 247 break; 248 } 249 250 if (c == 't') { 251 test_data_dir = optarg; 252 } 253 } 254 255 if (test_data_dir.size() == 0) { 256 printf("Test data flag (--test_data_dir) required\n\n"); 257 return -1; 258 } 259 260 if (test_data_dir[0] != '/') { 261 printf("Test data must be an absolute path, was %s\n\n", 262 test_data_dir.c_str()); 263 return -2; 264 } 265 266 return RUN_ALL_TESTS(); 267} 268