jUtAsserter : an embeddable unit tester for C++

Download jutasserter.zip

Synopsis:

main.cpp
moretests.cpp
UtAsserter.cpp
UtAsserter.h


main.cpp

Synopsis
#pragma warning(disable: 4786)
#include <exception>
#include <string>

#include <iostream>

#include "UtAsserter.h"

class myexception : public std::exception
  {
  public:
    myexception(const std::string& s) : mMsg(s)
      {
      }
    virtual const char* what() const throw()
      {
      return mMsg.c_str();
      }
    virtual ~myexception() throw()
      {
      }
    const std::string mMsg;
  } ;

//check for conflict with asserts
#include <assert.h>

TEST(a_test)
  {
  //test utassert
  utassert(0, 0);
  SelfTestAssert("");
  utassert(1, 0);
  SelfTestAssert("main.cpp(33) : a_test: FAILED: actual: 1 expected: 0");
  utassert(0L, 0L);
  SelfTestAssert("");
  utassert(1L, 0L);
  SelfTestAssert("main.cpp(37) : a_test: FAILED: actual: 1 expected: 0");
  utassert("s1", "s1");
  SelfTestAssert("");
  utassert("s1", "s2");
  SelfTestAssert("main.cpp(41) : a_test: FAILED: actual: s1 expected: s2");
  }

TEST(a_nulls_in_strings)
  {
  utassert((char*) 0, (char*) 0);
  SelfTestAssert("");
  utassert("", (const char*) 0);
  SelfTestAssert("main.cpp(49) : a_nulls_in_strings: FAILED: actual:  expected: null");
  utassert((const char*) 0, "");
  SelfTestAssert("main.cpp(51) : a_nulls_in_strings: FAILED: actual: null expected: ");
  utassert("", "");
  SelfTestAssert("");
  }

#ifdef UTASSERTER_INCLUDE_STL
TEST(stl_tests)
  {
  utassert(std::string("s1"), std::string("s2"));
  SelfTestAssert("main.cpp(60) : stl_tests: FAILED: actual: s1 expected: s2");
  }
#endif

#ifndef __GNUC__
TEST(excp_test)
  {
  utassert(0, 0);
  int x = 0;
  x = x / x;
  utassert(1, 1);
  }

TEST(excp_test3)
  {
  utassert(0, 0);
  throw std::exception("std excp");
  utassert(1, 1);
  }
#endif

TEST(excp_test2)
  {
  utassert(0, 0);
  throw myexception("my excp");
  utassert(1, 1);
  }

TEST(bool)
  {
  utassert(true, true);
  SelfTestAssert("");
  utassert(false, true);
  SelfTestAssert("main.cpp(93) : bool: FAILED: actual: false expected: true");
  utassert(true, false);
  SelfTestAssert("main.cpp(95) : bool: FAILED: actual: true expected: false");
  utassert(false, false);
  SelfTestAssert("");
  }

//------------------------------------------
int main ()
  {
  //normal example
  TestSuite::Run();

  //print the testcase names
  //TestSuite::Run(0, true);

  //print the testcase names and sort the test cases in alphabetical order
  //TestSuite::Run(-1, true);

  //print the testcase names and randomize the order of the test cases
  //TestSuite::Run(-2, true);

  //print the testcase names and randomize the order of the test cases using 500 as a seed
  //TestSuite::Run(500, true);

  TestSuite::CleanUp();
  return 0;
  }

moretests.cpp

Synopsis
#pragma warning(disable: 4786)

#include "UtAsserter.h"

TEST(a_test2)
  {
  //test utassert
  utassert(0, 0);
  SelfTestAssert("");
  utassert(1, 0);
  SelfTestAssert("moretests.cpp(10) : a_test2: FAILED: actual: 1 expected: 0");  
  }

#if 1
//generate lots of tests
#define GENTEST(n) \
  TEST(n) { utassert(0, 0); SelfTestAssert(""); }

GENTEST(a_test01)
GENTEST(a_test02)
GENTEST(a_test03)
GENTEST(a_test04)
GENTEST(a_test05)
GENTEST(a_test06)
GENTEST(a_test07)
GENTEST(a_test08)
GENTEST(a_test09)


