diff options
Diffstat (limited to 'src/ext_depends/D-YAML/source/dyaml/test')
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/common.d | 223 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/compare.d | 51 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/constructor.d | 40 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/emitter.d | 132 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/errors.d | 64 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d | 92 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/reader.d | 37 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/representer.d | 10 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/resolver.d | 39 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/suite.d | 384 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d | 224 | ||||
-rw-r--r-- | src/ext_depends/D-YAML/source/dyaml/test/tokens.d | 93 |
12 files changed, 613 insertions, 776 deletions
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/common.d b/src/ext_depends/D-YAML/source/dyaml/test/common.d deleted file mode 100644 index a6bafa9..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/common.d +++ /dev/null @@ -1,223 +0,0 @@ - -// Copyright Ferdinand Majerech 2011. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.common; - -version(unittest) -{ - -import dyaml.node; -import dyaml.event; - -import core.exception; -import std.algorithm; -import std.array; -import std.conv; -import std.file; -import std.range; -import std.path; -import std.traits; -import std.typecons; - -package: - -/** -Run a test. - -Params: - testFunction = Unittest function. - unittestExt = Extensions of data files needed for the unittest. - skipExt = Extensions that must not be used for the unittest. - */ -void run(D)(D testFunction, string[] unittestExt, string[] skipExt = []) -{ - immutable string dataDir = __FILE_FULL_PATH__.dirName ~ "/../../../test/data"; - auto testFilenames = findTestFilenames(dataDir); - - if (unittestExt.length > 0) - { - outer: foreach (base, extensions; testFilenames) - { - string[] filenames; - foreach (ext; unittestExt) - { - if (!extensions.canFind(ext)) - { - continue outer; - } - filenames ~= base ~ '.' ~ ext; - } - foreach (ext; skipExt) - { - if (extensions.canFind(ext)) - { - continue outer; - } - } - - execute(testFunction, filenames); - } - } - else - { - execute(testFunction, string[].init); - } -} - -// TODO: remove when a @safe ubyte[] file read can be done. -/** -Reads a file as an array of bytes. - -Params: - filename = Full path to file to read. - -Returns: The file's data. -*/ -ubyte[] readData(string filename) @trusted -{ - import std.file : read; - return cast(ubyte[])read(filename); -} -void assertNodesEqual(const scope Node gotNode, const scope Node expectedNode) @safe -{ - import std.format : format; - assert(gotNode == expectedNode, format!"got %s, expected %s"(gotNode.debugString, expectedNode.debugString)); -} - -/** -Determine if events in events1 are equivalent to events in events2. - -Params: - events1 = A range of events to compare with. - events2 = A second range of events to compare. - -Returns: true if the events are equivalent, false otherwise. -*/ -bool compareEvents(T, U)(T events1, U events2) -if (isInputRange!T && isInputRange!U && is(ElementType!T == Event) && is(ElementType!U == Event)) -{ - foreach (e1, e2; zip(events1, events2)) - { - //Different event types. - if (e1.id != e2.id) - { - return false; - } - //Different anchor (if applicable). - if (e1.id.among!(EventID.sequenceStart, EventID.mappingStart, EventID.alias_, EventID.scalar) - && e1.anchor != e2.anchor) - { - return false; - } - //Different collection tag (if applicable). - if (e1.id.among!(EventID.sequenceStart, EventID.mappingStart) && e1.tag != e2.tag) - { - return false; - } - if (e1.id == EventID.scalar) - { - //Different scalar tag (if applicable). - if (!(e1.implicit || e2.implicit) && e1.tag != e2.tag) - { - return false; - } - //Different scalar value. - if (e1.value != e2.value) - { - return false; - } - } - } - return true; -} -/** -Throw an Error if events in events1 aren't equivalent to events in events2. - -Params: - events1 = First event array to compare. - events2 = Second event array to compare. -*/ -void assertEventsEqual(T, U)(T events1, U events2) -if (isInputRange!T && isInputRange!U && is(ElementType!T == Event) && is(ElementType!U == Event)) -{ - auto events1Copy = events1.array; - auto events2Copy = events2.array; - assert(compareEvents(events1Copy, events2Copy), text("Got '", events1Copy, "', expected '", events2Copy, "'")); -} - -private: - -/** -Find unittest input filenames. - -Params: dir = Directory to look in. - -Returns: Test input base filenames and their extensions. -*/ - //@trusted due to dirEntries -string[][string] findTestFilenames(const string dir) @trusted -{ - //Groups of extensions indexed by base names. - string[][string] names; - foreach (string name; dirEntries(dir, SpanMode.shallow)) - { - if (isFile(name)) - { - string base = name.stripExtension(); - string ext = name.extension(); - if (ext is null) - { - ext = ""; - } - if (ext[0] == '.') - { - ext = ext[1 .. $]; - } - - //If the base name doesn't exist yet, add it; otherwise add new extension. - names[base] = ((base in names) is null) ? [ext] : names[base] ~ ext; - } - } - return names; -} - -/** -Recursively copy an array of strings to a tuple to use for unittest function input. - -Params: - index = Current index in the array/tuple. - tuple = Tuple to copy to. - strings = Strings to copy. -*/ -void stringsToTuple(uint index, F ...)(ref F tuple, const string[] strings) -in(F.length == strings.length) -do -{ - tuple[index] = strings[index]; - static if (index > 0) - { - stringsToTuple!(index - 1, F)(tuple, strings); - } -} - -/** -Execute an unittest on specified files. - -Params: - testName = Name of the unittest. - testFunction = Unittest function. - filenames = Names of input files to test with. - */ -void execute(D)(D testFunction, string[] filenames) -{ - //Convert filenames to parameters tuple and call the test function. - alias F = Parameters!D[0..$]; - F parameters; - stringsToTuple!(F.length - 1, F)(parameters, filenames); - testFunction(parameters); -} - -} // version(unittest) diff --git a/src/ext_depends/D-YAML/source/dyaml/test/compare.d b/src/ext_depends/D-YAML/source/dyaml/test/compare.d deleted file mode 100644 index 5a37fd0..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/compare.d +++ /dev/null @@ -1,51 +0,0 @@ - -// Copyright Ferdinand Majerech 2011. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.compare; - -@safe unittest -{ - import dyaml : Loader; - import dyaml.test.common : assertNodesEqual, compareEvents, run; - - /** - Test parser by comparing output from parsing two equivalent YAML files. - - Params: - dataFilename = YAML file to parse. - canonicalFilename = Another file to parse, in canonical YAML format. - */ - static void testParser(string dataFilename, string canonicalFilename) @safe - { - auto dataEvents = Loader.fromFile(dataFilename).parse(); - auto canonicalEvents = Loader.fromFile(canonicalFilename).parse(); - - //BUG: the return value isn't checked! This test currently fails... - compareEvents(dataEvents, canonicalEvents); - } - - /** - Test loader by comparing output from loading two equivalent YAML files. - - Params: - dataFilename = YAML file to load. - canonicalFilename = Another file to load, in canonical YAML format. - */ - static void testLoader(string dataFilename, string canonicalFilename) @safe - { - import std.array : array; - auto data = Loader.fromFile(dataFilename).array; - auto canonical = Loader.fromFile(canonicalFilename).array; - - assert(data.length == canonical.length, "Unequal node count"); - foreach (n; 0 .. data.length) - { - assertNodesEqual(data[n], canonical[n]); - } - } - run(&testParser, ["data", "canonical"]); - run(&testLoader, ["data", "canonical"], ["test_loader_skip"]); -} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/constructor.d b/src/ext_depends/D-YAML/source/dyaml/test/constructor.d index aeb8653..ad9fb62 100644 --- a/src/ext_depends/D-YAML/source/dyaml/test/constructor.d +++ b/src/ext_depends/D-YAML/source/dyaml/test/constructor.d @@ -6,10 +6,9 @@ module dyaml.test.constructor; +package version(unittest): -version(unittest) -{ - +import std.algorithm; import std.conv; import std.datetime; import std.exception; @@ -920,38 +919,3 @@ struct TestStruct return Node(value.to!string, "!tag2"); } } - -} // version(unittest) - - -@safe unittest -{ - import dyaml.test.common : assertNodesEqual, run; - /** - Constructor unittest. - - Params: - dataFilename = File name to read from. - codeDummy = Dummy .code filename, used to determine that - .data file with the same name should be used in this test. - */ - static void testConstructor(string dataFilename, string codeDummy) @safe - { - string base = dataFilename.baseName.stripExtension; - assert((base in expected) !is null, "Unimplemented constructor test: " ~ base); - - auto loader = Loader.fromFile(dataFilename); - - Node[] exp = expected[base]; - - //Compare with expected results document by document. - size_t i; - foreach (node; loader) - { - assertNodesEqual(node, exp[i]); - ++i; - } - assert(i == exp.length); - } - run(&testConstructor, ["data", "code"]); -} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/emitter.d b/src/ext_depends/D-YAML/source/dyaml/test/emitter.d deleted file mode 100644 index 293f236..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/emitter.d +++ /dev/null @@ -1,132 +0,0 @@ - -// Copyright Ferdinand Majerech 2011-2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.emitter; - -@safe unittest -{ - import std.array : Appender; - import std.range : ElementType, isInputRange; - - import dyaml : CollectionStyle, LineBreak, Loader, Mark, ScalarStyle; - import dyaml.emitter : Emitter; - import dyaml.event : Event, EventID, mappingStartEvent, scalarEvent, sequenceStartEvent; - import dyaml.test.common : assertEventsEqual, run; - - // Try to emit an event range. - static void emitTestCommon(T)(ref Appender!string emitStream, T events, bool canonical = false) @safe - if (isInputRange!T && is(ElementType!T == Event)) - { - auto emitter = Emitter!(typeof(emitStream), char)(emitStream, canonical, 2, 80, LineBreak.unix); - foreach (ref event; events) - { - emitter.emit(event); - } - } - /** - Test emitter by getting events from parsing a file, emitting them, parsing - the emitted result and comparing events from parsing the emitted result with - originally parsed events. - - Params: - dataFilename = YAML file to parse. - canonicalFilename = Canonical YAML file used as dummy to determine - which data files to load. - */ - static void testEmitterOnData(string dataFilename, string canonicalFilename) @safe - { - //Must exist due to Anchor, Tags reference counts. - auto loader = Loader.fromFile(dataFilename); - auto events = loader.parse(); - auto emitStream = Appender!string(); - emitTestCommon(emitStream, events); - - auto loader2 = Loader.fromString(emitStream.data); - loader2.name = "TEST"; - auto newEvents = loader2.parse(); - assertEventsEqual(events, newEvents); - } - /** - Test emitter by getting events from parsing a canonical YAML file, emitting - them both in canonical and normal format, parsing the emitted results and - comparing events from parsing the emitted result with originally parsed events. - - Params: canonicalFilename = Canonical YAML file to parse. - */ - static void testEmitterOnCanonical(string canonicalFilename) @safe - { - //Must exist due to Anchor, Tags reference counts. - auto loader = Loader.fromFile(canonicalFilename); - auto events = loader.parse(); - foreach (canonical; [false, true]) - { - auto emitStream = Appender!string(); - emitTestCommon(emitStream, events, canonical); - - auto loader2 = Loader.fromString(emitStream.data); - loader2.name = "TEST"; - auto newEvents = loader2.parse(); - assertEventsEqual(events, newEvents); - } - } - /** - Test emitter by getting events from parsing a file, emitting them with all - possible scalar and collection styles, parsing the emitted results and - comparing events from parsing the emitted result with originally parsed events. - - Params: - dataFilename = YAML file to parse. - canonicalFilename = Canonical YAML file used as dummy to determine - which data files to load. - */ - static void testEmitterStyles(string dataFilename, string canonicalFilename) @safe - { - foreach (filename; [dataFilename, canonicalFilename]) - { - //must exist due to Anchor, Tags reference counts - auto loader = Loader.fromFile(canonicalFilename); - auto events = loader.parse(); - foreach (flowStyle; [CollectionStyle.block, CollectionStyle.flow]) - { - foreach (style; [ScalarStyle.literal, ScalarStyle.folded, - ScalarStyle.doubleQuoted, ScalarStyle.singleQuoted, - ScalarStyle.plain]) - { - Event[] styledEvents; - foreach (event; events) - { - if (event.id == EventID.scalar) - { - event = scalarEvent(Mark(), Mark(), event.anchor, event.tag, - event.implicit, - event.value, style); - } - else if (event.id == EventID.sequenceStart) - { - event = sequenceStartEvent(Mark(), Mark(), event.anchor, - event.tag, event.implicit, flowStyle); - } - else if (event.id == EventID.mappingStart) - { - event = mappingStartEvent(Mark(), Mark(), event.anchor, - event.tag, event.implicit, flowStyle); - } - styledEvents ~= event; - } - auto emitStream = Appender!string(); - emitTestCommon(emitStream, styledEvents); - auto loader2 = Loader.fromString(emitStream.data); - loader2.name = "TEST"; - auto newEvents = loader2.parse(); - assertEventsEqual(events, newEvents); - } - } - } - } - run(&testEmitterOnData, ["data", "canonical"]); - run(&testEmitterOnCanonical, ["canonical"]); - run(&testEmitterStyles, ["data", "canonical"]); -} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/errors.d b/src/ext_depends/D-YAML/source/dyaml/test/errors.d deleted file mode 100644 index 43b019c..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/errors.d +++ /dev/null @@ -1,64 +0,0 @@ - -// Copyright Ferdinand Majerech 2011-2014 -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.errors; - -@safe unittest -{ - import std.array : array; - import std.exception : assertThrown; - - import dyaml : Loader; - import dyaml.test.common : run; - - /** - Loader error unittest from file stream. - - Params: errorFilename = File name to read from. - */ - static void testLoaderError(string errorFilename) @safe - { - assertThrown(Loader.fromFile(errorFilename).array, - __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception"); - } - - /** - Loader error unittest from string. - - Params: errorFilename = File name to read from. - */ - static void testLoaderErrorString(string errorFilename) @safe - { - assertThrown(Loader.fromFile(errorFilename).array, - __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception"); - } - - /** - Loader error unittest from filename. - - Params: errorFilename = File name to read from. - */ - static void testLoaderErrorFilename(string errorFilename) @safe - { - assertThrown(Loader.fromFile(errorFilename).array, - __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception"); - } - - /** - Loader error unittest loading a single document from a file. - - Params: errorFilename = File name to read from. - */ - static void testLoaderErrorSingle(string errorFilename) @safe - { - assertThrown(Loader.fromFile(errorFilename).load(), - __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception"); - } - run(&testLoaderError, ["loader-error"]); - run(&testLoaderErrorString, ["loader-error"]); - run(&testLoaderErrorFilename, ["loader-error"]); - run(&testLoaderErrorSingle, ["single-loader-error"]); -} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d b/src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d deleted file mode 100644 index 758def8..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d +++ /dev/null @@ -1,92 +0,0 @@ - -// Copyright Ferdinand Majerech 2011-2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.inputoutput; - -@safe unittest -{ - import std.array : join, split; - import std.conv : to; - import std.exception : assertThrown; - import std.file : readText; - import std.system : endian, Endian; - - import dyaml : Loader, Node, YAMLException; - import dyaml.test.common : run; - - /** - Get an UTF-16 byte order mark. - - Params: wrong = Get the incorrect BOM for this system. - - Returns: UTF-16 byte order mark. - */ - static wchar bom16(bool wrong = false) pure @safe - { - wchar little = '\uFEFF'; - wchar big = '\uFFFE'; - if (!wrong) - { - return endian == Endian.littleEndian ? little : big; - } - return endian == Endian.littleEndian ? big : little; - } - /** - Get an UTF-32 byte order mark. - - Params: wrong = Get the incorrect BOM for this system. - - Returns: UTF-32 byte order mark. - */ - static dchar bom32(bool wrong = false) pure @safe - { - dchar little = '\uFEFF'; - dchar big = '\uFFFE'; - if (!wrong) - { - return endian == Endian.littleEndian ? little : big; - } - return endian == Endian.littleEndian ? big : little; - } - /** - Unicode input unittest. Tests various encodings. - - Params: unicodeFilename = File name to read from. - */ - static void testUnicodeInput(string unicodeFilename) @safe - { - string data = readText(unicodeFilename); - string expected = data.split().join(" "); - - Node output = Loader.fromString(data).load(); - assert(output.as!string == expected); - - foreach (buffer; [cast(ubyte[]) (bom16() ~ data.to!(wchar[])), - cast(ubyte[]) (bom32() ~ data.to!(dchar[]))]) - { - output = Loader.fromBuffer(buffer).load(); - assert(output.as!string == expected); - } - } - /** - Unicode input error unittest. Tests various encodings with incorrect BOMs. - - Params: unicodeFilename = File name to read from. - */ - static void testUnicodeInputErrors(string unicodeFilename) @safe - { - string data = readText(unicodeFilename); - foreach (buffer; [cast(ubyte[]) (data.to!(wchar[])), - cast(ubyte[]) (data.to!(dchar[])), - cast(ubyte[]) (bom16(true) ~ data.to!(wchar[])), - cast(ubyte[]) (bom32(true) ~ data.to!(dchar[]))]) - { - assertThrown(Loader.fromBuffer(buffer).load()); - } - } - run(&testUnicodeInput, ["unicode"]); - run(&testUnicodeInputErrors, ["unicode"]); -} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/reader.d b/src/ext_depends/D-YAML/source/dyaml/test/reader.d deleted file mode 100644 index c20df6f..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/reader.d +++ /dev/null @@ -1,37 +0,0 @@ - -// Copyright Ferdinand Majerech 2011. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.reader; - -@safe unittest -{ - import std.exception :assertThrown; - - import dyaml.test.common : readData, run; - import dyaml.reader : Reader, ReaderException; - - /** - Try reading entire file through Reader, expecting an error (the file is invalid). - - Params: data = Stream to read. - */ - static void runReader(ubyte[] fileData) @safe - { - auto reader = new Reader(fileData); - while(reader.peek() != '\0') { reader.forward(); } - } - - /** - Stream error unittest. Tries to read invalid input files, expecting errors. - - Params: errorFilename = File name to read from. - */ - static void testStreamError(string errorFilename) @safe - { - assertThrown!ReaderException(runReader(readData(errorFilename))); - } - run(&testStreamError, ["stream-error"]); -} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/representer.d b/src/ext_depends/D-YAML/source/dyaml/test/representer.d index 4a1ae67..eeac157 100644 --- a/src/ext_depends/D-YAML/source/dyaml/test/representer.d +++ b/src/ext_depends/D-YAML/source/dyaml/test/representer.d @@ -9,12 +9,12 @@ module dyaml.test.representer; @safe unittest { import std.array : Appender, array; + import std.conv : text; import std.meta : AliasSeq; import std.path : baseName, stripExtension; import std.utf : toUTF8; import dyaml : dumper, Loader, Node; - import dyaml.test.common : assertNodesEqual, run; import dyaml.test.constructor : expected; /** @@ -38,13 +38,9 @@ module dyaml.test.representer; auto loader = Loader.fromString(emitStream.data.toUTF8); loader.name = "TEST"; - const readNodes = loader.array; + auto readNodes = loader.array; - assert(expectedNodes.length == readNodes.length); - foreach (n; 0 .. expectedNodes.length) - { - assertNodesEqual(expectedNodes[n], readNodes[n]); - } + assert(expectedNodes == readNodes, text("Got '", readNodes, "', expected '", expectedNodes, "'")); } } foreach (key, _; expected) diff --git a/src/ext_depends/D-YAML/source/dyaml/test/resolver.d b/src/ext_depends/D-YAML/source/dyaml/test/resolver.d deleted file mode 100644 index ea93720..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/resolver.d +++ /dev/null @@ -1,39 +0,0 @@ - -// Copyright Ferdinand Majerech 2011. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.resolver; - -@safe unittest -{ - import std.conv : text; - import std.file : readText; - import std.string : strip; - - import dyaml : Loader, Node, NodeID; - import dyaml.test.common : run; - - - /** - Implicit tag resolution unittest. - - Params: - dataFilename = File with unittest data. - detectFilename = Dummy filename used to specify which data filenames to use. - */ - static void testImplicitResolver(string dataFilename, string detectFilename) @safe - { - const correctTag = readText(detectFilename).strip(); - - auto node = Loader.fromFile(dataFilename).load(); - assert(node.nodeID == NodeID.sequence, text("Expected sequence when reading '", dataFilename, "', got ", node.nodeID)); - foreach (Node scalar; node) - { - assert(scalar.nodeID == NodeID.scalar, text("Expected sequence of scalars when reading '", dataFilename, "', got sequence of ", scalar.nodeID)); - assert(scalar.tag == correctTag, text("Expected tag '", correctTag, "' when reading '", dataFilename, "', got '", scalar.tag, "'")); - } - } - run(&testImplicitResolver, ["data", "detect"]); -} 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()); +} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d b/src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d new file mode 100644 index 0000000..3016843 --- /dev/null +++ b/src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d @@ -0,0 +1,224 @@ + +// Copyright Ferdinand Majerech 2011. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +module dyaml.test.suitehelpers; + +import dyaml; +import dyaml.emitter; +import dyaml.event; +import dyaml.parser; +import dyaml.reader; +import dyaml.scanner; +import dyaml.token; +import dyaml.test.constructor; + +import std.algorithm; +import std.array; +import std.conv; +import std.exception; +import std.file; +import std.range; +import std.string; + +package version(unittest): + +// Like other types, wchar and dchar use the system's endianness, so \uFEFF +// will always be the 'correct' BOM and '\uFFFE' will always be the 'wrong' one +enum wchar[] bom16 = ['\uFEFF', '\uFFFE']; +enum dchar[] bom32 = ['\uFEFF', '\uFFFE']; + +Parser parseData(string data, string name = "TEST") @safe +{ + auto reader = Reader(cast(ubyte[])data.dup, name); + auto scanner = Scanner(reader); + return new Parser(scanner); +} + +/** +Test scanner by scanning a document, expecting no errors. + +Params: + name = Name of the document being scanned + data = Data to scan. +*/ +void testScanner(string name, string data) @safe +{ + ubyte[] yamlData = cast(ubyte[])data.dup; + string[] tokens; + foreach (token; Scanner(Reader(yamlData, name))) + { + tokens ~= token.id.text; + } +} + +/** +Implicit tag resolution unittest. + +Params: + name = Name of the document being tested + data = Document to compare + detectData = The tag that each scalar should resolve to +*/ +bool testImplicitResolver(string name, string data, string detectData, out string generatedTags, out string expectedTags) @safe +{ + const correctTag = detectData.strip(); + + const node = Loader.fromString(data, name).load(); + if (node.nodeID != NodeID.sequence) + { + return false; + } + bool success = true; + foreach (const Node scalar; node) + { + generatedTags ~= scalar.tag ~ "\n"; + expectedTags ~= correctTag ~ "\n"; + if ((scalar.nodeID != NodeID.scalar) || (scalar.tag != correctTag)) + { + success = false; + } + } + return success; +} + +// Try to emit an event range. +Event[] emitTestCommon(string name, Event[] events, bool canonical) @safe +{ + auto emitStream = new Appender!string(); + auto emitter = Emitter!(typeof(emitStream), char)(emitStream, canonical, 2, 80, LineBreak.unix); + foreach (event; events) + { + emitter.emit(event); + } + return parseData(emitStream.data, name).array; +} +/** +Test emitter by checking if events remain equal after round-tripping, with and +without canonical output enabled. + +Params: + name = Name of the document being tested + events = Events to test + results = Events that were produced by round-tripping +*/ +bool testEmitter(string name, Event[] events, out Event[][2] results) @safe +{ + bool matching = true; + foreach (idx, canonicalOutput; [false, true]) + { + results[idx] = emitTestCommon(name, events, canonicalOutput); + + if (!equal!eventCompare(events, results[idx])) + { + matching = false; + } + } + return matching; +} +/** +Test emitter by checking if events remain equal after round-tripping, with all +combinations of styles. + +Params: + name = Name of the document being tested + events = Events to test + results = Events that were produced by round-tripping +*/ +bool testEmitterStyles(string name, Event[] events, out Event[][2 * 2 * 5] results) @safe +{ + size_t idx; + foreach (styles; cartesianProduct( + [CollectionStyle.block, CollectionStyle.flow], + [ScalarStyle.literal, ScalarStyle.folded, + ScalarStyle.doubleQuoted, ScalarStyle.singleQuoted, + ScalarStyle.plain], + [false, true])) + { + const collectionStyle = styles[0]; + const scalarStyle = styles[1]; + const canonical = styles[2]; + Event[] styledEvents; + foreach (event; events) + { + if (event.id == EventID.scalar) + { + event = scalarEvent(Mark(), Mark(), event.anchor, event.tag, + event.implicit, + event.value, scalarStyle); + } + else if (event.id == EventID.sequenceStart) + { + event = sequenceStartEvent(Mark(), Mark(), event.anchor, + event.tag, event.implicit, collectionStyle); + } + else if (event.id == EventID.mappingStart) + { + event = mappingStartEvent(Mark(), Mark(), event.anchor, + event.tag, event.implicit, collectionStyle); + } + styledEvents ~= event; + } + auto newEvents = emitTestCommon(name, styledEvents, canonical); + results[idx++] = newEvents; + if (!equal!eventCompare(events, newEvents)) + { + return false; + } + } + return true; +} + +/** +Constructor unittest. + +Params: + data = The document being tested + base = A unique id corresponding to one of the premade sequences in dyaml.test.constructor +*/ +bool testConstructor(string data, string base) @safe +{ + assert((base in expected) !is null, "Unimplemented constructor test: " ~ base); + auto loader = Loader.fromString(data); + + Node[] exp = expected[base]; + + //Compare with expected results document by document. + return equal(loader, exp); +} + +bool eventCompare(const Event a, const Event b) @safe pure +{ + //Different event types. + if (a.id != b.id) + { + return false; + } + //Different anchor (if applicable). + if (a.id.among!(EventID.sequenceStart, EventID.mappingStart, EventID.alias_, EventID.scalar) + && a.anchor != b.anchor) + { + return false; + } + //Different collection tag (if applicable). + if (a.id.among!(EventID.sequenceStart, EventID.mappingStart) && a.tag != b.tag) + { + return false; + } + if (a.id == EventID.scalar) + { + //Different scalar tag (if applicable). + if (!(a.implicit || b.implicit) && a.tag != b.tag) + { + return false; + } + //Different scalar value. + if (a.value != b.value) + { + return false; + } + } + return true; +} diff --git a/src/ext_depends/D-YAML/source/dyaml/test/tokens.d b/src/ext_depends/D-YAML/source/dyaml/test/tokens.d deleted file mode 100644 index d3dce6e..0000000 --- a/src/ext_depends/D-YAML/source/dyaml/test/tokens.d +++ /dev/null @@ -1,93 +0,0 @@ - -// Copyright Ferdinand Majerech 2011. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dyaml.test.tokens; - -@safe unittest -{ - import std.array : split; - import std.conv : text; - import std.file : readText; - - import dyaml.test.common : run; - import dyaml.reader : Reader; - import dyaml.scanner : Scanner; - import dyaml.token : TokenID; - - // Read and scan a YAML doc, returning a range of tokens. - static auto scanTestCommon(string filename) @safe - { - ubyte[] yamlData = cast(ubyte[])readText(filename).dup; - return Scanner(new Reader(yamlData, filename)); - } - - /** - Test tokens output by scanner. - - Params: - dataFilename = File to scan. - tokensFilename = File containing expected tokens. - */ - static void testTokens(string dataFilename, string tokensFilename) @safe - { - //representations of YAML tokens in tokens file. - auto replace = [ - TokenID.directive: "%", - TokenID.documentStart: "---", - TokenID.documentEnd: "...", - TokenID.alias_: "*", - TokenID.anchor: "&", - TokenID.tag: "!", - TokenID.scalar: "_", - TokenID.blockSequenceStart: "[[", - TokenID.blockMappingStart: "{{", - TokenID.blockEnd: "]}", - TokenID.flowSequenceStart: "[", - TokenID.flowSequenceEnd: "]", - TokenID.flowMappingStart: "{", - TokenID.flowMappingEnd: "}", - TokenID.blockEntry: ",", - TokenID.flowEntry: ",", - TokenID.key: "?", - TokenID.value: ":" - ]; - - string[] tokens; - string[] expectedTokens = readText(tokensFilename).split(); - - foreach (token; scanTestCommon(dataFilename)) - { - if (token.id != TokenID.streamStart && token.id != TokenID.streamEnd) - { - tokens ~= replace[token.id]; - } - } - - assert(tokens == expectedTokens, - text("In token test for '", tokensFilename, "', expected '", expectedTokens, "', got '", tokens, "'")); - } - - /** - Test scanner by scanning a file, expecting no errors. - - Params: - dataFilename = File to scan. - canonicalFilename = Another file to scan, in canonical YAML format. - */ - static void testScanner(string dataFilename, string canonicalFilename) @safe - { - foreach (filename; [dataFilename, canonicalFilename]) - { - string[] tokens; - foreach (token; scanTestCommon(filename)) - { - tokens ~= token.id.text; - } - } - } - run(&testTokens, ["data", "tokens"]); - run(&testScanner, ["data", "canonical"]); -} |