diff --git a/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCaseBase.java.182 b/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCaseBase.java.182 new file mode 100644 index 00000000000..f7aea1cef1d --- /dev/null +++ b/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCaseBase.java.182 @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.test; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.TempFiles; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.util.Collection; + +public abstract class KotlinLightCodeInsightFixtureTestCaseBase extends LightCodeInsightFixtureTestCase { + @NotNull + @Override + public Project getProject() { + return super.getProject(); + } + + @NotNull + @Override + public Editor getEditor() { + return super.getEditor(); + } + + @Override + public PsiFile getFile() { + return super.getFile(); + } + + protected final Collection myFilesToDelete = new THashSet<>(); + private final TempFiles myTempFiles = new TempFiles(myFilesToDelete); + + @Override + protected void tearDown() throws Exception { + myTempFiles.deleteAll(); + super.tearDown(); + } + + @NotNull + protected File createTempFile(@NotNull String name, @Nullable String text) throws IOException { + File directory = createTempDirectory(); + File file = new File(directory, name); + if (!file.createNewFile()) { + throw new IOException("Can't create " + file); + } + if (text != null) { + FileUtil.writeToFile(file, text); + } + return file; + } + + @NotNull + public VirtualFile createTempFile( + @NonNls @NotNull String ext, + @Nullable byte[] bom, + @NonNls @NotNull String content, + @NotNull Charset charset + ) throws IOException { + File temp = FileUtil.createTempFile("copy", "." + ext); + setContentOnDisk(temp, bom, content, charset); + + myFilesToDelete.add(temp); + final VirtualFile file = getVirtualFile(temp); + assert file != null : temp; + return file; + } + + public static void setContentOnDisk(@NotNull File file, @Nullable byte[] bom, @NotNull String content, @NotNull Charset charset) + throws IOException { + FileOutputStream stream = new FileOutputStream(file); + if (bom != null) { + stream.write(bom); + } + try (OutputStreamWriter writer = new OutputStreamWriter(stream, charset)) { + writer.write(content); + } + } + + @NotNull + protected File createTempDirectory() throws IOException { + return createTempDir(""); + } + + @NotNull + public File createTempDir(@NonNls @NotNull String prefix) throws IOException { + return createTempDir(prefix, true); + } + + @NotNull + public File createTempDir(@NonNls @NotNull String prefix, final boolean refresh) throws IOException { + final File tempDirectory = FileUtilRt.createTempDirectory("idea_test_" + prefix, null, false); + myFilesToDelete.add(tempDirectory); + if (refresh) { + getVirtualFile(tempDirectory); + } + return tempDirectory; + } + + protected static VirtualFile getVirtualFile(@NotNull File file) { + return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + } + +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt.182 b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt.182 new file mode 100644 index 00000000000..16a65845444 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt.182 @@ -0,0 +1,268 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.configuration + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.PathMacros +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess +import com.intellij.testFramework.PlatformTestCase +import com.intellij.testFramework.UsefulTestCase +import junit.framework.TestCase +import org.jetbrains.kotlin.idea.configuration.KotlinWithLibraryConfigurator.FileState +import org.jetbrains.kotlin.idea.framework.KotlinSdkType +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File +import java.nio.file.Path + +abstract class AbstractConfigureKotlinTest : PlatformTestCase() { + override fun setUp() { + super.setUp() + + val distPaths = with(PathUtil.kotlinPathsForIdeaPlugin) { + listOf( + stdlibPath, + stdlibSourcesPath, + reflectPath, + kotlinTestPath, + jsKotlinTestJarPath, + jsStdLibJarPath, + jsStdLibSrcJarPath + ) + } + + for (path in distPaths) { + VfsRootAccess.allowRootAccess(testRootDisposable, path.absolutePath) + } + } + + @Throws(Exception::class) + override fun tearDown() { + PathMacros.getInstance().removeMacro(TEMP_DIR_MACRO_KEY) + + super.tearDown() + } + + @Throws(Exception::class) + override fun initApplication() { + super.initApplication() + + KotlinSdkType.setUpIfNeeded() + + ApplicationManager.getApplication().runWriteAction { + ProjectJdkTable.getInstance().addJdk(PluginTestCaseBase.mockJdk6()) + ProjectJdkTable.getInstance().addJdk(PluginTestCaseBase.mockJdk8()) + ProjectJdkTable.getInstance().addJdk(PluginTestCaseBase.mockJdk9()) + } + + PluginTestCaseBase.clearSdkTable(testRootDisposable) + + val tempLibDir = FileUtil.createTempDirectory("temp", null) + PathMacros.getInstance().setMacro(TEMP_DIR_MACRO_KEY, FileUtilRt.toSystemDependentName(tempLibDir.absolutePath)) + } + + protected fun doTestConfigureModulesWithNonDefaultSetup(configurator: KotlinWithLibraryConfigurator) { + assertNoFilesInDefaultPaths() + + val modules = modules + for (module in modules) { + assertNotConfigured(module, configurator) + } + + configurator.configure(myProject, emptyList()) + + assertNoFilesInDefaultPaths() + + for (module in modules) { + assertProperlyConfigured(module, configurator) + } + } + + protected fun doTestOneJavaModule(jarState: FileState) { + doTestOneModule(jarState, JAVA_CONFIGURATOR) + } + + protected fun doTestOneJsModule(jarState: FileState) { + doTestOneModule(jarState, JS_CONFIGURATOR) + } + + private fun doTestOneModule(jarState: FileState, configurator: KotlinWithLibraryConfigurator) { + val module = module + + assertNotConfigured(module, configurator) + configure(module, jarState, configurator) + assertProperlyConfigured(module, configurator) + } + + override fun getModule(): Module { + val modules = ModuleManager.getInstance(myProject).modules + assert(modules.size == 1) { "One module should be loaded " + modules.size } + myModule = modules[0] + return super.getModule() + } + + val modules: Array + get() = ModuleManager.getInstance(myProject).modules + + override fun getProjectDirOrFile(): Path { + val projectFilePath = projectRoot + "/projectFile.ipr" + TestCase.assertTrue("Project file should exists " + projectFilePath, File(projectFilePath).exists()) + return File(projectFilePath).toPath() + } + + override fun doCreateProject(projectFile: Path): Project { + return myProjectManager.loadProject(projectFile.toFile().path)!! + } + + private val projectName: String + get() { + val testName = getTestName(true) + return if (testName.contains("_")) { + testName.substring(0, testName.indexOf("_")) + } + else testName + } + + protected val projectRoot: String + get() = BASE_PATH + projectName + + override fun setUpModule() {} + + private fun assertNoFilesInDefaultPaths() { + UsefulTestCase.assertDoesntExist(File(JAVA_CONFIGURATOR.getDefaultPathToJarFile(project))) + UsefulTestCase.assertDoesntExist(File(JS_CONFIGURATOR.getDefaultPathToJarFile(project))) + } + + companion object { + private val BASE_PATH = "idea/testData/configuration/" + private val TEMP_DIR_MACRO_KEY = "TEMP_TEST_DIR" + protected val JAVA_CONFIGURATOR: KotlinJavaModuleConfigurator by lazy { + object : KotlinJavaModuleConfigurator() { + override fun getDefaultPathToJarFile(project: Project) = getPathRelativeToTemp("default_jvm_lib") + } + } + + protected val JS_CONFIGURATOR: KotlinJsModuleConfigurator by lazy { + object : KotlinJsModuleConfigurator() { + override fun getDefaultPathToJarFile(project: Project) = getPathRelativeToTemp("default_js_lib") + } + } + + private fun configure( + modules: List, + runtimeState: FileState, + configurator: KotlinWithLibraryConfigurator, + jarFromDist: String, + jarFromTemp: String + ) { + val project = modules.first().project + val collector = createConfigureKotlinNotificationCollector(project) + + val pathToJar = getPathToJar(runtimeState, jarFromDist, jarFromTemp) + for (module in modules) { + configurator.configureModule(module, pathToJar, pathToJar, collector, runtimeState) + } + collector.showNotification() + } + + private fun getPathToJar(runtimeState: FileState, jarFromDist: String, jarFromTemp: String) = when (runtimeState) { + KotlinWithLibraryConfigurator.FileState.EXISTS -> jarFromDist + KotlinWithLibraryConfigurator.FileState.COPY -> jarFromTemp + KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY -> jarFromDist + } + + + private val pathToExistentRuntimeJar: String + get() = PathUtil.kotlinPathsForDistDirectory.stdlibPath.parent + + private val pathToExistentJsJar: String + get() = PathUtil.kotlinPathsForDistDirectory.jsStdLibJarPath.parent + + protected fun assertNotConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) { + TestCase.assertFalse( + String.format("Module %s should not be configured as %s Module", module.name, configurator.presentableText), + configurator.isConfigured(module)) + } + + protected fun assertConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) { + TestCase.assertTrue(String.format("Module %s should be configured with configurator '%s'", module.name, + configurator.presentableText), + configurator.isConfigured(module)) + } + + protected fun assertProperlyConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) { + assertConfigured(module, configurator) + assertNotConfigured(module, getOppositeConfigurator(configurator)) + } + + private fun getOppositeConfigurator(configurator: KotlinWithLibraryConfigurator): KotlinWithLibraryConfigurator { + if (configurator === JAVA_CONFIGURATOR) return JS_CONFIGURATOR + if (configurator === JS_CONFIGURATOR) return JAVA_CONFIGURATOR + + throw IllegalArgumentException("Only JS_CONFIGURATOR and JAVA_CONFIGURATOR are supported") + } + + private fun getPathRelativeToTemp(relativePath: String): String { + val tempPath = PathMacros.getInstance().getValue(TEMP_DIR_MACRO_KEY) + return tempPath + '/' + relativePath + } + } + + protected fun configure(module: Module, jarState: FileState, configurator: KotlinProjectConfigurator) { + if (configurator is KotlinJavaModuleConfigurator) { + configure( + listOf(module), jarState, + configurator as KotlinWithLibraryConfigurator, + pathToExistentRuntimeJar, pathToNonexistentRuntimeJar + ) + } + if (configurator is KotlinJsModuleConfigurator) { + configure( + listOf(module), jarState, + configurator as KotlinWithLibraryConfigurator, + pathToExistentJsJar, pathToNonexistentJsJar + ) + } + } + + private val pathToNonexistentRuntimeJar: String + get() { + val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/kotlin-runtime.jar" + myFilesToDelete.add(File(pathToTempKotlinRuntimeJar)) + return pathToTempKotlinRuntimeJar + } + + private val pathToNonexistentJsJar: String + get() { + val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.JS_LIB_JAR_NAME + myFilesToDelete.add(File(pathToTempKotlinRuntimeJar)) + return pathToTempKotlinRuntimeJar + } + + override fun getTestProjectJdk(): Sdk { + val projectRootManager = ProjectRootManager.getInstance(project) + return projectRootManager.projectSdk ?: throw IllegalStateException("SDK ${projectRootManager.projectSdkName} was not found") + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java.182 b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java.182 new file mode 100644 index 00000000000..2a24bf977da --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java.182 @@ -0,0 +1,357 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.configuration; + +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.LibraryOrderEntry; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.RootPolicy; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiJavaModule; +import com.intellij.psi.PsiRequiresStatement; +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments; +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments; +import org.jetbrains.kotlin.config.*; +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder; +import org.jetbrains.kotlin.idea.facet.FacetUtilsKt; +import org.jetbrains.kotlin.idea.facet.KotlinFacet; +import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil; +import org.jetbrains.kotlin.idea.project.PlatformKt; +import org.jetbrains.kotlin.idea.util.Java9StructureUtilKt; +import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtilKt; +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleKt; +import org.jetbrains.kotlin.utils.PathUtil; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.stream.StreamSupport; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +public class ConfigureKotlinTest extends AbstractConfigureKotlinTest { + public void testNewLibrary_copyJar() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.COPY); + + ModuleRootManager.getInstance(getModule()).orderEntries().forEachLibrary(library -> { + assertSameElements( + Arrays.stream(library.getRootProvider().getFiles(OrderRootType.CLASSES)).map(VirtualFile::getName).toArray(), + PathUtil.KOTLIN_JAVA_STDLIB_JAR, PathUtil.KOTLIN_JAVA_REFLECT_JAR, PathUtil.KOTLIN_TEST_JAR); + + assertSameElements( + Arrays.stream(library.getRootProvider().getFiles(OrderRootType.SOURCES)).map(VirtualFile::getName).toArray(), + PathUtil.KOTLIN_JAVA_STDLIB_SRC_JAR, PathUtil.KOTLIN_REFLECT_SRC_JAR, PathUtil.KOTLIN_TEST_SRC_JAR); + + return true; + }); + } + + public void testNewLibrary_doNotCopyJar() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY); + } + + public void testLibraryWithoutPaths_jarExists() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.EXISTS); + } + + public void testNewLibrary_jarExists() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.EXISTS); + } + + public void testLibraryWithoutPaths_copyJar() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.COPY); + } + + public void testLibraryWithoutPaths_doNotCopyJar() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY); + } + + @SuppressWarnings("ConstantConditions") + public void testTwoModules_exists() { + Module[] modules = getModules(); + for (Module module : modules) { + if (module.getName().equals("module1")) { + configure(module, KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY, Companion.getJAVA_CONFIGURATOR()); + Companion.assertConfigured(module, Companion.getJAVA_CONFIGURATOR()); + } + else if (module.getName().equals("module2")) { + Companion.assertNotConfigured(module, Companion.getJAVA_CONFIGURATOR()); + configure(module, KotlinWithLibraryConfigurator.FileState.EXISTS, Companion.getJAVA_CONFIGURATOR()); + Companion.assertConfigured(module, Companion.getJAVA_CONFIGURATOR()); + } + } + } + + public void testLibraryNonDefault_libExistInDefault() throws IOException { + Module module = getModule(); + + // Move fake runtime jar to default library path to pretend library is already configured + FileUtil.copy( + new File(getProject().getBasePath() + "/lib/kotlin-runtime.jar"), + new File(Companion.getJAVA_CONFIGURATOR().getDefaultPathToJarFile(getProject()) + "/kotlin-runtime.jar")); + + Companion.assertNotConfigured(module, Companion.getJAVA_CONFIGURATOR()); + Companion.getJAVA_CONFIGURATOR().configure(myProject, Collections.emptyList()); + Companion.assertProperlyConfigured(module, Companion.getJAVA_CONFIGURATOR()); + } + + public void testTwoModulesWithNonDefaultPath_doNotCopyInDefault() throws IOException { + doTestConfigureModulesWithNonDefaultSetup(Companion.getJAVA_CONFIGURATOR()); + assertEmpty(ConfigureKotlinInProjectUtilsKt.getCanBeConfiguredModules(myProject, Companion.getJS_CONFIGURATOR())); + } + + public void testTwoModulesWithJSNonDefaultPath_doNotCopyInDefault() throws IOException { + doTestConfigureModulesWithNonDefaultSetup(Companion.getJS_CONFIGURATOR()); + assertEmpty(ConfigureKotlinInProjectUtilsKt.getCanBeConfiguredModules(myProject, Companion.getJAVA_CONFIGURATOR())); + } + + public void testNewLibrary_jarExists_js() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.EXISTS); + } + + public void testNewLibrary_copyJar_js() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.COPY); + } + + public void testNewLibrary_doNotCopyJar_js() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY); + } + + public void testJsLibraryWithoutPaths_jarExists() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.EXISTS); + } + + public void testJsLibraryWithoutPaths_copyJar() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.COPY); + } + + public void testJsLibraryWithoutPaths_doNotCopyJar() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY); + } + + public void testJsLibraryWrongKind() { + doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.EXISTS); + assertEquals(1, ModuleRootManager.getInstance(getModule()).orderEntries().process(new LibraryCountingRootPolicy(), 0).intValue()); + } + + public void testProjectWithoutFacetWithRuntime106WithoutLanguageLevel() { + assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(myProject, null).getLanguageVersion()); + assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(getModule()).getLanguageVersion()); + } + + public void testProjectWithoutFacetWithRuntime11WithoutLanguageLevel() { + assertEquals(LanguageVersion.LATEST_STABLE, PlatformKt.getLanguageVersionSettings(myProject, null).getLanguageVersion()); + assertEquals(LanguageVersion.LATEST_STABLE, PlatformKt.getLanguageVersionSettings(getModule()).getLanguageVersion()); + } + + public void testProjectWithoutFacetWithRuntime11WithLanguageLevel10() { + assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(myProject, null).getLanguageVersion()); + assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(getModule()).getLanguageVersion()); + } + + public void testProjectWithFacetWithRuntime11WithLanguageLevel10() { + assertEquals(LanguageVersion.LATEST_STABLE, PlatformKt.getLanguageVersionSettings(myProject, null).getLanguageVersion()); + assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(getModule()).getLanguageVersion()); + } + + public void testJsLibraryVersion11() { + Library jsRuntime = KotlinRuntimeLibraryUtilKt.findAllUsedLibraries(myProject).keySet().iterator().next(); + String version = JsLibraryStdDetectionUtil.INSTANCE.getJsLibraryStdVersion(jsRuntime); + assertEquals("1.1.0", version); + } + + public void testJsLibraryVersion106() { + Library jsRuntime = KotlinRuntimeLibraryUtilKt.findAllUsedLibraries(myProject).keySet().iterator().next(); + String version = JsLibraryStdDetectionUtil.INSTANCE.getJsLibraryStdVersion(jsRuntime); + assertEquals("1.0.6", version); + } + + @SuppressWarnings("ConstantConditions") + public void testJvmProjectWithV1FacetConfig() { + KotlinFacetSettings settings = KotlinFacetSettingsProvider.Companion.getInstance(myProject).getInitializedSettings(getModule()); + K2JVMCompilerArguments arguments = (K2JVMCompilerArguments) settings.getCompilerArguments(); + assertEquals(false, settings.getUseProjectSettings()); + assertEquals("1.1", settings.getLanguageLevel().getDescription()); + assertEquals("1.0", settings.getApiLevel().getDescription()); + assertEquals(TargetPlatformKind.Jvm.Companion.get(JvmTarget.JVM_1_8), settings.getTargetPlatformKind()); + assertEquals("1.1", arguments.getLanguageVersion()); + assertEquals("1.0", arguments.getApiVersion()); + assertEquals(LanguageFeature.State.ENABLED_WITH_WARNING, CoroutineSupport.byCompilerArguments(arguments)); + assertEquals("1.7", arguments.getJvmTarget()); + assertEquals("-version -Xallow-kotlin-package -Xskip-metadata-version-check", settings.getCompilerSettings().getAdditionalArguments()); + } + + @SuppressWarnings("ConstantConditions") + public void testJsProjectWithV1FacetConfig() { + KotlinFacetSettings settings = KotlinFacetSettingsProvider.Companion.getInstance(myProject).getInitializedSettings(getModule()); + K2JSCompilerArguments arguments = (K2JSCompilerArguments) settings.getCompilerArguments(); + assertEquals(false, settings.getUseProjectSettings()); + assertEquals("1.1", settings.getLanguageLevel().getDescription()); + assertEquals("1.0", settings.getApiLevel().getDescription()); + assertEquals(TargetPlatformKind.JavaScript.INSTANCE, settings.getTargetPlatformKind()); + assertEquals("1.1", arguments.getLanguageVersion()); + assertEquals("1.0", arguments.getApiVersion()); + assertEquals(LanguageFeature.State.ENABLED_WITH_WARNING, CoroutineSupport.byCompilerArguments(arguments)); + assertEquals("amd", arguments.getModuleKind()); + assertEquals("-version -meta-info", settings.getCompilerSettings().getAdditionalArguments()); + } + + @SuppressWarnings("ConstantConditions") + public void testJvmProjectWithV2FacetConfig() { + KotlinFacetSettings settings = KotlinFacetSettingsProvider.Companion.getInstance(myProject).getInitializedSettings(getModule()); + K2JVMCompilerArguments arguments = (K2JVMCompilerArguments) settings.getCompilerArguments(); + assertEquals(false, settings.getUseProjectSettings()); + assertEquals("1.1", settings.getLanguageLevel().getDescription()); + assertEquals("1.0", settings.getApiLevel().getDescription()); + assertEquals(TargetPlatformKind.Jvm.Companion.get(JvmTarget.JVM_1_8), settings.getTargetPlatformKind()); + assertEquals("1.1", arguments.getLanguageVersion()); + assertEquals("1.0", arguments.getApiVersion()); + assertEquals(LanguageFeature.State.ENABLED, CoroutineSupport.byCompilerArguments(arguments)); + assertEquals("1.7", arguments.getJvmTarget()); + assertEquals("-version -Xallow-kotlin-package -Xskip-metadata-version-check", settings.getCompilerSettings().getAdditionalArguments()); + } + + @SuppressWarnings("ConstantConditions") + public void testJsProjectWithV2FacetConfig() { + KotlinFacetSettings settings = KotlinFacetSettingsProvider.Companion.getInstance(myProject).getInitializedSettings(getModule()); + K2JSCompilerArguments arguments = (K2JSCompilerArguments) settings.getCompilerArguments(); + assertEquals(false, settings.getUseProjectSettings()); + assertEquals("1.1", settings.getLanguageLevel().getDescription()); + assertEquals("1.0", settings.getApiLevel().getDescription()); + assertEquals(TargetPlatformKind.JavaScript.INSTANCE, settings.getTargetPlatformKind()); + assertEquals("1.1", arguments.getLanguageVersion()); + assertEquals("1.0", arguments.getApiVersion()); + assertEquals(LanguageFeature.State.ENABLED_WITH_ERROR, CoroutineSupport.byCompilerArguments(arguments)); + assertEquals("amd", arguments.getModuleKind()); + assertEquals("-version -meta-info", settings.getCompilerSettings().getAdditionalArguments()); + } + + @SuppressWarnings("ConstantConditions") + public void testJvmProjectWithV3FacetConfig() { + KotlinFacetSettings settings = KotlinFacetSettingsProvider.Companion.getInstance(myProject).getInitializedSettings(getModule()); + K2JVMCompilerArguments arguments = (K2JVMCompilerArguments) settings.getCompilerArguments(); + assertEquals(false, settings.getUseProjectSettings()); + assertEquals("1.1", settings.getLanguageLevel().getDescription()); + assertEquals("1.0", settings.getApiLevel().getDescription()); + assertEquals(TargetPlatformKind.Jvm.Companion.get(JvmTarget.JVM_1_8), settings.getTargetPlatformKind()); + assertEquals("1.1", arguments.getLanguageVersion()); + assertEquals("1.0", arguments.getApiVersion()); + assertEquals(LanguageFeature.State.ENABLED, CoroutineSupport.byCompilerArguments(arguments)); + assertEquals("1.7", arguments.getJvmTarget()); + assertEquals("-version -Xallow-kotlin-package -Xskip-metadata-version-check", settings.getCompilerSettings().getAdditionalArguments()); + } + + public void testImplementsDependency() { + ModuleManager moduleManager = ModuleManager.getInstance(myProject); + + Module module1 = moduleManager.findModuleByName("module1"); + assert module1 != null; + + Module module2 = moduleManager.findModuleByName("module2"); + assert module2 != null; + + assertEquals(KotlinFacet.Companion.get(module1).getConfiguration().getSettings().getImplementedModuleNames(), emptyList()); + assertEquals(KotlinFacet.Companion.get(module2).getConfiguration().getSettings().getImplementedModuleNames(), singletonList("module1")); + } + + public void testJava9WithModuleInfo() { + checkAddStdlibModule(); + } + + public void testJava9WithModuleInfoWithStdlibAlready() { + checkAddStdlibModule(); + } + + public void testProjectWithFreeArgs() { + assertEquals( + Collections.singletonList("true"), + KotlinCommonCompilerArgumentsHolder.Companion.getInstance(myProject).getSettings().getFreeArgs() + ); + } + + private void checkAddStdlibModule() { + doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.COPY); + + Module module = getModule(); + Sdk moduleSdk = ModuleRootManager.getInstance(getModule()).getSdk(); + assertNotNull("Module SDK is not defined", moduleSdk); + + PsiJavaModule javaModule = Java9StructureUtilKt.findFirstPsiJavaModule(module); + assertNotNull(javaModule); + + PsiRequiresStatement stdlibDirective = + Java9StructureUtilKt.findRequireDirective(javaModule, JavaModuleKt.KOTLIN_STDLIB_MODULE_NAME); + assertNotNull("Require directive for " + JavaModuleKt.KOTLIN_STDLIB_MODULE_NAME + " is expected", + stdlibDirective); + + long numberOfStdlib = StreamSupport.stream(javaModule.getRequires().spliterator(), false) + .filter((statement) -> JavaModuleKt.KOTLIN_STDLIB_MODULE_NAME.equals(statement.getModuleName())) + .count(); + + assertTrue("Only one standard library directive is expected", numberOfStdlib == 1); + } + + private void configureFacetAndCheckJvm(JvmTarget jvmTarget) { + IdeModifiableModelsProviderImpl modelsProvider = new IdeModifiableModelsProviderImpl(getProject()); + try { + KotlinFacet facet = FacetUtilsKt.getOrCreateFacet(getModule(), modelsProvider, false, false); + TargetPlatformKind.Jvm platformKind = TargetPlatformKind.Jvm.Companion.get(jvmTarget); + FacetUtilsKt.configureFacet( + facet, + "1.1", + LanguageFeature.State.ENABLED, + platformKind, + modelsProvider + ); + assertEquals(platformKind, facet.getConfiguration().getSettings().getTargetPlatformKind()); + assertEquals(jvmTarget.getDescription(), + ((K2JVMCompilerArguments) facet.getConfiguration().getSettings().getCompilerArguments()).getJvmTarget()); + } + finally { + modelsProvider.dispose(); + } + } + + @SuppressWarnings("ConstantConditions") + public void testJvm8InProjectJvm6InModule() { + configureFacetAndCheckJvm(JvmTarget.JVM_1_6); + } + + @SuppressWarnings("ConstantConditions") + public void testJvm6InProjectJvm8InModule() { + configureFacetAndCheckJvm(JvmTarget.JVM_1_8); + } + + public void testProjectWithoutFacetWithJvmTarget18() { + assertEquals(TargetPlatformKind.Jvm.Companion.get(JvmTarget.JVM_1_8), PlatformKt.getTargetPlatform(getModule())); + } + + private static class LibraryCountingRootPolicy extends RootPolicy { + @Override + public Integer visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Integer value) { + return value + 1; + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/folding/AbstractKotlinFoldingTest.java.182 b/idea/tests/org/jetbrains/kotlin/idea/folding/AbstractKotlinFoldingTest.java.182 new file mode 100644 index 00000000000..bc2aa03db2c --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/folding/AbstractKotlinFoldingTest.java.182 @@ -0,0 +1,119 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.folding; + +import com.intellij.codeInsight.folding.JavaCodeFoldingSettings; +import com.intellij.codeInsight.folding.impl.JavaCodeFoldingSettingsImpl; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCaseBase; +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor; +import org.jetbrains.kotlin.test.SettingsConfigurator; +import org.junit.Assert; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.function.Consumer; + +public abstract class AbstractKotlinFoldingTest extends KotlinLightCodeInsightFixtureTestCaseBase { + protected void doTest(@NotNull String path) { + myFixture.testFolding(path); + } + + protected void doSettingsFoldingTest(@NotNull String path) throws IOException{ + String fileText = FileUtil.loadFile(new File(path), true); + + String directText = fileText.replaceAll("~true~", "true").replaceAll("~false~", "false"); + directText += "\n\n// Generated from: " + path; + + Consumer doExpandSettingsTestFunction = this::doExpandSettingsTest; + + doTestWithSettings(directText, doExpandSettingsTestFunction); + + String invertedText = fileText + .replaceAll("~false~", "true").replaceAll("~true~", "false") + .replaceAll(SettingsConfigurator.SET_TRUE_DIRECTIVE, "~TEMP_TRUE_DIRECTIVE~") + .replaceAll(SettingsConfigurator.SET_FALSE_DIRECTIVE, SettingsConfigurator.SET_TRUE_DIRECTIVE) + .replaceAll("~TEMP_TRUE_DIRECTIVE~", SettingsConfigurator.SET_FALSE_DIRECTIVE); + invertedText += "\n\n// Generated from: " + path + " with !INVERTED! settings"; + + doTestWithSettings(invertedText, doExpandSettingsTestFunction); + } + + protected static void doTestWithSettings(@NotNull String fileText, @NotNull Consumer runnable) { + JavaCodeFoldingSettings settings = JavaCodeFoldingSettings.getInstance(); + JavaCodeFoldingSettingsImpl restoreSettings = new JavaCodeFoldingSettingsImpl(); + restoreSettings.loadState((JavaCodeFoldingSettingsImpl) settings); + + try { + SettingsConfigurator configurator = new SettingsConfigurator(fileText, settings); + configurator.configureSettings(); + + runnable.accept(fileText); + } + finally { + ((JavaCodeFoldingSettingsImpl) JavaCodeFoldingSettings.getInstance()).loadState(restoreSettings); + } + } + + private void doExpandSettingsTest(String fileText) { + try { + VirtualFile tempFile = createTempFile("kt", null, fileText, Charset.defaultCharset()); + assertFoldingRegionsForFile(tempFile.getPath()); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } + + // Rewritten version of CodeInsightTestFixtureImpl.testFoldingRegions(verificationFileName, true). + // Configure test with custom file name to force creating different editors for normal and inverted tests. + private void assertFoldingRegionsForFile(String verificationFileName) { + String START_FOLD = ""; + String END_FOLD = ""; + + String expectedContent; + File file = new File(verificationFileName); + + try { + expectedContent = FileUtil.loadFile(file); + } + catch (IOException e) { + throw new RuntimeException(e); + } + Assert.assertNotNull(expectedContent); + + expectedContent = StringUtil.replace(expectedContent, "\r", ""); + String cleanContent = expectedContent.replaceAll(START_FOLD, "").replaceAll(END_FOLD, ""); + + myFixture.configureByText(file.getName(), cleanContent); + String actual = ((CodeInsightTestFixtureImpl)myFixture).getFoldingDescription(true); + + Assert.assertEquals(expectedContent, actual); + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return KotlinLightProjectDescriptor.INSTANCE; + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveKotlinDeclarationsHandlerTest.kt.182 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveKotlinDeclarationsHandlerTest.kt.182 new file mode 100644 index 00000000000..b2aa9e52232 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveKotlinDeclarationsHandlerTest.kt.182 @@ -0,0 +1,205 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.move + +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.testFramework.PsiTestUtil +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsHandler +import org.jetbrains.kotlin.idea.refactoring.toPsiDirectory +import org.jetbrains.kotlin.idea.refactoring.toPsiFile +import org.jetbrains.kotlin.idea.test.KotlinMultiFileTestCase +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.test.extractMultipleMarkerOffsets +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance + +class MoveKotlinDeclarationsHandlerTest : KotlinMultiFileTestCase() { + override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + + override fun getTestRoot() = "/refactoring/moveHandler/declarations" + + private fun doTest(action: (rootDir: VirtualFile, handler: MoveKotlinDeclarationsHandler) -> Unit) { + val path = "$testDataPath$testRoot/${getTestName(true)}" + val rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, path, myFilesToDelete, false) + prepareProject(rootDir) + PsiDocumentManager.getInstance(myProject).commitAllDocuments() + action(rootDir, MoveKotlinDeclarationsHandler()) + } + + private fun getPsiDirectory(rootDir: VirtualFile, path: String) = rootDir.findFileByRelativePath(path)!!.toPsiDirectory(project)!! + + private fun getPsiFile(rootDir: VirtualFile, path: String) = rootDir.findFileByRelativePath(path)!!.toPsiFile(project)!! + + private fun getElementAtCaret(rootDir: VirtualFile, path: String) = getElementsAtCarets(rootDir, path).single() + + private fun getElementsAtCarets(rootDir: VirtualFile, path: String): List { + val file = getPsiFile(rootDir, path) + val document = FileDocumentManager.getInstance().getDocument(file.virtualFile)!! + return document.extractMultipleMarkerOffsets(project).map { file.findElementAt(it)!! } + } + + fun testObjectLiteral() = doTest { rootDir, handler -> + val objectDeclaration = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(!handler.canMove(arrayOf(objectDeclaration), null)) + } + + fun testLocalClass() = doTest { rootDir, handler -> + val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(!handler.canMove(arrayOf(klass), null)) + } + + fun testLocalFun() = doTest { rootDir, handler -> + val function = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(!handler.canMove(arrayOf(function), null)) + } + + fun testLocalVal() = doTest { rootDir, handler -> + val property = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(!handler.canMove(arrayOf(property), null)) + } + + fun testMemberFun() = doTest { rootDir, handler -> + val function = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(!handler.canMove(arrayOf(function), null)) + } + + fun testMemberVal() = doTest { rootDir, handler -> + val property = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(!handler.canMove(arrayOf(property), null)) + } + + fun testNestedClass() = doTest { rootDir, handler -> + val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(handler.canMove(arrayOf(klass), null)) + } + + fun testInnerClass() = doTest { rootDir, handler -> + val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(handler.canMove(arrayOf(klass), null)) + } + + fun testTopLevelClass() = doTest { rootDir, handler -> + val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(handler.canMove(arrayOf(klass), null)) + } + + fun testTopLevelFun() = doTest { rootDir, handler -> + val function = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(handler.canMove(arrayOf(function), null)) + } + + fun testTopLevelVal() = doTest { rootDir, handler -> + val property = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(handler.canMove(arrayOf(property), null)) + } + + fun testMultipleNestedClasses() = doTest { rootDir, handler -> + val classes = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType()!! } + assert(handler.canMove(classes.toTypedArray(), null)) + } + + fun testNestedAndTopLevelClass() = doTest { rootDir, handler -> + val classes = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType()!! } + assert(!handler.canMove(classes.toTypedArray(), null)) + } + + fun testMultipleTopLevelDeclarations() = doTest { rootDir, handler -> + val declarations = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType()!! } + assert(handler.canMove(declarations.toTypedArray(), null)) + } + + fun testMultipleTopLevelDeclarationsInDifferentFiles() = doTest { rootDir, handler -> + val declarations = listOf("test.kt", "test2.kt") + .flatMap { getElementsAtCarets(rootDir, it) } + .map { it.getNonStrictParentOfType()!! } + assert(handler.canMove(declarations.toTypedArray(), null)) + + val files = listOf("test.kt", "test2.kt").map { getPsiFile(rootDir, it) } + assert(handler.canMove(files.toTypedArray(), null)) + } + + fun testMultipleTopLevelDeclarationsInDifferentDirs() = doTest { rootDir, handler -> + val declarations = listOf("test1/test.kt", "test2/test2.kt") + .flatMap { getElementsAtCarets(rootDir, it) } + .map { it.getNonStrictParentOfType()!! } + assert(!handler.canMove(declarations.toTypedArray(), null)) + + val files = listOf("test1/test.kt", "test2/test2.kt").map { getPsiFile(rootDir, it) } + assert(!handler.canMove(files.toTypedArray(), null)) + } + + fun testFileAndTopLevelDeclarations() = doTest { rootDir, handler -> + val elements = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType()!! } + + getPsiFile(rootDir, "test2.kt") + assert(!handler.canMove(elements.toTypedArray(), null)) + } + + fun testCommonTargets() = doTest { rootDir, handler -> + val elementsToMove = arrayOf(getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!!) + + val targetPackage = JavaPsiFacade.getInstance(project).findPackage("pack")!! + assert(handler.canMove(elementsToMove, targetPackage)) + + val targetDirectory = getPsiDirectory(rootDir, "pack") + assert(handler.canMove(elementsToMove, targetDirectory)) + + val targetFile = getPsiFile(rootDir, "pack/test2.kt") + assert(handler.canMove(elementsToMove, targetFile)) + } + + fun testTopLevelClassToClass() = doTest { rootDir, handler -> + val elementsToMove = arrayOf(getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!!) + val targetFile = getPsiFile(rootDir, "test2.kt") as KtFile + + val topLevelTarget = targetFile.declarations.firstIsInstance() + assert(topLevelTarget.name == "B") + assert(!handler.canMove(elementsToMove, topLevelTarget)) + + val annotationTarget = targetFile.declarations.first { it.name == "Ann" } as KtClass + assert(!handler.canMove(elementsToMove, annotationTarget)) + + val nestedTarget = topLevelTarget.declarations.firstIsInstance() + assert(nestedTarget.name == "C") + assert(!handler.canMove(elementsToMove, nestedTarget)) + } + + fun testNestedClassToClass() = doTest { rootDir, handler -> + val elementsToMove = arrayOf(getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!!) + val targetFile = getPsiFile(rootDir, "test2.kt") as KtFile + + val topLevelTarget = targetFile.declarations.firstIsInstance() + assert(topLevelTarget.name == "B") + assert(handler.canMove(elementsToMove, topLevelTarget)) + + val annotationTarget = targetFile.declarations.first { it.name == "Ann" } as KtClass + assert(!handler.canMove(elementsToMove, annotationTarget)) + + val nestedTarget = topLevelTarget.declarations.firstIsInstance() + assert(nestedTarget.name == "C") + assert(handler.canMove(elementsToMove, nestedTarget)) + } + + fun testTypeAlias() = doTest { rootDir, handler -> + val typeAlias = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType()!! + assert(handler.canMove(arrayOf(typeAlias), null)) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt.182 b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt.182 new file mode 100644 index 00000000000..c12fd5ab47e --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt.182 @@ -0,0 +1,277 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.run + +import com.intellij.execution.Location +import com.intellij.execution.PsiLocation +import com.intellij.execution.actions.ConfigurationContext +import com.intellij.openapi.module.Module +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiManager +import com.intellij.refactoring.RefactoringFactory +import com.intellij.testFramework.MapDataContext +import com.intellij.testFramework.PsiTestUtil +import org.jetbrains.kotlin.idea.MainFunctionDetector +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.search.allScope +import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex +import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil +import org.jetbrains.kotlin.idea.test.KotlinCodeInsightTestCase +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.* +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.allChildren +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.junit.Assert +import java.io.File +import java.util.* + +private val RUN_PREFIX = "// RUN:" + +class RunConfigurationTest: KotlinCodeInsightTestCase() { + fun getTestProject() = myProject!! + override fun getModule() = myModule!! + + fun testMainInTest() { + val createResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createResult.module, addJdk(testRootDisposable, ::mockJdk)) + + val runConfiguration = createConfigurationFromMain("some.main") + val javaParameters = getJavaRunParameters(runConfiguration) + + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(createResult.srcOutputDir)) + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(createResult.testOutputDir)) + + fun functionVisitor(function: KtNamedFunction) { + val options = function.bodyExpression?.allChildren?.filterIsInstance()?.map { it.text.trim().replace("//", "").trim() }?.filter { it.isNotBlank() }?.toList() ?: emptyList() + if (options.isNotEmpty()) { + val assertIsMain = "yes" in options + val assertIsNotMain = "no" in options + + val bindingContext = function.analyze(BodyResolveMode.FULL) + val isMainFunction = MainFunctionDetector(bindingContext).isMain(function) + + if (assertIsMain) { + Assert.assertTrue("The function ${function.fqName?.asString()} should be main", isMainFunction) + } + if (assertIsNotMain) { + Assert.assertFalse("The function ${function.fqName?.asString()} should NOT be main", isMainFunction) + } + + if (isMainFunction) { + createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration() + + Assert.assertNotNull("Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}", + KotlinRunConfigurationProducer.getEntryPointContainer(function)) + } else { + try { + createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration() + Assert.fail("configuration for function ${function.fqName?.asString()} at least shouldn't pass checkConfiguration()") + } catch (expected: Throwable) { + } + + Assert.assertNull("Kotlin configuration producer shouldN'T produce configuration for ${function.fqName?.asString()}", + KotlinRunConfigurationProducer.getEntryPointContainer(function)) + } + } + } + + createResult.srcDir.children.filter { it.extension == "kt" }.forEach { + val psiFile = PsiManager.getInstance(createResult.module.project).findFile(it) + if (psiFile is KtFile) { + psiFile.acceptChildren(object : KtVisitorVoid() { + override fun visitNamedFunction(function: KtNamedFunction) { + functionVisitor(function) + } + }) + } + } + } + + fun testDependencyModuleClasspath() { + val dependencyModuleSrcDir = configureModule(moduleDirPath("module"), getTestProject().baseDir!!).srcOutputDir + + val moduleWithDependencyDir = runWriteAction { getTestProject().baseDir!!.createChildDirectory(this, "moduleWithDependency") } + + val moduleWithDependency = createModule("moduleWithDependency") + ModuleRootModificationUtil.setModuleSdk(moduleWithDependency, testProjectJdk) + + val moduleWithDependencySrcDir = configureModule( + moduleDirPath("moduleWithDependency"), moduleWithDependencyDir, configModule = moduleWithDependency).srcOutputDir + + ModuleRootModificationUtil.addDependency(moduleWithDependency, module) + + val kotlinRunConfiguration = createConfigurationFromMain("some.test.main") + kotlinRunConfiguration.setModule(moduleWithDependency) + + val javaParameters = getJavaRunParameters(kotlinRunConfiguration) + + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(dependencyModuleSrcDir)) + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(moduleWithDependencySrcDir)) + } + + fun testClassesAndObjects() { + doTest(ConfigLibraryUtil::configureKotlinRuntimeAndSdk) + } + + fun testInJsModule() { + doTest(ConfigLibraryUtil::configureKotlinJsRuntimeAndSdk) + } + + fun testUpdateOnClassRename() { + val createModuleResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk)) + + val runConfiguration = createConfigurationFromObject("renameTest.Foo", save = true) + + val obj = KotlinFullClassNameIndex.getInstance().get("renameTest.Foo", getTestProject(), getTestProject().allScope()).single() + val rename = RefactoringFactory.getInstance(getTestProject()).createRename(obj, "Bar") + rename.run() + + Assert.assertEquals("renameTest.Bar", runConfiguration.MAIN_CLASS_NAME) + } + + fun testUpdateOnPackageRename() { + val createModuleResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk)) + + val runConfiguration = createConfigurationFromObject("renameTest.Foo", save = true) + + val pkg = JavaPsiFacade.getInstance(getTestProject()).findPackage("renameTest")!! + val rename = RefactoringFactory.getInstance(getTestProject()).createRename(pkg, "afterRenameTest") + rename.run() + + Assert.assertEquals("afterRenameTest.Foo", runConfiguration.MAIN_CLASS_NAME) + } + + fun testWithModuleForJdk6() { + checkModuleInfoName(null, addJdk(testRootDisposable, ::mockJdk)) + } + + fun testWithModuleForJdk9() { + checkModuleInfoName("MAIN", addJdk(testRootDisposable, ::mockJdk9)) + } + + fun testWithModuleForJdk9WithoutModuleInfo() { + checkModuleInfoName(null, addJdk(testRootDisposable, ::mockJdk9)) + } + + private fun checkModuleInfoName(moduleName: String?, sdk: Sdk) { + val module = configureModule(moduleDirPath("module"), getTestProject().baseDir!!).module + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, sdk) + + val javaParameters = getJavaRunParameters(createConfigurationFromMain("some.main")) + + Assert.assertEquals(moduleName, javaParameters.moduleName) + } + + private fun doTest(configureRuntime: (Module, Sdk) -> Unit) { + val baseDir = getTestProject().baseDir!! + val createModuleResult = configureModule(moduleDirPath("module"), baseDir) + val srcDir = createModuleResult.srcDir + + configureRuntime(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk)) + + try { + val expectedClasses = ArrayList() + val actualClasses = ArrayList() + + val testFile = PsiManager.getInstance(getTestProject()).findFile(srcDir.findFileByRelativePath("test.kt")!!)!! + testFile.accept( + object : KtTreeVisitorVoid() { + override fun visitComment(comment: PsiComment) { + val declaration = comment.getStrictParentOfType()!! + val text = comment.text ?: return + if (!text.startsWith(RUN_PREFIX)) return + + val expectedClass = text.substring(RUN_PREFIX.length).trim() + if (expectedClass.isNotEmpty()) expectedClasses.add(expectedClass) + + val dataContext = MapDataContext() + dataContext.put(Location.DATA_KEY, PsiLocation(getTestProject(), declaration)) + val context = ConfigurationContext.getFromContext(dataContext) + val actualClass = (context.configuration?.configuration as? KotlinRunConfiguration)?.runClass + if (actualClass != null) { + actualClasses.add(actualClass) + } + } + } + ) + Assert.assertEquals(expectedClasses, actualClasses) + } + finally { + ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(createModuleResult.module, mockJdk()) + } + } + + private fun createConfigurationFromMain(mainFqn: String): KotlinRunConfiguration { + val mainFunction = KotlinTopLevelFunctionFqnNameIndex.getInstance().get(mainFqn, getTestProject(), getTestProject().allScope()).first() + + return createConfigurationFromElement(mainFunction) as KotlinRunConfiguration + } + + private fun createConfigurationFromObject(objectFqn: String, save: Boolean = false): KotlinRunConfiguration { + val obj = KotlinFullClassNameIndex.getInstance().get(objectFqn, getTestProject(), getTestProject().allScope()).single() + val mainFunction = obj.declarations.single { it is KtFunction && it.getName() == "main" } + return createConfigurationFromElement(mainFunction, save) as KotlinRunConfiguration + } + + private fun configureModule(moduleDir: String, outputParentDir: VirtualFile, configModule: Module = module): CreateModuleResult { + val srcPath = moduleDir + "/src" + val srcDir = PsiTestUtil.createTestProjectStructure(project, configModule, srcPath, myFilesToDelete, true) + + val testPath = moduleDir + "/test" + if (File(testPath).exists()) { + val testDir = PsiTestUtil.createTestProjectStructure(project, configModule, testPath, myFilesToDelete, false) + PsiTestUtil.addSourceRoot(module, testDir, true) + } + + val (srcOutDir, testOutDir) = runWriteAction { + val outDir = outputParentDir.createChildDirectory(this, "out") + val srcOutDir = outDir.createChildDirectory(this, "production") + val testOutDir = outDir.createChildDirectory(this, "test") + + PsiTestUtil.setCompilerOutputPath(configModule, srcOutDir.url, false) + PsiTestUtil.setCompilerOutputPath(configModule, testOutDir.url, true) + + Pair(srcOutDir, testOutDir) + } + + PsiDocumentManager.getInstance(getTestProject()).commitAllDocuments() + + return CreateModuleResult(configModule, srcDir, srcOutDir, testOutDir) + } + + private fun moduleDirPath(moduleName: String) = "${testDataPath}${getTestName(false)}/$moduleName" + + override fun getTestDataPath() = getTestDataPathBase() + "/run/" + override fun getTestProjectJdk() = mockJdk() + + private class CreateModuleResult( + val module: Module, + val srcDir: VirtualFile, + val srcOutputDir: VirtualFile, + val testOutputDir: VirtualFile + ) +}