Bug 185247 - JUnit tests for recursive symlinks
Test cases attached to Bug 105554 now added as plug-in tests.
Change-Id: Ib44cdc39e8f9620656349d191d51bba5107d7ed5
diff --git a/tests/org.eclipse.core.tests.resources/build.properties b/tests/org.eclipse.core.tests.resources/build.properties
index ce7b138..c8ae13c 100644
--- a/tests/org.eclipse.core.tests.resources/build.properties
+++ b/tests/org.eclipse.core.tests.resources/build.properties
@@ -19,7 +19,8 @@
about.html,\
META-INF/,\
test.xml,\
- Plugin_Testing/
+ Plugin_Testing/,\
+ resources/
src.includes = about.html
javacWarnings.resourcestests.jar=-raw,-unchecked
diff --git a/tests/org.eclipse.core.tests.resources/resources/bug185247/bug185247_LinuxTests.zip b/tests/org.eclipse.core.tests.resources/resources/bug185247/bug185247_LinuxTests.zip
new file mode 100644
index 0000000..8fc1673
--- /dev/null
+++ b/tests/org.eclipse.core.tests.resources/resources/bug185247/bug185247_LinuxTests.zip
Binary files differ
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java
index 21ef3a7..1dad556 100644
--- a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java
@@ -81,6 +81,8 @@
suite.addTest(PR_1GH2B0N_Test.suite());
suite.addTest(PR_1GHOM0N_Test.suite());
suite.addTest(Bug_530868.suite());
+ suite.addTest(Bug_185247_recursiveLinks.suite());
+ suite.addTest(Bug_185247_LinuxTests.suite());
return suite;
}
}
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_185247_LinuxTests.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_185247_LinuxTests.java
new file mode 100644
index 0000000..f9d894e
--- /dev/null
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_185247_LinuxTests.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Simeon Andreev and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Simeon Andreev - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.resources.regression;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.core.internal.resources.ProjectDescription;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.tests.resources.ResourceTest;
+
+
+/**
+ * Test cases for symbolic links in projects.
+ */
+public class Bug_185247_LinuxTests extends ResourceTest {
+
+ private static final boolean IS_LINUX = Platform.getOS().equals(Platform.OS_LINUX);
+ private final List<IProject> testProjects = new ArrayList<>();
+ private IPath testCasesLocation;
+
+ public static Test suite() {
+ return new TestSuite(Bug_185247_LinuxTests.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ IPath randomLocation = getRandomLocation();
+ deleteOnTearDown(randomLocation);
+ testCasesLocation = randomLocation.append("bug185247LinuxTests");
+ assertTrue("failed to create test location: " + testCasesLocation, testCasesLocation.toFile().mkdirs());
+ extractTestCasesArchive(testCasesLocation);
+ }
+
+ private void extractTestCasesArchive(IPath outputLocation) throws Exception {
+ if (IS_LINUX) {
+ URL testCasesArchive = Platform.getBundle("org.eclipse.core.tests.resources")
+ .getEntry("resources/bug185247/bug185247_LinuxTests.zip");
+ URL archiveLocation = FileLocator.resolve(testCasesArchive);
+ File archive = URIUtil.toFile(archiveLocation.toURI());
+ assertNotNull("cannot find archive with test cases", archive);
+ unzip(archive, outputLocation.toFile());
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ for (IProject testProject : testProjects) {
+ testProject.delete(false, true, getMonitor());
+ }
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ /**
+ *
+ */
+ public void test1_trivial() throws Exception {
+ runProjectTestCase();
+ }
+
+ /**
+ *
+ */
+ public void test2_mutual() throws Exception {
+ runProjectTestCase();
+ }
+
+ /**
+ *
+ */
+ public void test3_outside_tree() throws Exception {
+ runProjectTestCase();
+ }
+
+ /**
+ *
+ */
+ public void test5_transitive_mutual() throws Exception {
+ runProjectTestCase();
+ }
+
+ /**
+ *
+ */
+ public void test6_nonrecursive() throws Exception {
+ runProjectTestCase();
+ }
+
+ private void runProjectTestCase() throws Exception {
+ String projectName = getName();
+ // refresh should hang, if bug 105554 re-occurs
+ importProjectAndRefresh(projectName);
+ }
+
+ private void importProjectAndRefresh(String projectName) throws Exception {
+ if (IS_LINUX) {
+ IProject project = importTestProject(projectName);
+ project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());
+ }
+ }
+
+ private IProject importTestProject(String projectName) throws Exception {
+ IProject testProject = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ testProjects.add(testProject);
+ IProjectDescription projectDescription = new ProjectDescription();
+ projectDescription.setName(projectName);
+ String projectRoot = String.join(File.separator, testCasesLocation.toOSString(), "bug185247", projectName);
+ projectDescription.setLocationURI(URI.create(projectRoot));
+ testProject.create(projectDescription, getMonitor());
+ testProject.open(getMonitor());
+ assertTrue("expected project to be open: " + projectName, testProject.isAccessible());
+ return testProject;
+ }
+
+ private static void unzip(File archive, File outputDirectory) throws Exception {
+ String[] command = { "unzip", archive.toString(), "-d", outputDirectory.toString() };
+ executeCommand(command, outputDirectory);
+
+ }
+
+ private static void executeCommand(String[] command, File outputDirectory) throws Exception {
+ assertTrue("output directory does not exist: " + outputDirectory, outputDirectory.exists());
+ assertTrue("commands only availabe in Linux environment", IS_LINUX);
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+ File commandOutputFile = new File(outputDirectory, "commandOutput.txt");
+ if (!commandOutputFile.exists()) {
+ assertTrue("failed to create standard output and error file for unzip command",
+ commandOutputFile.createNewFile());
+ }
+ processBuilder.redirectOutput(commandOutputFile);
+ processBuilder.redirectError(commandOutputFile);
+ Process process = processBuilder.start();
+ int commandExitCode = process.waitFor();
+ String output = formatCommandOutput(command, commandOutputFile);
+ assertTrue("Failed to delete command output file. " + output, commandOutputFile.delete());
+ assertEquals("Failed to execute commmand. " + output, 0, commandExitCode);
+ }
+
+ private static String formatCommandOutput(String[] command, File commandOutputFile) throws IOException {
+ Path commandOutputPath = Paths.get(commandOutputFile.getAbsolutePath());
+ List<String> commandOutputLines = Files.readAllLines(commandOutputPath);
+ List<String> commandOutputHeader = Arrays.asList("Command:", Arrays.toString(command), "Output:");
+ List<String> commandToString = new ArrayList<>();
+ commandToString.addAll(commandOutputHeader);
+ commandToString.addAll(commandOutputLines);
+ String formattedOutput = String.join(System.lineSeparator(), commandToString);
+ return formattedOutput;
+ }
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_185247_recursiveLinks.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_185247_recursiveLinks.java
new file mode 100644
index 0000000..b7444aa
--- /dev/null
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_185247_recursiveLinks.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Simeon Andreev and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Simeon Andreev - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.resources.regression;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.resources.ProjectDescription;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.tests.resources.ResourceTest;
+
+
+/**
+ * Tests for recursive symbolic links in projects.
+ */
+public class Bug_185247_recursiveLinks extends ResourceTest {
+
+ private final List<IProject> testProjects = new ArrayList<>();
+
+ public static Test suite() {
+ return new TestSuite(Bug_185247_recursiveLinks.class);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ cleanUpTestProjects();
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ private void cleanUpTestProjects() throws CoreException {
+ for (IProject testProject : testProjects) {
+ testProject.delete(false, true, getMonitor());
+ }
+ }
+
+ /**
+ * Test project structure:
+ *
+ * <pre>
+ * project root
+ * |
+ * |-- directory
+ * |
+ * |-- link_current -> ./ (links "directory")
+ * </pre>
+ */
+ public void test1_linkCurrentDirectory() throws Exception {
+ CreateTestProjectStructure createSymlinks = directory -> {
+ createSymlink(directory, "link_current", "./");
+ };
+
+ runTest(createSymlinks);
+ }
+
+ /**
+ * Test project structure:
+ *
+ * <pre>
+ * project root
+ * |
+ * |-- directory
+ * |
+ * |-- link_parent -> ../ (links "project root")
+ * </pre>
+ */
+ public void test2_linkParentDirectory() throws Exception {
+ CreateTestProjectStructure createSymlinks = directory -> {
+ createSymlink(directory, "link_parent", "../");
+ };
+
+ runTest(createSymlinks);
+ }
+
+ /**
+ * Test project structure:
+ *
+ * <pre>
+ * project root
+ * |
+ * |-- directory
+ * |
+ * |-- subdirectory
+ * |
+ * |-- link_grandparent -> ../../ (links "project root")
+ * </pre>
+ */
+ public void test3_linkGrandparentDirectory() throws Exception {
+ CreateTestProjectStructure createSymlinks = directory -> {
+ File subdirectory = new File(directory, "subdirectory");
+ createDirectory(subdirectory);
+ createSymlink(subdirectory, "link_grandparent", "../../");
+ };
+
+ runTest(createSymlinks);
+ }
+
+ /**
+ * Test project structure:
+ *
+ * <pre>
+ * project root
+ * |
+ * |-- directory
+ * |
+ * |-- subdirectory1
+ * | |
+ * | |-- link_parent -> ../ (links directory)
+ * |
+ * |-- subdirectory2
+ * |
+ * |-- link_parent -> ../ (links directory)
+ * </pre>
+ */
+ public void test4_linkParentDirectoryTwice() throws Exception {
+ CreateTestProjectStructure createSymlinks = directory -> {
+ String[] subdirectoryNames = { "subdirectory1", "subdirectory2" };
+ for (String subdirectoryName : subdirectoryNames) {
+ File subdirectory = new File(directory, subdirectoryName);
+ createDirectory(subdirectory);
+ createSymlink(subdirectory, "link_parent", "../../");
+ }
+ };
+
+ runTest(createSymlinks);
+ }
+
+ /**
+ * Test project structure:
+ *
+ * <pre>
+ * project root
+ * |
+ * |-- directory
+ * |
+ * |-- subdirectory1
+ * | |
+ * | |-- link_parent -> /tmp/<random string>/bug185247recursive/test5_linkParentDirectoyTwiceWithAbsolutePath/directory
+ * |
+ * |-- subdirectory2
+ * |
+ * |-- link_parent -> /tmp/<random string>/bug185247recursive/test5_linkParentDirectoyTwiceWithAbsolutePath/directory
+ * </pre>
+ */
+ public void test5_linkParentDirectoyTwiceWithAbsolutePath() throws Exception {
+ CreateTestProjectStructure createSymlinks = directory -> {
+ String[] subdirectoryNames = { "subdirectory1", "subdirectory2" };
+ for (String subdirectoryName : subdirectoryNames) {
+ File subdirectory = new File(directory, subdirectoryName);
+ createDirectory(subdirectory);
+ createSymlink(subdirectory, "link_parent", directory.getAbsolutePath());
+ }
+ };
+
+ runTest(createSymlinks);
+ }
+
+ private void runTest(CreateTestProjectStructure createSymlinks) throws MalformedURLException, Exception {
+ if (!canCreateSymLinks()) {
+ /*
+ * we don't run this test case on platforms that have no symlinks, since we want
+ * to test recursive symlinks
+ */
+ return;
+ }
+
+ String projectName = getName();
+ IPath testRoot = getRandomLocation();
+ deleteOnTearDown(testRoot);
+ IPath projectRoot = testRoot.append("bug185247recursive").append(projectName);
+ File directory = projectRoot.append("directory").toFile();
+ createDirectory(directory);
+
+ createSymlinks.accept(directory);
+
+
+ URI projectRootLocation = URIUtil.toURI((projectRoot));
+ // refreshing the project with recursive symlinks should not hang
+ importProjectAndRefresh(projectName, projectRootLocation);
+ }
+
+ private static void createDirectory(File directory) {
+ assertTrue("failed to create test directory: " + directory, directory.mkdirs());
+ }
+
+ void createSymlink(File directory, String linkName, String linkTarget) {
+ assertTrue("symlinks not supported by platform", canCreateSymLinks());
+ boolean isDir = true;
+ createSymLink(directory, linkName, linkTarget, isDir);
+ }
+
+ private void importProjectAndRefresh(String projectName, URI projectRootLocation) throws Exception {
+ IProject project = importTestProject(projectName, projectRootLocation);
+ project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());
+ }
+
+ private IProject importTestProject(String projectName, URI projectRootLocation) throws Exception {
+ IProject testProject = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ testProjects.add(testProject);
+ IProjectDescription projectDescription = new ProjectDescription();
+ projectDescription.setName(projectName);
+ projectDescription.setLocationURI(projectRootLocation);
+ testProject.create(projectDescription, getMonitor());
+ testProject.open(getMonitor());
+ assertTrue("expected project to be open: " + projectName, testProject.isAccessible());
+ return testProject;
+ }
+
+ interface CreateTestProjectStructure extends Consumer<File> {
+ /*
+ * we give a class name for the runnable that we use to create different project
+ * structures in different tests.
+ */
+ }
+}