From c3a3a4a05bf54d7571e149dabaa1ace18fefe090 Mon Sep 17 00:00:00 2001 From: Jonathan Neugebauer Date: Tue, 15 Dec 2020 23:02:50 +0100 Subject: [PATCH 1/5] Improve modularity in use of language elements - Change mdAL DSL grammar to allow leaving parts out - Update and refactor tests accordingly --- .../mdal/ide/MdalLanguageServerTest.xtend | 10 + .../src/main/java/de/joneug/mdal/Mdal.xtext | 6 +- .../mdal/util/ExampleContentGenerator.xtend | 31 ++ .../joneug/mdal/validation/MdalValidator.java | 9 +- .../mdal/validation/SolutionValidator.xtend | 31 ++ .../java/de/joneug/mdal/MdalParsingTest.xtend | 63 ++-- .../validation/CustomFieldValidatorTest.xtend | 47 +++ .../mdal/validation/EntityValidatorTest.xtend | 354 ++++++++++++++++++ .../mdal/validation/GroupValidatorTest.xtend | 55 +++ .../IncludeFieldValidatorTest.xtend | 99 +++++ .../validation/PageFieldValidatorTest.xtend | 49 +++ .../validation/SolutionValidatorTest.xtend | 77 ++++ 12 files changed, 785 insertions(+), 46 deletions(-) create mode 100644 de.joneug.mdal/src/main/java/de/joneug/mdal/validation/SolutionValidator.xtend create mode 100644 de.joneug.mdal/src/test/java/de/joneug/mdal/validation/CustomFieldValidatorTest.xtend create mode 100644 de.joneug.mdal/src/test/java/de/joneug/mdal/validation/EntityValidatorTest.xtend create mode 100644 de.joneug.mdal/src/test/java/de/joneug/mdal/validation/GroupValidatorTest.xtend create mode 100644 de.joneug.mdal/src/test/java/de/joneug/mdal/validation/IncludeFieldValidatorTest.xtend create mode 100644 de.joneug.mdal/src/test/java/de/joneug/mdal/validation/PageFieldValidatorTest.xtend create mode 100644 de.joneug.mdal/src/test/java/de/joneug/mdal/validation/SolutionValidatorTest.xtend diff --git a/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend b/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend index 69d710d..a639b75 100644 --- a/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend +++ b/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend @@ -22,6 +22,16 @@ class MdalLanguageServerTest extends AbstractLanguageServerTest { val capabilities = initialize().capabilities assertTrue(capabilities.definitionProvider && capabilities.documentFormattingProvider) } + + @Test + def void testMinimalModel() { + initialize() + + val file = 'seminar.mdal'.writeFile("") + file.open(ExampleContentGenerator.generateMinimalModel.toString) + val diagnostics = diagnostics.get(file) + assertTrue('''Unexpected errors: «diagnostics»''', diagnostics.empty) + } @Test def void testCorrectModel() { diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/Mdal.xtext b/de.joneug.mdal/src/main/java/de/joneug/mdal/Mdal.xtext index 85d45bd..5e8ac93 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/Mdal.xtext +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/Mdal.xtext @@ -9,10 +9,10 @@ Solution: 'solution' name=STRING '{' ( ('Prefix' '=' prefix=STRING ';')? & - (master=Master) & + (master=Master)? & (supplementals+=Supplemental)* & - (document=Document) & - (ledgerEntry=LedgerEntry) + (document=Document)? & + (ledgerEntry=LedgerEntry)? ) '}' ; diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend index cf454a3..1d3c1c7 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend @@ -1,6 +1,37 @@ package de.joneug.mdal.util class ExampleContentGenerator { + + static def generateMinimalModel() ''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + field("Duration Days"; Decimal) + template("Dimensions"; Dimensions) + } + + cardPage { + group("General") { + field("Description") + field("Duration Days") + } + group("Posting Details") { + field("Dimensions") + } + } + + listPage { + field("Description") + field("Duration Days") + } + } + } + ''' static def generateCorrectModel() ''' solution "Seminar Management" { diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/MdalValidator.java b/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/MdalValidator.java index bf71d3c..19be520 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/MdalValidator.java +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/MdalValidator.java @@ -7,9 +7,10 @@ * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation */ -@ComposedChecks(validators = {IncludeFieldValidator.class, CustomFieldValidator.class, EntityValidator.class, PageFieldValidator.class, GroupValidator.class}) +@ComposedChecks(validators = { IncludeFieldValidator.class, CustomFieldValidator.class, EntityValidator.class, + PageFieldValidator.class, GroupValidator.class, SolutionValidator.class }) public class MdalValidator extends AbstractMdalValidator { - + public static final String FIELD_NAME_EXISTS = "f-name-exists"; public static final String ENTITY_NAME_EXISTS = "e-name-exists"; public static final String GROUP_NAME_EXISTS = "g-name-exists"; @@ -18,6 +19,8 @@ public class MdalValidator extends AbstractMdalValidator { public static final String INCLUDE_FIELD_UNKNOWN_ENTITY = "if-unknown-entity"; public static final String INCLUDE_FIELD_UNKNOWN_FIELD = "if-unknown-field"; public static final String CUSTOM_FIELD_UNKNOWN_TABLE = "cf-unknown-table"; - public static final String PAGE_FIELD_UNKNOWN_FIELD= "pf-unknown-field"; + public static final String PAGE_FIELD_UNKNOWN_FIELD = "pf-unknown-field"; + public static final String DOCUMENT_NO_MASTER = "doc-no-master"; + public static final String LEDGER_ENTRY_NO_DOCUMENT = "le-no-document"; } diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/SolutionValidator.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/SolutionValidator.xtend new file mode 100644 index 0000000..272d87e --- /dev/null +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/validation/SolutionValidator.xtend @@ -0,0 +1,31 @@ +package de.joneug.mdal.validation + +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Solution +import org.eclipse.xtext.validation.AbstractDeclarativeValidator +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.EValidatorRegistrar + +class SolutionValidator extends AbstractDeclarativeValidator { + + override register(EValidatorRegistrar registrar) {} + + @Check + def checkRequiredEntites(Solution solution) { + if (solution.master === null && solution.document !== null) { + error( + '''A master entity corresponding to the document «solution.document.name» has to be defined.''', + MdalPackage.Literals.SOLUTION__DOCUMENT, + MdalValidator.DOCUMENT_NO_MASTER + ) + } + if (solution.document === null && solution.ledgerEntry !== null) { + error( + '''A document entity corresponding to the ledger entry «solution.ledgerEntry.name» has to be defined.''', + MdalPackage.Literals.SOLUTION__LEDGER_ENTRY, + MdalValidator.LEDGER_ENTRY_NO_DOCUMENT + ) + } + } + +} diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend index da4bc2f..081208c 100644 --- a/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend @@ -1,11 +1,9 @@ package de.joneug.mdal import com.google.inject.Inject -import de.joneug.mdal.mdal.MdalPackage import de.joneug.mdal.mdal.Model import de.joneug.mdal.tests.MdalInjectorProvider import de.joneug.mdal.util.ExampleContentGenerator -import de.joneug.mdal.validation.MdalValidator import org.eclipse.xtext.diagnostics.Severity import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -15,73 +13,58 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertFalse import static org.junit.Assert.assertNotNull import static org.junit.Assert.assertTrue -import static extension de.joneug.mdal.extensions.EObjectExtensions.* import static extension de.joneug.mdal.extensions.ObjectExtensions.* @ExtendWith(InjectionExtension) @InjectWith(MdalInjectorProvider) class MdalParsingTest { - + @Inject ParseHelper parseHelper - - @Inject extension - ValidationTestHelper + + @Inject extension ValidationTestHelper + + @Test + def void testMinimalModel() { + val model = parseHelper.parse(ExampleContentGenerator.generateMinimalModel) + + assertNotNull(model) + val errors = model.eResource.errors + assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty) + model.assertNoIssues + } @Test def void testCorrectModel() { val model = parseHelper.parse(ExampleContentGenerator.generateCorrectModel) - - assertNotNull(model) - logDebug(model.dump()) + assertNotNull(model) val errors = model.eResource.errors assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty) - model.assertNoIssues } @Test def void testIncorrectModel() { + val model = parseHelper.parse("abc") + assertFalse(model.eResource.errors.isEmpty) + } + + @Test + def void testModelWithIssues() { val model = parseHelper.parse(ExampleContentGenerator.generateModelWithErrors) - assertNotNull(model) - + // Validate number of issues val issues = model.validate logDebug(issues) assertEquals(11, issues.length) assertEquals(8, issues.filter[it.severity == Severity.ERROR].length) assertEquals(3, issues.filter[it.severity == Severity.WARNING].length) - - // Master should have name or description - model.assertWarning(MdalPackage.eINSTANCE.master, MdalValidator.ENTITY_NAME_DESCRIPTION) - - // Unknown field - model.assertError(MdalPackage.eINSTANCE.includeField, MdalValidator.INCLUDE_FIELD_UNKNOWN_FIELD) - - // Unknown table - model.assertWarning(MdalPackage.eINSTANCE.customField, MdalValidator.CUSTOM_FIELD_UNKNOWN_TABLE) - - // Entity name already exists - model.assertError(MdalPackage.eINSTANCE.documentHeader, MdalValidator.ENTITY_NAME_EXISTS) - model.assertError(MdalPackage.eINSTANCE.documentLine, MdalValidator.ENTITY_NAME_EXISTS) - - // Field name already exists - model.assertError(MdalPackage.eINSTANCE.customField, MdalValidator.FIELD_NAME_EXISTS) - model.assertError(MdalPackage.eINSTANCE.includeField, MdalValidator.FIELD_NAME_EXISTS) - - // Page field unknown - model.assertError(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_UNKNOWN_FIELD) - - // Group name already exists - model.assertError(MdalPackage.eINSTANCE.group, MdalValidator.GROUP_NAME_EXISTS) - - // Duplicate page field - model.assertWarning(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_NAME_EXISTS) } - + } diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/CustomFieldValidatorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/CustomFieldValidatorTest.xtend new file mode 100644 index 0000000..b638366 --- /dev/null +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/CustomFieldValidatorTest.xtend @@ -0,0 +1,47 @@ +package de.joneug.mdal.validation + +import com.google.inject.Inject +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Model +import de.joneug.mdal.tests.MdalInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.Assert.assertNotNull + +@ExtendWith(InjectionExtension) +@InjectWith(MdalInjectorProvider) +class CustomFieldValidatorTest { + + @Inject + ParseHelper parseHelper + + @Inject extension ValidationTestHelper + + @Test + def void testUnknownTableRelation() { + // Tests method "checkTableRelation" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + field("Language Code"; Code[10]) { + TableRelation = "Language1"; + } + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.customField, MdalValidator.CUSTOM_FIELD_UNKNOWN_TABLE) + } + +} diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/EntityValidatorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/EntityValidatorTest.xtend new file mode 100644 index 0000000..b0c4b02 --- /dev/null +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/EntityValidatorTest.xtend @@ -0,0 +1,354 @@ +package de.joneug.mdal.validation + +import com.google.inject.Inject +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Model +import de.joneug.mdal.tests.MdalInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.Assert.assertNotNull + +@ExtendWith(InjectionExtension) +@InjectWith(MdalInjectorProvider) +class EntityValidatorTest { + + @Inject + ParseHelper parseHelper + + @Inject extension ValidationTestHelper + + @Test + def void testNamesNotUnique() { + // Tests method "checkNamesAreUnique" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + } + } + + supplemental "Seminar" { + ShortName = "Sem."; + + fields { + template("Name"; Name) + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.master, MdalValidator.ENTITY_NAME_EXISTS) + model.assertError(MdalPackage.eINSTANCE.supplemental, MdalValidator.ENTITY_NAME_EXISTS) + } + + @Test + def void testMissingNameOrDescription() { + // Tests method "checkHasNameOrDescription" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + field("Duration Days"; Decimal) + } + } + + supplemental "Instructor" { + ShortName = "Inst."; + + fields { + field("Contact No."; Code[20]) + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.master, MdalValidator.ENTITY_NAME_DESCRIPTION) + model.assertWarning(MdalPackage.eINSTANCE.supplemental, MdalValidator.ENTITY_NAME_DESCRIPTION) + } + + @Test + def void testDuplicateFieldNamesWithinRegularFields() { + // Tests method "checkFieldNamesAreUnique" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + supplemental "Seminar Room" { + ShortName = "Sem. Room"; + + fields { + template("Name"; Name) + field("Resource No."; Code[20]) + field("Resource No."; Code[20]) + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.customField, MdalValidator.FIELD_NAME_EXISTS) + } + + @Test + def void testDuplicateFieldNamesBetweenRegularAndIncludeFields() { + // Tests method "checkFieldNamesAreUnique" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + field("Seminar Price"; Decimal) + } + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + + fields { + field("Seminar Price"; Decimal) + include("Seminar Price"; "Seminar"."Seminar Price") + } + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.customField, MdalValidator.FIELD_NAME_EXISTS) + } + + @Test + def void testDuplicateFieldNamesWithinIncludeFields() { + // Tests method "checkFieldNamesAreUnique" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + field("Seminar Price"; Decimal) + } + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + + fields { + include("Seminar Price"; "Seminar"."Seminar Price") + include("Seminar Price"; "Seminar"."Seminar Price") + } + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.includeField, MdalValidator.FIELD_NAME_EXISTS) + } + + @Test + def void testMasterDuplicatePageFields() { + // Tests method "checkDuplicatePageFields" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + field("Seminar Price"; Decimal) + } + + cardPage { + group("General") { + field("Seminar Price") + field("Seminar Price") + } + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_NAME_EXISTS) + } + + @Test + def void testSupplementalDuplicatePageFields() { + // Tests method "checkDuplicatePageFields" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + supplemental "Instructor" { + ShortName = "Inst."; + + fields { + template("Name"; Name) + } + + listPage { + field("Name") + field("Name") + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_NAME_EXISTS) + } + + @Test + def void testDocumentHeaderDuplicatePageFields() { + // Tests method "checkDuplicatePageFields" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + + fields { + field("Starting Date"; Date) + } + + listPage { + field("Starting Date") + field("Starting Date") + } + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_NAME_EXISTS) + } + + @Test + def void testDocumentLineDuplicatePageFields() { + // Tests method "checkDuplicatePageFields" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + + fields { + field("Bill-to Customer No."; Code[20]) + } + + listPartPage { + field("Bill-to Customer No.") + field("Bill-to Customer No.") + } + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_NAME_EXISTS) + } + + @Test + def void testLedgerEntryDuplicatePageFields() { + // Tests method "checkDuplicatePageFields" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + } + } + + ledgerEntry "Seminar Ledger Entry" { + ShortName = "Sem. Ledger Entry"; + + fields { + field("Entry Type"; Enum["Registration"]) + } + + listPage { + field("Entry Type") + field("Entry Type") + } + } + } + ''') + assertNotNull(model) + model.assertWarning(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_NAME_EXISTS) + } + +} diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/GroupValidatorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/GroupValidatorTest.xtend new file mode 100644 index 0000000..64bdd24 --- /dev/null +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/GroupValidatorTest.xtend @@ -0,0 +1,55 @@ +package de.joneug.mdal.validation + +import com.google.inject.Inject +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Model +import de.joneug.mdal.tests.MdalInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.Assert.assertNotNull + +@ExtendWith(InjectionExtension) +@InjectWith(MdalInjectorProvider) +class GroupValidatorTest { + + @Inject + ParseHelper parseHelper + + @Inject extension ValidationTestHelper + + @Test + def void testNamesNotUnique() { + // Tests method "checkNamesAreUnique" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + field("Duration Days"; Decimal) + } + + cardPage { + group("General") { + field("Description") + } + group("General") { + field("Duration Days") + } + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.group, MdalValidator.GROUP_NAME_EXISTS) + } + +} diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/IncludeFieldValidatorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/IncludeFieldValidatorTest.xtend new file mode 100644 index 0000000..dc31f42 --- /dev/null +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/IncludeFieldValidatorTest.xtend @@ -0,0 +1,99 @@ +package de.joneug.mdal.validation + +import com.google.inject.Inject +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Model +import de.joneug.mdal.tests.MdalInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.Assert.assertNotNull + +@ExtendWith(InjectionExtension) +@InjectWith(MdalInjectorProvider) +class IncludeFieldValidatorTest { + + @Inject + ParseHelper parseHelper + + @Inject extension ValidationTestHelper + + @Test + def void testEntityUnknown() { + // Tests method "checkEntityAndField" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + } + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + + fields { + include("Description"; "Seminar1"."Description") + } + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.includeField, MdalValidator.INCLUDE_FIELD_UNKNOWN_ENTITY) + } + + @Test + def void testFieldUnknown() { + // Tests method "checkEntityAndField" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + } + } + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Open", "Closed"]; + + fields { + include("Description"; "Seminar"."Description1") + } + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.includeField, MdalValidator.INCLUDE_FIELD_UNKNOWN_FIELD) + } + +} diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/PageFieldValidatorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/PageFieldValidatorTest.xtend new file mode 100644 index 0000000..84f9865 --- /dev/null +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/PageFieldValidatorTest.xtend @@ -0,0 +1,49 @@ +package de.joneug.mdal.validation + +import com.google.inject.Inject +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Model +import de.joneug.mdal.tests.MdalInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.Assert.assertNotNull + +@ExtendWith(InjectionExtension) +@InjectWith(MdalInjectorProvider) +class PageFieldValidatorTest { + + @Inject + ParseHelper parseHelper + + @Inject extension ValidationTestHelper + + @Test + def void testEntityUnknown() { + // Tests method "checkFieldName" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + } + + listPage { + field("Description1") + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.pageField, MdalValidator.PAGE_FIELD_UNKNOWN_FIELD) + } + +} diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/SolutionValidatorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/SolutionValidatorTest.xtend new file mode 100644 index 0000000..55d448f --- /dev/null +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/validation/SolutionValidatorTest.xtend @@ -0,0 +1,77 @@ +package de.joneug.mdal.validation + +import com.google.inject.Inject +import de.joneug.mdal.mdal.MdalPackage +import de.joneug.mdal.mdal.Model +import de.joneug.mdal.tests.MdalInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.Assert.assertNotNull + +@ExtendWith(InjectionExtension) +@InjectWith(MdalInjectorProvider) +class SolutionValidatorTest { + + @Inject + ParseHelper parseHelper + + @Inject extension ValidationTestHelper + + @Test + def void testDocumentMissingMaster() { + // Tests method "checkRequiredEntites" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + document "Seminar Registration" { + ShortName = "Sem. Reg."; + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Planning", "Registration", "Closed", "Canceled"]; + + fields { + field("Starting Date"; Date) + } + } + + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + + fields { + field("Bill-to Customer No."; Code[20]) + } + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.solution, MdalValidator.DOCUMENT_NO_MASTER) + } + + @Test + def void testLedgerEntryMissingDocument() { + // Tests method "checkRequiredEntites" + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + + ledgerEntry "Seminar Ledger Entry" { + ShortName = "Sem. Ledger Entry"; + + fields { + field("Entry Type"; Enum["Registration"]) + } + } + } + ''') + assertNotNull(model) + model.assertError(MdalPackage.eINSTANCE.solution, MdalValidator.LEDGER_ENTRY_NO_DOCUMENT) + } + +} From 36001f95fa05e6c4d136ff701f01e25601b76eff Mon Sep 17 00:00:00 2001 From: Jonathan Neugebauer Date: Wed, 16 Dec 2020 12:20:10 +0100 Subject: [PATCH 2/5] Clean project structure --- .../mdal/tests/MdalInjectorProvider.java | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 de.joneug.mdal/src/test/xtext-gen/de/joneug/mdal/tests/MdalInjectorProvider.java diff --git a/de.joneug.mdal/src/test/xtext-gen/de/joneug/mdal/tests/MdalInjectorProvider.java b/de.joneug.mdal/src/test/xtext-gen/de/joneug/mdal/tests/MdalInjectorProvider.java deleted file mode 100644 index b4b6de3..0000000 --- a/de.joneug.mdal/src/test/xtext-gen/de/joneug/mdal/tests/MdalInjectorProvider.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * generated by Xtext 2.21.0 - */ -package de.joneug.mdal.tests; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import de.joneug.mdal.MdalRuntimeModule; -import de.joneug.mdal.MdalStandaloneSetup; -import org.eclipse.xtext.testing.GlobalRegistries; -import org.eclipse.xtext.testing.GlobalRegistries.GlobalStateMemento; -import org.eclipse.xtext.testing.IInjectorProvider; -import org.eclipse.xtext.testing.IRegistryConfigurator; - -public class MdalInjectorProvider implements IInjectorProvider, IRegistryConfigurator { - - protected GlobalStateMemento stateBeforeInjectorCreation; - protected GlobalStateMemento stateAfterInjectorCreation; - protected Injector injector; - - static { - GlobalRegistries.initializeDefaults(); - } - - @Override - public Injector getInjector() { - if (injector == null) { - this.injector = internalCreateInjector(); - stateAfterInjectorCreation = GlobalRegistries.makeCopyOfGlobalState(); - } - return injector; - } - - protected Injector internalCreateInjector() { - return new MdalStandaloneSetup() { - @Override - public Injector createInjector() { - return Guice.createInjector(createRuntimeModule()); - } - }.createInjectorAndDoEMFRegistration(); - } - - protected MdalRuntimeModule createRuntimeModule() { - // make it work also with Maven/Tycho and OSGI - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=493672 - return new MdalRuntimeModule() { - @Override - public ClassLoader bindClassLoaderToInstance() { - return MdalInjectorProvider.class - .getClassLoader(); - } - }; - } - - @Override - public void restoreRegistry() { - stateBeforeInjectorCreation.restoreGlobalState(); - stateBeforeInjectorCreation = null; - } - - @Override - public void setupRegistry() { - stateBeforeInjectorCreation = GlobalRegistries.makeCopyOfGlobalState(); - if (injector == null) { - getInjector(); - } - stateAfterInjectorCreation.restoreGlobalState(); - } -} From c422ee524777d4da6a61c5d878dbc941c4103cec Mon Sep 17 00:00:00 2001 From: Jonathan Neugebauer Date: Wed, 16 Dec 2020 12:21:38 +0100 Subject: [PATCH 3/5] Update dependencies --- build.gradle | 2 +- de.joneug.mdal.standalone/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c1374dc..26a02f5 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ allprojects { } subprojects { - ext.xtextVersion = '2.21.0' + ext.xtextVersion = '2.23.0' repositories { mavenCentral() } diff --git a/de.joneug.mdal.standalone/build.gradle b/de.joneug.mdal.standalone/build.gradle index d1d3904..adf6b62 100644 --- a/de.joneug.mdal.standalone/build.gradle +++ b/de.joneug.mdal.standalone/build.gradle @@ -5,7 +5,7 @@ dependencies { implementation "org.eclipse.xtext:org.eclipse.xtext:${xtextVersion}" implementation "org.eclipse.xtext:org.eclipse.xtext.xbase:${xtextVersion}" implementation "com.google.code.gson:gson:2.8.6" - implementation 'info.picocli:picocli:4.3.2' + implementation 'info.picocli:picocli:4.5.2' } apply plugin: 'application' From b50fa8e6c62d24e0ac9401c5e6a40ae1fd07e114 Mon Sep 17 00:00:00 2001 From: Jonathan Neugebauer Date: Wed, 16 Dec 2020 20:42:59 +0100 Subject: [PATCH 4/5] Update AL code generator to respect modularity --- .../mdal/extensions/DocumentExtensions.xtend | 10 +- .../extensions/DocumentHeaderExtensions.xtend | 52 +++---- .../mdal/extensions/MasterExtensions.xtend | 54 ++++--- .../mdal/extensions/SolutionExtensions.xtend | 138 ++++++++++-------- 4 files changed, 141 insertions(+), 113 deletions(-) diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentExtensions.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentExtensions.xtend index c283300..339b52a 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentExtensions.xtend +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentExtensions.xtend @@ -145,10 +145,12 @@ class DocumentExtensions { document.line.doGenerate // Codeunits - document.saveCodeunit(document.codeunitNamePost, document.doGenerateCodeunitPost) - document.saveCodeunit(document.codeunitNamePostYesNo, document.doGenerateCodeunitPostYesNo) - document.saveCodeunit(document.codeunitNameJnlCheckLine, document.doGenerateCodeunitJnlCheckLine) - document.saveCodeunit(document.codeunitNameJnlPostLine, document.doGenerateCodeunitJnlPostLine) + if (document.solution.ledgerEntry !== null) { + document.saveCodeunit(document.codeunitNamePost, document.doGenerateCodeunitPost) + document.saveCodeunit(document.codeunitNamePostYesNo, document.doGenerateCodeunitPostYesNo) + document.saveCodeunit(document.codeunitNameJnlCheckLine, document.doGenerateCodeunitJnlCheckLine) + document.saveCodeunit(document.codeunitNameJnlPostLine, document.doGenerateCodeunitJnlPostLine) + } } static def doGenerateStatusEnum(Document document) ''' diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentHeaderExtensions.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentHeaderExtensions.xtend index 617ebf0..fa0f516 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentHeaderExtensions.xtend +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/DocumentHeaderExtensions.xtend @@ -1295,31 +1295,33 @@ class DocumentHeaderExtensions { ToolTip = 'View or add comments for the record.'; } } - group("P&osting") - { - Caption = 'P&osting'; - Image = Post; - action(Post) - { - ApplicationArea = All; - Caption = 'P&ost'; - Ellipsis = true; - Image = PostOrder; - Promoted = true; - PromotedCategory = Category7; - PromotedIsBig = true; - ShortCutKey = 'F9'; - ToolTip = 'Finalize the document or journal by posting the amounts and quantities to the related accounts in your company books.'; - - trigger OnAction() - var - «document.codeunitVariableNamePostYesNo»: Codeunit «document.codeunitNamePostYesNo.saveQuote»; - begin - «document.codeunitVariableNamePostYesNo».Run(Rec); - CurrPage.Update(false); - end; - } - } + «IF document.solution.ledgerEntry !== null» + group("P&osting") + { + Caption = 'P&osting'; + Image = Post; + action(Post) + { + ApplicationArea = All; + Caption = 'P&ost'; + Ellipsis = true; + Image = PostOrder; + Promoted = true; + PromotedCategory = Category7; + PromotedIsBig = true; + ShortCutKey = 'F9'; + ToolTip = 'Finalize the document or journal by posting the amounts and quantities to the related accounts in your company books.'; + + trigger OnAction() + var + «document.codeunitVariableNamePostYesNo»: Codeunit «document.codeunitNamePostYesNo.saveQuote»; + begin + «document.codeunitVariableNamePostYesNo».Run(Rec); + CurrPage.Update(false); + end; + } + } + «ENDIF» } } } diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/MasterExtensions.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/MasterExtensions.xtend index 0991b8f..42365ba 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/MasterExtensions.xtend +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/MasterExtensions.xtend @@ -139,12 +139,14 @@ class MasterExtensions { CommentLine.SetRange("No.", "No."); CommentLine.DeleteAll(); - «document.header.tableVariableName».SetCurrentKey("«master.cleanedName» No."); - «document.header.tableVariableName».SetRange("«master.cleanedName» No.", "No."); - IF NOT «document.header.tableVariableName».IsEmpty THEN - Error( - ExistingDocumentsErr, - TableCaption, "No.", «document.header.tableVariableName».TableCaption); + «IF document !== null» + «document.header.tableVariableName».SetCurrentKey("«master.cleanedName» No."); + «document.header.tableVariableName».SetRange("«master.cleanedName» No.", "No."); + IF NOT «document.header.tableVariableName».IsEmpty THEN + Error( + ExistingDocumentsErr, + TableCaption, "No.", «document.header.tableVariableName».TableCaption); + «ENDIF» end; trigger OnRename() @@ -158,8 +160,10 @@ class MasterExtensions { NoSeriesMgt: Codeunit NoSeriesManagement; CommentLine: Record "Comment Line"; «master.tableVariableName»: Record "«master.tableName»"; - «document.header.tableVariableName»: Record "«document.header.tableName»"; - ExistingDocumentsErr: Label 'You cannot delete %1 %2 because there is at least one outstanding %3 for this «master.name».'; + «IF document !== null» + «document.header.tableVariableName»: Record "«document.header.tableName»"; + ExistingDocumentsErr: Label 'You cannot delete %1 %2 because there is at least one outstanding %3 for this «master.name».'; + «ENDIF» «IF master.hasTemplateOfType(TemplateDimensions)» DimMgt: Codeunit DimensionManagement; «ENDIF» @@ -379,22 +383,24 @@ class MasterExtensions { } } } - area(creation) - { - action(New«document.cleanedName») - { - AccessByPermission = tabledata «document.header.tableName.saveQuote» = RIM; - ApplicationArea = All; - Caption = '«document.name»'; - Image = NewDocument; - Promoted = true; - PromotedCategory = Category4; - RunObject = Page «document.documentPageName.saveQuote»; - RunPageLink = "«master.name» No." = field("No."); - RunPageMode = Create; - Visible = NOT IsOfficeAddin; - } - } + «IF document !== null» + area(creation) + { + action(New«document.cleanedName») + { + AccessByPermission = tabledata «document.header.tableName.saveQuote» = RIM; + ApplicationArea = All; + Caption = '«document.name»'; + Image = NewDocument; + Promoted = true; + PromotedCategory = Category4; + RunObject = Page «document.documentPageName.saveQuote»; + RunPageLink = "«master.name» No." = field("No."); + RunPageMode = Create; + Visible = NOT IsOfficeAddin; + } + } + «ENDIF» } var diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/SolutionExtensions.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/SolutionExtensions.xtend index e8a31b4..4229e20 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/SolutionExtensions.xtend +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/extensions/SolutionExtensions.xtend @@ -35,37 +35,45 @@ class SolutionExtensions { static def doGenerate(Solution solution) { solution.logInfo("Generating solution '" + solution.name + "'") - // Setup - solution.logInfo("Generating setup files") - solution.doGenerateSetup + if (solution.master !== null || solution.document !== null) { + // Setup + solution.logInfo("Generating setup files") + solution.doGenerateSetup + } // Comments solution.logInfo("Generating comment files") solution.doGenerateCommentObjects - // Source Code Setup - solution.logInfo("Generating source code setup files") - solution.doGenerateSourceCodeSetupObjects - - // Master - solution.logInfo("Generating master files") - solution.master.doGenerate + if (solution.master !== null) { + // Master + solution.logInfo("Generating master files") + solution.master.doGenerate + } // Supplementals solution.logInfo("Generating supplemental files") solution.supplementals.forEach[it.doGenerate] - // Document - solution.logInfo("Generating document files") - solution.document.doGenerate - - // Ledger Entry - solution.logInfo("Generating ledger entry files") - solution.ledgerEntry.doGenerate - - // Navigate Extensions - solution.logInfo("Generating navigate files") - solution.doGenerateNavigateExtensionObjects + if (solution.document !== null) { + // Document + solution.logInfo("Generating document files") + solution.document.doGenerate + + // Ledger Entry + if (solution.ledgerEntry !== null) { + solution.logInfo("Generating ledger entry files") + solution.ledgerEntry.doGenerate + } + + // Source Code Setup + solution.logInfo("Generating source code setup files") + solution.doGenerateSourceCodeSetupObjects + + // Navigate Extensions + solution.logInfo("Generating navigate files") + solution.doGenerateNavigateExtensionObjects + } } /* @@ -111,22 +119,24 @@ class SolutionExtensions { Caption = '«solution.master.cleanedName» Nos.'; TableRelation = "No. Series"; } - field(3; "«solution.document.shortName» Nos."; Code[20]) - { - Caption = '«solution.document.shortName» Nos.'; - TableRelation = "No. Series"; - } - field(4; "«solution.document.shortNamePosted» Nos."; Code[20]) - { - Caption = '«solution.document.shortNamePosted» Nos.'; - TableRelation = "No. Series"; - } - field(10; "Copy Comments"; Boolean) - { - AccessByPermission = TableData «solution.document.header.tableNamePosted.saveQuote» = R; - Caption = 'Copy Comments To Posted Reg.'; - InitValue = true; - } + «IF solution.document !== null» + field(3; "«solution.document.shortName» Nos."; Code[20]) + { + Caption = '«solution.document.shortName» Nos.'; + TableRelation = "No. Series"; + } + field(4; "«solution.document.shortNamePosted» Nos."; Code[20]) + { + Caption = '«solution.document.shortNamePosted» Nos.'; + TableRelation = "No. Series"; + } + field(10; "Copy Comments"; Boolean) + { + AccessByPermission = TableData «solution.document.header.tableNamePosted.saveQuote» = R; + Caption = 'Copy Comments To Posted Reg.'; + InitValue = true; + } + «ENDIF» } keys @@ -165,15 +175,17 @@ class SolutionExtensions { { area(content) { - group(General) - { - Caption = 'General'; - - field("Copy Comments"; "Copy Comments") - { - ApplicationArea = All; - } - } + «IF solution.document !== null» + group(General) + { + Caption = 'General'; + + field("Copy Comments"; "Copy Comments") + { + ApplicationArea = All; + } + } + «ENDIF» group("Number Series") { Caption = 'Number Series'; @@ -182,14 +194,16 @@ class SolutionExtensions { { ApplicationArea = All; } - field("«solution.document.shortName» Nos."; "«solution.document.shortName» Nos.") - { - ApplicationArea = All; - } - field("«solution.document.shortNamePosted» Nos."; "«solution.document.shortNamePosted» Nos.") - { - ApplicationArea = All; - } + «IF solution.document !== null» + field("«solution.document.shortName» Nos."; "«solution.document.shortName» Nos.") + { + ApplicationArea = All; + } + field("«solution.document.shortNamePosted» Nos."; "«solution.document.shortNamePosted» Nos.") + { + ApplicationArea = All; + } + «ENDIF» } } } @@ -214,12 +228,16 @@ class SolutionExtensions { */ static def void doGenerateCommentObjects(Solution solution) { - solution.saveEnumExt(solution.commentLineTableNameEnumExtName, solution.doGenerateCommentLineTableNameEnumExt) - solution.saveEnum(solution.commentDocumentTypeEnumName, solution.doGenerateCommentDocumentTypeEnum) - solution.saveTableExt(solution.commentLineTableExtName, solution.doGenerateCommentLineTableExtName) - solution.saveTable(solution.commentLineTableName, solution.doGenerateCommentLineTable) - solution.savePage(solution.commentListPageName, solution.doGenerateCommentListPage) - solution.savePage(solution.commentSheetPageName, solution.doGenerateCommentSheetPage) + if (solution.master !== null) { + solution.saveEnumExt(solution.commentLineTableNameEnumExtName, solution.doGenerateCommentLineTableNameEnumExt) + solution.saveTableExt(solution.commentLineTableExtName, solution.doGenerateCommentLineTableExtName) + } + if (solution.document !== null) { + solution.saveEnum(solution.commentDocumentTypeEnumName, solution.doGenerateCommentDocumentTypeEnum) + solution.saveTable(solution.commentLineTableName, solution.doGenerateCommentLineTable) + solution.savePage(solution.commentListPageName, solution.doGenerateCommentListPage) + solution.savePage(solution.commentSheetPageName, solution.doGenerateCommentSheetPage) + } } static def getCommentLineTableNameEnumExtName(Solution solution) { From af74d9bd752cffff0aa354a813b29fef34125833 Mon Sep 17 00:00:00 2001 From: Jonathan Neugebauer Date: Wed, 16 Dec 2020 22:18:56 +0100 Subject: [PATCH 5/5] Add tests --- .../mdal/ide/MdalLanguageServerTest.xtend | 14 ++- .../mdal/util/ExampleContentGenerator.xtend | 31 ----- .../java/de/joneug/mdal/MdalParsingTest.xtend | 72 ++++++++++- .../mdal/generator/MdalGeneratorTest.xtend | 118 ++++++++++++++++-- 4 files changed, 193 insertions(+), 42 deletions(-) diff --git a/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend b/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend index a639b75..f0273e2 100644 --- a/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend +++ b/de.joneug.mdal.ide/src/test/java/de/joneug/mdal/ide/MdalLanguageServerTest.xtend @@ -28,7 +28,19 @@ class MdalLanguageServerTest extends AbstractLanguageServerTest { initialize() val file = 'seminar.mdal'.writeFile("") - file.open(ExampleContentGenerator.generateMinimalModel.toString) + file.open(''' + solution "Seminar Management" { + Prefix = "SEM"; + + master "Seminar" { + ShortName = "Sem."; + + fields { + template("Description"; Description) + } + } + } + ''') val diagnostics = diagnostics.get(file) assertTrue('''Unexpected errors: «diagnostics»''', diagnostics.empty) } diff --git a/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend b/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend index 1d3c1c7..cf454a3 100644 --- a/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend +++ b/de.joneug.mdal/src/main/java/de/joneug/mdal/util/ExampleContentGenerator.xtend @@ -1,37 +1,6 @@ package de.joneug.mdal.util class ExampleContentGenerator { - - static def generateMinimalModel() ''' - solution "Seminar Management" { - Prefix = "SEM"; - - master "Seminar" { - ShortName = "Sem."; - - fields { - template("Description"; Description) - field("Duration Days"; Decimal) - template("Dimensions"; Dimensions) - } - - cardPage { - group("General") { - field("Description") - field("Duration Days") - } - group("Posting Details") { - field("Dimensions") - } - } - - listPage { - field("Description") - field("Duration Days") - } - } - } - ''' static def generateCorrectModel() ''' solution "Seminar Management" { diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend index 081208c..523d096 100644 --- a/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/MdalParsingTest.xtend @@ -29,8 +29,74 @@ class MdalParsingTest { @Inject extension ValidationTestHelper @Test - def void testMinimalModel() { - val model = parseHelper.parse(ExampleContentGenerator.generateMinimalModel) + def void testOnlyMaster() { + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + master "Seminar" { + ShortName = "Sem."; + fields { + template("Description"; Description) + } + } + } + ''') + + assertNotNull(model) + val errors = model.eResource.errors + assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty) + model.assertNoIssues + } + + @Test + def void testOnlySupplemental() { + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + supplemental "Seminar Room" { + ShortName = "Sem. Room"; + fields { + template("Name"; Name) + } + } + } + ''') + + assertNotNull(model) + val errors = model.eResource.errors + assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty) + model.assertNoIssues + } + + @Test + def void testMasterDocument() { + val model = parseHelper.parse(''' + solution "Seminar Management" { + Prefix = "SEM"; + master "Seminar" { + ShortName = "Sem."; + fields { + template("Description"; Description) + } + } + document "Seminar Registration" { + ShortName = "Sem. Reg."; + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Planning", "Registration", "Closed", "Canceled"]; + fields { + field("Starting Date"; Date) + } + } + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + fields { + field("Bill-to Customer No."; Code[20]) + } + } + } + } + ''') assertNotNull(model) val errors = model.eResource.errors @@ -55,7 +121,7 @@ class MdalParsingTest { } @Test - def void testModelWithIssues() { + def void testModelWithErrors() { val model = parseHelper.parse(ExampleContentGenerator.generateModelWithErrors) assertNotNull(model) diff --git a/de.joneug.mdal/src/test/java/de/joneug/mdal/generator/MdalGeneratorTest.xtend b/de.joneug.mdal/src/test/java/de/joneug/mdal/generator/MdalGeneratorTest.xtend index 5f9d6d0..04ceb3f 100644 --- a/de.joneug.mdal/src/test/java/de/joneug/mdal/generator/MdalGeneratorTest.xtend +++ b/de.joneug.mdal/src/test/java/de/joneug/mdal/generator/MdalGeneratorTest.xtend @@ -9,6 +9,7 @@ import org.eclipse.xtext.generator.InMemoryFileSystemAccess import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.eclipse.xtext.testing.util.ParseHelper +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith @@ -30,7 +31,7 @@ class MdalGeneratorTest { MdalGenerator generator InMemoryFileSystemAccess fsa - + GeneratorManagement management @BeforeEach @@ -39,11 +40,106 @@ class MdalGeneratorTest { this.fsa = new InMemoryFileSystemAccess() fsa.generateFile('app.json', ExampleContentGenerator.generateAppJson) this.management = GeneratorManagement.getInstance() - this.management.initializeFileSystemAccess(fsa) + this.management.initializeFileSystemAccess(fsa) + } + + @Test + def void testOnlyMaster() { + doGenerate(''' + solution "Seminar Management" { + Prefix = "SEM"; + master "Seminar" { + ShortName = "Sem."; + fields { + template("Description"; Description) + } + } + } + ''') + checkFileExists("EnumExt/SEMCommentLineTableNameExt.EnumExt.al") + checkFileExists("Page/SEMSeminarCard.Page.al") + checkFileExists("Page/SEMSeminarList.Page.al") + checkFileExists("Page/SEMSeminarSetup.Page.al") + checkFileExists("Table/SEMSeminar.Table.al") + checkFileExists("Table/SEMSeminarSetup.Table.al") + checkFileExists("TableExt/SEMCommentLineExt.TableExt.al") + } + + @Test + def void testOnlySupplemental() { + doGenerate(''' + solution "Seminar Management" { + Prefix = "SEM"; + supplemental "Seminar Room" { + ShortName = "Sem. Room"; + fields { + template("Name"; Name) + } + } + } + ''') + checkFileExists("Page/SEMSeminarRooms.Page.al") + checkFileExists("Table/SEMSeminarRoom.Table.al") + } + + @Test + def void testMasterDocument() { + doGenerate(''' + solution "Seminar Management" { + Prefix = "SEM"; + master "Seminar" { + ShortName = "Sem."; + fields { + template("Description"; Description) + } + } + document "Seminar Registration" { + ShortName = "Sem. Reg."; + header "Seminar Registration Header" { + ShortName = "Sem. Reg. Header"; + StatusCaptions = ["Planning", "Registration", "Closed", "Canceled"]; + fields { + field("Starting Date"; Date) + } + } + line "Seminar Registration Line" { + ShortName = "Sem. Reg. Line"; + fields { + field("Bill-to Customer No."; Code[20]) + } + } + } + } + ''') + checkFileExists("Codeunit/SEMNavigateEventSub.Codeunit.al") + checkFileExists("Enum/SEMSeminarCommentDocumentType.Enum.al") + checkFileExists("Enum/SEMSemRegStatus.Enum.al") + checkFileExists("EnumExt/SEMCommentLineTableNameExt.EnumExt.al") + checkFileExists("Page/SEMPostedSemReg.Page.al") + checkFileExists("Page/SEMPostedSemRegList.Page.al") + checkFileExists("Page/SEMPostedSemRegSubf.Page.al") + checkFileExists("Page/SEMSeminarCard.Page.al") + checkFileExists("Page/SEMSeminarCommentList.Page.al") + checkFileExists("Page/SEMSeminarCommentSheet.Page.al") + checkFileExists("Page/SEMSeminarList.Page.al") + checkFileExists("Page/SEMSeminarRegistration.Page.al") + checkFileExists("Page/SEMSeminarRegistrationList.Page.al") + checkFileExists("Page/SEMSeminarSetup.Page.al") + checkFileExists("Page/SEMSemRegSubf.Page.al") + checkFileExists("PageExt/SEMSourceCodeSetupExt.PageExt.al") + checkFileExists("Table/SEMPstdSemRegHeader.Table.al") + checkFileExists("Table/SEMPstdSemRegLine.Table.al") + checkFileExists("Table/SEMSeminar.Table.al") + checkFileExists("Table/SEMSeminarCommentLine.Table.al") + checkFileExists("Table/SEMSeminarSetup.Table.al") + checkFileExists("Table/SEMSemRegHeader.Table.al") + checkFileExists("Table/SEMSemRegLine.Table.al") + checkFileExists("TableExt/SEMCommentLineExt.TableExt.al") + checkFileExists("TableExt/SEMSourceCodeSetupExt.TableExt.al") } @Test - def void testDoGenerate() { + def void testCorrectModel() { doGenerate(ExampleContentGenerator.generateCorrectModel.toString) // Tables @@ -72,7 +168,7 @@ class MdalGeneratorTest { 'local procedure OnAfterGetSemSetup(var SemSetup: Record "SEM Seminar Setup")' ] ) - + // Pages checkFileContains( "Page/SEMSeminarSetup.Page.al", @@ -86,6 +182,13 @@ class MdalGeneratorTest { ) } + @Test + def void testIncorrectModel() { + Assertions.assertThrows(IllegalArgumentException, [ + doGenerate(ExampleContentGenerator.generateModelWithErrors.toString) + ]) + } + def doGenerate(String modelString) { val model = parseHelper.parse(modelString) logDebug(model.dump()) @@ -111,9 +214,10 @@ class MdalGeneratorTest { logDebug(fsa.getFileInDefaultOutput(filePath)) val fileContents = fsa.getFileInDefaultOutput(filePath).toString - - expectedContents.forEach[ - assertTrue('''File at path "«filePath»" doesn't contain the expected content "«it»"".''', fileContents.contains(it)) + + expectedContents.forEach [ + assertTrue('''File at path "«filePath»" doesn't contain the expected content "«it»"".''', + fileContents.contains(it)) ] }