Chris Gilling 97c77b4a86 add tests to check that exceptions are thrown for wrong types
* Add JSONTEST_ASSERT_THROWS macro to test if an expression
  throws an exceptions.
* add JSONTEST_FIXTURE(ValueTest, typeChecksThrowExceptions)
2014-08-13 02:00:41 -07:00

281 lines
11 KiB
C++

// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSONTEST_H_INCLUDED
#define JSONTEST_H_INCLUDED
#include <json/config.h>
#include <json/value.h>
#include <json/writer.h>
#include <stdio.h>
#include <deque>
#include <sstream>
#include <string>
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// Mini Unit Testing framework
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
/** \brief Unit testing framework.
* \warning: all assertions are non-aborting, test case execution will continue
* even if an assertion namespace.
* This constraint is for portability: the framework needs to compile
* on Visual Studio 6 and must not require exception usage.
*/
namespace JsonTest {
class Failure {
public:
const char *file_;
unsigned int line_;
std::string expr_;
std::string message_;
unsigned int nestingLevel_;
};
/// Context used to create the assertion callstack on failure.
/// Must be a POD to allow inline initialisation without stepping
/// into the debugger.
struct PredicateContext {
typedef unsigned int Id;
Id id_;
const char *file_;
unsigned int line_;
const char *expr_;
PredicateContext *next_;
/// Related Failure, set when the PredicateContext is converted
/// into a Failure.
Failure *failure_;
};
class TestResult {
public:
TestResult();
/// \internal Implementation detail for assertion macros
/// Not encapsulated to prevent step into when debugging failed assertions
/// Incremented by one on assertion predicate entry, decreased by one
/// by addPredicateContext().
PredicateContext::Id predicateId_;
/// \internal Implementation detail for predicate macros
PredicateContext *predicateStackTail_;
void setTestName(const std::string &name);
/// Adds an assertion failure.
TestResult &
addFailure(const char *file, unsigned int line, const char *expr = 0);
/// Removes the last PredicateContext added to the predicate stack
/// chained list.
/// Next messages will be targed at the PredicateContext that was removed.
TestResult &popPredicateContext();
bool failed() const;
void printFailure(bool printTestName) const;
// Generic operator that will work with anything ostream can deal with.
template <typename T> TestResult &operator<<(const T &value) {
std::ostringstream oss;
oss.precision(16);
oss.setf(std::ios_base::floatfield);
oss << value;
return addToLastFailure(oss.str());
}
// Specialized versions.
TestResult &operator<<(bool value);
// std:ostream does not support 64bits integers on all STL implementation
TestResult &operator<<(Json::Int64 value);
TestResult &operator<<(Json::UInt64 value);
private:
TestResult &addToLastFailure(const std::string &message);
unsigned int getAssertionNestingLevel() const;
/// Adds a failure or a predicate context
void addFailureInfo(const char *file,
unsigned int line,
const char *expr,
unsigned int nestingLevel);
static std::string indentText(const std::string &text,
const std::string &indent);
typedef std::deque<Failure> Failures;
Failures failures_;
std::string name_;
PredicateContext rootPredicateNode_;
PredicateContext::Id lastUsedPredicateId_;
/// Failure which is the target of the messages added using operator <<
Failure *messageTarget_;
};
class TestCase {
public:
TestCase();
virtual ~TestCase();
void run(TestResult &result);
virtual const char *testName() const = 0;
protected:
TestResult *result_;
private:
virtual void runTestCase() = 0;
};
/// Function pointer type for TestCase factory
typedef TestCase *(*TestCaseFactory)();
class Runner {
public:
Runner();
/// Adds a test to the suite
Runner &add(TestCaseFactory factory);
/// Runs test as specified on the command-line
/// If no command-line arguments are provided, run all tests.
/// If --list-tests is provided, then print the list of all test cases
/// If --test <testname> is provided, then run test testname.
int runCommandLine(int argc, const char *argv[]) const;
/// Runs all the test cases
bool runAllTest(bool printSummary) const;
/// Returns the number of test case in the suite
unsigned int testCount() const;
/// Returns the name of the test case at the specified index
std::string testNameAt(unsigned int index) const;
/// Runs the test case at the specified index using the specified TestResult
void runTestAt(unsigned int index, TestResult &result) const;
static void printUsage(const char *appName);
private: // prevents copy construction and assignment
Runner(const Runner &other);
Runner &operator=(const Runner &other);
private:
void listTests() const;
bool testIndex(const std::string &testName, unsigned int &index) const;
static void preventDialogOnCrash();
private:
typedef std::deque<TestCaseFactory> Factories;
Factories tests_;
};
template <typename T, typename U>
TestResult &checkEqual(TestResult &result,
const T &expected,
const U &actual,
const char *file,
unsigned int line,
const char *expr) {
if (static_cast<U>(expected) != actual) {
result.addFailure(file, line, expr);
result << "Expected: " << static_cast<U>(expected) << "\n";
result << "Actual : " << actual;
}
return result;
}
TestResult &checkStringEqual(TestResult &result,
const std::string &expected,
const std::string &actual,
const char *file,
unsigned int line,
const char *expr);
} // namespace JsonTest
/// \brief Asserts that the given expression is true.
/// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y;
/// JSONTEST_ASSERT( x == y );
#define JSONTEST_ASSERT(expr) \
if (expr) { \
} else \
result_->addFailure(__FILE__, __LINE__, #expr)
/// \brief Asserts that the given predicate is true.
/// The predicate may do other assertions and be a member function of the
/// fixture.
#define JSONTEST_ASSERT_PRED(expr) \
{ \
JsonTest::PredicateContext _minitest_Context = { \
result_->predicateId_, __FILE__, __LINE__, #expr \
}; \
result_->predicateStackTail_->next_ = &_minitest_Context; \
result_->predicateId_ += 1; \
result_->predicateStackTail_ = &_minitest_Context; \
(expr); \
result_->popPredicateContext(); \
}
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_EQUAL(expected, actual) \
JsonTest::checkEqual(*result_, \
expected, \
actual, \
__FILE__, \
__LINE__, \
#expected " == " #actual)
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_STRING_EQUAL(expected, actual) \
JsonTest::checkStringEqual(*result_, \
std::string(expected), \
std::string(actual), \
__FILE__, \
__LINE__, \
#expected " == " #actual)
/// \brief Asserts that a given expression throws an exception
#define JSONTEST_ASSERT_THROWS(expr) \
{ \
bool _threw = false; \
try { \
expr; \
} catch (...) { \
_threw = true; \
} \
if (!_threw) \
result_->addFailure(__FILE__, __LINE__, \
"expected exception thrown: " #expr); \
}
/// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE(FixtureType, name) \
class Test##FixtureType##name : public FixtureType { \
public: \
static JsonTest::TestCase *factory() { \
return new Test##FixtureType##name(); \
} \
\
public: /* overidden from TestCase */ \
virtual const char *testName() const { return #FixtureType "/" #name; } \
virtual void runTestCase(); \
}; \
\
void Test##FixtureType##name::runTestCase()
#define JSONTEST_FIXTURE_FACTORY(FixtureType, name) \
&Test##FixtureType##name::factory
#define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name) \
(runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name))
#endif // ifndef JSONTEST_H_INCLUDED
// vim: et ts=2 sts=2 sw=2 tw=0