1 /*
2  * Copyright (C) 2020 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 package com.android.tradefed.util;
17 
18 import com.android.tradefed.util.zip.CentralDirectoryInfo;
19 import com.android.tradefed.util.zip.EndCentralDirectoryInfo;
20 
21 import java.io.FileInputStream;
22 import java.io.FileOutputStream;
23 import java.io.RandomAccessFile;
24 import java.util.Enumeration;
25 import java.util.zip.ZipEntry;
26 import java.util.zip.ZipOutputStream;
27 
28 import java.io.File;
29 import java.io.IOException;
30 import java.util.List;
31 import java.util.zip.ZipFile;
32 import org.junit.AfterClass;
33 import org.junit.Assert;
34 import org.junit.BeforeClass;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.junit.runners.JUnit4;
38 
39 import static org.junit.Assert.fail;
40 
41 /** {@link ZipUtilFuncTest} functional test.*/
42 @RunWith(JUnit4.class)
43 public class ZipUtilFuncTest {
44 
45     static private File sLargeFile = null;
46     static private File sTempDir = null;
47     static final private String OVERFLOW = "ffffffff";
48     static final private String TEMPDIR = "Zip64Test";
49     static final private int SENTRIES = 700;
50 
51     @BeforeClass
setUpBeforeClass()52     public static void setUpBeforeClass() throws Exception {
53         // Programmingly make one large zip file.
54         sLargeFile = FileUtil.createTempFile(TEMPDIR, ".zip");
55         sTempDir = FileUtil.createTempDir(TEMPDIR);
56         createLargeZipFile();
57     }
58 
59     @AfterClass
tearDownAfterClass()60     public static void tearDownAfterClass() throws Exception {
61         FileUtil.deleteFile(sLargeFile);
62         FileUtil.recursiveDelete(sTempDir);
63     }
64 
65     /** Functional test for {@link EndCentralDirectoryInfo} to ensure the number of entries are the
66      *  same when use-zip64-in-partial-download is set and file is larger than 4GB. */
67     @Test
testCentralDirectoryInfoSize()68     public void testCentralDirectoryInfoSize() throws IOException {
69         EndCentralDirectoryInfo endCentralDirInfo = new EndCentralDirectoryInfo(sLargeFile, true);
70         Assert.assertEquals(SENTRIES, endCentralDirInfo.getEntryNumber());
71     }
72 
73     /**
74      * Functional test for {@link EndCentralDirectoryInfo} to ensure the central directory offset
75      * is overflow when use-zip64-in-partial-download is not set and file is larger than 4GB.
76      */
77     @Test
testCentralDirectoryOffsetWithoutUseZip64()78     public void testCentralDirectoryOffsetWithoutUseZip64() throws IOException {
79         EndCentralDirectoryInfo endCentralDirInfo = new EndCentralDirectoryInfo(sLargeFile, false);
80         Assert.assertEquals(OVERFLOW, Long.toHexString(endCentralDirInfo.getCentralDirOffset()));
81     }
82 
83     /**
84      * Functional test for {@link EndCentralDirectoryInfo} to ensure the central directory data
85      * is good when use-zip64-in-partial-download is set and file is larger than 4GB.
86      */
87     @Test
testCentralDirectoryInfos()88     public void testCentralDirectoryInfos() throws IOException {
89         EndCentralDirectoryInfo endCentralDirInfo = new EndCentralDirectoryInfo(sLargeFile, true);
90 
91         // Use ZipFile to make ensure the contents are equal.
92         ZipFile zipFile = new ZipFile(sLargeFile.getAbsolutePath());
93         List<CentralDirectoryInfo> zipEntries =
94                 ZipUtil.getZipCentralDirectoryInfos(
95                         sLargeFile,
96                         endCentralDirInfo,
97                         endCentralDirInfo.getCentralDirOffset(),
98                         true);
99         Assert.assertEquals(zipFile.size(), endCentralDirInfo.getEntryNumber());
100         for (CentralDirectoryInfo entry : zipEntries) {
101             Assert.assertFalse(validateCentralDirectoryInfo(entry));
102         }
103         validateCentralDirectoryInfos(zipFile, zipEntries);
104     }
105 
106     /**
107      * Functional test for {@link EndCentralDirectoryInfo}  to ensure getting expected exception
108      * when use-zip64-in-partial-download is set and file is larger than 4GB.
109      */
110     @Test
testCentralDirectoryInfosWithoutUseZip64()111     public void testCentralDirectoryInfosWithoutUseZip64() throws IOException {
112         EndCentralDirectoryInfo endCentralDirInfo = new EndCentralDirectoryInfo(sLargeFile, false);
113         try {
114             ZipUtil.getZipCentralDirectoryInfos(
115                         sLargeFile,
116                         endCentralDirInfo,
117                         endCentralDirInfo.getCentralDirOffset(),
118                         false);
119             fail("Should have thrown an exception.");
120         } catch (IOException expected) {
121             Assert.assertEquals(
122                 "Invalid central directory info for zip file is found.", expected.getMessage());
123         }
124     }
125 
126     /** Helper to create a large zip file with 700 entries, each entry is with 7MB size. */
createLargeZipFile()127     private static void createLargeZipFile() throws IOException {
128         FileOutputStream fos = new FileOutputStream(sLargeFile);
129         ZipOutputStream zos = new ZipOutputStream(fos);
130         // This sets the compression level to STORED, ie, uncompressed
131         zos.setLevel(ZipOutputStream.STORED);
132 
133         long largeSize = 7000000;
134         for (int i = 1 ; i <= SENTRIES ; i++) {
135             File srcDir = FileUtil.createTempDir("src", sTempDir);
136             String testLargeFile = String.format("testLargeFile_%s", i);
137             File localLargeFile = new File(srcDir, testLargeFile);
138             localLargeFile.createNewFile();
139             RandomAccessFile raf = new RandomAccessFile(localLargeFile, "rw");
140             raf.setLength(largeSize);
141             raf.close();
142             addToZipFile(localLargeFile.getAbsolutePath(), zos);
143         }
144         zos.close();
145         fos.close();
146     }
147 
148     /** Helper to create a file. */
addToZipFile(String fileName, ZipOutputStream zos)149     private static void addToZipFile(String fileName, ZipOutputStream zos) throws IOException {
150         File file = new File(fileName);
151         FileInputStream fis = new FileInputStream(file);
152         ZipEntry zipEntry = new ZipEntry(fileName);
153         zos.putNextEntry(zipEntry);
154         byte[] bytes = new byte[1024];
155         int length;
156         while ((length = fis.read(bytes)) >= 0) {
157             zos.write(bytes, 0, length);
158         }
159         zos.closeEntry();
160         fis.close();
161     }
162 
163     /** Helper to validate the {@link CentralDirectoryInfo} that the data is overflow or not. */
validateCentralDirectoryInfo(CentralDirectoryInfo entry)164     private boolean validateCentralDirectoryInfo(CentralDirectoryInfo entry) {
165         return (Long.toHexString(entry.getCompressedSize()).equals(OVERFLOW) ||
166                 Long.toHexString(entry.getUncompressedSize()).equals(OVERFLOW) ||
167                 Long.toHexString(entry.getLocalHeaderOffset()).equals(OVERFLOW));
168     }
169 
170     /** Helper to validate the {@link CentralDirectoryInfo} objects are equal. */
validateCentralDirectoryInfos( ZipFile zipFile, List<CentralDirectoryInfo> zipEntries)171     private void validateCentralDirectoryInfos(
172             ZipFile zipFile, List<CentralDirectoryInfo> zipEntries) {
173         for (CentralDirectoryInfo entry : zipEntries) {
174             boolean found = false;
175             Enumeration<? extends ZipEntry> entries = zipFile.entries();
176             while (entries.hasMoreElements()) {
177                 ZipEntry zipEntry = entries.nextElement();
178                 if (zipEntry.getName().equals(entry.getFileName())) {
179                     found = true;
180                     break;
181                 }
182             }
183             Assert.assertTrue(found);
184         }
185     }
186 }
187