001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress; 020 021import java.io.BufferedInputStream; 022import java.io.Closeable; 023import java.io.File; 024import java.io.FileInputStream; 025import java.io.FileNotFoundException; 026import java.io.FileOutputStream; 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.OutputStream; 030import java.net.URI; 031import java.net.URL; 032import java.util.ArrayList; 033import java.util.List; 034import java.util.Locale; 035 036import junit.framework.TestCase; 037 038import org.apache.commons.compress.archivers.ArchiveEntry; 039import org.apache.commons.compress.archivers.ArchiveInputStream; 040import org.apache.commons.compress.archivers.ArchiveOutputStream; 041import org.apache.commons.compress.archivers.ArchiveStreamFactory; 042import org.apache.commons.compress.utils.IOUtils; 043 044public abstract class AbstractTestCase extends TestCase { 045 046 protected File dir; 047 protected File resultDir; 048 049 private File archive; // used to delete the archive in tearDown 050 protected List<String> archiveList; // Lists the content of the archive as originally created 051 052 protected ArchiveStreamFactory factory = new ArchiveStreamFactory(); 053 054 public AbstractTestCase() { 055 super(); 056 } 057 058 public AbstractTestCase(String name) { 059 super(name); 060 } 061 062 @Override 063 protected void setUp() throws Exception { 064 dir = mkdir("dir"); 065 resultDir = mkdir("dir-result"); 066 archive = null; 067 } 068 069 public static File mkdir(String name) throws IOException { 070 File f = File.createTempFile(name, ""); 071 f.delete(); 072 f.mkdir(); 073 return f; 074 } 075 076 public static File getFile(String path) throws IOException { 077 URL url = AbstractTestCase.class.getClassLoader().getResource(path); 078 if (url == null) { 079 throw new FileNotFoundException("couldn't find " + path); 080 } 081 URI uri = null; 082 try { 083 uri = url.toURI(); 084 } catch (java.net.URISyntaxException ex) { 085// throw new IOException(ex); // JDK 1.6+ 086 IOException ioe = new IOException(); 087 ioe.initCause(ex); 088 throw ioe; 089 } 090 return new File(uri); 091 } 092 093 @Override 094 protected void tearDown() throws Exception { 095 rmdir(dir); 096 rmdir(resultDir); 097 dir = resultDir = null; 098 if (!tryHardToDelete(archive)) { 099 // 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}