| /******************************************************************************* |
| * Copyright (c) 2020 Simeon Andreev and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Simeon Andreev - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.tests.builder; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.nio.file.FileVisitResult; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.SimpleFileVisitor; |
| import java.nio.file.attribute.BasicFileAttributes; |
| |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.tests.util.Util; |
| import junit.framework.Test; |
| |
| public class Bug564905Test extends BuilderTests { |
| |
| private static final String OUTPUT_FOLDER_NAME = "bin"; |
| |
| private String projectName; |
| private IProject project; |
| private IPath projectPath; |
| private IPath src; |
| private IFolder outputFolder; |
| private boolean oldAutoBuilding; |
| |
| public Bug564905Test(String name) { |
| super(name); |
| } |
| |
| public static Test suite() { |
| return buildTestSuite(Bug564905Test.class); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| |
| this.projectName = "Bug564905Test"; |
| this.projectPath = env.addProject(this.projectName); |
| this.project = env.getWorkspace().getRoot().getProject(this.projectName); |
| env.addExternalJars(this.projectPath, Util.getJavaClassLibs()); |
| |
| env.removePackageFragmentRoot(this.projectPath, ""); |
| String outputFolderPath = "tmp/" + OUTPUT_FOLDER_NAME; |
| this.src = env.addPackageFragmentRoot(this.projectPath, "src", null, outputFolderPath); |
| this.outputFolder = env.getWorkspace().getRoot().getFolder(this.projectPath.append(outputFolderPath)); |
| |
| this.oldAutoBuilding = env.isAutoBuilding(); |
| env.setAutoBuilding(true); |
| waitForAutoBuild(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| TestBuilderParticipant.PARTICIPANT = null; |
| env.removeProject(this.projectPath); |
| env.setAutoBuilding(this.oldAutoBuilding); |
| waitForAutoBuild(); |
| |
| super.tearDown(); |
| } |
| |
| /** |
| * Test for Bug 564905, with option {@code org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder} enabled. |
| * |
| * When the output folder of a project is removed in file system, on refresh we expect a build. |
| */ |
| public void testBug564905_recreateModifiedClassFileInOutputFolder_enabled() throws Exception { |
| enableOption_recreateModifiedClassFileInOutputFolder(); |
| assertOutputFolderEmpty(); |
| |
| addSourceAndBuild(); |
| assertOutputFolderNotEmpty(); |
| |
| deleteOutputFolderAndWaitForAutoBuild(); |
| // we enabled "recreateModifiedClassFileInOutputFolder", so we expect compile artifacts |
| assertOutputFolderNotEmpty(); |
| } |
| |
| /** |
| * Test for Bug 564905, with option {@code org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder} disabled. |
| * |
| * When the output folder of a project is removed in file system, on refresh we don't expect a build |
| * as we don't use the option {@link JavaCore#CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER}. |
| */ |
| public void testBug564905_recreateModifiedClassFileInOutputFolder_disabled() throws Exception { |
| disableOption_recreateModifiedClassFileInOutputFolder(); |
| assertOutputFolderEmpty(); |
| |
| addSourceAndBuild(); |
| assertOutputFolderNotEmpty(); |
| |
| deleteOutputFolderAndWaitForAutoBuild(); |
| // we disabled "recreateModifiedClassFileInOutputFolder", so we don't expect compile artifacts |
| assertOutputFolderEmpty(); |
| } |
| |
| private void deleteOutputFolderAndWaitForAutoBuild() throws Exception { |
| // close the project, since the bug 564905 occurs when build state is read from disk |
| this.project.close(new NullProgressMonitor()); |
| waitForAutoBuild(); |
| URI outputFolderUri = this.outputFolder.getLocationURI(); |
| // delete the output folder with file system API, so that Eclipse resources API "doesn't notice" |
| deleteFolderInFileSystem(outputFolderUri); |
| |
| // re-open the project, refresh it, then wait for auto-build; expect that something was built |
| this.project.open(new NullProgressMonitor()); |
| this.project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); |
| waitForAutoBuild(); |
| } |
| |
| private void addSourceAndBuild() { |
| IPath srcPackage = env.addPackage(this.src, "p"); |
| IFolder srcPackageFolder = env.getWorkspace().getRoot().getFolder(srcPackage); |
| assertTrue("package in source must exist", srcPackageFolder.exists()); |
| env.addClass(this.src, "p", "X", "package p;\n public interface X { void foo() { /* we want something compiled, anything works */ } }"); |
| fullBuild(this.projectPath); |
| } |
| |
| private void enableOption_recreateModifiedClassFileInOutputFolder() throws Exception { |
| setJavaProjectOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, JavaCore.ENABLED); |
| } |
| |
| private void disableOption_recreateModifiedClassFileInOutputFolder() throws Exception { |
| setJavaProjectOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, JavaCore.DISABLED); |
| } |
| |
| private void setJavaProjectOption(String optionName, String optionValue) throws Exception { |
| IJavaProject javaProject = JavaCore.create(this.project); |
| javaProject.setOption(optionName, optionValue); |
| this.project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); |
| waitForAutoBuild(); |
| } |
| |
| private void waitForAutoBuild() throws InterruptedException { |
| Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, new NullProgressMonitor()); |
| } |
| |
| private static void deleteFolderInFileSystem(URI uri) throws IOException { |
| Files.walkFileTree(Paths.get(uri), new DeleteVisitor()); |
| } |
| |
| private void assertOutputFolderEmpty() throws CoreException { |
| assertTrue("output folder must exist", this.outputFolder.exists()); |
| IResource[] outputFolderContent = this.outputFolder.members(); |
| assertEquals("output folder must be empty, instead had contents: " + toString(outputFolderContent), 0, outputFolderContent.length); |
| } |
| |
| private void assertOutputFolderNotEmpty() throws CoreException { |
| assertTrue("output folder must exist", this.outputFolder.exists()); |
| assertTrue("output folder must not be empty", this.outputFolder.members().length > 0); |
| } |
| |
| private static String toString(IResource[] resources) { |
| StringBuilder result = new StringBuilder(); |
| for (IResource resource : resources) { |
| result.append(resource.getName()); |
| result.append(System.lineSeparator()); |
| } |
| return result.toString(); |
| } |
| |
| static class DeleteVisitor extends SimpleFileVisitor<Path> { |
| @Override |
| public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |
| Files.delete(file); |
| return FileVisitResult.CONTINUE; |
| } |
| |
| @Override |
| public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { |
| if (exc != null) { |
| throw exc; |
| } |
| Files.delete(dir); |
| return FileVisitResult.CONTINUE; |
| } |
| } |
| } |