aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ext_depends/D-YAML/source/dyaml/test/suite.d
diff options
context:
space:
mode:
authorRalph Amissah <ralph.amissah@gmail.com>2025-08-28 10:35:13 -0400
committerRalph Amissah <ralph.amissah@gmail.com>2025-08-28 10:35:13 -0400
commit3539b7f5308b617e99b0a4ea298a413acbda4d79 (patch)
tree42101adf54ce7fc060da9e312dc508aaf1302bf2 /src/ext_depends/D-YAML/source/dyaml/test/suite.d
parentdub 1.40.0, llvm 20 (diff)
src/ext_deplends d-yaml updated (v0.10.0)HEADmain
Diffstat (limited to 'src/ext_depends/D-YAML/source/dyaml/test/suite.d')
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/suite.d384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/suite.d b/src/ext_depends/D-YAML/source/dyaml/test/suite.d
new file mode 100644
index 0000000..2146c37
--- /dev/null
+++ b/src/ext_depends/D-YAML/source/dyaml/test/suite.d
@@ -0,0 +1,384 @@
+module dyaml.test.suite;
+
+import std.algorithm;
+import std.conv;
+import std.datetime.stopwatch;
+import std.exception;
+import std.file;
+import std.format;
+import std.meta;
+import std.path;
+import std.range;
+import std.stdio;
+import std.string;
+import std.typecons;
+import dyaml;
+import dyaml.event;
+import dyaml.parser;
+import dyaml.reader;
+import dyaml.scanner;
+import dyaml.test.suitehelpers;
+
+private version(unittest):
+
+debug(verbose)
+{
+ enum alwaysPrintTestResults = true;
+}
+else
+{
+ enum alwaysPrintTestResults = false;
+}
+
+struct TestResult {
+ string name;
+ Nullable!bool emitter;
+ Nullable!bool constructor;
+ Nullable!bool loaderError;
+ Nullable!bool mark1Error;
+ Nullable!bool mark2Error;
+ Nullable!bool implicitResolver;
+ Nullable!bool events;
+ Nullable!bool specificLoaderError;
+ Nullable!Mark mark1;
+ Nullable!Mark mark2;
+ Event[] parsedData;
+ Event[][2 * 2 * 5] parsedDataResult;
+ Node[] loadedData;
+ Exception nonDYAMLException;
+ MarkedYAMLException exception;
+ string eventsExpected;
+ string eventsGenerated;
+ string generatedLoadErrorMessage;
+ string expectedLoadErrorMessage;
+ string expectedTags;
+ string generatedTags;
+}
+
+/// Pretty-print the differences between two arrays
+auto prettyDifferencePrinter(alias eqPred = (a,b) => a == b, T)(string title, T[] expected, T[] got, bool trimWhitespace = false) @safe
+{
+ struct Result
+ {
+ void foo() {
+ toString(nullSink);
+ }
+ void toString(W)(ref W writer) const
+ {
+ import std.format : formattedWrite;
+ import std.range : put;
+ import std.string : lineSplitter;
+ size_t minWidth = 10;
+ foreach (line; chain(expected, got))
+ {
+ if (line.text.length + 1 > minWidth)
+ {
+ minWidth = line.text.length + 1;
+ }
+ }
+ void writeSideBySide(ubyte colour, string a, string b)
+ {
+ if (trimWhitespace)
+ {
+ a = strip(a);
+ b = strip(b);
+ }
+ writer.formattedWrite!"%s%-(%s%)%s"(colourPrinter(colour, a), " ".repeat(minWidth - a.length), colourPrinter(colour, b));
+ }
+ writefln!"%-(%s%)%s%-(%s%)"("=".repeat(max(0, minWidth * 2 - title.length) / 2), title, "=".repeat(max(0, minWidth * 2 - title.length) / 2));
+ writeSideBySide(0, "Expected", "Got");
+ put(writer, "\n");
+ foreach (line1, line2; zip(StoppingPolicy.longest, expected, got))
+ {
+ static if (is(T : const char[]))
+ {
+ if (trimWhitespace)
+ {
+ line1 = strip(line1);
+ line2 = strip(line2);
+ }
+ }
+ ubyte colour = (eqPred(line1, line2)) ? 32 : 31;
+ writeSideBySide(colour, line1.text, line2.text);
+ put(writer, "\n");
+ }
+ }
+ }
+ return Result();
+}
+
+/**
+Run a single test from the test suite.
+Params:
+ name = The filename of the document to load, containing the test data
+*/
+TestResult runTest(string name, Node doc) @safe
+{
+ TestResult result;
+ string[string] testData;
+ void tryLoadTestData(string what)
+ {
+ if (what in doc)
+ {
+ testData[what] = doc[what].as!string;
+ doc.removeAt(what);
+ }
+ }
+ string yamlPath(string testName, string section)
+ {
+ return format!"%s:%s"(testName, section);
+ }
+ tryLoadTestData("name");
+ result.name = name~"#"~testData.get("name", "UNNAMED");
+ Nullable!Mark getMark(string key)
+ {
+ if (auto node = key in doc)
+ {
+ Mark mark;
+ if ("name" in *node)
+ {
+ mark.name = (*node)["name"].as!string;
+ }
+ else // default to the test name
+ {
+ // if we ever have multiple yaml blocks to parse, be sure to change this
+ mark.name = yamlPath(result.name, "yaml");
+ }
+ if ("line" in *node)
+ {
+ mark.line = cast(ushort)((*node)["line"].as!ushort - 1);
+ }
+ if ("column" in *node)
+ {
+ mark.column = cast(ushort)((*node)["column"].as!ushort - 1);
+ }
+ return Nullable!Mark(mark);
+ }
+ return Nullable!Mark.init;
+ }
+ tryLoadTestData("tags");
+ tryLoadTestData("from");
+ tryLoadTestData("yaml");
+ tryLoadTestData("fail");
+ tryLoadTestData("json"); //not yet implemented
+ tryLoadTestData("dump"); //not yet implemented
+ tryLoadTestData("detect");
+ tryLoadTestData("tree");
+ tryLoadTestData("error");
+ tryLoadTestData("code");
+ assert("yaml" in testData);
+ {
+ result.expectedLoadErrorMessage = testData.get("error", "");
+ result.mark1 = getMark("mark");
+ result.mark2 = getMark("mark2");
+ try
+ {
+ result.parsedData = parseData(testData["yaml"], yamlPath(result.name, "yaml")).array;
+ result.loadedData = Loader.fromString(testData["yaml"], yamlPath(result.name, "yaml")).array;
+ result.emitter = testEmitterStyles(yamlPath(result.name, "canonical"), result.parsedData, result.parsedDataResult);
+ result.mark1Error = result.mark1.isNull;
+ result.mark2Error = result.mark2.isNull;
+ }
+ catch (MarkedYAMLException e)
+ {
+ result.exception = e;
+ result.generatedLoadErrorMessage = e.msg;
+ result.mark1Error = !result.mark1.isNull && (result.mark1.get() == e.mark);
+ result.mark2Error = result.mark2 == e.mark2;
+ if (testData.get("fail", "false") == "false")
+ {
+ result.loaderError = false;
+ }
+ else
+ {
+ result.loaderError = true;
+ }
+ }
+ catch (Exception e)
+ {
+ // all non-DYAML exceptions are failures.
+ result.nonDYAMLException = e;
+ result.generatedLoadErrorMessage = e.msg;
+ result.loaderError = false;
+ }
+ result.specificLoaderError = strip(result.generatedLoadErrorMessage) == strip(result.expectedLoadErrorMessage);
+ }
+ if (result.loaderError.get(false))
+ {
+ // skip other tests if loading failure was expected, because we don't
+ // have a way to run them yet
+ return result;
+ }
+ if ("tree" in testData)
+ {
+ result.eventsGenerated = result.parsedData.map!(x => strip(x.text)).join("\n");
+ result.eventsExpected = testData["tree"].lineSplitter.map!(x => strip(x)).join("\n");
+ result.events = result.eventsGenerated == result.eventsExpected;
+ }
+ if ("code" in testData)
+ {
+ result.constructor = testConstructor(testData["yaml"], testData["code"]);
+ }
+ if ("detect" in testData)
+ {
+ result.implicitResolver = testImplicitResolver(yamlPath(result.name, "yaml"), testData["yaml"], testData["detect"], result.generatedTags, result.expectedTags);
+ }
+ foreach (string remaining, Node _; doc)
+ {
+ writeln("Warning: Unhandled section '", remaining, "' in ", result.name);
+ }
+ return result;
+}
+
+enum goodColour = 32;
+enum badColour = 31;
+/**
+Print something to the console in colour.
+Params:
+ colour = The id of the colour to print, using the 256-colour palette
+ data = Something to print
+*/
+private auto colourPrinter(T)(ubyte colour, T data) @safe pure
+{
+ struct Printer
+ {
+ void toString(S)(ref S sink)
+ {
+ sink.formattedWrite!"\033[%s;1m%s\033[0m"(colour, data);
+ }
+ }
+ return Printer();
+}
+
+/**
+Run all tests in the test suite and print relevant results. The test docs are
+all found in the ./test/data dir.
+*/
+bool runTests()
+{
+ auto stopWatch = StopWatch(AutoStart.yes);
+ bool failed;
+ uint testsRun, testSetsRun, testsFailed;
+ foreach (string name; dirEntries(buildNormalizedPath("test"), "*.yaml", SpanMode.depth)/*.chain(dirEntries(buildNormalizedPath("yaml-test-suite/src"), "*.yaml", SpanMode.depth))*/)
+ {
+ Node doc;
+ try
+ {
+ doc = Loader.fromFile(name).load();
+ }
+ catch (Exception e)
+ {
+ writefln!"[%s] %s"(colourPrinter(badColour, "FAIL"), name);
+ writeln(colourPrinter(badColour, e));
+ assert(0, "Could not load test doc '"~name~"', bailing");
+ }
+ assert (doc.nodeID == NodeID.sequence, name~"'s root node is not a sequence!");
+ foreach (Node test; doc)
+ {
+ testSetsRun++;
+ bool resultPrinted;
+ // make sure the paths are normalized on windows by replacing backslashes with slashes
+ TestResult result = runTest(name.replace("\\", "/"), test);
+ void printResult(string label, Nullable!bool value)
+ {
+ if (!value.isNull)
+ {
+ if (!value.get)
+ {
+ testsFailed++;
+ }
+ testsRun++;
+ }
+ if (alwaysPrintTestResults && value.get(false))
+ {
+ resultPrinted = true;
+ writef!"[%s]"(colourPrinter(goodColour, label));
+ }
+ else if (!value.get(true))
+ {
+ resultPrinted = true;
+ failed = true;
+ writef!"[%s]"(colourPrinter(badColour, label));
+ }
+ }
+ printResult("Emitter", result.emitter);
+ printResult("Constructor", result.constructor);
+ printResult("Mark", result.mark1Error);
+ printResult("Context mark", result.mark2Error);
+ printResult("LoaderError", result.loaderError);
+ printResult("Resolver", result.implicitResolver);
+ printResult("Events", result.events);
+ printResult("SpecificLoaderError", result.specificLoaderError);
+ if (resultPrinted)
+ {
+ writeln(" ", result.name);
+ }
+ if (!result.loaderError.get(true))
+ {
+ if (result.exception is null && result.nonDYAMLException is null)
+ {
+ writeln("\tNo Exception thrown");
+ }
+ else if (result.nonDYAMLException !is null)
+ {
+ writeln(result.nonDYAMLException);
+ }
+ else if (result.exception !is null)
+ {
+ writeln(result.exception);
+ }
+ }
+ else
+ {
+ if (!result.mark1Error.get(true))
+ {
+ writeln(prettyDifferencePrinter("Mark mismatch", [result.mark1.text], [result.exception.mark.text]));
+ }
+ if (!result.mark2Error.get(true))
+ {
+ writeln(prettyDifferencePrinter("Context mark mismatch", [result.mark2.text], [result.exception.mark2.text]));
+ }
+ }
+ if (!result.emitter.get(true))
+ {
+ enum titles = [ "Normal", "Canonical" ];
+ enum styleTitles =
+ [
+ "Block literal", "Block folded", "Block double-quoted", "Block single-quoted", "Block plain",
+ "Flow literal", "Flow folded", "Flow double-quoted", "Flow single-quoted", "Flow plain",
+ "Block literal", "Block folded", "Block double-quoted", "Block single-quoted", "Block plain",
+ "Flow literal", "Flow folded", "Flow double-quoted", "Flow single-quoted", "Flow plain",
+ ];
+ foreach (idx, parsed; result.parsedDataResult)
+ {
+ writeln(prettyDifferencePrinter!eventCompare(styleTitles[idx], result.parsedData, parsed));
+ }
+ }
+ if (!result.events.get(true))
+ {
+ writeln(prettyDifferencePrinter("Events", result.eventsExpected.splitLines, result.eventsGenerated.splitLines, true));
+ }
+ if (!result.specificLoaderError.get(true))
+ {
+ writeln(prettyDifferencePrinter("Expected error", result.expectedLoadErrorMessage.splitLines, result.generatedLoadErrorMessage.splitLines));
+ }
+ if (!result.implicitResolver.get(true))
+ {
+ writeln(prettyDifferencePrinter("Expected error", result.expectedTags.splitLines, result.generatedTags.splitLines));
+ }
+ }
+ }
+ if (alwaysPrintTestResults || failed)
+ {
+ if (testsFailed > 0)
+ {
+ writeln(colourPrinter(badColour, "tests failed: "), testsFailed);
+ }
+ writeln(testSetsRun, " test sets (", testsRun, " tests total) completed successfully in ", stopWatch.peek());
+ }
+ return failed;
+}
+
+unittest {
+ assert(!runTests());
+}