Skip to content

Commit

Permalink
Merge branch 'release/0.1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
griwes committed Oct 2, 2014
2 parents 470d9f3 + ec9d9dc commit 7317d8f
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 57 deletions.
2 changes: 1 addition & 1 deletion VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* 0.1.x alpha:
* 0.1.0 - initial release; console reporter, subprocess runner and basic checking macros done at least partially - *finished on 09.06.2014*
* 0.1.1 - nested suites; negative tests; TeamCity reporter; duration measurement for the tests - *finished on 06.09.2014*
* 0.1.2 - refactor subprocess runner to allow printing (almost?) arbitrary output from tests; print info from exceptions thrown in tests
* 0.1.2 - refactor subprocess runner to allow printing (almost?) arbitrary output from tests; print info from exceptions thrown in tests; print files and lines in assertions - *finished on 02.10.2014*
* 0.1.3 - add support for sanitizers: leak, address, memory, thread, ub
* 0.1.4 - print values of operands in `MAYFLY_CHECK` and `MAYFLY_REQUIRE`
* 0.1.5 - test annotations
Expand Down
1 change: 1 addition & 0 deletions include/reaver/mayfly.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#pragma once

#include "mayfly/detail/atexit.h"
#include "mayfly/suite.h"
#include "mayfly/testcase.h"
#include "mayfly/reporter.h"
Expand Down
75 changes: 44 additions & 31 deletions include/reaver/mayfly/asserts.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,34 +169,47 @@ namespace reaver
}}
}

