Initial commit.
This commit is contained in:
commit
283c42bf8f
592 changed files with 26392 additions and 0 deletions
202
test/src/common.d
Normal file
202
test/src/common.d
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
// 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.testcommon;
|
||||
|
||||
public import std.conv;
|
||||
public import std.stdio;
|
||||
public import std.stream;
|
||||
public import yaml;
|
||||
|
||||
import core.exception;
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
import std.conv;
|
||||
import std.file;
|
||||
import std.path;
|
||||
import std.typecons;
|
||||
|
||||
package:
|
||||
|
||||
alias std.stream.File File;
|
||||
|
||||
/**
|
||||
* Run an unittest.
|
||||
*
|
||||
* Params: testName = Name of the unittest.
|
||||
* testFunction = Unittest function.
|
||||
* unittestExt = Extensions of data files needed for the unittest.
|
||||
* skipExt = Extensions that must not be used for the unittest.
|
||||
*/
|
||||
void run(F ...)(string testName, void function(bool, F) testFunction,
|
||||
string[] unittestExt, string[] skipExt = [])
|
||||
{
|
||||
immutable string dataDir = "test/data";
|
||||
auto testFilenames = findTestFilenames(dataDir);
|
||||
bool verbose = false;
|
||||
|
||||
Result[] results;
|
||||
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;}
|
||||
}
|
||||
|
||||
results ~= execute!F(testName, testFunction, filenames, verbose);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
results ~= execute!F(testName, testFunction, cast(string[])[], verbose);
|
||||
}
|
||||
display(results, verbose);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
///Unittest status.
|
||||
enum TestStatus
|
||||
{
|
||||
Success, //Unittest passed.
|
||||
Failure, //Unittest failed.
|
||||
Error //There's an error in the unittest.
|
||||
}
|
||||
|
||||
///Unittest result.
|
||||
alias Tuple!(string, "name", string[], "filenames", TestStatus, "kind", string, "info") Result;
|
||||
|
||||
/**
|
||||
* Find unittest input filenames.
|
||||
*
|
||||
* Params: dir = Directory to look in.
|
||||
*
|
||||
* Returns: Test input base filenames and their extensions.
|
||||
*/
|
||||
string[][string] findTestFilenames(in string dir)
|
||||
{
|
||||
//Groups of extensions indexed by base names.
|
||||
string[][string] names;
|
||||
foreach(string name; dirEntries(dir, SpanMode.shallow))
|
||||
{
|
||||
if(isFile(name))
|
||||
{
|
||||
string base = name.getName();
|
||||
string ext = name.getExt();
|
||||
if(ext is null){ext = "";}
|
||||
|
||||
//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, in string[] strings)
|
||||
in{assert(F.length == strings.length);}
|
||||
body
|
||||
{
|
||||
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.
|
||||
* verbose = Print verbose output?
|
||||
*
|
||||
* Returns: Information about the results of the unittest.
|
||||
*/
|
||||
Result execute(F ...)(in string testName, void function(bool, F) testFunction,
|
||||
string[] filenames, in bool verbose)
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
writeln("===========================================================================");
|
||||
writeln(testName ~ "(" ~ filenames.join(", ") ~ ")...");
|
||||
}
|
||||
|
||||
auto kind = TestStatus.Success;
|
||||
string info = "";
|
||||
try
|
||||
{
|
||||
//Convert filenames to parameters tuple and call the test function.
|
||||
F parameters;
|
||||
stringsToTuple!(F.length - 1, F)(parameters, filenames);
|
||||
testFunction(verbose, parameters);
|
||||
if(!verbose){write(".");}
|
||||
}
|
||||
catch(Throwable e)
|
||||
{
|
||||
info = to!string(typeid(e)) ~ "\n" ~ to!string(e);
|
||||
kind = (typeid(e) is typeid(AssertError)) ? TestStatus.Failure : TestStatus.Error;
|
||||
write((verbose ? to!string(e) : to!string(kind)) ~ " ");
|
||||
}
|
||||
|
||||
stdout.flush();
|
||||
|
||||
return Result(testName, filenames, kind, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display unittest results.
|
||||
*
|
||||
* Params: results = Unittest results.
|
||||
* verbose = Print verbose output?
|
||||
*/
|
||||
void display(Result[] results, in bool verbose)
|
||||
{
|
||||
if(results.length > 0 && !verbose){write("\n");}
|
||||
|
||||
size_t failures = 0;
|
||||
size_t errors = 0;
|
||||
|
||||
if(verbose)
|
||||
{
|
||||
writeln("===========================================================================");
|
||||
}
|
||||
//Results of each test.
|
||||
foreach(result; results)
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
writeln(result.name, "(" ~ result.filenames.join(", ") ~ "): ",
|
||||
to!string(result.kind));
|
||||
}
|
||||
|
||||
if(result.kind == TestStatus.Success){continue;}
|
||||
|
||||
if(result.kind == TestStatus.Failure){++failures;}
|
||||
else if(result.kind == TestStatus.Error){++errors;}
|
||||
writeln(result.info);
|
||||
writeln("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
}
|
||||
|
||||
//Totals.
|
||||
writeln("===========================================================================");
|
||||
writeln("TESTS: ", results.length);
|
||||
if(failures > 0){writeln("FAILURES: ", failures);}
|
||||
if(errors > 0) {writeln("ERRORS: ", errors);}
|
||||
}
|
61
test/src/compare.d
Normal file
61
test/src/compare.d
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
// 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.testcompare;
|
||||
|
||||
|
||||
import dyaml.testcommon;
|
||||
import dyaml.token;
|
||||
|
||||
|
||||
/**
|
||||
* Test parser by comparing output from parsing two equivalent YAML files.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* dataFilename = YAML file to parse.
|
||||
* canonicalFilename = Another file to parse, in canonical YAML format.
|
||||
*/
|
||||
void testParser(bool verbose, string dataFilename, string canonicalFilename)
|
||||
{
|
||||
auto dataEvents = Loader(dataFilename).parse();
|
||||
auto canonicalEvents = Loader(canonicalFilename).parse();
|
||||
|
||||
assert(dataEvents.length == canonicalEvents.length);
|
||||
|
||||
|
||||
foreach(e; 0 .. dataEvents.length)
|
||||
{
|
||||
assert(dataEvents[e].id == canonicalEvents[e].id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test loader by comparing output from loading two equivalent YAML files.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* dataFilename = YAML file to load.
|
||||
* canonicalFilename = Another file to load, in canonical YAML format.
|
||||
*/
|
||||
void testLoader(bool verbose, string dataFilename, string canonicalFilename)
|
||||
{
|
||||
auto data = loadAll(dataFilename);
|
||||
auto canonical = loadAll(canonicalFilename);
|
||||
|
||||
assert(data.length == canonical.length);
|
||||
foreach(n; 0 .. data.length)
|
||||
{
|
||||
assert(data[n] == canonical[n]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unittest
|
||||
{
|
||||
writeln("D:YAML comparison unittest");
|
||||
run("testParser", &testParser, ["data", "canonical"]);
|
||||
run("testLoader", &testLoader, ["data", "canonical"], ["test_loader_skip"]);
|
||||
}
|
408
test/src/constructor.d
Normal file
408
test/src/constructor.d
Normal file
|
@ -0,0 +1,408 @@
|
|||
|
||||
// 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.testconstructor;
|
||||
|
||||
|
||||
import std.datetime;
|
||||
import std.exception;
|
||||
import std.path;
|
||||
|
||||
import dyaml.testcommon;
|
||||
|
||||
|
||||
///Expected results of loading test inputs.
|
||||
Node[][string] expected;
|
||||
|
||||
///Initialize expected.
|
||||
static this()
|
||||
{
|
||||
expected["construct-binary.data"] = constructBinary();
|
||||
expected["construct-bool.data"] = constructBool();
|
||||
expected["construct-custom.data"] = constructCustom();
|
||||
expected["construct-float.data"] = constructFloat();
|
||||
expected["construct-int.data"] = constructInt();
|
||||
expected["construct-map.data"] = constructMap();
|
||||
expected["construct-merge.data"] = constructMerge();
|
||||
expected["construct-null.data"] = constructNull();
|
||||
expected["construct-omap.data"] = constructOMap();
|
||||
expected["construct-pairs.data"] = constructPairs();
|
||||
expected["construct-seq.data"] = constructSeq();
|
||||
expected["construct-set.data"] = constructSet();
|
||||
expected["construct-str-ascii.data"] = constructStrASCII();
|
||||
expected["construct-str.data"] = constructStr();
|
||||
expected["construct-str-utf8.data"] = constructStrUTF8();
|
||||
expected["construct-timestamp.data"] = constructTimestamp();
|
||||
expected["construct-value.data"] = constructValue();
|
||||
expected["duplicate-merge-key.data"] = duplicateMergeKey();
|
||||
expected["float-representer-2.3-bug.data"] = floatRepresenterBug();
|
||||
expected["invalid-single-quote-bug.data"] = invalidSingleQuoteBug();
|
||||
expected["more-floats.data"] = moreFloats();
|
||||
expected["negative-float-bug.data"] = negativeFloatBug();
|
||||
expected["single-dot-is-not-float-bug.data"] = singleDotFloatBug();
|
||||
expected["timestamp-bugs.data"] = timestampBugs();
|
||||
expected["utf16be.data"] = utf16be();
|
||||
expected["utf16le.data"] = utf16le();
|
||||
expected["utf8.data"] = utf8();
|
||||
expected["utf8-implicit.data"] = utf8implicit();
|
||||
}
|
||||
|
||||
///Construct a node with specified value.
|
||||
Node node(T)(T value)
|
||||
{
|
||||
static if(Node.Value.allowed!T){return Node(Node.Value(value));}
|
||||
else{return Node(Node.userValue(value));}
|
||||
}
|
||||
|
||||
///Construct a pair of nodes with specified values.
|
||||
Node.Pair pair(A, B)(A a, B b)
|
||||
{
|
||||
static if(is(A == Node) && is(B == Node)){return Node.Pair(a, b);}
|
||||
else static if(is(A == Node)) {return Node.Pair(a, node(b));}
|
||||
else static if(is(B == Node)) {return Node.Pair(node(a), b);}
|
||||
else {return Node.Pair(node(a), node(b));}
|
||||
}
|
||||
|
||||
///Test cases:
|
||||
|
||||
Node[] constructBinary()
|
||||
{
|
||||
auto canonical = cast(ubyte[])"GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;";
|
||||
auto generic = cast(ubyte[])"GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;";
|
||||
auto description = "The binary value above is a tiny arrow encoded as a gif image.";
|
||||
|
||||
return [node([pair("canonical", canonical),
|
||||
pair("generic", generic),
|
||||
pair("description", description)])];
|
||||
}
|
||||
|
||||
Node[] constructBool()
|
||||
{
|
||||
return [node([pair("canonical", true),
|
||||
pair("answer", false),
|
||||
pair("logical", true),
|
||||
pair("option", true),
|
||||
pair("but", [pair("y", "is a string"), pair("n", "is a string")])])];
|
||||
}
|
||||
|
||||
Node[] constructCustom()
|
||||
{
|
||||
return [node([node(new TestClass(1, 0, 0)),
|
||||
node(new TestClass(1, 2, 3)),
|
||||
node(TestStruct(10))])];
|
||||
}
|
||||
|
||||
Node[] constructFloat()
|
||||
{
|
||||
return [node([pair("canonical", cast(real)685230.15),
|
||||
pair("exponential", cast(real)685230.15),
|
||||
pair("fixed", cast(real)685230.15),
|
||||
pair("sexagesimal", cast(real)685230.15),
|
||||
pair("negative infinity", -real.infinity),
|
||||
pair("not a number", real.nan)])];
|
||||
}
|
||||
|
||||
Node[] constructInt()
|
||||
{
|
||||
return [node([pair("canonical", 685230L),
|
||||
pair("decimal", 685230L),
|
||||
pair("octal", 685230L),
|
||||
pair("hexadecimal", 685230L),
|
||||
pair("binary", 685230L),
|
||||
pair("sexagesimal", 685230L)])];
|
||||
}
|
||||
|
||||
Node[] constructMap()
|
||||
{
|
||||
return [node([pair("Block style",
|
||||
[pair("Clark", "Evans"),
|
||||
pair("Brian", "Ingerson"),
|
||||
pair("Oren", "Ben-Kiki")]),
|
||||
pair("Flow style",
|
||||
[pair("Clark", "Evans"),
|
||||
pair("Brian", "Ingerson"),
|
||||
pair("Oren", "Ben-Kiki")])])];
|
||||
}
|
||||
|
||||
Node[] constructMerge()
|
||||
{
|
||||
return [node([node([pair("x", 1L), pair("y", 2L)]),
|
||||
node([pair("x", 0L), pair("y", 2L)]),
|
||||
node([pair("r", 10L)]),
|
||||
node([pair("r", 1L)]),
|
||||
node([pair("x", 1L), pair("y", 2L), pair("r", 10L), pair("label", "center/big")]),
|
||||
node([pair("r", 10L), pair("label", "center/big"), pair("x", 1L), pair("y", 2L)]),
|
||||
node([pair("label", "center/big"), pair("x", 1L), pair("y", 2L), pair("r", 10L)]),
|
||||
node([pair("x", 1L), pair("label", "center/big"), pair("r", 10L), pair("y", 2L)])])];
|
||||
}
|
||||
|
||||
Node[] constructNull()
|
||||
{
|
||||
return [node(YAMLNull()),
|
||||
node([pair("empty", YAMLNull()),
|
||||
pair("canonical", YAMLNull()),
|
||||
pair("english", YAMLNull()),
|
||||
pair(YAMLNull(), "null key")]),
|
||||
node([pair("sparse",
|
||||
[node(YAMLNull()),
|
||||
node("2nd entry"),
|
||||
node(YAMLNull()),
|
||||
node("4th entry"),
|
||||
node(YAMLNull())])])];
|
||||
}
|
||||
|
||||
Node[] constructOMap()
|
||||
{
|
||||
return [node([pair("Bestiary",
|
||||
[pair("aardvark", "African pig-like ant eater. Ugly."),
|
||||
pair("anteater", "South-American ant eater. Two species."),
|
||||
pair("anaconda", "South-American constrictor snake. Scaly.")]),
|
||||
pair("Numbers",[pair("one", 1L),
|
||||
pair("two", 2L),
|
||||
pair("three", 3L)])])];
|
||||
}
|
||||
|
||||
Node[] constructPairs()
|
||||
{
|
||||
return [node([pair("Block tasks",
|
||||
[pair("meeting", "with team."),
|
||||
pair("meeting", "with boss."),
|
||||
pair("break", "lunch."),
|
||||
pair("meeting", "with client.")]),
|
||||
pair("Flow tasks",
|
||||
[pair("meeting", "with team"),
|
||||
pair("meeting", "with boss")])])];
|
||||
}
|
||||
|
||||
Node[] constructSeq()
|
||||
{
|
||||
return [node([pair("Block style",
|
||||
[node("Mercury"), node("Venus"), node("Earth"), node("Mars"),
|
||||
node("Jupiter"), node("Saturn"), node("Uranus"), node("Neptune"),
|
||||
node("Pluto")]),
|
||||
pair("Flow style",
|
||||
[node("Mercury"), node("Venus"), node("Earth"), node("Mars"),
|
||||
node("Jupiter"), node("Saturn"), node("Uranus"), node("Neptune"),
|
||||
node("Pluto")])])];
|
||||
}
|
||||
|
||||
Node[] constructSet()
|
||||
{
|
||||
return [node([pair("baseball players",
|
||||
[node("Mark McGwire"), node("Sammy Sosa"), node("Ken Griffey")]),
|
||||
pair("baseball teams",
|
||||
[node("Boston Red Sox"), node("Detroit Tigers"), node("New York Yankees")])])];
|
||||
}
|
||||
|
||||
Node[] constructStrASCII()
|
||||
{
|
||||
return [node("ascii string")];
|
||||
}
|
||||
|
||||
Node[] constructStr()
|
||||
{
|
||||
return [node([pair("string", "abcd")])];
|
||||
}
|
||||
|
||||
Node[] constructStrUTF8()
|
||||
{
|
||||
return [node("\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430")];
|
||||
}
|
||||
|
||||
Node[] constructTimestamp()
|
||||
{
|
||||
return [node([pair("canonical", SysTime(DateTime(2001, 12, 15, 2, 59, 43), FracSec.from!"hnsecs"(1000000), UTC())),
|
||||
pair("valid iso8601", SysTime(DateTime(2001, 12, 15, 2, 59, 43), FracSec.from!"hnsecs"(1000000), UTC())),
|
||||
pair("space separated", SysTime(DateTime(2001, 12, 15, 2, 59, 43), FracSec.from!"hnsecs"(1000000), UTC())),
|
||||
pair("no time zone (Z)", SysTime(DateTime(2001, 12, 15, 2, 59, 43), FracSec.from!"hnsecs"(1000000), UTC())),
|
||||
pair("date (00:00:00Z)", SysTime(DateTime(2002, 12, 14), UTC()))])];
|
||||
}
|
||||
|
||||
Node[] constructValue()
|
||||
{
|
||||
return[node([pair("link with",
|
||||
[node("library1.dll"), node("library2.dll")])]),
|
||||
node([pair("link with",
|
||||
[node([pair("=", "library1.dll"), pair("version", cast(real)1.2)]),
|
||||
node([pair("=", "library2.dll"), pair("version", cast(real)2.3)])])])];
|
||||
}
|
||||
|
||||
Node[] duplicateMergeKey()
|
||||
{
|
||||
return [node([pair("foo", "bar"),
|
||||
pair("x", 1L),
|
||||
pair("y", 2L),
|
||||
pair("z", 3L),
|
||||
pair("t", 4L)])];
|
||||
}
|
||||
|
||||
Node[] floatRepresenterBug()
|
||||
{
|
||||
return [node([pair(cast(real)1.0, 1L),
|
||||
pair(real.infinity, 10L),
|
||||
pair(-real.infinity, -10L),
|
||||
pair(real.nan, 100L)])];
|
||||
}
|
||||
|
||||
Node[] invalidSingleQuoteBug()
|
||||
{
|
||||
return [node([node("foo \'bar\'"), node("foo\n\'bar\'")])];
|
||||
}
|
||||
|
||||
Node[] moreFloats()
|
||||
{
|
||||
return [node([node(cast(real)0.0),
|
||||
node(cast(real)1.0),
|
||||
node(cast(real)-1.0),
|
||||
node(real.infinity),
|
||||
node(-real.infinity),
|
||||
node(real.nan),
|
||||
node(real.nan)])];
|
||||
}
|
||||
|
||||
Node[] negativeFloatBug()
|
||||
{
|
||||
return [node(cast(real)-1.0)];
|
||||
}
|
||||
|
||||
Node[] singleDotFloatBug()
|
||||
{
|
||||
return [node(".")];
|
||||
}
|
||||
|
||||
Node[] timestampBugs()
|
||||
{
|
||||
return [node([node(SysTime(DateTime(2001, 12, 15, 3, 29, 43), FracSec.from!"hnsecs"(1000000), UTC())),
|
||||
node(SysTime(DateTime(2001, 12, 14, 16, 29, 43), FracSec.from!"hnsecs"(1000000), UTC())),
|
||||
node(SysTime(DateTime(2001, 12, 14, 21, 59, 43), FracSec.from!"hnsecs"(10100), UTC())),
|
||||
node(SysTime(DateTime(2001, 12, 14, 21, 59, 43), new SimpleTimeZone(60))),
|
||||
node(SysTime(DateTime(2001, 12, 14, 21, 59, 43), new SimpleTimeZone(-90))),
|
||||
node(SysTime(DateTime(2005, 7, 8, 17, 35, 4), FracSec.from!"hnsecs"(5176000), UTC()))])];
|
||||
}
|
||||
|
||||
Node[] utf16be()
|
||||
{
|
||||
return [node("UTF-16-BE")];
|
||||
}
|
||||
|
||||
Node[] utf16le()
|
||||
{
|
||||
return [node("UTF-16-LE")];
|
||||
}
|
||||
|
||||
Node[] utf8()
|
||||
{
|
||||
return [node("UTF-8")];
|
||||
}
|
||||
|
||||
Node[] utf8implicit()
|
||||
{
|
||||
return [node("implicit UTF-8")];
|
||||
}
|
||||
|
||||
///Testing custom YAML class type.
|
||||
class TestClass
|
||||
{
|
||||
int x, y, z;
|
||||
|
||||
this(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
override bool opEquals(Object rhs)
|
||||
{
|
||||
if(typeid(rhs) != typeid(TestClass)){return false;}
|
||||
auto t = cast(TestClass)rhs;
|
||||
return x == t.x && y == t.y && z == t.z;
|
||||
}
|
||||
}
|
||||
|
||||
///Testing custom YAML struct type.
|
||||
struct TestStruct
|
||||
{
|
||||
int value;
|
||||
|
||||
bool opEquals(const ref TestStruct rhs) const
|
||||
{
|
||||
return value == rhs.value;
|
||||
}
|
||||
}
|
||||
|
||||
///Constructor function for TestClass.
|
||||
TestClass constructClass(Mark start, Mark end, Node.Pair[] pairs)
|
||||
{
|
||||
int x, y, z;
|
||||
foreach(ref pair; pairs)
|
||||
{
|
||||
switch(pair.key.get!string)
|
||||
{
|
||||
case "x": x = pair.value.get!int; break;
|
||||
case "y": y = pair.value.get!int; break;
|
||||
case "z": z = pair.value.get!int; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return new TestClass(x, y, z);
|
||||
}
|
||||
|
||||
///Constructor function for TestStruct.
|
||||
TestStruct constructStruct(Mark start, Mark end, string value)
|
||||
{
|
||||
return TestStruct(to!int(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor unittest.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* 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.
|
||||
*/
|
||||
void testConstructor(bool verbose, string dataFilename, string codeDummy)
|
||||
{
|
||||
string base = dataFilename.basename;
|
||||
enforce((base in expected) !is null,
|
||||
new Exception("Unimplemented constructor test: " ~ base));
|
||||
|
||||
auto constructor = new Constructor;
|
||||
constructor.addConstructor("!tag1", &constructClass);
|
||||
constructor.addConstructor("!tag2", &constructStruct);
|
||||
|
||||
auto resolver = new Resolver;
|
||||
auto loader = Loader(dataFilename, constructor, resolver);
|
||||
|
||||
//Compare with expected results document by document.
|
||||
size_t i = 0;
|
||||
foreach(node; loader)
|
||||
{
|
||||
if(node != expected[base][i])
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
writeln("Expected value:");
|
||||
writeln(expected[base][i].debugString);
|
||||
writeln("\n");
|
||||
writeln("Actual value:");
|
||||
writeln(node.debugString);
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
assert(i == expected[base].length);
|
||||
}
|
||||
|
||||
|
||||
unittest
|
||||
{
|
||||
writeln("D:YAML Constructor unittest");
|
||||
run("testConstructor", &testConstructor, ["data", "code"]);
|
||||
}
|
103
test/src/errors.d
Normal file
103
test/src/errors.d
Normal file
|
@ -0,0 +1,103 @@
|
|||
|
||||
// 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.testerrors;
|
||||
|
||||
|
||||
import dyaml.testcommon;
|
||||
|
||||
|
||||
/**
|
||||
* Loader error unittest from file stream.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* errorFilename = File name to read from.
|
||||
*/
|
||||
void testLoaderError(bool verbose, string errorFilename)
|
||||
{
|
||||
auto file = new File(errorFilename);
|
||||
scope(exit){file.close();}
|
||||
|
||||
Node[] nodes;
|
||||
try{nodes = loadAll(file, errorFilename);}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
return;
|
||||
}
|
||||
assert(false, "Expected an exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader error unittest from string.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* errorFilename = File name to read from.
|
||||
*/
|
||||
void testLoaderErrorString(bool verbose, string errorFilename)
|
||||
{
|
||||
//Load file to a buffer, then pass that to the YAML loader.
|
||||
auto file = new File(errorFilename);
|
||||
scope(exit){file.close();}
|
||||
ubyte[] buffer;
|
||||
buffer.length = file.available;
|
||||
file.read(buffer);
|
||||
|
||||
try
|
||||
{
|
||||
auto nodes = loadAll(new MemoryStream(buffer), errorFilename);
|
||||
}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
return;
|
||||
}
|
||||
assert(false, "Expected an exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader error unittest from filename.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* errorFilename = File name to read from.
|
||||
*/
|
||||
void testLoaderErrorFilename(bool verbose, string errorFilename)
|
||||
{
|
||||
try{auto nodes = loadAll(errorFilename);}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
return;
|
||||
}
|
||||
assert(false, "Expected an exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader error unittest loading a single document from a file.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* errorFilename = File name to read from.
|
||||
*/
|
||||
void testLoaderErrorSingle(bool verbose, string errorFilename)
|
||||
{
|
||||
try{auto nodes = load(errorFilename);}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
return;
|
||||
}
|
||||
assert(false, "Expected an exception");
|
||||
}
|
||||
|
||||
|
||||
unittest
|
||||
{
|
||||
writeln("D:YAML Errors unittest");
|
||||
run("testLoaderError", &testLoaderError, ["loader-error"]);
|
||||
run("testLoaderErrorString", &testLoaderErrorString, ["loader-error"]);
|
||||
run("testLoaderErrorFilename", &testLoaderErrorFilename, ["loader-error"]);
|
||||
run("testLoaderErrorSingle", &testLoaderErrorSingle, ["single-loader-error"]);
|
||||
}
|
101
test/src/inputoutput.d
Normal file
101
test/src/inputoutput.d
Normal file
|
@ -0,0 +1,101 @@
|
|||
|
||||
// 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.testinputoutput;
|
||||
|
||||
|
||||
import std.array;
|
||||
import std.file;
|
||||
import std.system;
|
||||
|
||||
import dyaml.testcommon;
|
||||
|
||||
|
||||
alias std.system.endian endian;
|
||||
|
||||
/**
|
||||
* Get an UTF-16 byte order mark.
|
||||
*
|
||||
* Params: wrong = Get the incorrect BOM for this system.
|
||||
*
|
||||
* Returns: UTF-16 byte order mark.
|
||||
*/
|
||||
wchar bom16(bool wrong = false) pure
|
||||
{
|
||||
wchar little = *(cast(wchar*)ByteOrderMarks[BOM.UTF16LE]);
|
||||
wchar big = *(cast(wchar*)ByteOrderMarks[BOM.UTF16BE]);
|
||||
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.
|
||||
*/
|
||||
dchar bom32(bool wrong = false) pure
|
||||
{
|
||||
dchar little = *(cast(dchar*)ByteOrderMarks[BOM.UTF32LE]);
|
||||
dchar big = *(cast(dchar*)ByteOrderMarks[BOM.UTF32BE]);
|
||||
if(!wrong){return endian == Endian.LittleEndian ? little : big;}
|
||||
return endian == Endian.LittleEndian ? big : little;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unicode input unittest. Tests various encodings.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* unicodeFilename = File name to read from.
|
||||
*/
|
||||
void testUnicodeInput(bool verbose, string unicodeFilename)
|
||||
{
|
||||
string data = readText(unicodeFilename);
|
||||
string expected = data.split().join(" ");
|
||||
|
||||
Node output = load(new MemoryStream(to!(char[])(data)), unicodeFilename);
|
||||
assert(output.get!string == expected);
|
||||
|
||||
foreach(stream; [new MemoryStream(cast(byte[])(bom16() ~ to!(wchar[])(data))),
|
||||
new MemoryStream(cast(byte[])(bom32() ~ to!(dchar[])(data)))])
|
||||
{
|
||||
output = load(stream, unicodeFilename);
|
||||
assert(output.get!string == expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unicode input error unittest. Tests various encodings with incorrect BOMs.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* unicodeFilename = File name to read from.
|
||||
*/
|
||||
void testUnicodeInputErrors(bool verbose, string unicodeFilename)
|
||||
{
|
||||
string data = readText(unicodeFilename);
|
||||
foreach(stream; [new MemoryStream(cast(byte[])(to!(wchar[])(data))),
|
||||
new MemoryStream(cast(byte[])(to!(wchar[])(data))),
|
||||
new MemoryStream(cast(byte[])(bom16(true) ~ to!(wchar[])(data))),
|
||||
new MemoryStream(cast(byte[])(bom32(true) ~ to!(dchar[])(data)))])
|
||||
{
|
||||
try{load(stream, unicodeFilename);}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
continue;
|
||||
}
|
||||
assert(false, "Expected an exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unittest
|
||||
{
|
||||
writeln("D:YAML I/O unittest");
|
||||
run("testUnicodeInput", &testUnicodeInput, ["unicode"]);
|
||||
run("testUnicodeInputErrors", &testUnicodeInputErrors, ["unicode"]);
|
||||
}
|
53
test/src/reader.d
Normal file
53
test/src/reader.d
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
// 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.testreader;
|
||||
|
||||
|
||||
import dyaml.testcommon;
|
||||
import dyaml.reader;
|
||||
|
||||
|
||||
/**
|
||||
* Try reading entire stream through Reader, expecting an error (the stream is invalid).
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* data = Stream to read.
|
||||
*/
|
||||
void runReader(in bool verbose, Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto reader = new Reader(stream);
|
||||
while(reader.peek() != '\0'){reader.forward();}
|
||||
}
|
||||
catch(ReaderException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
return;
|
||||
}
|
||||
assert(false, "Expected an exception");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stream error unittest. Tries to read invalid input streams, expecting errors.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* errorFilename = File name to read from.
|
||||
*/
|
||||
void testStreamError(bool verbose, string errorFilename)
|
||||
{
|
||||
auto file = new File(errorFilename);
|
||||
scope(exit){file.close();}
|
||||
runReader(verbose, file);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
writeln("D:YAML Reader unittest");
|
||||
run("testStreamError", &testStreamError, ["stream-error"]);
|
||||
}
|
91
test/src/tokens.d
Normal file
91
test/src/tokens.d
Normal file
|
@ -0,0 +1,91 @@
|
|||
|
||||
// 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.testtokens;
|
||||
|
||||
|
||||
import std.array;
|
||||
import std.file;
|
||||
|
||||
import dyaml.testcommon;
|
||||
import dyaml.token;
|
||||
|
||||
|
||||
/**
|
||||
* Test tokens output by scanner.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* dataFilename = File to scan.
|
||||
* tokensFilename = File containing expected tokens.
|
||||
*/
|
||||
void testTokens(bool verbose, string dataFilename, string tokensFilename)
|
||||
{
|
||||
//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[] tokens1;
|
||||
string[] tokens2 = readText(tokensFilename).split();
|
||||
scope(exit)
|
||||
{
|
||||
if(verbose){writeln("tokens1: ", tokens1, "\ntokens2: ", tokens2);}
|
||||
}
|
||||
|
||||
auto loader = Loader(dataFilename);
|
||||
foreach(token; loader.scan())
|
||||
{
|
||||
if(token.id != TokenID.StreamStart && token.id != TokenID.StreamEnd)
|
||||
{
|
||||
tokens1 ~= replace[token.id];
|
||||
}
|
||||
}
|
||||
|
||||
assert(tokens1 == tokens2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test scanner by scanning a file, expecting no errors.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* dataFilename = File to scan.
|
||||
* canonicalFilename = Another file to scan, in canonical YAML format.
|
||||
*/
|
||||
void testScanner(bool verbose, string dataFilename, string canonicalFilename)
|
||||
{
|
||||
foreach(filename; [dataFilename, canonicalFilename])
|
||||
{
|
||||
string[] tokens;
|
||||
scope(exit)
|
||||
{
|
||||
if(verbose){writeln(tokens);}
|
||||
}
|
||||
auto loader = Loader(filename);
|
||||
foreach(ref token; loader.scan()){tokens ~= to!string(token.id);}
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
writeln("D:YAML tokens unittest");
|
||||
run("testTokens", &testTokens, ["data", "tokens"]);
|
||||
run("testScanner", &testScanner, ["data", "canonical"]);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue