Initial commit.

This commit is contained in:
Ferdinand Majerech 2011-08-16 14:53:13 +02:00
commit 283c42bf8f
592 changed files with 26392 additions and 0 deletions

202
test/src/common.d Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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"]);
}