Bug 532572 - Undue reconcile delta on working copy creation for non-existing file
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/ElementChangeRecorderTest.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/ElementChangeRecorderTest.java
index 9f04445..52d58a1 100644
--- a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/ElementChangeRecorderTest.java
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/ElementChangeRecorderTest.java
@@ -103,30 +103,25 @@
public void test02()
{
- recorder.beginRecording(root);
- rootBody.addChild(a);
- //@formatter:off
- assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
- " A[+]: {}", recorder.endRecording().getDelta().toString());
- //@formatter:on
+ rootBody = null;
+ recorder.beginRecording(root, null, 0);
+ rootBody = new SourceElementBody();
+ assertEquals("root[+]: {}",
+ recorder.endRecording().getDelta().toString());
}
public void test03()
{
- rootBody.addChild(b);
- recorder.beginRecording(root);
- rootBody.addChild(a);
- //@formatter:off
- assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
- " A[+]: {}", recorder.endRecording().getDelta().toString());
- //@formatter:on
+ recorder.beginRecording(root, null, 0);
+ rootBody = null;
+ assertEquals("root[-]: {}",
+ recorder.endRecording().getDelta().toString());
}
public void test04()
{
- rootBody.addChild(b);
recorder.beginRecording(root);
- rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
+ rootBody.addChild(a);
//@formatter:off
assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
" A[+]: {}", recorder.endRecording().getDelta().toString());
@@ -135,29 +130,29 @@
public void test05()
{
- rootBody.addChild(a);
+ rootBody.addChild(b);
recorder.beginRecording(root);
- rootBody.removeChild(a);
+ rootBody.addChild(a);
//@formatter:off
assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
- " A[-]: {}", recorder.endRecording().getDelta().toString());
+ " A[+]: {}", recorder.endRecording().getDelta().toString());
//@formatter:on
}
public void test06()
{
- rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
+ rootBody.addChild(b);
recorder.beginRecording(root);
- rootBody.removeChild(a);
+ rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
//@formatter:off
assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
- " A[-]: {}", recorder.endRecording().getDelta().toString());
+ " A[+]: {}", recorder.endRecording().getDelta().toString());
//@formatter:on
}
public void test07()
{
- rootBody.setChildren(new SimpleSourceConstruct[] { b, a });
+ rootBody.addChild(a);
recorder.beginRecording(root);
rootBody.removeChild(a);
//@formatter:off
@@ -170,6 +165,28 @@
{
rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
recorder.beginRecording(root);
+ rootBody.removeChild(a);
+ //@formatter:off
+ assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
+ " A[-]: {}", recorder.endRecording().getDelta().toString());
+ //@formatter:on
+ }
+
+ public void test09()
+ {
+ rootBody.setChildren(new SimpleSourceConstruct[] { b, a });
+ recorder.beginRecording(root);
+ rootBody.removeChild(a);
+ //@formatter:off
+ assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
+ " A[-]: {}", recorder.endRecording().getDelta().toString());
+ //@formatter:on
+ }
+
+ public void test10()
+ {
+ rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
+ recorder.beginRecording(root);
rootBody.setChildren(new SimpleSourceConstruct[] { b, a });
//@formatter:off
assertEquals("root[*]: {CHILDREN | FINE GRAINED}\n" +
@@ -179,7 +196,7 @@
//@formatter:on
}
- public void test09()
+ public void test11()
{
rootBody.setFullRange(new TextRange(0, 0));
recorder.beginRecording(root);
@@ -189,7 +206,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test10()
+ public void test12()
{
rootBody.setFullRange(new TextRange(0, 1));
recorder.beginRecording(root);
@@ -199,7 +216,7 @@
recorder.endRecording().getDelta()));
}
- public void test11()
+ public void test13()
{
rootBody.setFullRange(new TextRange(0, 1));
Document document = new Document("a");
@@ -213,7 +230,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test12()
+ public void test14()
{
rootBody.setFullRange(new TextRange(0, 1));
Document document = new Document("a");
@@ -226,26 +243,26 @@
recorder.endRecording().getDelta()));
}
- public void test13()
- {
- recorder.beginRecording(root);
- rootBody = new SourceElementBody();
- rootBody.set(Property.get("p", String.class), "a");
- assertEquals("root[*]: {CONTENT | FINE GRAINED}",
- recorder.endRecording().getDelta().toString());
- }
-
- public void test14()
- {
- rootBody.set(Property.get("p", String.class), "a");
- recorder.beginRecording(root);
- rootBody = new SourceElementBody();
- assertEquals("root[*]: {CONTENT | FINE GRAINED}",
- recorder.endRecording().getDelta().toString());
- }
-
public void test15()
{
+ recorder.beginRecording(root);
+ rootBody = new SourceElementBody();
+ rootBody.set(Property.get("p", String.class), "a");
+ assertEquals("root[*]: {CONTENT | FINE GRAINED}",
+ recorder.endRecording().getDelta().toString());
+ }
+
+ public void test16()
+ {
+ rootBody.set(Property.get("p", String.class), "a");
+ recorder.beginRecording(root);
+ rootBody = new SourceElementBody();
+ assertEquals("root[*]: {CONTENT | FINE GRAINED}",
+ recorder.endRecording().getDelta().toString());
+ }
+
+ public void test17()
+ {
Property<String> p = Property.get("p", String.class);
rootBody.set(p, "a");
recorder.beginRecording(root);
@@ -255,7 +272,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test16()
+ public void test18()
{
Property<String[]> p = Property.get("p", String[].class);
rootBody.set(p, new String[] { "a" });
@@ -266,7 +283,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test17()
+ public void test19()
{
rootBody.set(Property.get("p1", String.class), "a");
recorder.beginRecording(root);
@@ -276,7 +293,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test18()
+ public void test20()
{
Property<String> p = Property.get("p", String.class);
rootBody.set(p, "a");
@@ -287,14 +304,14 @@
recorder.endRecording().getDelta()));
}
- public void test19()
+ public void test21()
{
recorder.beginRecording(root, null, 0);
assertEquals("root[*]: {CONTENT}",
recorder.endRecording().getDelta().toString());
}
- public void test20()
+ public void test22()
{
rootBody = null;
recorder.beginRecording(root, null, 0);
@@ -303,7 +320,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test21()
+ public void test23()
{
recorder.beginRecording(root, null, 0);
rootBody = null;
@@ -311,7 +328,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test22()
+ public void test24()
{
rootBody.addChild(a);
recorder.beginRecording(root, null, 0);
@@ -320,7 +337,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test23()
+ public void test25()
{
rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
recorder.beginRecording(root, null, 0);
@@ -329,7 +346,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test24()
+ public void test26()
{
recorder.beginRecording(root, null, 0);
rootBody = new SourceElementBody();
@@ -338,14 +355,14 @@
recorder.endRecording().getDelta().toString());
}
- public void test25()
+ public void test27()
{
recorder.beginRecording(root, null, 1);
assertTrue(ElementDeltas.isNullOrEmpty(
recorder.endRecording().getDelta()));
}
- public void test26()
+ public void test28()
{
rootBody.addChild(a);
recorder.beginRecording(root, null, 1);
@@ -357,7 +374,7 @@
//@formatter:on
}
- public void test27()
+ public void test29()
{
rootBody.addChild(a);
rootBody.addChild(b);
@@ -370,7 +387,7 @@
//@formatter:on
}
- public void test28()
+ public void test30()
{
recorder.beginRecording(root, null, 1);
rootBody = new SourceElementBody();
@@ -379,7 +396,7 @@
recorder.endRecording().getDelta().toString());
}
- public void test29()
+ public void test31()
{
rootBody.addChild(a);
recorder.beginRecording(root, null, 1);
@@ -391,7 +408,7 @@
//@formatter:on
}
- public void test30()
+ public void test32()
{
rootBody.addChild(a);
rootBody.addChild(b);
@@ -404,7 +421,7 @@
//@formatter:on
}
- public void test31()
+ public void test33()
{
rootBody.addChild(a);
rootBody.addChild(b);
@@ -417,7 +434,7 @@
//@formatter:on
}
- public void test32()
+ public void test34()
{
rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
recorder.beginRecording(root, null, 1);
@@ -432,7 +449,7 @@
//@formatter:on
}
- public void test33()
+ public void test35()
{
rootBody.setChildren(new SimpleSourceConstruct[] { a, b });
recorder.beginRecording(root, null, 2);
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/OutOfSyncSourceFileTest.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/OutOfSyncSourceFileTest.java
index 69a19d4..45b82b4 100644
--- a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/OutOfSyncSourceFileTest.java
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/OutOfSyncSourceFileTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015, 2017 1C-Soft LLC.
+ * Copyright (c) 2015, 2018 1C-Soft LLC.
* 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
@@ -50,7 +50,7 @@
preferences.putBoolean(ResourcesPlugin.PREF_AUTO_REFRESH, false);
preferences.putBoolean(ResourcesPlugin.PREF_LIGHTWEIGHT_AUTO_REFRESH,
false);
- file = setUpProject("Test001").getFile("file.txt");
+ file = setUpProject("Test001").getFile("a.foo");
localFile = file.getLocation().toFile();
sourceFile = new SimpleSourceFile(null, file.getName(), file,
new SimpleModelManager());
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/NullBodyCache.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleBodyCache.java
similarity index 73%
rename from org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/NullBodyCache.java
rename to org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleBodyCache.java
index 569c2ba..963848c 100644
--- a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/NullBodyCache.java
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleBodyCache.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 1C-Soft LLC.
+ * Copyright (c) 2018 1C-Soft LLC.
* 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
@@ -10,33 +10,40 @@
*******************************************************************************/
package org.eclipse.handly.model.impl.support;
+import java.util.HashMap;
+import java.util.Map;
+
import org.eclipse.handly.model.IElement;
/**
- * A null implementation of {@link IBodyCache} for tests.
+ * A simple body cache for tests.
*/
-class NullBodyCache
+public class SimpleBodyCache
implements IBodyCache
{
+ private Map<IElement, Object> map = new HashMap<>();
+
@Override
public Object get(IElement element)
{
- return null;
+ return map.get(element);
}
@Override
public Object peek(IElement element)
{
- return null;
+ return map.get(element);
}
@Override
public void put(IElement element, Object body)
{
+ map.put(element, body);
}
@Override
public void remove(IElement element)
{
+ map.remove(element);
}
}
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleModelManager.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleModelManager.java
index 78c0eec..ed08ff9 100644
--- a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleModelManager.java
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleModelManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 1C-Soft LLC.
+ * Copyright (c) 2017, 2018 1C-Soft LLC.
* 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
@@ -18,9 +18,9 @@
public class SimpleModelManager
implements IModelManager
{
- public IModel model = new SimpleModel();
+ public SimpleModel model = new SimpleModel();
public ElementManager elementManager = new ElementManager(
- new NullBodyCache());
+ new SimpleBodyCache());
@Override
public IModel getModel()
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleSourceFile.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleSourceFile.java
index ebe9b09..da57ea3 100644
--- a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleSourceFile.java
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/SimpleSourceFile.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015, 2017 1C-Soft LLC.
+ * Copyright (c) 2015, 2018 1C-Soft LLC.
* 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
@@ -47,6 +47,18 @@
throw new IllegalArgumentException();
}
+ /**
+ * Returns a child element with the given name.
+ * This is a handle-only method.
+ *
+ * @param name the name of the element
+ * @return the child element with the given name
+ */
+ public SimpleSourceConstruct getChild(String name)
+ {
+ return new SimpleSourceConstruct(this, name);
+ }
+
@Override
public IModelManager getModelManager_()
{
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/WorkingCopyNotificationTest.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/WorkingCopyNotificationTest.java
new file mode 100644
index 0000000..6102204
--- /dev/null
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/support/WorkingCopyNotificationTest.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2018 1C-Soft LLC.
+ * 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:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.model.impl.support;
+
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
+import static org.eclipse.handly.context.Contexts.of;
+import static org.eclipse.handly.model.IElementChangeEvent.POST_CHANGE;
+import static org.eclipse.handly.model.IElementChangeEvent.POST_RECONCILE;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.handly.buffer.Buffer;
+import org.eclipse.handly.buffer.BufferChange;
+import org.eclipse.handly.buffer.IBuffer;
+import org.eclipse.handly.context.IContext;
+import org.eclipse.handly.junit.WorkspaceTestCase;
+import org.eclipse.handly.model.Elements;
+import org.eclipse.handly.model.IElementChangeEvent;
+import org.eclipse.handly.model.impl.ISourceFileImplExtension;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
+
+/**
+ * Working copy change notification tests.
+ */
+public class WorkingCopyNotificationTest
+ extends WorkspaceTestCase
+{
+ private SimpleSourceFile sourceFile;
+ private SimpleSourceConstruct aChild;
+ private List<IElementChangeEvent> events;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ NotificationManager notificationManager = new NotificationManager();
+ notificationManager.addElementChangeListener((event) -> events.add(
+ event));
+ SimpleModelManager modelManager = new SimpleModelManager();
+ modelManager.model.context = of(INotificationManager.class,
+ notificationManager);
+ IFile file = setUpProject("Test001").getFile("a.foo");
+ sourceFile = new SimpleSourceFile(null, file.getName(), file,
+ modelManager)
+ {
+ @Override
+ public void buildSourceStructure_(IContext context,
+ IProgressMonitor monitor) throws CoreException
+ {
+ SourceElementBody body = new SourceElementBody();
+ if ("A".equals(context.get(SOURCE_CONTENTS)))
+ {
+ body.addChild(aChild);
+
+ context.get(NEW_ELEMENTS).put(aChild,
+ new SourceElementBody());
+ }
+ context.get(NEW_ELEMENTS).put(this, body);
+ };
+ };
+ aChild = sourceFile.getChild("A");
+ events = new ArrayList<>();
+ }
+
+ public void test1() throws Exception
+ {
+ sourceFile.reconcile_(EMPTY_CONTEXT, null); // not a working copy
+ assertTrue(events.isEmpty()); // -> no effect
+ }
+
+ public void test2() throws Exception
+ {
+ sourceFile.becomeWorkingCopy_(EMPTY_CONTEXT, null);
+ try
+ {
+ assertEquals(1, events.size());
+ assertEvent(0, POST_CHANGE,
+ "[Working copy] a.foo[*]: {WORKING COPY}");
+
+ sourceFile.reconcile_(EMPTY_CONTEXT, null); // no changes
+ assertEquals(1, events.size()); // -> no effect
+
+ try (IBuffer buffer = sourceFile.getBuffer_(EMPTY_CONTEXT, null))
+ {
+ buffer.applyChange(new BufferChange(new InsertEdit(0, "A")),
+ null);
+ }
+
+ sourceFile.reconcile_(EMPTY_CONTEXT, null);
+ assertEquals(2, events.size());
+ assertEvent(1, POST_RECONCILE,
+ "[Working copy] a.foo[*]: {CHILDREN | CONTENT | FINE GRAINED}\n"
+ + " A[+]: {}");
+ }
+ finally
+ {
+ sourceFile.releaseWorkingCopy_();
+ }
+ assertEquals(3, events.size());
+ assertEvent(2, POST_CHANGE, "a.foo[*]: {WORKING COPY}");
+ }
+
+ public void test3() throws Exception
+ {
+ sourceFile.becomeWorkingCopy_(of(
+ ISourceFileImplExtension.WORKING_COPY_BUFFER, new Buffer("A")),
+ null);
+ try
+ {
+ assertEquals(2, events.size());
+ assertEvent(0, POST_CHANGE,
+ "[Working copy] a.foo[*]: {WORKING COPY}");
+ assertEvent(1, POST_RECONCILE,
+ "[Working copy] a.foo[*]: {CHILDREN | CONTENT | FINE GRAINED}\n"
+ + " A[+]: {}");
+
+ sourceFile.reconcile_(of(Elements.FORCE_RECONCILING, true), null); // no changes
+ assertEquals(2, events.size()); // -> no effect
+
+ try (IBuffer buffer = sourceFile.getBuffer_(EMPTY_CONTEXT, null))
+ {
+ buffer.applyChange(new BufferChange(new DeleteEdit(0, 1)),
+ null);
+ }
+
+ sourceFile.reconcile_(EMPTY_CONTEXT, null);
+ assertEquals(3, events.size());
+ assertEvent(2, POST_RECONCILE,
+ "[Working copy] a.foo[*]: {CHILDREN | CONTENT | FINE GRAINED}\n"
+ + " A[-]: {}");
+ }
+ finally
+ {
+ sourceFile.releaseWorkingCopy_();
+ }
+ assertEquals(4, events.size());
+ assertEvent(3, POST_CHANGE, "a.foo[*]: {WORKING COPY}");
+ }
+
+ public void test4() throws Exception
+ {
+ sourceFile.getFile_().delete(true, null);
+ sourceFile.becomeWorkingCopy_(EMPTY_CONTEXT, null);
+ try
+ {
+ assertEquals(1, events.size());
+ assertEvent(0, POST_CHANGE,
+ "[Working copy] a.foo[+]: {WORKING COPY}");
+ }
+ finally
+ {
+ sourceFile.releaseWorkingCopy_();
+ }
+ assertEquals(2, events.size());
+ assertEvent(1, POST_CHANGE, "a.foo[-]: {WORKING COPY}");
+ }
+
+ private void assertEvent(int index, int type, String expectedDelta)
+ {
+ IElementChangeEvent event = events.get(index);
+ assertEquals(type, event.getType());
+ assertEquals(expectedDelta, event.getDeltas()[0].toString());
+ }
+}
diff --git a/org.eclipse.handly.tests/workspace/Test001/file.txt b/org.eclipse.handly.tests/workspace/Test001/a.foo
old mode 100644
new mode 100755
similarity index 100%
rename from org.eclipse.handly.tests/workspace/Test001/file.txt
rename to org.eclipse.handly.tests/workspace/Test001/a.foo
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/support/ISourceFileImplSupport.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/support/ISourceFileImplSupport.java
index 88eef38..404998f 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/support/ISourceFileImplSupport.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/support/ISourceFileImplSupport.java
@@ -13,6 +13,7 @@
import static org.eclipse.handly.context.Contexts.of;
import static org.eclipse.handly.context.Contexts.with;
import static org.eclipse.handly.model.Elements.CREATE_BUFFER;
+import static org.eclipse.handly.model.IElementDeltaConstants.CHANGED;
import static org.eclipse.handly.model.IElementDeltaConstants.F_WORKING_COPY;
import static org.eclipse.handly.util.ToStringOptions.FORMAT_STYLE;
import static org.eclipse.handly.util.ToStringOptions.FormatStyle.MEDIUM;
@@ -899,7 +900,7 @@
reconcileStructure(context, monitor);
IElementDelta delta = recorder.endRecording().getDelta();
- if (!ElementDeltas.isNullOrEmpty(delta))
+ if (delta != null && ElementDeltas.getKind(delta) == CHANGED)
{
Elements.getModelContext(sourceFile).get(
INotificationManager.class).fireElementChangeEvent(