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