diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index d9076ec..2f83f0d 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -5,6 +5,8 @@
+
+
-
\ No newline at end of file
+
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 247e294..d6bfecb 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -20,6 +20,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index c224ad5..c41c613 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
-
\ No newline at end of file
+
diff --git a/api/src/main/kotlin/org/modelix/mps/api/IModelixMpsApi.kt b/api/src/main/kotlin/org/modelix/mps/api/IModelixMpsApi.kt
index 49f0187..7e9ff69 100644
--- a/api/src/main/kotlin/org/modelix/mps/api/IModelixMpsApi.kt
+++ b/api/src/main/kotlin/org/modelix/mps/api/IModelixMpsApi.kt
@@ -1,7 +1,7 @@
package org.modelix.mps.api
-import jetbrains.mps.project.Project
import org.jetbrains.mps.openapi.module.SModule
+import org.jetbrains.mps.openapi.project.Project
interface IModelixMpsApi {
fun getMPSProjects(): List
diff --git a/build.gradle.kts b/build.gradle.kts
index 776e55f..12e95a8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,3 @@
-import org.gradle.kotlin.dsl.configure
-import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
@@ -49,6 +47,13 @@ for ((majorVersion, fullVersion) in mpsVersions) {
into(mpsDir)
}
}
+ // The build number of a local IDE is expected to contain a product code, otherwise an exception is thrown.
+ val buildTxt = mpsDir.get().asFile.resolve("build.txt")
+ val buildNumber = buildTxt.readText()
+ val prefix = "MPS-"
+ if (!buildNumber.startsWith(prefix)) {
+ buildTxt.writeText("$prefix$buildNumber")
+ }
dependencies {
"implementation"(project(":api"))
diff --git a/gradle.properties b/gradle.properties
index f7ee5d3..70da221 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-org.gradle.configuration-cache=true
+org.gradle.configuration-cache=false
org.gradle.parallel=true
org.gradle.caching=true
kotlin.daemon.jvmargs=-Xmx2g
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e6a780f..64ee9e9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -9,3 +9,5 @@
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.1.10" }
shadow = { id = "com.gradleup.shadow", version = "9.0.0-beta6" }
binaryCompatibility = { id ="org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.17.0" }
+intellij = { id = "org.jetbrains.intellij", version = "1.17.4" }
+intellij2 = { id = "org.jetbrains.intellij.platform", version = "2.2.1" }
diff --git a/impl203/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl203.kt b/impl203/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl203.kt
index 33f24a3..9951f30 100644
--- a/impl203/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl203.kt
+++ b/impl203/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl203.kt
@@ -2,9 +2,9 @@ package org.modelix.mps.api
import jetbrains.mps.lang.migration.runtime.base.VersionFixer
import jetbrains.mps.project.MPSProject
-import jetbrains.mps.project.Project
import jetbrains.mps.project.ProjectManager
import org.jetbrains.mps.openapi.module.SModule
+import org.jetbrains.mps.openapi.project.Project
open class ModelixMpsApiImpl203 : IModelixMpsApi {
override fun getMPSProjects(): List {
@@ -19,6 +19,6 @@ open class ModelixMpsApiImpl203 : IModelixMpsApi {
}
override fun fixVersions(project: Project, module: SModule) {
- VersionFixer(project, module, true).updateImportVersions()
+ VersionFixer(project as jetbrains.mps.project.Project, module, true).updateImportVersions()
}
}
diff --git a/impl211/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl211.kt b/impl211/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl211.kt
index 6510691..b18ff77 100644
--- a/impl211/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl211.kt
+++ b/impl211/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl211.kt
@@ -1,9 +1,9 @@
package org.modelix.mps.api
-import jetbrains.mps.project.Project
import jetbrains.mps.smodel.ModuleDependencyVersions
import jetbrains.mps.smodel.language.LanguageRegistry
import org.jetbrains.mps.openapi.module.SModule
+import org.jetbrains.mps.openapi.project.Project
open class ModelixMpsApiImpl211 : ModelixMpsApiImpl203() {
override fun fixVersions(project: Project, module: SModule) {
diff --git a/impl222/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl222.kt b/impl222/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl222.kt
index c2be93e..71214f2 100644
--- a/impl222/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl222.kt
+++ b/impl222/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl222.kt
@@ -1,8 +1,8 @@
package org.modelix.mps.api
import jetbrains.mps.project.MPSProject
-import jetbrains.mps.project.Project
import org.jetbrains.mps.openapi.module.SModule
+import org.jetbrains.mps.openapi.project.Project
open class ModelixMpsApiImpl222 : ModelixMpsApiImpl213() {
override fun getVirtualFolder(
diff --git a/impl243/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl243.kt b/impl243/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl243.kt
index 0b2eaef..d77029b 100644
--- a/impl243/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl243.kt
+++ b/impl243/src/main/kotlin/org/modelix/mps/api/ModelixMpsApiImpl243.kt
@@ -1,8 +1,8 @@
package org.modelix.mps.api
import jetbrains.mps.ide.MPSCoreComponents
-import jetbrains.mps.project.Project
import jetbrains.mps.project.ProjectManager
+import org.jetbrains.mps.openapi.project.Project
open class ModelixMpsApiImpl243 : ModelixMpsApiImpl241() {
override fun getMPSProjects(): List {
diff --git a/lib/api/lib.api b/lib/api/lib.api
index 400b6b0..d118234 100644
--- a/lib/api/lib.api
+++ b/lib/api/lib.api
@@ -1,9 +1,9 @@
public final class org/modelix/mps/api/ModelixMpsApi : org/modelix/mps/api/IModelixMpsApi {
public static final field INSTANCE Lorg/modelix/mps/api/ModelixMpsApi;
- public fun fixVersions (Ljetbrains/mps/project/Project;Lorg/jetbrains/mps/openapi/module/SModule;)V
+ public fun fixVersions (Lorg/jetbrains/mps/openapi/project/Project;Lorg/jetbrains/mps/openapi/module/SModule;)V
public fun getMPSProjects ()Ljava/util/List;
- public fun getVirtualFolder (Ljetbrains/mps/project/Project;Lorg/jetbrains/mps/openapi/module/SModule;)Ljava/lang/String;
public fun getVirtualFolder (Lorg/jetbrains/mps/openapi/module/SModule;)Ljava/lang/String;
+ public fun getVirtualFolder (Lorg/jetbrains/mps/openapi/project/Project;Lorg/jetbrains/mps/openapi/module/SModule;)Ljava/lang/String;
public fun getVirtualFolders (Lorg/jetbrains/mps/openapi/module/SModule;)Ljava/util/List;
}
diff --git a/lib/src/main/kotlin/org/modelix/mps/api/ModelixMpsApi.kt b/lib/src/main/kotlin/org/modelix/mps/api/ModelixMpsApi.kt
index e27ab6d..c34e9d7 100644
--- a/lib/src/main/kotlin/org/modelix/mps/api/ModelixMpsApi.kt
+++ b/lib/src/main/kotlin/org/modelix/mps/api/ModelixMpsApi.kt
@@ -4,7 +4,7 @@ import com.intellij.openapi.application.ApplicationInfo
private fun detectMpsVersion(): Int {
val info = ApplicationInfo.getInstance()
- return info.majorVersion.toInt() - 2000 + info.minorVersionMainPart.toInt()
+ return (info.majorVersion.toInt() - 2000) * 10 + info.minorVersionMainPart.toInt()
}
private fun resolveInstance(): IModelixMpsApi {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2682d4e..5cc9cde 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -6,6 +6,7 @@ plugins {
rootProject.name = "modelix.mps-api"
include("api")
include("lib")
+include("test")
// https://artifacts.itemis.cloud/service/rest/repository/browse/maven-mps/com/jetbrains/mps/
val mpsVersions = mapOf(
@@ -23,4 +24,7 @@ val mpsVersions = mapOf(
for (majorVersion in mpsVersions.keys) {
include("impl$majorVersion")
+ if (majorVersion < 242) { // MPS is not yet compatible to the new intellij plugin
+ include(":test:$majorVersion")
+ }
}
diff --git a/test/203/build.gradle.kts b/test/203/build.gradle.kts
new file mode 100644
index 0000000..08c2c8e
--- /dev/null
+++ b/test/203/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl203").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/203/src/test/kotlin/TestBase.kt b/test/203/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/203/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/203/src/test/kotlin/VirtualFolderTests203.kt b/test/203/src/test/kotlin/VirtualFolderTests203.kt
new file mode 100644
index 0000000..8f4d4a7
--- /dev/null
+++ b/test/203/src/test/kotlin/VirtualFolderTests203.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests203 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/203/testdata/SimpleProject/.mps/modules.xml b/test/203/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/203/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/203/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/203/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/203/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/203/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/203/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/203/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/211/build.gradle.kts b/test/211/build.gradle.kts
new file mode 100644
index 0000000..ebcba97
--- /dev/null
+++ b/test/211/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl211").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/211/src/test/kotlin/TestBase.kt b/test/211/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/211/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/211/src/test/kotlin/VirtualFolderTests211.kt b/test/211/src/test/kotlin/VirtualFolderTests211.kt
new file mode 100644
index 0000000..c3fab10
--- /dev/null
+++ b/test/211/src/test/kotlin/VirtualFolderTests211.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests211 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/211/testdata/SimpleProject/.mps/modules.xml b/test/211/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/211/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/211/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/211/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/211/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/211/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/211/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/211/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/212/build.gradle.kts b/test/212/build.gradle.kts
new file mode 100644
index 0000000..807db89
--- /dev/null
+++ b/test/212/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl212").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/212/src/test/kotlin/TestBase.kt b/test/212/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/212/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/212/src/test/kotlin/VirtualFolderTests212.kt b/test/212/src/test/kotlin/VirtualFolderTests212.kt
new file mode 100644
index 0000000..7293f9b
--- /dev/null
+++ b/test/212/src/test/kotlin/VirtualFolderTests212.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests212 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/212/testdata/SimpleProject/.mps/modules.xml b/test/212/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/212/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/212/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/212/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/212/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/212/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/212/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/212/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/213/build.gradle.kts b/test/213/build.gradle.kts
new file mode 100644
index 0000000..2b99c0f
--- /dev/null
+++ b/test/213/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl213").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/213/src/test/kotlin/TestBase.kt b/test/213/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/213/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/213/src/test/kotlin/VirtualFolderTests213.kt b/test/213/src/test/kotlin/VirtualFolderTests213.kt
new file mode 100644
index 0000000..38d98ab
--- /dev/null
+++ b/test/213/src/test/kotlin/VirtualFolderTests213.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests213 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/213/testdata/SimpleProject/.mps/modules.xml b/test/213/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/213/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/213/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/213/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/213/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/213/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/213/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/213/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/222/build.gradle.kts b/test/222/build.gradle.kts
new file mode 100644
index 0000000..d3f9bea
--- /dev/null
+++ b/test/222/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl222").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/222/src/test/kotlin/TestBase.kt b/test/222/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/222/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/222/src/test/kotlin/VirtualFolderTests222.kt b/test/222/src/test/kotlin/VirtualFolderTests222.kt
new file mode 100644
index 0000000..6677125
--- /dev/null
+++ b/test/222/src/test/kotlin/VirtualFolderTests222.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests222 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/222/testdata/SimpleProject/.mps/modules.xml b/test/222/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/222/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/222/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/222/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/222/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/222/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/222/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/222/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/223/build.gradle.kts b/test/223/build.gradle.kts
new file mode 100644
index 0000000..2a79527
--- /dev/null
+++ b/test/223/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl223").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/223/src/test/kotlin/TestBase.kt b/test/223/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/223/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/223/src/test/kotlin/VirtualFolderTests223.kt b/test/223/src/test/kotlin/VirtualFolderTests223.kt
new file mode 100644
index 0000000..086d16f
--- /dev/null
+++ b/test/223/src/test/kotlin/VirtualFolderTests223.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests223 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/223/testdata/SimpleProject/.mps/modules.xml b/test/223/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/223/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/223/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/223/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/223/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/223/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/223/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/223/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/232/build.gradle.kts b/test/232/build.gradle.kts
new file mode 100644
index 0000000..e320508
--- /dev/null
+++ b/test/232/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl232").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/232/src/test/kotlin/TestBase.kt b/test/232/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/232/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/232/src/test/kotlin/VirtualFolderTests232.kt b/test/232/src/test/kotlin/VirtualFolderTests232.kt
new file mode 100644
index 0000000..71ab34b
--- /dev/null
+++ b/test/232/src/test/kotlin/VirtualFolderTests232.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests232 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/232/testdata/SimpleProject/.mps/modules.xml b/test/232/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/232/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/232/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/232/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/232/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/232/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/232/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/232/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/233/build.gradle.kts b/test/233/build.gradle.kts
new file mode 100644
index 0000000..b9d7817
--- /dev/null
+++ b/test/233/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl233").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/233/src/test/kotlin/TestBase.kt b/test/233/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/233/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/233/src/test/kotlin/VirtualFolderTests233.kt b/test/233/src/test/kotlin/VirtualFolderTests233.kt
new file mode 100644
index 0000000..2d46a62
--- /dev/null
+++ b/test/233/src/test/kotlin/VirtualFolderTests233.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests233 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/233/testdata/SimpleProject/.mps/modules.xml b/test/233/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/233/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/233/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/233/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/233/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/233/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/233/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/233/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/241/build.gradle.kts b/test/241/build.gradle.kts
new file mode 100644
index 0000000..3329d34
--- /dev/null
+++ b/test/241/build.gradle.kts
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.intellij)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+intellij {
+ localPath = project(":impl241").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }
+ instrumentCode = false
+}
+
+tasks {
+ buildSearchableOptions {
+ enabled = false
+ }
+}
+
+dependencies {
+ testImplementation(project(":lib"))
+}
diff --git a/test/241/src/test/kotlin/TestBase.kt b/test/241/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/241/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/241/src/test/kotlin/VirtualFolderTests241.kt b/test/241/src/test/kotlin/VirtualFolderTests241.kt
new file mode 100644
index 0000000..8154d2a
--- /dev/null
+++ b/test/241/src/test/kotlin/VirtualFolderTests241.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests241 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/241/testdata/SimpleProject/.mps/modules.xml b/test/241/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/241/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/241/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/241/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/241/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/241/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/241/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/241/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/243/build.gradle.kts b/test/243/build.gradle.kts
new file mode 100644
index 0000000..65221a6
--- /dev/null
+++ b/test/243/build.gradle.kts
@@ -0,0 +1,31 @@
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.jetbrains.intellij.platform.gradle.TestFrameworkType
+
+plugins {
+ alias(libs.plugins.intellij2)
+ alias(libs.plugins.kotlin.jvm)
+}
+
+repositories {
+ intellijPlatform {
+ defaultRepositories()
+ localPlatformArtifacts()
+ }
+}
+
+intellijPlatform {
+}
+
+dependencies {
+ intellijPlatform {
+ if (Os.isOs(Os.FAMILY_MAC, null, "aarch64", null)) {
+ local(System.getProperty("user.home") + "/Applications/MPS 2024.3.app/Contents")
+ } else {
+ local(project.project(":impl243").layout.buildDirectory.dir("mps").map { it.asFile.absolutePath }.get())
+ }
+ // intellijIdeaCommunity("2024.3")
+ testFramework(TestFrameworkType.Platform)
+ }
+ testImplementation(project(":lib"))
+ testImplementation("junit:junit:4.13.2")
+}
diff --git a/test/243/src/test/kotlin/TestBase.kt b/test/243/src/test/kotlin/TestBase.kt
new file mode 100644
index 0000000..b784d0b
--- /dev/null
+++ b/test/243/src/test/kotlin/TestBase.kt
@@ -0,0 +1,103 @@
+import com.intellij.ide.impl.OpenProjectTask
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.TestApplicationManager
+import com.intellij.testFramework.UsefulTestCase
+import com.intellij.util.io.delete
+import jetbrains.mps.ide.ThreadUtils
+import jetbrains.mps.ide.project.ProjectHelper
+import jetbrains.mps.project.MPSProject
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Based on org.jetbrains.uast.test.env.AbstractLargeProjectTest
+ */
+@Suppress("removal")
+abstract class TestBase(val testDataName: String?) : UsefulTestCase() {
+ init {
+ // workaround for MPS 2023.3 failing to start in test mode
+ System.setProperty("intellij.platform.load.app.info.from.resources", "true")
+ }
+
+ protected lateinit var project: Project
+
+ override fun runInDispatchThread() = false
+
+ override fun setUp() {
+ super.setUp()
+ TestApplicationManager.getInstance()
+ project = openTestProject()
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ }
+
+ private fun openTestProject(): Project {
+ val projectDirParent = Path.of("build", "test-projects").toAbsolutePath()
+ projectDirParent.toFile().mkdirs()
+ val projectDir = Files.createTempDirectory(projectDirParent, "mps-project")
+ projectDir.delete(recursively = true)
+ projectDir.toFile().mkdirs()
+ projectDir.toFile().deleteOnExit()
+ val project = if (testDataName != null) {
+ val sourceDir = File("testdata/$testDataName")
+ sourceDir.copyRecursively(projectDir.toFile(), overwrite = true)
+ ProjectManagerEx.getInstanceEx().openProject(projectDir, OpenProjectTask())!!
+ } else {
+ ProjectManagerEx.getInstanceEx().newProject(projectDir, OpenProjectTask())!!
+ }
+
+ disposeOnTearDownInEdt { runCatching { ProjectManager.getInstance().closeAndDispose(project) } }
+
+ ApplicationManager.getApplication().invokeAndWait {
+ // empty - openTestProject executed not in EDT, so, invokeAndWait just forces
+ // processing of all events that were queued during project opening
+ }
+
+ return project
+ }
+
+ private fun disposeOnTearDownInEdt(runnable: Runnable) {
+ Disposer.register(
+ testRootDisposable,
+ Disposable {
+ ApplicationManager.getApplication().invokeAndWait(runnable)
+ },
+ )
+ }
+
+ protected val mpsProject: MPSProject get() {
+ return checkNotNull(ProjectHelper.fromIdeaProject(project)) { "MPS project not loaded" }
+ }
+
+ protected fun writeAction(body: () -> R): R {
+ return mpsProject.modelAccess.computeWriteAction(body)
+ }
+
+ protected fun writeActionOnEdt(body: () -> R): R {
+ return onEdt { writeAction { body() } }
+ }
+
+ protected fun onEdt(body: () -> R): R {
+ var result: R? = null
+ ThreadUtils.runInUIThreadAndWait {
+ result = body()
+ }
+ return result as R
+ }
+
+ protected fun readAction(body: () -> R): R {
+ var result: R? = null
+ mpsProject.modelAccess.runReadAction {
+ result = body()
+ }
+ return result as R
+ }
+}
diff --git a/test/243/src/test/kotlin/VirtualFolderTests243.kt b/test/243/src/test/kotlin/VirtualFolderTests243.kt
new file mode 100644
index 0000000..c561848
--- /dev/null
+++ b/test/243/src/test/kotlin/VirtualFolderTests243.kt
@@ -0,0 +1,10 @@
+import org.modelix.mps.api.ModelixMpsApi
+
+class VirtualFolderTests243 : TestBase("SimpleProject") {
+
+ fun `test getVirtualFolder`() {
+ val module = ModelixMpsApi.getMPSProjects().single().projectModules.single()
+ val folder = ModelixMpsApi.getVirtualFolder(module)
+ assertEquals("myFolder", folder)
+ }
+}
diff --git a/test/243/testdata/SimpleProject/.mps/modules.xml b/test/243/testdata/SimpleProject/.mps/modules.xml
new file mode 100644
index 0000000..6b0fa61
--- /dev/null
+++ b/test/243/testdata/SimpleProject/.mps/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/243/testdata/SimpleProject/solutions/Solution1/Solution1.msd b/test/243/testdata/SimpleProject/solutions/Solution1/Solution1.msd
new file mode 100644
index 0000000..f015948
--- /dev/null
+++ b/test/243/testdata/SimpleProject/solutions/Solution1/Solution1.msd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/243/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps b/test/243/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
new file mode 100644
index 0000000..64b0724
--- /dev/null
+++ b/test/243/testdata/SimpleProject/solutions/Solution1/models/Solution1.model1.mps
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+