/*
 *   Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you 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.ballerinalang.test.functions;

import org.ballerinalang.test.BCompileUtil;
import org.ballerinalang.test.BRunUtil;
import org.ballerinalang.test.CompileResult;
import org.ballerinalang.test.exceptions.BLangTestException;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.ballerinalang.test.BAssertUtil.validateError;
import static org.testng.Assert.assertEquals;

/**
 * Test cases for expression bodied functions.
 *
 * @since 1.2.0
 */
public class ExprBodiedFunctionTest {

    private CompileResult compileResult;

    @BeforeClass
    public void setup() {
        compileResult = BCompileUtil.compile("test-src/functions/expr_bodied_functions.bal");
    }

    @Test
    public void testSyntaxErrors() {
        CompileResult result = BCompileUtil.compile("test-src/functions/expr_bodied_functions_negative.bal");
        int index = 0;

        validateError(result, index++, "operator '+' not defined for 'record {| int a; |}' and 'int'", 18, 43);
        validateError(result, index++, "invalid token 'return'", 19, 5);
        validateError(result, index++, "missing close brace token", 19, 14);
        validateError(result, index++, "invalid token '}'", 20, 1);
        validateError(result, index++, "invalid token 'external'", 23, 43);
        validateError(result, index++, "incompatible types: expected 'int', found 'function (int,int) returns (int)'",
                26, 1);
        validateError(result, index++, "invalid token 'sum'", 26, 10);
        validateError(result, index++, "incompatible types: expected 'int', found 'typedesc<int>'", 26, 45);
        validateError(result, index++, "missing semicolon token", 26, 49);
        validateError(result, index++, "unknown type 'x'", 26, 49);
        validateError(result, index++, "missing identifier", 26, 51);

        Assert.assertEquals(result.getErrorCount(), index);
    }

    @Test(enabled = false)
    public void testTaintChecking() {
        CompileResult result = BCompileUtil.compile("test-src/functions/expr_bodied_functions_taint.bal");
        int index = 0;
        validateError(result, index++, "tainted value passed to untainted parameter 'param'", 25, 32);
        validateError(result, index++, "tainted value passed to untainted parameter 'param'", 32, 32);
        validateError(result, index++, "tainted value passed to untainted parameter 'param'", 41, 32);
        assertEquals(result.getErrorCount(), index);
    }

    @Test(dataProvider = "FunctionList")
    public void testExprBodiedFunctions(String funcName) {
        BRunUtil.invoke(compileResult, funcName);
    }

    @Test
    public void testClosures() {
        BRunUtil.invoke(compileResult, "testClosures", new Object[]{10});
    }

    @Test(expectedExceptions = BLangTestException.class,
          expectedExceptionsMessageRegExp = ".*NumberParsingError \\{\"message\":\"'string' value " +
                  "'invalid' cannot be converted to 'int'.*")
    public void testCheckPanic() {
        BRunUtil.invoke(compileResult, "testCheckPanic");
    }

    @DataProvider(name = "FunctionList")
    public Object[][] getTestFunctions() {
        return new Object[][]{
                {"testReturningLiterals"},
                {"testReturningLists"},
                {"testBinaryExprs"},
                {"testNilReturningFunctions"},
                {"testRecordAsAnExpr"},
                {"testSameVarRefAsExpr"},
                {"testFunctionInvocation"},
                {"testFunctionInvocationAsLambdas"},
                {"testExprsBodiesInMethods"},
                {"testObjectInitBodyAsAnExpr"},
                {"testObjectsAsExprBody"},
                {"testAnonFuncsAsExprBody"},
                {"testReturningXML"},
                {"testReturningStringTemplate"},
                {"testReturningServiceConstructors"},
                {"testLetExprAsExprBody"},
        };
    }

    @AfterClass
    public void tearDown() {
        compileResult = null;
    }
}
