From 7ae740dd9132528fa51d71e0ea1a0edf63de1327 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 8 Aug 2018 12:51:00 +0300 Subject: [PATCH] MPP: Add tests for new MPP model import --- .../NewMultiplatformProjectImportingTest.kt | 330 ++++++++++++++++++ .../kotlin/gradle/projectStructureDSL.kt | 205 +++++++++++ 2 files changed, 535 insertions(+) create mode 100644 idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt create mode 100644 idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt new file mode 100644 index 00000000000..a1fddcad491 --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt @@ -0,0 +1,330 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle + +import com.intellij.openapi.roots.* +import org.jetbrains.jps.model.java.JavaResourceRootType +import org.jetbrains.jps.model.java.JavaSourceRootType +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.KotlinResourceRootType +import org.jetbrains.kotlin.config.KotlinSourceRootType +import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.idea.codeInsight.gradle.GradleImportingTestCase +import org.junit.Test +import org.junit.runners.Parameterized + +class NewMultiplatformProjectImportingTest : GradleImportingTestCase() { + private val kotlinVersion = "1.2.70-dev-1648" + + @Test + fun testProjectDependency() { + createProjectSubFile( + "settings.gradle", + "include 'lib', 'app'" + ) + createProjectSubFile( + "build.gradle", + """ + buildscript { + repositories { + mavenLocal() + jcenter() + maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + } + } + + allprojects { + repositories { + mavenLocal() + jcenter() + maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } + } + } + """.trimIndent() + ) + createProjectSubFile( + "app/build.gradle", + """ + apply plugin: 'kotlin-multiplatform' + + kotlin { + sourceSets { + commonMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-common' + implementation project(':lib') + } + } + main { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib' + } + } + jsMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' + } + } + } + targets { + fromPreset(presets.jvmWithJava, 'jvm') + fromPreset(presets.js, 'js') + } + } + + apply plugin: 'application' + mainClassName = 'com.example.app.JvmGreeterKt' + """.trimIndent() + ) + createProjectSubFile( + "lib/build.gradle", + """ + apply plugin: 'kotlin-multiplatform' + + kotlin { + sourceSets { + commonMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-common' + } + } + jvmMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib' + } + } + jsMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' + } + } + } + targets { + fromPreset(presets.jvm, 'jvm') + fromPreset(presets.js, 'js') + } + } + """.trimIndent() + ) + + importProject() + + checkProjectStructure() { + allModules { + languageVersion("1.2") + apiVersion("1.2") + } + + module("project") + module("app") + // TODO: Delete metadata modules (after KT-26253 fixed) + module("app_metadataMain") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + moduleDependency("lib_metadataMain", DependencyScope.COMPILE) + sourceFolder("app/src/metadataMain/kotlin", KotlinSourceRootType.Source) + sourceFolder("app/src/metadataMain/resources", KotlinResourceRootType.Resource) + outputPath("app/build/classes/kotlin/metadata/main", true) + } + module("app_metadataTest") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + moduleDependency("app_commonTest", DependencyScope.COMPILE) + moduleDependency("app_metadataMain", DependencyScope.COMPILE) + moduleDependency("lib_metadataMain", DependencyScope.COMPILE) + sourceFolder("app/src/metadataTest/kotlin", KotlinSourceRootType.TestSource) + sourceFolder("app/src/metadataTest/resources", KotlinResourceRootType.TestResource) + outputPath("app/build/classes/kotlin/metadata/test", false) + } + module("app_commonMain") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + sourceFolder("app/src/commonMain/kotlin", KotlinSourceRootType.Source) + sourceFolder("app/src/commonMain/resources", KotlinResourceRootType.Resource) + inheritProjectOutput() + } + module("app_commonTest") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + sourceFolder("app/src/commonTest/kotlin", KotlinSourceRootType.TestSource) + sourceFolder("app/src/commonTest/resources", KotlinResourceRootType.TestResource) + inheritProjectOutput() + } + module("app_jsMain") { + platform(TargetPlatformKind.JavaScript) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_jsMain", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + sourceFolder("app/src/jsMain/kotlin", KotlinSourceRootType.Source) + sourceFolder("app/src/jsMain/resources", KotlinResourceRootType.Resource) + outputPath("app/build/classes/kotlin/js/main", true) + } + module("app_jsTest") { + platform(TargetPlatformKind.JavaScript) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_jsMain", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + moduleDependency("app_commonTest", DependencyScope.COMPILE) + moduleDependency("app_jsMain", DependencyScope.COMPILE) + sourceFolder("app/src/jsTest/kotlin", KotlinSourceRootType.TestSource) + sourceFolder("app/src/jsTest/resources", KotlinResourceRootType.TestResource) + outputPath("app/build/classes/kotlin/js/test", false) + } + module("app_jvmMain") { + platform(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains:annotations:13.0", DependencyScope.COMPILE) + moduleDependency("lib_jvmMain", DependencyScope.COMPILE) + moduleDependency("app_main", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + sourceFolder("app/src/jvmMain/kotlin", JavaSourceRootType.SOURCE) + sourceFolder("app/src/jvmMain/resources", JavaResourceRootType.RESOURCE) + outputPath("app/build/classes/kotlin/jvm/main", true) + } + module("app_jvmTest") { + platform(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains:annotations:13.0", DependencyScope.COMPILE) + moduleDependency("lib_jvmMain", DependencyScope.COMPILE) + moduleDependency("app_test", DependencyScope.COMPILE) + moduleDependency("app_jvmMain", DependencyScope.COMPILE) + moduleDependency("app_commonMain", DependencyScope.COMPILE) + moduleDependency("app_commonTest", DependencyScope.COMPILE) + sourceFolder("app/src/jvmTest/kotlin", JavaSourceRootType.TEST_SOURCE) + sourceFolder("app/src/jvmTest/resources", JavaResourceRootType.TEST_RESOURCE) + outputPath("app/build/classes/kotlin/jvm/test", false) + } + module("app_main") { + platform(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8]) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains:annotations:13.0", DependencyScope.COMPILE) + sourceFolder("app/src/main/java", JavaSourceRootType.SOURCE) + sourceFolder("app/src/main/kotlin", JavaSourceRootType.SOURCE) + sourceFolder("app/src/main/resources", JavaResourceRootType.RESOURCE) + inheritProjectOutput() + } + module("app_test") { + platform(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8]) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains:annotations:13.0", DependencyScope.COMPILE) + moduleDependency("app_main", DependencyScope.COMPILE) + sourceFolder("app/src/test/java", JavaSourceRootType.TEST_SOURCE) + sourceFolder("app/src/test/kotlin", JavaSourceRootType.TEST_SOURCE) + sourceFolder("app/src/test/resources", JavaResourceRootType.TEST_RESOURCE) + inheritProjectOutput() + } + module("lib") + module("lib_metadataMain") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + sourceFolder("lib/src/metadataMain/kotlin", KotlinSourceRootType.Source) + sourceFolder("lib/src/metadataMain/resources", KotlinResourceRootType.Resource) + outputPath("lib/build/classes/kotlin/metadata/main", true) + } + module("lib_metadataTest") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + moduleDependency("lib_commonTest", DependencyScope.COMPILE) + moduleDependency("lib_metadataMain", DependencyScope.COMPILE) + sourceFolder("lib/src/metadataTest/kotlin", KotlinSourceRootType.TestSource) + sourceFolder("lib/src/metadataTest/resources", KotlinResourceRootType.TestResource) + outputPath("lib/build/classes/kotlin/metadata/test", false) + } + module("lib_commonMain") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + sourceFolder("lib/src/commonMain/kotlin", KotlinSourceRootType.Source) + sourceFolder("lib/src/commonMain/resources", KotlinResourceRootType.Resource) + inheritProjectOutput() + } + module("lib_commonTest") { + platform(TargetPlatformKind.Common) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + sourceFolder("lib/src/commonTest/kotlin", KotlinSourceRootType.TestSource) + sourceFolder("lib/src/commonTest/resources", KotlinResourceRootType.TestResource) + inheritProjectOutput() + } + module("lib_jsMain") { + platform(TargetPlatformKind.JavaScript) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + sourceFolder("lib/src/jsMain/kotlin", KotlinSourceRootType.Source) + sourceFolder("lib/src/jsMain/resources", KotlinResourceRootType.Resource) + outputPath("lib/build/classes/kotlin/js/main", true) + } + module("lib_jsTest") { + platform(TargetPlatformKind.JavaScript) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + moduleDependency("lib_commonTest", DependencyScope.COMPILE) + moduleDependency("lib_jsMain", DependencyScope.COMPILE) + sourceFolder("lib/src/jsTest/kotlin", KotlinSourceRootType.TestSource) + sourceFolder("lib/src/jsTest/resources", KotlinResourceRootType.TestResource) + outputPath("lib/build/classes/kotlin/js/test", false) + } + module("lib_jvmMain") { + platform(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains:annotations:13.0", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + sourceFolder("lib/src/jvmMain/kotlin", JavaSourceRootType.SOURCE) + sourceFolder("lib/src/jvmMain/resources", JavaResourceRootType.RESOURCE) + outputPath("lib/build/classes/kotlin/jvm/main", true) + } + module("lib_jvmTest") { + platform(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", DependencyScope.COMPILE) + libraryDependency("Gradle: org.jetbrains:annotations:13.0", DependencyScope.COMPILE) + moduleDependency("lib_commonTest", DependencyScope.COMPILE) + moduleDependency("lib_commonMain", DependencyScope.COMPILE) + moduleDependency("lib_jvmMain", DependencyScope.COMPILE) + sourceFolder("lib/src/jvmTest/kotlin", JavaSourceRootType.TEST_SOURCE) + sourceFolder("lib/src/jvmTest/resources", JavaResourceRootType.TEST_RESOURCE) + outputPath("lib/build/classes/kotlin/jvm/test", false) + } + } + } + + private fun checkProjectStructure(body: ProjectInfo.() -> Unit = {}) { + checkProjectStructure(myProject, projectPath, body) + } + + override fun importProject() { + val isCreateEmptyContentRootDirectories = currentExternalProjectSettings.isCreateEmptyContentRootDirectories + currentExternalProjectSettings.isCreateEmptyContentRootDirectories = true + try { + super.importProject() + } finally { + currentExternalProjectSettings.isCreateEmptyContentRootDirectories = isCreateEmptyContentRootDirectories + } + } + + companion object { + @Parameterized.Parameters(name = "{index}: with Gradle-{0}") + @Throws(Throwable::class) + @JvmStatic + fun data() = listOf(arrayOf("4.7")) + } +} \ No newline at end of file diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt new file mode 100644 index 00000000000..a103c9897a5 --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt @@ -0,0 +1,205 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle + +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.rootManager +import com.intellij.openapi.roots.* +import com.intellij.openapi.util.io.FileUtil +import org.jetbrains.jps.model.module.JpsModuleSourceRootType +import org.jetbrains.jps.util.JpsPathUtil +import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.idea.project.languageVersionSettings +import org.jetbrains.kotlin.idea.project.targetPlatform + +class MessageCollector { + private val builder = StringBuilder() + + fun report(message: String) { + builder.append(message).append('\n') + } + + fun check() { + val message = builder.toString() + if (message.isNotEmpty()) { + assert(false) { message } + } + } +} + +class ProjectInfo(project: Project, private val projectPath: String) { + private val messageCollector = MessageCollector() + private val moduleManager = ModuleManager.getInstance(project) + private val expectedModuleNames = HashSet() + private var allModulesAsserter: (ModuleInfo.() -> Unit)? = null + + fun allModules(body: ModuleInfo.() -> Unit) { + assert(allModulesAsserter == null) + allModulesAsserter = body + } + + fun module(name: String, body: ModuleInfo.() -> Unit = {}) { + val module = moduleManager.findModuleByName(name) + if (module == null) { + messageCollector.report("No module found: '$name'") + return + } + val moduleInfo = ModuleInfo(module, messageCollector, projectPath) + allModulesAsserter?.let { moduleInfo.it() } + moduleInfo.run(body) + expectedModuleNames += name + } + + fun run(body: ProjectInfo.() -> Unit = {}) { + body() + + val actualNames = moduleManager.modules.map { it.name }.sorted() + val expectedNames = expectedModuleNames.sorted() + if (actualNames != expectedNames) { + messageCollector.report("Expected module list $expectedNames doesn't match the actual one: $actualNames") + } + + messageCollector.check() + } +} + +class ModuleInfo( + private val module: Module, + private val messageCollector: MessageCollector, + private val projectPath: String +) { + private val rootModel = module.rootManager + private val expectedDependencyNames = HashSet() + private val expectedSourceRoots = HashSet() + private val sourceFolderByPath by lazy { + rootModel.contentEntries.asSequence() + .flatMap { it.sourceFolders.asSequence() } + .mapNotNull { + val path = it.file?.path ?: return@mapNotNull null + FileUtil.getRelativePath(projectPath, path, '/')!! to it + } + .toMap() + } + + fun languageVersion(version: String) { + val actualVersion = module.languageVersionSettings.languageVersion.versionString + if (actualVersion != version) { + messageCollector.report("Module '${module.name}': expected language version '$version' but found '$actualVersion'") + } + } + + fun apiVersion(version: String) { + val actualVersion = module.languageVersionSettings.apiVersion.versionString + if (actualVersion != version) { + messageCollector.report("Module '${module.name}': expected API version '$version' but found '$actualVersion'") + } + } + + fun platform(platform: TargetPlatformKind<*>) { + val actualPlatform = module.targetPlatform + if (actualPlatform != platform) { + messageCollector.report( + "Module '${module.name}': expected platform '${platform.description}' but found '${actualPlatform?.description}'" + ) + } + } + + fun libraryDependency(libraryName: String, scope: DependencyScope) { + val libraryEntry = rootModel.orderEntries.filterIsInstance().singleOrNull { it.libraryName == libraryName } + if (libraryEntry == null) { + messageCollector.report("Module '${module.name}': No library dependency found: '$libraryName'") + return + } + checkDependencyScope(libraryEntry, scope) + expectedDependencyNames += libraryEntry.presentableName + } + + fun moduleDependency(moduleName: String, scope: DependencyScope) { + val moduleEntry = rootModel.orderEntries.filterIsInstance().singleOrNull { it.moduleName == moduleName } + if (moduleEntry == null) { + messageCollector.report("Module '${module.name}': No module dependency found: '$moduleName'") + return + } + checkDependencyScope(moduleEntry, scope) + expectedDependencyNames += moduleEntry.presentableName + } + + fun sourceFolder(pathInProject: String, rootType: JpsModuleSourceRootType<*>) { + val sourceFolder = sourceFolderByPath[pathInProject] + if (sourceFolder == null) { + messageCollector.report("Module '${module.name}': No source folder found: '$pathInProject'") + return + } + expectedSourceRoots += pathInProject + val actualRootType = sourceFolder.rootType + if (actualRootType != rootType) { + messageCollector.report( + "Module '${module.name}', source root '$pathInProject': Expected root type $rootType doesn't match the actual one: $actualRootType" + ) + return + } + } + + fun inheritProjectOutput() { + val isInherited = CompilerModuleExtension.getInstance(module)?.isCompilerOutputPathInherited ?: true + if (!isInherited) { + messageCollector.report("Module '${module.name}': project output is not inherited") + } + } + + fun outputPath(pathInProject: String, isProduction: Boolean) { + val compilerModuleExtension = CompilerModuleExtension.getInstance(module) + val url = if (isProduction) compilerModuleExtension?.compilerOutputUrl else compilerModuleExtension?.compilerOutputUrlForTests + val actualPathInProject = url?.let { + FileUtil.getRelativePath( + projectPath, + JpsPathUtil.urlToPath( + it + ), + '/' + ) + } + if (actualPathInProject != pathInProject) { + messageCollector.report( + "Module '${module.name}': Expected output path $pathInProject doesn't match the actual one: $actualPathInProject" + ) + return + } + } + + fun run(body: ModuleInfo.() -> Unit = {}) { + body() + + val actualDependencyNames = rootModel + .orderEntries + .filter { it is ModuleOrderEntry || it is LibraryOrderEntry } + .map { it.presentableName } + .sorted() + val expectedDependencyNames = expectedDependencyNames.sorted() + if (actualDependencyNames != expectedDependencyNames) { + messageCollector.report("Module '${module.name}': Expected dependency list $expectedDependencyNames doesn't match the actual one: $actualDependencyNames") + } + + val actualSourceRoots = sourceFolderByPath.keys.sorted() + val expectedSourceRoots = expectedSourceRoots.sorted() + if (actualSourceRoots != expectedSourceRoots) { + messageCollector.report("Module '${module.name}': Expected source root list $expectedSourceRoots doesn't match the actual one: $actualSourceRoots") + } + } + + private fun checkDependencyScope(library: ExportableOrderEntry, scope: DependencyScope) { + val actualScope = library.scope + if (actualScope != scope) { + messageCollector.report("Module '${module.name}': Dependency '${library.presentableName}': expected scope '$scope' but found '$actualScope'") + } + } +} + +fun checkProjectStructure(project: Project, projectPath: String, body: ProjectInfo.() -> Unit = {}) { + ProjectInfo(project, projectPath).run(body) +} \ No newline at end of file