GENTEST(a_test10)
GENTEST(a_test11)
GENTEST(a_test12)
GENTEST(a_test13)
GENTEST(a_test14)
GENTEST(a_test15)
GENTEST(a_test16)
GENTEST(a_test17)
GENTEST(a_test18)
GENTEST(a_test19)

GENTEST(a_test20)
GENTEST(a_test21)
GENTEST(a_test22)
GENTEST(a_test23)
GENTEST(a_test24)
GENTEST(a_test25)
GENTEST(a_test26)
GENTEST(a_test27)
GENTEST(a_test28)
GENTEST(a_test29)

GENTEST(a_test30)
GENTEST(a_test31)
GENTEST(a_test32)
GENTEST(a_test33)
GENTEST(a_test34)
GENTEST(a_test35)
GENTEST(a_test36)
GENTEST(a_test37)
GENTEST(a_test38)
GENTEST(a_test39)

GENTEST(a_test40)
GENTEST(a_test41)
GENTEST(a_test42)
GENTEST(a_test43)
GENTEST(a_test44)
GENTEST(a_test45)
GENTEST(a_test46)
GENTEST(a_test47)
GENTEST(a_test48)
GENTEST(a_test49)

#endif

UtAsserter.cpp

Synopsis
#pragma warning(disable: 4786)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <time.h>

#include <string>
#include <algorithm>
#include <vector>
#include <iostream>
#include <sstream>
using namespace std;

#include "UtAsserter.h"

#if defined(__GNUC__)
#define IS_GCC 1
#elif _MSC_VER >= 1300
#define IS_VC7 1
#else
#define IS_VC6 1
#endif

//print the output
//this is the only place where cout & OutputDebugString should be mentioned!
static void PrintToOutput(const string& ss)
  {
  cout << ss << endl;
  OutputDebugString(ss.c_str());
  OutputDebugString("\n");
  }

//Used for self testing only...
#if !defined(UTASSERTER_SELFTEST)
  //when undefined abosorb calls.
  inline void SelfTestClear() {}
  inline void SelfTestSet(const string& /*ss*/) {}

#else
//Generate code to self test
static string gCurrentOutputLine;

inline void SelfTestClear()
  {
  gCurrentOutputLine = "";
  }
inline void SelfTestSet(const string& ss)
  {
  gCurrentOutputLine = ss;
  }

void SelfTestAssert(const char* const expected)
  {
  string ex = expected;

#if defined(IS_VC7)
  if (ex != "") ex = string(".\\") + expected;
#elif defined(IS_VC6)
  if (ex != "") ex = string("D:\\projects\\src\\jUtAsserter\\") + expected;
#else
  if (ex != "") ex = string("D:/projects/src/jUtAsserter/") + expected;
#endif

  if (ex == gCurrentOutputLine) return;
  ostringstream msg;
  msg << "SELF TEST FAILED: \n   expected: '" << ex << "'\n   actual  : '" << gCurrentOutputLine << "'";
  PrintToOutput(msg.str());
  }
#endif

//save the current output line (for self test) and print it out
//Do not use this function in selftest
static void PrintIt(const string& ss)
  {
  SelfTestSet(ss);
  PrintToOutput(ss);
  }

//create the first part of the assert failure message
//the format is useful within the VC6 IDE for "next-error" (F4)
static void Prefix(ostringstream& ss, const char* const fname, long lineno, const string& casename)
  {
  ss << fname << "(" << lineno << ") : " << casename << ": FAILED: ";
  }

//Suffix can handle most of the types correctly
template <typename T>
static void Suffix(ostringstream& ss, T actual, T expected)
  {
  ss << "actual: " << actual << " expected: " << expected;
  }

#if !defined(IS_VC6)
template<>
#endif
static void Suffix(ostringstream& ss, bool actual, bool expected)
  {
  ss << "actual: " << boolalpha << actual << " expected: " << expected;
  }

