Package org.auraframework.impl.expression.parser

Source Code of org.auraframework.impl.expression.parser.ExpressionParserTest

/*
* Copyright (C) 2013 salesforce.com, inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.auraframework.impl.expression.parser;

import org.auraframework.expression.Expression;
import org.auraframework.expression.ExpressionType;
import org.auraframework.expression.PropertyReference;
import org.auraframework.impl.expression.AuraImplExpressionTestCase;
import org.auraframework.impl.expression.LiteralImpl;
import org.auraframework.impl.expression.PropertyReferenceImpl;
import org.auraframework.test.annotation.UnAdaptableTest;
import org.auraframework.throwable.AuraRuntimeException;
import org.auraframework.throwable.quickfix.InvalidExpressionException;

/**
* Tests for the expression parser
*/
public class ExpressionParserTest extends AuraImplExpressionTestCase {

    public ExpressionParserTest(String name) {
        super(name);
    }

    /**
     * Function precedence respects Java conventions.
     */
    public void testPrecedence() throws Exception {
        double result = 3.5 * 280 + 53145.3145 / -2.61;
        Expression e = buildExpression("3.5 * 280 + 53145.3145 / -2.61");
        assertEquals("Unexpected expression evaluation", result, e.evaluate(null));

        result = (536 + .346) * 1.56 / 634 + 11 % 5;
        e = buildExpression("(536 + .346) * 1.56 / 634 + 11 % 5");
        assertEquals("Unexpected expression evaluation", result, e.evaluate(null));

        e = buildExpression("1 - -5");
        // this is an integer
        assertEquals("Unexpected expression evaluation", 6.0, e.evaluate(null));

        e = buildExpression("true ? true : false ? 16 : 21");
        assertEquals("Unexpected expression evaluation", true, e.evaluate(null));
    }

    /**
     * Boolean precedence respects Java conventions.
     */
    public void testBoolPrecedence() throws Exception {
        boolean compare = 324.4326 < 259 / 134.6 + 2356;
        Expression e = buildExpression("324.4326 < 259 / 134.6 + 2356");
        assertEquals("Boolean precedence not evaluated correctly", compare, e.evaluate(null));

        compare = 324.4326 < 259 / (134.6 + 2356);
        e = buildExpression("324.4326 < 259 / (134.6 + 2356)");
        assertEquals("Boolean precedence not evaluated correctly", compare, e.evaluate(null));

        e = buildExpression("324.4326 == 259 / (134.6 + 2356) && (false || true)");
        assertFalse("Boolean precedence not evaluated correctly", e.evaluate(null));

        e = buildExpression("346 > 346 ? 6541 / 21 - 77 : 235.66");
        assertEquals("Boolean precedence not evaluated correctly", 235.66, e.evaluate(null));

        e = buildExpression("false == false ? 16 : 21");
        assertEquals("Boolean precedence not evaluated correctly", 16., e.evaluate(null));

        e = buildExpression("false && false ? 16 : 21");
        assertEquals("Boolean precedence not evaluated correctly", 21., e.evaluate(null));

        e = buildExpression("true ? 16 : 21 < 20");
        assertEquals("Boolean precedence not evaluated correctly", 16., e.evaluate(null));

        e = buildExpression("! true && false");
        assertEquals("Boolean precedence not evaluated correctly", false, e.evaluate(null));
    }

    /**
     * Alternate relational operators can be parsed.
     */
    public void testAlternateNames() throws Exception {
        Expression e = buildExpression("55 eq 55");
        assertTrue("Unexpected expression evaluation using alternate relational operators", e.evaluate(null));
        e = buildExpression("55 ne 55");
        assertFalse("Unexpected expression evaluation using alternate relational operators", e.evaluate(null));
        e = buildExpression("55 ge 55");
        assertTrue("Unexpected expression evaluation using alternate relational operators", e.evaluate(null));
        e = buildExpression("55 le 55");
        assertTrue("Unexpected expression evaluation using alternate relational operators", e.evaluate(null));
        e = buildExpression("55 lt 55");
        assertFalse("Unexpected expression evaluation using alternate relational operators", e.evaluate(null));
        e = buildExpression("55 gt 55");
        assertFalse("Unexpected expression evaluation using alternate relational operators", e.evaluate(null));
    }

