/******************************************************************************* * 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.assertTrue; import java.io.File; import org.eclipse.emf.ecore.EObject; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Ignore; 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; /** * A test for the incremental parser based on the existing TestSynthesizedAttributeGrammar test. * This test makes sure the parser does not crash when it has deal with changes of operators and * operator templates. * * The trick is that we use a synthesized attribute grammar to calculate the value of an expression. * That way we can also make sure the resulting AST/model is correct. * * @author Stephan Erb */ public class TestOperatorParsingScenarios extends IncrementalParserBasedTest { private static final String LANGUAGE = "ExpressionWithSynthesizedAttributes"; private static final File TCS = ScenarioFixtureData.EXPRESSION_WITH_SYNTHESIZED_ATTRIBUTE_TCS; private static final File[] METAMODELS = { ScenarioFixtureData.EXPRESSION_WITH_SYNTHESIZED_ATTRIBUTE_METAMODEL }; @BeforeClass public static void setupParser() throws Exception { setupParser(LANGUAGE, TCS, /*useModelUpdaters*/ true, METAMODELS); } /** * Make sure the simple stuff works. */ @Test public void testSimple() throws Exception { model.replace(0, model.getLength(), "1+2"); triggerParser(); assertLatestResultEquals(3); } /** * Reparse everything. The whole model can be dropped and replaced. */ @Test public void testEntireDifferentExpressions() throws Exception { model.replace(0, model.getLength(), "1+2"); triggerParser(); assertLatestResultEquals(3); model.replace(0, model.getLength(), "3*3*3"); triggerParser(); assertLatestResultEquals(27); model.replace(0, model.getLength(), "1/1/1"); triggerParser(); assertLatestResultEquals(1); } /** * Reparse everything. New expressions are somehow similar to the * existing ones. */ @Test public void testeDifferentExpressionsWithPartialReuse() throws Exception { model.replace(0, model.getLength(), "1*2*3"); triggerParser(); assertLatestResultEquals(6); model.replace(0, model.getLength(), "1*2*3+4+5"); triggerParser(); assertLatestResultEquals(15); model.replace(0, model.getLength(), "3+2+1*2*3+4+5"); triggerParser(); // FIXME: Should be 20 but is failing due to a bug in the event // managers EventAdapter (overriding addAdapter() breaks its protocol). assertLatestResultEquals(15); } /** * Change the operands but keep the operator */ @Test public void testReplaceOperandsSimple() throws Exception { model.replace(0, model.getLength(), "2 * 2"); triggerParser(); assertLatestResultEquals(4); model.replace("2 * ".length(), "2".length(), "3"); model.replace(0, "2".length(), "3"); triggerParser(); // FIXME: Should be 9 but is failing due to a bug in the event // managers EventAdapter (overriding addAdapter() breaks its protocol). assertLatestResultEquals(4); } /** * Change the operands but keep the operator. Do spaces make any difference? */ @Test public void testReplaceOperandsSimpleOtherLookahead() throws Exception { model.replace(0, model.getLength(), "2*2"); triggerParser(); assertLatestResultEquals(4); model.replace("2*".length(), "2".length(), "3"); model.replace(0, "2".length(), "3"); triggerParser(); // FIXME: Should be 9 but is failing due to a bug in the event // managers EventAdapter (overriding addAdapter() breaks its protocol). assertLatestResultEquals(4); } /** * We have a deep subtree and replace the deepest operand. * Mind that + is left associative. */ @Test public void testReplaceOperandDeep() throws Exception { model.replace(0, model.getLength(), "1+2+3+4+5"); triggerParser(); assertLatestResultEquals(15); model.replace(0, "1".length(), "10"); triggerParser(); // FIXME: Should be 24 but is failing due to a bug in the event // managers EventAdapter (overriding addAdapter() breaks its protocol). assertLatestResultEquals(15); } /** * We have a deep subtree and replace the highest operand. * Mind that + is left associative. */ @Test public void testReplaceOperandFlat() throws Exception { model.replace(0, model.getLength(), "1+2+3+4+5"); triggerParser(); assertLatestResultEquals(15); model.replace("1+2+3+4+".length(), "5".length(), "10"); triggerParser(); // FIXME: Should be 20 but is failing due to a bug in the event // managers EventAdapter (overriding addAdapter() breaks its protocol). assertLatestResultEquals(15); } /** * Change the operator from * to /. Both have same priority and associativity. */ @Test public void testReplaceOperatorSimple() throws Exception { model.replace(0, model.getLength(), "10*5"); triggerParser(); assertLatestResultEquals(50); model.replace("10".length(), "*".length(), "/"); triggerParser(); assertLatestResultEquals(2); } /** * Change the operator from * to /. Both have same priority and associativity. * Do the same for mor than one operator. */ @Test public void testReplaceOperatorComplex() throws Exception { model.replace(0, model.getLength(), "1000*100*10*1"); triggerParser(); assertLatestResultEquals(1000000); model.replace("1000".length(), "*".length(), "/"); triggerParser(); model.replace("1000*100".length(), "*".length(), "/"); triggerParser(); model.replace("1000*100*10".length(), "*".length(), "/"); triggerParser(); assertLatestResultEquals(1); } /** * Same as above, but parser not called in between. * Transoformation has to happein in one parse run. */ @Test public void testReplaceOperatorComplexAllInOne() throws Exception { model.replace(0, model.getLength(), "1000*100*10*1"); triggerParser(); assertLatestResultEquals(1000000); model.replace("1000".length(), "*".length(), "/"); model.replace("1000*100".length(), "*".length(), "/"); model.replace("1000*100*10".length(), "*".length(), "/"); triggerParser(); assertLatestResultEquals(1); } /** * Swith from one precedence level to another. */ @Test public void testChangeOperatorAndPreceedence() throws Exception { model.replace(0, model.getLength(), "1+1"); triggerParser(); assertLatestResultEquals(2); model.replace("1".length(), "+".length(), "*"); triggerParser(); assertLatestResultEquals(1); } /** * from -1 to 5-1 */ @Ignore("See issue https://github.com/FURCAS-dev/FURCAS/issues/120") @Test public void testFromUnaryToBinary() throws Exception { model.replace(0, model.getLength(), "-1"); triggerParser(); assertLatestResultEquals(-1); model.replace(0, 0, "5"); triggerParser(); assertLatestResultEquals(4); } /** * from 5-1 to -1 */ @Ignore("See issue https://github.com/FURCAS-dev/FURCAS/issues/120") @Test public void testFromBinaryToUnary() throws Exception { model.replace(0, model.getLength(), "5-1"); triggerParser(); assertLatestResultEquals(4); model.replace(0, "5".length(), ""); triggerParser(); assertLatestResultEquals(-1); } /** * 1+(2*3) is equal to 1+2*3 */ @Test public void testRemoveParenthesisWithNoMeaning() throws Exception { model.replace(0, model.getLength(), "1+(2*3)"); triggerParser(); assertLatestResultEquals(7); model.replace("1+(2*3".length(), ")".length(), ""); triggerParser(); model.replace("1+".length(), "(".length(), ""); triggerParser(); assertLatestResultEquals(7); } /** * Same as {@link #testRemoveParenthesisWithNoMeaning()} but the other * way round. */ @Test public void testAddParenthesisWithNoMeaning() throws Exception { model.replace(0, model.getLength(), "1+2*3"); triggerParser(); assertLatestResultEquals(7); model.replace("1+2*3".length(), 0, ")"); triggerParser(); model.replace("1+".length(), 0, "("); triggerParser(); assertLatestResultEquals(7); } /** * (1+2)*3 is not equal to 1+2*3. Removing the parenthesis * requires restructuring of the AST. */ @Test public void testRemoveParenthesisWithMeaning() throws Exception { model.replace(0, model.getLength(), "(1+2)*3"); triggerParser(); assertLatestResultEquals(9); model.replace("(1+2".length(), ")".length(), ""); triggerParser(); model.replace(0, "(".length(), ""); triggerParser(); assertLatestResultEquals(7); } /** * Same as {@link #testRemoveParenthesisWithMeaning()} but the other * way round. */ @Test public void testAddParenthesisWithMeaning() throws Exception { model.replace(0, model.getLength(), "1+2*3"); triggerParser(); assertLatestResultEquals(7); model.replace("1+2".length(), 0, ")"); model.replace(0, 0, "("); triggerParser(); assertLatestResultEquals(9); } private void assertLatestResultEquals(int expected) throws Exception { assertTrue("Parsing must have succeded in order to check the result", model.getRoot().getVersion() == Version.REFERENCE); EObject exprStatement = IncrementalParserFacade.getParsingResult(model.getRoot()); EObject expression = (EObject) exprStatement.eGet(exprStatement.eClass().getEStructuralFeature("expression")); Double result = (Double) expression.eGet(expression.eClass().getEStructuralFeature("calculatedValue")); Assert.assertEquals(expected, result, 0.001); } }