Refactoring: extract long function out of local context

This commit is contained in:
Nikolay Krasko
2020-02-14 16:02:23 +03:00
committed by Nikolay Krasko
parent 3a5f42cc5e
commit c0dac9bf32
@@ -9,6 +9,7 @@ import com.intellij.execution.Location
import com.intellij.execution.PsiLocation import com.intellij.execution.PsiLocation
import com.intellij.execution.actions.ConfigurationContext import com.intellij.execution.actions.ConfigurationContext
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.psi.JavaPsiFacade import com.intellij.psi.JavaPsiFacade
@@ -41,78 +42,28 @@ private const val RUN_PREFIX = "// RUN:"
@RunWith(JUnit3WithIdeaConfigurationRunner::class) @RunWith(JUnit3WithIdeaConfigurationRunner::class)
class RunConfigurationTest : AbstractRunConfigurationTest() { class RunConfigurationTest : AbstractRunConfigurationTest() {
fun testMainInTest() { fun testMainInTest() {
val createResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) val createModuleResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!)
configureLanguageAndApiVersion( configureLanguageAndApiVersion(
createResult.module.project, createResult.module, LanguageVersionSettingsImpl.DEFAULT.languageVersion.versionString createModuleResult.module.project, createModuleResult.module, LanguageVersionSettingsImpl.DEFAULT.languageVersion.versionString,
) )
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createResult.module, addJdk(testRootDisposable, ::mockJdk)) ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk))
val runConfiguration = createConfigurationFromMain("some.main") val runConfiguration = createConfigurationFromMain(getTestProject(), "some.main")
val javaParameters = getJavaRunParameters(runConfiguration) val javaParameters = getJavaRunParameters(runConfiguration)
assertTrue(javaParameters.classPath.rootDirs.contains(createResult.srcOutputDir)) assertTrue(javaParameters.classPath.rootDirs.contains(createModuleResult.srcOutputDir))
assertTrue(javaParameters.classPath.rootDirs.contains(createResult.testOutputDir)) assertTrue(javaParameters.classPath.rootDirs.contains(createModuleResult.testOutputDir))
fun functionVisitor(function: KtNamedFunction) { createModuleResult.srcDir?.children?.filter { it.extension == "kt" }?.forEach {
val file = function.containingKtFile val psiFile = PsiManager.getInstance(createModuleResult.module.project).findFile(it)
val options =
function.bodyExpression?.allChildren?.filterIsInstance<PsiComment>()?.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 languageVersionSettings = languageVersionSettingsFromText(listOf(file.text))
createResult.module.setLanguageVersionSettings(languageVersionSettings)
val isMainFunction =
MainFunctionDetector(languageVersionSettings) { it.resolveToDescriptorIfAny() }.isMain(function)
if (assertIsMain) {
assertTrue("$file: The function ${function.fqName?.asString()} should be main", isMainFunction)
}
if (assertIsNotMain) {
assertFalse("$file: The function ${function.fqName?.asString()} should NOT be main", isMainFunction)
}
if (isMainFunction) {
createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration()
assertNotNull(
"$file: Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}",
KotlinRunConfigurationProducer.getEntryPointContainer(function)
)
} else {
try {
createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration()
fail(
"$file: configuration for function ${function.fqName?.asString()} at least shouldn't pass checkConfiguration()"
)
} catch (expected: Throwable) {
}
if (function.containingFile.text.startsWith("// entryPointExists")) {
assertNotNull(
"$file: Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}",
KotlinRunConfigurationProducer.getEntryPointContainer(function)
)
} else {
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) { if (psiFile is KtFile) {
psiFile.acceptChildren(object : KtVisitorVoid() { psiFile.acceptChildren(
override fun visitNamedFunction(function: KtNamedFunction) { object : KtVisitorVoid() {
functionVisitor(function) override fun visitNamedFunction(function: KtNamedFunction) {
} functionVisitor(createModuleResult.module, function)
}) }
},
)
} }
} }
} }
@@ -126,12 +77,12 @@ class RunConfigurationTest : AbstractRunConfigurationTest() {
ModuleRootModificationUtil.setModuleSdk(moduleWithDependency, testProjectJdk) ModuleRootModificationUtil.setModuleSdk(moduleWithDependency, testProjectJdk)
val moduleWithDependencySrcDir = configureModule( val moduleWithDependencySrcDir = configureModule(
moduleDirPath("moduleWithDependency"), moduleWithDependencyDir, configModule = moduleWithDependency moduleDirPath("moduleWithDependency"), moduleWithDependencyDir, configModule = moduleWithDependency,
).srcOutputDir ).srcOutputDir
ModuleRootModificationUtil.addDependency(moduleWithDependency, module) ModuleRootModificationUtil.addDependency(moduleWithDependency, module)
val kotlinRunConfiguration = createConfigurationFromMain("some.test.main") val kotlinRunConfiguration = createConfigurationFromMain(getTestProject(), "some.test.main")
kotlinRunConfiguration.setModule(moduleWithDependency) kotlinRunConfiguration.setModule(moduleWithDependency)
val javaParameters = getJavaRunParameters(kotlinRunConfiguration) val javaParameters = getJavaRunParameters(kotlinRunConfiguration)
@@ -146,7 +97,7 @@ class RunConfigurationTest : AbstractRunConfigurationTest() {
ModuleRootModificationUtil.addDependency(myModule, createLibraryWithLongPaths(project)) ModuleRootModificationUtil.addDependency(myModule, createLibraryWithLongPaths(project))
val kotlinRunConfiguration = createConfigurationFromMain("some.test.main") val kotlinRunConfiguration = createConfigurationFromMain(getTestProject(),"some.test.main")
kotlinRunConfiguration.setModule(myModule) kotlinRunConfiguration.setModule(myModule)
val javaParameters = getJavaRunParameters(kotlinRunConfiguration) val javaParameters = getJavaRunParameters(kotlinRunConfiguration)
@@ -206,7 +157,7 @@ class RunConfigurationTest : AbstractRunConfigurationTest() {
val module = configureModule(moduleDirPath("module"), getTestProject().baseDir!!).module val module = configureModule(moduleDirPath("module"), getTestProject().baseDir!!).module
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, sdk) ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, sdk)
val javaParameters = getJavaRunParameters(createConfigurationFromMain("some.main")) val javaParameters = getJavaRunParameters(createConfigurationFromMain(getTestProject(),"some.main"))
assertEquals(moduleName, javaParameters.moduleName) assertEquals(moduleName, javaParameters.moduleName)
} }
@@ -241,7 +192,7 @@ class RunConfigurationTest : AbstractRunConfigurationTest() {
actualClasses.add(actualClass) actualClasses.add(actualClass)
} }
} }
} },
) )
assertEquals(expectedClasses, actualClasses) assertEquals(expectedClasses, actualClasses)
} finally { } finally {
@@ -249,18 +200,72 @@ class RunConfigurationTest : AbstractRunConfigurationTest() {
} }
} }
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 { private fun createConfigurationFromObject(objectFqn: String, save: Boolean = false): KotlinRunConfiguration {
val obj = KotlinFullClassNameIndex.getInstance().get(objectFqn, getTestProject(), getTestProject().allScope()).single() val obj = KotlinFullClassNameIndex.getInstance().get(objectFqn, getTestProject(), getTestProject().allScope()).single()
val mainFunction = obj.declarations.single { it is KtFunction && it.getName() == "main" } val mainFunction = obj.declarations.single { it is KtFunction && it.getName() == "main" }
return createConfigurationFromElement(mainFunction, save) as KotlinRunConfiguration return createConfigurationFromElement(mainFunction, save) as KotlinRunConfiguration
} }
companion object {
private fun functionVisitor(module: Module, function: KtNamedFunction) {
val project = function.project
val file = function.containingKtFile
val options = function.bodyExpression?.allChildren?.filterIsInstance<PsiComment>()
?.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 languageVersionSettings = languageVersionSettingsFromText(listOf(file.text))
module.setLanguageVersionSettings(languageVersionSettings)
val isMainFunction =
MainFunctionDetector(languageVersionSettings) { it.resolveToDescriptorIfAny() }.isMain(function)
if (assertIsMain) {
assertTrue("$file: The function ${function.fqName?.asString()} should be main", isMainFunction)
}
if (assertIsNotMain) {
assertFalse("$file: The function ${function.fqName?.asString()} should NOT be main", isMainFunction)
}
if (isMainFunction) {
createConfigurationFromMain(project, function.fqName?.asString()!!).checkConfiguration()
assertNotNull(
"$file: Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}",
KotlinRunConfigurationProducer.getEntryPointContainer(function),
)
} else {
try {
createConfigurationFromMain(project, function.fqName?.asString()!!).checkConfiguration()
fail(
"$file: configuration for function ${function.fqName?.asString()} at least shouldn't pass checkConfiguration()",
)
} catch (expected: Throwable) {
}
if (function.containingFile.text.startsWith("// entryPointExists")) {
assertNotNull(
"$file: Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}",
KotlinRunConfigurationProducer.getEntryPointContainer(function),
)
} else {
assertNull(
"Kotlin configuration producer shouldn't produce configuration for ${function.fqName?.asString()}",
KotlinRunConfigurationProducer.getEntryPointContainer(function),
)
}
}
}
}
private fun createConfigurationFromMain(project: Project, mainFqn: String): KotlinRunConfiguration {
val mainFunction = KotlinTopLevelFunctionFqnNameIndex.getInstance().get(mainFqn, project, project.allScope()).first()
return createConfigurationFromElement(mainFunction) as KotlinRunConfiguration
}
}
override fun getTestDataPath() = getTestDataPathBase() + "/run/" override fun getTestDataPath() = getTestDataPathBase() + "/run/"
} }