Skip to content

Commit

Permalink
implement new js sync mechanism
Browse files Browse the repository at this point in the history
This adds a new 3 way sync mechanism, where the test definitions (Asciidoc), that are generated from neo4j graphql (js) are stored separately as `*.js.adoc`.

The new tool JsTestCaseSync.kt now compares the js version with the previous js version and only take over test that were changed.
Additionally, the reformatting feature is extracted from the TestSuites into a new tool AsciidocReformater.kt. This removes code that is not test relevant out of the test factories.

As a refactoring, utility functions were also removed from the test factories into own *Utils classes.
  • Loading branch information
Andy2003 committed Dec 19, 2024
1 parent cf743db commit f9f233f
Show file tree
Hide file tree
Showing 492 changed files with 77,118 additions and 8,962 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ ij_asciidoc_blank_lines_after_header = 1
ij_asciidoc_blank_lines_keep_after_header = 1
ij_asciidoc_formatting_enabled = true
ij_asciidoc_one_sentence_per_line = true
trim_trailing_whitespace = false

[{*.ant,*.fo,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.qrc,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]
ij_xml_align_attributes = true
Expand Down
11 changes: 11 additions & 0 deletions .run/AsciidocReformater.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="org.neo4j.graphql.tools.AsciidocReformater" type="JetRunConfigurationType" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="org.neo4j.graphql.tools.AsciidocReformater" />
<module name="neo4j-graphql-java" />
<shortenClasspath name="NONE" />
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
11 changes: 11 additions & 0 deletions .run/JsTestCaseSync.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="org.neo4j.graphql.tools.JsTestCaseSync" type="JetRunConfigurationType" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="org.neo4j.graphql.tools.JsTestCaseSync" />
<module name="neo4j-graphql-java" />
<shortenClasspath name="NONE" />
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
13 changes: 0 additions & 13 deletions .run/Reformat Tests.run.xml

This file was deleted.

50 changes: 24 additions & 26 deletions core/src/test/kotlin/org/neo4j/graphql/asciidoc/AsciiDocParser.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package org.neo4j.graphql.asciidoc

import org.neo4j.graphql.asciidoc.ast.*
import org.apache.commons.csv.CSVFormat
import java.io.File
import org.neo4j.graphql.asciidoc.ast.*
import java.net.URI
import java.nio.file.Path
import java.util.regex.Pattern
import javax.ws.rs.core.UriBuilder
import kotlin.io.path.readLines

class AsciiDocParser(
fileName: String
private val file: Path
) {

private val file = File(AsciiDocParser::class.java.getResource("/$fileName")?.toURI()!!)
private val srcLocation = File("src/test/resources/", fileName).toURI()

private var root = Document(srcLocation)
private var root = Document(file.toUri())
private var currentSection: Section = root
private var currentDepth: Int = 0


fun parse(): Document {
val lines = file.readLines()
return parseLines(file.readLines())
}

fun parseContent(content: String): Document {
return parseLines(content.lines())
}

private fun parseLines(lines: List<String>): Document {
var title: String?

var insideCodeblock = false
Expand All @@ -29,7 +33,7 @@ class AsciiDocParser(

val fileContent = StringBuilder()

root = Document(srcLocation)
root = Document(file.toUri())
currentSection = root
currentDepth = 0
var caption: String? = null
Expand All @@ -42,11 +46,6 @@ class AsciiDocParser(
loop@ for ((lineNr, line) in lines.withIndex()) {
fileContent.append(line).append('\n')

if (line.startsWith("#") || line.startsWith("//")) {
offset += line.length + 1
continue
}

val headlineMatcher = HEADLINE_PATTERN.matcher(line)

when {
Expand All @@ -55,7 +54,7 @@ class AsciiDocParser(
addBlock(content)
val depth = headlineMatcher.group(1).length
title = headlineMatcher.group(2)
val uri = UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build()
val uri = uriWithLineNr(lineNr)
startSection(title, uri, depth)
}

Expand All @@ -65,10 +64,10 @@ class AsciiDocParser(

line.startsWith("[%header,format=csv") -> {
addBlock(content)
val uri = UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build()
val uri = uriWithLineNr(lineNr)

val parts = line.substring(19, line.indexOf("]")).trim().split(",")
val attributes = parts.slice(0..<parts.size).map {
val parts = line.substring(19, line.indexOf("]")).trim().split(",").filter { it.isNotBlank() }
val attributes = parts.slice(parts.indices).map {
val attributeParts = it.split("=")
attributeParts[0] to attributeParts.getOrNull(1)
}.toMap()
Expand All @@ -82,7 +81,7 @@ class AsciiDocParser(

line.startsWith("[source,") -> {
addBlock(content)
val uri = UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build()
val uri = uriWithLineNr(lineNr)

val parts = line.substring(8, line.indexOf("]")).trim().split(",")
val language = parts[0]
Expand All @@ -93,8 +92,6 @@ class AsciiDocParser(

currentCodeBlock = CodeBlock(uri, language, currentSection, attributes).also {
it.caption = caption
it.markerStart = offset
it.markerEnd = offset + line.length
currentSection.blocks.add(it)
}
caption = null
Expand Down Expand Up @@ -124,10 +121,8 @@ class AsciiDocParser(
line == "----" -> {
insideCodeblock = !insideCodeblock
if (insideCodeblock) {
currentCodeBlock?.start = offset + line.length + 1
content = StringBuilder()
} else if (currentCodeBlock != null) {
currentCodeBlock.end = offset
currentCodeBlock.content = content.toString().trim()
currentCodeBlock = null
content = StringBuilder()
Expand All @@ -145,10 +140,13 @@ class AsciiDocParser(
return root
}

private fun uriWithLineNr(lineNr: Int): URI =
UriBuilder.fromUri(file.toUri()).queryParam("line", lineNr + 1).build()

private fun addBlock(content: StringBuilder) {
val str = content.toString()
if (str.trim().isNotEmpty()) {
currentSection.let { it.blocks.add(Block(it, str)) }
if (str.isNotBlank()) {
currentSection.let { it.blocks.add(Block(it, str.trimEnd())) }
}
content.clear()
}
Expand Down
6 changes: 5 additions & 1 deletion core/src/test/kotlin/org/neo4j/graphql/asciidoc/ast/Block.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package org.neo4j.graphql.asciidoc.ast

class Block(
parent: StructuralNode,
val content: String
var content: String
) : StructuralNode(parent) {

override fun toString(): String {
return "Block(content='$content')"
}

override fun buildContent(contentExtractor: (CodeBlock) -> String): String {
return content + "\n"
}
}
16 changes: 11 additions & 5 deletions core/src/test/kotlin/org/neo4j/graphql/asciidoc/ast/CodeBlock.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ class CodeBlock(

var caption: String? = null

var markerStart: Int? = null
var markerEnd: Int? = null
var start: Int? = null
var end: Int? = null

lateinit var content: String

/**
Expand Down Expand Up @@ -49,4 +44,15 @@ class CodeBlock(
fun matches(language: String, filter: Map<String, String?> = emptyMap(), exactly: Boolean = false) =
this.language == language && filter.all { (k, v) -> attributes[k] == v } && (!exactly || attributes.size == filter.size)

override fun buildContent(contentExtractor: (CodeBlock) -> String): String {
val builder = StringBuilder()
caption?.let {
builder.append("\n.${it}\n")
}
builder.append(adjustedMarker)
builder.append("\n----\n")
builder.append(contentExtractor(this))
builder.append("\n----\n")
return builder.toString()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@ class Document(

lateinit var content: String

override fun buildContent(contentExtractor: (CodeBlock) -> String): String {
val builder = StringBuilder()
blocks.forEach {
builder.append(it.buildContent(contentExtractor))
}
return builder.toString()
}

}
72 changes: 72 additions & 0 deletions core/src/test/kotlin/org/neo4j/graphql/asciidoc/ast/Section.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.neo4j.graphql.asciidoc.ast

import org.neo4j.graphql.domain.CodeBlockPredicate
import java.net.URI

open class Section(
Expand All @@ -11,4 +12,75 @@ open class Section(
override fun toString(): String {
return "Section(title='$title')"
}

override fun buildContent(contentExtractor: (CodeBlock) -> String): String {

val builder = StringBuilder("\n")
var current: Section? = this
do {
builder.append("=")
current = current?.parent
if (current is Document && current.blocks.filterIsInstance<Section>().size > 1) {
// we are in a document with multiple sections, so we need to add another level to the title
builder.append("=")
}
} while (current != null && current !is Document)
builder.append(" ${title}\n")
blocks.forEach {
builder.append(it.buildContent(contentExtractor))
}
return builder.toString()
}

/**
* Find all directly nested code blocks of a given section matching the language and filter
*/
fun findCodeBlocks(
predicate: CodeBlockPredicate,
): List<CodeBlock> =
blocks
.filterIsInstance<CodeBlock>()
.filter { predicate.matches(it) }

/**
* Find a single code block of a given section matching the language and filter
*/
fun findSingleOrNullCodeBlock(
predicate: CodeBlockPredicate,
): CodeBlock? =
findCodeBlocks(predicate)
.also { require(it.size <= 1) { "Found more than one code block matching the predicate" } }
.singleOrNull()

/**
* Find all setup blocks for a given section, including the setup blocks of the parent sections
*/
fun findSetupCodeBlocks(
predicate: CodeBlockPredicate,
): List<CodeBlock> {
val result = mutableListOf<CodeBlock>()
var currentSection: Section? = this
while (currentSection != null) {
result.addAll(currentSection.findCodeBlocks(predicate))
currentSection.blocks
.filterIsInstance<Section>()
.filter { it.title == "Setup" }
.forEach { result.addAll(it.findCodeBlocks(predicate)) }
currentSection = currentSection.parent
}
return result
}

fun addAfter(insertPoint: StructuralNode?, node: StructuralNode) {
if (insertPoint == null) {
blocks.add(node)
} else {
val index = blocks.indexOf(insertPoint)
if (index == -1) {
blocks.add(node)
} else {
blocks.add(index + 1, node)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ sealed class StructuralNode(
open val parent: StructuralNode?
) {
val blocks = mutableListOf<StructuralNode>()

abstract fun buildContent(contentExtractor: (CodeBlock) -> String): String
}
18 changes: 18 additions & 0 deletions core/src/test/kotlin/org/neo4j/graphql/asciidoc/ast/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,22 @@ class Table(
var start: Int? = null
var end: Int? = null

override fun buildContent(contentExtractor: (CodeBlock) -> String): String {
val builder = StringBuilder()
caption?.let {
builder.append("\n.${it}\n")
}
builder.append("[%header,format=csv")
attributes.forEach { (k, v) ->
builder.append(",${k}=${v}")
}
builder.append("]\n|===\n")
builder.append(records.first().parser.headerNames.joinToString(",")).append("\n")
records.forEach { record ->
builder.append(record.joinToString(",")).append("\n")
}
builder.append("|===\n")
return builder.toString()
}

}
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
package org.neo4j.graphql.asciidoc.ast

class ThematicBreak: StructuralNode(null)
class ThematicBreak: StructuralNode(null) {

override fun buildContent(contentExtractor: (CodeBlock) -> String): String {
return "\n'''\n"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.neo4j.graphql.domain

import org.neo4j.graphql.asciidoc.ast.CodeBlock


class CodeBlockPredicate private constructor(
private val language: String,
private val filter: Map<String, String?> = emptyMap(),
private val exactly: Boolean = false
) {

fun matches(codeBlock: CodeBlock) = codeBlock.matches(language, filter, exactly)

companion object {
val CYPHER = CodeBlockPredicate("cypher", exactly = true)
val CYPHER_PARAMS = CodeBlockPredicate("json", exactly = true)
val GRAPHQL_SOURCE_SCHEMA = CodeBlockPredicate("graphql", mapOf("schema" to "true"))
val GRAPHQL_AUGMENTED_SCHEMA = CodeBlockPredicate("graphql", mapOf("augmented" to "true"))
val GRAPHQL_REQUEST = CodeBlockPredicate("graphql", mapOf("request" to "true"))
val GRAPHQL_REQUEST_VARIABLES = CodeBlockPredicate("json", mapOf("request" to "true"))
val GRAPHQL_RESPONSE = CodeBlockPredicate("json", mapOf("response" to "true"))
val QUERY_CONFIG = CodeBlockPredicate("json", mapOf("query-config" to "true"))
val SCHEMA_CONFIG = CodeBlockPredicate("json", mapOf("schema-config" to "true"))
val TEST_DATA = CodeBlockPredicate("cypher", mapOf("test-data" to "true"))
val CUSTOM_RESOLVER = CodeBlockPredicate("kotlin")
}

}
Loading

0 comments on commit f9f233f

Please sign in to comment.