mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
add specification test support module
This commit is contained in:
parent
ecabd71048
commit
334bbd0379
@ -1,3 +1,19 @@
|
||||
<!--
|
||||
~ Copyright (c) 2022 nosqlbench
|
||||
~
|
||||
~ Licensed 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -26,6 +42,12 @@
|
||||
<version>4.17.15-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.nosqlbench</groupId>
|
||||
<artifactId>nb-spectest</artifactId>
|
||||
<version>4.17.15-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.nosqlbench</groupId>
|
||||
<artifactId>drivers-api</artifactId>
|
||||
|
@ -16,368 +16,26 @@
|
||||
|
||||
package io.nosqlbench.engine.api.activityconfig.rawyaml;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.vladsch.flexmark.ast.FencedCodeBlock;
|
||||
import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.ast.Document;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
|
||||
import io.nosqlbench.nb.api.content.NBIO;
|
||||
import io.nosqlbench.nb.spectest.core.SpecTest;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class UniformWorkloadSpecificationTest {
|
||||
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(UniformWorkloadSpecificationTest.class);
|
||||
|
||||
private static final Parser parser = Parser.builder().extensions(List.of(YamlFrontMatterExtension.create())).build();
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
|
||||
@Test
|
||||
public void testTemplatedWorkloads() {
|
||||
testSpecPath(
|
||||
NBIO.fs().prefix("target/classes/workload_definition/")
|
||||
.name("templated_workloads.md")
|
||||
.one().asPath()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemplatedOperations() {
|
||||
testSpecPath(
|
||||
NBIO.fs().prefix("target/classes/workload_definition/")
|
||||
.name("templated_operations.md")
|
||||
.one().asPath()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemplatedOperationVariations() {
|
||||
testSpecPath(
|
||||
NBIO.fs().prefix("target/classes/workload_definition/")
|
||||
.name("templated_operation_variations.md")
|
||||
.one().asPath()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemplateVariables() {
|
||||
testSpecPath(
|
||||
NBIO.fs().prefix("target/classes/workload_definition/")
|
||||
.name("template_variables.md")
|
||||
.one().asPath()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommandAPI() {
|
||||
testSpecPath(
|
||||
NBIO.fs().prefix("target/classes/workload_definition/")
|
||||
.name("command_api.md")
|
||||
.one().asPath()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public String summarize(Node node) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (node != null) {
|
||||
sb.append("-> ").append(node.getClass().getSimpleName()).append("\n");
|
||||
sb.append(node.getChars()).append("\n");
|
||||
node = node.getNext();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String summarize(List<Node> nodes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Node node : nodes) {
|
||||
sb.append(node.getClass().getSimpleName()).append("\n");
|
||||
sb.append(node.getChars()).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private void testSpecPath(Path specPath) {
|
||||
LinkedList<TestSet> tests = new LinkedList<>();
|
||||
Pattern emphasis = Pattern.compile("\\*(.*?)\\*\n");
|
||||
Class<?> fcbclass = FencedCodeBlock.class;
|
||||
NodePredicates p = new NodePredicates(emphasis, fcbclass, emphasis, fcbclass, emphasis, fcbclass);
|
||||
HeadingScanner headings = new HeadingScanner(" > ");
|
||||
|
||||
List<TestBlock> testblocks = new ArrayList<>();
|
||||
|
||||
headings.update(specPath);
|
||||
|
||||
String input = null;
|
||||
try {
|
||||
input = Files.readString(specPath);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Document parsed = parser.parse(input);
|
||||
Node node = parsed.getFirstChild();
|
||||
// summarize(node,System.out);
|
||||
int index = 0;
|
||||
|
||||
while (node != null) {
|
||||
headings.update(node);
|
||||
if (p.test(node)) {
|
||||
List<Node> found = p.get();
|
||||
// System.out.println(summarize(found));
|
||||
testblocks.add(new TestBlock(
|
||||
new TestSet(headings, found.get(0), found.get(1), specPath),
|
||||
new TestSet(headings, found.get(2), found.get(3), specPath),
|
||||
new TestSet(headings, found.get(4), found.get(5), specPath)
|
||||
));
|
||||
node = found.get(found.size() - 1);
|
||||
headings.index();
|
||||
}
|
||||
if (node != null) {
|
||||
node = node.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
for (TestBlock testblock : testblocks) {
|
||||
runTest(testblock);
|
||||
}
|
||||
}
|
||||
|
||||
private void runTest(TestBlock testblock) {
|
||||
|
||||
if (testblock.size() == 3) {
|
||||
String tuple = testblock.get(0).info.toString() + "->" + testblock.get(1).info + "->" + testblock.get(2).info;
|
||||
tuple = tuple.replaceAll("[^-a-zA-Z0-9<> _]", "");
|
||||
|
||||
System.out.println(testblock.get(0).getDesc());
|
||||
System.out.println(testblock.get(0).getLocationRef());
|
||||
|
||||
if (tuple.equals("yaml->json->ops")) {
|
||||
testBracket_YamlJsonOps(testblock);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Test block sized " + testblock.size() + " unrecognized by test loader.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Not thread-safe!
|
||||
*/
|
||||
private static class NodePredicates implements Predicate<Node>, Supplier<List<Node>> {
|
||||
final List<Predicate<Node>> predicates;
|
||||
final List<Node> found = new ArrayList<>();
|
||||
|
||||
public NodePredicates(Object... predicates) {
|
||||
this.predicates = Arrays.stream(predicates).map(NodePredicate::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Node node) {
|
||||
found.clear();
|
||||
|
||||
for (Predicate<Node> predicate : predicates) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (!predicate.test(node)) {
|
||||
return false;
|
||||
}
|
||||
found.add(node);
|
||||
|
||||
node = node.getNext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> get() {
|
||||
return List.copyOf(found);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not thread-safe!
|
||||
*/
|
||||
private static class NodePredicate implements Predicate<Node>, Supplier<Node> {
|
||||
private final Predicate<Node> predicate;
|
||||
private Node found = null;
|
||||
|
||||
public NodePredicate(Object o) {
|
||||
this.predicate = resolvePredicate(o);
|
||||
}
|
||||
|
||||
private Predicate<Node> resolvePredicate(Object object) {
|
||||
if (object instanceof Predicate) {
|
||||
return (Predicate<Node>) object;
|
||||
} else if (object instanceof Class) {
|
||||
return (n) -> object.equals(n.getClass());
|
||||
} else if (object instanceof Pattern) {
|
||||
return (n) -> ((Pattern) object).matcher(n.getChars()).matches();
|
||||
} else if (object instanceof CharSequence) {
|
||||
return (n) -> Pattern.compile(object.toString()).matcher(n.getChars()).matches();
|
||||
} else {
|
||||
throw new RuntimeException("no Node predicate for type " + object.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Node node) {
|
||||
this.found = null;
|
||||
boolean isFound = predicate.test(node);
|
||||
if (isFound) {
|
||||
this.found = node;
|
||||
}
|
||||
return isFound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node get() {
|
||||
return this.found;
|
||||
}
|
||||
}
|
||||
|
||||
private final static Predicate<Node> FORMATS = new Predicate<Node>() {
|
||||
|
||||
@Override
|
||||
public boolean test(Node node) {
|
||||
int lookahead = 6;
|
||||
List<Class<?>> types = new ArrayList<>(lookahead);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private void testBracket_YamlJsonOps(TestBlock block) {
|
||||
|
||||
validateYamlWithJson(
|
||||
block.get(0).getDesc(),
|
||||
block.get(0).text.toString(),
|
||||
block.get(1).text.toString(),
|
||||
block.get(1)
|
||||
);
|
||||
|
||||
validateYamlWithOpsModel(
|
||||
block.get(0).getDesc(),
|
||||
block.get(0).text.toString(),
|
||||
block.get(2).text.toString(),
|
||||
block.get(2)
|
||||
);
|
||||
}
|
||||
|
||||
private void validateYamlWithOpsModel(String desc, String yaml, String json, TestSet testref) {
|
||||
System.out.format("%-40s", "- checking yaml->ops");
|
||||
|
||||
JsonParser parser = new JsonParser();
|
||||
try {
|
||||
JsonElement elem = parser.parse(json);
|
||||
if (elem.isJsonArray()) {
|
||||
Type type = new TypeToken<List<Map<String, Object>>>() {
|
||||
}.getType();
|
||||
List<Map<String, Object>> expectedList = gson.fromJson(json, type);
|
||||
|
||||
StmtsDocList stmtsDocs = StatementsLoader.loadString(yaml, Map.of());
|
||||
List<OpTemplate> stmts = stmtsDocs.getStmts();
|
||||
List<Map<String, Object>> stmt_objs = stmts.stream().map(OpTemplate::asData).collect(Collectors.toList());
|
||||
|
||||
assertThat(stmt_objs).isEqualTo(expectedList);
|
||||
|
||||
}
|
||||
|
||||
System.out.println("OK");
|
||||
} catch (Exception e) {
|
||||
// System.out.println("Error while validating equivalence between the yaml and the rendered op context:");
|
||||
// System.out.println("yaml:");
|
||||
// System.out.println(yaml);
|
||||
// System.out.println("ops:");
|
||||
// System.out.println(json);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare one or more raw yaml docs to JSON5 representation of the same.
|
||||
* For clarity in the docs, a single object is allowed in the json5, in which case
|
||||
* an error is thrown if the yaml side contains more or less than 1 element.
|
||||
*
|
||||
* @param desc A moniker describing the test
|
||||
* @param yaml YAML describing a templated workload
|
||||
* @param json JSON describing a templated workload
|
||||
*/
|
||||
private void validateYamlWithJson(String desc, String yaml, String json, TestSet testset) {
|
||||
System.out.format("%-40s", "- checking yaml->json");
|
||||
|
||||
// StmtsDocList stmts = StatementsLoader.loadString(yaml);
|
||||
JsonParser parser = new JsonParser();
|
||||
|
||||
try {
|
||||
List<Map<String, Object>> docmaps = new RawYamlLoader().loadString(logger, yaml);
|
||||
JsonElement elem = JsonParser.parseString(json);
|
||||
if (elem.isJsonArray()) {
|
||||
Type type = new TypeToken<List<Map<String, Object>>>() {
|
||||
}.getType();
|
||||
List<Map<String, Object>> expectedList = gson.fromJson(json, type);
|
||||
assertThat(docmaps).isEqualTo(expectedList);
|
||||
System.out.println("OK");
|
||||
} else if (elem.isJsonObject()) {
|
||||
Map<String, Object> expectedSingle = gson.fromJson(json, Map.class);
|
||||
|
||||
compareEach(expectedSingle, docmaps.get(0));
|
||||
assertThat(docmaps.get(0)).isEqualTo(expectedSingle);
|
||||
if (docmaps.size() != 1) {
|
||||
throw new RuntimeException("comparator expected a single object, but found " + docmaps.size());
|
||||
}
|
||||
System.out.println("OK");
|
||||
} else {
|
||||
System.out.println("ERROR");
|
||||
throw new RuntimeException("unknown type in comparator: " + json);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("error while verifying model:\n");
|
||||
sb.append(" path: ").append(testset.getPath().toString()).append("\n");
|
||||
sb.append(" line: ").append(testset.getRefNode().getLineNumber()).append("\n");
|
||||
|
||||
logger.error(sb + ": " + e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void compareEach(List<Map<String, Object>> expected, List<Map<String, Object>> docmaps) {
|
||||
assertThat(docmaps).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void compareEach(Map<String, Object> structure, Map<String, Object> stringObjectMap) {
|
||||
assertThat(stringObjectMap).isEqualTo(structure);
|
||||
SpecTest specTester = SpecTest.builder()
|
||||
.path("target/classes/workload_definition/")
|
||||
.matchNodes(
|
||||
Pattern.compile("\\*(.*?)\\*\n?"), FencedCodeBlock.class, 0, 1, 0 ,1)
|
||||
.validators(new YamlSpecValidator())
|
||||
.build();
|
||||
specTester.run();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.engine.api.activityconfig.rawyaml;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
import io.nosqlbench.nb.spectest.loaders.STDefaultLoader;
|
||||
import io.nosqlbench.nb.spectest.testtypes.STNamedCodeTuples;
|
||||
import io.nosqlbench.nb.spectest.testtypes.STNodeReference;
|
||||
import io.nosqlbench.nb.spectest.types.STAssemblyValidator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* <P>This validator looks at a {@link STNodeAssembly} as a sequence of
|
||||
* 6 parsed nodes, interpreted as 3 2-tuples of name and content,
|
||||
* where the three tuples represent:
|
||||
* <OL>
|
||||
* <LI>An example op template in YAML form as provided by a user</LI>
|
||||
* <LI>An equivalent example op template in JSON form as provided by a user</LI>
|
||||
* <LI>The normalized op template datastructure as provided to the driver developer,
|
||||
* represented as a JSON schematic.</LI>
|
||||
* </OL>
|
||||
*
|
||||
* <P>These are checked for validity by first checking the first and second for data structure
|
||||
* equivalence, and then processing the first through the op template API and checking the
|
||||
* result with the third part.</P>
|
||||
*
|
||||
* <P>This validator is meant be used with {@link STNodeAssembly}s which are found by the
|
||||
* {@link STDefaultLoader} scanner type.</P>
|
||||
*
|
||||
*
|
||||
* </P>
|
||||
*/
|
||||
public class YamlSpecValidator implements STAssemblyValidator {
|
||||
private final static Logger logger = LogManager.getLogger(YamlSpecValidator.class);
|
||||
private final static Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
@Override
|
||||
public void validate(STNodeAssembly assembly) {
|
||||
runTest(assembly);
|
||||
}
|
||||
|
||||
private void runTest(STNodeAssembly testblock) {
|
||||
|
||||
if (testblock.size() == 6) {
|
||||
STNamedCodeTuples tuples = testblock.getAsNameAndCodeTuples();
|
||||
String name = tuples.getTypeSignature();
|
||||
|
||||
|
||||
System.out.println(testblock.get(0).getDesc());
|
||||
System.out.println(testblock.get(0).getLocationRef());
|
||||
|
||||
if (tuples.getTypeSignature().equals("yaml->json->ops")) {
|
||||
testBracket_YamlJsonOps(tuples);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Test block sized " + testblock.size() + " unrecognized by test loader.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void testBracket_YamlJsonOps(STNamedCodeTuples tuples) {
|
||||
|
||||
validateYamlWithJson(
|
||||
tuples.get(0).getName(),
|
||||
tuples.get(0).getData(),
|
||||
tuples.get(1).getName(),
|
||||
tuples.get(1)
|
||||
);
|
||||
|
||||
validateYamlWithOpsModel(
|
||||
tuples.get(1).getName(),
|
||||
tuples.get(1).getData(),
|
||||
tuples.get(2).getData(),
|
||||
tuples.get(2)
|
||||
);
|
||||
}
|
||||
|
||||
private void validateYamlWithOpsModel(String desc, String yaml, String json, STNodeReference testref) {
|
||||
System.out.format("%-40s", "- checking yaml->ops");
|
||||
|
||||
try {
|
||||
JsonElement elem = JsonParser.parseString(json);
|
||||
if (elem.isJsonArray()) {
|
||||
Type type = new TypeToken<List<Map<String, Object>>>() {
|
||||
}.getType();
|
||||
List<Map<String, Object>> expectedList = gson.fromJson(json, type);
|
||||
|
||||
StmtsDocList stmtsDocs = StatementsLoader.loadString(yaml, Map.of());
|
||||
List<OpTemplate> stmts = stmtsDocs.getStmts();
|
||||
List<Map<String, Object>> stmt_objs = stmts.stream().map(OpTemplate::asData).collect(Collectors.toList());
|
||||
|
||||
assertThat(stmt_objs).isEqualTo(expectedList);
|
||||
|
||||
}
|
||||
|
||||
System.out.println("OK");
|
||||
} catch (Exception e) {
|
||||
// System.out.println("Error while validating equivalence between the yaml and the rendered op context:");
|
||||
// System.out.println("yaml:");
|
||||
// System.out.println(yaml);
|
||||
// System.out.println("ops:");
|
||||
// System.out.println(json);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare one or more raw yaml docs to JSON5 representation of the same.
|
||||
* For clarity in the docs, a single object is allowed in the json5, in which case
|
||||
* an error is thrown if the yaml side contains more or less than 1 element.
|
||||
*
|
||||
* @param desc A moniker describing the test
|
||||
* @param yaml YAML describing a templated workload
|
||||
* @param json JSON describing a templated workload
|
||||
*/
|
||||
private void validateYamlWithJson(String desc, String yaml, String json, STNodeReference testset) {
|
||||
System.out.format("%-40s", "- checking yaml->json");
|
||||
|
||||
// StmtsDocList stmts = StatementsLoader.loadString(yaml);
|
||||
|
||||
try {
|
||||
List<Map<String, Object>> docmaps = new RawYamlLoader().loadString(logger, yaml);
|
||||
JsonElement elem = JsonParser.parseString(json);
|
||||
if (elem.isJsonArray()) {
|
||||
Type type = new TypeToken<List<Map<String, Object>>>() {
|
||||
}.getType();
|
||||
List<Map<String, Object>> expectedList = gson.fromJson(json, type);
|
||||
assertThat(docmaps).isEqualTo(expectedList);
|
||||
System.out.println("OK");
|
||||
} else if (elem.isJsonObject()) {
|
||||
Map<String, Object> expectedSingle = gson.fromJson(json, Map.class);
|
||||
|
||||
compareEach(expectedSingle, docmaps.get(0));
|
||||
assertThat(docmaps.get(0)).isEqualTo(expectedSingle);
|
||||
if (docmaps.size() != 1) {
|
||||
throw new RuntimeException("comparator expected a single object, but found " + docmaps.size());
|
||||
}
|
||||
System.out.println("OK");
|
||||
} else {
|
||||
System.out.println("ERROR");
|
||||
throw new RuntimeException("unknown type in comparator: " + json);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("error while verifying model:\n");
|
||||
sb.append(" path: ").append(testset.getPath().toString()).append("\n");
|
||||
sb.append(" line: ").append(testset.getLineNumber()).append("\n");
|
||||
|
||||
logger.error(sb + ": " + e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void compareEach(List<Map<String, Object>> expected, List<Map<String, Object>> docmaps) {
|
||||
assertThat(docmaps).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void compareEach(Map<String, Object> structure, Map<String, Object> stringObjectMap) {
|
||||
assertThat(stringObjectMap).isEqualTo(structure);
|
||||
}
|
||||
|
||||
}
|
80
nb-spectest/pom.xml
Normal file
80
nb-spectest/pom.xml
Normal file
@ -0,0 +1,80 @@
|
||||
<!--
|
||||
~ Copyright (c) 2022 nosqlbench
|
||||
~
|
||||
~ Licensed 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>mvn-defaults</artifactId>
|
||||
<groupId>io.nosqlbench</groupId>
|
||||
<version>4.17.15-SNAPSHOT</version>
|
||||
<relativePath>../mvn-defaults</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>nb-spectest</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
This module provides the ability to run tests based on tests as specification,
|
||||
using markdown as a wrapper format. This allows for a type of literate exposition
|
||||
of various tests in a form that can be used as markdown documentation.
|
||||
With this, tests, examples, specifications, and documentation can all be one
|
||||
and the same.
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
<artifactId>flexmark-ext-yaml-front-matter</artifactId>
|
||||
<version>0.62.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
<artifactId>flexmark-html2md-converter</artifactId>
|
||||
<version>0.62.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<!-- perf testing -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<excludes>
|
||||
<exclude>docs-for-testing-only/**</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.core;
|
||||
|
||||
import io.nosqlbench.nb.spectest.testtypes.STNodeReference;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class STNameAndCodeTuple implements STNodeReference {
|
||||
|
||||
private final STNode nameNode;
|
||||
private final STNode dataNode;
|
||||
public STNameAndCodeTuple(STNode nameNode, STNode dataNode) {
|
||||
this.nameNode = nameNode;
|
||||
this.dataNode = dataNode;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return nameNode.getDesc();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return nameNode.text.toString();
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return dataNode.text.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath() {
|
||||
return dataNode.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineNumber() {
|
||||
return dataNode.getLine();
|
||||
}
|
||||
}
|
@ -14,15 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.engine.api.activityconfig.rawyaml;
|
||||
package io.nosqlbench.nb.spectest.core;
|
||||
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
final class TestSet {
|
||||
/**
|
||||
* A {@link STNode} is a set of metadata that describes a test target from a
|
||||
* test specification file, in terms of {@link Node} sequences and context.
|
||||
* It contains naming and locating information as well as the document nodes containing
|
||||
* the target element.
|
||||
*/
|
||||
public final class STNode {
|
||||
|
||||
private final String description;
|
||||
private final Path path;
|
||||
private final int line;
|
||||
@ -30,9 +38,8 @@ final class TestSet {
|
||||
public CharSequence info;
|
||||
public CharSequence text;
|
||||
|
||||
public TestSet(Supplier<CharSequence> desc, Node infoNode, Node dataNode, Path path) {
|
||||
public STNode(Supplier<CharSequence> desc, Node dataNode, Path path) {
|
||||
this.description = desc.get().toString();
|
||||
this.info = infoNode.getChars();
|
||||
this.text = dataNode.getFirstChild().getChars();
|
||||
this.line = dataNode.getFirstChild().getLineNumber();
|
||||
this.path = path;
|
||||
@ -58,7 +65,7 @@ final class TestSet {
|
||||
/**
|
||||
* Provide the logical path of the file being examined in this test set.
|
||||
* If the system properties indicate that the test is being run from within intellij,
|
||||
* the path will be relatized from the next module level up to allow for hot linking
|
||||
* the path will be relativized from the next module level up to allow for hot linking
|
||||
* directly to files.
|
||||
* @return A useful relative path to the file being tested
|
||||
*/
|
||||
@ -77,4 +84,30 @@ final class TestSet {
|
||||
public String toString() {
|
||||
return this.getDesc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
STNode that = (STNode) o;
|
||||
|
||||
if (line != that.line) return false;
|
||||
if (!Objects.equals(description, that.description)) return false;
|
||||
if (!Objects.equals(path, that.path)) return false;
|
||||
if (!Objects.equals(refnode, that.refnode)) return false;
|
||||
if (!Objects.equals(info, that.info)) return false;
|
||||
return Objects.equals(text, that.text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = description != null ? description.hashCode() : 0;
|
||||
result = 31 * result + (path != null ? path.hashCode() : 0);
|
||||
result = 31 * result + line;
|
||||
result = 31 * result + (refnode != null ? refnode.hashCode() : 0);
|
||||
result = 31 * result + (info != null ? info.hashCode() : 0);
|
||||
result = 31 * result + (text != null ? text.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.core;
|
||||
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import io.nosqlbench.nb.spectest.testtypes.STNamedCodeTuples;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <P>A {@link STNodeAssembly} is a sequence of {@link STNode}s. This is meant to
|
||||
* group sets of test data together into a single cohesive specification which
|
||||
* contains its own set of documentation, examples, and full-content validation.
|
||||
* As such, the markdown which provides the specification should be usable directly
|
||||
* as detailed user-facing documentation.</P>
|
||||
*
|
||||
* <P>The method of discovery, assembly, and validation of a {@link STNodeAssembly} is
|
||||
* determined by the testing harness assembled around it. In any case, the type of
|
||||
* presentation structure and validation logic that accompanies a given assembly
|
||||
* should be self-evident, and documented clearly within the surrounding
|
||||
* markdown.</P>
|
||||
*/
|
||||
public class STNodeAssembly extends ArrayList<STNode> {
|
||||
|
||||
public STNodeAssembly(STNode... testsets) {
|
||||
this.addAll(Arrays.asList(testsets));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (size()>0 ? get(0).toString() : "NONE");
|
||||
}
|
||||
|
||||
public STNamedCodeTuples getAsNameAndCodeTuples() {
|
||||
if ((size()%2)==0) {
|
||||
List<STNameAndCodeTuple> tuples = new ArrayList<>();
|
||||
for (int i = 0; i < size(); i+=2) {
|
||||
tuples.add(new STNameAndCodeTuple(get(i),get(i+1)));
|
||||
}
|
||||
return new STNamedCodeTuples(tuples);
|
||||
} else {
|
||||
throw new InvalidParameterException("Impossible with an odd number of elements.");
|
||||
}
|
||||
}
|
||||
|
||||
public String getDescription(int index) {
|
||||
assertRange(index);
|
||||
return get(index).getDesc();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
if (size()==0) {
|
||||
return "";
|
||||
}
|
||||
return get(0).getDesc();
|
||||
}
|
||||
|
||||
public String getAsText(int index) {
|
||||
assertRange(index);
|
||||
return get(index).text.toString();
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
if (size()==0) {
|
||||
return null;
|
||||
}
|
||||
return get(0).getPath();
|
||||
}
|
||||
|
||||
public Node getRefNode(int index) {
|
||||
assertRange(index);
|
||||
return get(index).getRefNode();
|
||||
}
|
||||
|
||||
private void assertRange(int index) {
|
||||
if (size()-1<index) {
|
||||
throw new InvalidParameterException("index " + index + " was out of bounds. Your pattern matching and access patterns to this data are not in sync.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.core;
|
||||
|
||||
import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import io.nosqlbench.nb.spectest.loaders.STFileScanner;
|
||||
import io.nosqlbench.nb.spectest.types.STAssemblyValidator;
|
||||
import io.nosqlbench.nb.spectest.types.STBuilderFacets;
|
||||
import io.nosqlbench.nb.spectest.types.STPathLoader;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This is the entry point into a specification test. By default, all files
|
||||
* within the provided path are checked for the scanner patterns,
|
||||
* and all validators are run. This means:
|
||||
*
|
||||
* <OL>
|
||||
* <LI>All provided paths which are markdown files will be scanned.</LI>
|
||||
* <LI>All provided paths which are directories will be recursively scanned for contained markdown files.</LI>
|
||||
* <LI>Every included markdown file will be scanned for matching element sequences.</LI>
|
||||
* <LI>Every matching element sequence will be validated.</LI>
|
||||
* </OL>
|
||||
*
|
||||
* <p>The details of each of these steps should be documented in the specific classes which implement
|
||||
* the behavior. At a high level:
|
||||
*
|
||||
* Key concepts are:
|
||||
* <UL>
|
||||
* <LI>Specification Files - These are the markdown files and included sets of specification tuples.
|
||||
* Specification tuples are sequences of content elements which are recognizable by a scanner
|
||||
* as going together to form a multi-part specification and test.</LI>
|
||||
* <LI>Scanners - A scanner is a pattern matcher specification, in the form of a set of predicate objects.
|
||||
* These are tested against sequences of elements that are found within specification files. Each
|
||||
* time a scanner matches a sequence of elements, a specification template is extracted and submitted
|
||||
* to a set of validators.</LI>
|
||||
* <LI>Specification Template - This is merely a set of content elements which represents a specific
|
||||
* specification or example. When a specification template is found, it is extracted with some context
|
||||
* metadata that makes it easy to identify and report (like the heading), as well as the content needed
|
||||
* for a specification validator to confirm whether or not it passes a test for validity.</LI>
|
||||
* <LI>Validators - A validator knows how to assert the validity of a specification template. It is
|
||||
* essentially a set of test assertions against a specification template. There are default validators
|
||||
* which do different types of validation based on the content type.</LI>
|
||||
* </UL>
|
||||
*
|
||||
* Effectively, all specifications are described prosaically within the markdown, while the actual specifications
|
||||
* which are meant to be validated are captured within fenced code sections. The type of fenced code section
|
||||
* determines how the inner content is interpreted and validated.
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public class SpecTest implements Runnable {
|
||||
|
||||
private static final Parser parser = Parser.builder().extensions(List.of(YamlFrontMatterExtension.create())).build();
|
||||
private final List<Path> paths;
|
||||
private final List<STPathLoader> pathLoaders;
|
||||
private final List<STAssemblyValidator> validators;
|
||||
|
||||
private SpecTest(List<Path> paths, List<STPathLoader> pathLoaders, List<STAssemblyValidator> validators) {
|
||||
this.paths = paths;
|
||||
this.pathLoaders = pathLoaders;
|
||||
this.validators = validators;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Set<STNodeAssembly> testables = new LinkedHashSet<>();
|
||||
|
||||
for (Path path : paths) {
|
||||
List<Path> matchingPaths = STFileScanner.findMatching(".*\\.md", paths.toArray(new Path[0]));
|
||||
for (Path matchingPath : matchingPaths) {
|
||||
// STNodeScanner scanner = new STDefaultNodeScanner();
|
||||
for (STPathLoader pathLoader : pathLoaders) {
|
||||
|
||||
List<STNodeAssembly> testableAssemblies = pathLoader.apply(matchingPath);
|
||||
for (STNodeAssembly assembly : testableAssemblies) {
|
||||
if (testables.contains(assembly)) {
|
||||
throw new RuntimeException("Found duplicate testable assembly in path set:\n" +
|
||||
"assembly: " + assembly.toString());
|
||||
}
|
||||
testables.add(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (STNodeAssembly assembly : testables) {
|
||||
for (STAssemblyValidator validator : validators) {
|
||||
validator.validate(assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static STBuilderFacets.WantsPaths builder() {
|
||||
return new SpecTest.Builder();
|
||||
}
|
||||
|
||||
private static class Builder extends SpecTestBuilder {
|
||||
@Override
|
||||
public SpecTest build() {
|
||||
return new SpecTest(paths,scanners,validators);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.core;
|
||||
|
||||
import io.nosqlbench.nb.spectest.loaders.STDefaultLoader;
|
||||
import io.nosqlbench.nb.spectest.loaders.STNodePredicate;
|
||||
import io.nosqlbench.nb.spectest.types.STAssemblyValidator;
|
||||
import io.nosqlbench.nb.spectest.types.STBuilderFacets;
|
||||
import io.nosqlbench.nb.spectest.types.STPathLoader;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SpecTestBuilder implements STBuilderFacets.All {
|
||||
|
||||
protected List<Path> paths = new ArrayList<>();
|
||||
protected List<STPathLoader> scanners = new ArrayList<>();
|
||||
protected List<STAssemblyValidator> validators = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public STBuilderFacets.WantsPathsOrScannersOrValidators paths(Path... paths) {
|
||||
this.paths.addAll(Arrays.asList(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STBuilderFacets.WantsValidatorsOrDone scanners(STPathLoader... scanners) {
|
||||
this.scanners.addAll(Arrays.asList(scanners));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STBuilderFacets.WantsValidatorsOrDone scanners(STNodePredicate predicate) {
|
||||
return this.matchNodes(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public STBuilderFacets.WantsValidatorsOrDone matchNodes(Object... predicates) {
|
||||
return this.scanners(new STDefaultLoader(predicates));
|
||||
}
|
||||
|
||||
@Override
|
||||
public STBuilderFacets.Done validators(STAssemblyValidator... validators) {
|
||||
this.validators.addAll(Arrays.asList(validators));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.loaders;
|
||||
|
||||
import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
import io.nosqlbench.nb.spectest.types.STAssemblyValidator;
|
||||
import io.nosqlbench.nb.spectest.types.STNodeLoader;
|
||||
import io.nosqlbench.nb.spectest.types.STPathLoader;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <P>This scanner looks for testable specifications which are matched as a sequence of
|
||||
* 3 2-tuples, described in {@link YamlSpecValidator}. The required markdown structure for
|
||||
* this looks like:
|
||||
* <PRE>{@code
|
||||
* ## call form with defaults
|
||||
*
|
||||
* *yaml:*
|
||||
* ```yaml
|
||||
* name: TEMPLATE(myname,thedefault)
|
||||
* ```
|
||||
*
|
||||
* *json:*
|
||||
* ```json5
|
||||
* {
|
||||
* "name": "thedefault"
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* *ops:*
|
||||
* ```json5
|
||||
* []
|
||||
* ```
|
||||
* }</PRE>
|
||||
* </P>
|
||||
*
|
||||
* <P>Specifically, the emphasis and colon paragraph blocks indicate the naming of the elements, and the
|
||||
* fenced code sections represent the content. The name elements are not matched for the name specifically,
|
||||
* although the {@link STAssemblyValidator} which consumes these {@link STNodeAssembly}s will interpret them
|
||||
* as described above.
|
||||
* </P>
|
||||
*
|
||||
*/
|
||||
public class STDefaultLoader implements STPathLoader {
|
||||
private final STNodePredicates predicates;
|
||||
private static final Parser parser = Parser.builder().extensions(List.of(YamlFrontMatterExtension.create())).build();
|
||||
|
||||
public STDefaultLoader(Object... predicates) {
|
||||
|
||||
if (predicates.length==0) {
|
||||
throw new InvalidParameterException("An empty spec scanner is invalid.");
|
||||
}
|
||||
if ((predicates.length % 2) != 0) {
|
||||
throw new InvalidParameterException("You can only provide predicates in sequences of 2-tuples, where" +
|
||||
"each even index is a naming element and each odd index is the associated test content. " +
|
||||
"But " + predicates.length + " were provided: " + Arrays.toString(predicates));
|
||||
}
|
||||
this.predicates = new STNodePredicates(predicates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<STNodeAssembly> apply(Path specPath) {
|
||||
List<STNodeAssembly> assemblies = new ArrayList<>();
|
||||
List<Path> matchingPaths = STFileScanner.findMatching(specPath);
|
||||
STNodeLoader nodeLoader = new STDefaultNodeLoader(predicates);
|
||||
|
||||
for (Path matchingPath : matchingPaths) {
|
||||
List<STNodeAssembly> found = nodeLoader.apply(matchingPath, null);
|
||||
assemblies.addAll(found);
|
||||
}
|
||||
|
||||
|
||||
return assemblies;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.loaders;
|
||||
|
||||
import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.ast.Document;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import io.nosqlbench.nb.spectest.core.STNode;
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
import io.nosqlbench.nb.spectest.types.STNodeLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class STDefaultNodeLoader implements STNodeLoader {
|
||||
|
||||
private final STNodePredicates predicates;
|
||||
private final Parser parser = Parser.builder().extensions(List.of(YamlFrontMatterExtension.create())).build();
|
||||
|
||||
|
||||
public STDefaultNodeLoader(Object... predicates) {
|
||||
// if (predicates.length==0) {
|
||||
// throw new InvalidParameterException("An empty spec scanner is invalid.");
|
||||
// }
|
||||
// if ((predicates.length % 2) != 0) {
|
||||
// throw new InvalidParameterException("You can only provide predicates in sequences of 2-tuples, where" +
|
||||
// "each even index is a naming element and each odd index is the associated test content. " +
|
||||
// "But " + predicates.length + " were provided: " + Arrays.toString(predicates));
|
||||
// }
|
||||
this.predicates = new STNodePredicates(predicates);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<STNodeAssembly> apply(Path path, Node node) {
|
||||
List<STNodeAssembly> assemblies = new ArrayList<>();
|
||||
|
||||
if (node==null) {
|
||||
if (path==null) {
|
||||
throw new InvalidParameterException("You must provide at least a path.");
|
||||
}
|
||||
try {
|
||||
String input = Files.readString(path);
|
||||
node = parser.parse(input);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
if (node instanceof Document d) {
|
||||
node=d.getFirstChild();
|
||||
}
|
||||
|
||||
STHeadingScanner headings = new STHeadingScanner(" > ");
|
||||
headings.update(path); // null is allowed here
|
||||
|
||||
while (node != null) {
|
||||
headings.update(node);
|
||||
Optional<List<Node>> optionalStanza = predicates.apply(node);
|
||||
|
||||
if (optionalStanza.isPresent()) {
|
||||
List<Node> found = optionalStanza.get();
|
||||
List<STNode> stnodes = found.stream().map(n -> new STNode(headings, n, path)).toList();
|
||||
STNodeAssembly testassy = new STNodeAssembly(stnodes.toArray(new STNode[0]));
|
||||
node = found.get(found.size() - 1);
|
||||
headings.index();
|
||||
assemblies.add(testassy);
|
||||
}
|
||||
if (node != null) {
|
||||
node = node.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
return assemblies;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.loaders;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class STFileScanner {
|
||||
public static List<Path> findMatching(Path... paths) {
|
||||
return findMatching(".*\\.md",paths);
|
||||
}
|
||||
public static List<Path> findMatching(String regex, Path... paths) {
|
||||
List<Path> found = new ArrayList<>();
|
||||
return findMatching(found, Pattern.compile(regex), paths);
|
||||
}
|
||||
|
||||
private static List<Path> findMatching(List<Path> accumulator, Pattern regex, Path... paths) {
|
||||
|
||||
for (Path path : paths) {
|
||||
if (Files.isDirectory(path)) {
|
||||
DirectoryStream<Path> dirdata;
|
||||
try {
|
||||
dirdata = Files.newDirectoryStream(path);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error while scanning for specifications: " + e,e);
|
||||
}
|
||||
for (Path dirdatum : dirdata) {
|
||||
findMatching(accumulator,regex,dirdatum);
|
||||
}
|
||||
} else if (Files.isReadable(path)) {
|
||||
if (regex.matcher(path.getFileName().toString()).matches()) {
|
||||
accumulator.add(path);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("unreadable path: '" + path + "'");
|
||||
}
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.engine.api.activityconfig.rawyaml;
|
||||
package io.nosqlbench.nb.spectest.loaders;
|
||||
|
||||
import com.vladsch.flexmark.ast.Heading;
|
||||
|
||||
@ -23,13 +23,13 @@ import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class HeadingScanner implements Supplier<CharSequence> {
|
||||
public class STHeadingScanner implements Supplier<CharSequence> {
|
||||
private final LinkedList<Heading> tnodes = new LinkedList<>();
|
||||
private Path path;
|
||||
private int index;
|
||||
private final String delimiter;
|
||||
|
||||
public HeadingScanner(String delimiter) {
|
||||
public STHeadingScanner(String delimiter) {
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
@ -56,10 +56,11 @@ public class HeadingScanner implements Supplier<CharSequence> {
|
||||
}
|
||||
|
||||
/**
|
||||
* If this method is called, then the heading is presented as
|
||||
* If this method is called, then the heading index is incremented to track
|
||||
* enumerations (represented as numeric indices) of headings within a common flow of elements
|
||||
* @return
|
||||
*/
|
||||
public HeadingScanner index() {
|
||||
public STHeadingScanner index() {
|
||||
index++;
|
||||
return this;
|
||||
}
|
||||
@ -74,8 +75,9 @@ public class HeadingScanner implements Supplier<CharSequence> {
|
||||
* @param object Any object which might be a Heading or Path
|
||||
* @return this HeadingScanner for method chaining
|
||||
*/
|
||||
public HeadingScanner update(Object object) {
|
||||
public STHeadingScanner update(Object object) {
|
||||
if (object==null ) {
|
||||
updatePath(null);
|
||||
} else if (object instanceof Path) {
|
||||
updatePath((Path)object);
|
||||
} else if (object instanceof Heading) {
|
||||
@ -114,4 +116,5 @@ public class HeadingScanner implements Supplier<CharSequence> {
|
||||
public CharSequence get() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.loaders;
|
||||
|
||||
import com.vladsch.flexmark.ast.Paragraph;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import com.vladsch.flexmark.util.sequence.BasedSequence;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <P>Construct a sequence of {com.vladsch.flexmark.util.ast.Node} matchers using
|
||||
* one of a few common predicate forms, inferred from the types of the predicate
|
||||
* formats provided.</P>
|
||||
*
|
||||
* <P>The available forms are:
|
||||
* <OL>
|
||||
* <LI>A fully constructed {@link Predicate} of {@link Node}. When provided,
|
||||
* this predicate is automatically checked against dummy data to ensure
|
||||
* the generic signature is valid.</LI>
|
||||
* <LI>A {@link Class} of any type of {@link Node}, which asserts that the
|
||||
* node's class is equal to that specified. (No type hierarchy checks are allowed.)</LI>
|
||||
* <LI>A fully compiled {@link Pattern}, which checks the textual content of
|
||||
* the node for a match.</LI>
|
||||
* <LI>A string, which is automatically promoted to a pattern as above, and checked
|
||||
* in the same way. By default, patterns provided this way are automatically
|
||||
* prefixed and suffixed with '.*' unless a regex character is found there. Also,
|
||||
* all matches are created with Pattern.MULTILINE and Pattern.DOTALL.
|
||||
* </LI>
|
||||
* </OL>
|
||||
* </P>
|
||||
*/
|
||||
public class STNodePredicate implements Predicate<Node>, Supplier<Node> {
|
||||
private final Predicate<Node> predicate;
|
||||
private Node found = null;
|
||||
|
||||
public STNodePredicate(Object o) {
|
||||
this.predicate = resolvePredicate(o);
|
||||
}
|
||||
|
||||
private Predicate<Node> resolvePredicate(Object object) {
|
||||
if (object instanceof Predicate predicate) {
|
||||
Paragraph paragraph = new Paragraph(BasedSequence.of("test paragraph"));
|
||||
predicate.test(paragraph);
|
||||
return predicate;
|
||||
} else if (object instanceof Class c) {
|
||||
return new ClassPredicate(c);
|
||||
} else if (object instanceof Pattern p) {
|
||||
return new PatternPredicate(p);
|
||||
} else if (object instanceof CharSequence cs) {
|
||||
return new PatternPredicate(cs.toString());
|
||||
} else {
|
||||
throw new RuntimeException("no Node predicate for type " + object.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
private final static class ClassPredicate implements Predicate<Node> {
|
||||
private final Class<? extends Node> matchingClass;
|
||||
|
||||
public ClassPredicate(Class<? extends Node> matchingClass) {
|
||||
this.matchingClass = matchingClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Node node) {
|
||||
Class<? extends Node> classToMatch = node.getClass();
|
||||
boolean matches = matchingClass.equals(classToMatch);
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
private final static class PatternPredicate implements Predicate<Node> {
|
||||
private final Pattern pattern;
|
||||
|
||||
private PatternPredicate(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
private PatternPredicate(String pattern) {
|
||||
String newPattern = (pattern.matches("^[a-zA-Z0-9]") ? ".*" : "") + pattern + (pattern.matches("[a-zA-Z0-9]$") ? ".*" : "");
|
||||
Pattern compiled = Pattern.compile(newPattern, Pattern.MULTILINE | Pattern.DOTALL);
|
||||
this.pattern = compiled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Node node) {
|
||||
BasedSequence chars = node.getChars();
|
||||
Matcher matcher = pattern.matcher(chars);
|
||||
boolean matches = matcher.matches();
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Node node) {
|
||||
this.found = null;
|
||||
boolean isFound = predicate.test(node);
|
||||
if (isFound) {
|
||||
this.found = node;
|
||||
}
|
||||
return isFound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node get() {
|
||||
return this.found;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.loaders;
|
||||
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* <P>{@link STNodePredicates} is a convenient wrapper around {@link STNodePredicate}
|
||||
* evaluation so that creating a {@link Node} scanner is a one-liner.
|
||||
* Any of the supported types for {@link STNodePredicate} are valid, in addition
|
||||
* to numbers which reference previous positions in the argument list (zero-indexed.)</P>
|
||||
*
|
||||
* <p>This allows for existing node matchers to be re-used by reference. For Example,
|
||||
* a specification like <pre>{@code new NodePredicates("__\\w__",0,0)}</pre> represents
|
||||
* a regular expression which would match "__a__" or "__b__" and then two more subsequent
|
||||
* matches against similar content, for a total of 3 consecutive matching nodes.</p>
|
||||
*/
|
||||
public class STNodePredicates implements Function<Node, Optional<List<Node>>> {
|
||||
final List<Predicate<Node>> predicates;
|
||||
final List<Node> found = new ArrayList<>();
|
||||
|
||||
public STNodePredicates(STNodePredicates predicates) {
|
||||
this.predicates = predicates.predicates;
|
||||
}
|
||||
|
||||
public STNodePredicates(Object... predicateSpecs) {
|
||||
this.predicates = new ArrayList<Predicate<Node>>(predicateSpecs.length);
|
||||
|
||||
for (int i = 0; i < predicateSpecs.length; i++) {
|
||||
if (predicateSpecs[i] instanceof STNodePredicates pspecs) {
|
||||
predicates.addAll(pspecs.predicates);
|
||||
} else if (predicateSpecs[i] instanceof Number number) {
|
||||
if (i > number.intValue()) {
|
||||
predicates.add(predicates.get(number.intValue()));
|
||||
} else {
|
||||
throw new InvalidParameterException("predicate reference at " + i + " references invalid position at " + number.intValue());
|
||||
}
|
||||
} else {
|
||||
predicates.add(new STNodePredicate(predicateSpecs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<Node>> apply(Node node) {
|
||||
if (test(node)) {
|
||||
return Optional.of(List.copyOf(found));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean test(Node node) {
|
||||
found.clear();
|
||||
|
||||
for (Predicate<Node> predicate : predicates) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (!predicate.test(node)) {
|
||||
return false;
|
||||
}
|
||||
found.add(node);
|
||||
|
||||
node = node.getNext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <P>This package and module provides a set of modular testing tools which enable
|
||||
* specifications, documentation, and tests to be one and the same. This is a form
|
||||
* of literate programming, but more focused on providing a clearly documented
|
||||
* and tested specification, including detailed examples and corner cases.</P>
|
||||
*
|
||||
* <H2>Motivation</H2>
|
||||
* <P>There is a common problem in many OSS projects and code bases which has no
|
||||
* simple and definitive solution so far: Many APIs, type systems, protocols, and other
|
||||
* foundational design elements which are the bedrock of stable systems are themselves
|
||||
* unstable in practice. While it is true that this has much to do with management
|
||||
* style and project hygiene, and thus varies wildly from project to project, it is
|
||||
* still true that there exists no generally accepted method of ensconcing those
|
||||
* for users in a way that remains correct, provable, and current. In other words,
|
||||
* identifying the foundational elements of a system which should be held up as
|
||||
* key concepts and so reliable that they can be taken for granted is crucial to
|
||||
* the longevity and utility of any non-trivial OSS project. This module and package
|
||||
* are simply a support system to enable some degree of that within the NoSQLBench
|
||||
* project.</P>
|
||||
*
|
||||
* <H2>Concepts</H2>
|
||||
* <p>
|
||||
* Specifications to be used with this system are expected to be woven into a set
|
||||
* of user-appropriate markdown documentation (or other type of documentation system,
|
||||
* if you have an need for another, jump in and help!). Any documentation form is
|
||||
* allowed as long as there is some regular structure within the documentation that can be
|
||||
* used to extract stanzas of specification. These stanzas will generally be a sequence
|
||||
* of parsed nodes which fit a sequence pattern. During test phases, provided paths
|
||||
* are scanned for matching markdown content, that content is scanned for specific
|
||||
* node patterns, and those sequences are extracted into self-contained test definitions,
|
||||
* known as {@link io.nosqlbench.nb.spectest.core.STNodeAssembly}. Since all of the wiring
|
||||
* for these tests operates on the AST of a parsed markdown file, the
|
||||
* {@link com.vladsch.flexmark.util.ast.Node} nomenclature is retained in all the class names.
|
||||
* Those test definitions are then fed to the validators which are provided in test code.
|
||||
* </p>
|
||||
*
|
||||
* <H2>Validation Methods</H2>
|
||||
* <P>All of the validations are meant to check the correctness of a type of operation. This can be some
|
||||
* thing as simple as loading a data structure into memory and verifying that it is equal to a known value. It is important
|
||||
* to bear in mind that no specification for a system stands alone. The whole point of having the
|
||||
* specification is to capture, at an implementable, testable, mechanical level of detail, how
|
||||
* the system in question should behave. More specifically, the specification is meant to say what
|
||||
* the system should do with inputs of any kind, and how you can know that it is doing that faithfully.
|
||||
* As such, you should endeavor to capture these behaviors within your spec, such that the stanzas
|
||||
* hold enough information to actually exercise parts of a system which can prove or disprove it's
|
||||
* conformance to a specification. Any tests which do not do this are merely documenting a format and
|
||||
* are of little use above and beyond documentation.</p>
|
||||
*
|
||||
* <H3>Fenced Data</H3>
|
||||
* <p>Generally speaking, you will provide fenced code within your documentation that can be presented
|
||||
* in a friendly format to readers of your documentation system. These fenced code sections are the
|
||||
* easiest place to put data which needs to be verified. Within a stanza, adjacent fenced code sections
|
||||
* can represent testable tuples, like (program, output), or (input, program, output). It is up
|
||||
* to you to decide what the structure should be. Further, it is useful to look at the fenced code
|
||||
* </p>
|
||||
*
|
||||
* <H3>Output Assertions</H3>
|
||||
*/
|
||||
package io.nosqlbench.nb.spectest;
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.testtypes;
|
||||
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A {@link STNameCodeTuple} is a set of metadata that describes a test target from a
|
||||
* test specification file, in terms of {@link Node} sequences and context.
|
||||
* It contains naming and locating information as well as the document nodes containing
|
||||
* the target element.
|
||||
*/
|
||||
public final class STNameCodeTuple {
|
||||
|
||||
private final String description;
|
||||
private final Path path;
|
||||
private final int line;
|
||||
private final Node refnode;
|
||||
public CharSequence info;
|
||||
public CharSequence text;
|
||||
|
||||
public STNameCodeTuple(Supplier<CharSequence> desc, Node infoNode, Node dataNode, Path path) {
|
||||
this.description = desc.get().toString();
|
||||
this.info = infoNode.getChars();
|
||||
this.text = dataNode.getFirstChild().getChars();
|
||||
this.line = dataNode.getFirstChild().getLineNumber();
|
||||
this.path = path;
|
||||
this.refnode = dataNode;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public Node getRefNode() {
|
||||
return refnode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the logical path of the file being examined in this test set.
|
||||
* If the system properties indicate that the test is being run from within intellij,
|
||||
* the path will be relativized from the next module level up to allow for hot linking
|
||||
* directly to files.
|
||||
* @return A useful relative path to the file being tested
|
||||
*/
|
||||
public String getLocationRef() {
|
||||
boolean inij = System.getProperty("sun.java.command","").toLowerCase(Locale.ROOT).contains("intellij");
|
||||
Path vcwd = Path.of(".").toAbsolutePath().normalize();
|
||||
vcwd = inij ? vcwd.getParent().normalize() : vcwd;
|
||||
Path relpath = vcwd.relativize(this.path.toAbsolutePath());
|
||||
if (inij) {
|
||||
relpath = Path.of(relpath.toString().replace("target/classes/","src/main/resources/"));
|
||||
}
|
||||
return "\t at (" + relpath + ":" + this.getLine() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getDesc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
STNameCodeTuple that = (STNameCodeTuple) o;
|
||||
|
||||
if (line != that.line) return false;
|
||||
if (!Objects.equals(description, that.description)) return false;
|
||||
if (!Objects.equals(path, that.path)) return false;
|
||||
if (!Objects.equals(refnode, that.refnode)) return false;
|
||||
if (!Objects.equals(info, that.info)) return false;
|
||||
return Objects.equals(text, that.text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = description != null ? description.hashCode() : 0;
|
||||
result = 31 * result + (path != null ? path.hashCode() : 0);
|
||||
result = 31 * result + line;
|
||||
result = 31 * result + (refnode != null ? refnode.hashCode() : 0);
|
||||
result = 31 * result + (info != null ? info.hashCode() : 0);
|
||||
result = 31 * result + (text != null ? text.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.testtypes;
|
||||
|
||||
import io.nosqlbench.nb.spectest.core.STNameAndCodeTuple;
|
||||
import io.nosqlbench.nb.spectest.core.STNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a view over an {@link io.nosqlbench.nb.spectest.core.STNodeAssembly},
|
||||
* which is merely backed by a {@link List} of {@link STNode}s. This view, however,
|
||||
* imposes a structure over the nodes which is name, data, name, data, ... and so on.
|
||||
* This is a convenient way to consume and validate Node data which follows the form:
|
||||
* <pre>{@code
|
||||
* *name1:*
|
||||
* ```yaml
|
||||
* document-property: version1
|
||||
* ```
|
||||
* *name2:*
|
||||
* ```yaml
|
||||
* document-property: version2
|
||||
* ```
|
||||
* *name3:*
|
||||
* ```yaml
|
||||
* document-property: version3
|
||||
* ```
|
||||
* }</pre>
|
||||
*
|
||||
* In this example, there are six consecutive nodes which contain 3 names and three fenced code sections.
|
||||
*/
|
||||
public class STNamedCodeTuples {
|
||||
|
||||
private final List<STNameAndCodeTuple> tuples;
|
||||
|
||||
public STNamedCodeTuples(List<STNameAndCodeTuple> tuples) {
|
||||
this.tuples = tuples;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>For the STNamedCodeTuples view of a testable set of nodes, it is useful
|
||||
* to see what the name structure looks like in order to conditionally
|
||||
* match different types of testable sequences based on the asserted names
|
||||
* in the documentation. This will return the named, concatenated with "->",
|
||||
* and sanitized with all non-alphanumeric and > and $lt;, space, underscore
|
||||
* and dash removed.</p>
|
||||
*
|
||||
* <p>For the example structure at the class level, this would look like:
|
||||
* <pre>{@code name1->name2->name3}</pre></p>
|
||||
* @return A signature of the node sequences based on the name elements
|
||||
*/
|
||||
public String getTypeSignature() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (STNameAndCodeTuple tuple : tuples) {
|
||||
sb.append(tuple.getDesc()).append("->");
|
||||
}
|
||||
sb.setLength(sb.length()-"->".length());
|
||||
return sb.toString().replaceAll("[^-a-zA-Z0-9<> _]", "");
|
||||
}
|
||||
|
||||
public STNameAndCodeTuple get(int i) {
|
||||
return tuples.get(i);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.testtypes;
|
||||
|
||||
public class STNamedNodes {
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.testtypes;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface STNodeReference {
|
||||
Path getPath();
|
||||
int getLineNumber();
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.types;
|
||||
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
|
||||
public interface STAssemblyValidator {
|
||||
void validate(STNodeAssembly assembly);
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.types;
|
||||
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import io.nosqlbench.nb.spectest.core.SpecTest;
|
||||
import io.nosqlbench.nb.spectest.core.SpecTestBuilder;
|
||||
import io.nosqlbench.nb.spectest.loaders.STNodePredicate;
|
||||
import io.nosqlbench.nb.spectest.loaders.STNodePredicates;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface STBuilderFacets {
|
||||
|
||||
interface All extends WantsPaths, WantsPathsOrScannersOrValidators, WantsScannersOrValidators {}
|
||||
|
||||
interface WantsPaths {
|
||||
/**
|
||||
* Provide additional path or paths, which can be either a readable file or a directory containing readable
|
||||
* files or other directories. Only files which match a markdown path with a '.md' extension
|
||||
* will be validated.
|
||||
* @param paths Paths to test
|
||||
* @return this {@link SpecTestBuilder} for method chaining
|
||||
*/
|
||||
WantsPathsOrScannersOrValidators paths(Path... paths);
|
||||
/**
|
||||
* Provide additional path or paths, which can be either a readable file or a directory containing readable
|
||||
* files or other directories. Only files which match a markdown path with a '.md' extension
|
||||
* will be validated.
|
||||
* @param paths Paths to test
|
||||
* @return this {@link SpecTestBuilder} for method chaining
|
||||
*/
|
||||
default WantsPathsOrScannersOrValidators paths(String... paths) {
|
||||
Path[] args = new Path[paths.length];
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
args[i]=Path.of(paths[i]);
|
||||
}
|
||||
return this.paths(args);
|
||||
}
|
||||
/**
|
||||
* Provide an additional path, which can be either a readable file or a directory containing readable
|
||||
* files or other directories. Only files which match a markdown path with a '.md' extension
|
||||
* will be validated.
|
||||
* @param path Paths to test
|
||||
* @return this {@link SpecTestBuilder} for method chaining
|
||||
*/
|
||||
default WantsPathsOrScannersOrValidators path(Path path) {
|
||||
return this.paths(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an additional path or paths, which can be either a readable file or a directory containing readable
|
||||
* files or other directories. Only files which match a markdown path with a '.md' extension
|
||||
* will be validated.
|
||||
* @param path Paths to test
|
||||
* @return this {@link SpecTestBuilder} for method chaining
|
||||
*/
|
||||
default WantsPathsOrScannersOrValidators path(String path) {
|
||||
return this.paths(Path.of(path));
|
||||
}
|
||||
}
|
||||
|
||||
interface WantsPathsOrScannersOrValidators extends WantsPaths, WantsScannersOrValidators {
|
||||
}
|
||||
|
||||
interface WantsScannersOrValidators extends WantsValidatorsOrDone {
|
||||
/**
|
||||
* Attach a set of scanners against {@link Node} sequences
|
||||
* @param scanners
|
||||
* @return
|
||||
*/
|
||||
WantsValidatorsOrDone scanners(STPathLoader... scanners);
|
||||
WantsValidatorsOrDone scanners(STNodePredicate predicate);
|
||||
|
||||
/**
|
||||
* <P>Attach a {@link Node} scanner to this spec test builder by describing the
|
||||
* sequence of nodes which are valid, with no extra node types allowed between the
|
||||
* specified elements. This is a direct sequence matcher that is checked at each
|
||||
* position in the parsed element stream. When a sequence is found, the search
|
||||
* is resumed on the next element not included in the result.</P>
|
||||
*
|
||||
* <P>The predicates can be one of the types supported by {@link STNodePredicate}
|
||||
* and {@link STNodePredicates}.</P>
|
||||
*
|
||||
* @param predicates The pattern to match
|
||||
* @return this SpecTestBuilder for builder method chaining
|
||||
*/
|
||||
WantsValidatorsOrDone matchNodes(Object... predicates);
|
||||
}
|
||||
|
||||
interface WantsValidatorsOrDone extends Done{
|
||||
Done validators(STAssemblyValidator... validators);
|
||||
}
|
||||
|
||||
interface Done {
|
||||
SpecTest build();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.types;
|
||||
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public interface STNodeLoader extends BiFunction<Path, Node, List<STNodeAssembly>> {
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.types;
|
||||
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
import io.nosqlbench.nb.spectest.loaders.STNodePredicates;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A NodeScanner extracts a sequence of {@link STNodeAssembly}s, typically
|
||||
* by use of {@link STNodePredicates}
|
||||
*/
|
||||
public interface STPathLoader extends Function<Path, List<STNodeAssembly>> {
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest.types;
|
||||
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface STTypedAssembly extends Consumer<STNodeAssembly> {
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest;
|
||||
|
||||
import io.nosqlbench.nb.spectest.loaders.STFileScanner;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class STFileScannerTest {
|
||||
|
||||
@Test
|
||||
public void findTestSpecs() {
|
||||
List<Path> found = STFileScanner.findMatching(".*\\.md", Path.of("src/test/resources"));
|
||||
assertThat(found).contains(Path.of("src/test/resources/spectestdir/scanner-test-file.md"));
|
||||
}
|
||||
|
||||
}
|
@ -14,18 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.engine.api.activityconfig.rawyaml;
|
||||
package io.nosqlbench.nb.spectest;
|
||||
|
||||
import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import io.nosqlbench.nb.spectest.loaders.STHeadingScanner;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class HeadingScannerTest {
|
||||
public class STHeadingScannerTest {
|
||||
Parser parser = Parser.builder().extensions(List.of(YamlFrontMatterExtension.create())).build();
|
||||
|
||||
@Test
|
||||
@ -34,19 +34,19 @@ public class HeadingScannerTest {
|
||||
.parse("# heading1\n\n## heading2\n\ntext\n\n# heading3")
|
||||
.getFirstChild();
|
||||
|
||||
HeadingScanner scanner = new HeadingScanner(".");
|
||||
STHeadingScanner scanner = new STHeadingScanner(".");
|
||||
|
||||
assertThat(scanner.update(node).toString()).isEqualTo("heading1"); // Paragraph
|
||||
Assertions.assertThat(scanner.update(node).toString()).isEqualTo("heading1"); // Paragraph
|
||||
node = node.getNext();
|
||||
assertThat(scanner.update(node).toString()).isEqualTo("heading1.heading2"); // Paragraph
|
||||
Assertions.assertThat(scanner.update(node).toString()).isEqualTo("heading1.heading2"); // Paragraph
|
||||
node=node.getNext();
|
||||
assertThat(scanner.update(node).toString()).isEqualTo("heading1.heading2"); // Paragraph
|
||||
Assertions.assertThat(scanner.update(node).toString()).isEqualTo("heading1.heading2"); // Paragraph
|
||||
node=node.getNext();
|
||||
assertThat(scanner.update(node).toString()).isEqualTo("heading3"); // Paragraph
|
||||
Assertions.assertThat(scanner.update(node).toString()).isEqualTo("heading3"); // Paragraph
|
||||
scanner.index();
|
||||
assertThat(scanner.toString()).isEqualTo("heading3 (01)"); // Paragraph
|
||||
Assertions.assertThat(scanner.toString()).isEqualTo("heading3 (01)"); // Paragraph
|
||||
node=node.getNext();
|
||||
assertThat(node).isNull();
|
||||
Assertions.assertThat(node).isNull();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest;
|
||||
|
||||
import io.nosqlbench.nb.spectest.loaders.STNodePredicate;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class STNodePredicateTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidPredicateType() {
|
||||
Predicate<Number> numPredicate = s -> s.longValue()==1_000_000L;
|
||||
assertThatThrownBy(() -> new STNodePredicate(numPredicate))
|
||||
.isInstanceOf(ClassCastException.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.nb.spectest;
|
||||
|
||||
import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.ast.Document;
|
||||
import io.nosqlbench.nb.spectest.core.STNodeAssembly;
|
||||
import io.nosqlbench.nb.spectest.loaders.STDefaultNodeLoader;
|
||||
import io.nosqlbench.nb.spectest.loaders.STNodePredicates;
|
||||
import io.nosqlbench.nb.spectest.types.STNodeLoader;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class STNodePredicatesTest {
|
||||
|
||||
@Test
|
||||
public void testBackReferences() {
|
||||
STNodePredicates predicateShouldMatch1 = new STNodePredicates(".*__\\w__.*", 0, 0);
|
||||
STNodePredicates predicateShouldMatch2 = new STNodePredicates("__\\w__", 0, 0);
|
||||
STNodePredicates predicateShouldNotMatch3 = new STNodePredicates("^__\\w__", 0, 0);
|
||||
STNodePredicates predicateShouldNotMatch4 = new STNodePredicates("^__\\w__$", 0, 0);
|
||||
String testMarkdown = """
|
||||
paragraph contents with __a__.
|
||||
|
||||
paragraph contents with __b__.
|
||||
|
||||
paragraph contents with __c__.
|
||||
""";
|
||||
|
||||
|
||||
Parser parser = Parser.builder().extensions(List.of(YamlFrontMatterExtension.create())).build();
|
||||
Document document = parser.parse(testMarkdown);
|
||||
// STDefaultLoader scanner = new STDefaultLoader(predicates);
|
||||
STNodeLoader scanner = new STDefaultNodeLoader(predicateShouldMatch1);
|
||||
|
||||
List<STNodeAssembly> assemblies = scanner.apply(null, document);
|
||||
assertThat(assemblies).hasSizeGreaterThan(0);
|
||||
System.out.print(assemblies);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
# Test
|
Loading…
Reference in New Issue
Block a user