diff --git a/build.gradle b/build.gradle index 1f2f9cfc..9c893378 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,16 @@ subprojects { name "SE Nexus" url = publicNexus } + if(isPipelineFullBuild) { + maven { + name "Official SysMLv2 Github Package Registry" + url sysmlGithub + credentials { + username = githubUser + password = githubToken + } + } + } } // Java JDK Warning diff --git a/gradle.properties b/gradle.properties index 97f253bf..6287d3bd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,3 +29,4 @@ junit_version = 5.8.2 # Version of published artifacts version = 7.9.0-SNAPSHOT +omgVersion = 0.58.0 diff --git a/language/build.gradle b/language/build.gradle index 6da742ac..b202c30a 100644 --- a/language/build.gradle +++ b/language/build.gradle @@ -33,9 +33,29 @@ test { useJUnitPlatform() } +def isPipelineFullBuild = (project.findProperty("isPipelineFullBuild")?.toString()?.toBoolean() ?: false) +println "language:isPipelineFullBuild=" + isPipelineFullBuild +def omg_version = (project.findProperty("omgVersion") ?: "0.58.0").toString() + /* ============================================================ */ /* ======================= Dependencies ======================= */ /* ============================================================ */ +sourceSets { + omgTest { + java.srcDir("src/omgTest/java") + resources.srcDir("src/omgTest/resources") + + // Let omgTest reuse the normal test classpath (JUnit, assertj, etc.) + compileClasspath += sourceSets.test.compileClasspath + runtimeClasspath += sourceSets.test.runtimeClasspath + } +} + +configurations { + omgTestImplementation.extendsFrom(testImplementation) + omgTestRuntimeOnly.extendsFrom(testRuntimeOnly) +} + dependencies { // language dependencies @@ -46,6 +66,32 @@ dependencies { implementation "org.apache.commons:commons-lang3:$commons_lang_version" implementation "commons-cli:commons-cli:$commons_cli_version" + + if (isPipelineFullBuild) { + add("omgTestImplementation", [group: "org.omg.sysml", name: "org.omg.sysml.interactive", version: omg_version, classifier: "all"]) + } +} + +tasks.register("omgTest", Test) { + description = "Runs OMG SysML v2 pilot parser compatibility tests (only in full pipeline build)." + group = "verification" + + testClassesDirs = sourceSets.omgTest.output.classesDirs + classpath = sourceSets.omgTest.runtimeClasspath + + shouldRunAfter(tasks.test) + useJUnitPlatform() + + onlyIf { isPipelineFullBuild } +} + +// If you want `./gradlew :language:check` to include it in full builds: +tasks.named("check") { + dependsOn(tasks.named("omgTest")) +} + +tasks.named("processOmgTestResources") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } /* ============================================================ */ @@ -109,5 +155,4 @@ publishing { url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl } } - defaultTasks('build') diff --git a/language/src/omgTest/java/parser/ParsersComparisonFullTest.java b/language/src/omgTest/java/parser/ParsersComparisonFullTest.java new file mode 100644 index 00000000..9dffea26 --- /dev/null +++ b/language/src/omgTest/java/parser/ParsersComparisonFullTest.java @@ -0,0 +1,88 @@ +package parser; + +import de.monticore.lang.sysmlv2.SysMLv2Mill; +import de.monticore.lang.sysmlv2._ast.ASTSysMLModel; +import de.monticore.lang.sysmlv2._parser.SysMLv2Parser; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.omg.sysml.interactive.SysMLInteractive; +import org.eclipse.xtext.validation.Issue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ParsersComparisonFullTest { + private static final String MODEL_PATH = "src/omgTest/resources/parser"; + + private SysMLv2Parser parser = SysMLv2Mill.parser(); + + // SysMLInteractive is a singleton; keep one reference + private static SysMLInteractive official; + + @BeforeAll + public static void init() { + Log.init(); + SysMLv2Mill.init(); + + official = SysMLInteractive.getInstance(); + official.setVerbose(false); + } + + @BeforeEach + public void reset() { + parser.setError(false); + } + + @ParameterizedTest(name = "{index} - {0} does parse w/o errors (MontiCore + official)") + @ValueSource(strings = { + "packages.sysml", + "imports.sysml", + "ports.sysml", + "parts.sysml", + "states.sysml", + "parallel_states.sysml", + "actions.sysml", + "items.sysml", + "assert.sysml", + "constraints.sysml", + "requirements.sysml", + "streams.sysml", + "streamsFilter.sysml", + "refinement.sysml", + "cardinalities.sysml", + "connections.sysml", + "collections.sysml", + "StateDecomposition1.sysml", + "FlowConectionInterfaceExample.sysml", + "StateActions.sysml", + "ConditionalSuccessionExample-1.sysml" + }) + public void testParsingModels(String modelName) throws IOException { + Path modelPath = Path.of(MODEL_PATH, modelName); + + // 1) MontiCore parser (existing behavior) + Optional ast = SysMLv2Mill.parser().parse( + modelPath.toString()); //wie mache ich hier volle Validierung mit CoCos? + assertFalse(parser.hasErrors(), "MontiCore parsing should not have failed"); + assertTrue(ast.isPresent(), "MontiCore AST should have been created"); + + // 2) Official OMG parser + String input = Files.readString(modelPath); + official.parse(input); + List issues = official.validate(); + + assertTrue( + issues.isEmpty(), + () -> "Official OMG validation issues for " + modelName + ":\n" + ); + } +} diff --git a/language/src/omgTest/java/parser/ParsersComparisonTest.java b/language/src/omgTest/java/parser/ParsersComparisonTest.java new file mode 100644 index 00000000..e5f5697e --- /dev/null +++ b/language/src/omgTest/java/parser/ParsersComparisonTest.java @@ -0,0 +1,79 @@ +package parser; + +import de.monticore.lang.sysmlv2.SysMLv2Mill; +import de.monticore.lang.sysmlv2._ast.ASTSysMLModel; +import de.monticore.lang.sysmlv2._parser.SysMLv2Parser; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.omg.sysml.interactive.SysMLInteractive; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +public class ParsersComparisonTest { + private static final String MODEL_PATH = "src/omgTest/resources/parser"; + + private SysMLv2Parser parser = SysMLv2Mill.parser(); + + // SysMLInteractive is a singleton; keep one reference + private static SysMLInteractive official; + + @BeforeAll + public static void init() { + Log.init(); + SysMLv2Mill.init(); + + official = SysMLInteractive.getInstance(); + official.setVerbose(false); + } + + @BeforeEach + public void reset() { + parser.setError(false); + } + + @ParameterizedTest(name = "{index} - {0} does parse w/o errors (MontiCore + official)") + @ValueSource(strings = { + "packages.sysml", + "imports.sysml", + "ports.sysml", + "parts.sysml", + "states.sysml", + "parallel_states.sysml", + "actions.sysml", + "items.sysml", + "assert.sysml", + "constraints.sysml", + "requirements.sysml", + "streams.sysml", + "streamsFilter.sysml", + "refinement.sysml", + "cardinalities.sysml", + "connections.sysml", + "collections.sysml", + "StateDecomposition1.sysml", + "FlowConectionInterfaceExample.sysml", + "StateActions.sysml", + "ConditionalSuccessionExample-1.sysml" + }) + public void testParsingModels(String modelName) throws IOException { + Path modelPath = Path.of(MODEL_PATH, modelName); + + // 1) MontiCore parser (existing behavior) + Optional ast = SysMLv2Mill.parser().parse(modelPath.toString()); + assertFalse(parser.hasErrors(), "MontiCore parsing should not have failed"); + assertTrue(ast.isPresent(), "MontiCore AST should have been created"); + + // 2) Official OMG parser + String input = Files.readString(modelPath); + official.parse(input); + + } +} diff --git a/language/src/omgTest/resources/parser/ConditionalSuccessionExample-1.sysml b/language/src/omgTest/resources/parser/ConditionalSuccessionExample-1.sysml new file mode 100644 index 00000000..2095af22 --- /dev/null +++ b/language/src/omgTest/resources/parser/ConditionalSuccessionExample-1.sysml @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package ConditionalSuccessionExample1 { + + part def Scene; + part def Image { + attribute isWellFocused: ScalarValues::Boolean; + } + part def Picture; + action def Focus { in part scene : Scene; out part image : Image; } + action def Shoot { in part image: Image; out part picture : Picture; } + action def TakePicture { in part scene : Scene; out part picture : Picture; } + + action takePicture : TakePicture { + in item scene; + out item picture; + first start; + then focus; + action focus : Focus { + in item scene; + out item image; + } + + first focus + if focus.image.isWellFocused then shoot; + + flow from focus.image to shoot.image; + + action shoot : Shoot { + in item; + out item picture; + } + then done; + } + +} diff --git a/language/src/omgTest/resources/parser/FlowConectionInterfaceExample.sysml b/language/src/omgTest/resources/parser/FlowConectionInterfaceExample.sysml new file mode 100644 index 00000000..9db5a1f4 --- /dev/null +++ b/language/src/omgTest/resources/parser/FlowConectionInterfaceExample.sysml @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package FlowConnectionInterfaceExample { + import PortExample::*; + + part def Vehicle; + + interface def FuelInterface { + end supplierPort : FuelOutPort; + end consumerPort : FuelInPort; + + flow supplierPort.fuelSupply to consumerPort.fuelSupply; + flow consumerPort.fuelReturn to supplierPort.fuelReturn; + } + + part vehicle : Vehicle { + part tankAssy : FuelTankAssembly; + part eng : Engine; + + interface : FuelInterface connect + tankAssy.fuelTankPort to + eng.engineFuelPort; + } +} diff --git a/language/src/omgTest/resources/parser/StateActions.sysml b/language/src/omgTest/resources/parser/StateActions.sysml new file mode 100644 index 00000000..e17abdc3 --- /dev/null +++ b/language/src/omgTest/resources/parser/StateActions.sysml @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package StateActions { + + attribute def VehicleStartSignal; + attribute def VehicleOnSignal; + attribute def VehicleOffSignal; + + part def Vehicle; + + action performSelfTest { } + + state def VehicleStates { } + + state vehicleStates : VehicleStates { + + entry; then off; + + state off; + accept VehicleStartSignal + then starting; + + state starting; + accept VehicleOnSignal + then on; + + state on { + entry performSelfTe; + do action providePower { /* ... */ } + exit action applyParkingBrake { /* ... */ } + } + accept VehicleOffSignal + then off; + } + +} diff --git a/language/src/omgTest/resources/parser/StateDecomposition1.sysml b/language/src/omgTest/resources/parser/StateDecomposition1.sysml new file mode 100644 index 00000000..8c537c5d --- /dev/null +++ b/language/src/omgTest/resources/parser/StateDecomposition1.sysml @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package StateDecomposition1 { + + attribute def VehicleStartSignal; + attribute def VehicleOnSignal; + attribute def VehicleOffSignal; + + state def VehicleStates; + + state vehicleStates : VehicleStates { + entry; then off; + + state off; + accept VehicleStartSignal + then starting; + + state starting; + accept VehicleOnSignal + then on; + + state on; + accept VehicleOffSignal + then off; + } +} diff --git a/language/src/omgTest/resources/parser/actions.sysml b/language/src/omgTest/resources/parser/actions.sysml new file mode 100644 index 00000000..3ff3c320 --- /dev/null +++ b/language/src/omgTest/resources/parser/actions.sysml @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ +send reference.'to'.something to somewhere.elsewhere; + +assign reference.'to'.someting := some + complex * expression; + +accept handle:Type via some.reference; + +accept handle; + +action def Jump; + +action def Around { + action jump: Jump; + action jumpjumpjump; +} + +action { + send a to b; + assign x := y; + fork a; + then c; + then d; + + decide a; + if true then c; + if false then d; + + join joinNode; + + action c; + merge e; + + perform a; +} diff --git a/language/src/omgTest/resources/parser/assert.sysml b/language/src/omgTest/resources/parser/assert.sysml new file mode 100644 index 00000000..f6b722b0 --- /dev/null +++ b/language/src/omgTest/resources/parser/assert.sysml @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ +port def Integers { + out attribute channel: int; +} + +part def LogicBasedConstraints { + port input: ~Integers; + port output: Integers; + + assert constraint childsPlay { + (1+1==2) implies false + } + + assert constraint behavior { + input.channel == output.channel + } +} diff --git a/language/src/omgTest/resources/parser/cardinalities.sysml b/language/src/omgTest/resources/parser/cardinalities.sysml new file mode 100644 index 00000000..9d1e3b85 --- /dev/null +++ b/language/src/omgTest/resources/parser/cardinalities.sysml @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ +port def Fluid { + attribute fluidFlow: int[*]; +} + +part tank { + port lubrication: Fluid[1..*]; + port filtering: Fluid[4]; +} + +part specification { + port filtering: Fluid[4]; + + assert constraint expressions { + filtering[2] == filtering[3] + 1 + } +} diff --git a/language/src/omgTest/resources/parser/collections.sysml b/language/src/omgTest/resources/parser/collections.sysml new file mode 100644 index 00000000..b271a479 --- /dev/null +++ b/language/src/omgTest/resources/parser/collections.sysml @@ -0,0 +1 @@ +attribute mySet : MyType[*] = { the, elements, dont, matter }; diff --git a/language/src/omgTest/resources/parser/connections.sysml b/language/src/omgTest/resources/parser/connections.sysml new file mode 100644 index 00000000..5d2c5eff --- /dev/null +++ b/language/src/omgTest/resources/parser/connections.sysml @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +flow of Fuel from port1 to port2; +flow port1 to port2; + +bind a = b; + +message of Fuel from port1 to port2; +message port1 to port2; + +interface def interFace { + end port1; + end port2; +} +interface interFace connect a to b; +interface a to b; diff --git a/language/src/omgTest/resources/parser/constraints.sysml b/language/src/omgTest/resources/parser/constraints.sysml new file mode 100644 index 00000000..fde3c462 --- /dev/null +++ b/language/src/omgTest/resources/parser/constraints.sysml @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ +constraint vanilla { + 1 < 2 +} + +assume constraint assumptions { + true +} + +require shortcut { + false +} diff --git a/language/src/omgTest/resources/parser/ifelseactions.sysml b/language/src/omgTest/resources/parser/ifelseactions.sysml new file mode 100644 index 00000000..2f887b48 --- /dev/null +++ b/language/src/omgTest/resources/parser/ifelseactions.sysml @@ -0,0 +1,6 @@ +if threat.level == high { + perform soundAlarm {in cause = threat;} +} else { + action sendNotification {in msg = threat;} + action beginMonitoring {in target = threat;} +} diff --git a/language/src/omgTest/resources/parser/imports.sysml b/language/src/omgTest/resources/parser/imports.sysml new file mode 100644 index 00000000..ec84c4e0 --- /dev/null +++ b/language/src/omgTest/resources/parser/imports.sysml @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +import SomeName; + +import some.qualified.Name; + +import some::sysml::qualified::Name; diff --git a/language/src/omgTest/resources/parser/items.sysml b/language/src/omgTest/resources/parser/items.sysml new file mode 100644 index 00000000..009f8f8f --- /dev/null +++ b/language/src/omgTest/resources/parser/items.sysml @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ + +item def Fuel; +item def Person; + +item fuel: Fuel; diff --git a/language/src/omgTest/resources/parser/packages.sysml b/language/src/omgTest/resources/parser/packages.sysml new file mode 100644 index 00000000..9b994f15 --- /dev/null +++ b/language/src/omgTest/resources/parser/packages.sysml @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package WithoutBody; + +package WithNestation { + package Deep { + package RealDeep; + } +} diff --git a/language/src/omgTest/resources/parser/parallel_states.sysml b/language/src/omgTest/resources/parser/parallel_states.sysml new file mode 100644 index 00000000..e251d06e --- /dev/null +++ b/language/src/omgTest/resources/parser/parallel_states.sysml @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +state vehicleStates : VehicleStates parallel { + + state operationalStates { + entry; then off; + + state off; + accept VehicleStartSignal + then starting; + + state starting; + accept VehicleOnSignal + then on; + + state on; + accept VehicleOffSignal + then off; + } + + state healthStates; +} diff --git a/language/src/omgTest/resources/parser/parts.sysml b/language/src/omgTest/resources/parser/parts.sysml new file mode 100644 index 00000000..f0e42083 --- /dev/null +++ b/language/src/omgTest/resources/parser/parts.sysml @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +part Driver; + +part def Vehicle { + part driver: Driver; +} + +attribute def Volume; + +part def Engine { + timing delayed; + + attribute v: Volume; +} + +enum def MODE { + enum WORKING; + enum BROKEN; +} + +attribute def Status { + attribute m: MODE; +} + +port def Fluid { + attribute fluidFlow: int; +} + +part tank { + port fluidIn: Fluid; + port fluidOut: ~Fluid; + + connect fluidIn to fluidOut; + connect fluidIn.fluidFlow to fluidOut.fluidFlow; +} diff --git a/language/src/omgTest/resources/parser/ports.sysml b/language/src/omgTest/resources/parser/ports.sysml new file mode 100644 index 00000000..66045482 --- /dev/null +++ b/language/src/omgTest/resources/parser/ports.sysml @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +port def Connector { + in attribute dataIn: int; + attribute control: boolean; + out attribute daatOut: int; + inout attribute checksum: String; +} + +port inTheWild : Connector; +port reverse : ~Connector; diff --git a/language/src/omgTest/resources/parser/refinement.sysml b/language/src/omgTest/resources/parser/refinement.sysml new file mode 100644 index 00000000..466ecd9e --- /dev/null +++ b/language/src/omgTest/resources/parser/refinement.sysml @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +part def A refines B, C; diff --git a/language/src/omgTest/resources/parser/requirements.sysml b/language/src/omgTest/resources/parser/requirements.sysml new file mode 100644 index 00000000..786d3463 --- /dev/null +++ b/language/src/omgTest/resources/parser/requirements.sysml @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +requirement def <'Human ID'> R1 { + subject s:S; +} + +satisfy requirement r1:R1 { + subject s:S; +} + +satisfy requirement r1_1:R1 { + constraint vanilla; + assume constraint assumptions; + require shortcut; +} diff --git a/language/src/omgTest/resources/parser/states.sysml b/language/src/omgTest/resources/parser/states.sysml new file mode 100644 index 00000000..754fa283 --- /dev/null +++ b/language/src/omgTest/resources/parser/states.sysml @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +state def Initial; + +state def Machine { + entry; + then I; + + state I; + + transition + first I + accept someEvent + if condition + do action someAction + then O; + +} + +exhibit state Behavior { + entry; + then S; +} + +state operationalStates { + entry; + do action { send a to b; } + exit someAction; + then off; +} + diff --git a/language/src/omgTest/resources/parser/streams.sysml b/language/src/omgTest/resources/parser/streams.sysml new file mode 100644 index 00000000..0a3d8cb8 --- /dev/null +++ b/language/src/omgTest/resources/parser/streams.sysml @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ +port def Integers { + out attribute channel: int; +} + +part def LogicBasedConstraints { + port input: ~Integers; + port output: Integers; + + assert constraint streams { + #input.channel > output.channel.get(5) + } +} diff --git a/language/src/omgTest/resources/parser/streamsFilter.sysml b/language/src/omgTest/resources/parser/streamsFilter.sysml new file mode 100644 index 00000000..027a95ec --- /dev/null +++ b/language/src/omgTest/resources/parser/streamsFilter.sysml @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ +port def Integers { + out attribute channel: boolean; +} + +part def LogicBasedConstraints { + port input: ~Integers; + port output: Integers; + + assert constraint streams { + input.channel.filter(e -> e) == + } +} diff --git a/visualization/build.gradle b/visualization/build.gradle index 182e021b..1968b7d0 100644 --- a/visualization/build.gradle +++ b/visualization/build.gradle @@ -5,7 +5,7 @@ plugins { tasks.build.dependsOn shadowJar -def omg_version= '0.18.0' +def omg_version= project.findProperty("omgVersion") dependencies { implementation group: 'org.omg.sysml', name: 'org.omg.sysml.interactive', version: omg_version, classifier: 'all'