#if !defined(IS_VC6)
template<>
#endif
static void Suffix(ostringstream& ss, const char* actual, const char* expected)
  {
  if (actual == 0 && expected == 0)
    ss << "actual: null expected: null";
  else if (actual == 0 && expected != 0)
    ss << "actual: null expected: " << expected;
  else if (actual != 0 && expected == 0)
    ss << "actual: " << actual << " expected: null";
  else
    ss << "actual: " << actual << " expected: " << expected;
  }

//holds test case information
struct TestCase
  {
  explicit TestCase(const char* const fname, long lineno, const char* const casename, TestSuite::TESTCASEFP tc)
    : mName(casename), mFile(fname), mLineno(lineno), mTest(tc)
    {
    }
  void operator()() const
    {
    mTest();
    }
  void GetPrefix(ostringstream& ss) const
    {
    Prefix(ss, mFile.c_str(), mLineno, mName);
    }
  bool operator < (const TestCase& other) const
    {
    return mName < other.mName;
    }

  std::string mName;
  std::string mFile;
  long mLineno;
  TestSuite::TESTCASEFP mTest;
  } ;

//holds private variables
struct TestSuiteImpl
  {
  typedef std::vector<TestCase> TestCaseList;

  TestSuiteImpl()
    : mNumAsserts(0), mNumFailures(0), mNumExcps(0)
    {
    }
  ~TestSuiteImpl()
    {
    mSuite.clear();
    }
  const TestCase& Add(const TestCase& tc)
    {
    mSuite.push_back(tc);
    return tc;
    }
  void BumpAsserts()
    {
    mNumAsserts++;
    }
  void BumpFailures()
    {
    mNumFailures++;
    }

  //runs the actual suite of test cases
  void Run(int order, bool printtestcasenames)
    {
    switch(order)
      {
      case  0: break; //do nothing
      case -1: sort(mSuite.begin(), mSuite.end()); break;
      case -2:
        srand((unsigned int)time(0));
        random_shuffle(mSuite.begin(), mSuite.end());
        break;
      default:
        srand(order);
        random_shuffle(mSuite.begin(), mSuite.end());
        break;
      }

    for(TestCaseList::iterator it = mSuite.begin(); it != mSuite.end(); ++it)
      {
      try
        {
        CurrentCaseName = &((*it).mName);
        if (printtestcasenames)
          {
          ostringstream ss;
          ss << "TestCase: " << *CurrentCaseName;
          PrintIt(ss.str());
          }
        (*it)();
        }
      catch(const char* ex)
        {
        ReportException(*it, ex);
        }
      catch(int ex)
        {
        ReportException(*it, ex);
        }
      catch(unsigned int ex)
        {
        ReportException(*it, ex);
        }
      catch(const exception& ex)
        {
        ReportException(*it, ex.what());
        }
      catch(const exception* const ex)
        {
        ReportException(*it, ex->what());
        }
      catch(...)
        {
        ReportException(*it, (const char*)0);
        }
      }
    }

  template <typename T>
    void ReportException(const TestCase& tc, T ex)
    {
    mNumExcps++;
    ostringstream ss;
    tc.GetPrefix(ss);
    ss << "threw exception";
    if (ex != 0)
      ss << ": " << ex;
    PrintIt(ss.str());
    }

  //generate a report of failures, etc.
  void Report()
    {
    ReportStat("Num Test Cases", (unsigned int) mSuite.size());
    ReportStat("Num Asserts   ", mNumAsserts);
    ReportStat("Num Failures  ", mNumFailures);
    ReportStat("Num Exceptions", mNumExcps);
    }

  template <typename T>
    void ReportStat(const string& title, T stat)
    {
    ostringstream ss;
    ss << title << ": " << stat;
    PrintIt(ss.str());
    }

  TestCaseList mSuite;
  string* CurrentCaseName;
  int mNumAsserts;
  int mNumFailures;
  int mNumExcps;
  } ;

//the test suite
static TestSuiteImpl* gsuite = 0;

//forward the Run request to impl
void TestSuite::Run(int order, bool printtestcasenames)
  {
  if (gsuite == 0) return;
  gsuite->Run(order, printtestcasenames);
  gsuite->Report();
  }

void TestSuite::CleanUp()
  {
  delete gsuite;
  gsuite = 0;
  }

