View Javadoc
1   /*
2    * Copyright (C) 2010, 2013 Matthias Sohn <matthias.sohn@sap.com>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.util;
45  
46  import static org.junit.Assert.assertEquals;
47  import static org.junit.Assert.assertFalse;
48  import static org.junit.Assert.assertTrue;
49  import static org.junit.Assert.fail;
50  
51  import java.io.File;
52  import java.io.IOException;
53  import java.util.regex.Matcher;
54  
55  import org.eclipse.jgit.junit.JGitTestUtil;
56  import org.junit.After;
57  import org.junit.Assume;
58  import org.junit.Before;
59  import org.junit.Test;
60  
61  public class FileUtilTest {
62  	private File trash;
63  
64  	@Before
65  	public void setUp() throws Exception {
66  		trash = File.createTempFile("tmp_", "");
67  		trash.delete();
68  		assertTrue("mkdir " + trash, trash.mkdir());
69  	}
70  
71  	@After
72  	public void tearDown() throws Exception {
73  		FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY);
74  	}
75  
76  	@Test
77  	public void testDeleteFile() throws IOException {
78  		File f = new File(trash, "test");
79  		FileUtils.createNewFile(f);
80  		FileUtils.delete(f);
81  		assertFalse(f.exists());
82  
83  		try {
84  			FileUtils.delete(f);
85  			fail("deletion of non-existing file must fail");
86  		} catch (IOException e) {
87  			// expected
88  		}
89  
90  		try {
91  			FileUtils.delete(f, FileUtils.SKIP_MISSING);
92  		} catch (IOException e) {
93  			fail("deletion of non-existing file must not fail with option SKIP_MISSING");
94  		}
95  	}
96  
97  	@Test
98  	public void testDeleteRecursive() throws IOException {
99  		File f1 = new File(trash, "test/test/a");
100 		FileUtils.mkdirs(f1.getParentFile());
101 		FileUtils.createNewFile(f1);
102 		File f2 = new File(trash, "test/test/b");
103 		FileUtils.createNewFile(f2);
104 		File d = new File(trash, "test");
105 		FileUtils.delete(d, FileUtils.RECURSIVE);
106 		assertFalse(d.exists());
107 
108 		try {
109 			FileUtils.delete(d, FileUtils.RECURSIVE);
110 			fail("recursive deletion of non-existing directory must fail");
111 		} catch (IOException e) {
112 			// expected
113 		}
114 
115 		try {
116 			FileUtils.delete(d, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
117 		} catch (IOException e) {
118 			fail("recursive deletion of non-existing directory must not fail with option SKIP_MISSING");
119 		}
120 	}
121 
122 	@Test
123 
124 	public void testDeleteRecursiveEmpty() throws IOException {
125 		File f1 = new File(trash, "test/test/a");
126 		File f2 = new File(trash, "test/a");
127 		File d1 = new File(trash, "test");
128 		File d2 = new File(trash, "test/test");
129 		File d3 = new File(trash, "test/b");
130 		FileUtils.mkdirs(f1.getParentFile());
131 		FileUtils.createNewFile(f2);
132 		FileUtils.createNewFile(f1);
133 		FileUtils.mkdirs(d3);
134 
135 		// Cannot delete hierarchy since files exist
136 		try {
137 			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY);
138 			fail("delete should fail");
139 		} catch (IOException e1) {
140 			try {
141 				FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY|FileUtils.RECURSIVE);
142 				fail("delete should fail");
143 			} catch (IOException e2) {
144 				// Everything still there
145 				assertTrue(f1.exists());
146 				assertTrue(f2.exists());
147 				assertTrue(d1.exists());
148 				assertTrue(d2.exists());
149 				assertTrue(d3.exists());
150 			}
151 		}
152 
153 		// setup: delete files, only directories left
154 		assertTrue(f1.delete());
155 		assertTrue(f2.delete());
156 
157 		// Shall not delete hierarchy without recursive
158 		try {
159 			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY);
160 			fail("delete should fail");
161 		} catch (IOException e2) {
162 			// Everything still there
163 			assertTrue(d1.exists());
164 			assertTrue(d2.exists());
165 			assertTrue(d3.exists());
166 		}
167 
168 		// Now delete the empty hierarchy
169 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
170 				| FileUtils.RECURSIVE);
171 		assertFalse(d2.exists());
172 
173 		// Will fail to delete non-existing without SKIP_MISSING
174 		try {
175 			FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY);
176 			fail("Cannot delete non-existent entity");
177 		} catch (IOException e) {
178 			// ok
179 		}
180 
181 		// ..with SKIP_MISSING there is no exception
182 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
183 				| FileUtils.SKIP_MISSING);
184 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
185 				| FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
186 
187 		// essentially the same, using IGNORE_ERRORS
188 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
189 				| FileUtils.IGNORE_ERRORS);
190 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
191 				| FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS);
192 	}
193 
194 	@Test
195 	public void testDeleteRecursiveEmptyNeedsToCheckFilesFirst()
196 			throws IOException {
197 		File d1 = new File(trash, "test");
198 		File d2 = new File(trash, "test/a");
199 		File d3 = new File(trash, "test/b");
200 		File f1 = new File(trash, "test/c");
201 		File d4 = new File(trash, "test/d");
202 		FileUtils.mkdirs(d1);
203 		FileUtils.mkdirs(d2);
204 		FileUtils.mkdirs(d3);
205 		FileUtils.mkdirs(d4);
206 		FileUtils.createNewFile(f1);
207 
208 		// Cannot delete hierarchy since file exists
209 		try {
210 			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY
211 					| FileUtils.RECURSIVE);
212 			fail("delete should fail");
213 		} catch (IOException e) {
214 			// Everything still there
215 			assertTrue(f1.exists());
216 			assertTrue(d1.exists());
217 			assertTrue(d2.exists());
218 			assertTrue(d3.exists());
219 			assertTrue(d4.exists());
220 		}
221 	}
222 
223 	@Test
224 	public void testDeleteRecursiveEmptyDirectoriesOnlyButIsFile()
225 			throws IOException {
226 		File f1 = new File(trash, "test/test/a");
227 		FileUtils.mkdirs(f1.getParentFile());
228 		FileUtils.createNewFile(f1);
229 		try {
230 			FileUtils.delete(f1, FileUtils.EMPTY_DIRECTORIES_ONLY);
231 			fail("delete should fail");
232 		} catch (IOException e) {
233 			assertTrue(f1.exists());
234 		}
235 	}
236 
237 	@Test
238 	public void testMkdir() throws IOException {
239 		File d = new File(trash, "test");
240 		FileUtils.mkdir(d);
241 		assertTrue(d.exists() && d.isDirectory());
242 
243 		try {
244 			FileUtils.mkdir(d);
245 			fail("creation of existing directory must fail");
246 		} catch (IOException e) {
247 			// expected
248 		}
249 
250 		FileUtils.mkdir(d, true);
251 		assertTrue(d.exists() && d.isDirectory());
252 
253 		assertTrue(d.delete());
254 		File f = new File(trash, "test");
255 		FileUtils.createNewFile(f);
256 		try {
257 			FileUtils.mkdir(d);
258 			fail("creation of directory having same path as existing file must"
259 					+ " fail");
260 		} catch (IOException e) {
261 			// expected
262 		}
263 		assertTrue(f.delete());
264 	}
265 
266 	@Test
267 	public void testMkdirs() throws IOException {
268 		File root = new File(trash, "test");
269 		assertTrue(root.mkdir());
270 
271 		File d = new File(root, "test/test");
272 		FileUtils.mkdirs(d);
273 		assertTrue(d.exists() && d.isDirectory());
274 
275 		try {
276 			FileUtils.mkdirs(d);
277 			fail("creation of existing directory hierarchy must fail");
278 		} catch (IOException e) {
279 			// expected
280 		}
281 
282 		FileUtils.mkdirs(d, true);
283 		assertTrue(d.exists() && d.isDirectory());
284 
285 		FileUtils.delete(root, FileUtils.RECURSIVE);
286 		File f = new File(trash, "test");
287 		FileUtils.createNewFile(f);
288 		try {
289 			FileUtils.mkdirs(d);
290 			fail("creation of directory having path conflicting with existing"
291 					+ " file must fail");
292 		} catch (IOException e) {
293 			// expected
294 		}
295 		assertTrue(f.delete());
296 	}
297 
298 	@Test
299 	public void testCreateNewFile() throws IOException {
300 		File f = new File(trash, "x");
301 		FileUtils.createNewFile(f);
302 		assertTrue(f.exists());
303 
304 		try {
305 			FileUtils.createNewFile(f);
306 			fail("creation of already existing file must fail");
307 		} catch (IOException e) {
308 			// expected
309 		}
310 
311 		FileUtils.delete(f);
312 	}
313 
314 	@Test
315 	public void testDeleteEmptyTreeOk() throws IOException {
316 		File t = new File(trash, "t");
317 		FileUtils.mkdir(t);
318 		FileUtils.mkdir(new File(t, "d"));
319 		FileUtils.mkdir(new File(new File(t, "d"), "e"));
320 		FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE);
321 		assertFalse(t.exists());
322 	}
323 
324 	@Test
325 	public void testDeleteNotEmptyTreeNotOk() throws IOException {
326 		File t = new File(trash, "t");
327 		FileUtils.mkdir(t);
328 		FileUtils.mkdir(new File(t, "d"));
329 		File f = new File(new File(t, "d"), "f");
330 		FileUtils.createNewFile(f);
331 		FileUtils.mkdir(new File(new File(t, "d"), "e"));
332 		try {
333 			FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE);
334 			fail("expected failure to delete f");
335 		} catch (IOException e) {
336 			assertTrue(e.getMessage().endsWith(f.getAbsolutePath()));
337 		}
338 		assertTrue(t.exists());
339 	}
340 
341 	@Test
342 	public void testDeleteNotEmptyTreeNotOkButIgnoreFail() throws IOException {
343 		File t = new File(trash, "t");
344 		FileUtils.mkdir(t);
345 		FileUtils.mkdir(new File(t, "d"));
346 		File f = new File(new File(t, "d"), "f");
347 		FileUtils.createNewFile(f);
348 		File e = new File(new File(t, "d"), "e");
349 		FileUtils.mkdir(e);
350 		FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE
351 				| FileUtils.IGNORE_ERRORS);
352 		// Should have deleted as much as possible, but not all
353 		assertTrue(t.exists());
354 		assertTrue(f.exists());
355 		assertFalse(e.exists());
356 	}
357 
358 	@Test
359 	public void testRenameOverNonExistingFile() throws IOException {
360 		File d = new File(trash, "d");
361 		FileUtils.mkdirs(d);
362 		File f1 = new File(trash, "d/f");
363 		File f2 = new File(trash, "d/g");
364 		JGitTestUtil.write(f1, "f1");
365 		// test
366 		FileUtils.rename(f1, f2);
367 		assertFalse(f1.exists());
368 		assertTrue(f2.exists());
369 		assertEquals("f1", JGitTestUtil.read(f2));
370 	}
371 
372 	@Test
373 	public void testRenameOverExistingFile() throws IOException {
374 		File d = new File(trash, "d");
375 		FileUtils.mkdirs(d);
376 		File f1 = new File(trash, "d/f");
377 		File f2 = new File(trash, "d/g");
378 		JGitTestUtil.write(f1, "f1");
379 		JGitTestUtil.write(f2, "f2");
380 		// test
381 		FileUtils.rename(f1, f2);
382 		assertFalse(f1.exists());
383 		assertTrue(f2.exists());
384 		assertEquals("f1", JGitTestUtil.read(f2));
385 	}
386 
387 	@Test
388 	public void testRenameOverExistingNonEmptyDirectory() throws IOException {
389 		File d = new File(trash, "d");
390 		FileUtils.mkdirs(d);
391 		File f1 = new File(trash, "d/f");
392 		File f2 = new File(trash, "d/g");
393 		File d1 = new File(trash, "d/g/h/i");
394 		File f3 = new File(trash, "d/g/h/f");
395 		FileUtils.mkdirs(d1);
396 		JGitTestUtil.write(f1, "f1");
397 		JGitTestUtil.write(f3, "f3");
398 		// test
399 		try {
400 			FileUtils.rename(f1, f2);
401 			fail("rename to non-empty directory should fail");
402 		} catch (IOException e) {
403 			assertEquals("f1", JGitTestUtil.read(f1)); // untouched source
404 			assertEquals("f3", JGitTestUtil.read(f3)); // untouched
405 			// empty directories within f2 may or may not have been deleted
406 		}
407 	}
408 
409 	@Test
410 	public void testRenameOverExistingEmptyDirectory() throws IOException {
411 		File d = new File(trash, "d");
412 		FileUtils.mkdirs(d);
413 		File f1 = new File(trash, "d/f");
414 		File f2 = new File(trash, "d/g");
415 		File d1 = new File(trash, "d/g/h/i");
416 		FileUtils.mkdirs(d1);
417 		JGitTestUtil.write(f1, "f1");
418 		// test
419 		FileUtils.rename(f1, f2);
420 		assertFalse(f1.exists());
421 		assertTrue(f2.exists());
422 		assertEquals("f1", JGitTestUtil.read(f2));
423 	}
424 
425 	@Test
426 	public void testCreateSymlink() throws IOException {
427 		FS fs = FS.DETECTED;
428 		// show test as ignored if the FS doesn't support symlinks
429 		Assume.assumeTrue(fs.supportsSymlinks());
430 		fs.createSymLink(new File(trash, "x"), "y");
431 		String target = fs.readSymLink(new File(trash, "x"));
432 		assertEquals("y", target);
433 	}
434 
435 	@Test
436 	public void testCreateSymlinkOverrideExisting() throws IOException {
437 		FS fs = FS.DETECTED;
438 		// show test as ignored if the FS doesn't support symlinks
439 		Assume.assumeTrue(fs.supportsSymlinks());
440 		File file = new File(trash, "x");
441 		fs.createSymLink(file, "y");
442 		String target = fs.readSymLink(file);
443 		assertEquals("y", target);
444 		fs.createSymLink(file, "z");
445 		target = fs.readSymLink(file);
446 		assertEquals("z", target);
447 	}
448 
449 	@Test
450 	public void testRelativize_doc() {
451 		// This is the javadoc example
452 		String base = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\project");
453 		String other = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\another_project\\pom.xml");
454 		String expected = toOSPathString("..\\another_project\\pom.xml");
455 
456 		String actual = FileUtils.relativize(base, other);
457 		assertEquals(expected, actual);
458 	}
459 
460 	@Test
461 	public void testRelativize_mixedCase() {
462 		SystemReader systemReader = SystemReader.getInstance();
463 		String base = toOSPathString("C:\\git\\jgit");
464 		String other = toOSPathString("C:\\Git\\test\\d\\f.txt");
465 		String expectedCaseInsensitive = toOSPathString("..\\test\\d\\f.txt");
466 		String expectedCaseSensitive = toOSPathString("..\\..\\Git\\test\\d\\f.txt");
467 
468 		if (systemReader.isWindows()) {
469 			String actual = FileUtils.relativize(base, other);
470 			assertEquals(expectedCaseInsensitive, actual);
471 		} else if (systemReader.isMacOS()) {
472 			String actual = FileUtils.relativize(base, other);
473 			assertEquals(expectedCaseInsensitive, actual);
474 		} else {
475 			String actual = FileUtils.relativize(base, other);
476 			assertEquals(expectedCaseSensitive, actual);
477 		}
478 	}
479 
480 	@Test
481 	public void testRelativize_scheme() {
482 		String base = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1/file.java");
483 		String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project");
484 		// 'file.java' is treated as a folder
485 		String expected = toOSPathString("../../project");
486 
487 		String actual = FileUtils.relativize(base, other);
488 		assertEquals(expected, actual);
489 	}
490 
491 	@Test
492 	public void testRelativize_equalPaths() {
493 		String base = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
494 		String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
495 		String expected = "";
496 
497 		String actual = FileUtils.relativize(base, other);
498 		assertEquals(expected, actual);
499 	}
500 
501 	@Test
502 	public void testRelativize_whitespaces() {
503 		String base = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1");
504 		String other = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1/file");
505 		String expected = "file";
506 
507 		String actual = FileUtils.relativize(base, other);
508 		assertEquals(expected, actual);
509 	}
510 
511 	private String toOSPathString(String path) {
512 		return path.replaceAll("/|\\\\",
513 				Matcher.quoteReplacement(File.separator));
514 	}
515 }