diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1f7081610c..2745814af1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,6 +39,7 @@ micrometer = "1.13.2" dokka = "1.9.20" detekt = "1.23.6" xmlunit = "2.10.0" +kotest = "5.9.1" [libraries] @@ -84,7 +85,8 @@ ktor-serialization-json = { group = "io.ktor", name = "ktor-serialization-kotlin keycloak-authz-client = { group = "org.keycloak", name = "keycloak-authz-client", version = "25.0.1" } -kotest-assertions-coreJvm = { group = "io.kotest", name = "kotest-assertions-core-jvm", version = "5.9.1" } +kotest-assertions-core = { group = "io.kotest", name = "kotest-assertions-core", version.ref = "kotest" } +kotest-assertions-coreJvm = { group = "io.kotest", name = "kotest-assertions-core-jvm", version.ref = "kotest" } kotest-assertions-ktor = { group = "io.kotest.extensions", name = "kotest-assertions-ktor", version = "2.0.0" } guava = { group = "com.google.guava", name = "guava", version = "33.2.1-jre" } diff --git a/model-api/build.gradle.kts b/model-api/build.gradle.kts index 87edd3585d..31f9ec55df 100644 --- a/model-api/build.gradle.kts +++ b/model-api/build.gradle.kts @@ -31,6 +31,7 @@ kotlin { val commonTest by getting { dependencies { implementation(kotlin("test")) + implementation(libs.kotest.assertions.core) } } val jvmMain by getting { diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt index b45023a4c2..3e0a123caf 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt @@ -68,7 +68,11 @@ object BuiltinLanguages { init { addConcept(this) } } - object INamedConcept : SimpleConcept(conceptName = "INamedConcept") { + object INamedConcept : SimpleConcept( + conceptName = "INamedConcept", + is_abstract = true, + uid = "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/1169194658468", + ) { init { addConcept(this) } val name = SimpleProperty( "name", diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt index da3d0f109f..078a8de999 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt @@ -56,6 +56,8 @@ interface IConcept { /** * Checks if this concept is abstract. * + * A concept is abstract if it is not designated to be instantiated directly. + * * @return true if the concept is abstract, false otherwise */ fun isAbstract(): Boolean diff --git a/model-api/src/commonTest/kotlin/org/modelix/model/api/BuiltinLanguagesTest.kt b/model-api/src/commonTest/kotlin/org/modelix/model/api/BuiltinLanguagesTest.kt index 34489692f0..d76b7cca2e 100644 --- a/model-api/src/commonTest/kotlin/org/modelix/model/api/BuiltinLanguagesTest.kt +++ b/model-api/src/commonTest/kotlin/org/modelix/model/api/BuiltinLanguagesTest.kt @@ -16,6 +16,8 @@ package org.modelix.model.api +import io.kotest.inspectors.forAll +import io.kotest.matchers.string.shouldStartWith import kotlin.test.Test import kotlin.test.assertEquals @@ -41,4 +43,14 @@ class BuiltinLanguagesTest { // They were only accessible by directly calling Model.modelImports for example. assertEquals(3, childLinks.size) } + + @Test + fun allBuiltInLanguagesHaveMpsConceptId() { + val concepts = BuiltinLanguages.getAllLanguages() + .flatMap { it.getConcepts() } + + concepts.forAll { concept: IConcept -> + concept.getUID().shouldStartWith("mps:") + } + } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt index b064d9fb16..018ae6ddc4 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt @@ -54,6 +54,11 @@ data class MPSConcept(val concept: SAbstractConceptAdapter) : IConcept { } override fun isAbstract(): Boolean { + // In MPS `org.jetbrains.mps.openapi.language.SAbstractConcept.isAbstract` + // returns `true` for abstract concepts and interface concepts. + // See https://github.com/JetBrains/MPS/blob/78b81f56866370e227262000e597a211f885b9e6/core/kernel/source/jetbrains/mps/smodel/adapter/structure/concept/SConceptAdapterById.java#L54 + // This exactly matches with the definition of `IConcept.isAbstract`, + // as such concepts are not designated to be instantiated directly. return concept.isAbstract }