You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FSTest.java 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. * Copyright (C) 2012-2013, Robin Rosenberg <robin.rosenberg@dewire.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.util;
  11. import static java.time.Instant.EPOCH;
  12. import static org.junit.Assert.assertEquals;
  13. import static org.junit.Assert.assertFalse;
  14. import static org.junit.Assert.assertTrue;
  15. import static org.junit.Assume.assumeNoException;
  16. import static org.junit.Assume.assumeTrue;
  17. import java.io.File;
  18. import java.io.IOException;
  19. import java.nio.charset.Charset;
  20. import java.nio.file.Files;
  21. import java.nio.file.InvalidPathException;
  22. import java.nio.file.Path;
  23. import java.nio.file.attribute.FileTime;
  24. import java.nio.file.attribute.PosixFileAttributeView;
  25. import java.nio.file.attribute.PosixFilePermission;
  26. import java.time.Duration;
  27. import java.time.ZoneId;
  28. import java.time.format.DateTimeFormatter;
  29. import java.util.Locale;
  30. import java.util.Set;
  31. import java.util.concurrent.TimeUnit;
  32. import org.eclipse.jgit.errors.CommandFailedException;
  33. import org.eclipse.jgit.junit.MockSystemReader;
  34. import org.eclipse.jgit.junit.RepositoryTestCase;
  35. import org.eclipse.jgit.lib.RepositoryCache;
  36. import org.junit.After;
  37. import org.junit.Assume;
  38. import org.junit.Before;
  39. import org.junit.Test;
  40. public class FSTest {
  41. private File trash;
  42. @Before
  43. public void setUp() throws Exception {
  44. SystemReader.setInstance(new MockSystemReader());
  45. trash = File.createTempFile("tmp_", "");
  46. trash.delete();
  47. assertTrue("mkdir " + trash, trash.mkdir());
  48. }
  49. @After
  50. public void tearDown() throws Exception {
  51. FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY);
  52. }
  53. /**
  54. * The old File methods traverse symbolic links and look at the targets.
  55. * With symbolic links we usually want to modify/look at the link. For some
  56. * reason the executable attribute seems to always look at the target, but
  57. * for the other attributes like lastModified, hidden and exists we must
  58. * differ between the link and the target.
  59. *
  60. * @throws IOException
  61. * @throws InterruptedException
  62. */
  63. @Test
  64. public void testSymlinkAttributes() throws IOException, InterruptedException {
  65. Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
  66. FS fs = FS.DETECTED;
  67. File link = new File(trash, "a");
  68. File target = new File(trash, "b");
  69. fs.createSymLink(link, "b");
  70. assertTrue(fs.exists(link));
  71. String targetName = fs.readSymLink(link);
  72. assertEquals("b", targetName);
  73. assertTrue(fs.lastModifiedInstant(link).compareTo(EPOCH) > 0);
  74. assertTrue(fs.exists(link));
  75. assertFalse(fs.canExecute(link));
  76. // The length of a symbolic link is a length of the target file path.
  77. assertEquals(1, fs.length(link));
  78. assertFalse(fs.exists(target));
  79. assertFalse(fs.isFile(target));
  80. assertFalse(fs.isDirectory(target));
  81. assertFalse(fs.canExecute(target));
  82. RepositoryTestCase.fsTick(link);
  83. // Now create the link target
  84. FileUtils.createNewFile(target);
  85. assertTrue(fs.exists(link));
  86. assertTrue(fs.lastModifiedInstant(link).compareTo(EPOCH) > 0);
  87. assertTrue(fs.lastModifiedInstant(target)
  88. .compareTo(fs.lastModifiedInstant(link)) > 0);
  89. assertFalse(fs.canExecute(link));
  90. fs.setExecute(target, true);
  91. assertFalse(fs.canExecute(link));
  92. assumeTrue(fs.supportsExecute());
  93. assertTrue(fs.canExecute(target));
  94. }
  95. @Test
  96. public void testUnicodeFilePath() throws IOException {
  97. Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
  98. FS fs = FS.DETECTED;
  99. File link = new File(trash, "ä");
  100. File target = new File(trash, "å");
  101. try {
  102. // Check if the runtime can support Unicode file paths.
  103. link.toPath();
  104. target.toPath();
  105. } catch (InvalidPathException e) {
  106. // When executing a test with LANG environment variable set to non
  107. // UTF-8 encoding, it seems that JRE cannot handle Unicode file
  108. // paths. This happens when this test is executed in Bazel as it
  109. // unsets LANG
  110. // (https://docs.bazel.build/versions/master/test-encyclopedia.html#initial-conditions).
  111. // Skip the test if the runtime cannot handle Unicode characters.
  112. assumeNoException(e);
  113. }
  114. fs.createSymLink(link, "å");
  115. assertTrue(fs.exists(link));
  116. assertEquals("å", fs.readSymLink(link));
  117. }
  118. @Test
  119. public void testExecutableAttributes() throws Exception {
  120. FS fs = FS.DETECTED.newInstance();
  121. // If this assumption fails the test is halted and ignored.
  122. assumeTrue(fs instanceof FS_POSIX);
  123. ((FS_POSIX) fs).setUmask(0022);
  124. File f = new File(trash, "bla");
  125. assertTrue(f.createNewFile());
  126. assertFalse(fs.canExecute(f));
  127. Set<PosixFilePermission> permissions = readPermissions(f);
  128. assertTrue(!permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
  129. assertTrue(!permissions.contains(PosixFilePermission.GROUP_EXECUTE));
  130. assertTrue(!permissions.contains(PosixFilePermission.OWNER_EXECUTE));
  131. fs.setExecute(f, true);
  132. permissions = readPermissions(f);
  133. assertTrue("'owner' execute permission not set",
  134. permissions.contains(PosixFilePermission.OWNER_EXECUTE));
  135. assertTrue("'group' execute permission not set",
  136. permissions.contains(PosixFilePermission.GROUP_EXECUTE));
  137. assertTrue("'others' execute permission not set",
  138. permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
  139. ((FS_POSIX) fs).setUmask(0033);
  140. fs.setExecute(f, false);
  141. assertFalse(fs.canExecute(f));
  142. fs.setExecute(f, true);
  143. permissions = readPermissions(f);
  144. assertTrue("'owner' execute permission not set",
  145. permissions.contains(PosixFilePermission.OWNER_EXECUTE));
  146. assertFalse("'group' execute permission set",
  147. permissions.contains(PosixFilePermission.GROUP_EXECUTE));
  148. assertFalse("'others' execute permission set",
  149. permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
  150. }
  151. private Set<PosixFilePermission> readPermissions(File f) throws IOException {
  152. return Files
  153. .getFileAttributeView(f.toPath(), PosixFileAttributeView.class)
  154. .readAttributes().permissions();
  155. }
  156. @Test(expected = CommandFailedException.class)
  157. public void testReadPipePosixCommandFailure()
  158. throws CommandFailedException {
  159. FS fs = FS.DETECTED.newInstance();
  160. assumeTrue(fs instanceof FS_POSIX);
  161. FS.readPipe(fs.userHome(),
  162. new String[] { "/bin/sh", "-c", "exit 1" },
  163. Charset.defaultCharset().name());
  164. }
  165. @Test(expected = CommandFailedException.class)
  166. public void testReadPipeCommandStartFailure()
  167. throws CommandFailedException {
  168. FS fs = FS.DETECTED.newInstance();
  169. FS.readPipe(fs.userHome(),
  170. new String[] { "this-command-does-not-exist" },
  171. Charset.defaultCharset().name());
  172. }
  173. @Test
  174. public void testFsTimestampResolution() throws Exception {
  175. DateTimeFormatter formatter = DateTimeFormatter
  176. .ofPattern("uuuu-MMM-dd HH:mm:ss.nnnnnnnnn", Locale.ENGLISH)
  177. .withZone(ZoneId.systemDefault());
  178. Path dir = Files.createTempDirectory("probe-filesystem");
  179. Duration resolution = FS.getFileStoreAttributes(dir)
  180. .getFsTimestampResolution();
  181. long resolutionNs = resolution.toNanos();
  182. assertTrue(resolutionNs > 0);
  183. for (int i = 0; i < 10; i++) {
  184. Path f = null;
  185. try {
  186. f = dir.resolve("testTimestampResolution" + i);
  187. Files.createFile(f);
  188. FileUtils.touch(f);
  189. FileTime t1 = Files.getLastModifiedTime(f);
  190. TimeUnit.NANOSECONDS.sleep(resolutionNs);
  191. FileUtils.touch(f);
  192. FileTime t2 = Files.getLastModifiedTime(f);
  193. assertTrue(String.format(
  194. "expected t2=%s to be larger than t1=%s\nsince file timestamp resolution was measured to be %,d ns",
  195. formatter.format(t2.toInstant()),
  196. formatter.format(t1.toInstant()),
  197. Long.valueOf(resolutionNs)), t2.compareTo(t1) > 0);
  198. } finally {
  199. if (f != null) {
  200. Files.delete(f);
  201. }
  202. }
  203. }
  204. }
  205. // bug 548682
  206. @Test
  207. public void testRepoCacheRelativePathUnbornRepo() {
  208. assertFalse(RepositoryCache.FileKey
  209. .isGitRepository(new File("repo.git"), FS.DETECTED));
  210. }
  211. }