Skip to content

Commit

Permalink
enhancements to README and more code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Kosta-Github committed Mar 28, 2014
1 parent 6acebe8 commit ae8410a
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 56 deletions.
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,75 @@ Feature set:
- can use custom reporters (provided: simple ide/console reporter, JUnit XML reporter)
- provides capabilities for mocking classes and interfaces (planned)


status: alpha
=============
The development has just started and is not considered to be stable at the moment.


sample usage
============
A very simple failing test case would look like this:
```C++
CUTE_TEST("Test 2 strings for equality") {
auto str1 = std::string("hello");
auto str2 = std::string("world");
CUTE_ASSERT(str1 == str2);
}
```
This would produce the following output with the provided IDE reporter:
```
.../cute_tests.cpp:64: error: Test 2 strings for equality
.../cute_tests.cpp:64: error: duration: 0 ms
.../cute_tests.cpp:67: error: reason: str1 == str2
.../cute_tests.cpp:67: error: with: str1 == str2 => "hello" == "world"
.../cute_tests.cpp:67: error: with: str1 => "hello"
.../cute_tests.cpp:67: error: with: str2 => "world"
```
Capturing additional values and reporting them in case of an error can be achieved by adding some `CUTE_CAPTURE()` calls to `CUTE_ASSERT()`:
```C++
CUTE_TEST("Capture additional values") {
auto str1 = std::string("hello");
auto str2 = std::string("world");
auto value = 42;
CUTE_ASSERT(str1 == str2,
CUTE_CAPTURE(str1.length()),
CUTE_CAPTURE(str2.capacity()),
CUTE_CAPTURE(value)
);
}
```
This would produce the following output with the provided IDE reporter:
```
.../cute_tests.cpp:70: error: Capture additional values
.../cute_tests.cpp:70: error: duration: 0 ms
.../cute_tests.cpp:79: error: reason: str1 == str2
.../cute_tests.cpp:79: error: with: str1 == str2 => "hello" == "world"
.../cute_tests.cpp:79: error: with: str1.size() => 5
.../cute_tests.cpp:79: error: with: str2.capacity() => 22
.../cute_tests.cpp:79: error: with: value => 42
```

The following macros can be used for validation within a test case:
- `CUTE_ASSERT(ex)`: verifies that the given boolean expression `ex` evaluates to `true`.
- `CUTE_ASSERT_THROWS_NOT(ex)`: verifies that evaluating the given expression `ex` will not throw an `exception`.
- `CUTE_ASSERT_THROWS(ex)`: verifies that evaluating the given expression `ex` will throw an `exception`.
- `CUTE_ASSERT_THROWS_AS(ex, excp)`: verifies that evaluating the given expression `ex` will throw an `exception` of type `excp` or derived from it.

Each validation macro can be extended by several `CUTE_CAPTURE()` occurrences in order to capture additional values and provide more context information in case of a failure.

Note that `cute` stops a test case by throwing an `exception` as soon as a violation of a `CUTE_ASSERT()` is detected.