#define MAYFLY_REQUIRE(...) \
try { if (!(__VA_ARGS__)) { ::reaver::mayfly::log_assertion(#__VA_ARGS__, true); } } \
catch (::reaver::mayfly::expected_failure_exit) { throw; } \
catch (...) { ::reaver::mayfly::log_assertion(#__VA_ARGS__ " has thrown an unexpected exception", true); }

#define MAYFLY_CHECK(...) \
try { if (!(__VA_ARGS__)) { ::reaver::mayfly::log_assertion(#__VA_ARGS__); } } \
catch (::reaver::mayfly::expected_failure_exit) { throw; } \
catch (...) { ::reaver::mayfly::log_assertion(#__VA_ARGS__ " has thrown an unexpected exception", true); }

#define MAYFLY_REQUIRE_THROWS(...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(#__VA_ARGS__ " should have thrown, but didn't", true); } catch (...) {}

#define MAYFLY_CHECK_THROWS(...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(#__VA_ARGS__ " should have thrown, but didn't"); } catch (...) {}

#define MAYFLY_REQUIRE_THROWS_TYPE(type, ...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(#__VA_ARGS__ " should have thrown " #type ", but didn't throw anything", true); } \
catch (type & e) {} \
catch (...) { ::reaver::mayfly::log_assertion(#__VA_ARGS__ " should have thrown " #type ", but has thrown something else", true); }

#define MAYFLY_CHECK_THROWS_TYPE(type, ...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(#__VA_ARGS__ " should have thrown " #type ", but didn't throw anything"); } \
catch (type &) {} \
catch (...) { ::reaver::mayfly::log_assertion(#__VA_ARGS__ " should have thrown " #type ", but has thrown something else"); }

#define MAYFLY_REQUIRE_NOTHROW(...) \
try { __VA_ARGS__; } catch (...) { ::reaver::mayfly::log_assertion(#__VA_ARGS__ " shouldn't have thrown", true); }

#define MAYFLY_CHECK_NOTHROW(...) \
try { __VA_ARGS__; } catch (...) { ::reaver::mayfly::log_assertion(#__VA_ARGS__ " shouldn't have thrown"); }
#define MAYFLY_REQUIRE(...) \
try { if (!(__VA_ARGS__)) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); } } \
catch (::reaver::mayfly::expected_failure_exit &) { throw; } \
catch (::reaver::mayfly::assertions_failed &) { throw; } \
catch (...) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " has thrown an unexpected exception" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); }

#define MAYFLY_CHECK(...) \
try { if (!(__VA_ARGS__)) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")"); } } \
catch (::reaver::mayfly::expected_failure_exit) { throw; } \
catch (...) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " has thrown an unexpected exception" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); }

#define MAYFLY_REQUIRE_THROWS(...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " should have thrown, but didn't" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); } catch (...) {}

#define MAYFLY_CHECK_THROWS(...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " should have thrown, but didn't"} \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")"); } catch (...) {}

#define MAYFLY_REQUIRE_THROWS_TYPE(type, ...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " should have thrown " #type ", but didn't throw anything" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); } \
catch (type & e) {} \
catch (...) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " should have thrown " #type ", but has thrown something else" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); }

#define MAYFLY_CHECK_THROWS_TYPE(type, ...) \
try { __VA_ARGS__; ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " should have thrown " #type ", but didn't throw anything" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")"); } \
catch (type &) {} \
catch (...) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " should have thrown " #type ", but has thrown something else" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")"); }

#define MAYFLY_REQUIRE_NOTHROW(...) \
try { __VA_ARGS__; } catch (...) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " shouldn't have thrown" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")", true); }

#define MAYFLY_CHECK_NOTHROW(...) \
try { __VA_ARGS__; } catch (...) { ::reaver::mayfly::log_assertion(::std::string{ #__VA_ARGS__ " shouldn't have thrown" } \
+ " (in " + __FILE__ + " at line " + ::std::to_string(__LINE__) + ")"); }
78 changes: 78 additions & 0 deletions include/reaver/mayfly/detail/atexit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Mayfly License
*
* Copyright © 2014 Michał "Griwes" Dominiak
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation is required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
**/

#pragma once

#include <cstdlib>
#include <vector>
#include <atomic>

namespace reaver
{
namespace mayfly { inline namespace _v1
{
namespace _detail
{
class _atexit_registry;
_atexit_registry & _default_atexit_registry();

class _atexit_registry
{
public:
void atexit(void (*f)())
{
_functions.push_back(f);
}

friend _atexit_registry & _default_atexit_registry();

private:
std::vector<void (*)()> _functions;
};

inline _atexit_registry & _default_atexit_registry()
{
static _atexit_registry registry;
static std::atomic<bool> executed{ false };

if (!executed)
{
std::atexit([]
{
auto & registry = _default_atexit_registry();

for (auto it = registry._functions.rbegin(); it != registry._functions.rend(); ++it)
{
(*it)();
}
});

executed = true;
}

return registry;
}

static auto & _ = _default_atexit_registry();
}
}}
}
85 changes: 62 additions & 23 deletions include/reaver/mayfly/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ namespace reaver
{
testcase_result result;
result.name = t.name();
result.status = testcase_status::passed;

std::vector<std::string> output;

if (_threads == 1)
{
Expand All @@ -166,7 +169,6 @@ namespace reaver
try
{
t();
result.status = testcase_status::passed;
}

catch (reaver::exception & e)
Expand Down Expand Up @@ -213,7 +215,7 @@ namespace reaver
t = std::thread{ [&]()
{
std::unique_lock<std::mutex> lock{ m };
if (!cv.wait_for(lock, std::chrono::seconds(_timeout), [&]() -> bool { return finished_flag; }))
if (!cv.wait_for(lock, std::chrono::seconds{ _timeout }, [&]() -> bool { return finished_flag; }))
{
timeout_flag = true;
boost::process::terminate(child);
Expand All @@ -224,39 +226,57 @@ namespace reaver
boost::iostreams::file_descriptor_source source{ p.source, boost::iostreams::close_handle };
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is(source);

std::uintmax_t retval;
std::string message;

try
{
is >> retval;
std::getline(is, message);
message = message.substr(1); // remove the leading space; thanks, b.iostreams for no .ignore()
enum { not_started, started, finished, exited } state = not_started;

try
while (std::getline(is, message))
{
if (message.substr(0, 2) == "{{")
{
finished_flag = true;
cv.notify_all();
t.join();
}
if (message == "{{started}}")
{
assert(state == not_started);
state = started;
}

catch (...)
{
}
else if (message == "{{finished}}")
{
assert(state == started);
state = finished;
}

if (!is || retval > 3)
{
result.status = testcase_status::crashed;
else if (message == "{{exit}}")
{
assert(state == finished);
state = exited;
}

else if (message.substr(0, 8) == "{{failed")
{
assert(state == started);
result.status = testcase_status::failed;
result.description = message.substr(9, message.length() - 11);
}

else if (message == "{{error unexpected test status}}")
{
throw unexpected_result{};
}

else if (message == "{{error not found}}")
{
result.status = testcase_status::not_found;
}
}

else
{
result.status = static_cast<testcase_status>(retval);
result.description = message;
output.push_back(std::move(message));
}
}

catch (...)
if (state != exited)
{
if (timeout_flag)
{
Expand All @@ -269,6 +289,17 @@ namespace reaver
}
}

try
{
finished_flag = true;
cv.notify_all();
t.join();
}

catch (...)
{
}

auto duration = std::chrono::high_resolution_clock::now() - begin;
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(duration);

Expand All @@ -282,11 +313,19 @@ namespace reaver
{
std::unique_lock<const reporter> lock(rep);
rep.test_started(t);
for (auto && message : output)
{
logger::dlog() << message;
}
rep.test_finished(result);
}

else
{
for (auto && message : output)
{
logger::dlog() << message;
}
rep.test_finished(result);
}

Expand Down Expand Up @@ -323,7 +362,7 @@ namespace reaver
return *default_runner;
}

constexpr static const char * version_string = "Reaver Project's Mayfly v0.1.1 alpha\nCopyright © 2014 Reaver Project Team\n";
constexpr static const char * version_string = "Reaver Project's Mayfly v0.1.2 alpha\nCopyright © 2014 Reaver Project Team\n";

class invalid_testcase_name_format : public exception
{
Expand Down
28 changes: 26 additions & 2 deletions include/reaver/mayfly/subprocess.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "testcase.h"
#include "suite.h"
#include "reporter.h"
#include "detail/atexit.h"

namespace reaver
{
Expand All @@ -45,20 +46,43 @@ namespace reaver

virtual void test_started(const testcase & t) const override
{
_detail::_default_atexit_registry().atexit(_atexit);

std::cout << "{{started}}\n";
}

virtual void test_finished(const testcase_result & result) const override
{
std::cout << static_cast<std::uintmax_t>(result.status) << ' ' << result.description << std::flush;
switch (result.status)
{
case testcase_status::passed:
break;

case testcase_status::failed:
std::cout << "{{failed " << result.description << "}}\n";
break;

default:
std::cout << "{{error unexpected test status}}\n";
break;
}

std::cout << "{{finished}}\n";
}

virtual void summary(const std::vector<std::pair<testcase_status, std::string>> & summary, std::uintmax_t passed, std::uintmax_t total) const override
{
if (!total)
{
std::cout << static_cast<std::uintmax_t>(testcase_status::not_found) << std::flush;
std::cout << "{{error not found}}\n";
}
}

private:
static void _atexit()
{
std::cout << "{{exit}}\n";
}
};

MAYFLY_REPORTER_REGISTER("subprocess", subprocess_reporter)
Expand Down
9 changes: 9 additions & 0 deletions include/reaver/mayfly/testcase.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ namespace reaver
not_found = 5
};

class unexpected_result : public exception
{
public:
unexpected_result() : exception{ logger::crash }
{
*this << "testcase execution resulted in an unknown status code.";
}
};

struct testcase_result
{
std::string name;
Expand Down

0 comments on commit 7317d8f

Please sign in to comment.