dyaml/test/src/common.d

204 lines
5.6 KiB
D

// 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 dyaml.all;
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.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, 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);}
}