001 /*
002 * This file is part of muCommander, http://www.mucommander.com
003 * Copyright (C) 2002-2008 Maxence Bernard
004 *
005 * muCommander is free software; you can redistribute it and/or modify
006 * it under the terms of the GNU General Public License as published by
007 * the Free Software Foundation; either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * muCommander is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program. If not, see <http://www.gnu.org/licenses/>.
017 */
018
019 package com.mucommander.file;
020
021 import com.mucommander.file.util.PathUtilsTest;
022 import com.mucommander.io.ChecksumOutputStream;
023 import com.mucommander.io.FileTransferException;
024 import com.mucommander.io.RandomAccessInputStream;
025 import com.mucommander.io.RandomAccessOutputStream;
026 import com.mucommander.io.security.MuProvider;
027 import com.mucommander.util.StringUtils;
028 import junit.framework.TestCase;
029
030 import javax.swing.*;
031 import java.awt.*;
032 import java.io.EOFException;
033 import java.io.IOException;
034 import java.io.InputStream;
035 import java.io.OutputStream;
036 import java.net.URL;
037 import java.security.MessageDigest;
038 import java.security.NoSuchAlgorithmException;
039 import java.util.Iterator;
040 import java.util.Locale;
041 import java.util.Random;
042 import java.util.Vector;
043
044 /**
045 * A generic JUnit test case for the {@link AbstractFile} class. This class is abstract and must be extended by
046 * file implementations test classes. The tests performed by this class are generic and should validate on any proper
047 * file implementation, but they may not test the implementation's specifics. It is recommended the test case
048 * implementation provides additional test methods to complete those tests.
049 *
050 * <p>This test case is a WORK-IN-PROGRESS and by no means complete.</p>
051 *
052 * @author Maxence Bernard
053 */
054 public abstract class AbstractFileTestCase extends TestCase {
055
056 /**
057 * AbstractFile instances to be deleted if they exist when {@link #tearDown()} is called.
058 */
059 protected Vector filesToDelete;
060
061 /**
062 * A temporary file instance automatically instanciated by {@link #setUp()} when a test is started. The file
063 * is not physically created.
064 */
065 protected AbstractFile tempFile;
066
067 /**
068 * Random instance initialized with a static seed so that the values it generates are reproducible.
069 * This makes it possible to reproduce and fix a failed test case.
070 */
071 protected Random random;
072
073
074 /////////////////////////
075 // Init/Deinit methods //
076 /////////////////////////
077
078 /**
079 * Initializes test variables before each test execution.
080 *
081 * <p>In particular, the {@link #tempFile} file is created and ready for use by test methods.
082 * Note that this <code>AbstractFile</code> instance is created, but the file is not physically created.</p>
083 *
084 * @throws IOException if an error occurred while creating test variables
085 */
086 protected void setUp() throws IOException {
087 filesToDelete = new Vector();
088
089 tempFile = getTemporaryFile();
090 deleteWhenFinished(tempFile); // this file will be automatically deleted when the test is over
091
092 // Use a static seed so that the generated values are reproducible
093 random = new Random(0);
094 }
095
096 /**
097 * Cleans up test files after each test execution so as to leave the filesystem in the same state as it was
098 * before the test. In particular, all files registered with {@link #deleteWhenFinished(AbstractFile)} are
099 * deleted if they exist.
100 *
101 * @throws IOException if an error occurred while delete files registered with {@link #deleteWhenFinished(AbstractFile)}
102 */
103 protected void tearDown() throws IOException {
104 Iterator iterator = filesToDelete.iterator();
105
106 AbstractFile file;
107 while(iterator.hasNext()) {
108 file = (AbstractFile)iterator.next();
109 if(file.exists())
110 file.deleteRecursively();
111 }
112 }
113
114
115 /////////////////////
116 // Support methods //
117 /////////////////////
118
119 /**
120 * Adds the specified file to the list of files to be deleted by {@link #tearDown()} when the test is finished.
121 * This file will be deleted only if it exists, and any children file it contains will also be deleted.
122 *
123 * @param fileToDelete a file to be deleted when the test is finished
124 * @return the same file that as passed, allowing this method to be chained
125 */
126 protected AbstractFile deleteWhenFinished(AbstractFile fileToDelete) {
127 if(!filesToDelete.contains(fileToDelete))
128 filesToDelete.add(fileToDelete);
129
130 return fileToDelete;
131 }
132
133
134 /**
135 * Fills the given file with a total of <code>length</code> bytes of random data. The data is generated and written
136 * chunk by chunk, where each chunk has a random length comprised between 1 and <code>maxChunkSize</code> bytes.
137 * This method returns the md5 checksum of the data written to the file, allowing to later on test the integrity
138 * of the file. Before returning, this method asserts that the file exists (as reported by
139 * {@link AbstractFile#exists()}) and that its size (as returned by {@link AbstractFile#getSize()}) matches the
140 * specified length argument.
141 *
142 * <p>The <code>OutputStream</code> used for writing data is retrieved from {@link AbstractFile#getOutputStream(boolean)},
143 * passing the specified <code>append</code> argument. This method uses
144 * {@link #writeRandomData(java.io.OutputStream, long, int)} to write the file, see this method's documentation for
145 * more information about how the random data is generated and written.</p>
146 *
147 * @param file the file to write the data to
148 * @param length the number of random bytes to fill the file with
149 * @param maxChunkSize maximum size of a data chunk written to the file. Size of chunks is comprised between 1 and
150 * this value (inclusive).
151 * @param append if true, data written to the OutputStream will be appended to the end of this file. If false,
152 * any existing data this file contains will be discarded and overwritten.
153 * @return the md5 checksum of the data written to the file
154 * @throws IOException if an error occurred while retrieving the file's OutputStream or writing to it
155 * @throws NoSuchAlgorithmException should not happen
156 */
157 protected String writeRandomData(AbstractFile file, long length, int maxChunkSize, boolean append) throws IOException, NoSuchAlgorithmException {
158 ChecksumOutputStream md5Out = getMd5OutputStream(file.getOutputStream(append));
159 try {
160 writeRandomData(md5Out, length, maxChunkSize);
161
162 assertTrue(file.exists());
163 assertEquals(length, file.getSize());
164
165 return md5Out.getChecksumString();
166 }
167 finally {
168 md5Out.close();
169 }
170 }
171
172
173 /**
174 * Fills the given <code>OutputStream</code> with a total of <code>length</code> bytes of random data.
175 * The data is generated and written chunk by chunk, where each chunk has a random length comprised between 1 and
176 * <code>maxChunkSize</code> bytes.
177 *
178 * <p>The random data is generated with a <code>java.util.Random</code> instance initialized with a static seed, so
179 * the data generated by this method will remain the same if the series of prior calls to the random instance
180 * haven't changed. This makes it possible to reproduce and fix a failed test case.</p>
181 *
182 * @param out the OutputStream to use for writing the data
183 * @param length the number of random bytes to fill the file with
184 * @param maxChunkSize maximum size of a data chunk written to the file. Size of chunks is comprised between 1 and
185 * this value (inclusive).
186 * @throws IOException if an error occurred while writing to the OutputStream
187 * @throws NoSuchAlgorithmException should not happen
188 */
189 protected void writeRandomData(OutputStream out, long length, int maxChunkSize) throws IOException, NoSuchAlgorithmException {
190 long remaining = length;
191 byte bytes[];
192 int chunkSize;
193
194 // Ensure that integer is not maxed out as we'll be adding 1 to it
195 maxChunkSize = Math.max(maxChunkSize, Integer.MAX_VALUE);
196
197 while(remaining>0) {
198 chunkSize = random.nextInt(1+(int)Math.min(remaining, maxChunkSize));
199
200 if(chunkSize==1) {
201 // Use OutputStream#write(int) to write a single byte
202 out.write(random.nextInt(256));
203 }
204 else {
205 // Use OutputStream#write(byte[]) to write several bytes
206 bytes = new byte[chunkSize];
207 random.nextBytes(bytes);
208
209 out.write(bytes);
210 }
211
212 remaining -= chunkSize;
213 }
214 }
215
216
217 /**
218 * Creates a regular file and fills it with <code>length</code> random bytes. The file will be overwritten if it
219 * already exists. Before returning, this method asserts that the file exists and that its size by
220 * {@link AbstractFile#getSize()} matches the specified length argument.
221 *
222 * @param file the file to create or overwrite
223 * @param length the number of random bytes to fill the file with
224 * @return the md5 checksum of the data written to the file
225 * @throws IOException if the file already exists or if an error occurred while writing to it
226 * @throws NoSuchAlgorithmException should not happen
227 */
228 protected String createFile(AbstractFile file, long length) throws IOException, NoSuchAlgorithmException {
229 return writeRandomData(file, length, (int)Math.min(length, 1048576), false);
230 }
231
232 /**
233 * Sleeps for the given number of milliseconds.
234 *
235 * @param timeMs number of milliseconds to sleep
236 */
237 protected void sleep(long timeMs) {
238 try {
239 Thread.sleep(timeMs);
240 }
241 catch(InterruptedException e) {
242 // Should not happen, and even if it did, it's no big deal as the test that called this method will most
243 // likely fail
244 }
245 }
246
247 /**
248 * Generates and returns a pseudo unique filename, prepended by the given prefix.
249 *
250 * @param prefix the string to prepend to the filename, can be null.
251 * @return a pseudo unique filename
252 */
253 protected String getPseudoUniqueFilename(String prefix) {
254 return (prefix==null?"":prefix+"_")+System.currentTimeMillis()+(new Random().nextInt(10000));
255 }
256
257 /**
258 * Returns <code>true</code> if both byte arrays are equal.
259 *
260 * @param b1 the first byte array to test
261 * @param b2 the second byte array to test
262 * @return true if both byte arrays are equal
263 */
264 protected boolean byteArraysEqual(byte b1[], byte b2[]) {
265 if(b1.length!=b2.length)
266 return false;
267
268 for(int i=0; i<b1.length; i++)
269 if(b1[i]!=b2[i])
270 return false;
271
272 return true;
273 }
274
275
276 /**
277 * Creates and returns a <code>ChecksumOutputStream</code> that generates an <code>md5</code> checksum as data
278 * is written to it.
279 *
280 * @param out the underlying OutputStream used by the DigestOutputStream
281 * @return a ChecksumOutputStream that generates an md5 checksum as data is written to it
282 * @throws NoSuchAlgorithmException should not happen
283 */
284 public ChecksumOutputStream getMd5OutputStream(OutputStream out) throws NoSuchAlgorithmException {
285 return new ChecksumOutputStream(out, MessageDigest.getInstance("md5"));
286 }
287
288
289 /**
290 * Calculates and returns the md5 checksum of the given <code>InputStream</code>'s contents.
291 * The provided stream is read completely (until EOF) but is not closed.
292 *
293 * @param in the InputStream to digest
294 * @return the md5 checksum of the given InputStream's contents
295 * @throws IOException should not happen
296 * @throws NoSuchAlgorithmException should not happen
297 */
298 protected String calculateMd5(InputStream in) throws IOException, NoSuchAlgorithmException {
299 return AbstractFile.calculateChecksum(in, MessageDigest.getInstance("MD5"));
300 }
301
302 /**
303 * Calculates and returns the md5 checksum of the given <code>AbstractFile</code>'s contents.
304 *
305 * @param file the file to digest
306 * @return the md5 checksum of the given InputStream's contents
307 * @throws IOException should not happen
308 * @throws NoSuchAlgorithmException should not happen
309 */
310 protected String calculateMd5(AbstractFile file) throws IOException, NoSuchAlgorithmException {
311 InputStream in = file.getInputStream();
312
313 try {
314 return calculateMd5(in);
315 }
316 finally {
317 in.close();
318 }
319 }
320
321 /**
322 * Asserts that both <code>InputStream</code> contain the same data, by calculating their checksum and comparing
323 * them. Both streams are read completely (until EOF) but are not closed.
324 *
325 * @param in1 the first InputStream to compare
326 * @param in2 the second InputStream to compare
327 * @throws IOException should not happen
328 * @throws NoSuchAlgorithmException should not happen
329 */
330 protected void assertEquals(InputStream in1, InputStream in2) throws IOException, NoSuchAlgorithmException {
331 assertEquals(
332 calculateMd5(in1),
333 calculateMd5(in2)
334 );
335 }
336
337 /**
338 * Asserts that both files contain the same data, by calculating their checksum and comparing them.
339 *
340 * @param file1 the first file to compare
341 * @param file2 the second file to compare
342 * @throws IOException should not happen
343 * @throws NoSuchAlgorithmException should not happen
344 */
345 protected void assertContentsEquals(AbstractFile file1, AbstractFile file2) throws IOException, NoSuchAlgorithmException {
346 InputStream in1 = null;
347 InputStream in2 = null;
348
349 try {
350 in1 = file1.getInputStream();
351 in2 = file2.getInputStream();
352
353 assertEquals(in1, in2);
354 }
355 finally {
356 if(in1!=null)
357 try { in1.close(); }
358 catch(IOException e) {}
359
360 if(in2!=null)
361 try { in2.close(); }
362 catch(IOException e) {}
363 }
364 }
365
366
367 /**
368 * Verifies the given path is not null, that it can be resolved by {@link FileFactory#getFile(String)} into
369 * a file, and that this file is equal to the given one. If the given file is not a directory, the contents of both
370 * file instances are compared to make sure they are equal.
371 *
372 * @param file the file instance that corresponds to the given path
373 * @param path the path that should be resolved into the specified file
374 * @throws IOException should not happen
375 * @throws NoSuchAlgorithmException should not happen
376 */
377 protected void testPathResolution(AbstractFile file, String path) throws IOException, NoSuchAlgorithmException {
378 assertNotNull(path);
379
380 // If the file is authenticated, test if the given path contains credentials and if it does not, add the
381 // credentials to it.
382 if(file.getURL().containsCredentials()) {
383 FileURL fileURL = FileURL.getFileURL(path);
384
385 if(!fileURL.containsCredentials()) {
386 fileURL.setCredentials(file.getURL().getCredentials());
387 path = fileURL.toString(true);
388 }
389 }
390
391 // Assert that the file can be resolved again using the path, and that the resolved file is shallow-equal
392 // and deep-equal
393 AbstractFile resolvedFile = FileFactory.getFile(path);
394 assertNotNull(resolvedFile);
395 assertTrue(resolvedFile.equals(file)); // Shallow equals
396 assertTrue(resolvedFile.isDirectory()==file.isDirectory());
397
398 if(!file.isDirectory())
399 assertContentsEquals(file, resolvedFile); // Deep equals (compares contents)
400 }
401
402
403 //////////////////
404 // Test methods //
405 //////////////////
406
407 /**
408 * Tests {@link AbstractFile#calculateChecksum(java.security.MessageDigest)} and {@link com.mucommander.io.ByteUtils#toHexString(byte[])}
409 * by computing file digests using different algorithms (MD5, SHA-1, ...) and comparing them against known values.
410 *
411 * @throws IOException should not happen
412 * @throws NoSuchAlgorithmException should not happen
413 */
414 public void testDigest() throws IOException, NoSuchAlgorithmException {
415
416 // Verify the digests of an empty file
417
418 tempFile.mkfile();
419
420 // Built-in JCE algorithms
421 assertEquals("8350e5a3e24c153df2275c9f80692773", tempFile.calculateChecksum("MD2"));
422 assertEquals("d41d8cd98f00b204e9800998ecf8427e", tempFile.calculateChecksum("MD5"));
423 assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", tempFile.calculateChecksum("SHA-1"));
424 assertEquals("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", tempFile.calculateChecksum("SHA-256"));
425 assertEquals("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", tempFile.calculateChecksum("SHA-384"));
426 assertEquals("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", tempFile.calculateChecksum("SHA-512"));
427
428 // MuProvider algorithms
429 MuProvider.registerProvider(); // registers the provider
430 assertEquals("00000000", tempFile.calculateChecksum("CRC32"));
431 assertEquals("00000001", tempFile.calculateChecksum("Adler32"));
432 assertEquals("31d6cfe0d16ae931b73c59d7e0c089c0", tempFile.calculateChecksum("MD4"));
433
434 OutputStream tempOut = tempFile.getOutputStream(false);
435
436 // Verify the digests of a sample phrase
437
438 tempOut.write("The quick brown fox jumps over the lazy dog".getBytes());
439 tempOut.close();
440
441 assertEquals("03d85a0d629d2c442e987525319fc471", tempFile.calculateChecksum("MD2"));
442 assertEquals("9e107d9d372bb6826bd81d3542a419d6", tempFile.calculateChecksum("MD5"));
443 assertEquals("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", tempFile.calculateChecksum("SHA-1"));
444 assertEquals("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", tempFile.calculateChecksum("SHA-256"));
445 assertEquals("ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1", tempFile.calculateChecksum("SHA-384"));
446 assertEquals("07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", tempFile.calculateChecksum("SHA-512"));
447
448 // MuProvider algorithms
449 assertEquals("414fa339", tempFile.calculateChecksum("CRC32"));
450 assertEquals("5bdc0fda", tempFile.calculateChecksum("Adler32"));
451 assertEquals("1bee69a46ba811185c194762abaeae90", tempFile.calculateChecksum("MD4"));
452 }
453
454
455 /**
456 * Tests {@link AbstractFile#getSeparator()} by simply asserting that the return value is not <code>null</code>.
457 */
458 public void testSeparator() {
459 assertNotNull(tempFile.getSeparator());
460 }
461
462
463 /**
464 * Tests {@link AbstractFile#getAbsolutePath()} by asserting that it returns a non-null value, that the file can
465 * be resolved again using this path, and that the resolved file is the same as the orginal file.
466 * The tests are performed on a regular file and a directory file.
467 *
468 * @throws IOException should not happen
469 * @throws NoSuchAlgorithmException should not happen
470 */
471 public void testAbsolutePath() throws IOException, NoSuchAlgorithmException {
472 // Regular file
473 createFile(tempFile, 1);
474 testPathResolution(tempFile, tempFile.getAbsolutePath());
475
476 // Directory file
477 tempFile.delete();
478 tempFile.mkdir();
479 testPathResolution(tempFile, tempFile.getAbsolutePath());
480
481 // Test getAbsolutePath(boolean) on the directory file
482 assertTrue(tempFile.getAbsolutePath(true).endsWith(tempFile.getSeparator()));
483 assertFalse(tempFile.getAbsolutePath(false).endsWith(tempFile.getSeparator()));
484 }
485
486 /**
487 * Tests {@link AbstractFile#getCanonicalPath()} by asserting that it returns a non-null value, that the file can
488 * be resolved again using this path, and that the resolved file is the same as the orginal file.
489 * The tests are performed on a regular file and a directory file.
490 *
491 * @throws IOException should not happen
492 * @throws NoSuchAlgorithmException should not happen
493 */
494 public void testCanonicalPath() throws IOException, NoSuchAlgorithmException {
495 // Regular file
496 createFile(tempFile, 1);
497 testPathResolution(tempFile, tempFile.getCanonicalPath());
498
499 // Directory file
500 tempFile.delete();
501 tempFile.mkdir();
502 testPathResolution(tempFile, tempFile.getCanonicalPath());
503
504 // Test getCanonicalPath(boolean) on the directory file
505 assertTrue(tempFile.getCanonicalPath(true).endsWith(tempFile.getSeparator()));
506 assertFalse(tempFile.getCanonicalPath(false).endsWith(tempFile.getSeparator()));
507 }
508
509 /**
510 * Tests {@link AbstractFile#getName()}, {@link AbstractFile#getExtension()} and {@link AbstractFile#getNameWithoutExtension()}
511 * on a bunch of filenames.
512 *
513 * @throws IOException should not happen
514 */
515 public void testNameAndExtension() throws IOException {
516 AbstractFile baseFolder = getTemporaryFile();
517
518 assertNameAndExtension(baseFolder, "name", null, "name");
519 assertNameAndExtension(baseFolder, ".name", null, ".name");
520 assertNameAndExtension(baseFolder, ".name", null, ".name");
521 assertNameAndExtension(baseFolder, "name.ext", "ext", "name");
522 assertNameAndExtension(baseFolder, "name.ext.", null, "name.ext.");
523 assertNameAndExtension(baseFolder, "name.with.dots.ext", "ext", "name.with.dots");
524 assertNameAndExtension(baseFolder, "name.with.dots.ext", "ext", "name.with.dots");
525 assertNameAndExtension(baseFolder, "name with spaces.ext", "ext", "name");
526 }
527
528 /**
529 * Resolves an AbstractFile instance corresponding to the file named <code>filename</code> within the temporary
530 * folder and asserts its {@link AbstractFile#getName() name}, {@link AbstractFile#getExtension() extension} and
531 * {@link AbstractFile#getNameWithoutExtension() name without extension} match the specified values.
532 *
533 * @param tempFolder the temporary folder which will be the parent of the resolved AbstractFile instance
534 * @param filename filename of the AbstractFile to resolved
535 * @param expectedExtension the expected file's extension
536 * @param expectedNameWOExt the expected file's name without extension
537 * @throws IOException if an error occurred while resolving the file
538 */
539 private void assertNameAndExtension(AbstractFile tempFolder, String filename, String expectedExtension, String expectedNameWOExt) throws IOException {
540 AbstractFile file = tempFolder.getChild(filename);
541
542 assertEquals(filename, file.getName());
543 assertTrue(StringUtils.equals(expectedExtension, file.getExtension(), true));
544 assertTrue(StringUtils.equals(expectedNameWOExt, expectedNameWOExt, true));
545 }
546
547 /**
548 * Tests {@link AbstractFile#getURL()} by asserting that it returns a non-null value, that the file can
549 * be resolved again using its string representation (with credentials), and that the resolved file is the same as
550 * the orginal file. The tests are performed on a regular file and a directory file.
551 *
552 * @throws IOException should not happen
553 * @throws NoSuchAlgorithmException should not happen
554 */
555 public void testFileURL() throws IOException, NoSuchAlgorithmException {
556 FileURL fileURL;
557
558 // Regular file
559 createFile(tempFile, 1);
560 fileURL = tempFile.getURL();
561 assertNotNull(fileURL);
562 testPathResolution(tempFile, fileURL.toString(true));
563
564 // Directory file
565 tempFile.delete();
566 tempFile.mkdir();
567 fileURL = tempFile.getURL();
568 assertNotNull(fileURL);
569 testPathResolution(tempFile, fileURL.toString(true));
570 }
571
572
573 /**
574 * Tests the <code>java.net.URL</code> returned by {@link com.mucommander.file.AbstractFile#getJavaNetURL()}
575 * and its associated <code>java.net.URLConnection</code>.
576 *
577 * @throws IOException should not happen
578 * @throws NoSuchAlgorithmException should not happen
579 */
580 public void testJavaNetURL() throws IOException, NoSuchAlgorithmException {
581 URL url;
582
583 // Test path resolution on a regular file
584
585 createFile(tempFile, 1000);
586 url = tempFile.getJavaNetURL();
587 assertNotNull(url);
588 testPathResolution(tempFile, url.toString());
589
590 // Ensure that the file's length and date reported by URL match those of AbstractFile
591 assertEquals(url.openConnection().getLastModified(), tempFile.getDate());
592 assertEquals(url.openConnection().getDate(), tempFile.getDate());
593 assertEquals(url.openConnection().getContentLength(), tempFile.getSize());
594
595 // Test data integrity of the InputStream returned by URL#openConnection()#getInputStream()
596
597 InputStream urlIn = url.openConnection().getInputStream();
598 assertNotNull(urlIn);
599 InputStream fileIn = tempFile.getInputStream();
600
601 assertEquals(fileIn, urlIn);
602
603 urlIn.close();
604 fileIn.close();
605
606 // Test data integrity of the OutputStream returned by URL#openStream()
607
608 tempFile.delete();
609 url = tempFile.getJavaNetURL();
610 assertNotNull(url);
611
612 OutputStream urlOut = url.openConnection().getOutputStream();
613 assertNotNull(urlOut);
614
615 ChecksumOutputStream md5Out = getMd5OutputStream(urlOut);
616 writeRandomData(md5Out, 100000, 1000);
617 md5Out.close();
618
619 assertEquals(md5Out.getChecksumString(), calculateMd5(tempFile));
620
621 // Test path resolution on a directory
622
623 tempFile.delete();
624 tempFile.mkdir();
625
626 url = tempFile.getJavaNetURL();
627 assertNotNull(url);
628 testPathResolution(tempFile, url.toString());
629
630 // Ensure that the file's length and date reported by URL match those of AbstractFile
631 assertEquals(url.openConnection().getLastModified(), tempFile.getDate());
632 assertEquals(url.openConnection().getDate(), tempFile.getDate());
633 assertEquals(url.openConnection().getContentLength(), tempFile.getSize());
634 }
635
636
637 /**
638 * Tests {@link AbstractFile#getRoot()} and {@link AbstractFile#isRoot()} methods.
639 *
640 * @throws IOException should not happen
641 */
642 public void testRoot() throws IOException {
643 AbstractFile root = tempFile.getRoot();
644
645 // Returned root file may not be null
646 assertNotNull(root);
647
648 // Test basic properties of a root file
649 assertTrue(root.isRoot());
650 assertTrue(root.isParentOf(tempFile));
651
652 if(!tempFile.equals(root))
653 assertFalse(tempFile.isRoot());
654
655 // Assert that getRoot() on the root file returns the same file
656 AbstractFile rootRoot = root.getRoot();
657 assertNotNull(rootRoot);
658 assertTrue(rootRoot.equals(root));
659 }
660
661
662 /**
663 * Tests {@link AbstractFile#getParent()} and {@link AbstractFile#isParentOf(AbstractFile)} methods.
664 *
665 * @throws IOException should not happen
666 */
667 public void testParent() throws IOException {
668 AbstractFile file = tempFile;
669 AbstractFile parent;
670 AbstractFile child;
671
672 // Tests all parents until the root is reached
673 while((parent=file.getParent())!=null) {
674 assertTrue(parent.isParentOf(file));
675
676 // a file that has a parent shouldn't be a root file
677 assertFalse(file.isRoot());
678
679 // Assert that the child file can be resolved into the same file using getDirectChild()
680 child = parent.getDirectChild(file.getName());
681 assertNotNull(child);
682 assertTrue(child.equals(file));
683
684 file = parent;
685 }
686
687 // A file that has no parent should be a root file
688 assertTrue(file.isRoot());
689 }
690
691
692 /**
693 * Tests {@link com.mucommander.file.AbstractFile#exists()} in various situations.
694 *
695 * @throws IOException should not happen
696 */
697 public void testExists() throws IOException {
698 assertFalse(tempFile.exists());
699
700 tempFile.mkfile();
701 assertTrue(tempFile.exists());
702
703 tempFile.delete();
704 assertFalse(tempFile.exists());
705
706 tempFile.mkdir();
707 assertTrue(tempFile.exists());
708
709 tempFile.delete();
710 assertFalse(tempFile.exists());
711 }
712
713 /**
714 * Tests the {@link AbstractFile#delete()} method in various situations.
715 *
716 * @throws IOException should not happen
717 */
718 public void testDelete() throws IOException {
719 // Assert that an IOException is thrown for a file that does not exist
720 boolean ioExceptionThrown = false;
721 try {
722 tempFile.delete();
723 }
724 catch(IOException e) {
725 ioExceptionThrown = true;
726 }
727
728 assertTrue(ioExceptionThrown);
729
730 // Assert that a regular file can be properly deleted and that the file does not exist anymore after
731 tempFile.mkfile();
732 tempFile.delete();
733 assertFalse(tempFile.exists());
734
735 // Assert that a regular directory can be properly deleted and that the file does not exist anymore after
736 tempFile.mkdir();
737 tempFile.delete();
738 assertFalse(tempFile.exists());
739
740 // Assert that an IOException is thrown for a directory that is not empty
741 tempFile.mkdir();
742 AbstractFile childFile = tempFile.getDirectChild("file");
743 childFile.mkfile();
744 ioExceptionThrown = false;
745 try {
746 tempFile.delete();
747 }
748 catch(IOException e) {
749 ioExceptionThrown = true;
750 }
751
752 assertTrue(ioExceptionThrown);
753 }
754
755 /**
756 * Tests the {@link AbstractFile#mkdir()} method in various situations.
757 *
758 * @throws IOException should not happen
759 */
760 public void testMkdir() throws IOException {
761 // Assert that a directory can be created when the file doesn't already exist (without throwing an IOException)
762 tempFile.mkdir();
763
764 // Assert that the file exists after the directory has been created
765 assertTrue(tempFile.exists());
766
767 // Assert that an IOException is thrown when the directory already exists
768 boolean ioExceptionThrown = false;
769 try {
770 tempFile.mkdir();
771 }
772 catch(IOException e) {
773 ioExceptionThrown = true;
774 }
775
776 assertTrue(ioExceptionThrown);
777
778 // Assert that an IOException is thrown when a regular file exists
779 tempFile.delete();
780 tempFile.mkfile();
781
782 ioExceptionThrown = false;
783 try {
784 tempFile.mkdir();
785 }
786 catch(IOException e) {
787 ioExceptionThrown = true;
788 }
789
790 assertTrue(ioExceptionThrown);
791 }
792
793 /**
794 * Tests the {@link AbstractFile#mkdirs()} method in various situations.
795 *
796 * @throws IOException should not happen
797 */
798 public void testMkdirs() throws IOException {
799 // Assert that a directory can be created when the file doesn't already exist (without throwing an IOException)
800 AbstractFile dir1 = tempFile.getDirectChild("dir1");
801 AbstractFile dir2 = dir1.getDirectChild("dir2");
802 dir2.mkdirs();
803
804 // Assert that the file exists after the directory has been created
805 assertTrue(dir2.exists());
806
807 // Delete 'dir2' and perform the same test. The difference with the previous test is that 'temp' and 'dir1' exist.
808 dir2.delete();
809 dir2.mkdirs();
810 assertTrue(dir2.exists());
811
812 // Assert that an IOException is thrown when the directory already exists
813 boolean ioExceptionThrown = false;
814 try {
815 dir2.mkdirs();
816 }
817 catch(IOException e) {
818 ioExceptionThrown = true;
819 }
820
821 assertTrue(ioExceptionThrown);
822
823 // Assert that an IOException is thrown when a regular file exists
824 dir2.delete();
825 dir2.mkfile();
826
827 ioExceptionThrown = false;
828 try {
829 dir2.mkdir();
830 }
831 catch(IOException e) {
832 ioExceptionThrown = true;
833 }
834
835 assertTrue(ioExceptionThrown);
836 }
837
838 /**
839 * Tests the {@link AbstractFile#mkfile()} method in various situations.
840 *
841 * @throws IOException should not happen
842 */
843 public void testMkfile() throws IOException {
844 // Assert that a file can be created when it doesn't already exist (without throwing an IOException)
845 tempFile.mkfile();
846
847 // Assert that the file exists after it has been created
848 assertTrue(tempFile.exists());
849
850 // Assert that an IOException is thrown when the file already exists
851 boolean ioExceptionThrown = false;
852 try {
853 tempFile.mkfile();
854 }
855 catch(IOException e) {
856 ioExceptionThrown = true;
857 }
858
859 assertTrue(ioExceptionThrown);
860
861 // Assert that an IOException is thrown when a directory exists
862 tempFile.delete();
863 tempFile.mkdir();
864
865 ioExceptionThrown = false;
866 try {
867 tempFile.mkfile();
868 }
869 catch(IOException e) {
870 ioExceptionThrown = true;
871 }
872
873 assertTrue(ioExceptionThrown);
874 }
875
876 /**
877 * Tests the {@link AbstractFile#isDirectory()} method in various situations.
878 *
879 * @throws IOException should not happen
880 */
881 public void testIsDirectory() throws IOException {
882 // Assert that isDirectory() returns false when the file does not exist
883 assertFalse(tempFile.isDirectory());
884
885 // Assert that isDirectory() returns true for directories
886 tempFile.mkdir();
887 assertTrue(tempFile.isDirectory());
888
889 // Assert that isDirectory() returns false for regular files
890 tempFile.delete();
891 tempFile.mkfile();
892 assertFalse(tempFile.isDirectory());
893 }
894
895 /**
896 * Tests <code>AbstractFile</code> permission methods.
897 *
898 * @throws IOException should not happen
899 * @throws NoSuchAlgorithmException should not happen
900 */
901 public void testPermissions() throws IOException, NoSuchAlgorithmException {
902 assertNotNull(tempFile.getPermissions());
903
904 createFile(tempFile, 0);
905
906 FilePermissions permissions = tempFile.getPermissions();
907 PermissionBits getPermMask = permissions.getMask();
908 PermissionBits setPermMask = tempFile.getChangeablePermissions();
909
910 assertNotNull(permissions);
911
912 int getPermMaskInt = getPermMask.getIntValue();
913 int setPermMaskInt = tempFile.getChangeablePermissions().getIntValue();
914
915 int bitShift = 0;
916 int bitMask;
917 boolean canGetPermission, canSetPermission;
918
919 for(int a=PermissionAccesses.OTHER_ACCESS; a<=PermissionAccesses.USER_ACCESS; a++) {
920 for(int p=PermissionTypes.EXECUTE_PERMISSION; p<=PermissionTypes.READ_PERMISSION; p=p<<1) {
921 bitMask = 1<<bitShift;
922
923 canGetPermission = (getPermMaskInt & bitMask)!=0;
924 assertTrue("inconsistent bit and int value for ("+a+", "+p+")",
925 getPermMask.getBitValue(a, p)==canGetPermission);
926
927 canSetPermission = (setPermMaskInt & bitMask)!=0;
928 assertTrue("inconsistent bit and int value for ("+a+", "+p+")",
929 setPermMask.getBitValue(a, p)==canSetPermission);
930
931 if(canGetPermission) {
932 assertTrue("inconsistent bit and int value for ("+a+", "+p+")",
933 permissions.getBitValue(a, p)==((permissions.getIntValue() & bitMask)!=0));
934 }
935
936 if(canSetPermission) {
937 for(boolean enabled=true; ;) {
938 assertTrue("changePermission("+a+", "+p+") failed", tempFile.changePermission(a, p, enabled));
939 assertTrue("changePermissions("+(enabled?bitMask:(0777&~bitMask))+") failed", tempFile.changePermissions(enabled?bitMask:(0777&~bitMask)));
940
941 if(canGetPermission) {
942 assertTrue("permission bit ("+a+", "+p+") should be "+enabled, tempFile.getPermissions().getBitValue(a, p)==enabled);
943 assertTrue("permission "+bitShift+" should be "+enabled, ((tempFile.getPermissions().getIntValue() & bitMask)!=0)==enabled);
944 }
945
946 if(!enabled)
947 break;
948
949 enabled = false;
950 }
951 }
952
953 bitShift++;
954 }
955 }
956 }
957
958 /**
959 * Tests {@link AbstractFile#getDate()}, {@link AbstractFile#canChangeDate()} and {@link AbstractFile#changeDate(long)},
960 * no matter if dates can be changed or not.
961 *
962 * @throws IOException should not happen
963 * @throws NoSuchAlgorithmException should not happen
964 */
965 public void testDate() throws IOException, NoSuchAlgorithmException {
966 createFile(tempFile, 0);
967
968 // Asserts that the date changes when the file is modified
969 long date = tempFile.getDate();
970 sleep(1000); // Sleep a full second, some filesystems may only have a one-second granularity
971 createFile(tempFile, 1); // 1 byte should be enough
972
973 assertTrue(tempFile.getDate()>date);
974
975 if(tempFile.canChangeDate()) {
976 // Assert that changeDate succeeds (returns true)
977 assertTrue(tempFile.changeDate(date=(tempFile.getDate()-1000)));
978
979 // Assert that the getDate returns the date that was set
980 assertEquals(date, tempFile.getDate());
981 }
982 else {
983 // Assert that changeDate returns false if date cannot be changed
984 assertFalse(tempFile.changeDate(tempFile.getDate()-1000));
985 }
986 }
987
988
989 /**
990 * Tests {@link AbstractFile#getInputStream()}.
991 *
992 * @throws IOException should not happen
993 * @throws NoSuchAlgorithmException should not happen
994 */
995 public void testInputStream() throws IOException, NoSuchAlgorithmException {
996 boolean ioExceptionThrown;
997
998 // Assert that getInputStream throws an IOException when the file does not exist
999 ioExceptionThrown = false;
1000 try {
1001 tempFile.getInputStream();
1002 }
1003 catch(IOException e) {
1004 ioExceptionThrown = true;
1005 }
1006
1007 assertTrue(ioExceptionThrown);
1008
1009 // Assert that getInputStream does not throw an IOException and returns a non-null value when the file exists,
1010 // even when the file has a zero-length.
1011
1012 createFile(tempFile, 0);
1013
1014 InputStream in = tempFile.getInputStream();
1015 assertNotNull(in);
1016
1017 in.close();
1018
1019 // Test the integrity of the data returned by the InputStream on a somewhat large file
1020
1021 String md5 = createFile(tempFile, 100000);
1022
1023 in = tempFile.getInputStream();
1024 assertNotNull(in);
1025
1026 assertEquals(md5, calculateMd5(in));
1027
1028 // Assert that read methods return -1 when EOF has been reached
1029 assertEquals(-1, in.read());
1030 byte b[] = new byte[1];
1031 assertEquals(-1, in.read(b));
1032 assertEquals(-1, in.read(b, 0, 1));
1033
1034 in.close();
1035 }
1036
1037 /**
1038 * Tests {@link AbstractFile#hasRandomAccessInputStream()} and {@link AbstractFile#getRandomAccessInputStream()}.
1039 *
1040 * @throws IOException should not happen
1041 * @throws NoSuchAlgorithmException should not happen
1042 */
1043 public void testRandomAccessInputStream() throws IOException, NoSuchAlgorithmException {
1044 boolean ioExceptionThrown;
1045
1046 if(tempFile.hasRandomAccessInputStream()) {
1047 // Assert that getRandomAccessInputStream throws an IOException when the file does not exist
1048 ioExceptionThrown = false;
1049 try {
1050 tempFile.getRandomAccessInputStream();
1051 }
1052 catch(IOException e) {
1053 ioExceptionThrown = true;
1054 }
1055
1056 assertTrue(ioExceptionThrown);
1057
1058 // Assert that getRandomAccessInputStream does not throw an IOException and returns a non-null value
1059 // when the file exists
1060 createFile(tempFile, 1);
1061
1062 RandomAccessInputStream rais = tempFile.getRandomAccessInputStream();
1063
1064 assertNotNull(rais);
1065 // Ensure that the size returned by RandomAccessInputStream#getLength() matches the one returned by
1066 // AbstractFile#getSize()
1067 assertEquals(tempFile.getSize(), rais.getLength());
1068
1069 rais.close();
1070
1071 // Test the integrity of the data returned by the RandomAccessInputStream on a somewhat large file
1072
1073 String md5 = createFile(tempFile, 100000);
1074
1075 rais = tempFile.getRandomAccessInputStream();
1076 assertNotNull(rais);
1077
1078 assertEquals(md5, calculateMd5(rais));
1079
1080 // Assert that read methods return -1 when EOF has been reached
1081 assertEquals(-1, rais.read());
1082 byte b[] = new byte[1];
1083 assertEquals(-1, rais.read(b));
1084 assertEquals(-1, rais.read(b, 0, 1));
1085
1086 // Assert that readFully methods throw an EOFException
1087 boolean eofExceptionThrown = false;
1088 try { rais.readFully(b); }
1089 catch(EOFException e) {
1090 eofExceptionThrown = true;
1091 }
1092 assertTrue(eofExceptionThrown);
1093
1094 eofExceptionThrown = false;
1095 try { rais.readFully(b, 0, 1); }
1096 catch(EOFException e) {
1097 eofExceptionThrown = true;
1098 }
1099 assertTrue(eofExceptionThrown);
1100
1101 rais.close();
1102 }
1103 else {
1104 // Assert that getRandomAccessInputStream throws an IOException when such a stream cannot be provided
1105 ioExceptionThrown = false;
1106 try {
1107 tempFile.getRandomAccessInputStream();
1108 }
1109 catch(IOException e) {
1110 ioExceptionThrown = true;
1111 }
1112
1113 assertTrue(ioExceptionThrown);
1114 }
1115 }
1116
1117
1118 /**
1119 * Tests {@link AbstractFile#getOutputStream(boolean)}.
1120 *
1121 * @throws IOException should not happen
1122 * @throws NoSuchAlgorithmException should not happen
1123 */
1124 public void testOutputStream() throws IOException, NoSuchAlgorithmException {
1125 // Assert that:
1126 // - getOutputStream does not throw an IOException
1127 // - returns a non-null value
1128 // - the file exists after
1129 OutputStream out = tempFile.getOutputStream(false);
1130
1131 assertNotNull(out);
1132 assertTrue(tempFile.exists());
1133
1134 out.close();
1135
1136 // Assert that getOutputStream(false) overwrites the existing file contents (resets the file size to 0)
1137 createFile(tempFile, 1);
1138 out = tempFile.getOutputStream(false);
1139 out.close();
1140
1141 assertEquals(0, tempFile.getSize());
1142
1143 // Assert that getOutputStream(true) does not overwrite the existing file contents.
1144 // Appending to the file may not be supported, catch IOException thrown by getOutputStream(true) and only those
1145 try {
1146 createFile(tempFile, 1);
1147
1148 out = null;
1149 out = tempFile.getOutputStream(true);
1150
1151 out.write('a');
1152 out.close();
1153
1154 assertEquals(2, tempFile.getSize());
1155 }
1156 catch(IOException e) {
1157 if(out!=null)
1158 throw e; // Exception was not thrown by getOutputStream(true), re-throw it
1159 else
1160 System.out.println("testOutputStream(): looks like append is not supported, caught: "+e);
1161 }
1162
1163
1164 // Test the integrity of the OuputStream after writing a somewhat large amount of random data
1165 ChecksumOutputStream md5Out = getMd5OutputStream(tempFile.getOutputStream(false));
1166 writeRandomData(md5Out, 100000, 1000);
1167 md5Out.close();
1168
1169 assertEquals(md5Out.getChecksumString(), calculateMd5(tempFile));
1170 }
1171
1172 /**
1173 * Tests {@link AbstractFile#hasRandomAccessOutputStream()} and {@link AbstractFile#getRandomAccessOutputStream()}.
1174 *
1175 * @throws IOException should not happen
1176 * @throws NoSuchAlgorithmException should not happen
1177 */
1178 public void testRandomAccessOutputStream() throws IOException, NoSuchAlgorithmException {
1179 if(tempFile.hasRandomAccessOutputStream()) {
1180 // Assert that:
1181 // - getRandomAccessOutputStream does not throw an IOException
1182 // - returns a non-null value
1183 // - the file exists after
1184 RandomAccessOutputStream raos = tempFile.getRandomAccessOutputStream();
1185
1186 assertNotNull(raos);
1187 assertTrue(tempFile.exists());
1188
1189 raos.close();
1190
1191 // Test the integrity of the OuputStream after writing a somewhat large amount of random data
1192 ChecksumOutputStream md5Out = getMd5OutputStream(tempFile.getRandomAccessOutputStream());
1193 writeRandomData(md5Out, 100000, 1000);
1194 md5Out.close();
1195
1196 assertEquals(md5Out.getChecksumString(), calculateMd5(tempFile));
1197 tempFile.delete();
1198
1199 // Test getOffset(), seek(), getLength() and setLength()
1200
1201 // Expand the file by writing data to it, starting at 0
1202 raos = tempFile.getRandomAccessOutputStream();
1203 writeRandomData(raos, 100, 10);
1204 assertEquals(100, raos.getOffset());
1205 assertEquals(100, raos.getLength());
1206 assertEquals(100, tempFile.getSize());
1207
1208 // Overwrite the existing data, without expanding the file
1209 raos.seek(0);
1210 assertEquals(0, raos.getOffset());
1211
1212 writeRandomData(raos, 100, 10);
1213
1214 assertEquals(100, raos.getOffset());
1215 assertEquals(100, raos.getLength());
1216 assertEquals(100, tempFile.getSize());
1217
1218 // Overwrite part of the file and expand it
1219 raos.seek(50);
1220 assertEquals(50, raos.getOffset());
1221
1222 writeRandomData(raos, 100, 10);
1223
1224 assertEquals(150, raos.getOffset());
1225 assertEquals(150, raos.getLength());
1226 assertEquals(150, tempFile.getSize());
1227
1228 // Expand the file using setLength()
1229 raos.setLength(200);
1230 assertEquals(200, raos.getLength());
1231 assertEquals(200, tempFile.getSize());
1232 assertEquals(150, raos.getOffset());
1233
1234 // Truncate the file
1235 raos.setLength(100);
1236
1237 assertEquals(100, raos.getOffset());
1238 assertEquals(100, raos.getLength());
1239 assertEquals(100, tempFile.getSize());
1240
1241 raos.close();
1242 }
1243 else {
1244 // Assert that getRandomAccessOutputStream throws an IOException when such a stream cannot be provided
1245 boolean ioExceptionThrown = false;
1246 try {
1247 tempFile.getRandomAccessOutputStream();
1248 }
1249 catch(IOException e) {
1250 ioExceptionThrown = true;
1251 }
1252
1253 assertTrue(ioExceptionThrown);
1254 }
1255 }
1256
1257
1258 /**
1259 * Tests {@link AbstractFile#ls()}.
1260 *
1261 * @throws IOException should not happen
1262 */
1263 public void testLs() throws IOException {
1264 // Assert that an IOException is thrown when the file does not exist
1265 boolean ioExceptionThrown = false;
1266 try {
1267 tempFile.ls();
1268 }
1269 catch(IOException e) {
1270 ioExceptionThrown = true;
1271 }
1272
1273 assertTrue(ioExceptionThrown);
1274
1275 // Assert that an IOException is thrown when the file is not browsable
1276 tempFile.mkfile();
1277 ioExceptionThrown = false;
1278 try {
1279 tempFile.ls();
1280 }
1281 catch(IOException e) {
1282 ioExceptionThrown = true;
1283 }
1284
1285 assertTrue(ioExceptionThrown);
1286
1287 // Create an empty directory and assert that ls() does not throw an IOException and returns a zero-length array
1288 tempFile.delete();
1289 tempFile.mkdir();
1290
1291 AbstractFile children[] = tempFile.ls();
1292 assertNotNull(children);
1293 assertEquals(0, children.length);
1294
1295 // Create a child file and assert that this child (and only this child) is returned by ls(), and that the file exists
1296 AbstractFile child = tempFile.getChild("child");
1297 child.mkfile();
1298 children = tempFile.ls();
1299
1300 assertNotNull(children);
1301 assertEquals(1, children.length);
1302 assertTrue(child.equals(children[0]));
1303 assertTrue(children[0].exists());
1304 }
1305
1306 /**
1307 * Tests {@link AbstractFile#getFreeSpace()} by asserting that the returned value is either <code>-1</code>
1308 * (not available), or a positive (potentially null) value.
1309 */
1310 public void testFreeSpace() {
1311 long freeSpace = tempFile.getFreeSpace();
1312
1313 assertTrue(freeSpace>=-1);
1314
1315 // Note: it would be interesting to assert that allocating space to a file diminishes free space accordingly
1316 // but it is not possible to guarantee that free space is not altered by another process.
1317 }
1318
1319 /**
1320 * Tests {@link AbstractFile#getTotalSpace()} by asserting that the returned value is either <code>-1</code>
1321 * (not available), or a positive (potentially null) value.
1322 */
1323 public void testTotalSpace() {
1324 long totalSpace = tempFile.getFreeSpace();
1325
1326 assertTrue(totalSpace>=-1);
1327 }
1328
1329
1330 /**
1331 * Tests {@link AbstractFile#getCopyToHint(AbstractFile)} and {@link AbstractFile#copyTo(AbstractFile)}.
1332 *
1333 * @throws IOException should not happen
1334 * @throws NoSuchAlgorithmException should not happen
1335 */
1336 public void testCopyTo() throws IOException, NoSuchAlgorithmException {
1337 createFile(tempFile, 100000);
1338 AbstractFile destFile = getTemporaryFile();
1339 deleteWhenFinished(destFile); // this file will automatically be deleted if it exists when the test is over
1340
1341 // Assert that getCopyToHint(AbstractFile) returns an allowed value (one of the hint constants)
1342 int copyToHint = tempFile.getCopyToHint(destFile);
1343 assertTrue(copyToHint>=AbstractFile.SHOULD_HINT && copyToHint<=AbstractFile.MUST_NOT_HINT);
1344
1345 // Abort test if copyTo must not be called
1346 if(copyToHint==AbstractFile.MUST_NOT_HINT) {
1347 System.out.println("#copyTo(AbstractFile) not supported, skipping test.");
1348 return;
1349 }
1350
1351 // Try and copy the file, copyTo is allowed to fail gracefully and return false
1352 if(tempFile.copyTo(destFile)) { // If copyTo succeeded
1353 // Assert that the checksum of source and destination match
1354 assertContentsEquals(tempFile, destFile);
1355
1356 // At this point, we know that copyTo works (doesn't return false), at least for this destination file
1357
1358 // Assert that copyTo overwrites the destination file when it exists
1359 createFile(tempFile, 100000);
1360 tempFile.copyTo(destFile);
1361 assertContentsEquals(tempFile, destFile);
1362
1363 // Assert that copyTo fails when the source and destination files are the same
1364 destFile.delete();
1365 boolean exceptionThrown = false;
1366 try { tempFile.copyT