    /**
     * Number literals include integers, decimals, and exponents.
     */
    public void testLiteralNumbers() throws Exception {
        Expression e = buildExpression("5");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        LiteralImpl l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 5.0, l.getValue());

        e = buildExpression("5.345");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 5.345, l.getValue());

        e = buildExpression("911.");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 911., l.getValue());

        e = buildExpression(".119");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", .119, l.getValue());

        e = buildExpression("1e10");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 1e10, l.getValue());

        e = buildExpression("2.e20");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 2.e20, l.getValue());

        e = buildExpression("0e0");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 0e0, l.getValue());

        e = buildExpression("1e01");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", 1e01, l.getValue());

        e = buildExpression(".3e3");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Unexpected number literal value", .3e3, l.getValue());
    }

    /**
     * String literals require single quotes and may be empty.
     */
    public void testLiteralStrings() throws Exception {
        Expression e = buildExpression("'ahfdh'");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        LiteralImpl l = (LiteralImpl) e;
        assertEquals("ahfdh", l.getValue());

        verifyInvalidExpressionException("\"ahfdh\"", "unexpected token: '\"' at column 1 of expression: \"ahfdh\"");

        e = buildExpression("''");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("", l.getValue());
    }

    /**
     * Boolean literals are case insensitive.
     */
    public void testLiteralBools() throws Exception {
        Expression e = buildExpression("true");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        LiteralImpl l = (LiteralImpl) e;
        assertTrue("Case sensitivity of Boolean literlas failed", l.getValue());

        e = buildExpression("TRUE");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertTrue("Case sensitivity of Boolean literlas failed", l.getValue());

        e = buildExpression("false");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertFalse("Case sensitivity of Boolean literlas failed", l.getValue());

        e = buildExpression("FALSE");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertFalse("Case sensitivity of Boolean literlas failed", l.getValue());
    }

    /**
     * Property references can have multiple parts.
     */
    public void testPropertyReference() throws Exception {
        Expression e = buildExpression("im");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        PropertyReference pr = (PropertyReference) e;
        assertEquals("Unexpected root of PropertyReference", "im", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());

        e = buildExpression("im.parsing");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        pr = (PropertyReferenceImpl) e;
        assertEquals("Unexpected root of PropertyReference", "im", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming", "parsing", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());

        e = buildExpression("im.not.nullish");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        pr = (PropertyReferenceImpl) e;
        assertEquals("Unexpected root of PropertyReference", "im", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming", "not", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming twice", "nullish", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());

        e = buildExpression("_i.am_very._.readable_");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        pr = (PropertyReferenceImpl) e;
        assertEquals("Unexpected root of PropertyReference", "_i", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming", "am_very", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming twice", "_", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming thrice", "readable_", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());
    }

    /**
     * Property references can access array members
     */
    public void testArrayAccessors() throws Exception {
        Expression e = buildExpression("im[0]");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        PropertyReference pr = (PropertyReference) e;
        assertEquals("Unexpected root of PropertyReference", "im", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unable to access array member via PropertyReference", "0", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());

        e = buildExpression("im.an[3151345]");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        pr = (PropertyReference) e;
        assertEquals("Unexpected root of PropertyReference", "im", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming", "an", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unable to access array member via PropertyReference after stemming", "3151345", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());

        e = buildExpression("im.an[3151345].array");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        pr = (PropertyReference) e;
        assertEquals("Unexpected root of PropertyReference", "im", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming", "an", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unable to access array member via PropertyReference after stemming", "3151345", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unexpected root of PropertyReference after stemming and accessing array", "array", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());

        e = buildExpression("multi[1][364]");
        assertEquals("Unexpected expression type", ExpressionType.PROPERTY, e.getExpressionType());
        pr = (PropertyReference) e;
        assertEquals("Unexpected root of PropertyReference", "multi", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unable to access array member via PropertyReference", "1", pr.getRoot());
        pr = pr.getStem();
        assertEquals("Unable to access second array member via PropertyReference", "364", pr.getRoot());
        assertNull("Stemming end of PropertyReference should return null", pr.getStem());
    }

    /**
     * Array access negative test cases
     */
    public void testInvalidArrayAccessor() throws Exception {
        verifyInvalidExpressionException("[21]", "unexpected token: a left square bracket");
        verifyInvalidExpressionException("blah[letters]", "expecting a positive integer, found 'letters'");
        verifyInvalidExpressionException("postfix[5$]", "expecting a right square bracket, found '$'");
        verifyInvalidExpressionException("prefix[$2]", "expecting a positive integer, found '$2'");
        verifyInvalidExpressionException("negative[-5]", "expecting a positive integer, found '-'");
        verifyInvalidExpressionException("positive[+5]", "expecting a positive integer, found '+'");
        verifyInvalidExpressionException("decimal[5.5]", "expecting a positive integer, found '5.5'");
        verifyInvalidExpressionException("unclosed[5", "unexpected end of expression");
        verifyInvalidExpressionException("unopened5]", "unexpected token: an identifier");
        verifyInvalidExpressionException("noindex[]", "expecting a positive integer, found ']'");
        verifyInvalidExpressionException("paren[(33)]", "expecting a positive integer, found '('");
    }

    /**
     * Consecutive boolean operations are parsed correctly.
     */
    public void testBoolChain() throws Exception {
        verifyInvalidExpressionException("true == true == true", "unexpected token: '=='");
        verifyInvalidExpressionException("1 > 2 == true", "unexpected token: '=='");
        verifyInvalidExpressionException("true == 4 < 5", "unexpected token: '<'");
    }

    /**
     * Escape sequences in strings are parsed correctly.
     */
    public void testEscapedString() throws Exception {
        Expression e = buildExpression("\t'over there! '\r\n\n");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        LiteralImpl l = (LiteralImpl) e;
        assertEquals("Escape sequences not parsed correctly", "over there! ", l.getValue());

        e = buildExpression(" '\\'stuff me,\\' the unfilled teddy bear said to the child.'");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Escape sequences not parsed correctly", "'stuff me,' the unfilled teddy bear said to the child.",
                l.getValue());

        e = buildExpression("'the child blinked and replied,\\n \\t\\'I\\'d be delighted.\\''");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Escape sequences not parsed correctly", "the child blinked and replied,\n \t'I'd be delighted.'",
                l.getValue());

        e = buildExpression("'top\\\\\\\"2\\\"\\b\\f\\r\\n\\tbottom'");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Escape sequences not parsed correctly", "top\\\"2\"\b\f\r\n\tbottom", l.getValue());
    }

    /**
     * Unescaped backslash in string should throw a parse exception.
     */
    // currently fails when slash up front
    public void testNonEscapeBackslashInString() throws Exception {
        verifyInvalidExpressionException("'not\\u32unicode'", "mismatched character 'u' expecting set null");
        verifyInvalidExpressionException("'\\'", "expecting ''', found '<EOF>'");
        verifyInvalidExpressionException("'back \\ slash'", "unexpected token: ' '");
        verifyInvalidExpressionException("'\\escaped'", "unexpected token: 'e'");
        verifyInvalidExpressionException("'not\\'", "expecting ''', found '<EOF>'");
    }

    /**
     * Unicode escapes in strings are parsed correctly.
     */
    public void testUnicodeEscapesInString() throws Exception {
        Expression e = buildExpression("'\\u0032'");
        assertEquals(ExpressionType.LITERAL, e.getExpressionType());
        LiteralImpl l = (LiteralImpl) e;
        assertEquals("2", l.getValue());

        e = buildExpression("'good\\u0032go'");
        assertEquals(ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("good2go", l.getValue());

        e = buildExpression("'Ocean\\'s \\u0031\\u0031'");
        assertEquals(ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("Ocean's 11", l.getValue());

        e = buildExpression("'\\u0031\\u0032\\u0020Monkeys\\u0020\\u0099'");
        assertEquals(ExpressionType.LITERAL, e.getExpressionType());
        l = (LiteralImpl) e;
        assertEquals("12 Monkeys \u0099", l.getValue());
    }

    /**
     * Operators may be accessed as function calls and may be nested.
     */
    public void testFunctionCalls() throws Exception {
        Expression e = buildExpression("or(true, false)");
        assertTrue("Operator could not be accessed as function call", e.evaluate(null));
        e = buildExpression("and(false, true)");
        assertFalse("Operator could not be accessed as function call", e.evaluate(null));
        e = buildExpression("add(24, 23525)");
        assertEquals("Operator could not be accessed as function call", 24 + 23525, e.evaluate(null));

        e = buildExpression("sub(24, add(63, 23525))");
        assertEquals("Error evaluating nested operators as function calls", 24.0 - (63 + 23525), e.evaluate(null));

        e = buildExpression("add('The child', ' picked up the teddy bear in one hand')");
        assertEquals("Error adding strings with add operator as function call",
                "The child picked up the teddy bear in one hand", e.evaluate(null));

        e = buildExpression("notequals(add(' and grasped a', ' pick axe in the other.'), '\"Wait!\" screamed the bear.')");
        assertTrue("Error in evaluating nested operators as funtion calls", e.evaluate(null));

        e = buildExpression("if(equals(true, greaterthan(-2, -52)), add('The child hesitated, ', '\"I cannot stuff you without making a hole to stuff into.\"'), ' The bear squinted and noodled on this for a minute.')");
        assertEquals("Error in evaluating nested operators as funtion calls",
                "The child hesitated, \"I cannot stuff you without making a hole to stuff into.\"", e.evaluate(null));
    }

    /**
     * Non-english string literals are parsed correctly.
     */
    public void testNonEnglishString() throws Exception {
        Expression e = buildExpression("'天ぷらが食べたいです'");
        assertEquals("Unexpected expression type", ExpressionType.LITERAL, e.getExpressionType());
        assertEquals("Unexpected evaluation of non-english string", "天ぷらが食べたいです", e.evaluate(null));
    }

    /**
     * Missing operands throw a parse exception.
     */
    public void testMissingOperands() throws Exception {
        verifyInvalidExpressionException("1+", "unexpected end of expression");
        verifyInvalidExpressionException("2-", "unexpected end of expression");
        verifyInvalidExpressionException("*3", "unexpected token: a multiplication");
        verifyInvalidExpressionException("/4", "unexpected token: a division");
        verifyInvalidExpressionException("5%", "unexpected end of expression");
        verifyInvalidExpressionException("?", "unexpected token: a question mark");
    }

    /**
     * Mismatched parentheses throw a parse exception.
     */
    public void testIncompleteParentheses() throws Exception {
        verifyInvalidExpressionException("(1", "unexpected end of expression");
        verifyInvalidExpressionException("2)", "unexpected token: ')'");
    }

    /**
     * Unclosed string throws a parse exception.
     */
    @UnAdaptableTest
    public void testIncompleteString() throws Exception {
        verifyInvalidExpressionException("'unclosed string", "expecting ''', found '<EOF>'");
        verifyInvalidExpressionException("unclosed string'", "unexpected token: an identifier");
    }

    /**
     * Unclosed braces throws a parse exception.
     */
    public void testIncompleteBraces() throws Exception {
        verifyInvalidExpressionException("{!v.noend", "unclosed brace");
        verifyInvalidExpressionException("v.dangled}", "unexpected token: '}'");
        verifyInvalidExpressionException("v.x + {!v.inner}", "unexpected token: '{'");
    }

    /**
     * Invalid characters in property names throw a parse exception.
     */
    public void testPropertyNameInvalid() throws Exception {
        verifyInvalidExpressionException("\\upfront", "unexpected token: '\\'");
        verifyInvalidExpressionException("followup\\", "unexpected token: '\\'");
        verifyInvalidExpressionException("in\\side", "unexpected token: '\\'");
        verifyInvalidExpressionException("5horsemen", "unexpected token: 'horsemen'");
        verifyInvalidExpressionException("9.1.1", "unexpected token: '.1'");
        verifyInvalidExpressionException("in.2.u", "unexpected token: an identifier");
        verifyInvalidExpressionException(".hidden", "unexpected token: a period");
        verifyInvalidExpressionException("ends.with.", "unexpected end of expression");
        verifyInvalidExpressionException("man...middle", "expecting an identifier, found '.'");
    }

    /**
     * Undefined function
     */
    public void testUndefinedFunction() throws Exception {
        try {
            buildExpression("undefined(4)");
            fail("Expecting AuraRuntimeException for undefined function");
        } catch (AuraRuntimeException e) {
            assertTrue("Unexpected error message trying to parse <undefined(4)>. Expected to start with: "
                    + "No function found for key: undefined" + ". But got: " + e.getMessage(), e.getMessage()
                    .startsWith("No function found for key: undefined"));
        }
    }

    /**
     * Multiple statemts, separated by a semicolon
     */
    public void testMultipleStatements() throws Exception {
        verifyInvalidExpressionException("5==1;5==1;", "unexpected token: ';' at column 5");
    }

    /**
     * Invalid syntax - consecutive property names
     */
    public void testMultipleProperties() throws Exception {
        verifyInvalidExpressionException("test invalid", "unexpected token: an identifier");
    }

    /**
     * Invalid syntax - consecutive literals
     */
    public void testMultipleLiterals() throws Exception {
        verifyInvalidExpressionException("5 'invalid'", "unexpected token: ''invalid''");
    }

    /**
     * Invalid syntax - consecutive functions
     */
    public void testMultipleFunctions() throws Exception {
        verifyInvalidExpressionException("add() subtract()", "unexpected token: 'subtract'");
    }

    /**
     * Invalid syntax - consecutive property names separated by newline
     */
    public void testMultilineProperty() throws Exception {
        verifyInvalidExpressionException("invalid\r\ntest", "unexpected token: an identifier");
    }

    /**
     * Invalid syntax - consecutive literals separated by newline
     */
    public void testMultilineLiteral() throws Exception {
        verifyInvalidExpressionException("5\r\n1", "unexpected token: '1'");
    }

    /**
     * Invalid syntax - consecutive literals separated by newline
     */
    public void testLocationInErrorMessage() throws Exception {
        verifyInvalidExpressionException("5\r\n  1", "unexpected token: '1' at line 2, column 3");
    }

    /**
     * Verify the correct exception type is thrown and contains the correct error message.
     */
    private void verifyInvalidExpressionException(String expression, String msgStartsWith) throws Exception {
        try {
            buildExpression(expression);
            fail("No execption thrown for <" + expression + ">. Expected InvalidExpressionException");
        } catch (InvalidExpressionException e) {
            assertTrue("Unexpected error message trying to parse <" + expression + ">. Expected to start with: "
                    + msgStartsWith + ". But got: " + e.getMessage(), e.getMessage().startsWith(msgStartsWith));
        }
    }
}
TOP

Related Classes of org.auraframework.impl.expression.parser.ExpressionParserTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.