Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimeon Andreev2020-06-30 07:00:22 -0400
committerAndrey Loskutov2020-07-03 12:17:01 -0400
commit03c0989a5bc07e20793e344c6ad788f76b8de370 (patch)
tree3196ecdfb44bc87c94c5029449207001050d5791
parent79f63ad5bfa3069919490bbf6567f3c766e5f2e8 (diff)
downloadeclipse.jdt.core-03c0989a5bc07e20793e344c6ad788f76b8de370.tar.gz
eclipse.jdt.core-03c0989a5bc07e20793e344c6ad788f76b8de370.tar.xz
eclipse.jdt.core-03c0989a5bc07e20793e344c6ad788f76b8de370.zip
Bug 564905 - [regression] Missing full build on .class file changesI20200705-1800I20200705-0710I20200705-0600I20200704-1800I20200704-0600I20200703-1800
If the output folder of a project is deleted in file system, while the project is closed, the project is not built on re-open/refresh. The same is true if Eclipse was closed when deleting the output folder (e.g. with a git clean). This is a regression caused by a fix for bug 563030. When State.typeLocators is persisted, instead of writing key/value pairs, value/value pairs are written. As a result, after persisting the project build state, IncrementalImageBuilder.checkForClassFileChanges() no longer detects .class file changes. It looks with keys in State.typeLocators, while the map has value->value pairs. Change-Id: I154764626d7a6ff264c0d3d4931d1689df60878e Signed-off-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
-rw-r--r--org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug564905Test.java202
-rw-r--r--org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuilderTests.java1
-rw-r--r--org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java7
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java4
4 files changed, 212 insertions, 2 deletions
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug564905Test.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug564905Test.java
new file mode 100644
index 0000000000..86f54d2d5c
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug564905Test.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * 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;
+ }
+ }
+}
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuilderTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuilderTests.java
index fbb210a593..1938645b43 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuilderTests.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuilderTests.java
@@ -548,6 +548,7 @@ public class BuilderTests extends TestCase {
Bug530366Test.class,
Bug531382Test.class,
Bug549457Test.class,
+ Bug564905Test.class,
Bug561287Test.class,
Bug562420Test.class,
LeakTestsBefore9.class,
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java
index f3ddd9a730..fed6380618 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java
@@ -120,6 +120,13 @@ public class StateTest extends BuilderTests {
State readState = JavaBuilder.readState(project, new DataInputStream(new ByteArrayInputStream(bytes)));
Map<String, ReferenceCollection> readReferences = readState.getReferences();
assertEqualLookupTables(savedState.getReferences(), readReferences);
+ assertEqualTypeLocators(savedState.typeLocators, readState.typeLocators);
+ }
+
+ private void assertEqualTypeLocators(Map<String, String> tl1, Map<String, String> tl2) {
+ assertEquals(tl1.size(), tl2.size());
+ assertEquals(tl1.toString(), tl2.toString());
+
}
private void assertEqualLookupTables(Map<String, ReferenceCollection> expectation, Map<String, ReferenceCollection> actual) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
index 737ad74602..4447a90877 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
@@ -153,7 +153,7 @@ public boolean isKnownPackage(String qualifiedPackageName) {
LinkedHashSet<String> names = new LinkedHashSet<>(this.typeLocators.size());
Set<Entry<String, String>> keyTable = this.typeLocators.entrySet();
for (Entry<String, String> entry : keyTable) {
- String packageName = entry.getValue(); // is a type name of the form p1/p2/A
+ String packageName = entry.getKey(); // is a type name of the form p1/p2/A
int last = packageName.lastIndexOf('/');
packageName = last == -1 ? null : packageName.substring(0, last);
while (packageName != null && !names.contains(packageName)) {
@@ -756,7 +756,7 @@ void write(DataOutputStream out) throws IOException {
String value = entry.getValue();
if (key != null) {
length--;
- out.writeUTF(value);
+ out.writeUTF(key);
Integer index = (Integer) internedTypeLocators.get(value);
out.writeInt(index.intValue());
}

Back to the top