blob: ea9a5fa56166b3eb5c71daacdd7878aa859cf4c8 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vfs.local;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Set;
import static com.intellij.openapi.util.io.IoTestUtil.*;
import static com.intellij.testFramework.PlatformTestUtil.assertPathsEqual;
public class SymlinkHandlingTest extends SymlinkTestCase {
public void testMissingLink() throws Exception {
File missingFile = new File(myTempDir, "missing_file");
assertTrue(missingFile.getPath(), !missingFile.exists() || missingFile.delete());
File missingLinkFile = createSymLink(missingFile.getPath(), myTempDir.getPath() + "/missing_link", false);
VirtualFile missingLinkVFile = refreshAndFind(missingLinkFile);
assertNotNull(missingLinkVFile);
assertBrokenLink(missingLinkVFile);
assertVisitedPaths(missingLinkVFile.getPath());
}
public void testSelfLink() throws Exception {
String target = new File(myTempDir.getPath(), "self_link").getPath();
File selfLinkFile = createSymLink(target, target, false);
VirtualFile selfLinkVFile = refreshAndFind(selfLinkFile);
assertNotNull(selfLinkVFile);
assertBrokenLink(selfLinkVFile);
assertVisitedPaths(selfLinkVFile.getPath());
}
public void testDotLink() throws Exception {
File dotLinkFile = createSymLink(".", myTempDir + "/dot_link");
VirtualFile dotLinkVFile = refreshAndFind(dotLinkFile);
assertNotNull(dotLinkVFile);
assertTrue(dotLinkVFile.is(VFileProperty.SYMLINK));
assertTrue(dotLinkVFile.isDirectory());
assertPathsEqual(myTempDir.getPath(), dotLinkVFile.getCanonicalPath());
assertVisitedPaths(dotLinkVFile.getPath());
}
public void testCircularLink() throws Exception {
File upDir = createTestDir(myTempDir, "sub");
File upLinkFile = createSymLink(upDir.getPath(), upDir.getPath() + "/up_link");
VirtualFile upLinkVFile = refreshAndFind(upLinkFile);
assertNotNull(upLinkVFile);
assertTrue(upLinkVFile.is(VFileProperty.SYMLINK));
assertTrue(upLinkVFile.isDirectory());
assertPathsEqual(upDir.getPath(), upLinkVFile.getCanonicalPath());
assertVisitedPaths(upDir.getPath(), upLinkVFile.getPath());
File repeatedLinksFile = new File(upDir.getPath() + StringUtil.repeat(File.separator + upLinkFile.getName(), 4));
assertTrue(repeatedLinksFile.getPath(), repeatedLinksFile.isDirectory());
VirtualFile repeatedLinksVFile = refreshAndFind(repeatedLinksFile);
assertNotNull(repeatedLinksFile.getPath(), repeatedLinksVFile);
assertTrue(repeatedLinksVFile.is(VFileProperty.SYMLINK));
assertTrue(repeatedLinksVFile.isDirectory());
assertPathsEqual(upDir.getPath(), repeatedLinksVFile.getCanonicalPath());
assertEquals(upLinkVFile.getCanonicalFile(), repeatedLinksVFile.getCanonicalFile());
}
public void testMutualRecursiveLinks() throws Exception {
File circularDir1 = createTestDir(myTempDir, "dir1");
File circularDir2 = createTestDir(myTempDir, "dir2");
File circularLink1 = createSymLink(circularDir2.getPath(), circularDir1 + "/link1");
File circularLink2 = createSymLink(circularDir1.getPath(), circularDir2 + "/link2");
VirtualFile circularLink1VFile = refreshAndFind(circularLink1);
VirtualFile circularLink2VFile = refreshAndFind(circularLink2);
assertNotNull(circularLink1VFile);
assertNotNull(circularLink2VFile);
assertVisitedPaths(circularDir1.getPath(), circularLink1.getPath(), circularLink1.getPath() + "/" + circularLink2.getName(),
circularDir2.getPath(), circularLink2.getPath(), circularLink2.getPath() + "/" + circularLink1.getName());
}
public void testDuplicateLinks() throws Exception {
File targetDir = createTestDir(myTempDir, "target");
File link1 = createSymLink(targetDir.getPath(), myTempDir + "/link1");
File link2 = createSymLink(targetDir.getPath(), myTempDir + "/link2");
assertVisitedPaths(targetDir.getPath(), link1.getPath(), link2.getPath());
}
public void testTargetIsWritable() throws Exception {
File targetFile = createTestFile(myTempDir, "target.txt");
File linkFile = createSymLink(targetFile.getPath(), myTempDir + "/link");
VirtualFile linkVFile = refreshAndFind(linkFile);
assertTrue("link=" + linkFile + ", vLink=" + linkVFile, linkVFile != null && !linkVFile.isDirectory() &&
linkVFile.is(VFileProperty.SYMLINK));
setWritableAndCheck(targetFile, true);
refresh();
assertTrue(linkVFile.getPath(), linkVFile.isWritable());
setWritableAndCheck(targetFile, false);
refresh();
assertFalse(linkVFile.getPath(), linkVFile.isWritable());
File targetDir = createTestDir(myTempDir, "target");
File linkDir = createSymLink(targetDir.getPath(), myTempDir + "/linkDir");
VirtualFile linkVDir = refreshAndFind(linkDir);
assertTrue("link=" + linkDir + ", vLink=" + linkVDir, linkVDir != null && linkVDir.isDirectory() && linkVDir.is(VFileProperty.SYMLINK));
if (!SystemInfo.isWindows) {
setWritableAndCheck(targetDir, true);
refresh();
assertTrue(linkVDir.getPath(), linkVDir.isWritable());
setWritableAndCheck(targetDir, false);
refresh();
assertFalse(linkVDir.getPath(), linkVDir.isWritable());
}
else {
assertEquals(linkVDir.getPath(), targetDir.canWrite(), linkVDir.isWritable());
}
}
private static void setWritableAndCheck(File file, boolean writable) {
assertTrue(file.getPath(), file.setWritable(writable, false));
assertEquals(file.getPath(), writable, file.canWrite());
}
public void testLinkDeleteIsSafe() throws Exception {
File targetFile = createTestFile(myTempDir, "target");
File linkFile = createSymLink(targetFile.getPath(), myTempDir + "/link");
VirtualFile linkVFile = refreshAndFind(linkFile);
assertTrue("link=" + linkFile + ", vLink=" + linkVFile, linkVFile != null && !linkVFile.isDirectory() &&
linkVFile.is(VFileProperty.SYMLINK));
AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass());
try {
linkVFile.delete(this);
}
finally {
token.finish();
}
assertFalse(linkVFile.toString(), linkVFile.isValid());
assertFalse(linkFile.exists());
assertTrue(targetFile.exists());
File targetDir = createTestDir(myTempDir, "targetDir");
File childFile = new File(targetDir, "child.txt");
assertTrue(childFile.getPath(), childFile.exists() || childFile.createNewFile());
File linkDir = createSymLink(targetDir.getPath(), myTempDir + "/linkDir");
VirtualFile linkVDir = refreshAndFind(linkDir);
assertTrue("link=" + linkDir + ", vLink=" + linkVDir,
linkVDir != null && linkVDir.isDirectory() && linkVDir.is(VFileProperty.SYMLINK) && linkVDir.getChildren().length == 1);
token = ApplicationManager.getApplication().acquireWriteActionLock(getClass());
try {
linkVDir.delete(this);
}
finally {
token.finish();
}
assertFalse(linkVDir.toString(), linkVDir.isValid());
assertFalse(linkDir.exists());
assertTrue(targetDir.exists());
assertTrue(childFile.exists());
}
public void testTransGenderRefresh() throws Exception {
File targetFile = createTestFile(myTempDir, "target");
File targetDir = createTestDir(myTempDir, "targetDir");
// file link
File link = createSymLink(targetFile.getPath(), myTempDir + "/link");
VirtualFile vFile1 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vFile1,
vFile1 != null && !vFile1.isDirectory() && vFile1.is(VFileProperty.SYMLINK));
// file link => dir
assertTrue(link.getPath(), link.delete() && link.mkdir() && link.isDirectory());
VirtualFile vFile2 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vFile2,
!vFile1.isValid() && vFile2 != null && vFile2.isDirectory() && !vFile2.is(VFileProperty.SYMLINK));
// dir => dir link
assertTrue(link.getPath(), link.delete());
link = createSymLink(targetDir.getPath(), myTempDir + "/link");
vFile1 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vFile1,
!vFile2.isValid() && vFile1 != null && vFile1.isDirectory() && vFile1.is(VFileProperty.SYMLINK));
// dir link => file
assertTrue(link.getPath(), link.delete() && link.createNewFile() && link.isFile());
vFile2 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vFile1,
!vFile1.isValid() && vFile2 != null && !vFile2.isDirectory() && !vFile2.is(VFileProperty.SYMLINK));
// file => file link
assertTrue(link.getPath(), link.delete());
link = createSymLink(targetFile.getPath(), myTempDir + "/link");
vFile1 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vFile1,
!vFile2.isValid() && vFile1 != null && !vFile1.isDirectory() && vFile1.is(VFileProperty.SYMLINK));
}
public void testDirLinkSwitch() throws Exception {
File targetDir1 = createTestDir(myTempDir, "target1");
File targetDir2 = createTestDir(myTempDir, "target2");
assertTrue(new File(targetDir1, "child1.txt").createNewFile());
assertTrue(new File(targetDir2, "child11.txt").createNewFile());
assertTrue(new File(targetDir2, "child12.txt").createNewFile());
File link = createSymLink(targetDir1.getPath(), myTempDir + "/link");
VirtualFile vLink1 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vLink1,
vLink1 != null && vLink1.isDirectory() && vLink1.is(VFileProperty.SYMLINK));
assertEquals(1, vLink1.getChildren().length);
assertPathsEqual(targetDir1.getPath(), vLink1.getCanonicalPath());
assertTrue(link.toString(), link.delete());
createSymLink(targetDir2.getPath(), myTempDir + "/" + link.getName());
refresh();
assertTrue(vLink1.isValid());
VirtualFile vLink2 = myFileSystem.findFileByIoFile(link);
assertEquals(vLink1, vLink2);
assertTrue("link=" + link + ", vLink=" + vLink2,
vLink2.isDirectory() && vLink2.is(VFileProperty.SYMLINK));
assertEquals(2, vLink2.getChildren().length);
assertPathsEqual(targetDir2.getPath(), vLink1.getCanonicalPath());
}
public void testFileLinkSwitch() throws Exception {
File target1 = createTestFile(myTempDir, "target1.txt");
FileUtil.writeToFile(target1, "some text");
File target2 = createTestFile(myTempDir, "target2.txt");
FileUtil.writeToFile(target2, "some quite another text");
File link = createSymLink(target1.getPath(), myTempDir + "/link");
VirtualFile vLink1 = refreshAndFind(link);
assertTrue("link=" + link + ", vLink=" + vLink1,
vLink1 != null && !vLink1.isDirectory() && vLink1.is(VFileProperty.SYMLINK));
assertEquals(FileUtil.loadFile(target1), VfsUtilCore.loadText(vLink1));
assertPathsEqual(target1.getPath(), vLink1.getCanonicalPath());
assertTrue(link.toString(), link.delete());
createSymLink(target2.getPath(), myTempDir + "/" + link.getName());
refresh();
assertTrue(vLink1.isValid());
VirtualFile vLink2 = myFileSystem.findFileByIoFile(link);
assertEquals(vLink1, vLink2);
assertTrue("link=" + link + ", vLink=" + vLink2,
!vLink2.isDirectory() && vLink2.is(VFileProperty.SYMLINK));
assertEquals(FileUtil.loadFile(target2), VfsUtilCore.loadText(vLink2));
assertPathsEqual(target2.getPath(), vLink1.getCanonicalPath());
}
/*
public void testContentSynchronization() throws Exception {
final File file = createTempFile(myTempDir, "file.", ".txt");
final VirtualFile vFile = refreshAndFind(file);
assertNotNull(file.getPath(), vFile);
assertTrue(file.getPath(), vFile.isValid());
final File link1 = createTempLink(file.getPath(), myTempDir + "/link1-" + file.getName());
final File link2 = createTempLink(file.getPath(), myTempDir + "/link2-" + link1.getName());
final VirtualFile vLink = refreshAndFind(link2);
assertNotNull(link2.getPath(), vLink);
assertTrue(link2.getPath(), vLink.isValid());
String fileContent = VfsUtilCore.loadText(vFile);
assertEquals("", fileContent);
String linkContent = VfsUtilCore.loadText(vLink);
assertEquals("", linkContent);
fileContent = "new content";
vFile.setBinaryContent(fileContent.getBytes());
assertEquals(fileContent.length(), vFile.getLength());
assertEquals(fileContent.length(), vLink.getLength());
linkContent = VfsUtilCore.loadText(vLink);
assertEquals(fileContent, linkContent);
linkContent = "newer content";
vLink.setBinaryContent(linkContent.getBytes());
assertEquals(linkContent.length(), vLink.getLength());
assertEquals(linkContent.length(), vFile.getLength());
fileContent = VfsUtilCore.loadText(vFile);
assertEquals(linkContent, fileContent);
final String extContent = "changed externally";
FileUtil.writeToFile(file, extContent.getBytes());
refresh();
assertEquals(extContent.length(), vFile.getLength());
assertEquals(extContent.length(), vLink.getLength());
fileContent = VfsUtilCore.loadText(vFile);
linkContent = VfsUtilCore.loadText(vLink);
assertEquals(extContent, fileContent);
assertEquals(extContent, linkContent);
}
*/
public void testTraversePathBehindLink() throws Exception {
File topDir = createTestDir(myTempDir, "top");
File subDir1 = createTestDir(topDir, "sub1");
File link = createSymLink(subDir1.getPath(), myTempDir + "/link");
VirtualFile vLink = refreshAndFind(link);
assertNotNull(link.getPath(), vLink);
File subDir2 = createTestDir(topDir, "sub2");
File subChild = createTestFile(subDir2, "subChild.txt");
VirtualFile vSubChild = refreshAndFind(subChild);
assertNotNull(subChild.getPath(), vSubChild);
String relPath = "../" + subDir2.getName() + "/" + subChild.getName();
VirtualFile vSubChildRel;
vSubChildRel = vLink.findFileByRelativePath(relPath);
assertEquals(vSubChild, vSubChildRel);
vSubChildRel = LocalFileSystem.getInstance().findFileByPath(vLink.getPath() + "/" + relPath);
assertEquals(vSubChild, vSubChildRel);
}
@Nullable
private VirtualFile refreshAndFind(File ioFile) {
refresh();
return myFileSystem.findFileByPath(ioFile.getPath());
}
private static void assertBrokenLink(@NotNull VirtualFile link) {
assertTrue(link.is(VFileProperty.SYMLINK));
assertEquals(0, link.getLength());
assertNull(link.getCanonicalPath(), link.getCanonicalPath());
}
private void assertVisitedPaths(String... expected) {
VirtualFile vDir = refreshAndFind(myTempDir);
assertNotNull(vDir);
Set<String> expectedSet = new HashSet<String>(expected.length + 1, 1);
ContainerUtil.addAll(expectedSet, FileUtil.toSystemIndependentName(myTempDir.getPath()));
ContainerUtil.addAll(expectedSet, ContainerUtil.map(expected, new Function<String, String>() {
@Override
public String fun(String path) {
return FileUtil.toSystemIndependentName(path);
}
}));
final Set<String> actualSet = new HashSet<String>();
VfsUtilCore.visitChildrenRecursively(vDir, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
actualSet.add(file.getPath());
return true;
}
});
assertEquals(expectedSet, actualSet);
}
}