//add a test case to the suite
const TestCase& TestSuite::CreateTestCase(const char* const fname, long lineno, const char* const casename, TestSuite::TESTCASEFP tc)
  {
  if (gsuite == 0)
    gsuite = new TestSuiteImpl();
  return gsuite->Add(TestCase(fname, lineno, casename, tc));
  }

//most asserts will follow this code exactly
//the only difference will be the incoming types for the expected
//and actual values... so use a template
template <class T>
static bool cmp(T lhs, T rhs)
  {
  return lhs == rhs;
  }

#if !defined(IS_VC6)
template<>
#endif
static bool cmp(const char*  lhs, const char*  rhs)
  {
  if (lhs == 0 && rhs == 0) return true;
  if (lhs == 0 && rhs != 0) return false;
  if (lhs != 0 && rhs == 0) return false;
  return strcmp(lhs, rhs) == 0;
  }

template <class T>
static inline void AssertIt(const char* const fname, long lineno, T actual, T expected)
  {
  SelfTestClear();
  gsuite->BumpAsserts();
  if (cmp<T>(actual, expected)) return;
  gsuite->BumpFailures();
  ostringstream ss;
  Prefix(ss, fname, lineno, *gsuite->CurrentCaseName);
  Suffix<T>(ss, actual, expected);
  PrintIt(ss.str());
  }

//just forward to the templated function
void TestSuite::testassert(const char* const fname, long lineno, int actual, int expected)
  {
  AssertIt(fname, lineno, actual, expected);
  }
void TestSuite::testassert(const char* const fname, long lineno, long actual, long expected)
  {
  AssertIt(fname, lineno, actual, expected);
  }
void TestSuite::testassert(const char* const fname, long lineno, const char* const actual, const char* const expected)
  {
  AssertIt(fname, lineno, actual, expected);
  }
void TestSuite::testassert(const char* const fname, long lineno, bool actual, bool expected)
  {
  AssertIt(fname, lineno, actual, expected);
  }

#ifdef UTASSERTER_INCLUDE_STL
void TestSuite::testassert(const char* const fname, long lineno, const string& actual, const string& expected)
  {
  AssertIt(fname, lineno, actual, expected);
  }
#endif

UtAsserter.h

Synopsis
#pragma once
//use this define if you want to include asserts for STL types
//leave it commented out if it conflicts with other libraries
//#define UTASSERTER_INCLUDE_STL

#ifdef UTASSERTER_INCLUDE_STL
#include <string>
#endif

//--------------
struct TestCase;
class TestSuite
  {
  public:
    //run all testcases in the suite
    //if randomizeseed ==  0, the test cases are run in insertion order
    //if randomizeseed == -1, the test cases are run in sorted order
    //if randomizeseed == -2, the test cases are run in random order using the time as a seed
    //else, the test cases are run in random order using randomizeseed as a seed
    static void Run(int order = 0, bool printtestcasenames = false);
    static void CleanUp();
    typedef void (*TESTCASEFP) ();
    static const TestCase& CreateTestCase(const char* fname, long lineno, const char* casename, TESTCASEFP tc);

    //define all of the assert overloads
#define DefineAssert(t) static void testassert(const char* fname, long lineno, t actual, t expected)
    DefineAssert(int);
    DefineAssert(long);
    DefineAssert(const char*);
    DefineAssert(bool);
#ifdef UTASSERTER_INCLUDE_STL
    DefineAssert(const std::string&);
#endif

  private:
    TestSuite();  //don't allow creation or copy
    ~TestSuite();
  } ;

//------------
//define a test
#define TEST(s) \
  void Test##s(); \
  const TestCase& TestCase_Test_##s = TestSuite::CreateTestCase(__FILE__, __LINE__, #s, Test##s); \
  void Test##s()

//assert any failing behaviour
#define utassert(actual, expected) TestSuite::testassert(__FILE__, __LINE__, actual, expected)

//only use this for testing UtAsserter
#if defined(UTASSERTER_SELFTEST)
void SelfTestAssert(const char* const expected);
#else
#define SelfTestAssert(s)
#endif







Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010