Add test for consistency check between the Kotlin specification and spec tests
This commit is contained in:
@@ -9,6 +9,7 @@ dependencies {
|
||||
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
testRuntimeOnly(intellijPluginDep("java"))
|
||||
}
|
||||
compile("org.jsoup:jsoup:1.10.3")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -45,3 +46,11 @@ val remoteRunTests by task<Test> {
|
||||
includeTests.forEach { includeTestsMatching(packagePrefix + it) }
|
||||
}
|
||||
}
|
||||
|
||||
val specConsistencyTests by task<Test> {
|
||||
workingDir = rootDir
|
||||
|
||||
filter {
|
||||
includeTestsMatching("org.jetbrains.kotlin.spec.consistency.SpecTestsConsistencyTest")
|
||||
}
|
||||
}
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.spec.consistency
|
||||
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.spec.utils.GeneralConfiguration
|
||||
import org.jetbrains.kotlin.spec.utils.SpecTestLinkedType
|
||||
import org.jetbrains.kotlin.spec.utils.TestArea
|
||||
import org.jetbrains.kotlin.spec.utils.parsers.CommonParser.parseLinkedSpecTest
|
||||
import org.jetbrains.kotlin.spec.utils.spec.SpecSentencesStorage
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import kotlin.io.walkTopDown
|
||||
|
||||
@TestDataPath("\$PROJECT_ROOT/compiler/tests-spec/testData/")
|
||||
@RunWith(com.intellij.testFramework.Parameterized::class)
|
||||
class SpecTestsConsistencyTest : TestCase() {
|
||||
@org.junit.runners.Parameterized.Parameter
|
||||
lateinit var testFilePath: String
|
||||
|
||||
companion object {
|
||||
private val specSentencesStorage = SpecSentencesStorage()
|
||||
|
||||
private const val CURRENT_VERSION = "0.1-99"
|
||||
|
||||
@org.junit.runners.Parameterized.Parameters(name = "{0}")
|
||||
@JvmStatic
|
||||
fun getTestFiles() = emptyList<Array<Any>>()
|
||||
|
||||
@com.intellij.testFramework.Parameterized.Parameters(name = "{0}")
|
||||
@JvmStatic
|
||||
fun getTestFiles(klass: Class<*>): List<Array<String>> {
|
||||
val testFiles = mutableListOf<Array<String>>()
|
||||
|
||||
TestArea.values().forEach { testArea ->
|
||||
val testDataPath =
|
||||
"${GeneralConfiguration.TESTDATA_PATH}/${testArea.testDataPath}/${SpecTestLinkedType.LINKED.testDataPath}"
|
||||
|
||||
testFiles += File(testDataPath).let { testsDir ->
|
||||
testsDir.walkTopDown().filter { it.extension == "kt" }.map {
|
||||
arrayOf(it.relativeTo(File(GeneralConfiguration.TESTDATA_PATH)).path.replace("/", "$"))
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
|
||||
return testFiles
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doTest() {
|
||||
val file = File("${GeneralConfiguration.TESTDATA_PATH}/${testFilePath.replace("$", "/")}")
|
||||
val specSentences = specSentencesStorage.getLatest() ?: return
|
||||
val test = parseLinkedSpecTest(file.canonicalPath, mapOf("main" to file.readText()))
|
||||
val sectionsPath = setOf(*test.place.sections.toTypedArray(), test.place.paragraphNumber).joinToString()
|
||||
val sentenceNumber = test.place.sentenceNumber
|
||||
val paragraphSentences = specSentences[sectionsPath]
|
||||
|
||||
if (paragraphSentences != null && paragraphSentences.size >= sentenceNumber) {
|
||||
val specSentencesForCurrentTest =
|
||||
specSentencesStorage[test.specVersion] ?: throw Exception("spec ${test.specVersion} not found")
|
||||
val paragraphForTestSentences =
|
||||
specSentencesForCurrentTest[sectionsPath] ?: throw Exception("$sectionsPath not found")
|
||||
if (paragraphForTestSentences.size < sentenceNumber) {
|
||||
throw Exception("$sentenceNumber not found")
|
||||
}
|
||||
val expectedSentence = paragraphForTestSentences[sentenceNumber - 1]
|
||||
val actualSentence = paragraphSentences[sentenceNumber - 1]
|
||||
val locationSentenceText = "$sectionsPath paragraph, $sentenceNumber sentence"
|
||||
|
||||
println("Comparing versions: ${test.specVersion} (for expected) and ${specSentencesStorage.latestSpecVersion} (for actual)")
|
||||
println("Expected location: $locationSentenceText")
|
||||
|
||||
assertEquals(expectedSentence, actualSentence)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.spec.utils.spec
|
||||
|
||||
import groovy.util.Node
|
||||
import groovy.util.XmlParser
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
import java.io.BufferedInputStream
|
||||
import java.net.URL
|
||||
import java.nio.charset.Charset
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object HtmlSpecLoader {
|
||||
private const val SPEC_DOCS_TC_CONFIGURATION_ID = "Kotlin_Spec_DocsMaster"
|
||||
private const val TC_URL = "https://teamcity.jetbrains.com"
|
||||
private const val TC_PATH_PREFIX = "guestAuth/app/rest/builds"
|
||||
private const val HTML_SPEC_PATH = "/html/kotlin-spec.html"
|
||||
private const val STABLE_BRANCH = "master"
|
||||
|
||||
private fun loadRawHtmlSpec(specVersion: String, buildNumber: String): String {
|
||||
val htmlSpecLink =
|
||||
"$TC_URL/$TC_PATH_PREFIX/buildType:(id:$SPEC_DOCS_TC_CONFIGURATION_ID),number:$buildNumber,branch:default:any/artifacts/content/kotlin-spec-$specVersion-$buildNumber.zip%21$HTML_SPEC_PATH"
|
||||
|
||||
return BufferedInputStream(URL(htmlSpecLink).openStream()).readBytes().toString(Charset.forName("UTF-8"))
|
||||
}
|
||||
|
||||
private fun getLastSpecVersion(): Pair<String, String> {
|
||||
val xmlParser = XmlParser()
|
||||
val buildInfo =
|
||||
xmlParser.parse("$TC_URL/$TC_PATH_PREFIX/buildType:(id:$SPEC_DOCS_TC_CONFIGURATION_ID),count:1,status:SUCCESS?branch=$STABLE_BRANCH")
|
||||
val artifactsLink = (buildInfo.children().find { (it as Node).name() == "artifacts" } as Node).attribute("href").toString()
|
||||
val artifacts = xmlParser.parse(TC_URL + artifactsLink)
|
||||
val artifactName = (artifacts.children().single() as Node).attribute("name").toString()
|
||||
val artifactNameMatches =
|
||||
Pattern.compile("""kotlin-spec-(?<specVersion>\d+\.\d+)-(?<buildNumber>[1-9]\d*)\.zip""").matcher(artifactName).apply { find() }
|
||||
|
||||
return Pair(artifactNameMatches.group("specVersion"), artifactNameMatches.group("buildNumber"))
|
||||
}
|
||||
|
||||
private fun parseHtmlSpec(htmlSpecContent: String) = Jsoup.parse(htmlSpecContent).body()
|
||||
|
||||
fun loadSpec(version: String): Element? {
|
||||
val specVersion = version.substringBefore("-")
|
||||
val buildNumber = version.substringAfter("-")
|
||||
|
||||
return parseHtmlSpec(loadRawHtmlSpec(specVersion, buildNumber))
|
||||
}
|
||||
|
||||
fun loadLatestSpec(): Pair<String, Element?> {
|
||||
val (specVersion, buildNumber) = getLastSpecVersion()
|
||||
|
||||
return Pair("$specVersion-$buildNumber", parseHtmlSpec(loadRawHtmlSpec(specVersion, buildNumber)))
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.spec.utils.spec
|
||||
|
||||
import org.jsoup.nodes.Element
|
||||
import java.util.*
|
||||
|
||||
typealias SentencesByLocation = MutableMap<String, MutableList<String>>
|
||||
|
||||
object HtmlSpecSentencesMapBuilder {
|
||||
enum class SectionTag(val level: Int) { h1(1), h2(2), h3(3), h4(4), h5(5) }
|
||||
|
||||
private const val PARAGRAPH_SELECTORS = ".paragraph, ul, ol"
|
||||
private const val SECTION_SELECTORS = "h2, h3, h4, h5"
|
||||
|
||||
fun build(spec: Element): SentencesByLocation {
|
||||
var paragraphCounter = 0
|
||||
val currentSectionsPath = Stack<Pair<SectionTag, String>>()
|
||||
val sentencesByLocation: SentencesByLocation = mutableMapOf()
|
||||
|
||||
spec.select("$SECTION_SELECTORS, $PARAGRAPH_SELECTORS, .sentence").forEach { element ->
|
||||
when {
|
||||
element.`is`(SECTION_SELECTORS) -> {
|
||||
val sectionTag = SectionTag.valueOf(element.tagName().toLowerCase())
|
||||
while (!currentSectionsPath.empty() && currentSectionsPath.peek().first.level >= sectionTag.level) {
|
||||
currentSectionsPath.pop()
|
||||
}
|
||||
|
||||
currentSectionsPath.push(Pair(sectionTag, element.attr("id")))
|
||||
paragraphCounter = 0
|
||||
}
|
||||
element.`is`(PARAGRAPH_SELECTORS) -> paragraphCounter++
|
||||
else -> {
|
||||
if (!element.parents().`is`(PARAGRAPH_SELECTORS)) return@forEach
|
||||
val sentenceLocation =
|
||||
currentSectionsPath.map { it.second }.toMutableSet().apply { add(paragraphCounter.toString()) }.joinToString()
|
||||
sentencesByLocation.putIfAbsent(sentenceLocation, mutableListOf())
|
||||
sentencesByLocation[sentenceLocation]!!.add(element.text())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sentencesByLocation
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.spec.utils.spec
|
||||
|
||||
class SpecSentencesStorage {
|
||||
lateinit var latestSpecVersion: String
|
||||
private val specSentences = mutableMapOf<String, SentencesByLocation>()
|
||||
|
||||
operator fun get(version: String): SentencesByLocation? {
|
||||
return specSentences.getOrPut(version) {
|
||||
val htmlSpec = HtmlSpecLoader.loadSpec(version) ?: return null
|
||||
HtmlSpecSentencesMapBuilder.build(htmlSpec)
|
||||
}
|
||||
}
|
||||
|
||||
fun getLatest(): SentencesByLocation? {
|
||||
return specSentences.getOrPut("latest") {
|
||||
val (version, htmlSpec) = HtmlSpecLoader.loadLatestSpec()
|
||||
if (htmlSpec == null) return null
|
||||
latestSpecVersion = version
|
||||
HtmlSpecSentencesMapBuilder.build(htmlSpec)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user