/******************************************************************************* * Copyright (c) 2011 SAP AG 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: * SAP AG - initial API and implementation ******************************************************************************/ package com.sap.furcas.runtime.parser.incremental; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import org.junit.BeforeClass; import org.junit.Test; import com.sap.furcas.metamodel.FURCAS.textblocks.Version; import com.sap.furcas.runtime.parser.incremental.testbase.IncrementalParserBasedTest; import com.sap.furcas.test.fixture.ScenarioFixtureData; import com.sap.ide.cts.parser.incremental.IncrementalParserFacade; /** * Test actions that the incremental parser might encounter within an * editor environment. The parser is called regularly to simulate the * editor which starts the parser after almost every modification. * * @author Stephan Erb */ public class TestParsingScenarios extends IncrementalParserBasedTest { private static final String LANGUAGE = "NestedScopesTestSyntax"; private static final File TCS = ScenarioFixtureData.NESTED_SCOPE_TCS; private static final File[] METAMODELS = { ScenarioFixtureData.NESTED_SCOPE_METAMODEL }; @BeforeClass public static void setupParser() throws Exception { setupParser(LANGUAGE, TCS, /*useModelUpdaters*/ true, METAMODELS); } protected ParsingResult triggerParserAndExpectReuse() { ParsingResult result = super.triggerParser(); // Within this testclass we expect full re-use of all root elements if (result.oldRoot != null) { assertEquals(result.oldRoot, result.newRoot); } return result; } @Test public void testInitialParse() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testReparseWithoutModificationsSimple() throws Exception { model.replace(0, 0, "{ { /*inner*/ } }"); triggerParserAndExpectReuse(); Object initialRootObject = IncrementalParserFacade.getParsingResult(model.getRoot()); // Just set the same text again. Everything is the same, thus re-use can be expected model.replace(0, model.getLength(), "{ { /*inner*/ } }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); assertTrue(initialRootObject == IncrementalParserFacade.getParsingResult(model.getRoot())); } @Test public void testReparseWithoutModifications() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); Object initialRootObject = IncrementalParserFacade.getParsingResult(model.getRoot()); // Just set the same text again. Everything is the same, thus re-use can be expected model.replace(0, model.getLength(), "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); assertTrue(initialRootObject == IncrementalParserFacade.getParsingResult(model.getRoot())); } @Test public void testAddWhiteSpaces() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); model.replace(0, 0, " \n "); // add whitespace at the beginning model.replace(model.getLength(), 0, " \n "); // add whitespace at the end triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddWhiteSpacesWithoutIntermediateParse() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); model.replace(0, 0, " \n "); // add whitespace at the beginning model.replace(model.getLength(), 0, " \n "); // add whitespace at the end triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddNewBlockBefore() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // add new block before the existing sub-block model.replace("{ def a; use a; ".length(), 0, "{ use a; }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddNewBlockAfter() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // add new block after the existing sub-block model.replace("{ def a; use a; { def b; use b; }".length(), 0, "{ use a; }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddNewBlockBeforeAndAfter() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // add new block after the existing sub-block model.replace("{ def a; use a; { def b; use b; }".length(), 0, "{ use a; }"); // add new block before the existing sub-block model.replace("{ def a; use a; ".length(), 0, "{ use a; }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddBlockCommentsAroundBlock() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // quote an existing block model.replace("{ def a; use a; ".length(), 0, "/*"); model.replace(model.getLength()-2, 0, "*/"); // -2 to insert just after the } closing the inner block triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddBlockCommentsAroundBlockWithIntermediateErrors() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // quote an existing block in a partial fashion. Will lead to parsing errors model.replace("{ def a; use a; ".length(), 0, "/*"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // now fix the error model.replace(model.getLength()-1, 0, "*/"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddLineComment() throws Exception { model.replace(0, 0, "{ \n def a; use a; \n { def b; use b; }\n { def c; use c; }}"); triggerParserAndExpectReuse(); model.replace("{ \n def a; use a; \n ".length(), 0, "//"); // before the { opening the first inner block triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddLineComments() throws Exception { model.replace(0, 0, "{ \n def a; use a; \n { \n def b; use b; \n } \n}"); triggerParserAndExpectReuse(); model.replace("{ \n def a; use a; \n { \n def b; use b; \n".length(), 0, "//"); // before the } closing the inner block model.replace("{ \n def a; use a; \n { \n".length(), 0, "//"); // before the content of the inner block model.replace("{ \n def a; use a; \n".length(), 0, "//"); // before the { opening the inner block triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testAddLineCommentsWithIntermediateErrors() throws Exception { model.replace(0, 0, "{ \n def a; use a; \n { \n def b; use b; \n } \n}"); triggerParserAndExpectReuse(); // same as testcase above, but parser tirggered in-between model.replace("{ \n def a; use a; \n { \n def b; use b; \n".length(), 0, "//"); // before the } closing the inner block triggerParserAndExpectReuse(); model.replace("{ \n def a; use a; \n { \n".length(), 0, "//"); // before the conent of the inner block triggerParserAndExpectReuse(); model.replace("{ \n def a; use a; \n".length(), 0, "//"); // before the { opening the inner block triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testInsertInBlock() throws Exception{ model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // Insert a new definition in the root block model.replace("{ def a; ".length(), 0, "def new;"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Insert a new usage in the sub block model.replace("{ def a; use a; { def b; ".length(), 0, "use new;"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testInsertAtBlockBoundary() throws Exception{ model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // Insert a new definition at the end of the root block model.replace("{ def a; use a; { def b; use b; } ".length(), 0, "def last; "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Insert a new definition at the end of the sub block model.replace("{ def a; use a; { def b; use b; ".length(), 0, "def last; "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Insert a new definition at the beginning of the sub block model.replace("{ def a; use a; {".length(), 0, "def first;"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Insert a new definition at the beginning of the root block model.replace("{".length(), 0, "def first;"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testInsertCharactersInBlock() throws Exception{ model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // We want to insert a new definition (def new;) but will add it via // many little steps, similar to a potential editor session model.replace("{ def a".length(), 0, " "); triggerParserAndExpectReuse(); model.replace("{ def a".length(), 0, " "); triggerParserAndExpectReuse(); model.replace("{ def a ".length(), 0, "de"); triggerParserAndExpectReuse(); model.replace("{ def a de".length(), 0, "f"); triggerParserAndExpectReuse(); model.replace("{ def a".length(), 0, ";"); triggerParserAndExpectReuse(); model.replace("{ def a; def ".length(), 0, "n"); triggerParserAndExpectReuse(); model.replace("{ def a; def n".length(), 0, "ew"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testDeleteBlock() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // Replace block contents with comment model.replace("{ def a; use a; {".length(), " def b; use b;".length(), " /* now empty */"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Remove everything from empty up to the end (but not including the } belonging to the outer block} model.replace("{ def a; use a;".length(), model.getLength() - "{ def a; use a;".length() -1, ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testDeleteBlockWithIntermediateErrors() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // Replace block contents with comment model.replace("{ def a; use a; {".length(), " def b; use b;".length(), " /* now empty */"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Remove everything from empty up to the end (including the } belonging to the outer block} // Will lead to parsing errors because outer block is not closed model.replace("{ def a; use a;".length(), model.getLength() - "{ def a; use a;".length() , ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // add missing quote. Parsing errors should go away model.replace(model.getLength(), 0, "}"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testDeleteInBlock() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); model.replace("{ def a; use a; { def b; ".length(), "use b;".length(), ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); model.replace("{ def a; use a; { ".length(), "def b;".length(), ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); model.replace("{ def a; ".length(), "use a;".length(), ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); model.replace("{ ".length(), "def a;".length(), ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testEnterAndRemoveParsingErrors() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); model.replace(0, "{ def a; ".length(), " BUG "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // re-add the required starting { model.replace(0, " BUG".length(), " { "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testEnterAndRemoveParsingErrorsViaComment() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); model.replace(0, "{ def a; ".length(), " BUG "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // Add newline after BUG model.replace(" BUG".length(), 0, "\n"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // Fix the bug by adding a comment before the problematic region model.replace(0, 0, "//"); model.replace("// BUG ".length(), 0, "{"); // re add after commented line triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Remove the comment again. Parsing problem should be back model.replace(0, "// ".length(), ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // Re-add the comment. Everything should be fine again. model.replace(0, 0, "//"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testEnterAndRemoveLexerErrors() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); model.replace("{ def a; use".length(), 0, "&%$ Unlexable"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); model.replace("{ def a; use".length(), "&%$ Unlexable".length(), " /* Removed unlexable cruft */ "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testMergeTwoBlocks() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // remove the { which starts the second block model.replace("{ def a; use a; ".length(), "{".length(), " "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // remove the last }. This will fix the parsing error and the two // blocks are finnally merged. model.replace(model.getLength()-1, "}".length(), " "); triggerParser(); // can't expect reuse. Don't know which block will survive assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testMergeTwoBlocksInstant() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // this is the same as #testMergeTwoBlocks() but without intermediate errors // remove the { which starts the second block and the last } // which originally belonged to the outer block model.replace("{ def a; use a; ".length(), "{".length(), " "); model.replace(model.getLength()-1, "}".length(), " "); triggerParser(); // can't expect reuse. Don't know which block will survive assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testMergeTwoBlocksInstant2() throws Exception { model.replace(0, 0, "{ def a; use a; { def b; use b; } }"); triggerParserAndExpectReuse(); // this is the same as #testMergeTwoBlocksInstant() but // now the inner bracket is removed. // remove the { which starts the second block and the second last } // which originally belonged to the inner block model.replace("{ def a; use a; ".length(), "{".length(), " "); model.replace(model.getLength()-"} }".length(), "}".length(), " "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSplitTwoBlocks() throws Exception { model.replace(0, 0, "{ def a; use a; def b; use b; }"); triggerParserAndExpectReuse(); // enter { after use a; to open a sub block model.replace("{ def a; use a; ".length(), 0, "{"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // close the sub-block with a }. This will fix the parsing error and lead // to the instantiation of a sub-block model.replace(model.getLength()-1, 0, " }"); //FIXME: Fails when we simply replace with "}" triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSplitTwoBlocksInner() throws Exception { model.replace(0, 0, "{ def a; use a; def b; use b; }"); triggerParserAndExpectReuse(); // enter { after def a; to open a sub block model.replace("{ def a; ".length(), 0, "{"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // close the sub-block with a }. This is similar to the testcase above, // but we make sure the created subblock lies within the // root block and not at its boundary model.replace("{ def a; { use a;".length(), 0, "}"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testMergeTwoTokens() throws Exception { model.replace(0, 0, "{ def Double; }"); triggerParserAndExpectReuse(); // add space model.replace("{ def Double".length(), 0, " "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // add new identifier after the space model.replace("{ def Double ".length(), 0, "Name"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // merge the two tokens into one by removing the space model.replace("{ def Double".length(), 1, ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSplitToken() throws Exception { model.replace(0, 0, "{ def DoubleName; }"); triggerParserAndExpectReuse(); // split token via space model.replace("{ def Double".length(), 0, " "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // fix error by morphing the token fragment into a valid definition model.replace("{ def Double".length(), 0, "; def"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSplitToken2() throws Exception { model.replace(0, 0, "{ def DoubleName; }"); triggerParserAndExpectReuse(); // split token via space model.replace("{ def Double".length(), 0, " "); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // fix error by removing one of the two token fragments model.replace("{ def ".length(), "Double".length(), ""); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testDeeplyNestedBlocks() throws Exception { model.replace(0, 0, "{ }"); triggerParserAndExpectReuse(); // add several new blocks. model.replace("{".length(), 0, "{{ def a; { {use a;} {use a;} }}}"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // morph outmost block into an inner block model.replace(0 , 0, "{\n"); model.replace(model.getLength() , 0, "\n}"); triggerParser(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // morph back to outmost block model.replace(0 , 1, ""); // delete { model.replace(model.getLength()-1 , 1, ""); // delete } triggerParser(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // remove all blocks at once. model.replace(0, model.getLength(), "{}"); triggerParser(); // Cannot expect reuse. We don't know which of the block shall be retained. assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testResetCharactsIndividually() throws Exception { model.replace(0, 0, "{ def a; use a; { { def b; use b; } } {} {} }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); String initialContent = model.get(0, model.getLength()); // Loop over the whole model. Replace each char with itself. // So basically do a no-op for (int i = 0; i < model.getLength(); i++) { model.replace(i, 1, model.get(i, 1)); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } assertEquals(initialContent, model.get(0, model.getLength())); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testReplaceCharactersWithWhiteSpace() throws Exception { model.replace(0, 0, "{ def a; use a; { { def b; use b; } } {} {} }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // Loop over the whole model. Replace each char with itself, // except the first and last bracket. Keep the { } to still have a valid model for (int i = 1; i < model.getLength()-1; i++) { model.replace(i, 1, " "); triggerParserAndExpectReuse(); // can have intermediate errors here } assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testReplaceTokenWithItself() throws Exception { model.replace(0, 0, "{ { def a; } }"); triggerParserAndExpectReuse(); // rename a to a model.replace("{ { def ".length(), "a".length(), "a"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // rename ; to ; model.replace("{ { def a".length(), ";".length(), ";"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testReplaceSubBlockWithItself() throws Exception { model.replace(0, 0, "{ { def a; } { def b; } { def c; } }"); triggerParserAndExpectReuse(); // replace the inner block with itself model.replace("{ { def a; } ".length(), "{ def b; }".length(), "{ def b; }"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSwapStatementsViaRename() throws Exception { model.replace(0, 0, "{ def a; def b; }"); triggerParserAndExpectReuse(); // rename "a" to "b" and "b" to "a". model.replace("{ def a; def ".length(), "b".length(), "a"); triggerParserAndExpectReuse(); model.replace("{ def ".length(), "a".length(), "b"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSwapStatementsViaRenameInstant() throws Exception { model.replace(0, 0, "{ def a; def b; }"); triggerParserAndExpectReuse(); // rename "a" to "b" and "b" to "a" without intermediate parsing. model.replace("{ def a; def ".length(), "b".length(), "a"); model.replace("{ def ".length(), "a".length(), "b"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSwapStatements() throws Exception { model.replace(0, 0, "{ def a; def b; }"); triggerParserAndExpectReuse(); // replace "def a" with "def b" and "def b" with "def a" model.replace("{ def a; ".length(), "def b;".length(), "def a;"); triggerParserAndExpectReuse(); model.replace("{ ".length(), "def a;".length(), "def b;"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSwapStatementsInstant() throws Exception { model.replace(0, 0, "{ def a; def b; }"); triggerParserAndExpectReuse(); // replace "def a" with "def b" and "def b" with "def a". // Without intermediate parsing. model.replace("{ def a; ".length(), "def b;".length(), "def a;"); model.replace("{ ".length(), "def a;".length(), "def b;"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSwapStatementsComplex() throws Exception { model.replace(0, 0, "{{ def a; { { {use a; {def c;} } } {use a; def d;} }}}"); triggerParserAndExpectReuse(); // swap the right most "def d" with "use a; {def c;}" model.replace("{{ def a; { { {".length(), "use a; {def c;}".length(), "def d;"); triggerParserAndExpectReuse(); model.replace("{{ def a; { { {def d; } } {use a; ".length(), "def d;".length(), "use a; {def c;}"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testSwapStatementsComplexInstant() throws Exception { model.replace(0, 0, "{{ def a; { { {use a; {def c;} } } {use a; def d;} }}}"); triggerParserAndExpectReuse(); // swap the right most "def d" with "use a; {def c;}" without intermediate parsing. model.replace("{{ def a; { { {".length(), "use a; {def c;}" .length(), "def d;"); model.replace("{{ def a; { { {def d; } } {use a; ".length(), "def d;".length(), "use a; {def c;}"); triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testMoveStatement() throws Exception { model.replace(0, 0, "{{ def a; { {use a; {def c;} } {use a;} }}}"); triggerParserAndExpectReuse(); // Move def c; to different places. // move before use a; model.replace("{{ def a; { {use a; {".length(), "def c;".length(), ""); // delete model.replace("{{ def a; { {".length(), 0, "def c;"); // insert triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // move before def a; model.replace("{{ def a; { {".length(), "def c;".length(), ""); // delete model.replace("{{".length(), 0, "def c;"); // insert triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); // move to the end before the last } model.replace("{{".length(), "def c;".length(), ""); // delete model.replace("{{ def a; { {use a; {} } {use a;} }".length(), 0, "def c;"); // insert triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } @Test public void testMoveStatementWithErrors() throws Exception { model.replace(0, 0, "{{ def a; { {use a; {def c;} } {use a;} }}}"); triggerParserAndExpectReuse(); // Move def c; out of scope. Then back again. // move behind the last closing } model.replace("{{ def a; { {use a; {".length(), "def c;".length(), ""); // delete model.replace("{{ def a; { {use a; {} } {use a;} }}}".length(), 0, "def c;"); // insert triggerParserAndExpectReuse(); // has to yield errors. Statement may not be placed there assertTrue(model.getRoot().getVersion() != Version.REFERENCE); // move to the left of the right most } model.replace("{{ def a; { {use a; {} } {use a;} }}}".length(), "def c;".length(), ""); // delete model.replace("{{ def a; { {use a; {} } {use a;} }}".length(), 0, "def c;"); // insert triggerParserAndExpectReuse(); assertTrue(model.getRoot().getVersion() == Version.REFERENCE); } }