From aec5a5f29b3914cb9023f8c5e8f222284a2625e2 Mon Sep 17 00:00:00 2001 From: Mauricio Uyaguari Date: Fri, 12 Jan 2024 13:33:51 -0500 Subject: [PATCH] uplift function test grammar (#2537) --- .../toPureGraph/HelperFunctionBuilder.java | 4 + .../TestFunctionCompilationFromGrammar.java | 321 +++--------------- .../api/test/TestGrammarToJsonApi.java | 2 +- .../from/antlr4/domain/DomainLexerGrammar.g4 | 8 +- .../from/antlr4/domain/DomainParserGrammar.g4 | 48 +-- .../from/domain/DomainParseTreeWalker.java | 228 ++++++++++--- .../to/HelperDomainGrammarComposer.java | 148 +++++--- ...lperValueSpecificationGrammarComposer.java | 6 + .../pure/grammar/test/TestGrammarParser.java | 15 + .../roundtrip/TestDomainGrammarRoundtrip.java | 177 ++++------ .../domain/ParameterValue.java | 2 + .../function/FunctionTestSuite.java | 1 - ...gend-testable-function-test-model-m2m.pure | 42 +-- ...stable-function-test-model-relational.pure | 101 +----- .../legend-testable-function-test-model.pure | 46 +-- .../test/TestRelationalGrammarRoundtrip.java | 27 ++ 16 files changed, 477 insertions(+), 699 deletions(-) diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperFunctionBuilder.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperFunctionBuilder.java index 1546c73110a..bf7fc980b50 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperFunctionBuilder.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperFunctionBuilder.java @@ -141,6 +141,10 @@ private static Root_meta_legend_function_metamodel_StoreTestData buildFunctionTe private static Root_meta_legend_function_metamodel_ParameterValue processFunctionTestParameterValue(ParameterValue parameterValue, CompileContext context) { Root_meta_legend_function_metamodel_ParameterValue pureParameterValue = new Root_meta_legend_function_metamodel_ParameterValue_Impl("", null, context.pureModel.getClass("meta::legend::function::metamodel::ParameterValue")); + if (parameterValue.name == null || parameterValue.name.isEmpty()) + { + throw new EngineException("No associated parameter found for value.", parameterValue.sourceInformation, EngineErrorType.COMPILATION); + } pureParameterValue._name(parameterValue.name); pureParameterValue._value(Lists.immutable.with(parameterValue.value.accept(new ValueSpecificationBuilder(context, Lists.mutable.empty(), new ProcessingContext(""))))); return pureParameterValue; diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/test/java/org/finos/legend/engine/language/pure/compiler/test/fromGrammar/TestFunctionCompilationFromGrammar.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/test/java/org/finos/legend/engine/language/pure/compiler/test/fromGrammar/TestFunctionCompilationFromGrammar.java index 8e62dbcbfa6..a33a605c13a 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/test/java/org/finos/legend/engine/language/pure/compiler/test/fromGrammar/TestFunctionCompilationFromGrammar.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/test/java/org/finos/legend/engine/language/pure/compiler/test/fromGrammar/TestFunctionCompilationFromGrammar.java @@ -29,314 +29,81 @@ public void testFunctionTest() "{\n" + " ''\n" + "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testFail:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " },\n" + - " testPass:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n"); - - test("function model::MyFunc(): String[1]\n" + "{\n" + - " ''\n" + - "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " },\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n", "COMPILATION error at [6:3-43:3]: Multiple tests found with ids : 'testDuplicate'"); + " testSuite1\n" + + " (\n" + + " testFail | MyFunc() => (JSON) '[]';\n" + + " testPass | MyFunc() => (JSON) '[]';\n" + + " )\n" + + " testFail | MyFunc() => (JSON) '[]';\n" + + " testPass | MyFunc() => (JSON) '[]';\n" + + "}\n"); test("function model::MyFunc(): String[1]\n" + "{\n" + " ''\n" + "}\n" + - "[\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " },\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n", "COMPILATION error at [1:1-50:1]: Multiple testSuites found with ids : 'duplicateSuite'"); - - - test("function model::MyFunc(firstName: String[1]): String[1]\n" + "{\n" + - " ''\n" + - "}\n" + - "[\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " parameters:\n" + - " [\n" + - " firstName = 'Nicole'\n" + - " ]\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n"); + " testSuite1\n" + + " (\n" + + " testDuplicate | MyFunc() => (JSON) '[]';\n" + + " testDuplicate | MyFunc() => (JSON) '[]';\n" + + " )\n" + + "}\n", "COMPILATION error at [6:3-10:3]: Multiple tests found with ids : 'testDuplicate'"); + test("function model::MyFunc(): String[1]\n" + + "{\n" + + " ''\n" + + "}\n" + + "{\n" + + " duplicateSuite\n" + + " (\n" + + " testDuplicate | MyFunc() => (JSON) '[]';\n" + + " )\n" + + " duplicateSuite\n" + + " (\n" + + " testDuplicate | MyFunc() => (JSON) '[]';\n" + + " )\n" + + "}\n", "COMPILATION error at [1:1-14:1]: Multiple testSuites found with ids : 'duplicateSuite'"); test("function model::MyFunc(firstName: String[1]): String[1]\n" + "{\n" + " ''\n" + "}\n" + - "[\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n", "COMPILATION error at [10:7-25:7]: Parameter value required for parameter: 'firstName'"); + "{\n" + + " testDuplicate | MyFunc('Nicole') => (JSON) '[]';\n" + + "}\n"); test("function model::MyFunc(firstName: String[1]): String[1]\n" + "{\n" + " ''\n" + "}\n" + - "[\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n", "COMPILATION error at [10:7-25:7]: Parameter value required for parameter: 'firstName'"); + "{\n" + + " testDuplicate | MyFunc() => (JSON) '[]';\n" + + "}\n", "COMPILATION error at [6:3-42]: Parameter value required for parameter: 'firstName'"); - test("function model::MyFunc(): String[1]\n" + + test("function model::MyFunc(firstName: String[1], test: Integer[1], whoops: String[1]): String[1]\n" + "{\n" + " ''\n" + "}\n" + - "[\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " parameters: [" + - " notFound = 'xx' " + - " ]" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n", "COMPILATION error at [10:7-25:7]: Parameter values not found in function parameter: notFound"); + "{\n" + + " testDuplicate | MyFunc('John', 1) => (JSON) '[]';\n" + + "}\n", "COMPILATION error at [6:3-51]: Parameter value required for parameter: 'whoops'"); test("function model::MyFunc(): String[1]\n" + "{\n" + " ''\n" + "}\n" + - "[\n" + - " duplicateSuite:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testDuplicate:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n", "COMPILATION error at [10:7-15:7]: Tests should have at least 1 assert"); - + "{\n" + + " testDuplicate | MyFunc('John') => (JSON) '[]';\n" + + "}\n", "COMPILATION error at [6:26-31]: No associated parameter found for value."); test("function model::Hello(name: String[1]): String[1]\n" + "{\n" + " 'Hello!. My name is ' + $name + '.';\n" + "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testFail:\n" + - " {\n" + - " parameters: \n" + - " [\n" + - " name = 'John'\n" + - " ]\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualTo\n" + - " #{\n" + - " expected: 'Hello!. My name is John.';\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n"); + "{\n" + + " myTest | Hello('John') => 'Hello! My name is John.';\n" + + "}\n"); } } diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar-api/src/test/java/org/finos/legend/engine/language/pure/grammar/api/test/TestGrammarToJsonApi.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar-api/src/test/java/org/finos/legend/engine/language/pure/grammar/api/test/TestGrammarToJsonApi.java index 70399d6423d..3988a9d6a0d 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar-api/src/test/java/org/finos/legend/engine/language/pure/grammar/api/test/TestGrammarToJsonApi.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar-api/src/test/java/org/finos/legend/engine/language/pure/grammar/api/test/TestGrammarToJsonApi.java @@ -83,7 +83,7 @@ public void testLambdaParsingError() @Test public void testMixedParsingErrors() { - test("{\"code\": \"Class A {},\", \"isolatedLambdas\": {\"good\": \"|'good'\", \"bad\": \"|,\"}}", "{\"codeError\":{\"message\":\"Unexpected token\",\"sourceInformation\":{\"endColumn\":11,\"endLine\":1,\"sourceId\":\"\",\"startColumn\":11,\"startLine\":1}},\"isolatedLambdas\":{\"lambdaErrors\":{\"bad\":{\"message\":\"Unexpected token ','. Valid alternatives: ['import', 'Class', 'Association', 'Profile', 'Enum', 'Measure', 'function', 'extends', 'stereotypes', 'tags', 'Error', 'Warn', 'native', 'projects', 'as', 'composite', 'shared', 'none', 'data', 'tests', 'parameters', 'asserts', 'store', 'all', 'let', 'allVersions', 'allVersionsInRange', 'toBytes', '!', '[', '(', '$', '^', '|', '@', '+', '-']\",\"sourceInformation\":{\"endColumn\":2,\"endLine\":1,\"sourceId\":\"bad\",\"startColumn\":2,\"startLine\":1}}},\"lambdas\":{\"good\":{\"_type\":\"lambda\",\"body\":[{\"_type\":\"string\",\"sourceInformation\":{\"endColumn\":7,\"endLine\":1,\"sourceId\":\"good\",\"startColumn\":2,\"startLine\":1},\"value\":\"good\"}],\"parameters\":[],\"sourceInformation\":{\"endColumn\":7,\"endLine\":1,\"sourceId\":\"good\",\"startColumn\":1,\"startLine\":1}}}},\"renderStyle\":\"STANDARD\"}"); + test("{\"code\": \"Class A {},\", \"isolatedLambdas\": {\"good\": \"|'good'\", \"bad\": \"|,\"}}", "{\"codeError\":{\"message\":\"Unexpected token\",\"sourceInformation\":{\"endColumn\":11,\"endLine\":1,\"sourceId\":\"\",\"startColumn\":11,\"startLine\":1}},\"isolatedLambdas\":{\"lambdaErrors\":{\"bad\":{\"message\":\"Unexpected token ','. Valid alternatives: ['import', 'Class', 'Association', 'Profile', 'Enum', 'Measure', 'function', 'extends', 'stereotypes', 'tags', 'Error', 'Warn', 'native', 'projects', 'as', 'composite', 'shared', 'none', 'all', 'let', 'allVersions', 'allVersionsInRange', 'toBytes', '!', '[', '(', '$', '^', '|', '@', '+', '-']\",\"sourceInformation\":{\"endColumn\":2,\"endLine\":1,\"sourceId\":\"bad\",\"startColumn\":2,\"startLine\":1}}},\"lambdas\":{\"good\":{\"_type\":\"lambda\",\"body\":[{\"_type\":\"string\",\"sourceInformation\":{\"endColumn\":7,\"endLine\":1,\"sourceId\":\"good\",\"startColumn\":2,\"startLine\":1},\"value\":\"good\"}],\"parameters\":[],\"sourceInformation\":{\"endColumn\":7,\"endLine\":1,\"sourceId\":\"good\",\"startColumn\":1,\"startLine\":1}}}},\"renderStyle\":\"STANDARD\"}"); } @Test diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainLexerGrammar.g4 b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainLexerGrammar.g4 index bf66e24ad28..175b8b8694e 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainLexerGrammar.g4 +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainLexerGrammar.g4 @@ -34,10 +34,4 @@ AS: 'as'; AGGREGATION_TYPE_COMPOSITE: 'composite'; AGGREGATION_TYPE_SHARED: 'shared'; -AGGREGATION_TYPE_NONE: 'none'; - -FUNCTION_TEST_DATA: 'data'; -FUNCTION_SUITE_TESTS: 'tests'; -FUNCTION_TEST_PARAMETERS: 'parameters'; -FUNCTION_TEST_ASSERTS: 'asserts'; -FUNCTION_TEST_DATA_STORE: 'store'; +AGGREGATION_TYPE_NONE: 'none'; \ No newline at end of file diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainParserGrammar.g4 b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainParserGrammar.g4 index 28d696903dd..c6c0b701957 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainParserGrammar.g4 +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/antlr4/org/finos/legend/engine/language/pure/grammar/from/antlr4/domain/DomainParserGrammar.g4 @@ -20,8 +20,6 @@ identifier: VALID_STRING | STRING | NATIVE | PROJECTS | AS | CONSTRAINT_ENFORCEMENT_LEVEL_ERROR | CONSTRAINT_ENFORCEMENT_LEVEL_WARN | AGGREGATION_TYPE_COMPOSITE | AGGREGATION_TYPE_SHARED | AGGREGATION_TYPE_NONE - | FUNCTION_TEST_DATA | FUNCTION_SUITE_TESTS | FUNCTION_TEST_PARAMETERS - | FUNCTION_TEST_ASSERTS | FUNCTION_TEST_DATA_STORE ; @@ -172,47 +170,35 @@ functionDefinition: FUNCTION stereotypes? taggedValu BRACE_OPEN codeBlock BRACE_CLOSE - functionTestSuites? + functionTestSuiteDef? ; -functionTestSuites: BRACKET_OPEN - functionTestSuite (COMMA functionTestSuite)* - BRACKET_CLOSE -; -functionTestSuite: identifier COLON BRACE_OPEN (testData | functionTestSuiteTests )* BRACE_CLOSE -; -testData: FUNCTION_TEST_DATA COLON BRACKET_OPEN (storeTestData ( COMMA storeTestData )*)? BRACKET_CLOSE SEMI_COLON -; -storeTestData: BRACE_OPEN - ( - storePointer | - storeData - )* +functionTestSuiteDef: BRACE_OPEN + (simpleFunctionTest | simpleFunctionSuite | functionData)* BRACE_CLOSE ; -storePointer: FUNCTION_TEST_DATA_STORE COLON qualifiedName SEMI_COLON +simpleFunctionSuite: identifier + PAREN_OPEN + (functionData)* + simpleFunctionTest (simpleFunctionTest)* + PAREN_CLOSE ; -storeData: FUNCTION_TEST_DATA COLON embeddedData SEMI_COLON +functionData: qualifiedName COLON functionDataValue SEMI_COLON ; -embeddedData: identifier ISLAND_OPEN (embeddedDataContent)* +functionDataValue: (qualifiedName | externalFormatValue | embeddedData) ; -embeddedDataContent: ISLAND_START | ISLAND_BRACE_OPEN | ISLAND_CONTENT | ISLAND_HASH | ISLAND_BRACE_CLOSE | ISLAND_END +simpleFunctionTest: identifier PIPE identifier PAREN_OPEN functionParams PAREN_CLOSE EQUAL GREATER_THAN (externalFormatValue | primitiveValue) SEMI_COLON ; -functionTestSuiteTests: FUNCTION_SUITE_TESTS COLON BRACKET_OPEN (functionTestBlock ( COMMA functionTestBlock )*)? BRACKET_CLOSE +externalFormatValue: contentType STRING ; -functionTestBlock: identifier COLON BRACE_OPEN (functionTestParameters | functionTestAsserts )* BRACE_CLOSE +contentType: PAREN_OPEN identifier PAREN_CLOSE ; -functionTestParameters: FUNCTION_TEST_PARAMETERS COLON BRACKET_OPEN ( functionTestParameter ( COMMA functionTestParameter )* )? BRACKET_CLOSE +functionParams: (primitiveValue (COMMA primitiveValue)*)? ; -functionTestParameter: identifier EQUAL primitiveValue -; -functionTestAsserts: FUNCTION_TEST_ASSERTS COLON BRACKET_OPEN ( functionTestAssert ( COMMA functionTestAssert )* )? BRACKET_CLOSE -; -functionTestAssert: identifier COLON testAssertion -; -testAssertion: identifier ISLAND_OPEN (testAssertionContent)* +embeddedData: identifier ISLAND_OPEN (embeddedDataContent)* ; -testAssertionContent: ISLAND_START | ISLAND_BRACE_OPEN | ISLAND_CONTENT | ISLAND_HASH | ISLAND_BRACE_CLOSE | ISLAND_END +embeddedDataContent: ISLAND_START | ISLAND_BRACE_OPEN | ISLAND_CONTENT | ISLAND_HASH | ISLAND_BRACE_CLOSE | ISLAND_END ; + // -------------------------------------- CONSTRAINT -------------------------------------- constraints: BRACKET_OPEN diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/from/domain/DomainParseTreeWalker.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/from/domain/DomainParseTreeWalker.java index ec401d0d4bb..c59f9f3ce59 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/from/domain/DomainParseTreeWalker.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/from/domain/DomainParseTreeWalker.java @@ -21,7 +21,6 @@ import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.tree.TerminalNode; -import org.checkerframework.dataflow.qual.Pure; import org.eclipse.collections.api.list.ListIterable; import org.eclipse.collections.api.list.MutableList; import org.eclipse.collections.impl.factory.Lists; @@ -39,17 +38,17 @@ import org.finos.legend.engine.language.pure.grammar.from.antlr4.navigation.NavigationParserGrammar; import org.finos.legend.engine.language.pure.grammar.from.data.embedded.HelperEmbeddedDataGrammarParser; import org.finos.legend.engine.language.pure.grammar.from.extension.EmbeddedPureParser; -import org.finos.legend.engine.language.pure.grammar.from.test.assertion.HelperTestAssertionGrammarParser; import org.finos.legend.engine.language.pure.grammar.to.HelperValueSpecificationGrammarComposer; import org.finos.legend.engine.protocol.pure.v1.model.SourceInformation; import org.finos.legend.engine.protocol.pure.v1.model.context.EngineErrorType; +import org.finos.legend.engine.protocol.pure.v1.model.data.DataElementReference; +import org.finos.legend.engine.protocol.pure.v1.model.data.ExternalFormatData; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.PackageableElement; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.AggregationKind; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Association; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Class; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Constraint; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.DefaultValue; -import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Domain; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.EnumValue; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Enumeration; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Measure; @@ -62,8 +61,12 @@ import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.TagPtr; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.TaggedValue; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Unit; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTest; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.StoreTestData; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.section.ImportAwareCodeSection; +import org.finos.legend.engine.protocol.pure.v1.model.test.assertion.EqualTo; +import org.finos.legend.engine.protocol.pure.v1.model.test.assertion.EqualToJson; import org.finos.legend.engine.protocol.pure.v1.model.test.assertion.TestAssertion; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.ValueSpecification; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.Variable; @@ -93,6 +96,10 @@ public class DomainParseTreeWalker { private static final String TILDE = "~"; + private static final String DEFAULT_TESTABLE_ID = "default"; + private static final String KNOWN_JSON_CONTENT_TYPE = "JSON"; + private static final String KNOWN_XML_CONTENT_TYPE = "XML"; + private final ParseTreeWalkerSourceInformation walkerSourceInformation; private final PureGrammarParserContext parserContext; private final boolean allowPropertyBracketExpression; @@ -424,86 +431,201 @@ private org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain variable.sourceInformation = this.walkerSourceInformation.getSourceInformation(functionVariableExpressionContext); return variable; }); - if (ctx.functionTestSuites() != null) + if (ctx.functionTestSuiteDef() != null) { - func.tests = ListIterate.collect(ctx.functionTestSuites().functionTestSuite(), this::visitFunctionSuite); + List suites = Lists.mutable.empty(); + DomainParserGrammar.FunctionTestSuiteDefContext functionTestSuiteDefContext = ctx.functionTestSuiteDef(); + if ((functionTestSuiteDefContext.simpleFunctionTest() != null && !functionTestSuiteDefContext.simpleFunctionTest().isEmpty()) || (functionTestSuiteDefContext.functionData() != null && !functionTestSuiteDefContext.functionData().isEmpty())) + { + org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite functionTestSuite = new org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite(); + functionTestSuite.id = DEFAULT_TESTABLE_ID; + functionTestSuite.tests = Lists.mutable.empty(); + functionTestSuite.sourceInformation = this.walkerSourceInformation.getSourceInformation(functionTestSuiteDefContext); + if (functionTestSuiteDefContext.simpleFunctionTest() != null) + { + for (DomainParserGrammar.SimpleFunctionTestContext simpleFunctionTestContext: functionTestSuiteDefContext.simpleFunctionTest()) + { + this.processFuncTest(ctx, functionTestSuite,func.parameters, simpleFunctionTestContext); + } + } + if (functionTestSuiteDefContext.functionData() != null) + { + functionTestSuite.testData = Lists.mutable.empty(); + for (DomainParserGrammar.FunctionDataContext functionDataContext: functionTestSuiteDefContext.functionData()) + { + functionTestSuite.testData.add(this.processStoreTestData(functionDataContext)); + } + } + suites.add(functionTestSuite); + } + if (functionTestSuiteDefContext.simpleFunctionSuite() != null) + { + for (DomainParserGrammar.SimpleFunctionSuiteContext simpleFunctionSuiteContext: ctx.functionTestSuiteDef().simpleFunctionSuite()) + { + org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite functionTestSuite = new org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite(); + functionTestSuite.id = PureGrammarParserUtility.fromIdentifier(simpleFunctionSuiteContext.identifier()); + functionTestSuite.tests = Lists.mutable.empty(); + functionTestSuite.sourceInformation = this.walkerSourceInformation.getSourceInformation(simpleFunctionSuiteContext); + for (DomainParserGrammar.SimpleFunctionTestContext simpleFunctionTestContext: simpleFunctionSuiteContext.simpleFunctionTest()) + { + this.processFuncTest(ctx, functionTestSuite,func.parameters, simpleFunctionTestContext); + } + if (simpleFunctionSuiteContext.functionData() != null) + { + functionTestSuite.testData = Lists.mutable.empty(); + for (DomainParserGrammar.FunctionDataContext functionDataContext: simpleFunctionSuiteContext.functionData()) + { + functionTestSuite.testData.add(this.processStoreTestData(functionDataContext)); + } + } + suites.add(functionTestSuite); + } + } + func.tests = suites; } func.returnType = ctx.functionTypeSignature().type().getText(); func.returnMultiplicity = this.buildMultiplicity(ctx.functionTypeSignature().multiplicity().multiplicityArgument()); func.sourceInformation = this.walkerSourceInformation.getSourceInformation(ctx); func.name = PureGrammarParserUtility.fromIdentifier(ctx.qualifiedName().identifier()) + HelperValueSpecificationGrammarComposer.getFunctionSignature(func); return func; + } - private org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite visitFunctionSuite(DomainParserGrammar.FunctionTestSuiteContext functionTestSuiteContext) + private void processFuncTest(DomainParserGrammar.FunctionDefinitionContext functionCtx, FunctionTestSuite parentSuite, List functionParameters, DomainParserGrammar.SimpleFunctionTestContext simpleFunctionTestContext) { - org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite suite = new org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite(); - suite.sourceInformation = this.walkerSourceInformation.getSourceInformation(functionTestSuiteContext); - suite.id = PureGrammarParserUtility.fromIdentifier(functionTestSuiteContext.identifier()); - // data - DomainParserGrammar.TestDataContext testDataContext = PureGrammarParserUtility.validateAndExtractOptionalField(functionTestSuiteContext.testData(), "data", suite.sourceInformation); - if (testDataContext != null) + DomainParserGrammar.IdentifierContext functionTestNameCtx = simpleFunctionTestContext.identifier(1); + String functionName = PureGrammarParserUtility.fromIdentifier(functionTestNameCtx); + String userTestFunctionName = PureGrammarParserUtility.fromIdentifier(functionCtx.qualifiedName().identifier()); + if (!userTestFunctionName.equals(functionName)) { - suite.testData = ListIterate.collect(testDataContext.storeTestData(), this::visitStoreTestData); + throw new EngineException("Function name in test '" + functionName + "' does not match function name '" + userTestFunctionName + "'", walkerSourceInformation.getSourceInformation(simpleFunctionTestContext.identifier(1)), EngineErrorType.PARSER); } - // Tests - DomainParserGrammar.FunctionTestSuiteTestsContext testSuiteTestsContext = PureGrammarParserUtility.validateAndExtractRequiredField(functionTestSuiteContext.functionTestSuiteTests(), "tests", suite.sourceInformation); - suite.tests = ListIterate.collect(testSuiteTestsContext.functionTestBlock(), this::visitFunctionTest); - return suite; + FunctionTest functionTest = new FunctionTest(); + functionTest.id = PureGrammarParserUtility.fromIdentifier(simpleFunctionTestContext.identifier(0)); + functionTest.sourceInformation = this.walkerSourceInformation.getSourceInformation(simpleFunctionTestContext); + DomainParserGrammar.FunctionParamsContext functionParamsContext = simpleFunctionTestContext.functionParams(); + if (functionParamsContext != null && !functionParamsContext.primitiveValue().isEmpty()) + { + functionTest.parameters = Lists.mutable.empty(); + for (int idx = 0; idx < functionParamsContext.primitiveValue().size(); idx++) + { + DomainParserGrammar.PrimitiveValueContext primitiveValueContext = functionParamsContext.primitiveValue().get(idx); + Variable variable = null; + try + { + variable = functionParameters.get(idx); + } + catch (IndexOutOfBoundsException exception) + { + // ignore + } + functionTest.parameters.add(this.visitFunctionTestParameter(primitiveValueContext, variable)); + } + } + functionTest.assertions = Lists.mutable.with(visitTestAssertion(simpleFunctionTestContext)); + parentSuite.tests.add(functionTest); } - private org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.StoreTestData visitStoreTestData(DomainParserGrammar.StoreTestDataContext storeDataContext) + private ParameterValue visitFunctionTestParameter(DomainParserGrammar.PrimitiveValueContext primitiveValueContext, Variable variable) { - StoreTestData storeTestData = new StoreTestData(); - DomainParserGrammar.StorePointerContext storePointerContext = PureGrammarParserUtility.validateAndExtractRequiredField(storeDataContext.storePointer(), "store", storeTestData.sourceInformation); - storeTestData.store = PureGrammarParserUtility.fromQualifiedName(storePointerContext.qualifiedName().packagePath() == null ? Collections.emptyList() : storePointerContext.qualifiedName().packagePath().identifier(), storePointerContext.qualifiedName().identifier()); - DomainParserGrammar.StoreDataContext dataContext = PureGrammarParserUtility.validateAndExtractRequiredField(storeDataContext.storeData(), "data", storeTestData.sourceInformation); - storeTestData.data = HelperEmbeddedDataGrammarParser.parseEmbeddedData(dataContext.embeddedData(), this.walkerSourceInformation, this.parserContext.getPureGrammarParserExtensions()); - return storeTestData; + ParameterValue parameterValue = new ParameterValue(); + DomainParser parser = new DomainParser(); + int startLine = primitiveValueContext.getStart().getLine(); + int lineOffset = walkerSourceInformation.getLineOffset() + startLine - 1; + int columnOffset = (startLine == 1 ? walkerSourceInformation.getColumnOffset() : 0) + primitiveValueContext.getStart().getCharPositionInLine(); + ParseTreeWalkerSourceInformation serviceParamSourceInformation = new ParseTreeWalkerSourceInformation.Builder(walkerSourceInformation.getSourceId(), lineOffset, columnOffset).build(); + String expectedValue = primitiveValueContext.start.getInputStream().getText(Interval.of(primitiveValueContext.start.getStartIndex(), primitiveValueContext.stop.getStopIndex())); + parameterValue.value = parser.parsePrimitiveValue(expectedValue, serviceParamSourceInformation, null); + if (variable != null) + { + parameterValue.name = variable.name; + } + parameterValue.sourceInformation = this.walkerSourceInformation.getSourceInformation(primitiveValueContext); + return parameterValue; } - private org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTest visitFunctionTest(DomainParserGrammar.FunctionTestBlockContext ctx) + private org.finos.legend.engine.protocol.pure.v1.model.test.assertion.TestAssertion visitTestAssertion(DomainParserGrammar.SimpleFunctionTestContext functionTestSuiteContext) { - org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTest functionTest = new org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTest(); - functionTest.sourceInformation = this.walkerSourceInformation.getSourceInformation(ctx); - functionTest.id = PureGrammarParserUtility.fromIdentifier(ctx.identifier()); - // parameters value - DomainParserGrammar.FunctionTestParametersContext testParameterContext = PureGrammarParserUtility.validateAndExtractOptionalField(ctx.functionTestParameters(), "parameters", functionTest.sourceInformation); - if (testParameterContext != null) + TestAssertion assertion; + if (functionTestSuiteContext.externalFormatValue() != null) { - functionTest.parameters = ListIterate.collect(testParameterContext.functionTestParameter(), this::visitFunctionTestParameter); + String contentType = PureGrammarParserUtility.fromIdentifier(functionTestSuiteContext.externalFormatValue().contentType().identifier()); + String value = PureGrammarParserUtility.fromGrammarString(functionTestSuiteContext.externalFormatValue().STRING().getText(), true); + assertion = createEqualToJSON(contentType, value, walkerSourceInformation.getSourceInformation(functionTestSuiteContext.externalFormatValue())); } - DomainParserGrammar.FunctionTestAssertsContext testAssertsContext = PureGrammarParserUtility.validateAndExtractRequiredField(ctx.functionTestAsserts(), "asserts", functionTest.sourceInformation); - functionTest.assertions = ListIterate.collect(testAssertsContext.functionTestAssert(), this::visitFunctionTestAsserts); - return functionTest; + else + { + DomainParser parser = new DomainParser(); + int startLine = functionTestSuiteContext.primitiveValue().getStart().getLine(); + int lineOffset = walkerSourceInformation.getLineOffset() + startLine - 1; + int columnOffset = (startLine == 1 ? walkerSourceInformation.getColumnOffset() : 0) + functionTestSuiteContext.primitiveValue().getStart().getCharPositionInLine(); + ParseTreeWalkerSourceInformation serviceParamSourceInformation = new ParseTreeWalkerSourceInformation.Builder(walkerSourceInformation.getSourceId(), lineOffset, columnOffset).build(); + String expectedValue = functionTestSuiteContext.primitiveValue().start.getInputStream().getText(Interval.of(functionTestSuiteContext.primitiveValue().start.getStartIndex(), functionTestSuiteContext.primitiveValue().stop.getStopIndex())); + ValueSpecification valueSpecification = parser.parsePrimitiveValue(expectedValue, serviceParamSourceInformation, null); + EqualTo equalToAssertion = new EqualTo(); + equalToAssertion.expected = valueSpecification; + assertion = equalToAssertion; + } + assertion.id = DEFAULT_TESTABLE_ID; + return assertion; } - private TestAssertion visitFunctionTestAsserts(DomainParserGrammar.FunctionTestAssertContext ctx) + private EqualToJson createEqualToJSON(String contentType, String val, SourceInformation sourceInformation) { - TestAssertion testAssertion = HelperTestAssertionGrammarParser.parseTestAssertion(ctx.testAssertion(), this.walkerSourceInformation, this.parserContext.getPureGrammarParserExtensions()); - testAssertion.id = PureGrammarParserUtility.fromIdentifier(ctx.identifier()); - return testAssertion; + EqualToJson equalToJson = new EqualToJson(); + equalToJson.expected = visitExternalFormat(contentType, val, sourceInformation); + equalToJson.sourceInformation = sourceInformation; + return equalToJson; } - private ParameterValue visitFunctionTestParameter(DomainParserGrammar.FunctionTestParameterContext ctx) + private ExternalFormatData visitExternalFormat(String contentType, String val, SourceInformation sourceInformation) { - ParameterValue parameterValue = new ParameterValue(); - parameterValue.name = PureGrammarParserUtility.fromIdentifier(ctx.identifier()); - parameterValue.value = this.visitTestParameter(ctx.primitiveValue()); - return parameterValue; + ExternalFormatData externalFormatData = new ExternalFormatData(); + externalFormatData.sourceInformation = sourceInformation; + externalFormatData.data = val; + if (contentType.equals(KNOWN_JSON_CONTENT_TYPE)) + { + externalFormatData.contentType = "application/json"; + } + else if (contentType.equals(KNOWN_XML_CONTENT_TYPE)) + { + externalFormatData.contentType = "application/xml"; + } + else + { + externalFormatData.contentType = contentType; + } + return externalFormatData; } - private ValueSpecification visitTestParameter(DomainParserGrammar.PrimitiveValueContext primitiveValueContext) + private org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.StoreTestData processStoreTestData(DomainParserGrammar.FunctionDataContext functionDataContext) { - DomainParser parser = new DomainParser(); - int startLine = primitiveValueContext.getStart().getLine(); - int lineOffset = walkerSourceInformation.getLineOffset() + startLine - 1; - int columnOffset = (startLine == 1 ? walkerSourceInformation.getColumnOffset() : 0) + primitiveValueContext.getStart().getCharPositionInLine(); - ParseTreeWalkerSourceInformation serviceParamSourceInformation = new ParseTreeWalkerSourceInformation.Builder(walkerSourceInformation.getSourceId(), lineOffset, columnOffset).build(); - String expectedValue = primitiveValueContext.start.getInputStream().getText(Interval.of(primitiveValueContext.start.getStartIndex(), primitiveValueContext.stop.getStopIndex())); - ValueSpecification valueSpecification = parser.parsePrimitiveValue(expectedValue, serviceParamSourceInformation, null); - return valueSpecification; + StoreTestData storeTestData = new StoreTestData(); + storeTestData.store = PureGrammarParserUtility.fromQualifiedName(functionDataContext.qualifiedName().packagePath() == null ? Collections.emptyList() : functionDataContext.qualifiedName().packagePath().identifier(), functionDataContext.qualifiedName().identifier()); + if (functionDataContext.functionDataValue() != null) + { + DomainParserGrammar.FunctionDataValueContext dataValueContext = functionDataContext.functionDataValue(); + if (dataValueContext.qualifiedName() != null) + { + DataElementReference dataElementReference = new DataElementReference(); + dataElementReference.dataElement = PureGrammarParserUtility.fromQualifiedName(dataValueContext.qualifiedName().packagePath() == null ? Collections.emptyList() : dataValueContext.qualifiedName().packagePath().identifier(), dataValueContext.qualifiedName().identifier()); + storeTestData.data = dataElementReference; + dataElementReference.sourceInformation = this.walkerSourceInformation.getSourceInformation(dataValueContext.qualifiedName()); + } + else if (dataValueContext.externalFormatValue() != null) + { + String contentType = PureGrammarParserUtility.fromIdentifier(dataValueContext.externalFormatValue().contentType().identifier()); + SourceInformation sourceInformation = this.walkerSourceInformation.getSourceInformation(dataValueContext.externalFormatValue()); + String value = PureGrammarParserUtility.fromGrammarString(dataValueContext.externalFormatValue().STRING().getText(), true); + storeTestData.data = this.visitExternalFormat(contentType, value, sourceInformation); + } + else if (dataValueContext.embeddedData() != null) + { + storeTestData.data = HelperEmbeddedDataGrammarParser.parseEmbeddedData(dataValueContext.embeddedData(), this.walkerSourceInformation, this.parserContext.getPureGrammarParserExtensions()); + } + } + return storeTestData; } // ----------------------------------------------- MEASURE ----------------------------------------------- diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperDomainGrammarComposer.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperDomainGrammarComposer.java index d8b2310a789..a2e665bc460 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperDomainGrammarComposer.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperDomainGrammarComposer.java @@ -17,13 +17,14 @@ import org.eclipse.collections.impl.utility.LazyIterate; import org.eclipse.collections.impl.utility.ListIterate; import org.finos.legend.engine.language.pure.grammar.to.data.HelperEmbeddedDataGrammarComposer; -import org.finos.legend.engine.language.pure.grammar.to.test.assertion.HelperTestAssertionGrammarComposer; import org.finos.legend.engine.protocol.pure.v1.model.context.EngineErrorType; +import org.finos.legend.engine.protocol.pure.v1.model.data.DataElementReference; +import org.finos.legend.engine.protocol.pure.v1.model.data.EmbeddedData; +import org.finos.legend.engine.protocol.pure.v1.model.data.ExternalFormatData; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.AggregationKind; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Constraint; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Function; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Multiplicity; -import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.ParameterValue; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Property; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.QualifiedProperty; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.StereotypePtr; @@ -32,6 +33,9 @@ import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTest; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.FunctionTestSuite; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.function.StoreTestData; +import org.finos.legend.engine.protocol.pure.v1.model.test.assertion.EqualTo; +import org.finos.legend.engine.protocol.pure.v1.model.test.assertion.EqualToJson; +import org.finos.legend.engine.protocol.pure.v1.model.test.assertion.TestAssertion; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.Variable; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; @@ -46,6 +50,11 @@ public class HelperDomainGrammarComposer { + + private static String DEFAULT_TESTABLE_ID = "default"; + private static final String APPLICATION_JSON = "application/json"; + private static final String APPLICATION_XML = "application/xml"; + public static String renderStereotypePointer(StereotypePtr stereotypePtr) { return PureGrammarComposerUtility.convertPath(stereotypePtr.profile) + "." + PureGrammarComposerUtility.convertIdentifier(stereotypePtr.value); @@ -179,90 +188,123 @@ public static String renderFunctionTestSuites(Function function, PureGrammarComp { return stringBuilder.toString(); } - stringBuilder.append("\n[\n"); - stringBuilder.append(String.join(",\n", ListIterate.collect(function.tests, suite -> renderFunctionTestSuite(suite, context)))).append("\n"); - stringBuilder.append("]"); + stringBuilder.append("\n{\n"); + stringBuilder.append(String.join("\n" + (function.tests.size() > 1 ? "\n" : ""), ListIterate.collect(function.tests, suite -> renderFunctionTestSuite(function, suite, context)))); + stringBuilder.append("\n}"); return stringBuilder.toString(); } - public static String renderFunctionTestSuite(FunctionTestSuite functionTestSuite, PureGrammarComposerContext context) + public static String renderFunctionTestSuite(Function function, FunctionTestSuite functionTestSuite, PureGrammarComposerContext context) { int baseIndentation = 1; StringBuilder str = new StringBuilder(); - str.append(getTabString(baseIndentation)).append(functionTestSuite.id).append(":\n"); - str.append(getTabString(baseIndentation)).append("{\n"); - if (functionTestSuite.testData != null) + if (!functionTestSuite.id.equals(DEFAULT_TESTABLE_ID)) { - str.append(getTabString(baseIndentation + 1)).append("data").append(":\n"); - str.append(getTabString(baseIndentation + 1)).append("[\n"); - str.append(String.join(",\n", ListIterate.collect(functionTestSuite.testData, storeData -> renderFunctionTestData(storeData, baseIndentation + 2, context)))).append("\n"); - str.append(getTabString(baseIndentation + 1)).append("];\n"); + str.append(getTabString(baseIndentation)).append(functionTestSuite.id).append("\n").append(getTabString(baseIndentation)).append("(\n"); + if (functionTestSuite.testData != null && !functionTestSuite.testData.isEmpty()) + { + str.append(String.join("\n", ListIterate.collect(functionTestSuite.testData, test -> renderFunctionTestData(test, baseIndentation + 1, context)))).append("\n"); + } + if (functionTestSuite.tests != null) + { + str.append(String.join("\n", ListIterate.collect(functionTestSuite.tests, test -> renderFunctionTest(function, (FunctionTest) test, baseIndentation + 1, context)))); + } + str.append("\n").append(getTabString(baseIndentation)).append(")"); } - // tests - if (functionTestSuite.tests != null) + else { - str.append(getTabString(baseIndentation + 1)).append("tests").append(":\n"); - str.append(getTabString(baseIndentation + 1)).append("[\n"); - str.append(String.join(",\n", ListIterate.collect(functionTestSuite.tests, test -> renderFunctionTest((FunctionTest) test, baseIndentation + 2, context)))).append("\n"); - str.append(getTabString(baseIndentation + 1)).append("]\n"); + if (functionTestSuite.testData != null && !functionTestSuite.testData.isEmpty()) + { + str.append(String.join("\n", ListIterate.collect(functionTestSuite.testData, test -> renderFunctionTestData(test, baseIndentation, context)))).append("\n"); + } + if (functionTestSuite.tests != null) + { + str.append(String.join("\n", ListIterate.collect(functionTestSuite.tests, test -> renderFunctionTest(function, (FunctionTest) test, baseIndentation, context)))); + } } - str.append(getTabString(baseIndentation)).append("}"); return str.toString(); } public static String renderFunctionTestData(StoreTestData storeTestData, int currentInt, PureGrammarComposerContext context) { StringBuilder dataStrBuilder = new StringBuilder(); - dataStrBuilder.append(getTabString(currentInt)).append("{\n"); - dataStrBuilder.append(getTabString(currentInt + 1)).append("store").append(": ").append(storeTestData.store).append(";\n"); - dataStrBuilder.append(getTabString(currentInt + 1)).append("data").append(":\n"); - dataStrBuilder.append(HelperEmbeddedDataGrammarComposer.composeEmbeddedData(storeTestData.data, PureGrammarComposerContext.Builder.newInstance(context).withIndentationString(getTabString(currentInt + 2)).build())); - dataStrBuilder.append(";\n"); - dataStrBuilder.append(getTabString(currentInt)).append("}"); + dataStrBuilder.append(getTabString(currentInt)); + dataStrBuilder.append(storeTestData.store + ":"); + EmbeddedData embeddedData = storeTestData.data; + if (embeddedData instanceof DataElementReference) + { + dataStrBuilder.append(" "); + dataStrBuilder.append(((DataElementReference) embeddedData).dataElement); + } + else if (embeddedData instanceof ExternalFormatData) + { + dataStrBuilder.append(" "); + dataStrBuilder.append(renderSimpleExternalFormat(((ExternalFormatData) embeddedData))); + } + else + { + dataStrBuilder.append("\n"); + dataStrBuilder.append(HelperEmbeddedDataGrammarComposer.composeEmbeddedData(storeTestData.data, PureGrammarComposerContext.Builder.newInstance(context).withIndentationString(getTabString(currentInt + 2)).build())); + } + dataStrBuilder.append(";"); return dataStrBuilder.toString(); } - public static String renderFunctionTest(FunctionTest functionTest, int baseInd, PureGrammarComposerContext context) + public static String renderFunctionTest(Function function, FunctionTest functionTest, int currentInt, PureGrammarComposerContext context) { StringBuilder str = new StringBuilder(); - str.append(getTabString(baseInd)).append(functionTest.id).append(":\n"); - str.append(getTabString(baseInd)).append("{\n"); - - if (functionTest.doc != null) + str.append(getTabString(currentInt)).append(functionTest.id).append(" | ").append(HelperValueSpecificationGrammarComposer.getFunctionNameWithNoPackage(function)).append("("); + if (functionTest.parameters != null && !functionTest.parameters.isEmpty()) { - str.append(getTabString(baseInd + 1)).append("doc: ").append(convertString(functionTest.doc, true) + ";\n"); + str.append(LazyIterate.collect(functionTest.parameters, parameterValue -> parameterValue.value.accept(DEPRECATED_PureGrammarComposerCore.Builder.newInstance(context).build())).makeString(",")); } - // Parameters - if (functionTest.parameters != null && !functionTest.parameters.isEmpty()) + str.append(") => "); + if (functionTest.assertions.size() > 1) { - str.append(getTabString(baseInd + 1)).append("parameters:\n"); - str.append(getTabString(baseInd + 1)).append("[\n"); - str.append(String.join(",\n", ListIterate.collect(functionTest.parameters, param -> renderFunctionTestParam(param, baseInd + 2, context)))).append("\n"); - str.append(getTabString(baseInd + 1)).append("]\n"); + throw new EngineException("Unable to generate grammar for function tests with more than one assertion", functionTest.sourceInformation, EngineErrorType.COMPOSER); } - // Assertions - if (functionTest.assertions != null && !functionTest.assertions.isEmpty()) + if (functionTest.assertions.size() == 1) { - str.append(getTabString(baseInd + 1)).append("asserts:\n"); - str.append(getTabString(baseInd + 1)).append("[\n"); - str.append(String.join(",\n", ListIterate.collect(functionTest.assertions, testAssertion -> HelperTestAssertionGrammarComposer.composeTestAssertion(testAssertion, PureGrammarComposerContext.Builder.newInstance(context).withIndentationString(getTabString(baseInd + 2)).build())))).append("\n"); - str.append(getTabString(baseInd + 1)).append("]\n"); + str.append(renderTestAssertion(functionTest.assertions.get(0), context)); } - str.append(getTabString(baseInd)).append("}"); + str.append(";"); return str.toString(); } - - private static String renderFunctionTestParam(ParameterValue parameterValue, int baseIndentation, PureGrammarComposerContext context) + private static String renderTestAssertion(TestAssertion testAssertion, PureGrammarComposerContext context) { - StringBuilder str = new StringBuilder(); - - str.append(getTabString(baseIndentation)).append(parameterValue.name); - str.append(" = "); - str.append(parameterValue.value.accept(DEPRECATED_PureGrammarComposerCore.Builder.newInstance(context).build())); + if (testAssertion instanceof EqualTo) + { + return ((EqualTo)testAssertion).expected.accept(DEPRECATED_PureGrammarComposerCore.Builder.newInstance(context).build()); + } + else if (testAssertion instanceof EqualToJson) + { + EqualToJson equalToJson = (EqualToJson) testAssertion; + ExternalFormatData externalFormatData = equalToJson.expected; + return renderSimpleExternalFormat(externalFormatData); + } + else + { + throw new EngineException("Unknown test assertion type: " + testAssertion.toString(), testAssertion.sourceInformation, EngineErrorType.COMPOSER); + } + } - return str.toString(); + private static String renderSimpleExternalFormat(ExternalFormatData externalFormatData) + { + if (externalFormatData.contentType.equals(APPLICATION_JSON)) + { + return "(JSON) " + "'" + externalFormatData.data + "'"; + } + else if (externalFormatData.contentType.equals(APPLICATION_XML)) + { + return "(XML) " + "'" + externalFormatData.data + "'"; + } + else + { + return "(" + externalFormatData.contentType + ") " + "'" + externalFormatData.data + "'"; + } } + } diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperValueSpecificationGrammarComposer.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperValueSpecificationGrammarComposer.java index e663d669534..5229ff12e92 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperValueSpecificationGrammarComposer.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/main/java/org/finos/legend/engine/language/pure/grammar/to/HelperValueSpecificationGrammarComposer.java @@ -254,6 +254,12 @@ public static String getFunctionName(org.finos.legend.engine.protocol.pure.v1.mo return fn._package == null || fn._package.isEmpty() ? name : fn._package + "::" + name; } + public static String getFunctionNameWithNoPackage(org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Function fn) + { + int signatureIndex = fn.name.indexOf(getFunctionSignature(fn)); + return signatureIndex > 0 ? fn.name.substring(0, signatureIndex) : fn.name; + } + public static String getFunctionSignature(Function function) { String functionSignature = LazyIterate.collect(function.parameters, HelperValueSpecificationGrammarComposer::getParameterSignature).select(Objects::nonNull).makeString("__") diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestGrammarParser.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestGrammarParser.java index d3534abe020..aa616ede4e6 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestGrammarParser.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestGrammarParser.java @@ -164,6 +164,21 @@ public void testInvalidPropertyAggregationKind() "}\n", "PARSER error at [4:4-11]: Unexpected token 'entrance'"); } + + @Test + public void testFunction() + { + test("function my::SimpleFunction(): String[1]\n" + + "{\n" + + " 'Hello World!'\n" + + "}\n" + + "{\n" + + " myTest | SimpleFunctionMisMatch() => 'Hello World!';\n" + + "}", + "PARSER error at [6:12-33]: Function name in test 'SimpleFunctionMisMatch' does not match function name 'SimpleFunction'" + ); + } + public static void testFromJson(Class _class, String path, String code) { PureModelContextData modelData = null; diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/roundtrip/TestDomainGrammarRoundtrip.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/roundtrip/TestDomainGrammarRoundtrip.java index 0b779006f67..35819e8f84f 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/roundtrip/TestDomainGrammarRoundtrip.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-language-pure-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/roundtrip/TestDomainGrammarRoundtrip.java @@ -490,131 +490,76 @@ public void testFunction() @Test public void testFunctionTest() { - test("function model::Simple(): String[1]\n" + + + test("function my::SimpleFunction(): String[1]\n" + "{\n" + - " 'Hello ' + ' World!'\n" + + " 'Hello World!'\n" + "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " test_1:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '{}';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n\n" + - "function model::Simple2(): String[1]\n" + "{\n" + - " 'Hello ' + ' World!'\n" + + " myTest | SimpleFunction() => 'Hello World!';\n" + + "}\n\n" + + "function my::Hello(name: String[1]): String[1]\n" + + "{\n" + + " 'Hello ' + $name\n" + "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " test_1:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '{}';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n"); - test("function model::P(): String[1]\n" + "{\n" + - " 'x'\n" + + " myTest | Hello('John') => 'Hello John!';\n" + + "}\n"); + + test("function my::Hello(name: String[1], age: Integer[1]): String[1]\n" + + "{\n" + + " 'Hello ' + $name\n" + + "}\n" + + "{\n" + + " myTest | Hello('John',20) => 'Hello John!';\n" + + " myOtherTest | Hello('Nicole',20) => 'Hello Nicole!';\n" + + "}\n"); + test("function my::Hello(name: String[1]): String[1]\n" + + "{\n" + + " 'Hello ' + $name\n" + "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " data:\n" + - " [\n" + - " {\n" + - " store: store::TestDB;\n" + - " data:\n" + - " Reference\n" + - " #{\n" + - " testServiceStoreTestSuites::TestData\n" + - " }#;\n" + - " }\n" + - " ];\n" + - " tests:\n" + - " [\n" + - " test_1:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[]';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n" - ); + "{\n" + + " myTest | Hello('John') => 'Hello John!';\n" + + " myTest | Hello('Nicole') => 'Hello Nicole!';\n" + + "\n" + + " MySuite\n" + + " (\n" + + " myTest | Hello('John') => 'Hello John!';\n" + + " myTest | Hello('Nicole') => 'Hello Nicole!';\n" + + " )\n\n" + + " MySuite2\n" + + " (\n" + + " ModelStore: (JSON) '{}';\n" + + " store::MyStore: testing::MyReference;\n" + + " myTest | Hello('John') => (XML) 'Hello John!';\n" + + " myTest | Hello('Nicole') => (JSON) 'Hello Nicole!';\n" + + " )\n" + + "}\n"); - test("function model::Hello(name: String[1]): String[1]\n" + + test("function my::Hello(name: String[1], age: Integer[1]): String[1]\n" + "{\n" + - " 'Hello! My name is ' + $name + '.'\n" + + " 'Hello ' + $name\n" + "}\n" + - "[\n" + - " testSuite_1:\n" + - " {\n" + - " tests:\n" + - " [\n" + - " testFail:\n" + - " {\n" + - " parameters:\n" + - " [\n" + - " name = 'John'\n" + - " ]\n" + - " asserts:\n" + - " [\n" + - " assertion_1:\n" + - " EqualTo\n" + - " #{\n" + - " expected:\n" + - " 'Hello! My name is John.';\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]\n"); + "{\n" + + " ModelStore: (JSON) '{}';\n" + + " store::MyStore: testing::MyReference;\n" + + " myTest | Hello('John',20) => 'Hello John!';\n" + + " myOtherTest | Hello('Nicole',20) => 'Hello Nicole!';\n" + + "}\n"); + test("function my::Hello(name: String[1]): String[1]\n" + + "{\n" + + " 'Hello ' + $name\n" + + "}\n" + + "{\n" + + " MySuite2\n" + + " (\n" + + " ModelStore: (JSON) '{}';\n" + + " store::MyStore: testing::MyReference;\n" + + " myTest | Hello('John') => (XML) 'Hello John!';\n" + + " myTest | Hello('Nicole') => (JSON) 'Hello Nicole!';\n" + + " )\n" + + "}\n"); + } @Test diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/domain/ParameterValue.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/domain/ParameterValue.java index 9be46eef7e4..fdad9dc8b11 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/domain/ParameterValue.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/domain/ParameterValue.java @@ -14,10 +14,12 @@ package org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain; +import org.finos.legend.engine.protocol.pure.v1.model.SourceInformation; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.ValueSpecification; public class ParameterValue { + public SourceInformation sourceInformation; public String name; public ValueSpecification value; } diff --git a/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/function/FunctionTestSuite.java b/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/function/FunctionTestSuite.java index a86eb0b0e80..531e4b62d9b 100644 --- a/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/function/FunctionTestSuite.java +++ b/legend-engine-core/legend-engine-core-language-pure/legend-engine-protocol-pure/src/main/java/org/finos/legend/engine/protocol/pure/v1/model/packageableElement/function/FunctionTestSuite.java @@ -21,7 +21,6 @@ public class FunctionTestSuite extends TestSuite { - public List connectionsTestData; public List testData; diff --git a/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-m2m.pure b/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-m2m.pure index 13fa6d37371..e9699e5eff7 100644 --- a/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-m2m.pure +++ b/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-m2m.pure @@ -28,41 +28,13 @@ function model::PersonQuery(): String[1] model::MyRuntime ) } -[ - testSuite_1: - { - data: - [ - { - store: ModelStore; - data: ExternalFormat - #{ - contentType: 'application/json'; - data: '{\n "firstName": "John",\n "lastName": "Doe"\n}'; - }#; - } - ]; - tests: - [ - test_1: - { - asserts: - [ - assertion_1: - EqualToJson - #{ - expected: - ExternalFormat - #{ - contentType: 'application/json'; - data: '{\n "fullName" : "John Doe"\n}'; - }#; - }# - ] - } - ] - } - ] +{ + testSuite_1 + ( + ModelStore: (JSON) '{\n "firstName": "John",\n "lastName": "Doe"\n}'; + test_1 | PersonQuery() => (JSON) '{\n "fullName" : "John Doe"\n}'; + ) +} ###Mapping Mapping model::M2MMapping diff --git a/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-relational.pure b/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-relational.pure index ccef9eb6e85..40b6f8bbc95 100644 --- a/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-relational.pure +++ b/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model-relational.pure @@ -140,14 +140,10 @@ function model::PersonQuery(): meta::pure::tds::TabularDataSet[1] execution::Runtime ) } -[ - testSuite_1: - { - data: - [ - { - store: store::TestDB; - data: +{ + testSuite_1 + ( + store::TestDB: Relational #{ default.PersonTable: @@ -156,29 +152,9 @@ function model::PersonQuery(): meta::pure::tds::TabularDataSet[1] '2,1,Nicole,Smith,FTC\n'+ '3,2,Time,Smith,FTE\n'; }#; - } - ]; - tests: - [ - test_1: - { - asserts: - [ - assertion_1: - EqualToJson - #{ - expected: - ExternalFormat - #{ - contentType: 'application/json'; - data: '[ {\n "First Name" : "I\'m John",\n "Last Name" : "Doe, Jr"\n}, {\n "First Name" : "Nicole",\n "Last Name" : "Smith"\n}, {\n "First Name" : "Time",\n "Last Name" : "Smith"\n} ]'; - }#; - }# - ] - } - ] - } -] + test_1 | PersonQuery() => (JSON) '[ {\n "First Name" : "I\'m John",\n "Last Name" : "Doe, Jr"\n}, {\n "First Name" : "Nicole",\n "Last Name" : "Smith"\n}, {\n "First Name" : "Time",\n "Last Name" : "Smith"\n} ]'; + ) +} function model::PersonWithParams(firstName: String[1]): meta::pure::tds::TabularDataSet[1] { model::Person.all()->filter( @@ -198,14 +174,10 @@ function model::PersonWithParams(firstName: String[1]): meta::pure::tds::Tabular execution::Runtime ) } -[ - testSuite_1: - { - data: - [ - { - store: store::TestDB; - data: +{ + testSuite_1 + ( + store::TestDB: Relational #{ default.PersonTable: @@ -214,50 +186,7 @@ function model::PersonWithParams(firstName: String[1]): meta::pure::tds::Tabular '2,1,Nicole,Smith,FTC\n'+ '3,2,Time,Smith,FTE\n'; }#; - } - ]; - tests: - [ - testFail: - { - parameters: - [ - firstName = 'Nicole' - ] - asserts: - [ - assertion_1: - EqualToJson - #{ - expected: - ExternalFormat - #{ - contentType: 'application/json'; - data: '[]'; - }#; - }# - ] - }, - testPass: - { - parameters: - [ - firstName = 'Nicole' - ] - asserts: - [ - assertion_1: - EqualToJson - #{ - expected: - ExternalFormat - #{ - contentType: 'application/json'; - data: '[ {\n "First Name" : "Nicole",\n "Last Name" : "Smith"\n} ]'; - }#; - }# - ] - } - ] - } -] \ No newline at end of file + testFail | PersonWithParams('Nicole') => (JSON) '[]'; + testPass | PersonWithParams('Nicole') => (JSON) '[{\n "First Name" : "Nicole","Last Name" : "Smith"} ]'; + ) +} \ No newline at end of file diff --git a/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model.pure b/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model.pure index 0a4c0c639ac..0dc31d00e04 100644 --- a/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model.pure +++ b/legend-engine-core/legend-engine-core-test/legend-engine-test-runner-function/src/test/resources/testable/legend-testable-function-test-model.pure @@ -2,42 +2,10 @@ function model::Hello(name: String[1]): String[1] { 'Hello World! My name is ' + $name + '.'; } -[ - testSuite_1: - { - tests: - [ - testPass: - { - parameters: - [ - name = 'John' - ] - asserts: - [ - assertion_1: - EqualTo - #{ - expected: 'Hello World! My name is John.'; - }# - ] - }, - testFail: - { - parameters: - [ - name = 'John' - ] - asserts: - [ - assertion_1: - EqualTo - #{ - expected: 'Hello World! My name is Johnx.'; - }# - ] - } - - ] - } -] +{ + testSuite_1 + ( + testPass | Hello('John') => 'Hello World! My name is John.'; + testFail | Hello('John') => 'Hello World! My name is Johnx.'; + ) +} diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestRelationalGrammarRoundtrip.java b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestRelationalGrammarRoundtrip.java index 8831ffaf99d..c474bc1ac53 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestRelationalGrammarRoundtrip.java +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/grammar/test/TestRelationalGrammarRoundtrip.java @@ -881,4 +881,31 @@ public void testEnumDynaFunctionWithJoin() ); } + + @Test + public void testFunctionTest() + + { + test("function model::PersonWithParams(firstName: String[1]): meta::pure::tds::TabularDataSet[1]\n" + + "{\n" + + " model::Person.all()->filter(x|$x.firstName == $firstName)->project([x|$x.firstName, x|$x.lastName], ['First Name', 'Last Name'])->from(execution::RelationalMapping, execution::Runtime)\n" + + "}\n" + + "{\n" + + " testSuite_1\n" + + " (\n" + + " store::TestDB:\n" + + " Relational\n" + + " #{\n" + + " default.PersonTable:\n" + + " 'id,firm_id,firstName,lastName,employeeType\\n'+\n" + + " '1,1,I\\'m John,\"Doe, Jr\",FTO\\n'+\n" + + " '2,1,Nicole,Smith,FTC\\n'+\n" + + " '3,2,Time,Smith,FTE\\n';\n" + + " }#;\n" + + " testFail | PersonWithParams('Nicole') => (JSON) '[]';\n" + + " testPass | PersonWithParams('Nicole') => (JSON) '[{ \"First Name\" : \"Nicole\", \"Last Name\" : \"Smith\"} ]';\n" + + " )\n" + + "}\n"); + } + }