package com.intellij.configurationScript import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.configurationScript.providers.PluginsConfiguration import com.intellij.configurationScript.schemaGenerators.ComponentStateJsonSchemaGenerator import com.intellij.configurationScript.schemaGenerators.RunConfigurationJsonSchemaGenerator import com.intellij.json.JsonFileType import com.intellij.openapi.components.BaseState import com.intellij.testFramework.EditorTestUtil import com.intellij.testFramework.LightVirtualFile import com.intellij.testFramework.PlatformTestUtil.getCommunityPath import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.testFramework.fixtures.BasePlatformTestCase import com.intellij.util.io.sanitizeFileName import com.intellij.util.xmlb.annotations.XCollection import com.jetbrains.jsonSchema.impl.JsonSchemaCompletionContributor import com.jetbrains.jsonSchema.impl.JsonSchemaReader import org.intellij.lang.annotations.Language import org.jetbrains.io.JsonObjectBuilder import java.nio.file.Paths // this test requires YamlJsonSchemaCompletionContributor, that's why intellij.yaml is added as test dependency internal class ConfigurationSchemaTest : BasePlatformTestCase() { companion object { private val testSnapshotDir = Paths.get(getCommunityPath(), "plugins/configuration-script", "testSnapshots") } fun `test map and description`() { val variants = testRunConfigurations(""" runConfigurations: java: """.trimIndent()) checkDescription(variants, "env", "Environment variables") checkDescription(variants, "isAllowRunningInParallel", "Allow parallel run") checkDescription(variants, "isShowConsoleOnStdErr", "Show console when a message is printed to standard error stream") checkDescription(variants, "isShowConsoleOnStdOut", "Show console when a message is printed to standard output stream") } fun `test array or object`() { val variants = testRunConfigurations(""" runConfigurations: java: """.trimIndent()) val texts = variants.map { val presentation = LookupElementPresentation() it.renderElement(presentation) presentation.itemText } assertThat(texts).contains("{...}", "[...]") } fun `test no isAllowRunningInParallel if singleton policy not configurable`() { val variants = testRunConfigurations(""" runConfigurations: compound: """.trimIndent()) assertThat(variantsToText(variants)).isEqualTo(""" configurations (array) """.trimIndent()) } private class Foo : BaseState() { @Suppress("unused") var a by string() } fun `test component state`() { testFooComponentState("foo".trimIndent(), """ foo: """) } fun `test component state - nested key`() { testFooComponentState("foo.bar", """ foo: bar: """) } fun `test list of objects`() { testComponentState("sdks", """ sdks: """, beanClass = JdkAutoHints::class.java, expectedVariants = """sdks (array)""") } fun `test list of strings`() { testComponentState("plugins", """ plugins: """, beanClass = PluginsConfiguration::class.java, expectedVariants = """repositories (array)""") } private fun testFooComponentState(path: String, fileContent: String) { testComponentState(path, fileContent, Foo::class.java, expectedVariants = "a (string)") } private fun testComponentState(path: String, @Language("YAML") fileContent: String, beanClass: Class, expectedVariants: String) { val variants = test(fileContent.trimIndent(), listOf(object : SchemaGenerator { private val componentStateJsonSchemaGenerator = ComponentStateJsonSchemaGenerator() override fun generate(rootBuilder: JsonObjectBuilder) { componentStateJsonSchemaGenerator.doGenerate(rootBuilder, mapOf(path to beanClass)) } override val definitionNodeKey: CharSequence? get() = componentStateJsonSchemaGenerator.definitionNodeKey override fun generateDefinitions() = componentStateJsonSchemaGenerator.generateDefinitions() }), schemaValidator = { val snapshotFile = testSnapshotDir.resolve(sanitizeFileName(name) + ".json") assertThat(it.toString()).toMatchSnapshot(snapshotFile) }) assertThat(variantsToText(variants)).isEqualTo(expectedVariants) } private fun checkDescription(variants: List, name: String, expectedDescription: String) { val variant = variants.first { it.lookupString == name } val presentation = LookupElementPresentation() variant.renderElement(presentation) assertThat(presentation.typeText).isEqualTo(expectedDescription) } private fun testRunConfigurations(@Language("YAML") text: String, schemaValidator: ((CharSequence) -> Unit)? = null): List { return test(text, listOf(RunConfigurationJsonSchemaGenerator()), schemaValidator) } private fun test(@Language("YAML") text: String, generators: List, schemaValidator: ((CharSequence) -> Unit)? = null): List { val position = EditorTestUtil.getCaretPosition(text) assertThat(position).isGreaterThan(0) @Suppress("SpellCheckingInspection") val file = myFixture.configureByText("intellij.yaml", text.replace("", "IntelliJIDEARulezzz")) val element = file.findElementAt(myFixture.editor.caretModel.offset) assertThat(element).isNotNull val schemaContent = doGenerateConfigurationSchema(generators) schemaValidator?.invoke(schemaContent) val schemaFile = LightVirtualFile("scheme.json", JsonFileType.INSTANCE, schemaContent, Charsets.UTF_8, 0) val schemaObject = JsonSchemaReader.readFromFile(project, schemaFile) assertThat(schemaObject).isNotNull return JsonSchemaCompletionContributor.getCompletionVariants(schemaObject, element!!, element) } } private fun variantsToText(variants: List): String { return variants .asSequence() .sortedBy { it.lookupString } .joinToString("\n") { "${it.lookupString} (${getTypeTest(it)})" } } private fun getTypeTest(variant: LookupElement): String { val presentation = LookupElementPresentation() variant.renderElement(presentation) return presentation.typeText!! } @Suppress("unused") internal class JdkAutoHint: BaseState() { var sdkName by string() var sdkPath by string() } internal class JdkAutoHints : BaseState() { @get:XCollection val sdks by list() }