|
1 | 1 | package org.neo4j.graphql.utils
|
2 | 2 |
|
3 | 3 | import org.codehaus.jackson.map.ObjectMapper
|
| 4 | +import org.junit.jupiter.api.DynamicContainer |
| 5 | +import org.junit.jupiter.api.DynamicNode |
4 | 6 | import java.io.File
|
| 7 | +import java.net.URI |
| 8 | +import java.util.regex.Pattern |
| 9 | +import java.util.stream.Stream |
| 10 | +import javax.ws.rs.core.UriBuilder |
5 | 11 |
|
6 |
| -open class AsciiDocTestSuite { |
7 |
| - class ParsedFile( |
8 |
| - var schema: String, |
9 |
| - var file: File, |
10 |
| - val tests: MutableList<ParsedBlock> |
11 |
| - ) |
| 12 | +open class AsciiDocTestSuite(private val fileName: String) { |
12 | 13 |
|
13 | 14 | class ParsedBlock(
|
14 |
| - var title: String? = null, |
15 |
| - var line: Int = 0, |
16 |
| - var ignore: Boolean = false, |
17 |
| - val codeBlocks: MutableMap<String, StringBuilder> = mutableMapOf() |
| 15 | + val uri: URI, |
| 16 | + val code: StringBuilder |
18 | 17 | )
|
19 | 18 |
|
20 |
| - companion object { |
21 |
| - val MAPPER = ObjectMapper() |
| 19 | + fun parse(blocks: LinkedHashSet<String>): Stream<DynamicNode> { |
| 20 | + val file = File(AsciiDocTestSuite::class.java.getResource("/$fileName").toURI()) |
| 21 | + val srcLocation = File("src/test/resources/", fileName).toURI() |
| 22 | + var root: DocumentLevel? = null |
| 23 | + var currentDocumentLevel: DocumentLevel? = null |
| 24 | + |
| 25 | + val lines = file.readLines() |
| 26 | + val terminatorElement = blocks.lastOrNull() |
| 27 | + |
| 28 | + var schema: String? = null |
| 29 | + var title: String? = null |
| 30 | + var current: StringBuilder? = null |
| 31 | + |
| 32 | + var codeBlocks = mutableMapOf<String, ParsedBlock>() |
| 33 | + var ignore = false |
| 34 | + var inside = false |
| 35 | + |
| 36 | + var currentDepth = 0 |
22 | 37 |
|
23 |
| - fun parse(fileName: String, blocks: LinkedHashSet<String>): ParsedFile { |
24 |
| - val file = File(AsciiDocTestSuite::class.java.getResource("/$fileName").toURI()) |
25 |
| - val lines = file.readLines() |
26 |
| - val terminatorElement = blocks.last() |
27 |
| - val tests: MutableList<ParsedBlock> = mutableListOf() |
28 |
| - |
29 |
| - var schema: String? = null |
30 |
| - var lastTitle: String? = null |
31 |
| - var titleCount = 1 |
32 |
| - var current: StringBuilder? = null |
33 |
| - |
34 |
| - var testSet = ParsedBlock() |
35 |
| - var inside = false |
36 |
| - for ((lineNr, line) in lines.withIndex()) { |
37 |
| - if (line.startsWith("#") || line.startsWith("//")) { |
38 |
| - continue |
| 38 | + loop@ for ((lineNr, line) in lines.withIndex()) { |
| 39 | + if (line.startsWith("#") || line.startsWith("//")) { |
| 40 | + continue |
| 41 | + } |
| 42 | + val headlineMatcher = HEADLINE_PATTERN.matcher(line) |
| 43 | + when { |
| 44 | + line == "[source,graphql,schema=true]" -> schema = "" |
| 45 | + blocks.contains(line) -> { |
| 46 | + current = StringBuilder() |
| 47 | + codeBlocks[line] = ParsedBlock( |
| 48 | + UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build(), |
| 49 | + current |
| 50 | + ) |
39 | 51 | }
|
40 |
| - when { |
41 |
| - line == "[source,graphql,schema=true]" -> schema = "" |
42 |
| - blocks.contains(line) -> { |
43 |
| - current = StringBuilder() |
44 |
| - testSet.codeBlocks[line] = current |
| 52 | + line == "----" -> { |
| 53 | + if (schema?.isNotBlank() == true && current == null) { |
| 54 | + val tests = schemaTestFactory(schema) |
| 55 | + currentDocumentLevel?.tests?.add(tests) |
| 56 | + if (terminatorElement == null) { |
| 57 | + break@loop |
| 58 | + } |
45 | 59 | }
|
46 |
| - line == "----" -> { |
47 |
| - if (testSet.codeBlocks[terminatorElement]?.isNotEmpty() == true) { |
48 |
| - tests.add(testSet) |
49 |
| - if (testSet.title == null) { |
50 |
| - testSet.title = lastTitle + " " + ++titleCount |
51 |
| - } else { |
52 |
| - titleCount = 1 |
53 |
| - lastTitle = testSet.title |
54 |
| - } |
55 |
| - testSet = ParsedBlock() |
| 60 | + if (codeBlocks[terminatorElement]?.code?.isNotEmpty() == true) { |
| 61 | + val tests = testFactory( |
| 62 | + title ?: throw IllegalStateException("Title should be defined (line $lineNr)"), |
| 63 | + schema ?: throw IllegalStateException("Schema should be defined"), |
| 64 | + codeBlocks, |
| 65 | + ignore) |
| 66 | + currentDocumentLevel?.tests?.add(tests) |
| 67 | + codeBlocks = mutableMapOf() |
| 68 | + } |
| 69 | + inside = !inside |
| 70 | + } |
| 71 | + headlineMatcher.matches() -> { |
| 72 | + val uri = UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build() |
| 73 | + val depth = headlineMatcher.group(1).length |
| 74 | + title = headlineMatcher.group(2) |
| 75 | + if (root == null) { |
| 76 | + root = DocumentLevel(null, title, uri) |
| 77 | + currentDocumentLevel = root |
| 78 | + } else { |
| 79 | + val parent = when { |
| 80 | + depth > currentDepth -> currentDocumentLevel |
| 81 | + depth == currentDepth -> currentDocumentLevel?.parent |
| 82 | + ?: throw IllegalStateException("cannot create sub-level on null") |
| 83 | + else -> currentDocumentLevel?.parent?.parent |
| 84 | + ?: throw IllegalStateException("cannot create sub-level on null") |
56 | 85 | }
|
57 |
| - inside = !inside |
| 86 | + currentDocumentLevel = DocumentLevel(parent, title, uri) |
58 | 87 | }
|
59 |
| - line.startsWith("=== ") -> { |
60 |
| - testSet.title = line.substring(4) |
61 |
| - testSet.line = lineNr + 1 |
| 88 | + currentDepth = depth |
| 89 | + } |
| 90 | + line.startsWith("CAUTION:") -> ignore = true |
| 91 | + inside -> when { |
| 92 | + current != null -> current.append(line).append("\n") |
| 93 | + schema != null -> schema += line + "\n" |
| 94 | + } |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + return root?.generateTests() ?: Stream.empty() |
| 99 | + } |
| 100 | + |
| 101 | + open fun testFactory(title: String, schema: String, codeBlocks: Map<String, ParsedBlock>, ignore: Boolean): List<DynamicNode> { |
| 102 | + return emptyList() |
| 103 | + } |
| 104 | + |
| 105 | + open fun schemaTestFactory(schema: String): List<DynamicNode> { |
| 106 | + return emptyList() |
| 107 | + } |
| 108 | + |
| 109 | + companion object { |
| 110 | + val MAPPER = ObjectMapper() |
| 111 | + val HEADLINE_PATTERN: Pattern = Pattern.compile("^(=+) (.*)$") |
| 112 | + |
| 113 | + class DocumentLevel( |
| 114 | + val parent: DocumentLevel?, |
| 115 | + val name: String, |
| 116 | + private val testSourceUri: URI |
| 117 | + ) { |
| 118 | + private val children = mutableListOf<DocumentLevel>() |
| 119 | + val tests = mutableListOf<List<DynamicNode>>() |
| 120 | + |
| 121 | + init { |
| 122 | + parent?.children?.add(this) |
| 123 | + } |
| 124 | + |
| 125 | + fun generateTests(): Stream<DynamicNode> { |
| 126 | + val streamBuilder = Stream.builder<DynamicNode>() |
| 127 | + if (tests.size > 1) { |
| 128 | + if (children.isNotEmpty()) { |
| 129 | + streamBuilder.add(DynamicContainer.dynamicContainer(name, testSourceUri, children.stream().flatMap { it.generateTests() })) |
62 | 130 | }
|
63 |
| - line.startsWith("CAUTION:") -> testSet.ignore = true |
64 |
| - inside -> when { |
65 |
| - current != null -> current.append(line).append("\n") |
66 |
| - schema != null -> schema += line + "\n" |
| 131 | + for ((index, test) in tests.withIndex()) { |
| 132 | + streamBuilder.add(DynamicContainer.dynamicContainer(name + " " + (index + 1), testSourceUri, test.stream())) |
67 | 133 | }
|
| 134 | + } else { |
| 135 | + val nodes = Stream.concat( |
| 136 | + tests.stream().flatMap { it.stream() }, |
| 137 | + children.stream().flatMap { it.generateTests() } |
| 138 | + ) |
| 139 | + streamBuilder.add(DynamicContainer.dynamicContainer(name, testSourceUri, nodes)) |
68 | 140 | }
|
| 141 | + return streamBuilder.build() |
69 | 142 | }
|
70 |
| - return ParsedFile(schema ?: throw IllegalStateException("no schema found"), |
71 |
| - File("src/test/resources/$fileName").absoluteFile, tests) |
72 | 143 | }
|
73 | 144 |
|
74 | 145 | private fun fixNumber(v: Any?): Any? = when (v) {
|
|
0 commit comments