in buildSrc/src/main/groovy/org/opensearch/gradle/doc/SnippetsTask.groovy [88:321]
void executeTask() {
/*
* Walks each line of each file, building snippets as it encounters
* the lines that make up the snippet.
*/
for (File file: docs) {
String lastLanguage
int lastLanguageLine
Snippet snippet = null
StringBuilder contents = null
List substitutions = null
String testEnv = null
Closure emit = {
snippet.contents = contents.toString()
contents = null
Closure doSubstitution = { String pattern, String subst ->
/*
* $body is really common but it looks like a
* backreference so we just escape it here to make the
* tests cleaner.
*/
subst = subst.replace('$body', '\\$body')
subst = subst.replace('$_path', '\\$_path')
// \n is a new line....
subst = subst.replace('\\n', '\n')
snippet.contents = snippet.contents.replaceAll(
pattern, subst)
}
defaultSubstitutions.each doSubstitution
if (substitutions != null) {
substitutions.each doSubstitution
substitutions = null
}
if (snippet.language == null) {
throw new InvalidUserDataException("$snippet: "
+ "Snippet missing a language. This is required by "
+ "OpenSearch's doc testing infrastructure so we "
+ "be sure we don't accidentally forget to test a "
+ "snippet.")
}
// Try to detect snippets that contain `curl`
if (snippet.language == 'sh' || snippet.language == 'shell') {
snippet.curl = snippet.contents.contains('curl')
if (snippet.console == false && snippet.curl == false) {
throw new InvalidUserDataException("$snippet: "
+ "No need for NOTCONSOLE if snippet doesn't "
+ "contain `curl`.")
}
}
if (snippet.testResponse
&& ('js' == snippet.language || 'console-result' == snippet.language)
&& null == snippet.skip) {
String quoted = snippet.contents
// quote values starting with $
.replaceAll(/([:,])\s*(\$[^ ,\n}]+)/, '$1 "$2"')
// quote fields starting with $
.replaceAll(/(\$[^ ,\n}]+)\s*:/, '"$1":')
JsonSlurper slurper =
new JsonSlurper(type: JsonParserType.INDEX_OVERLAY)
try {
slurper.parseText(quoted)
} catch (JsonException e) {
throw new InvalidUserDataException("Invalid json "
+ "in $snippet. The error is:\n${e.message}.\n"
+ "After substitutions and munging, the json "
+ "looks like:\n$quoted", e)
}
}
perSnippet(snippet)
snippet = null
}
file.eachLine('UTF-8') { String line, int lineNumber ->
Matcher matcher
matcher = line =~ /\[testenv="([^"]+)"\]\s*/
if (matcher.matches()) {
testEnv = matcher.group(1)
}
if (line ==~ /-{4,}\s*/) { // Four dashes looks like a snippet
if (snippet == null) {
Path path = docs.dir.toPath().relativize(file.toPath())
snippet = new Snippet(path: path, start: lineNumber, testEnv: testEnv)
if (lastLanguageLine == lineNumber - 1) {
snippet.language = lastLanguage
}
} else {
snippet.end = lineNumber
}
return
}
matcher = line =~ /\["?source"?,\s*"?([-\w]+)"?(,.*)?].*/
if (matcher.matches()) {
lastLanguage = matcher.group(1)
lastLanguageLine = lineNumber
return
}
if (line ==~ /\/\/\s*AUTOSENSE\s*/) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "AUTOSENSE has been replaced by CONSOLE.")
}
if (line ==~ /\/\/\s*CONSOLE\s*/) {
if (snippet == null) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "CONSOLE not paired with a snippet")
}
if (snippet.console != null) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "Can't be both CONSOLE and NOTCONSOLE")
}
snippet.console = true
return
}
if (line ==~ /\/\/\s*NOTCONSOLE\s*/) {
if (snippet == null) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "NOTCONSOLE not paired with a snippet")
}
if (snippet.console != null) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "Can't be both CONSOLE and NOTCONSOLE")
}
snippet.console = false
return
}
matcher = line =~ /\/\/\s*TEST(\[(.+)\])?\s*/
if (matcher.matches()) {
if (snippet == null) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "TEST not paired with a snippet at ")
}
snippet.test = true
if (matcher.group(2) != null) {
String loc = "$file:$lineNumber"
parse(loc, matcher.group(2), TEST_SYNTAX) {
if (it.group(1) != null) {
snippet.catchPart = it.group(1)
return
}
if (it.group(2) != null) {
if (substitutions == null) {
substitutions = []
}
substitutions.add([it.group(2), it.group(3)])
return
}
if (it.group(4) != null) {
snippet.skip = it.group(4)
return
}
if (it.group(5) != null) {
snippet.continued = true
return
}
if (it.group(6) != null) {
snippet.setup = it.group(6)
return
}
if (it.group(7) != null) {
snippet.warnings.add(it.group(7))
return
}
if (it.group(8) != null) {
snippet.skipShardsFailures = true
return
}
throw new InvalidUserDataException(
"Invalid test marker: $line")
}
}
return
}
matcher = line =~ /\/\/\s*TESTRESPONSE(\[(.+)\])?\s*/
if (matcher.matches()) {
if (snippet == null) {
throw new InvalidUserDataException("$file:$lineNumber: "
+ "TESTRESPONSE not paired with a snippet")
}
snippet.testResponse = true
if (matcher.group(2) != null) {
if (substitutions == null) {
substitutions = []
}
String loc = "$file:$lineNumber"
parse(loc, matcher.group(2), /(?:$SUBSTITUTION|$NON_JSON|$SKIP) ?/) {
if (it.group(1) != null) {
// TESTRESPONSE[s/adsf/jkl/]
substitutions.add([it.group(1), it.group(2)])
} else if (it.group(3) != null) {
// TESTRESPONSE[non_json]
substitutions.add(['^', '/'])
substitutions.add(['\n$', '\\\\s*/'])
substitutions.add(['( +)', '$1\\\\s+'])
substitutions.add(['\n', '\\\\s*\n '])
} else if (it.group(4) != null) {
// TESTRESPONSE[skip:reason]
snippet.skip = it.group(4)
}
}
}
return
}
if (line ==~ /\/\/\s*TESTSETUP\s*/) {
snippet.testSetup = true
return
}
if (line ==~ /\/\/\s*TEARDOWN\s*/) {
snippet.testTearDown = true
return
}
if (snippet == null) {
// Outside
return
}
if (snippet.end == Snippet.NOT_FINISHED) {
// Inside
if (contents == null) {
contents = new StringBuilder()
}
// We don't need the annotations
line = line.replaceAll(/<\d+>/, '')
// Nor any trailing spaces
line = line.replaceAll(/\s+$/, '')
contents.append(line).append('\n')
return
}
// Allow line continuations for console snippets within lists
if (snippet != null && line.trim() == '+') {
return
}
// Just finished
emit()
}
if (snippet != null) emit()
}
}