related work
============
Based on the following ideas and frameworks:
- [lest](https://github.com/martinmoene/lest)
- [catch](https://github.com/philsquared/Catch)
- [turtle](http://turtle.sourceforge.net/)


dependencies
============
- [cmake](http://cmake.org/) (only required for building and executing the self-test unit test suite)
6 changes: 3 additions & 3 deletions cute/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ namespace cute {

auto const count_end = eval.checks_performed.load();
if(count_start == count_end) {
eval.register_exception(cute::exception("no check performed in test case", test.file, test.line, ""));
eval.register_exception(cute::exception(test.file, test.line, "no check performed in test case"));
}

// ensure that the temp folder can be cleared and that no file locks exists after the test case
if(!eval.delete_temp_folder()) {
eval.register_exception(cute::exception("could not cleanup temp folder", test.file, test.line, ""));
eval.register_exception(cute::exception(test.file, test.line, "could not cleanup temp folder"));
}
} catch(...) {
// nothing to do
Expand Down Expand Up @@ -109,7 +109,7 @@ namespace cute {

if(auto test = ctx.current_test) {
auto rep = test_result(*test, result_type::fatal);
rep.excp = std::make_shared<exception>("std::terminate() called", test->file, test->line, "");
rep.excp = std::make_shared<exception>(test->file, test->line, "std::terminate() called");

for(auto&& reporter : *ctx.reporters) {
if(reporter) { reporter(rep, 0, 1); }
Expand Down
62 changes: 31 additions & 31 deletions cute/detail/macros_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,39 @@
#define CUTE_DETAIL_UNIQUE_NAME_LINE(NAME, LINE) CUTE_DETAIL_UNIQUE_NAME_LINE_2(NAME, LINE)
#define CUTE_DETAIL_UNIQUE_NAME(NAME) CUTE_DETAIL_UNIQUE_NAME_LINE(NAME, __LINE__)

#define CUTE_DETAIL_ASSERT(EXPR_EVAL, FILE, LINE, EXPR_TEXT, CAPS1, CAP2) \
try { \
++cute::detail::eval_context::current().checks_performed; \
if(!(EXPR_EVAL)) { \
cute::detail::eval_context::current().register_exception(cute::exception(EXPR_TEXT, FILE, LINE, "", CAPS1, CAP2)); \
} \
} catch(cute::exception const& ex) { \
cute::detail::eval_context::current().register_exception(ex); \
} catch(std::exception const &ex) { \
cute::detail::eval_context::current().register_exception( \
cute::exception("got an unexpected exception with message \"" + std::string(ex.what()) + "\"", FILE, LINE, EXPR_TEXT, CAPS1, CAP2) \
); \
} catch(...) { \
cute::detail::eval_context::current().register_exception( \
cute::exception("got an unexpected exception of unknown type", FILE, LINE, EXPR_TEXT, CAPS1, CAP2) \
); \
#define CUTE_DETAIL_ASSERT(EXPR_EVAL, FILE, LINE, EXPR_TEXT, CAPTURES1, CAPTURES2) \
try { \
++cute::detail::eval_context::current().checks_performed; \
if(!(EXPR_EVAL)) { \
cute::detail::eval_context::current().register_exception(cute::exception(FILE, LINE, EXPR_TEXT, "", CAPTURES1, CAPTURES2)); \
} \
} catch(cute::exception const& ex) { \
cute::detail::eval_context::current().register_exception(ex); \
} catch(std::exception const &ex) { \
cute::detail::eval_context::current().register_exception( \
cute::exception(FILE, LINE, "got an unexpected exception with message \"" + std::string(ex.what()) + "\"", EXPR_TEXT, CAPTURES1, CAPTURES2) \
); \
} catch(...) { \
cute::detail::eval_context::current().register_exception( \
cute::exception(FILE, LINE, "got an unexpected exception of unknown type", EXPR_TEXT, CAPTURES1, CAPTURES2) \
); \
}

#define CUTE_DETAIL_ASSERT_THROWS_AS(EXPR, EXCEPT) \
{ \
auto CUTE_DETAIL_UNIQUE_NAME(exception_ok) = false; \
try { \
++cute::detail::eval_context::current().checks_performed; \
static_cast<void>(EXPR); \
} catch(EXCEPT const&) { \
CUTE_DETAIL_UNIQUE_NAME(exception_ok) = true; \
} catch(...) { \
} \
if(!CUTE_DETAIL_UNIQUE_NAME(exception_ok)) { \
cute::detail::eval_context::current().register_exception( \
cute::exception("didn't get an expected exception of type \"" #EXCEPT "\"", __FILE__, __LINE__, #EXPR) \
); \
} \
#define CUTE_DETAIL_ASSERT_THROWS_AS(EXPR, EXCEPT, CAPTURES) \
{ \
auto CUTE_DETAIL_UNIQUE_NAME(exception_ok) = false; \
try { \
++cute::detail::eval_context::current().checks_performed; \
static_cast<void>(EXPR); \
} catch(EXCEPT const&) { \
CUTE_DETAIL_UNIQUE_NAME(exception_ok) = true; \
} catch(...) { \
} \
if(!CUTE_DETAIL_UNIQUE_NAME(exception_ok)) { \
cute::detail::eval_context::current().register_exception( \
cute::exception(__FILE__, __LINE__, "didn't get an expected exception of type \"" #EXCEPT "\"", #EXPR, CAPTURES) \
); \
} \
}

#define CUTE_DETAIL_INIT() \
Expand Down
23 changes: 14 additions & 9 deletions cute/exception.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,37 @@

#include "capture.hpp"

#include <stdexcept>
#include <cassert>
#include <string>
#include <vector>

namespace cute {

struct exception : std::runtime_error {
// The cute exception is not derived from std::exception by intend,
// otherwise we could not differentiate within CUTE_THROWS_AS()
// between expected std::exceptions and cute::exceptions.
struct exception {
std::string const file;
int const line;
std::string const expr;
std::string const message;
std::string const expression;
cute::captures const captures;

inline exception(
std::string msg_,
std::string file_,
int line_,
std::string expr_,
std::string message_,
std::string expression_ = "",
cute::captures caps1_ = cute::captures(),
cute::captures caps2_ = cute::captures()
) :
std::runtime_error(std::move(msg_)),
file(std::move(file_)),
line(std::move(line_)),
expr(std::move(expr_)),
message(std::move(message_)),
expression(std::move(expression_)),
captures(std::move(caps1_), std::move(caps2_))
{ }
{
assert(!message.empty());
}
};

} // namespace cute
15 changes: 7 additions & 8 deletions cute/macros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@

#include "detail/macros_impl.hpp"

#define CUTE_CAPTURE(EXPR) cute::capture(#EXPR, CUTE_DETAIL_DECOMPOSE(EXPR))
#define CUTE_INIT() CUTE_DETAIL_INIT()
#define CUTE_TEST(...) CUTE_DETAIL_TEST(__VA_ARGS__)
#define CUTE_CAPTURE(EXPR) cute::capture(#EXPR, CUTE_DETAIL_DECOMPOSE(EXPR))

#define CUTE_ASSERT(EXPR, ...) CUTE_DETAIL_ASSERT(EXPR, __FILE__, __LINE__, #EXPR, CUTE_CAPTURE(EXPR), cute::captures(__VA_ARGS__))
#define CUTE_ASSERT_THROWS_NOT(EXPR) CUTE_DETAIL_ASSERT(((void)(EXPR), true), __FILE__, __LINE__, #EXPR, cute::captures(), cute::captures())
#define CUTE_ASSERT_THROWS(EXPR) CUTE_DETAIL_ASSERT_THROWS_AS(EXPR, std::exception)
#define CUTE_ASSERT_THROWS_AS(EXPR, EXCEPT) CUTE_DETAIL_ASSERT_THROWS_AS(EXPR, EXCEPT)
#define CUTE_ASSERT(EXPR, ...) CUTE_DETAIL_ASSERT(EXPR, __FILE__, __LINE__, #EXPR, CUTE_CAPTURE(EXPR), cute::captures(__VA_ARGS__))
#define CUTE_ASSERT_THROWS_NOT(EXPR, ...) CUTE_DETAIL_ASSERT(((void)(EXPR), true), __FILE__, __LINE__, #EXPR, cute::captures(__VA_ARGS__), cute::captures())
#define CUTE_ASSERT_THROWS(EXPR, ...) CUTE_DETAIL_ASSERT_THROWS_AS(EXPR, std::exception, cute::captures(__VA_ARGS__))
#define CUTE_ASSERT_THROWS_AS(EXPR, EXCEPT, ...) CUTE_DETAIL_ASSERT_THROWS_AS(EXPR, EXCEPT, cute::captures(__VA_ARGS__))

#define CUTE_INIT() CUTE_DETAIL_INIT()

#define CUTE_TEST(...) CUTE_DETAIL_TEST(__VA_ARGS__)
8 changes: 5 additions & 3 deletions cute/reporters/reporter_ide.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ namespace cute {
}

auto test_header = detail::ide_make_file_line_string(res.test.file, res.test.line) + type;
os << test_header << res.test.name << " [" << (test_index_cur + 1) << "/" << test_index_max << "]" << std::endl;
os << test_header << res.test.name;
if(test_index_max > 1) { os << " [" << (test_index_cur + 1) << "/" << test_index_max << "]"; }
os << std::endl;

if(res.result != result_type::skip) {
os << test_header << " duration: " << res.duration_ms << " ms" << std::endl;
Expand All @@ -52,8 +54,8 @@ namespace cute {
if(auto ex = res.excp.get()) {
auto ex_header = detail::ide_make_file_line_string(ex->file, ex->line) + type;

os << ex_header << " reason: " << ex->what() << std::endl;
if(!ex->expr.empty()) { os << ex_header << " expression: " << ex->expr << std::endl; }
os << ex_header << " reason: " << ex->message << std::endl;
if(!ex->expression.empty()) { os << ex_header << " expression: " << ex->expression << std::endl; }

for(auto&& c : ex->captures.list) {
os << ex_header << " with: " << c.name;
Expand Down
8 changes: 6 additions & 2 deletions cute/reporters/reporter_junit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#pragma once

#include "reporter_ide.hpp"
#include "../test_result.hpp"
#include "../test_suite_result.hpp"

Expand Down Expand Up @@ -56,11 +57,14 @@ namespace cute {
default: assert(false);
}

std::ostringstream text;
reporter_ide(text, test, 0, 1);

out << " <error ";
out << "type=\"" << type << "\" ";
out << "message=\"" << xml_encode(ex->what()) << "\" ";
out << "message=\"" << xml_encode(ex->message) << "\" ";
out << ">" << std::endl;
out << "at " << xml_encode(ex->file) << ":" << ex->line << std::endl;
out << xml_encode(text.str());
out << " </error>" << std::endl;
}

Expand Down

0 comments on commit ae8410a

Please sign in to comment.