parent
e15a1f3265
commit
aab07c0720
27
arc/validate/asserts.hxx
Normal file
27
arc/validate/asserts.hxx
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef arc__validate__asserts_hxx_
|
||||
#define arc__validate__asserts_hxx_
|
||||
|
||||
#include <arc/validate/except.hxx>
|
||||
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
|
||||
#define ARC_VALIDATE_ASSERT_TRUE(expr) \
|
||||
::arc::validate::asserts::assert_true(expr)
|
||||
|
||||
namespace arc::validate::asserts
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
assert_true(T const&, std::source_location = std::source_location::current());
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
assert_false(T const&, std::source_location = std::source_location::current());
|
||||
|
||||
} // namespace arc::validate::asserts
|
||||
|
||||
#include <arc/validate/asserts.txx>
|
||||
|
||||
#endif
|
46
arc/validate/asserts.txx
Normal file
46
arc/validate/asserts.txx
Normal file
@ -0,0 +1,46 @@
|
||||
namespace arc::validate::asserts
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
assert_true(T const& expr, std::source_location origin)
|
||||
{
|
||||
struct extras_t
|
||||
{
|
||||
std::source_location origin;
|
||||
|
||||
void
|
||||
print(std::ostream& o) const
|
||||
{
|
||||
o << " assertion failed: " << origin.file_name() << ':' << origin.line() << '\n';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (expr != true) {
|
||||
throw failure_t{origin, "assertion failed", extras_t{origin}};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
assert_false(T const& expr, std::source_location origin)
|
||||
{
|
||||
struct extras_t
|
||||
{
|
||||
std::source_location origin;
|
||||
|
||||
void
|
||||
print(std::ostream& o) const
|
||||
{
|
||||
o << " assertion failed: " << origin.file_name() << ':' << origin.line() << '\n';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (expr != false) {
|
||||
throw failure_t{origin, "assertion failed", extras_t{origin}};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace arc::validate::asserts
|
112
arc/validate/command-line.cxx
Normal file
112
arc/validate/command-line.cxx
Normal file
@ -0,0 +1,112 @@
|
||||
#include <arc/validate/command-line.hxx>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
command_line_t
|
||||
parse_command_line(std::vector<std::string> const& args)
|
||||
{
|
||||
command_line_t command_line;
|
||||
|
||||
bool only_files{false};
|
||||
|
||||
for (auto it = args.begin(); it != args.end(); ++it) {
|
||||
auto const& option = *it;
|
||||
|
||||
// fixme: should empty options be an error?
|
||||
//
|
||||
if (option.size() < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (only_files || '-' != option[0] || 1 == option.size()) {
|
||||
throw command_line_error_t{"invalid file argument '" + option + "'"};
|
||||
}
|
||||
|
||||
if ("--" == option) {
|
||||
only_files = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--version" == option) {
|
||||
command_line.print_version = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--help" == option) {
|
||||
command_line.print_usage = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--print-information" == option) {
|
||||
command_line.print_information = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--print-warnings" == option) {
|
||||
command_line.print_warnings = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--print-success" == option) {
|
||||
command_line.print_success = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--print-failure" == option) {
|
||||
command_line.print_failure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--verbose" == option) {
|
||||
command_line.print_information = true;
|
||||
command_line.print_warnings = true;
|
||||
command_line.print_success = true;
|
||||
command_line.print_failure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("-i" == option || "--include" == option) {
|
||||
++it;
|
||||
|
||||
if (it == args.end()) {
|
||||
throw command_line_error_t{"missing argument to '--include'"};
|
||||
}
|
||||
|
||||
if (!command_line.exclude.empty()) {
|
||||
throw command_line_error_t{"cannot specify both `-i, --include` and `-e, --exclude`"};
|
||||
}
|
||||
|
||||
command_line.include.emplace(*it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("-e" == option || "--exclude" == option) {
|
||||
++it;
|
||||
|
||||
if (it == args.end()) {
|
||||
throw command_line_error_t{"missing argument to '--exclude'"};
|
||||
}
|
||||
|
||||
if (!command_line.include.empty()) {
|
||||
throw command_line_error_t{"cannot specify both `-i, --include` and `-e, --exclude`"};
|
||||
}
|
||||
|
||||
command_line.exclude.emplace(*it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("-l" == option || "--list" == option) {
|
||||
command_line.list = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw command_line_error_t{"unrecognized option '" + option + "'"};
|
||||
}
|
||||
|
||||
|
||||
return command_line;
|
||||
|
||||
}
|
||||
|
||||
}
|
46
arc/validate/command-line.hxx
Normal file
46
arc/validate/command-line.hxx
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef arc__validate__command_line_hxx_
|
||||
#define arc__validate__command_line_hxx_
|
||||
|
||||
#include <arc/validate/export.hxx>
|
||||
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
struct LIBARC_VALIDATE_SYMEXPORT command_line_t
|
||||
{
|
||||
bool print_version{};
|
||||
bool print_usage{};
|
||||
bool print_information{};
|
||||
bool print_warnings{};
|
||||
bool print_success{};
|
||||
bool print_failure{};
|
||||
|
||||
std::set<std::string> include;
|
||||
std::set<std::string> exclude;
|
||||
|
||||
bool list{};
|
||||
|
||||
};
|
||||
|
||||
LIBARC_VALIDATE_SYMEXPORT
|
||||
command_line_t
|
||||
parse_command_line(std::vector<std::string> const&);
|
||||
|
||||
/// Exception class used to indicate command line error.
|
||||
///
|
||||
class LIBARC_VALIDATE_SYMEXPORT command_line_error_t
|
||||
: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
|
||||
};
|
||||
|
||||
} // namespace arc::validate
|
||||
|
||||
#endif
|
27
arc/validate/except.cxx
Normal file
27
arc/validate/except.cxx
Normal file
@ -0,0 +1,27 @@
|
||||
#include <arc/validate/except.hxx>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
failure_t::
|
||||
failure_t(std::source_location origin,
|
||||
std::string message,
|
||||
extras_t extras)
|
||||
: extras_{std::move(extras)}
|
||||
{}
|
||||
|
||||
failure_t::extras_t const&
|
||||
failure_t::
|
||||
extras() const
|
||||
{
|
||||
return extras_;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& o, failure_t::extras_t const& extras)
|
||||
{
|
||||
extras.extras_->print(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
} // namespace arc::validate
|
88
arc/validate/except.hxx
Normal file
88
arc/validate/except.hxx
Normal file
@ -0,0 +1,88 @@
|
||||
#ifndef arc__validate__except_hxx_
|
||||
#define arc__validate__except_hxx_
|
||||
|
||||
#include <arc/validate/export.hxx>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <source_location>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
class LIBARC_VALIDATE_SYMEXPORT skipped_t
|
||||
{};
|
||||
|
||||
class LIBARC_VALIDATE_SYMEXPORT not_implemented_t
|
||||
{};
|
||||
|
||||
class LIBARC_VALIDATE_SYMEXPORT failure_t
|
||||
{
|
||||
public:
|
||||
class LIBARC_VALIDATE_SYMEXPORT extras_t
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
extras_t(T extras)
|
||||
: extras_{std::make_shared<container_t<T>>(std::move(extras))}
|
||||
{}
|
||||
|
||||
friend
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, extras_t const&);
|
||||
|
||||
private:
|
||||
struct concept_t
|
||||
{
|
||||
virtual
|
||||
~concept_t() noexcept = default;
|
||||
|
||||
virtual
|
||||
void
|
||||
print(std::ostream&) const = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct container_t
|
||||
: concept_t
|
||||
{
|
||||
template<typename... Args>
|
||||
container_t(Args&&... args)
|
||||
: extras{std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
void
|
||||
print(std::ostream& o) const
|
||||
{
|
||||
extras.print(o);
|
||||
}
|
||||
|
||||
T extras;
|
||||
|
||||
};
|
||||
|
||||
std::shared_ptr<concept_t const> extras_;
|
||||
|
||||
};
|
||||
|
||||
failure_t(std::source_location,
|
||||
std::string,
|
||||
extras_t);
|
||||
|
||||
extras_t const&
|
||||
extras() const;
|
||||
|
||||
private:
|
||||
extras_t extras_;
|
||||
|
||||
};
|
||||
|
||||
LIBARC_VALIDATE_SYMEXPORT
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, failure_t::extras_t const&);
|
||||
|
||||
} // namespace arc::validate
|
||||
|
||||
#endif
|
155
arc/validate/main.cxx
Normal file
155
arc/validate/main.cxx
Normal file
@ -0,0 +1,155 @@
|
||||
#include <arc/validate/command-line.hxx>
|
||||
#include <arc/validate/except.hxx>
|
||||
#include <arc/validate/main.hxx>
|
||||
#include <arc/validate/test.hxx>
|
||||
#include <arc/validate/version.hxx>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
static
|
||||
void
|
||||
print_version()
|
||||
{
|
||||
std::cout << "libarc-validate test runner (" << LIBARC_VALIDATE_VERSION_ID << ")\n";
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
print_usage(std::string const& execname)
|
||||
{
|
||||
std::cout << "Usage: " << execname << " [<option>...] [<file>...]\n"
|
||||
<< '\n'
|
||||
<< "Mandatory arguments to long options are mandatory for short options too.\n"
|
||||
<< '\n'
|
||||
<< " -- Treat all remaining arguments as files.\n"
|
||||
<< " --version Print version information and exit.\n"
|
||||
<< " --help Display this help and exit.\n"
|
||||
<< " --print-information Print test information.\n"
|
||||
<< " --print-warnings Print warnings.\n"
|
||||
<< " --print-success Print successful test information.\n"
|
||||
<< " --print-failure Print failed test information.\n"
|
||||
<< " --verbose Print everything.\n"
|
||||
<< " -i, --include <name> Include execution of test <name>.\n"
|
||||
<< " -e, --exclude <name> Exclude execution of test <name>.\n"
|
||||
<< " -l, --list List available tests and exit.\n"
|
||||
<< '\n'
|
||||
<< "If any filenames are specified on the command line the program will terminate.\n"
|
||||
<< '\n'
|
||||
<< "Unknown tests passed as arguments to --include or --exclude are ignored.\n"
|
||||
<< '\n'
|
||||
;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
execute_test(test_t const& test,
|
||||
bool print_information,
|
||||
bool print_warnings,
|
||||
bool print_success,
|
||||
bool print_failure)
|
||||
{
|
||||
if (print_information) {
|
||||
std::cout << "Executing test: " << test.name() << '\n';
|
||||
}
|
||||
|
||||
try {
|
||||
test.function()();
|
||||
|
||||
if (print_success) {
|
||||
std::cout << "Test passed: " << test.name() << '\n';
|
||||
}
|
||||
}
|
||||
catch (skipped_t const&) {
|
||||
if (print_information) {
|
||||
std::cout << "Test skipped: " << test.name() << '\n';
|
||||
}
|
||||
}
|
||||
catch (not_implemented_t const&) {
|
||||
if (print_warnings) {
|
||||
std::cout << "Test not implemented: " << test.name() << '\n';
|
||||
}
|
||||
}
|
||||
catch (failure_t const& failure) {
|
||||
if (print_failure) {
|
||||
std::cout << "Test failed: " << test.name() << '\n';
|
||||
std::cout << failure.extras() << '\n';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
return main(argv[0], {argv + 1, argv + argc});
|
||||
}
|
||||
|
||||
int
|
||||
main(std::string const& execname, std::vector<std::string> const& args)
|
||||
try
|
||||
{
|
||||
auto command_line = parse_command_line(args);
|
||||
|
||||
if (command_line.print_version) {
|
||||
print_version();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command_line.print_usage) {
|
||||
print_usage(execname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command_line.list) {
|
||||
std::cout << "Available tests:\n\n";
|
||||
|
||||
for (auto const& j : test_t::get_tests()) {
|
||||
std::cout << " - \"" << j->name() << "\"\n";
|
||||
}
|
||||
|
||||
std::cout << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Let's execute some tests!
|
||||
//
|
||||
|
||||
bool failed{false};
|
||||
auto tests = test_t::get_tests(command_line.include, command_line.exclude);
|
||||
|
||||
for (auto const& j : tests) {
|
||||
bool success = execute_test(
|
||||
*j,
|
||||
command_line.print_information,
|
||||
command_line.print_warnings,
|
||||
command_line.print_success,
|
||||
command_line.print_failure
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
std::cerr << execname << ": error: " << ex.what() << ".\n";
|
||||
return 1;
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << execname << ": unknown error.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace arc::validate
|
22
arc/validate/main.hxx
Normal file
22
arc/validate/main.hxx
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef arc__validate__main_hxx_
|
||||
#define arc__validate__main_hxx_
|
||||
|
||||
#include <arc/validate/export.hxx>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
LIBARC_VALIDATE_SYMEXPORT
|
||||
int
|
||||
main(int, char*[]);
|
||||
|
||||
LIBARC_VALIDATE_SYMEXPORT
|
||||
int
|
||||
main(std::string const&, std::vector<std::string> const&);
|
||||
|
||||
} // namespace arc::validate
|
||||
|
||||
#endif
|
97
arc/validate/test.cxx
Normal file
97
arc/validate/test.cxx
Normal file
@ -0,0 +1,97 @@
|
||||
#include <arc/validate/test.hxx>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
std::map<std::string, test_t*> test_t::tests_;
|
||||
|
||||
test_t::
|
||||
test_t(std::string file, int line, std::string name, std::function<void()> f)
|
||||
: file_{std::move(file)},
|
||||
line_{line},
|
||||
name_{
|
||||
name.empty() ? throw std::invalid_argument{"invalid name"} : name
|
||||
},
|
||||
function_{std::move(f)}
|
||||
{
|
||||
if (tests_.contains(name)) {
|
||||
throw std::invalid_argument{"test already registered: " + name};
|
||||
}
|
||||
|
||||
tests_.emplace(name, this);
|
||||
}
|
||||
|
||||
test_t::
|
||||
~test_t() noexcept
|
||||
{
|
||||
tests_.erase(name_);
|
||||
}
|
||||
|
||||
std::string const&
|
||||
test_t::
|
||||
file() const
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
int
|
||||
test_t::
|
||||
line() const
|
||||
{
|
||||
return line_;
|
||||
}
|
||||
|
||||
std::string const&
|
||||
test_t::
|
||||
name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
std::function<void()> const&
|
||||
test_t::
|
||||
function() const
|
||||
{
|
||||
return function_;
|
||||
}
|
||||
|
||||
std::vector<test_t*>
|
||||
test_t::
|
||||
get_tests()
|
||||
{
|
||||
std::vector<test_t*> tests;
|
||||
|
||||
for (auto const& j : tests_) {
|
||||
tests.emplace_back(j.second);
|
||||
}
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
std::vector<test_t*>
|
||||
test_t::
|
||||
get_tests(std::set<std::string> const& include,
|
||||
std::set<std::string> const& exclude)
|
||||
{
|
||||
std::vector<test_t*> tests;
|
||||
|
||||
auto all_tests = get_tests();
|
||||
|
||||
for (auto const& j : all_tests) {
|
||||
if (!include.empty() && !include.contains(j->name())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exclude.contains(j->name())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tests.emplace_back(j);
|
||||
}
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
} // namespace arc::validate
|
79
arc/validate/test.hxx
Normal file
79
arc/validate/test.hxx
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef arc__validate__test_hxx_
|
||||
#define arc__validate__test_hxx_
|
||||
|
||||
#include <arc/validate/export.hxx>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define ARC_VALIDATE_DEFINE_TEST_3(file, line, name) \
|
||||
static void test_func_##line(); \
|
||||
static ::arc::validate::test_t test_##line(file, line, name, test_func_##line); \
|
||||
static void test_func_##line()
|
||||
|
||||
#define ARC_VALIDATE_DEFINE_TEST_2(file, line, name) ARC_VALIDATE_DEFINE_TEST_3(file, line, name)
|
||||
|
||||
#ifndef ARC_VALIDATE_UNPREFIXED_MACROS
|
||||
# define ARC_VALIDATE_DEFINE_TEST(name) ARC_VALIDATE_DEFINE_TEST_2(__FILE__, __LINE__, #name)
|
||||
#else
|
||||
# define DEFINE_TEST(name) ARC_VALIDATE_DEFINE_TEST_2(__FILE__, __LINE__, #name)
|
||||
#endif
|
||||
|
||||
namespace arc::validate
|
||||
{
|
||||
|
||||
/// Represents a single test case.
|
||||
///
|
||||
class LIBARC_VALIDATE_SYMEXPORT test_t
|
||||
{
|
||||
public:
|
||||
test_t(std::string, int, std::string, std::function<void()>);
|
||||
|
||||
test_t(test_t const&) = delete;
|
||||
test_t(test_t&&) = delete;
|
||||
|
||||
~test_t() noexcept;
|
||||
|
||||
std::string const&
|
||||
file() const;
|
||||
|
||||
int
|
||||
line() const;
|
||||
|
||||
std::string const&
|
||||
name() const;
|
||||
|
||||
std::function<void()> const&
|
||||
function() const;
|
||||
|
||||
test_t& operator=(test_t const&) = delete;
|
||||
test_t& operator=(test_t&&) = delete;
|
||||
|
||||
static
|
||||
std::vector<test_t*>
|
||||
get_tests();
|
||||
|
||||
static
|
||||
std::vector<test_t*>
|
||||
get_tests(std::set<std::string> const&,
|
||||
std::set<std::string> const&);
|
||||
|
||||
private:
|
||||
std::string file_;
|
||||
int line_;
|
||||
std::string name_;
|
||||
std::function<void()> function_;
|
||||
|
||||
/// Holds a map of all registered tests.
|
||||
///
|
||||
static std::map<std::string, test_t*> tests_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace arc::validate
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
#ifndef arc__validate__export_hxx_
|
||||
#define arc__validate__export_hxx_
|
||||
#ifndef arc__validate__version_hxx_
|
||||
#define arc__validate__version_hxx_
|
||||
|
||||
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user