| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| package org.apache.beam.sdk.util; |
| |
| import static org.hamcrest.Matchers.arrayWithSize; |
| import static org.hamcrest.Matchers.not; |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Path; |
| import java.util.Arrays; |
| import java.util.Enumeration; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.io.ByteSource; |
| import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.io.CharSource; |
| import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.io.Files; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TemporaryFolder; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Tests for the {@link ZipFiles} class. These tests make sure that the handling of zip-files works |
| * fine. |
| */ |
| @RunWith(JUnit4.class) |
| public class ZipFilesTest { |
| @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); |
| private File tmpDir; |
| |
| @Rule public TemporaryFolder tmpOutputFolder = new TemporaryFolder(); |
| private File zipFile; |
| |
| @Before |
| public void setUp() throws Exception { |
| tmpDir = tmpFolder.getRoot(); |
| zipFile = createZipFileHandle(); // the file is not actually created |
| } |
| |
| /** |
| * Verify that zipping and unzipping works fine. We zip a directory having some subdirectories, |
| * unzip it again and verify the structure to be in place. |
| */ |
| @Test |
| public void testZipWithSubdirectories() throws Exception { |
| File zipDir = new File(tmpDir, "zip"); |
| File subDir1 = new File(zipDir, "subDir1"); |
| File subDir2 = new File(subDir1, "subdir2"); |
| assertTrue(subDir2.mkdirs()); |
| createFileWithContents(subDir2, "myTextFile.txt", "Simple Text"); |
| |
| assertZipAndUnzipOfDirectoryMatchesOriginal(tmpDir); |
| } |
| |
| /** An empty subdirectory must have its own zip-entry. */ |
| @Test |
| public void testEmptySubdirectoryHasZipEntry() throws Exception { |
| File zipDir = new File(tmpDir, "zip"); |
| File subDirEmpty = new File(zipDir, "subDirEmpty"); |
| assertTrue(subDirEmpty.mkdirs()); |
| |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| assertZipOnlyContains("zip/subDirEmpty/"); |
| } |
| |
| /** A directory with contents should not have a zip entry. */ |
| @Test |
| public void testSubdirectoryWithContentsHasNoZipEntry() throws Exception { |
| File zipDir = new File(tmpDir, "zip"); |
| File subDirContent = new File(zipDir, "subdirContent"); |
| assertTrue(subDirContent.mkdirs()); |
| createFileWithContents(subDirContent, "myTextFile.txt", "Simple Text"); |
| |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| assertZipOnlyContains("zip/subdirContent/myTextFile.txt"); |
| } |
| |
| @Test |
| public void testZipDirectoryToOutputStream() throws Exception { |
| createFileWithContents(tmpDir, "myTextFile.txt", "Simple Text"); |
| File[] sourceFiles = tmpDir.listFiles(); |
| Arrays.sort(sourceFiles); |
| assertThat(sourceFiles, not(arrayWithSize(0))); |
| |
| try (FileOutputStream outputStream = new FileOutputStream(zipFile)) { |
| ZipFiles.zipDirectory(tmpDir, outputStream); |
| } |
| File outputDir = Files.createTempDir(); |
| ZipFiles.unzipFile(zipFile, outputDir); |
| File[] outputFiles = outputDir.listFiles(); |
| Arrays.sort(outputFiles); |
| |
| assertThat(outputFiles, arrayWithSize(sourceFiles.length)); |
| for (int i = 0; i < sourceFiles.length; i++) { |
| compareFileContents(sourceFiles[i], outputFiles[i]); |
| } |
| |
| removeRecursive(outputDir.toPath()); |
| assertTrue(zipFile.delete()); |
| } |
| |
| @Test |
| public void testEntries() throws Exception { |
| File zipDir = new File(tmpDir, "zip"); |
| File subDir1 = new File(zipDir, "subDir1"); |
| File subDir2 = new File(subDir1, "subdir2"); |
| assertTrue(subDir2.mkdirs()); |
| createFileWithContents(subDir2, "myTextFile.txt", "Simple Text"); |
| |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| |
| try (ZipFile zip = new ZipFile(zipFile)) { |
| Enumeration<? extends ZipEntry> entries = zip.entries(); |
| for (ZipEntry entry : ZipFiles.entries(zip)) { |
| assertTrue(entries.hasMoreElements()); |
| // ZipEntry doesn't override equals |
| assertEquals(entry.getName(), entries.nextElement().getName()); |
| } |
| assertFalse(entries.hasMoreElements()); |
| } |
| } |
| |
| @Test |
| public void testAsByteSource() throws Exception { |
| File zipDir = new File(tmpDir, "zip"); |
| assertTrue(zipDir.mkdirs()); |
| createFileWithContents(zipDir, "myTextFile.txt", "Simple Text"); |
| |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| |
| try (ZipFile zip = new ZipFile(zipFile)) { |
| ZipEntry entry = zip.getEntry("zip/myTextFile.txt"); |
| ByteSource byteSource = ZipFiles.asByteSource(zip, entry); |
| if (entry.getSize() != -1) { |
| assertEquals(entry.getSize(), byteSource.size()); |
| } |
| assertArrayEquals("Simple Text".getBytes(StandardCharsets.UTF_8), byteSource.read()); |
| } |
| } |
| |
| @Test |
| public void testAsCharSource() throws Exception { |
| File zipDir = new File(tmpDir, "zip"); |
| assertTrue(zipDir.mkdirs()); |
| createFileWithContents(zipDir, "myTextFile.txt", "Simple Text"); |
| |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| |
| try (ZipFile zip = new ZipFile(zipFile)) { |
| ZipEntry entry = zip.getEntry("zip/myTextFile.txt"); |
| CharSource charSource = ZipFiles.asCharSource(zip, entry, StandardCharsets.UTF_8); |
| assertEquals("Simple Text", charSource.read()); |
| } |
| } |
| |
| private void assertZipOnlyContains(String zipFileEntry) throws IOException { |
| try (ZipFile zippedFile = new ZipFile(zipFile)) { |
| assertEquals(1, zippedFile.size()); |
| ZipEntry entry = zippedFile.entries().nextElement(); |
| assertEquals(zipFileEntry, entry.getName()); |
| } |
| } |
| |
| /** try to unzip to a non-existent directory and make sure that it fails. */ |
| @Test |
| public void testInvalidTargetDirectory() throws IOException { |
| File zipDir = new File(tmpDir, "zipdir"); |
| assertTrue(zipDir.mkdir()); |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| File invalidDirectory = new File("/foo/bar"); |
| assertFalse(invalidDirectory.exists()); |
| try { |
| ZipFiles.unzipFile(zipFile, invalidDirectory); |
| fail("We expect the IllegalArgumentException, but it never occured"); |
| } catch (IllegalArgumentException e) { |
| // This is the expected exception - we passed the test. |
| } |
| } |
| |
| /** Try to unzip to an existing directory, but failing to create directories. */ |
| @Test |
| public void testDirectoryCreateFailed() throws IOException { |
| File zipDir = new File(tmpDir, "zipdir"); |
| assertTrue(zipDir.mkdir()); |
| ZipFiles.zipDirectory(tmpDir, zipFile); |
| File targetDirectory = Files.createTempDir(); |
| // Touch a file where the directory should be. |
| Files.touch(new File(targetDirectory, "zipdir")); |
| try { |
| ZipFiles.unzipFile(zipFile, targetDirectory); |
| fail("We expect the IOException, but it never occured"); |
| } catch (IOException e) { |
| // This is the expected exception - we passed the test. |
| } |
| } |
| |
| /** |
| * zip and unzip a certain directory, and verify the content afterward to be identical. |
| * |
| * @param sourceDir the directory to zip |
| */ |
| private void assertZipAndUnzipOfDirectoryMatchesOriginal(File sourceDir) throws IOException { |
| File[] sourceFiles = sourceDir.listFiles(); |
| Arrays.sort(sourceFiles); |
| |
| File zipFile = createZipFileHandle(); |
| ZipFiles.zipDirectory(sourceDir, zipFile); |
| File outputDir = Files.createTempDir(); |
| ZipFiles.unzipFile(zipFile, outputDir); |
| File[] outputFiles = outputDir.listFiles(); |
| Arrays.sort(outputFiles); |
| |
| assertThat(outputFiles, arrayWithSize(sourceFiles.length)); |
| for (int i = 0; i < sourceFiles.length; i++) { |
| compareFileContents(sourceFiles[i], outputFiles[i]); |
| } |
| |
| removeRecursive(outputDir.toPath()); |
| assertTrue(zipFile.delete()); |
| } |
| |
| /** |
| * Compare the content of two files or directories recursively. |
| * |
| * @param expected the expected directory or file content |
| * @param actual the actual directory or file content |
| */ |
| private void compareFileContents(File expected, File actual) throws IOException { |
| assertEquals(expected.isDirectory(), actual.isDirectory()); |
| assertEquals(expected.getName(), actual.getName()); |
| if (expected.isDirectory()) { |
| // Go through the children step by step. |
| File[] expectedChildren = expected.listFiles(); |
| Arrays.sort(expectedChildren); |
| File[] actualChildren = actual.listFiles(); |
| Arrays.sort(actualChildren); |
| assertThat(actualChildren, arrayWithSize(expectedChildren.length)); |
| for (int i = 0; i < expectedChildren.length; i++) { |
| compareFileContents(expectedChildren[i], actualChildren[i]); |
| } |
| } else { |
| // Compare the file content itself. |
| assertTrue(Files.equal(expected, actual)); |
| } |
| } |
| |
| /** Create a File object to which we can safely zip a file. */ |
| private File createZipFileHandle() throws IOException { |
| File zipFile = File.createTempFile("test", "zip", tmpOutputFolder.getRoot()); |
| assertTrue(zipFile.delete()); |
| return zipFile; |
| } |
| |
| // This is not generally safe as it does not handle symlinks, etc. However it is safe |
| // enough for these tests. |
| private static void removeRecursive(Path path) throws IOException { |
| Iterable<File> files = Files.fileTraverser().depthFirstPostOrder(path.toFile()); |
| for (File f : files) { |
| java.nio.file.Files.delete(f.toPath()); |
| } |
| } |
| |
| /** Create file dir/fileName with contents fileContents. */ |
| private void createFileWithContents(File dir, String fileName, String fileContents) |
| throws IOException { |
| File txtFile = new File(dir, fileName); |
| Files.asCharSink(txtFile, StandardCharsets.UTF_8).write(fileContents); |
| } |
| } |