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