View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress;
20  
21  import java.io.BufferedInputStream;
22  import java.io.Closeable;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileNotFoundException;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.net.URI;
31  import java.net.URL;
32  import java.util.ArrayList;
33  import java.util.List;
34  import java.util.Locale;
35  
36  import junit.framework.TestCase;
37  
38  import org.apache.commons.compress.archivers.ArchiveEntry;
39  import org.apache.commons.compress.archivers.ArchiveInputStream;
40  import org.apache.commons.compress.archivers.ArchiveOutputStream;
41  import org.apache.commons.compress.archivers.ArchiveStreamFactory;
42  import org.apache.commons.compress.utils.IOUtils;
43  
44  public abstract class AbstractTestCase extends TestCase {
45  
46      protected File dir;
47      protected File resultDir;
48  
49      private File archive; // used to delete the archive in tearDown
50      protected List<String> archiveList; // Lists the content of the archive as originally created
51  
52      protected ArchiveStreamFactory factory = new ArchiveStreamFactory();
53  
54      public AbstractTestCase() {
55      }
56  
57      public AbstractTestCase(String name) {
58          super(name);
59      }
60  
61      @Override
62      protected void setUp() throws Exception {
63          dir = mkdir("dir");
64          resultDir = mkdir("dir-result");
65          archive = null;
66      }
67  
68      public static File mkdir(String name) throws IOException {
69          File f = File.createTempFile(name, "");
70          f.delete();
71          f.mkdir();
72          return f;
73      }
74  
75      public static File getFile(String path) throws IOException {
76          URL url = AbstractTestCase.class.getClassLoader().getResource(path);
77          if (url == null) {
78              throw new FileNotFoundException("couldn't find " + path);
79          }
80          URI uri = null;
81          try {
82              uri = url.toURI();
83          } catch (java.net.URISyntaxException ex) {
84  //          throw new IOException(ex); // JDK 1.6+
85              IOException ioe = new IOException();
86              ioe.initCause(ex);
87              throw ioe;
88          }
89          return new File(uri);
90      }
91  
92      @Override
93      protected void tearDown() throws Exception {
94          rmdir(dir);
95          rmdir(resultDir);
96          dir = resultDir = null;
97          if (!tryHardToDelete(archive)) {
98              // Note: this exception won't be shown if the test has already failed
99              throw new Exception("Could not delete "+archive.getPath());
100         }
101     }
102 
103     public static void rmdir(File f) {
104         String[] s = f.list();
105         if (s != null) {
106             for (String element : s) {
107                 final File file = new File(f, element);
108                 if (file.isDirectory()){
109                     rmdir(file);
110                 }
111                 boolean ok = tryHardToDelete(file);
112                 if (!ok && file.exists()){
113                     System.out.println("Failed to delete "+element+" in "+f.getPath());
114                 }
115             }
116         }
117         tryHardToDelete(f); // safer to delete and check
118         if (f.exists()){
119             throw new Error("Failed to delete "+f.getPath());
120         }
121     }
122 
123     private static final boolean ON_WINDOWS =
124         System.getProperty("os.name").toLowerCase(Locale.ENGLISH)
125         .indexOf("windows") > -1;
126 
127     /**
128      * Accommodate Windows bug encountered in both Sun and IBM JDKs.
129      * Others possible. If the delete does not work, call System.gc(),
130      * wait a little and try again.
131      *
132      * @return whether deletion was successful
133      * @since Stolen from FileUtils in Ant 1.8.0
134      */
135     public static boolean tryHardToDelete(File f) {
136         if (f != null && f.exists() && !f.delete()) {
137             if (ON_WINDOWS) {
138                 System.gc();
139             }
140             try {
141                 Thread.sleep(10);
142             } catch (InterruptedException ex) {
143                 // Ignore Exception
144             }
145             return f.delete();
146         }
147         return true;
148     }
149 
150     /**
151      * Creates an archive of textbased files in several directories. The
152      * archivername is the factory identifier for the archiver, for example zip,
153      * tar, cpio, jar, ar. The archive is created as a temp file.
154      *
155      * The archive contains the following files:
156      * <ul>
157      * <li>testdata/test1.xml</li>
158      * <li>testdata/test2.xml</li>
159      * <li>test/test3.xml</li>
160      * <li>bla/test4.xml</li>
161      * <li>bla/test5.xml</li>
162      * <li>bla/blubber/test6.xml</li>
163      * <li>test.txt</li>
164      * <li>something/bla</li>
165      * <li>test with spaces.txt</li>
166      * </ul>
167      *
168      * @param archivename
169      *            the identifier of this archive
170      * @return the newly created file
171      * @throws Exception
172      *             in case something goes wrong
173      */
174     protected File createArchive(String archivename) throws Exception {
175         ArchiveOutputStream out = null;
176         OutputStream stream = null;
177         try {
178             archive = File.createTempFile("test", "." + archivename);
179             archive.deleteOnExit();
180             archiveList = new ArrayList<String>();
181 
182             stream = new FileOutputStream(archive);
183             out = factory.createArchiveOutputStream(archivename, stream);
184 
185             final File file1 = getFile("test1.xml");
186             final File file2 = getFile("test2.xml");
187             final File file3 = getFile("test3.xml");
188             final File file4 = getFile("test4.xml");
189             final File file5 = getFile("test.txt");
190             final File file6 = getFile("test with spaces.txt");
191 
192             addArchiveEntry(out, "testdata/test1.xml", file1);
193             addArchiveEntry(out, "testdata/test2.xml", file2);
194             addArchiveEntry(out, "test/test3.xml", file3);
195             addArchiveEntry(out, "bla/test4.xml", file4);
196             addArchiveEntry(out, "bla/test5.xml", file4);
197             addArchiveEntry(out, "bla/blubber/test6.xml", file4);
198             addArchiveEntry(out, "test.txt", file5);
199             addArchiveEntry(out, "something/bla", file6);
200             addArchiveEntry(out, "test with spaces.txt", file6);
201 
202             out.finish();
203             return archive;
204         } finally {
205             if (out != null) {
206                 out.close();
207             } else if (stream != null) {
208                 stream.close();
209             }
210         }
211     }
212 
213     /**
214      * Add an entry to the archive, and keep track of the names in archiveList.
215      *
216      * @param out
217      * @param file1
218      * @throws IOException
219      * @throws FileNotFoundException
220      */
221     private void addArchiveEntry(ArchiveOutputStream out, String filename, final File infile)
222             throws IOException, FileNotFoundException {
223         ArchiveEntry entry = out.createArchiveEntry(infile, filename);
224         out.putArchiveEntry(entry);
225         IOUtils.copy(new FileInputStream(infile), out);
226         out.closeArchiveEntry();
227         archiveList.add(filename);
228     }
229 
230     /**
231      * Create an empty archive.
232      * @param archivename
233      * @return the archive File
234      * @throws Exception
235      */
236     protected File createEmptyArchive(String archivename) throws Exception {
237         ArchiveOutputStream out = null;
238         OutputStream stream = null;
239         archiveList = new ArrayList<String>();
240         try {
241             archive = File.createTempFile("empty", "." + archivename);
242             archive.deleteOnExit();
243             stream = new FileOutputStream(archive);
244             out = factory.createArchiveOutputStream(archivename, stream);
245             out.finish();
246         } finally {
247             if (out != null) {
248                 out.close();
249             } else if (stream != null) {
250                 stream.close();
251             }
252         }
253         return archive;
254     }
255 
256     /**
257      * Create an archive with a single file "test1.xml".
258      *
259      * @param archivename
260      * @return the archive File
261      * @throws Exception
262      */
263     protected File createSingleEntryArchive(String archivename) throws Exception {
264         ArchiveOutputStream out = null;
265         OutputStream stream = null;
266         archiveList = new ArrayList<String>();
267         try {
268             archive = File.createTempFile("empty", "." + archivename);
269             archive.deleteOnExit();
270             stream = new FileOutputStream(archive);
271             out = factory.createArchiveOutputStream(archivename, stream);
272             // Use short file name so does not cause problems for ar
273             addArchiveEntry(out, "test1.xml", getFile("test1.xml"));
274             out.finish();
275         } finally {
276             if (out != null) {
277                 out.close();
278             } else if (stream != null) {
279                 stream.close();
280             }
281         }
282         return archive;
283     }
284 
285     /**
286      * Checks if an archive contains all expected files.
287      *
288      * @param archive
289      *            the archive to check
290      * @param expected
291      *            a list with expected string filenames
292      * @throws Exception
293      */
294     protected void checkArchiveContent(File archive, List<String> expected)
295             throws Exception {
296         final InputStream is = new FileInputStream(archive);
297         try {
298             final BufferedInputStream buf = new BufferedInputStream(is);
299             final ArchiveInputStream in = factory.createArchiveInputStream(buf);
300             this.checkArchiveContent(in, expected);
301         } finally {
302             is.close();
303         }
304     }
305 
306     /**
307      * Checks that an archive input stream can be read, and that the file data matches file sizes.
308      *
309      * @param in
310      * @param expected list of expected entries or {@code null} if no check of names desired
311      * @throws Exception
312      */
313     protected void checkArchiveContent(ArchiveInputStream in, List<String> expected)
314             throws Exception {
315         checkArchiveContent(in, expected, true);
316     }
317 
318     /**
319      * Checks that an archive input stream can be read, and that the file data matches file sizes.
320      *
321      * @param in
322      * @param expected list of expected entries or {@code null} if no check of names desired
323      * @param cleanUp Cleans up resources if true
324      * @return returns the created result file if cleanUp = false, or null otherwise
325      * @throws Exception
326      */
327     protected File checkArchiveContent(ArchiveInputStream in, List<String> expected, boolean cleanUp)
328             throws Exception {
329         File result = mkdir("dir-result");
330         result.deleteOnExit();
331 
332         try {
333             ArchiveEntry entry = null;
334             while ((entry = in.getNextEntry()) != null) {
335                 File outfile = new File(result.getCanonicalPath() + "/result/"
336                         + entry.getName());
337                 long copied=0;
338                 if (entry.isDirectory()){
339                     outfile.mkdirs();
340                 } else {
341                     outfile.getParentFile().mkdirs();
342                     OutputStream out = new FileOutputStream(outfile);
343                     try {
344                         copied=IOUtils.copy(in, out);
345                     } finally {
346                         out.close();
347                     }
348                 }
349                 final long size = entry.getSize();
350                 if (size != ArchiveEntry.SIZE_UNKNOWN) {
351                     assertEquals("Entry.size should equal bytes read.",size, copied);
352                 }
353 
354                 if (!outfile.exists()) {
355                     fail("extraction failed: " + entry.getName());
356                 }
357                 if (expected != null && !expected.remove(getExpectedString(entry))) {
358                     fail("unexpected entry: " + getExpectedString(entry));
359                 }
360             }
361             in.close();
362             if (expected != null && expected.size() > 0) {
363                 for (String name : expected) {
364                     fail("Expected entry: " + name);
365                 }
366             }
367             if (expected != null) {
368                 assertEquals(0, expected.size());
369             }
370         } finally {
371             if (cleanUp) {
372                 rmdir(result);
373             }
374         }
375         return result;
376     }
377 
378     /**
379      * Override this method to change what is to be compared in the List.
380      * For example, size + name instead of just name.
381      *
382      * @param entry
383      * @return returns the entry name
384      */
385     protected String getExpectedString(ArchiveEntry entry) {
386         return entry.getName();
387     }
388 
389     /**
390      * Creates a temporary directory and a temporary file inside that
391      * directory, returns both of them (the directory is the first
392      * element of the two element array).
393      */
394     protected File[] createTempDirAndFile() throws IOException {
395         File tmpDir = mkdir("testdir");
396         tmpDir.deleteOnExit();
397         File tmpFile = File.createTempFile("testfile", "", tmpDir);
398         tmpFile.deleteOnExit();
399         FileOutputStream fos = new FileOutputStream(tmpFile);
400         try {
401             fos.write(new byte[] {'f', 'o', 'o'});
402             return new File[] {tmpDir, tmpFile};
403         } finally {
404             fos.close();
405         }
406     }
407 
408     protected void closeQuietly(Closeable closeable){
409         if (closeable != null) {
410             try {
411                 closeable.close();
412             } catch (IOException ignored) {
413                 // ignored
414             }
415         }
416     }
417 
418     protected static interface StreamWrapper<I extends InputStream> {
419         I wrap(InputStream in) throws Exception;
420     }
421 }