From a591f0fce8ebccd87f2e199479a0f91ad182f003 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Sun, 7 Apr 2013 14:21:43 -0600 Subject: [PATCH] generate TAP output with the default C++ templates --- README.md | 10 ++++-- src/com/dogcows/resources/C++Driver | 47 +++++++++++++++++---------- src/com/dogcows/resources/C++Makefile | 38 +++++++++++++++------- 3 files changed, 64 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 7068851..0872d10 100644 --- a/README.md +++ b/README.md @@ -182,8 +182,7 @@ template. If you are using the default Makefile template for C++, typing ":make" without any arguments will compile your code. Typing ":make run" will run all of the test cases against your code. Typing ":make test" will also run the test -cases against your code, up until the first failed test at which point it will -abort. +cases against your code, except it will abort at the first failed test. A Makefile template is not yet provided for any other language, but you can write one yourself if you are so inclined. Read on to learn how templates @@ -210,6 +209,13 @@ set to C++, the driver code would be in the driver.cc file. You normally don't have to do anything with this file. It just provides supporting code for running the test cases against your code. +The driver should output TAP (Test Anything Protocol) so that tests can be run +in a test harness such as [prove](http://search.cpan.org/perldoc?prove). The +default Makefile template has a `prove` target (type ":make prove") that can +run the tests in a test harness; the test harness is `prove` unless otherwise +configured. TAP output is also very human-readable all by itself, so having +a test harness isn't really required. + A default driver template is currently only provided for the C++ language. You could write your own template if you wanted to. diff --git a/src/com/dogcows/resources/C++Driver b/src/com/dogcows/resources/C++Driver index 460916b..d08ded8 100644 --- a/src/com/dogcows/resources/C++Driver +++ b/src/com/dogcows/resources/C++Driver @@ -11,8 +11,6 @@ #include #include -using namespace std; - const static double __EPSILON = 1e-9; static double __time = 0.0; @@ -126,11 +124,17 @@ bool __equals(double actual, double expected) bool __equals(const std::vector& actual, const std::vector& expected) { if (actual.size() != expected.size()) + { return false; + } for (size_t i = 0; i < actual.size(); ++i) + { if (!__equals(actual[i], expected[i])) + { return false; + } + } return true; } @@ -144,50 +148,57 @@ int main(int argc, char* argv[]) if (1 < argc) __abort_on_fail = true; + std::cout << "TAP version 13" << std::endl; + std::cout.flush(); + std::ifstream __in("testcases.txt"); for(;;) { + int __testnum = __pass + __fail + 1; + $RETURNTYPE$ __expected; $METHODPARAMDECLARES$ __in >> __expected >> $METHODPARAMSTREAMIN$; if (!__in.good()) break; - std::cout << "----------------------------------------" << std::endl - << "Test " << (__pass + __fail) << ": "; + std::cout << "# input for test " << __testnum << ": " << $METHODPARAMSTREAMOUT$ << std::endl; std::cout.flush(); __timer_start(); - $CLASSNAME$ object; - $RETURNTYPE$ __actual = object.$METHODNAME$($METHODPARAMNAMES$); + $CLASSNAME$ __object; + $RETURNTYPE$ __actual = __object.$METHODNAME$($METHODPARAMNAMES$); double __t = __timer_stop(); + std::cout << "# test completed in " << __t << " seconds" << std::endl; + std::cout.flush(); + if (__equals(__actual, __expected)) { - std::cout << "[PASS] in " << __t << " seconds." << std::endl; + std::cout << "ok"; ++__pass; } else { - std::cout << "[FAIL] in " << __t << " seconds." << std::endl - << "-> Input: " << $METHODPARAMSTREAMOUT$ << std::endl - << " Actual: " << __actual << std::endl - << " Expected: " << __expected << std::endl; + std::cout << "not ok"; ++__fail; - if (__abort_on_fail) std::abort(); } + + std::cout << " " << __testnum << " - " << __actual << " must equal " << __expected << std::endl; + std::cout.flush(); + + if (__abort_on_fail && 0 < __fail) std::abort(); } - std::cout << "========================================" << std::endl - << " Total Pass: " << __pass << std::endl - << " Total Fail: " << __fail << std::endl; + std::cout << "1.." << (__pass + __fail) << std::endl + << "# passed: " << __pass << std::endl + << "# failed: " << __fail << std::endl; if (__fail == 0) { - std::cout << std::endl << "Nice! " - << "Don't forget to compile remotely before submitting." - << std::endl; + std::cout << std::endl + << "# Nice! Don't forget to compile remotely before submitting." << std::endl; } return __fail; diff --git a/src/com/dogcows/resources/C++Makefile b/src/com/dogcows/resources/C++Makefile index 68c794a..3406fb0 100644 --- a/src/com/dogcows/resources/C++Makefile +++ b/src/com/dogcows/resources/C++Makefile @@ -1,26 +1,42 @@ +# Set the command for your C++ compiler, and specify any compiler flags you +# want to use (e.g. -g -Werror). CXX = g++ -CXXFLAGS = -O0 -ggdb -Wall +CXXFLAGS = -ggdb -Wall -all: driver +# The driver outputs TAP (Test Anything Protocol), so it can also be used with +# any TAP test harness (e.g. prove). Set the path to your test harness here, +# then use the `prove' target to run the tests with that harness. +PROVE = prove +PROVEFLAGS = -e "" -run: all - ./driver -test: all - ./driver -exit_on_fail +ifeq ($(OS),Windows_NT) +EXEEXT = .exe +endif -driver.o: $CLASSNAME$.o -driver: driver.o - $(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS) $(LDLIBS) +all: driver$(EXEEXT) clean: - rm -f $CLASSNAME$.o driver.o driver + rm -f driver$(EXEEXT) distclean: clean rm -f $CLASSNAME$ -.PHONY: all run test clean distclean +run: all + ./driver$(EXEEXT) + +test: all + ./driver$(EXEEXT) --abort-on-fail + +prove: all + $(PROVE) $(PROVEFLAGS) ./driver$(EXEEXT) + + +%$(EXEEXT): %.cc + $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +.PHONY: all clean distclean run test prove # vim:ft=make:noet:ts=8 -- 2.43.0