This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new fa779353ec19 CAMEL-25736: camel-jbang - the AI samples and the
documentation tests read the YAML examples with a longer delimiter or a leading
comment (#28021)
fa779353ec19 is described below
commit fa779353ec1958f0eefc2d41cd77642bcd1658f6
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 17 20:29:18 2026 +0200
CAMEL-25736: camel-jbang - the AI samples and the documentation tests read
the YAML examples with a longer delimiter or a leading comment (#28021)
DocBlocks, the extractor of the doc-samples plugin and of the documentation
tests, now reads a listing with a
delimiter longer than ---- (14 examples on 3 pages) and a block that opens
with a comment before a top-level entry
(35 examples on about 20 pages); a list of steps with a comment above it
stays a fragment. CatalogSamples uses
DocBlocks instead of its own regex, so camel_catalog_sample reads the docs
as the plugin and the tests do, without
the callout markers and the blocks marked as what to avoid.
The examples the tests then checked for the first time that were wrong are
fixed: docling-batch-processing (the
aggregate's strategy, output and a property the strategy does not have),
keycloak-consumer (a filter without steps,
an extra expression level), keycloak-producer (an operation that does not
exist, application.yaml syntax in the
route file) and keycloak-security (handled as a plain value); with their
catalog copies.
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
.../catalog/docs/docling-batch-processing.adoc | 13 ++-
.../camel/catalog/docs/keycloak-consumer.adoc | 28 +++----
.../camel/catalog/docs/keycloak-producer.adoc | 19 +++--
.../camel/catalog/docs/keycloak-security.adoc | 4 +-
.../src/main/docs/docling-batch-processing.adoc | 13 ++-
.../src/main/docs/keycloak-consumer.adoc | 28 +++----
.../src/main/docs/keycloak-producer.adoc | 19 +++--
.../src/main/docs/keycloak-security.adoc | 4 +-
.../dsl/jbang/core/commands/ai/CatalogSamples.java | 13 ++-
.../apache/camel/dsl/yaml/validator/DocBlocks.java | 71 ++++++++++++++--
.../camel/dsl/yaml/validator/DocBlocksTest.java | 98 ++++++++++++++++++++++
.../yaml/validator/UserManualDocExamplesTest.java | 16 ++--
12 files changed, 244 insertions(+), 82 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docling-batch-processing.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docling-batch-processing.adoc
index 477e747a0f1e..80d99254369f 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docling-batch-processing.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docling-batch-processing.adoc
@@ -174,8 +174,6 @@ YAML::
- beans:
- name: documentListProcessor
type:
"#class:org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy"
- properties:
- strategyMethodName: "aggregate"
# Route 1: Collect documents
- route:
@@ -205,14 +203,15 @@ YAML::
concurrentConsumers: 1
steps:
- aggregate:
- aggregationStrategy:
- bean: "documentListProcessor"
+ aggregationStrategy: documentListProcessor
correlationExpression:
- constant: true
+ constant:
+ expression: "true"
completionSize: 10
completionTimeout: 5000
- - to:
- uri: direct:batch-process
+ steps:
+ - to:
+ uri: direct:batch-process
# Route 3: Process batch
- route:
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
index 9c0484ac670c..f2f0bb098fa4 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
@@ -563,21 +563,21 @@ YAML::
expression:
simple:
expression: "${body.type} == 'LOGIN_ERROR'"
- - aggregate:
- correlationExpression:
- expression:
- simple:
- expression: "${body.ipAddress}"
- aggregationStrategy: "#arrayListAggregation"
- completionSize: 5
- completionTimeout: 300000
steps:
- - log:
- message: "SECURITY ALERT: Multiple failed login attempts
from ${body[0].ipAddress}"
- - to:
- uri: direct:block-ip
- - to:
- uri: direct:send-security-alert
+ - aggregate:
+ correlationExpression:
+ simple:
+ expression: "${body.ipAddress}"
+ aggregationStrategy:
"#class:org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy"
+ completionSize: 5
+ completionTimeout: 300000
+ steps:
+ - log:
+ message: "SECURITY ALERT: Multiple failed login
attempts from ${body[0].ipAddress}"
+ - to:
+ uri: direct:block-ip
+ - to:
+ uri: direct:send-security-alert
----
====
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
index fe3c24e952a6..320d98815f57 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
@@ -784,7 +784,7 @@ YAML::
- to:
uri: keycloak:admin
parameters:
- operation: setUserPassword
+ operation: resetUserPassword
- setHeader:
name: CamelKeycloakRoleName
constant: "admin"
@@ -794,14 +794,15 @@ YAML::
operation: assignRoleToUser
- transform:
constant: "User environment setup completed successfully"
+----
+
+The component configuration, in `application.properties`:
-# Component configuration
-camel:
- component:
- keycloak:
- server-url: "http://localhost:8080"
- realm: "master"
- username: "admin"
- password: "admin"
+[source,properties]
+----
+camel.component.keycloak.server-url = http://localhost:8080
+camel.component.keycloak.realm = master
+camel.component.keycloak.username = admin
+camel.component.keycloak.password = admin
----
====
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
index e4b7da310e94..eebdd1767291 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
@@ -1006,7 +1006,9 @@ YAML::
- onException:
exception:
- "org.apache.camel.CamelAuthorizationException"
- handled: true
+ handled:
+ constant:
+ expression: "true"
steps:
- setHeader:
name: "CamelHttpResponseCode"
diff --git
a/components/camel-ai/camel-docling/src/main/docs/docling-batch-processing.adoc
b/components/camel-ai/camel-docling/src/main/docs/docling-batch-processing.adoc
index 477e747a0f1e..80d99254369f 100644
---
a/components/camel-ai/camel-docling/src/main/docs/docling-batch-processing.adoc
+++
b/components/camel-ai/camel-docling/src/main/docs/docling-batch-processing.adoc
@@ -174,8 +174,6 @@ YAML::
- beans:
- name: documentListProcessor
type:
"#class:org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy"
- properties:
- strategyMethodName: "aggregate"
# Route 1: Collect documents
- route:
@@ -205,14 +203,15 @@ YAML::
concurrentConsumers: 1
steps:
- aggregate:
- aggregationStrategy:
- bean: "documentListProcessor"
+ aggregationStrategy: documentListProcessor
correlationExpression:
- constant: true
+ constant:
+ expression: "true"
completionSize: 10
completionTimeout: 5000
- - to:
- uri: direct:batch-process
+ steps:
+ - to:
+ uri: direct:batch-process
# Route 3: Process batch
- route:
diff --git a/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
b/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
index 9c0484ac670c..f2f0bb098fa4 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
@@ -563,21 +563,21 @@ YAML::
expression:
simple:
expression: "${body.type} == 'LOGIN_ERROR'"
- - aggregate:
- correlationExpression:
- expression:
- simple:
- expression: "${body.ipAddress}"
- aggregationStrategy: "#arrayListAggregation"
- completionSize: 5
- completionTimeout: 300000
steps:
- - log:
- message: "SECURITY ALERT: Multiple failed login attempts
from ${body[0].ipAddress}"
- - to:
- uri: direct:block-ip
- - to:
- uri: direct:send-security-alert
+ - aggregate:
+ correlationExpression:
+ simple:
+ expression: "${body.ipAddress}"
+ aggregationStrategy:
"#class:org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy"
+ completionSize: 5
+ completionTimeout: 300000
+ steps:
+ - log:
+ message: "SECURITY ALERT: Multiple failed login
attempts from ${body[0].ipAddress}"
+ - to:
+ uri: direct:block-ip
+ - to:
+ uri: direct:send-security-alert
----
====
diff --git a/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
b/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
index fe3c24e952a6..320d98815f57 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
@@ -784,7 +784,7 @@ YAML::
- to:
uri: keycloak:admin
parameters:
- operation: setUserPassword
+ operation: resetUserPassword
- setHeader:
name: CamelKeycloakRoleName
constant: "admin"
@@ -794,14 +794,15 @@ YAML::
operation: assignRoleToUser
- transform:
constant: "User environment setup completed successfully"
+----
+
+The component configuration, in `application.properties`:
-# Component configuration
-camel:
- component:
- keycloak:
- server-url: "http://localhost:8080"
- realm: "master"
- username: "admin"
- password: "admin"
+[source,properties]
+----
+camel.component.keycloak.server-url = http://localhost:8080
+camel.component.keycloak.realm = master
+camel.component.keycloak.username = admin
+camel.component.keycloak.password = admin
----
====
diff --git a/components/camel-keycloak/src/main/docs/keycloak-security.adoc
b/components/camel-keycloak/src/main/docs/keycloak-security.adoc
index e4b7da310e94..eebdd1767291 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-security.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-security.adoc
@@ -1006,7 +1006,9 @@ YAML::
- onException:
exception:
- "org.apache.camel.CamelAuthorizationException"
- handled: true
+ handled:
+ constant:
+ expression: "true"
steps:
- setHeader:
name: "CamelHttpResponseCode"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
index 599e6cca0a36..208786c44d8b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
@@ -34,6 +34,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.dsl.yaml.validator.DocBlocks;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.tooling.model.DataFormatModel;
import org.apache.camel.tooling.model.EipModel;
@@ -195,7 +196,6 @@ public final class CatalogSamples {
return sample(catalog, null, name, limit);
}
- private static final Pattern YAML_BLOCK =
Pattern.compile("\\[source,yaml\\]\\s*\\n----\\n(.*?)\\n----", Pattern.DOTALL);
private static final Map<String, Found> DOC_CACHE = new
ConcurrentHashMap<>();
/** The validated examples of a page family, and for a component whether
any of them uses its endpoint. */
@@ -274,13 +274,10 @@ public final class CatalogSamples {
if (doc == null) {
continue;
}
- Matcher m = YAML_BLOCK.matcher(doc);
- while (m.find()) {
- String yaml = m.group(1).stripTrailing() + "\n";
- if (!yaml.stripLeading().startsWith("- ")) {
- // a fragment (an option list, a manifest), not a route
file
- continue;
- }
+ // the route examples as the doc-samples plugin and the
documentation tests read them (CAMEL-25736): a
+ // longer delimiter and a leading comment are fine, callout
markers are left out, and a block marked as
+ // what to avoid is no sample; the fragments (an option list, a
manifest) are not route files
+ for (String yaml : DocBlocks.examples(doc)) {
if (!SourceValidator.validateYamlSchema(yaml,
catalog).isEmpty()) {
continue;
}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/DocBlocks.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/DocBlocks.java
index 99f19e3523b8..e547538379e3 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/DocBlocks.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/DocBlocks.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.yaml.validator;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -34,7 +35,11 @@ public final class DocBlocks {
/** The AsciiDoc comment that keeps a block out of the examples. */
public static final String SKIP_MARKER = "// yaml-validator: skip";
- private static final Pattern YAML_BLOCK =
Pattern.compile("\\[source,yaml\\]\\s*\\n----\\n(.*?)\\n----",
+ /**
+ * A YAML listing block: four or more dashes, closed by the same line. A
delimiter longer than four dashes is valid
+ * AsciiDoc and common in older pages, and their examples were not read
(CAMEL-25736).
+ */
+ private static final Pattern YAML_BLOCK =
Pattern.compile("\\[source,yaml\\]\\s*\\n(-{4,})\\n(.*?)\\n\\1(?=\\n|$)",
Pattern.DOTALL);
/** An AsciiDoc callout marker at the end of a line, which is
documentation and not part of the route. */
@@ -46,25 +51,79 @@ public final class DocBlocks {
private DocBlocks() {
}
+ /** A route example of a page, and the offset in the page where its YAML
starts (to tell its line). */
+ public record Example(int start, String yaml) {
+ }
+
/**
- * The route examples of the page: the YAML blocks that start with a
top-level list entry, without their callout
- * markers, and without the blocks that carry {@value #SKIP_MARKER}.
+ * The route examples of the page: the YAML blocks that start with a
top-level list entry (after the comment lines
+ * that may come first), without their callout markers, and without the
blocks that carry {@value #SKIP_MARKER}.
*/
public static List<String> examples(String doc) {
List<String> answer = new ArrayList<>();
+ for (Example example : examplesAt(doc)) {
+ answer.add(example.yaml());
+ }
+ return answer;
+ }
+
+ /** The route examples of the page as {@link #examples(String)} finds
them, each with where it starts. */
+ public static List<Example> examplesAt(String doc) {
+ List<Example> answer = new ArrayList<>();
if (doc == null) {
return answer;
}
Matcher m = YAML_BLOCK.matcher(doc);
while (m.find()) {
- String yaml =
CALLOUT.matcher(m.group(1)).replaceAll("").stripTrailing() + "\n";
- if (yaml.stripLeading().startsWith("- ") && !markedToSkip(doc,
m.start())) {
- answer.add(yaml);
+ String yaml =
CALLOUT.matcher(m.group(2)).replaceAll("").stripTrailing() + "\n";
+ if (startsWithEntry(yaml) && !markedToSkip(doc, m.start())) {
+ answer.add(new Example(m.start(2), yaml));
}
}
return answer;
}
+ /**
+ * The entries a YAML DSL file is a list of: the roots of the schema, and
the kebab-case names the parser also
+ * accepts. A kamelet is a step, not one of them.
+ */
+ private static final Set<String> TOP_LEVEL = Set.of("beans",
"dataFormats", "errorHandler", "from", "intercept",
+ "interceptFrom", "interceptSendToEndpoint", "onCompletion",
"onException", "rest", "restConfiguration",
+ "route", "route-configuration", "route-template",
"routeConfiguration", "routeTemplate", "semantic",
+ "sslContextParameters", "templated-route", "templatedRoute",
"transformers", "validators");
+
+ /** The key of a list entry, as route in "- route:". */
+ private static final Pattern ENTRY_KEY = Pattern.compile("- ([\\w-]+):");
+
+ /**
+ * Whether the YAML is a route file: its first line that is not blank or a
comment is a list entry. After a comment
+ * it must be a top-level entry: a block that opens with "# send the
dataset to a queue" and then - route: is a
+ * route file, and was skipped (CAMEL-25736), while a list of steps with a
comment above it is a fragment of a
+ * route.
+ */
+ static boolean startsWithEntry(String yaml) {
+ boolean comment = false;
+ for (String line : yaml.split("\n")) {
+ String s = line.strip();
+ if (s.isEmpty()) {
+ continue;
+ }
+ if (s.startsWith("#")) {
+ comment = true;
+ continue;
+ }
+ if (!s.startsWith("- ")) {
+ return false;
+ }
+ if (!comment) {
+ return true;
+ }
+ Matcher m = ENTRY_KEY.matcher(s);
+ return m.lookingAt() && TOP_LEVEL.contains(m.group(1));
+ }
+ return false;
+ }
+
/** Whether the block at this offset carries {@link #SKIP_MARKER} in the
lines before it. */
public static boolean markedToSkip(String doc, int blockStart) {
int from = Math.max(0, blockStart - MARKER_LOOKBEHIND);
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/DocBlocksTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/DocBlocksTest.java
new file mode 100644
index 000000000000..4b9144d2033d
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/DocBlocksTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.yaml.validator;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * CAMEL-25736: the route examples of a page include the blocks with a
delimiter longer than four dashes, and the blocks
+ * that open with a comment; 53 examples of the documentation were not read
before.
+ */
+class DocBlocksTest {
+
+ @Test
+ void aLongerDelimiterAndALeadingCommentAreRead() {
+ String doc = """
+ = A page
+
+ [source,yaml]
+
--------------------------------------------------------------------------------
+ - from:
+ uri: direct:search
+ steps:
+ - log:
+ message: "${body}"
+
--------------------------------------------------------------------------------
+
+ [source,yaml]
+ ----
+ # send the dataset to a queue
+ - route:
+ from:
+ uri: dataset:foo
+ steps:
+ - to:
+ uri: seda:queue
+ ----
+
+ [source,yaml]
+ ----
+ key: a fragment, not a route
+ ----
+
+ [source,yaml]
+ ----
+ # the steps to add to the route: a fragment too
+ - setHeader:
+ name: CamelJmsDestinationName
+ expression:
+ constant:
+ expression: "queue:///MY_QUEUE"
+ ----
+ """;
+ List<String> examples = DocBlocks.examples(doc);
+ assertEquals(2, examples.size(), String.valueOf(examples));
+ assertTrue(examples.get(0).startsWith("- from:"), examples.get(0));
+ assertTrue(examples.get(1).startsWith("# send the dataset to a
queue\n- route:"), examples.get(1));
+ }
+
+ @Test
+ void afterACommentOnlyARootOfTheDslIsARouteFile() {
+ // a kamelet is a step; semantic is a root of the schema
+ String doc = "[source,yaml]\n----\n# the step to add\n- kamelet:\n
name: beer-source\n----\n"
+ + "[source,yaml]\n----\n# the semantic entry\n-
semantic:\n id: orders\n----\n";
+ List<DocBlocks.Example> examples = DocBlocks.examplesAt(doc);
+ assertEquals(1, examples.size(), String.valueOf(examples));
+ assertTrue(examples.get(0).yaml().startsWith("# the semantic entry\n-
semantic:"), examples.get(0).yaml());
+ assertEquals(doc.indexOf("# the semantic entry"),
examples.get(0).start());
+ }
+
+ @Test
+ void aBlockClosesAtItsOwnDelimiter() {
+ // a shorter or a longer line of dashes inside the block is part of it
+ String doc = "[source,yaml]\n--------\n- from:\n uri: direct:a\n
steps:\n - log:\n"
+ + " message:
x\n----\n----------\n--------\nafter\n";
+ List<String> examples = DocBlocks.examples(doc);
+ assertEquals(1, examples.size());
+ assertTrue(examples.get(0).endsWith("----\n----------\n"),
examples.get(0));
+ }
+}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/UserManualDocExamplesTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/UserManualDocExamplesTest.java
index 174a198934f5..f545d1f34c0a 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/UserManualDocExamplesTest.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/UserManualDocExamplesTest.java
@@ -50,7 +50,6 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
class UserManualDocExamplesTest {
private static final Path PAGES = Path.of("docs", "user-manual",
"modules", "ROOT", "pages");
- private static final Pattern YAML_BLOCK =
Pattern.compile("\\[source,yaml\\]\\n-{4,}\\n(.*?)\\n-{4,}", Pattern.DOTALL);
private static final Pattern FIRST_KEY =
Pattern.compile("^\\s*-\\s+([A-Za-z]+)\\s*:");
private static YamlValidator validator;
@@ -104,10 +103,10 @@ class UserManualDocExamplesTest {
String page = entry.getKey();
String doc = entry.getValue();
int n = 0;
- Matcher m = YAML_BLOCK.matcher(doc);
- while (m.find()) {
- String yaml = m.group(1).stripTrailing() + "\n";
- Matcher key = FIRST_KEY.matcher(yaml.stripLeading());
+ // the blocks as the doc-samples plugin and the other
documentation tests read them
+ for (DocBlocks.Example example : DocBlocks.examplesAt(doc)) {
+ String yaml = example.yaml();
+ Matcher key = FIRST_KEY.matcher(firstEntry(yaml));
if (!key.find() || !roots.contains(key.group(1))) {
// a step or an option fragment, or the YAML of another
tool: not a route file
continue;
@@ -116,7 +115,7 @@ class UserManualDocExamplesTest {
examples++;
List<Error> errors = validator.validate(yaml);
if (!errors.isEmpty()) {
- int line = 1 + countLines(doc, m.start(1));
+ int line = 1 + countLines(doc, example.start());
failures.add(page + ".adoc:" + line + " example " + n + ":
" + errors.get(0).getMessage());
}
}
@@ -151,6 +150,11 @@ class UserManualDocExamplesTest {
return answer;
}
+ /** The first line of the YAML that is not blank or a comment. */
+ private static String firstEntry(String yaml) {
+ return yaml.lines().map(String::strip).filter(l -> !l.isEmpty() &&
!l.startsWith("#")).findFirst().orElse("");
+ }
+
private static int countLines(String text, int end) {
int n = 0;
for (int i = 0; i < end; i++) {