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.Before;
58  import org.junit.Test;
59  
60  public class FileUtilTest {
61  	private File trash;
62  
63  	@Before
64  	public void setUp() throws Exception {
65  		trash = File.createTempFile("tmp_", "");
66  		trash.delete();
67  		assertTrue("mkdir " + trash, trash.mkdir());
68  	}
69  
70  	@After
71  	public void tearDown() throws Exception {
72  		FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY);
73  	}
74  
75  	@Test
76  	public void testDeleteFile() throws IOException {
77  		File f = new File(trash, "test");
78  		FileUtils.createNewFile(f);
79  		FileUtils.delete(f);
80  		assertFalse(f.exists());
81  
82  		try {
83  			FileUtils.delete(f);
84  			fail("deletion of non-existing file must fail");
85  		} catch (IOException e) {
86  			// expected
87  		}
88  
89  		try {
90  			FileUtils.delete(f, FileUtils.SKIP_MISSING);
91  		} catch (IOException e) {
92  			fail("deletion of non-existing file must not fail with option SKIP_MISSING");
93  		}
94  	}
95  
96  	@Test
97  	public void testDeleteRecursive() throws IOException {
98  		File f1 = new File(trash, "test/test/a");
99  		FileUtils.mkdirs(f1.getParentFile());
100 		FileUtils.createNewFile(f1);
101 		File f2 = new File(trash, "test/test/b");
102 		FileUtils.createNewFile(f2);
103 		File d = new File(trash, "test");
104 		FileUtils.delete(d, FileUtils.RECURSIVE);
105 		assertFalse(d.exists());
106 
107 		try {
108 			FileUtils.delete(d, FileUtils.RECURSIVE);
109 			fail("recursive deletion of non-existing directory must fail");
110 		} catch (IOException e) {
111 			// expected
112 		}
113 
114 		try {
115 			FileUtils.delete(d, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
116 		} catch (IOException e) {
117 			fail("recursive deletion of non-existing directory must not fail with option SKIP_MISSING");
118 		}
119 	}
120 
121 	@Test
122 
123 	public void testDeleteRecursiveEmpty() throws IOException {
124 		File f1 = new File(trash, "test/test/a");
125 		File f2 = new File(trash, "test/a");
126 		File d1 = new File(trash, "test");
127 		File d2 = new File(trash, "test/test");
128 		File d3 = new File(trash, "test/b");
129 		FileUtils.mkdirs(f1.getParentFile());
130 		FileUtils.createNewFile(f2);
131 		FileUtils.createNewFile(f1);
132 		FileUtils.mkdirs(d3);
133 
134 		// Cannot delete hierarchy since files exist
135 		try {
136 			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY);
137 			fail("delete should fail");
138 		} catch (IOException e1) {
139 			try {
140 				FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY|FileUtils.RECURSIVE);
141 				fail("delete should fail");
142 			} catch (IOException e2) {
143 				// Everything still there
144 				assertTrue(f1.exists());
145 				assertTrue(f2.exists());
146 				assertTrue(d1.exists());
147 				assertTrue(d2.exists());
148 				assertTrue(d3.exists());
149 			}
150 		}
151 
152 		// setup: delete files, only directories left
153 		assertTrue(f1.delete());
154 		assertTrue(f2.delete());
155 
156 		// Shall not delete hierarchy without recursive
157 		try {
158 			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY);
159 			fail("delete should fail");
160 		} catch (IOException e2) {
161 			// Everything still there
162 			assertTrue(d1.exists());
163 			assertTrue(d2.exists());
164 			assertTrue(d3.exists());
165 		}
166 
167 		// Now delete the empty hierarchy
168 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
169 				| FileUtils.RECURSIVE);
170 		assertFalse(d2.exists());
171 
172 		// Will fail to delete non-existing without SKIP_MISSING
173 		try {
174 			FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY);
175 			fail("Cannot delete non-existent entity");
176 		} catch (IOException e) {
177 			// ok
178 		}
179 
180 		// ..with SKIP_MISSING there is no exception
181 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
182 				| FileUtils.SKIP_MISSING);
183 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
184 				| FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
185 
186 		// essentially the same, using IGNORE_ERRORS
187 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
188 				| FileUtils.IGNORE_ERRORS);
189 		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
190 				| FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS);
191 	}
192 
193 	@Test
194 	public void testDeleteRecursiveEmptyNeedsToCheckFilesFirst()
195 			throws IOException {
196 		File d1 = new File(trash, "test");
197 		File d2 = new File(trash, "test/a");
198 		File d3 = new File(trash, "test/b");
199 		File f1 = new File(trash, "test/c");
200 		File d4 = new File(trash, "test/d");
201 		FileUtils.mkdirs(d1);
202 		FileUtils.mkdirs(d2);
203 		FileUtils.mkdirs(d3);
204 		FileUtils.mkdirs(d4);
205 		FileUtils.createNewFile(f1);
206 
207 		// Cannot delete hierarchy since file exists
208 		try {
209 			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY
210 					| FileUtils.RECURSIVE);
211 			fail("delete should fail");
212 		} catch (IOException e) {
213 			// Everything still there
214 			assertTrue(f1.exists());
215 			assertTrue(d1.exists());
216 			assertTrue(d2.exists());
217 			assertTrue(d3.exists());
218 			assertTrue(d4.exists());
219 		}
220 	}
221 
222 	@Test
223 	public void testDeleteRecursiveEmptyDirectoriesOnlyButIsFile()
224 			throws IOException {
225 		File f1 = new File(trash, "test/test/a");
226 		FileUtils.mkdirs(f1.getParentFile());
227 		FileUtils.createNewFile(f1);
228 		try {
229 			FileUtils.delete(f1, FileUtils.EMPTY_DIRECTORIES_ONLY);
230 			fail("delete should fail");
231 		} catch (IOException e) {
232 			assertTrue(f1.exists());
233 		}
234 	}
235 
236 	@Test
237 	public void testMkdir() throws IOException {
238 		File d = new File(trash, "test");
239 		FileUtils.mkdir(d);
240 		assertTrue(d.exists() && d.isDirectory());
241 
242 		try {
243 			FileUtils.mkdir(d);
244 			fail("creation of existing directory must fail");
245 		} catch (IOException e) {
246 			// expected
247 		}
248 
249 		FileUtils.mkdir(d, true);
250 		assertTrue(d.exists() && d.isDirectory());
251 
252 		assertTrue(d.delete());
253 		File f = new File(trash, "test");
254 		FileUtils.createNewFile(f);
255 		try {
256 			FileUtils.mkdir(d);
257 			fail("creation of directory having same path as existing file must"
258 					+ " fail");
259 		} catch (IOException e) {
260 			// expected
261 		}
262 		assertTrue(f.delete());
263 	}
264 
265 	@Test
266 	public void testMkdirs() throws IOException {
267 		File root = new File(trash, "test");
268 		assertTrue(root.mkdir());
269 
270 		File d = new File(root, "test/test");
271 		FileUtils.mkdirs(d);
272 		assertTrue(d.exists() && d.isDirectory());
273 
274 		try {
275 			FileUtils.mkdirs(d);
276 			fail("creation of existing directory hierarchy must fail");
277 		} catch (IOException e) {
278 			// expected
279 		}
280 
281 		FileUtils.mkdirs(d, true);
282 		assertTrue(d.exists() && d.isDirectory());
283 
284 		FileUtils.delete(root, FileUtils.RECURSIVE);
285 		File f = new File(trash, "test");
286 		FileUtils.createNewFile(f);
287 		try {
288 			FileUtils.mkdirs(d);
289 			fail("creation of directory having path conflicting with existing"
290 					+ " file must fail");
291 		} catch (IOException e) {
292 			// expected
293 		}
294 		assertTrue(f.delete());
295 	}
296 
297 	@Test
298 	public void testCreateNewFile() throws IOException {
299 		File f = new File(trash, "x");
300 		FileUtils.createNewFile(f);
301 		assertTrue(f.exists());
302 
303 		try {
304 			FileUtils.createNewFile(f);
305 			fail("creation of already existing file must fail");
306 		} catch (IOException e) {
307 			// expected
308 		}
309 
310 		FileUtils.delete(f);
311 	}
312 
313 	@Test
314 	public void testDeleteEmptyTreeOk() throws IOException {
315 		File t = new File(trash, "t");
316 		FileUtils.mkdir(t);
317 		FileUtils.mkdir(new File(t, "d"));
318 		FileUtils.mkdir(new File(new File(t, "d"), "e"));
319 		FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE);
320 		assertFalse(t.exists());
321 	}
322 
323 	@Test
324 	public void testDeleteNotEmptyTreeNotOk() throws IOException {
325 		File t = new File(trash, "t");
326 		FileUtils.mkdir(t);
327 		FileUtils.mkdir(new File(t, "d"));
328 		File f = new File(new File(t, "d"), "f");
329 		FileUtils.createNewFile(f);
330 		FileUtils.mkdir(new File(new File(t, "d"), "e"));
331 		try {
332 			FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE);
333 			fail("expected failure to delete f");
334 		} catch (IOException e) {
335 			assertTrue(e.getMessage().endsWith(f.getAbsolutePath()));
336 		}
337 		assertTrue(t.exists());
338 	}
339 
340 	@Test
341 	public void testDeleteNotEmptyTreeNotOkButIgnoreFail() throws IOException {
342 		File t = new File(trash, "t");
343 		FileUtils.mkdir(t);
344 		FileUtils.mkdir(new File(t, "d"));
345 		File f = new File(new File(t, "d"), "f");
346 		FileUtils.createNewFile(f);
347 		File e = new File(new File(t, "d"), "e");
348 		FileUtils.mkdir(e);
349 		FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE
350 				| FileUtils.IGNORE_ERRORS);
351 		// Should have deleted as much as possible, but not all
352 		assertTrue(t.exists());
353 		assertTrue(f.exists());
354 		assertFalse(e.exists());
355 	}
356 
357 	@Test
358 	public void testRenameOverNonExistingFile() throws IOException {
359 		File d = new File(trash, "d");
360 		FileUtils.mkdirs(d);
361 		File f1 = new File(trash, "d/f");
362 		File f2 = new File(trash, "d/g");
363 		JGitTestUtil.write(f1, "f1");
364 		// test
365 		FileUtils.rename(f1, f2);
366 		assertFalse(f1.exists());
367 		assertTrue(f2.exists());
368 		assertEquals("f1", JGitTestUtil.read(f2));
369 	}
370 
371 	@Test
372 	public void testRenameOverExistingFile() throws IOException {
373 		File d = new File(trash, "d");
374 		FileUtils.mkdirs(d);
375 		File f1 = new File(trash, "d/f");
376 		File f2 = new File(trash, "d/g");
377 		JGitTestUtil.write(f1, "f1");
378 		JGitTestUtil.write(f2, "f2");
379 		// test
380 		FileUtils.rename(f1, f2);
381 		assertFalse(f1.exists());
382 		assertTrue(f2.exists());
383 		assertEquals("f1", JGitTestUtil.read(f2));
384 	}
385 
386 	@Test
387 	public void testRenameOverExistingNonEmptyDirectory() throws IOException {
388 		File d = new File(trash, "d");
389 		FileUtils.mkdirs(d);
390 		File f1 = new File(trash, "d/f");
391 		File f2 = new File(trash, "d/g");
392 		File d1 = new File(trash, "d/g/h/i");
393 		File f3 = new File(trash, "d/g/h/f");
394 		FileUtils.mkdirs(d1);
395 		JGitTestUtil.write(f1, "f1");
396 		JGitTestUtil.write(f3, "f3");
397 		// test
398 		try {
399 			FileUtils.rename(f1, f2);
400 			fail("rename to non-empty directory should fail");
401 		} catch (IOException e) {
402 			assertEquals("f1", JGitTestUtil.read(f1)); // untouched source
403 			assertEquals("f3", JGitTestUtil.read(f3)); // untouched
404 			// empty directories within f2 may or may not have been deleted
405 		}
406 	}
407 
408 	@Test
409 	public void testRenameOverExistingEmptyDirectory() throws IOException {
410 		File d = new File(trash, "d");
411 		FileUtils.mkdirs(d);
412 		File f1 = new File(trash, "d/f");
413 		File f2 = new File(trash, "d/g");
414 		File d1 = new File(trash, "d/g/h/i");
415 		FileUtils.mkdirs(d1);
416 		JGitTestUtil.write(f1, "f1");
417 		// test
418 		FileUtils.rename(f1, f2);
419 		assertFalse(f1.exists());
420 		assertTrue(f2.exists());
421 		assertEquals("f1", JGitTestUtil.read(f2));
422 	}
423 
424 	@Test
425 	public void testCreateSymlink() throws IOException {
426 		FS fs = FS.DETECTED;
427 		try {
428 			fs.createSymLink(new File(trash, "x"), "y");
429 		} catch (IOException e) {
430 			if (fs.supportsSymlinks())
431 				fail("FS claims to support symlinks but attempt to create symlink failed");
432 			return;
433 		}
434 		assertTrue(fs.supportsSymlinks());
435 		String target = fs.readSymLink(new File(trash, "x"));
436 		assertEquals("y", target);
437 	}
438 
439 	@Test
440 	public void testRelativize_doc() {
441 		// This is the javadoc example
442 		String base = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\project");
443 		String other = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\another_project\\pom.xml");
444 		String expected = toOSPathString("..\\another_project\\pom.xml");
445 
446 		String actual = FileUtils.relativize(base, other);
447 		assertEquals(expected, actual);
448 	}
449 
450 	@Test
451 	public void testRelativize_mixedCase() {
452 		SystemReader systemReader = SystemReader.getInstance();
453 		String base = toOSPathString("C:\\git\\jgit");
454 		String other = toOSPathString("C:\\Git\\test\\d\\f.txt");
455 		String expectedCaseInsensitive = toOSPathString("..\\test\\d\\f.txt");
456 		String expectedCaseSensitive = toOSPathString("..\\..\\Git\\test\\d\\f.txt");
457 
458 		if (systemReader.isWindows()) {
459 			String actual = FileUtils.relativize(base, other);
460 			assertEquals(expectedCaseInsensitive, actual);
461 		} else if (systemReader.isMacOS()) {
462 			String actual = FileUtils.relativize(base, other);
463 			assertEquals(expectedCaseInsensitive, actual);
464 		} else {
465 			String actual = FileUtils.relativize(base, other);
466 			assertEquals(expectedCaseSensitive, actual);
467 		}
468 	}
469 
470 	@Test
471 	public void testRelativize_scheme() {
472 		String base = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1/file.java");
473 		String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project");
474 		// 'file.java' is treated as a folder
475 		String expected = toOSPathString("../../project");
476 
477 		String actual = FileUtils.relativize(base, other);
478 		assertEquals(expected, actual);
479 	}
480 
481 	@Test
482 	public void testRelativize_equalPaths() {
483 		String base = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
484 		String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
485 		String expected = "";
486 
487 		String actual = FileUtils.relativize(base, other);
488 		assertEquals(expected, actual);
489 	}
490 
491 	@Test
492 	public void testRelativize_whitespaces() {
493 		String base = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1");
494 		String other = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1/file");
495 		String expected = "file";
496 
497 		String actual = FileUtils.relativize(base, other);
498 		assertEquals(expected, actual);
499 	}
500 
501 	private String toOSPathString(String path) {
502 		return path.replaceAll("/|\\\\",
503 				Matcher.quoteReplacement(File.separator));
504 	}
505 }