Examples block

Solomon E via Digitalmars-d digitalmars-d at puremagic.com
Sun Aug 21 02:22:54 PDT 2016


On Sunday, 21 August 2016 at 09:02:09 UTC, Solomon E wrote:
> On Saturday, 20 August 2016 at 20:39:13 UTC, Engine Machine 
> wrote:
>> We have a unittest, what about an examples?
....
>
> It seems like there could be a library function that 
> compile-time-reflects to make a collection of all the functions 
> in a module that have names starting with "maintest" and calls 
> each of them in a try block, with a catch block that just 
> prints the error messages to stderr and incorrect return codes 
> and counts the total fails and finally counts the number of 
> tests run to return the success ratio.
>
[replying to my own post]

So here's what I wrote on the idea in D, as it cooled down this 
evening. It was an interesting exercise in D style. I kept 
getting blocked by suggested features not being available (static 
foreach, enum string[], etc.) So I decided to cut it down to 
something a little simpler than I was going for, no imports or 
string mixins or traits or underscores, just the core D language.

I think it shows there are enough features in D to get something 
done like building a test framework that allows multiplying the 
number of tests that can be run, at a small overhead in verbosity 
(i.e. where I had to repeat the function refs and function names.)

module patterntester;

struct PatternTestResults(returnT)
{
     ResultsArray!(returnT)[string] funCases;
     int failures;
     int successes;
     int exceptions;
     int tries;
     int funs;
     ulong cases;
}

struct ResultsArray(returnT)
{
     PatternTestResult!returnT[int] res;
}

enum SingleTestStatus { UNTESTED = 0, SUCCESS, FAILURE, EXCEPTION 
};

struct PatternTestResult(returnT)
{
     returnT returned;
     Exception exception;
     SingleTestStatus status;
}

struct TestIO(argsT, returnT)
{
     argsT arguments;
     returnT expect;
     this(argsT args, returnT ret)
     {
         arguments = args;
         expect = ret;
     }
}

immutable(Match!(argsT, returnT))[] FunctionGetter(string 
pattern, argsT,
                                                    returnT)()
{
     Match!(argsT, returnT)[] matches;
     foreach(num, fun; maintests.contents)
     {
         string sym = maintests.names[num];
         if (pattern == sym[0 .. pattern.length])
         {
             matches ~= Match!(argsT, returnT)(sym, fun);
         }
     }
     return matches.idup;
}

struct Match(argsT, returnT)
{
     string funName;
     returnT function(argsT) funRef;
     this(string funStr, returnT function(argsT) funRefer)
     {
         funName = funStr;
         funRef = funRefer;
     }
}

PatternTestResults!returnT PatternTest(string pattern, argsT, 
returnT)
                        (TestIO!(argsT, returnT)[int] tests)
{
     enum Match!(argsT, returnT)[] matches
         = FunctionGetter!(pattern, argsT, returnT)();
     PatternTestResults!returnT results;
     alias STS = SingleTestStatus;
     foreach(match; matches)
     {
         string funStr = match.funName;
         returnT function(argsT) funref = match.funRef;
         results.funCases[funStr] = ResultsArray!returnT();
         foreach(tnum, testPair; tests)
         {
             argsT args = testPair.arguments;
             returnT expect = testPair.expect;
             auto singleResult = PatternTestResult!returnT();
             try
             {
                 auto exitcode = funref(args);
                 if (exitcode != expect)
                 {
                     ++results.failures;
                     singleResult.status = STS.FAILURE;
                 }
                 else
                 {
                     ++results.successes;
                     singleResult.status = STS.SUCCESS;
                 }
                 singleResult.returned = exitcode;
             }
             catch (Exception ex)
             {
                 ++results.failures;
                 ++results.exceptions;
                 singleResult.exception = ex;
                 singleResult.status = STS.EXCEPTION;
             }
             finally
             {
                 ++results.tries;
             }
             results.funCases[funStr].res[tnum] = singleResult;
         }
         ++results.funs;
     }
     results.cases = tests.length;
     assert(results.cases * results.funs == results.tries);
     return results;
}

struct Flist(argsT, returnT)
{
     alias funt = returnT function(argsT);
     funt[] contents;
     string[] names;
}

enum maintests = Flist!(string[], int)([&maintestA,  &maintestB,  
&maintestC,
                                         &maintestD],
                                        ["maintestA", "maintestB", 
"maintestC",
                                         "maintestD"]);

int maintestA(string[] args)
{
     return 0;
}

int maintestB(string[] args)
{
     return 1;
}

int maintestC(string[] args)
{
     throw new Exception("meant throw");
     return 0;
}

int maintestD(string[] args)
{
     return 3 * (args[$ - 1] == "-A");
}

unittest
{
     string df = "./a.out";
     alias fntype = TestIO!(string[], int);

     // first test set: one success
     auto aresult = PatternTest!("maintestA", string[], int)
                                ([1: fntype([df,"a","b"], 0)]);
     assert(aresult.failures == 0);
     assert(aresult.tries == 1);
     assert(aresult.exceptions == 0);
     assert(aresult.successes == 1);
     assert(aresult.funCases["maintestA"].res[1].returned == 0);
     assert(aresult.funCases["maintestA"].res[1].exception is 
null);
     alias STS = SingleTestStatus;
     assert(aresult.funCases["maintestA"].res[1].status == 
STS.SUCCESS);

     // second test set: one failure
     auto bresult = PatternTest!("maintestB", string[], int)
                                ([1: fntype([df,"a","b"], 0)]);
     assert(bresult.failures == 1);
     assert(bresult.tries == 1);
     assert(bresult.exceptions == 0);
     assert(bresult.successes == 0);
     assert(bresult.funCases["maintestB"].res[1].returned == 1);
     assert(bresult.funCases["maintestB"].res[1].exception is 
null);

     // third test set: one exception
     auto cresult = PatternTest!("maintestC", string[], int)
                                ([1: fntype([df,"a","b"], 0)]);
     assert(cresult.failures == 1);
     assert(cresult.tries == 1);
     assert(cresult.exceptions == 1);
     assert(cresult.successes == 0);
     assert(cresult.funCases["maintestC"].res[1].returned == 
int.init);
     assert(cresult.funCases["maintestC"].res[1].exception !is 
null);
     assert(cresult.funCases["maintestC"].res[1].exception.msg == 
"meant throw");

     // fourth test set: multiplied tests
     auto dresult = PatternTest!("maintest", string[], int)
                                ([1: fntype([df,"a","b"], 0),
                                  2: fntype([df,"c","d"], 0),
                                  3: fntype([df,"-x","-A"], 3)]);
     assert(dresult.failures == 7);
     assert(dresult.tries == 12);
     assert(dresult.exceptions == 3);
     assert(dresult.successes == 5);

}

void main(string[] args)
{
     assert(args[0] == "./d.out");
}



More information about the Digitalmars-d mailing list