Hello BitShift Validate

This commit is contained in:
2026-06-24 14:13:35 +02:00
commit 3caa3d05d6
57 changed files with 6363 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
indent_size = 4
max_line_length = off
trim_trailing_whitespace = false
+1
View File
@@ -0,0 +1 @@
* text=auto
+70
View File
@@ -0,0 +1,70 @@
name: on-push
on:
push:
tags-ignore:
- '**'
branches:
- '**'
jobs:
gcc:
runs-on: linux
container: code.helloryan.se/infra/buildenv/f44-x86_64-unified:latest
steps:
- name: Prepare environment
run: |
mkdir /src
- name: Configure repository access
run: |
git config --global http.$GITHUB_SERVER_URL/.extraheader "Authorization: token ${{ secrets.PACKAGE_PULL_TOKEN }}"
- name: Fetch repository
run: |
cd /src
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git .
git checkout "$GITHUB_SHA"
- name: Create build configuration
run: |
bpkg create -d /build cc config.cxx=g++ config.cc.coptions="-Wall -Werror"
- name: Initialize
run: |
cd /src
bdep init -A /build
- name: Build
run: |
cd /src
b
- name: Test
run: |
cd /src
b -j 1 -s test
clang:
runs-on: linux
container: code.helloryan.se/infra/buildenv/f44-x86_64-unified:latest
steps:
- name: Prepare environment
run: |
mkdir /src
- name: Configure repository access
run: |
git config --global http.$GITHUB_SERVER_URL/.extraheader "Authorization: token ${{ secrets.PACKAGE_PULL_TOKEN }}"
- name: Fetch repository
run: |
cd /src
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git .
git checkout "$GITHUB_SHA"
- name: Create build configuration
run: |
bpkg create -d /build cc config.cxx=clang++ config.cc.coptions="-Wall -Werror"
- name: Initialize
run: |
cd /src
bdep init -A /build
- name: Build
run: |
cd /src
b
- name: Test
run: |
cd /src
b -j 1 -s test
+38
View File
@@ -0,0 +1,38 @@
name: on-push
on: workflow_dispatch
jobs:
publish-docs:
runs-on: linux
container: code.helloryan.se/infra/buildenv/f44-x86_64-unified:latest
steps:
- name: Prepare environment
run: |
mkdir /src
- name: Configure repository access
run: |
git config --global http.$GITHUB_SERVER_URL/.extraheader "Authorization: token ${{ secrets.PACKAGE_PULL_TOKEN }}"
- name: Configure MinIO CLI
run: |
mc alias set garage https://buckets.helloryan.se/ ${{ secrets.BITSHIFT_S3_KEY_ID }} ${{ secrets.BITSHIFT_S3_SECRET_KEY }}
- name: Fetch repository
run: |
cd /src
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git .
git checkout "$GITHUB_SHA"
- name: Create build configuration
run: |
bpkg create -d /build cc config.cxx=g++ config.cc.coptions="-Wall -Werror"
- name: Initialize
run: |
cd /src
bdep init -A /build
- name: Build Documentation
run: |
cd /src
b alias{docs}
- name: Publish Documentation
run: |
cd /build/bitshift-validate/docs/html
mc mirror --remove --overwrite . garage/validate.bitshift.helloryan.se/
+38
View File
@@ -0,0 +1,38 @@
.bdep/
# Local default options files.
#
.build2/local/
# Compiler/linker output.
#
# Note that *.exe.dlls directories can be symlinks thus
# no trailing /.
#
*.d
*.t
*.i
*.i.*
*.ii
*.ii.*
*.o
*.obj
*.gcm
*.pcm
*.ifc
*.so
*.dylib
*.dll
*.a
*.lib
*.exp
*.pdb
*.ilk
*.exe
*.exe.dlls
*.exe.manifest
*.pc
# Compilation database.
#
compile_commands.json
+2933
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
Copyright © 2026 Per Ryan Edin and the BitShift Contributors.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+101
View File
@@ -0,0 +1,101 @@
Validate
========
BitShift Validate is a minimalist unit-testing library for C++. Its primary
purpose is to test the various other BitShift projects without making them
dependent on larger unit-testing libraries.
Why BitShift Validate?
----------------------
A small and dependency-free unit-testing library with first-class build2
support was required for the other BitShift Projects.
And a shitload of other reasons, for example, not embracing CMake(Shit) or
supporting Google world-dominance by using GTest.
Requirements
------------
- A modern C++ compiler with C++23 suppport (GCC 14+ or Clang 18+),
- [build2][build2]: The build2 build and package management system.
Usage
-----
To start using BitShift Validate to test your project, add the following
`depends` value to your `manifest`, adjusting the version constraint as
appropriate:
```
depends: bitshift-validate ^<VERSION>
```
Then import the library in your `buildfile`:
```
test_libs =
import test_libs =+ bitshift-validate%lib{validate}
./: exe{my-test}: {hxx ixx txx cxx}{**} $test_libs
```
Importable Targets
------------------
This package provides the following importable targets:
```
lib{validate}
```
### `lib{validate}`
Implementation of the test library.
Usage Example
-------------
```c++
#include <bitshift/validate/validate.hxx>
BV_TEST(example.test, "an example test")
{
int x = 0;
int y = 1;
BV_ASSERT_NOT_EQUAL(x, y);
}
int
main(int argc, char* argv[])
{
return bitshift::validate::main(argc, argv);
}
```
Documentation
-------------
The latest API reference documentation can be found at:
https://bitshift.helloryan.se/validate/
Bugs
----
There are probably many goblins lurking in the code. If you find one, please
report it promptly to [ryan@helloryan.se][ryan@helloryan.se]. Please include as
much information as possible and preferably a link to a reproducible.
Contributing
------------
If you want to actively contribute to the project, send an e-mail to
[ryan@helloryan.se][ryan@helloryan.se], to discuss repository access.
Contributions are welcome in many forms, from code contributions to simple
issue reporting. Don't hesitate to get in touch!
[build2]: https://build2.org/
[ryan@helloryan.se]: mailto:ryan@helloryan.se
+9
View File
@@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test
+115
View File
@@ -0,0 +1,115 @@
#include <bitshift/validate/ansi.hxx>
namespace bitshift::validate
{
ansi::
ansi()
{}
ansi::
ansi(color color, style style)
: _color{color}, _style{style}
{}
ansi::color
ansi::
get_color() const
{
return _color;
}
ansi::style
ansi::
get_style() const
{
return _style;
}
ansi::style
operator|(ansi::style lhs, ansi::style rhs)
{
return static_cast<ansi::style>(
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs)
);
}
ostream&
operator<<(ostream& o, ansi const& ansi)
{
o << "\033[0m";
switch (ansi.get_color()) {
case ansi::kBlack:
o << "\033[30";
break;
case ansi::kRed:
o << "\033[31";
break;
case ansi::kGreen:
o << "\033[32";
break;
case ansi::kYellow:
o << "\033[33";
break;
case ansi::kBlue:
o << "\033[34";
break;
case ansi::kPurple:
o << "\033[35";
break;
case ansi::kCyan:
o << "\033[36";
break;
case ansi::kWhite:
o << "\033[37";
break;
}
if ((ansi.get_style() & ansi::kBold) != 0) {
o << ";1";
}
if ((ansi.get_style() & ansi::kItalic) != 0) {
o << ";3";
}
if ((ansi.get_style() & ansi::kUnderline) != 0) {
o << ";4";
}
if ((ansi.get_style() & ansi::kBlinking) != 0) {
o << ";5";
}
o << 'm';
return o;
}
ostream&
operator<<(ostream& o, ansi::reset_t const&)
{
return o << "\033[0m";
}
ostream&
operator<<(ostream& o, ansi::color const& color)
{
return o << ansi{color};
}
ostream&
operator<<(ostream& o, ansi::style const& style)
{
return o << ansi{ansi::color::kBlack, style};
}
} // namespace bitshift::validate
+94
View File
@@ -0,0 +1,94 @@
#ifndef bitshift__validate__ansi_hxx_
#define bitshift__validate__ansi_hxx_
#include <bitshift/validate/types.hxx>
namespace bitshift::validate
{
/// @brief Terminal ANSI-styles class
///
class ansi
{
public:
/// @brief Terminal color enumeration.
///
enum color
: uint8_t
{
kBlack,
kRed,
kGreen,
kYellow,
kBlue,
kPurple,
kCyan,
kWhite
};
/// @brief Terminal style enumeration.
///
enum style
: uint8_t
{
kNormal = 0,
kBold = 1 << 1,
kItalic = 1 << 2,
kUnderline = 1 << 3,
kBlinking = 1 << 4
};
/// @brief Tag-dispatch type for reset.
///
struct reset_t { };
/// @brief Tag for reset.
///
static constexpr reset_t const reset{};
/// @brief Constructor.
///
ansi();
/// @brief Constructor.
///
ansi(color, style = kNormal);
/// @brief Get color.
///
color
get_color() const;
/// @brief Get style.
///
style
get_style() const;
private:
color _color{kBlack};
style _style{kNormal};
};
/// @brief Style-combinator operator.
///
ansi::style
operator|(ansi::style, ansi::style);
ostream&
operator<<(ostream&, ansi const&);
ostream&
operator<<(ostream&, ansi::reset_t const&);
ostream&
operator<<(ostream&, ansi::color const&);
ostream&
operator<<(ostream&, ansi::style const&);
} // namespace bitshift::validate
#endif
+49
View File
@@ -0,0 +1,49 @@
#ifndef bitshift__validate__assert_hxx_
#define bitshift__validate__assert_hxx_
#include <bitshift/validate/types.hxx>
#include <bitshift/validate/failure.hxx>
#include <bitshift/validate/traits.hxx>
namespace bitshift::validate::assertion
{
template<typename T>
void
assert_true(T const& expr,
string const& expr_str,
source_location const& origin = source_location::current());
template<typename T>
void
assert_false(T const& expr,
string const& expr_str,
source_location const& origin = source_location::current());
template<typename T>
void
assert_nullptr(T const& expr,
string const& expr_str,
source_location const& origin = source_location::current());
template<typename T1, typename T2 = T1>
void
assert_equal(T1 const& lhs,
T2 const& rhs,
string const& lhs_str,
string const& rhs_str,
source_location const& origin = source_location::current());
template<typename T1, typename T2 = T1>
void
assert_not_equal(T1 const& lhs,
T2 const& rhs,
string const& lhs_str,
string const& rhs_str,
source_location const& origin = source_location::current());
} // namespace bitshift::validate::assertion
#include <bitshift/validate/assert.txx>
#endif
+161
View File
@@ -0,0 +1,161 @@
namespace bitshift::validate::assertion
{
template<typename T>
void
assert_true(T const& expr,
string const& expr_str,
source_location const& origin)
{
if (expr == true) {
return;
}
string value_str;
if constexpr (traits::has_output_operator<T>) {
stringstream str;
str << expr;
value_str = str.str();
}
throw unary_assertion_failure{
"expected expression to be true; was false",
origin.file_name(),
origin.line(),
std::move(expr_str),
std::move(value_str)
};
}
template<typename T>
void
assert_false(T const& expr,
string const& expr_str,
source_location const& origin)
{
if (expr == false) {
return;
}
string value_str;
if constexpr (traits::has_output_operator<T>) {
stringstream str;
str << expr;
value_str = str.str();
}
throw unary_assertion_failure{
"expected expression to be false; was true",
origin.file_name(),
origin.line(),
std::move(expr_str),
std::move(value_str)
};
}
template<typename T>
void
assert_nullptr(T const& expr,
string const& expr_str,
source_location const& origin)
{
if (expr == nullptr) {
return;
}
string value_str;
if constexpr (traits::has_output_operator<T>) {
stringstream str;
str << expr;
value_str = str.str();
}
throw unary_assertion_failure{
"expected expression to be null; was not null",
origin.file_name(),
origin.line(),
std::move(expr_str),
std::move(value_str)
};
}
template<typename T1, typename T2>
void
assert_equal(T1 const& lhs,
T2 const& rhs,
string const& lhs_str,
string const& rhs_str,
source_location const& origin)
{
if (lhs == rhs) {
return;
}
std::string lhs_val;
std::string rhs_val;
if constexpr (traits::has_output_operator<T1>) {
std::stringstream str;
str << lhs;
lhs_val = str.str();
}
if constexpr (traits::has_output_operator<T2>) {
std::stringstream str;
str << rhs;
rhs_val = str.str();
}
throw binary_assertion_failure{
"expected lhs to equal rhs",
origin.file_name(),
origin.line(),
std::move(lhs_str),
std::move(lhs_val),
std::move(rhs_str),
std::move(rhs_val)
};
}
template<typename T1, typename T2>
void
assert_not_equal(T1 const& lhs,
T2 const& rhs,
string const& lhs_str,
string const& rhs_str,
source_location const& origin)
{
if (lhs != rhs) {
return;
}
std::string lhs_val;
std::string rhs_val;
if constexpr (traits::has_output_operator<T1>) {
std::stringstream str;
str << lhs;
lhs_val = str.str();
}
if constexpr (traits::has_output_operator<T2>) {
std::stringstream str;
str << rhs;
rhs_val = str.str();
}
throw binary_assertion_failure{
"expected lhs to not equal rhs",
origin.file_name(),
origin.line(),
std::move(lhs_str),
std::move(lhs_val),
std::move(rhs_str),
std::move(rhs_val)
};
}
} // namespace bitshift::validate::assertion
+58
View File
@@ -0,0 +1,58 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
./: lib{validate}: libul{validate}
libul{validate}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version}
libul{validate}: $impl_libs $intf_libs
# Unit tests.
#
exe{*.test}:
{
test = true
install = false
}
for t: cxx{**.test...}
{
d = $directory($t)
n = $name($t)...
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n}
$d/exe{$n}: libul{validate}: bin.whole = false
}
hxx{version}: in{version} $src_root/manifest
# Build options.
#
cxx.poptions =+ "-I$out_root" "-I$src_root"
# Export options.
#
lib{validate}:
{
cxx.export.poptions = "-I$out_root" "-I$src_root"
cxx.export.libs = $intf_libs
}
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{validate}: bin.lib.version = "-$version.project_id"
else
lib{validate}: bin.lib.version = "-$version.major.$version.minor"
# Install into the bitshift/validate/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/bitshift/validate/
install.subdirs = true
}
View File
View File
+142
View File
@@ -0,0 +1,142 @@
#include <bitshift/validate/execute.hxx>
#include <bitshift/validate/failure.hxx>
#include <bitshift/validate/filter.hxx>
#include <bitshift/validate/setup.hxx>
#include <bitshift/validate/teardown.hxx>
#include <bitshift/validate/test-case.hxx>
namespace bitshift::validate
{
/// @brief Execute setup functions.
///
static
void
execute_setup(reporter& r)
{
auto current = setup::first();
if (!current) {
return;
}
do {
r.log_setup(current->file(), current->line());
current->run();
current = current->next();
} while (current && current != setup::first());
}
/// @brief Execute teardown functions.
///
static
void
execute_teardown(reporter& r)
{
auto current = teardown::first();
if (!current) {
return;
}
do {
r.log_teardown(current->file(), current->line());
current->run();
current = current->next();
} while (current && current != teardown::first());
}
enum execute_result
{
kExecuteIgnored,
kExecutePassed,
kExecuteFailed
};
/// @brief Execute a single test case.
///
/// @param r The reporter.
/// @param tc The test case to execute.
///
static
execute_result
execute_single(reporter& r, test_case& tc)
{
execute_setup(r);
r.log_test_case(tc.name(), tc.description(), tc.file(), tc.line());
try {
tc.run();
}
catch (ignore const&) {
execute_teardown(r);
return kExecuteIgnored;
}
catch (assertion_failure const& failure) {
failure.report(r);
execute_teardown(r);
return kExecuteFailed;
}
execute_teardown(r);
return kExecutePassed;
}
bool
execute_all(reporter& r, strings const& filters)
{
auto current = test_case::first();
if (!current) {
return true;
}
uint16_t total{};
uint16_t ignored{};
uint16_t pass{};
uint16_t fail{};
do {
bool selected{filters.empty()};
for (auto const& j : filters) {
if (match_filter(j, current->name())) {
selected = true;
}
}
if (selected) {
++total;
auto result = execute_single(r, *current);
switch (result) {
case kExecuteIgnored:
++ignored;
break;
case kExecutePassed:
++pass;
break;
case kExecuteFailed:
++fail;
break;
}
}
current = current->next();
} while (current && current != test_case::first());
r.log_summary(total, ignored, pass, fail);
return fail == 0;
}
} // namespace bitshift::validate
+22
View File
@@ -0,0 +1,22 @@
#ifndef bitshift__validate__execute_hxx_
#define bitshift__validate__execute_hxx_
#include <bitshift/validate/types.hxx>
#include <bitshift/validate/reporting.hxx>
namespace bitshift::validate
{
/// @brief Execute all test cases.
///
/// @param r The reporter.
/// @param filters Test case filters.
///
/// @return Returns @c true if all tests passed; @c false otherwise.
///
bool
execute_all(reporter& r, strings const& filters);
} // namespace bitshift::validate
#endif
+130
View File
@@ -0,0 +1,130 @@
#include <bitshift/validate/failure.hxx>
#include <bitshift/validate/reporting.hxx>
namespace bitshift::validate
{
unary_assertion_failure::
unary_assertion_failure(string message,
string file,
uint32_t line,
string expr,
string val)
: _message{std::move(message)}
, _file {std::move(file)}
, _line {line}
, _expr {std::move(expr)}
, _val {std::move(val)}
{}
string const&
unary_assertion_failure::
get_file() const
{
return _file;
}
uint32_t
unary_assertion_failure::
get_line() const
{
return _line;
}
string const&
unary_assertion_failure::
get_expr() const
{
return _expr;
}
string const&
unary_assertion_failure::
get_val() const
{
return _val;
}
void
unary_assertion_failure::
report(reporter& r) const
{
auto p = r.log_failure(_message, _file, _line);
p << "expression: " << _expr << '\n';
p << "value : " << _val << '\n';
}
binary_assertion_failure::
binary_assertion_failure(string message,
string file,
uint32_t line,
string lhs_expr,
string lhs_val,
string rhs_expr,
string rhs_val)
: _message {std::move(message)}
, _file {std::move(file)}
, _line {line}
, _lhs_expr{std::move(lhs_expr)}
, _lhs_val {std::move(lhs_val)}
, _rhs_expr{std::move(rhs_expr)}
, _rhs_val {std::move(rhs_val)}
{}
string const&
binary_assertion_failure::
get_file() const
{
return _file;
}
uint32_t
binary_assertion_failure::
get_line() const
{
return _line;
}
string const&
binary_assertion_failure::
get_lhs_expr() const
{
return _lhs_expr;
}
string const&
binary_assertion_failure::
get_lhs_val() const
{
return _lhs_val;
}
string const&
binary_assertion_failure::
get_rhs_expr() const
{
return _rhs_expr;
}
string const&
binary_assertion_failure::
get_rhs_val() const
{
return _rhs_val;
}
void
binary_assertion_failure::
report(reporter& r) const
{
auto p = r.log_failure(_message, _file, _line);
p << "lhs expression: " << _lhs_expr << '\n';
p << "lhs value : " << _lhs_val << '\n';
p << "rhs expression: " << _rhs_expr << '\n';
p << "rhs value : " << _rhs_val << '\n';
}
} // namespace bitshift::validate
+208
View File
@@ -0,0 +1,208 @@
#ifndef bitshift__validate__failure_hxx_
#define bitshift__validate__failure_hxx_
#include <bitshift/validate/types.hxx>
namespace bitshift::validate
{
struct ignore { };
class reporter;
/// @brief Class used to indicate an assertion failure.
///
class assertion_failure
{
public:
/// @brief Destructor.
///
virtual
~assertion_failure() noexcept = default;
/// @brief Report assertion failure.
///
/// @param r The reporter.
///
virtual
void
report(reporter& r) const = 0;
protected:
/// @brief Constructor.
///
assertion_failure() = default;
/// @brief Copy-construction is disabled.
///
assertion_failure(assertion_failure const&) = delete;
/// @brief Move-construction is disabled.
///
assertion_failure(assertion_failure&&) = delete;
/// @brief Copy-assignment is disabled.
///
assertion_failure& operator=(assertion_failure const&) = delete;
/// @brief Move-assignment is disabled.
///
assertion_failure& operator=(assertion_failure&&) = delete;
};
/// @brief Indicates a unary assertion failure.
///
class unary_assertion_failure
: public assertion_failure
{
public:
/// @brief Constructor.
///
/// @param message The assertion failure message.
/// @param file The file in which the assertion failure occurred.
/// @param line The line on which the assertion failure occurred.
/// @param expr The expression that caused the assertion failure.
/// @param val The expression value that caused the assertion failure.
///
unary_assertion_failure(string message,
string file,
uint32_t line,
string expr,
string val);
/// @brief Copy-construction is disabled.
///
unary_assertion_failure(unary_assertion_failure const&) = delete;
/// @brief Move-construction is disabled.
///
unary_assertion_failure(unary_assertion_failure&&) = delete;
/// @brief Get file.
///
string const&
get_file() const;
/// @brief Get line.
///
uint32_t
get_line() const;
/// @brief Get expression.
///
string const&
get_expr() const;
/// @brief Get expression value.
///
string const&
get_val() const;
void
report(reporter&) const override;
/// @brief Copy-assignment is disabled.
///
unary_assertion_failure& operator=(unary_assertion_failure const&) = delete;
/// @brief Move-assignment is disabled.
///
unary_assertion_failure& operator=(unary_assertion_failure&&) = delete;
private:
string _message;
string _file;
uint32_t _line{};
string _expr;
string _val;
};
/// @brief Indicates a binary assertion failure.
///
class binary_assertion_failure
: public assertion_failure
{
public:
/// @brief Constructor.
///
/// @param message The assertion failure message.
/// @param file The file in which the assertion failure occurred.
/// @param line The line on which the assertion failure occurred.
/// @param lhs_expr The left-hand side expression that caused the assertion failure.
/// @param lhs_val The left-hand side expression value that caused the assertion failure.
/// @param rhs_expr The right-hand side expression that caused the assertion failure.
/// @param rhs_val The right-hand side expression value that caused the assertion failure.
///
binary_assertion_failure(string message,
string file,
uint32_t line,
string lhs_expr,
string lhs_val,
string rhs_expr,
string rhs_val);
/// @brief Copy-construction is disabled.
///
binary_assertion_failure(binary_assertion_failure const&) = delete;
/// @brief Move-construction is disabled.
///
binary_assertion_failure(binary_assertion_failure&&) = delete;
/// @brief Get file.
///
string const&
get_file() const;
/// @brief Get line.
///
uint32_t
get_line() const;
/// @brief Get left-hand side expression.
///
string const&
get_lhs_expr() const;
/// @brief Get left-hand side expression value.
///
string const&
get_lhs_val() const;
/// @brief Get right-hand side expression.
///
string const&
get_rhs_expr() const;
/// @brief Get right-hand side expression value.
///
string const&
get_rhs_val() const;
void
report(reporter&) const override;
/// @brief Copy-assignment is disabled.
///
binary_assertion_failure& operator=(binary_assertion_failure const&) = delete;
/// @brief Move-assignment is disabled.
///
binary_assertion_failure& operator=(binary_assertion_failure&&) = delete;
private:
string _message;
string _file;
uint32_t _line{};
string _lhs_expr;
string _lhs_val;
string _rhs_expr;
string _rhs_val;
};
} // namespace bitshift::validate
#endif
+64
View File
@@ -0,0 +1,64 @@
#include <bitshift/validate/filter.hxx>
namespace bitshift::validate
{
bool
match_filter(string const& filter, string const& subject)
{
auto fcur = filter.begin();
auto fend = filter.end();
auto scur = subject.begin();
auto send = subject.end();
while (fcur != fend && scur != send) {
auto a = fcur;
for (;;) {
if (fcur == fend || *fcur == '.') {
break;
}
++fcur;
}
auto aend = fcur;
if (fcur != fend && *fcur == '.') {
++fcur;
}
auto b = scur;
for (;;) {
if (scur == send || *scur == '.') {
break;
}
++scur;
}
auto bend = scur;
if (scur != send && *scur == '.') {
++scur;
}
string astr{a, aend};
string bstr{b, bend};
if (astr == "*") {
continue;
}
if (astr != bstr) {
return false;
}
}
// We determine a match by check that we've reached the end of both the
// filter and the subject we're testing.
//
return fcur == fend && scur == send;
}
} // namespace bitshift::validate
+19
View File
@@ -0,0 +1,19 @@
#ifndef bitshift__validate__filter_hxx_
#define bitshift__validate__filter_hxx_
#include <bitshift/validate/types.hxx>
namespace bitshift::validate
{
/// @brief Match subject against a given filter pattern.
///
/// @param filter The filter pattern to match against.
/// @param subject The subject to test.
///
bool
match_filter(string const& filter, string const& subject);
} // namespace bitshift::validate
#endif
+114
View File
@@ -0,0 +1,114 @@
#ifndef bitshift__validate__intrusive_list_hxx_
#define bitshift__validate__intrusive_list_hxx_
namespace bitshift::validate
{
template<typename T>
class intrusive_list
{
public:
protected:
/// @brief Constructor.
///
intrusive_list()
{
if (!first()) {
first() = self();
}
if (!last()) {
last() = self();
}
first()->_prev = self();
last()->_next = self();
_prev = last();
_next = first();
last() = self();
}
/// @brief Copy-construction is disabled.
///
intrusive_list(intrusive_list<T> const&) = delete;
/// @brief Move-construction is disabled.
///
intrusive_list(intrusive_list<T>&&) = delete;
/// @brief Destructor.
///
~intrusive_list() noexcept
{
if (first() == this) {
first() = _next;
}
if (last() == this) {
last() = _prev;
}
_prev->_next = _next;
_next->_prev = _prev;
}
/// @brief Copy-assignment is disabled.
///
intrusive_list<T>& operator=(intrusive_list<T> const&) = delete;
/// @brief Move-assignment is disabled.
///
intrusive_list<T>& operator=(intrusive_list<T>&&) = delete;
protected:
/// @brief Get the previous node.
///
T*
previous()
{
return static_cast<T*>(_prev);
}
/// @brief Get the next node.
///
T*
next()
{
return static_cast<T*>(_next);
}
static
T*&
first()
{
static T* ptr{nullptr};
return ptr;
}
static
T*&
last()
{
static T* ptr{nullptr};
return ptr;
}
private:
T*
self()
{
return static_cast<T*>(this);
}
private:
T* _prev{nullptr};
T* _next{nullptr};
};
} // namespace bitshift::validate
#endif
+202
View File
@@ -0,0 +1,202 @@
#ifndef bitshift__validate__macro_hxx_
#define bitshift__validate__macro_hxx_
#include <bitshift/validate/assert.hxx>
#include <bitshift/validate/setup.hxx>
#include <bitshift/validate/teardown.hxx>
#include <bitshift/validate/test-case.hxx>
/// @defgroup assertions Assertions
///
/// @brief Describes the BitShift Validate assertion macros.
///
#define BV_TEST_SETUP_2(file, line) \
static \
void \
test_setup_func_##line(); \
bitshift::validate::setup \
test_setup_##line{file, line, &test_setup_func_##line}; \
void \
test_setup_func_##line()
#define BV_TEST_SETUP_1(file, line) \
BV_TEST_SETUP_2(file, line)
/// @ingroup assertions
/// @brief Register a test setup function.
/// @param desc A description of the setup function.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST_SETUP(setup environment)
/// {
/// // ...
/// }
/// ```
///
#define BV_TEST_SETUP(desc) \
BV_TEST_SETUP_1(__FILE__, __LINE__)
#define BV_TEST_TEARDOWN_2(file, line) \
static \
void \
test_teardown_func_##line(); \
bitshift::validate::teardown \
test_teardown_##line{file, line, &test_teardown_func_##line}; \
void \
test_teardown_func_##line()
#define BV_TEST_TEARDOWN_1(file, line) \
BV_TEST_TEARDOWN_2(file, line)
/// @ingroup assertions
/// @brief Register a test teardown function.
/// @param desc A description of the teardown function.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST_TEARDOWN(cleanup resources)
/// {
/// // ...
/// }
/// ```
///
#define BV_TEST_TEARDOWN(desc) \
BV_TEST_TEARDOWN_1(__FILE__, __LINE__)
#define BV_TEST_2(name, desc, file, line) \
static \
void \
test_func_##line(); \
bitshift::validate::test_case \
test_##line{#name, desc, file, line, &test_func_##line}; \
void \
test_func_##line()
#define BV_TEST_1(name, desc, file, line) \
BV_TEST_2(name, desc, file, line)
/// @ingroup assertions
/// @brief Register a test function.
/// @param name An identifier for the test.
/// @param desc A decsription of the test.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST(str.length, "test string length")
/// {
/// // ...
/// }
/// ```
///
#define BV_TEST(name, desc) \
BV_TEST_1(name, desc, __FILE__, __LINE__)
/// @ingroup assertions
/// @brief Ignore a test.
/// @hideinitializer
///
#define BV_IGNORE throw bitshift::validate::ignore {};
/// @ingroup assertions
/// @brief Assert a @c true value.
/// @param expr The expression to test.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST(str.empty, "test string 'empty'")
/// {
/// std::string empty{};
/// BV_ASSERT_TRUE(empty.empty());
/// }
/// ```
///
#define BV_ASSERT_TRUE(expr) \
bitshift::validate::assertion::assert_true((expr), #expr)
/// @ingroup assertions
/// @brief Assert a @c false value.
/// @param expr The expression to test.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST(str.not_empty, "test string not 'empty'")
/// {
/// std::string not_empty{"hello, world"};
/// BV_ASSERT_FALSE(not_empty.empty());
/// }
/// ```
///
#define BV_ASSERT_FALSE(expr) \
bitshift::validate::assertion::assert_false((expr), #expr)
/// @ingroup assertions
/// @brief Assert a @c nullptr value.
/// @param expr The expression to test.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST(ptr.is_null, "test null pointer")
/// {
/// char const* this_is_null{nullptr};
/// BV_ASSERT_NULLPTR(this_is_null);
/// }
/// ```
///
#define BV_ASSERT_NULLPTR(expr) \
bitshift::validate::assertion::assert_nullptr((expr), #expr)
/// @ingroup assertions
/// @brief Assert @a lhs is equal to @a rhs.
/// @param lhs The left-hand side expression to test.
/// @param rhs The right-hand side expression to test.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST(equal, "test equal")
/// {
/// int lhs = 1;
/// int rhs = 1;
/// BV_ASSERT_EQUAL(lhs, rhs);
/// }
/// ```
///
#define BV_ASSERT_EQUAL(lhs, rhs) \
bitshift::validate::assertion::assert_equal((lhs), (rhs), #lhs, #rhs)
/// @ingroup assertions
/// @brief Assert @a lhs is not equal to @a rhs.
/// @param lhs The left-hand side expression to test.
/// @param rhs The right-hand side expression to test.
/// @hideinitializer
///
/// ### Usage Example:
///
/// ```cxx
/// BV_TEST(not_equal, "test not equal")
/// {
/// int lhs = 1;
/// int rhs = 2;
/// BV_ASSERT_NOT_EQUAL(lhs, rhs);
/// }
/// ```
///
#define BV_ASSERT_NOT_EQUAL(lhs, rhs) \
bitshift::validate::assertion::assert_not_equal((lhs), (rhs), #lhs, #rhs)
#endif
+165
View File
@@ -0,0 +1,165 @@
#include <bitshift/validate/main.hxx>
#include <bitshift/validate/ansi.hxx>
#include <bitshift/validate/execute.hxx>
#include <bitshift/validate/options.hxx>
#include <bitshift/validate/test-case.hxx>
#include <bitshift/validate/version.hxx>
#include <iomanip>
namespace bitshift::validate
{
/// @brief Print version information.
///
/// @param os The output stream.
///
static
void
print_version(ostream& os)
{
os << "BitShift Validate Test Runner (" << BITSHIFT_VALIDATE_VERSION_STR << ")\n";
}
/// @brief Print usage information.
///
/// @param execname The program execname.
/// @param os The output stream.
///
static
void
print_usage(string const& execname, ostream& os)
{
auto b = [](auto&&... args) -> string
{
stringstream str;
str << ansi::kBold;
(
(str << args), ...
);
str << ansi::reset;
return str.str();
};
os << b("usage: ") << execname << " [<option>...]\n"
<< '\n'
<< "This is the BitShift Validate Test Runner.\n"
<< '\n'
<< b("OPTIONS") << '\n'
<< " -v, --version Show version information\n"
<< " -h, --help Show this help\n"
<< " -l, --list List all available tests\n"
<< " -q, --quiet Silence all output\n"
<< " --show-file Include file information in output\n"
<< " --show-line Include line information in output\n"
<< " -f, --filter <filter> Add a test filter\n"
<< '\n'
;
}
static
void
list_test_cases(ostream& os)
{
auto current = test_case::first();
if (!current) {
return;
}
size_t max_length{0};
// Calculate the length of the longest test case name.
//
do {
if (auto length = current->name().size(); length > max_length) {
max_length = length;
}
current = current->next();
} while (current && current != test_case::first());
current = test_case::first();
if (!current) {
return;
}
do {
os << ansi::kBold << std::setw(max_length) << std::left
<< current->name() << ansi::reset << ": "
<< current->description()
<< "\n";
current = current->next();
} while (current && current != test_case::first());
}
int
main(int argc, char* argv[])
try
{
options opts{strings{argv + 1, argc + argv}};
if (opts.version) {
print_version(cout);
return 0;
}
if (opts.usage) {
print_usage(argv[0], cout);
return 0;
}
if (opts.list) {
list_test_cases(cout);
return 0;
}
using vtype = variant<quiet_reporter, ostream_reporter>;
auto create_reporter = [&] -> vtype
{
if (opts.quiet) {
return vtype{std::in_place_type<quiet_reporter>};
}
return vtype{
std::in_place_type<ostream_reporter>,
cout,
opts.show_file,
opts.show_line
};
};
auto r = create_reporter();
auto get_reporter = [&] -> reporter&
{
if (std::holds_alternative<quiet_reporter>(r)) {
return std::get<quiet_reporter>(r);
}
else if (std::holds_alternative<ostream_reporter>(r)) {
return std::get<ostream_reporter>(r);
}
else {
throw runtime_error{"invalid reporter"};
}
};
if (opts.show_line && !opts.show_file) {
throw runtime_error{"'--show-line' requires '--show-file'"};
}
return execute_all(get_reporter(), opts.filters) ? 0 : 1;
}
catch (exception const& ex) {
cerr << argv[0] << ": error: " << ex.what() << '\n';
return 1;
}
catch (...) {
return 1;
}
} // namespace bitshift::validate
+19
View File
@@ -0,0 +1,19 @@
#ifndef bitshift__validate__main_hxx_
#define bitshift__validate__main_hxx_
#include <bitshift/validate/types.hxx>
namespace bitshift::validate
{
/// @brief Main.
///
/// @param argc Your typical argc.
/// @param argv Your typical argv.
///
int
main(int argc, char* argv[]);
} // namespace bitshift::validate
#endif
+103
View File
@@ -0,0 +1,103 @@
#include <bitshift/validate/options.hxx>
namespace bitshift::validate
{
options::
options(strings args)
{
for (auto it = args.begin(); it != args.end(); ++it) {
if (it->empty()) {
continue;
}
if (*it == "--") {
throw runtime_error{"invalid argument '--'"};
}
if ('-' != (*it)[0] || 1 == it->size()) {
throw runtime_error{"invalid argument '" + *it + "'"};
}
if ('-' == (*it)[0] && '-' != (*it)[1]) {
for (auto const& j : it->substr(1)) {
switch (j) {
case 'v':
version = true;
continue;
case 'h':
usage = true;
continue;
case 'l':
list = true;
continue;
case 'q':
quiet = true;
continue;
case 'f':
++it;
if (it == args.end()) {
throw runtime_error{"missing argument to '-f'"};
}
filters.emplace_back(*it);
continue;
}
throw runtime_error{"unrecognized option '" + string{&j, 1} + "'"};
}
continue;
}
if (*it == "--version") {
version = true;
continue;
}
if (*it == "--help") {
usage = true;
continue;
}
if (*it == "--list") {
list = true;
continue;
}
if (*it == "--quiet") {
quiet = true;
continue;
}
if (*it == "--show-file") {
show_file = true;
continue;
}
if (*it == "--show-line") {
show_line = true;
continue;
}
if (*it == "--filter") {
++it;
if (it == args.end()) {
throw runtime_error{"missing argument to '--filter'"};
}
filters.emplace_back(*it);
continue;
}
throw runtime_error{"unrecognized option '" + *it + "'"};
}
}
} // namespace bitshift::validate
+50
View File
@@ -0,0 +1,50 @@
#ifndef bitshift__validate__options_hxx_
#define bitshift__validate__options_hxx_
#include <bitshift/validate/types.hxx>
namespace bitshift::validate
{
struct options
{
/// @brief Constructor.
///
/// @param args Program arguments.
///
explicit
options(strings args);
/// @brief '-v' or '--version' specified on the command-line.
///
bool version{false};
/// @brief '-h- or '--help' specified on the command-line.
///
bool usage{false};
/// @brief '-l' or '--list' specified on the command-line.
///
bool list{};
/// @brief '-q' or '--quiet' specified on the command-line.
///
bool quiet{};
/// @brief '--show-file' specified on the command-line.
///
bool show_file{};
/// @brief '--show-line' specified on the command-line.
///
bool show_line{};
/// @brief '--filter' option(s).
///
strings filters;
};
} // namespace bitshift::validate
#endif
+263
View File
@@ -0,0 +1,263 @@
#include <bitshift/validate/reporting.hxx>
#include <bitshift/validate/ansi.hxx>
namespace bitshift::validate
{
static
void
indent(ostream& os, size_t count, string const& str)
{
stringstream sstr{str};
for (string line; std::getline(sstr, line);) {
os << string(count, ' ') << line << "\n";
}
}
static
string
_bold(auto&&... args)
{
stringstream str;
str << ansi::kBold;
(
(str << args), ...
);
str << ansi::reset;
return str.str();
}
reporter::
~reporter() noexcept
{}
reporter::proxy
reporter::
log_setup(string const& file, int line)
{
return proxy{
[this, file, line](string const& detail)
{
do_log_setup(file, line, detail);
}
};
}
reporter::proxy
reporter::
log_teardown(string const& file, int line)
{
return proxy{
[this, file, line](string const& detail)
{
do_log_teardown(file, line, detail);
}
};
}
reporter::proxy
reporter::
log_test_case(string const& name,
string const& description,
string const& file,
int line)
{
return proxy{
[this, name, description, file, line](string const& detail)
{
do_log_test_case(name, description, file, line, detail);
}
};
}
reporter::proxy
reporter::
log_failure(string message, string file, int line)
{
return proxy{
[this, message, file, line](string const& detail)
{
do_log_failure(message, file, line, detail);
}
};
}
void
reporter::
log_summary(uint16_t total,
uint16_t ignored,
uint16_t pass,
uint16_t fail)
{
do_log_summary(total, ignored, pass, fail);
}
reporter::
reporter()
{}
reporter::proxy::
~proxy() noexcept(false) // noexcept(false) since _cb(...) may throw.
{
_cb(_detail.str());
}
reporter::proxy::
proxy(function<void(string const&)> cb)
: _cb{cb}
{}
ostream_reporter::
ostream_reporter(ostream& os, bool show_file, bool show_line)
: _os {os}
, _show_file{show_file}
, _show_line{show_line}
{}
ostream_reporter::
~ostream_reporter() noexcept
{}
ostream&
ostream_reporter::
output()
{
return _os;
}
void
ostream_reporter::
do_log_setup(string const& file, int line, string const& detail)
{
_os << "Executing setup...\n";
if (_show_file) {
_os << " defined in " << file;
if (_show_line) {
_os << " at line " << line;
}
_os << '\n';
}
indent(_os, 6, detail);
}
void
ostream_reporter::
do_log_teardown(string const& file, int line, string const& detail)
{
_os << "Executing teardown...\n";
if (_show_file) {
_os << " defined in " << file;
if (_show_line) {
_os << " at line " << line;
}
_os << '\n';
}
indent(_os, 6, detail);
}
void
ostream_reporter::
do_log_test_case(string const& name,
string const& description,
string const& file,
int line,
string const& detail)
{
_os << "Executing test case \""
<< ansi::kBold << name << ansi::reset << "\": "
<< description << "\n";
if (_show_file) {
_os << " defined in " << file;
if (_show_line) {
_os << " at line " << line;
}
_os << '\n';
}
indent(_os, 6, detail);
}
void
ostream_reporter::
do_log_failure(string const& message,
string const& file,
int line,
string const& detail)
{
_os << ansi{ansi::kRed, ansi::kBold}
<< " assertion failure: " << ansi::reset << message << '\n'
<< " source " << file << ":" << line << ":\n";
indent(_os, 6, detail);
}
void
ostream_reporter::
do_log_summary(uint16_t total,
uint16_t ignored,
uint16_t pass,
uint16_t fail)
{
_os << ansi::kBold << "Test statistics\n" << ansi::reset
<< " Tests : " << total << '\n'
<< " Tests ignored: " << ignored << '\n'
<< " Tests passed : " << pass << '\n'
<< " Tests failed : " << fail << '\n'
;
}
quiet_reporter::
quiet_reporter()
{}
quiet_reporter::
~quiet_reporter() noexcept
{}
void
quiet_reporter::
do_log_setup(string const&, int, string const&)
{}
void
quiet_reporter::
do_log_teardown(string const&, int, string const&)
{}
void
quiet_reporter::
do_log_test_case(string const&,
string const&,
string const&,
int,
string const&)
{}
void
quiet_reporter::
do_log_failure(string const&, string const&, int, string const&)
{}
void
quiet_reporter::
do_log_summary(uint16_t, uint16_t, uint16_t, uint16_t)
{}
} // namespace bitshift::validate
+350
View File
@@ -0,0 +1,350 @@
#ifndef bitshift__validate__reporting_hxx_
#define bitshift__validate__reporting_hxx_
#include <bitshift/validate/types.hxx>
#include <bitshift/validate/setup.hxx>
#include <bitshift/validate/teardown.hxx>
#include <bitshift/validate/test-case.hxx>
namespace bitshift::validate
{
/// @brief Abstract test suite reporter.
///
class reporter
{
class proxy;
friend proxy;
public:
/// @brief Destructor.
///
virtual
~reporter() noexcept;
/// @brief Log test suite setup execution.
///
/// @param file The file in which the setup is defined.
/// @param line The line on which the setup is defined.
///
proxy
log_setup(string const& file, int line);
/// @brief Log test suite teardown execution.
///
/// @param file The file in which the teardown is defined.
/// @param line The line on which the teardown is defined.
///
proxy
log_teardown(string const& file, int line);
/// @brief Log test case execution.
///
/// @param name The name of the test case.
/// @param description A description of the test case.
/// @param file The file in which the test case is defined.
/// @param line The line on which the test case is defined.
///
proxy
log_test_case(string const& name,
string const& description,
string const& file,
int line);
/// @brief Log test case failure.
///
/// @param message A message describing the failure.
/// @param file The file in which the failure occurred.
/// @param line The line on which the error occurred.
///
proxy
log_failure(string message, string file, int line);
/// @brief Log test suite summary.
///
/// @param total The total number of tests.
/// @param ignored The total number of ignored tests.
/// @param pass The total number of passed tests.
/// @param fail The total number of failed tests.
///
void
log_summary(uint16_t total,
uint16_t ignored,
uint16_t pass,
uint16_t fail);
protected:
/// @brief Constructor.
///
reporter();
/// @brief Copy-construction is disabled.
///
reporter(reporter const&) = delete;
/// @brief Move-construction is disabled.
///
reporter(reporter&&) = delete;
/// @brief Log test suite setup execution.
///
/// @param file The file in which the setup is defined.
/// @param line The line on which the setup is defined.
/// @param detail Extra details.
///
virtual
void
do_log_setup(string const& file, int line, string const& detail) = 0;
/// @brief Log test suite teardown execution.
///
/// @param file The file in which the teardown is defined.
/// @param line The line on which the teardown is defined.
/// @param detail Extra details.
///
virtual
void
do_log_teardown(string const& file, int line, string const& detail) = 0;
/// @brief Log test case execution.
///
/// @param name The name of the test case.
/// @param description A description of the test case.
/// @param file The file in which the test case is defined.
/// @param line The line on which the test case is defined.
/// @param detail Extra details.
///
virtual
void
do_log_test_case(string const& name,
string const& description,
string const& file,
int line,
string const& detail) = 0;
/// @brief Log test case failure.
///
/// @param message A message describing the failure.
/// @param file The file in which the failure occurred.
/// @param line The line on which the error occurred.
/// @param detail Extra details.
///
virtual
void
do_log_failure(string const& message,
string const& file,
int line,
string const& detail) = 0;
/// @brief Log test suite summary.
///
/// @param total The total number of tests.
/// @param ignored The total number of ignored tests.
/// @param pass The total number of passed tests.
/// @param fail The total number of failed tests.
///
virtual
void
do_log_summary(uint16_t total,
uint16_t ignored,
uint16_t pass,
uint16_t fail) = 0;
/// @brief Copy-construction is disabled.
///
reporter& operator=(reporter const&) = delete;
/// @brief Move-construction is disabled.
///
reporter& operator=(reporter&&) = delete;
};
class reporter::proxy
{
friend reporter;
public:
/// @brief Destructor.
///
~proxy() noexcept(false);
template<typename U>
proxy&
operator<<(U const& other)
{
_detail << std::boolalpha << other;
return *this;
}
private:
/// @brief Constructor.
///
/// @param cb Function call from destructor.
///
explicit
proxy(function<void(string const&)> cb);
/// @brief Copy-construction is disabled.
///
proxy(proxy const&) = delete;
/// @brief Move-construction is disabled.
///
proxy(proxy&&) = delete;
/// @brief Copy-assignment is disabled.
///
proxy& operator=(proxy const&) = delete;
/// @brief Move-assignment is disabled.
///
proxy& operator=(proxy&&) = delete;
private:
function<void(string const&)> _cb;
stringstream _detail;
};
/// @brief Default ostream-based reporter.
///
class ostream_reporter
: public reporter
{
public:
/// @brief Constructor.
///
/// @param os The output stream.
/// @param show_file Include file information in output.
/// @param show_line Include line information in output.
///
ostream_reporter(ostream& os, bool show_file, bool show_line);
/// @brief Copy-construction is disabled.
///
ostream_reporter(ostream_reporter const&) = delete;
/// @brief Move-construction is disabled.
///
ostream_reporter(ostream_reporter&&) = delete;
/// @brief Destructor.
///
~ostream_reporter() noexcept override;
/// @brief Get a reference to the associated output stream.
///
ostream&
output();
/// @brief Copy-assignment is disabled.
///
ostream_reporter& operator=(ostream_reporter const&) = delete;
/// @brief Move-assignment is disabled.
///
ostream_reporter& operator=(ostream_reporter&&) = delete;
protected:
void
do_log_setup(string const& file,
int line,
string const& detail) override;
void
do_log_teardown(string const& file,
int line,
string const& detail) override;
void
do_log_test_case(string const& name,
string const& description,
string const& file,
int line,
string const& detail) override;
void
do_log_failure(string const& message,
string const& file,
int line,
string const& detail) override;
void
do_log_summary(uint16_t total,
uint16_t ignored,
uint16_t pass,
uint16_t fail) override;
private:
ostream& _os;
bool _show_file{};
bool _show_line{};
};
/// @brief Let there be silence.
///
class quiet_reporter
: public reporter
{
public:
/// @brief Constructor.
///
quiet_reporter();
/// @brief Copy-construction is disabled.
///
quiet_reporter(quiet_reporter const&) = delete;
/// @brief Move-construction is disabled.
///
quiet_reporter(quiet_reporter&&) = delete;
/// @brief Destructor.
///
~quiet_reporter() noexcept override;
/// @brief Copy-assignment is disabled.
///
quiet_reporter& operator=(quiet_reporter const&) = delete;
/// @brief Move-assignment is disabled.
///
quiet_reporter& operator=(quiet_reporter&&) = delete;
protected:
void
do_log_setup(string const& file,
int line,
string const& detail) override;
void
do_log_teardown(string const& file,
int line,
string const& detail) override;
void
do_log_test_case(string const& name,
string const& description,
string const& file,
int line,
string const& detail) override;
void
do_log_failure(string const& message,
string const& file,
int line,
string const& detail) override;
void
do_log_summary(uint16_t total,
uint16_t ignored,
uint16_t pass,
uint16_t fail) override;
};
} // namespace bitshift::validate
#endif
+66
View File
@@ -0,0 +1,66 @@
#include <bitshift/validate/setup.hxx>
namespace bitshift::validate
{
setup::
setup(string file, int line, function<void()> func)
: _file{std::move(file)}
, _line{line}
, _func{func}
{}
setup::
~setup() noexcept
{}
string const&
setup::
file() const
{
return _file;
}
int
setup::
line() const
{
return _line;
}
setup*
setup::
next()
{
return intrusive_list<setup>::next();
}
setup*
setup::
previous()
{
return intrusive_list<setup>::previous();
}
void
setup::
run()
{
_func();
}
setup*
setup::
first()
{
return intrusive_list<setup>::first();
}
setup*
setup::
last()
{
return intrusive_list<setup>::last();
}
} // namespace bitshift::validate
+91
View File
@@ -0,0 +1,91 @@
#ifndef bitshift__validate__setup_hxx_
#define bitshift__validate__setup_hxx_
#include <bitshift/validate/types.hxx>
#include <bitshift/validate/intrusive-list.hxx>
namespace bitshift::validate
{
class setup
: intrusive_list<setup>
{
friend intrusive_list<setup>;
public:
/// @brief Constructor.
///
/// @param file The file in which the setup function is defined.
/// @param line The line on which the setup function is defined.
/// @param func The setup function.
///
setup(string file, int line, function<void()> func);
/// @brief Copy-construction is disabled.
///
setup(setup const&) = delete;
/// @brief Move-construction is disabled.
///
setup(setup&&) = delete;
/// @brief Destructor.
///
~setup() noexcept;
/// @brief Get the file in which the setup function is defined.
///
string const&
file() const;
/// @brief Get the line on which the setup function is defined.
///
int
line() const;
/// @brief Get a pointer to the next setup function.
///
setup*
next();
/// @brief Get a pointer to the previous setup function.
///
setup*
previous();
/// @brief Run the setup function.
///
void
run();
/// @brief Copy-construction is disabled.
///
setup& operator=(setup const&) = delete;
/// @brief Move-construction is disabled.
///
setup& operator=(setup&&) = delete;
/// @brief Get a pointer to the first setup function.
///
static
setup*
first();
/// @brief Get a pointer to the last setup function.
///
static
setup*
last();
private:
string _file;
int _line{};
function<void()> _func;
};
} // namespace bitshift::validate
#endif
+66
View File
@@ -0,0 +1,66 @@
#include <bitshift/validate/teardown.hxx>
namespace bitshift::validate
{
teardown::
teardown(string file, int line, function<void()> func)
: _file{std::move(file)}
, _line{line}
, _func{func}
{}
teardown::
~teardown() noexcept
{}
string const&
teardown::
file() const
{
return _file;
}
int
teardown::
line() const
{
return _line;
}
teardown*
teardown::
next()
{
return intrusive_list<teardown>::next();
}
teardown*
teardown::
previous()
{
return intrusive_list<teardown>::previous();
}
void
teardown::
run()
{
_func();
}
teardown*
teardown::
first()
{
return intrusive_list<teardown>::first();
}
teardown*
teardown::
last()
{
return intrusive_list<teardown>::last();
}
} // namespace bitshift::validate
+91
View File
@@ -0,0 +1,91 @@
#ifndef bitshift__validate__teardown_hxx_
#define bitshift__validate__teardown_hxx_
#include <bitshift/validate/types.hxx>
#include <bitshift/validate/intrusive-list.hxx>
namespace bitshift::validate
{
class teardown
: intrusive_list<teardown>
{
friend intrusive_list<teardown>;
public:
/// @brief Constructor.
///
/// @param file The file in which the teardown function is defined.
/// @param line The line on which the teardown function is defined.
/// @param func The teardown function.
///
teardown(string file, int line, function<void()> func);
/// @brief Copy-construction is disabled.
///
teardown(teardown const&) = delete;
/// @brief Move-construction is disabled.
///
teardown(teardown&&) = delete;
/// @brief Destructor.
///
~teardown() noexcept;
/// @brief Get the file in which the teardown function is defined.
///
string const&
file() const;
/// @brief Get the line on which the teardown function is defined.
///
int
line() const;
/// @brief Get a pointer to the next teardown function.
///
teardown*
next();
/// @brief Get a pointer to the previous teardown function.
///
teardown*
previous();
/// @brief Run the teardown function.
///
void
run();
/// @brief Copy-construction is disabled.
///
teardown& operator=(teardown const&) = delete;
/// @brief Move-construction is disabled.
///
teardown& operator=(teardown&&) = delete;
/// @brief Get a pointer to the first teardown function.
///
static
teardown*
first();
/// @brief Get a pointer to the last teardown function.
///
static
teardown*
last();
private:
string _file;
int _line{};
function<void()> _func;
};
} // namespace bitshift::validate
#endif
+86
View File
@@ -0,0 +1,86 @@
#include <bitshift/validate/test-case.hxx>
namespace bitshift::validate
{
test_case::
test_case(string name,
string description,
string file,
int line,
function<void()> func)
: _name {std::move(name)}
, _description{std::move(description)}
, _file {std::move(file)}
, _line {line}
, _func {func}
{}
test_case::
~test_case() noexcept
{}
string const&
test_case::
name() const
{
return _name;
}
string const&
test_case::
description() const
{
return _description;
}
string const&
test_case::
file() const
{
return _file;
}
int
test_case::
line() const
{
return _line;
}
test_case*
test_case::
next()
{
return intrusive_list<test_case>::next();
}
test_case*
test_case::
previous()
{
return intrusive_list<test_case>::previous();
}
void
test_case::
run()
{
_func();
}
test_case*
test_case::
first()
{
return intrusive_list<test_case>::first();
}
test_case*
test_case::
last()
{
return intrusive_list<test_case>::last();
}
} // namespace bitshift::validate
+109
View File
@@ -0,0 +1,109 @@
#ifndef bitshift__validate__test_case_hxx_
#define bitshift__validate__test_case_hxx_
#include <bitshift/validate/types.hxx>
#include <bitshift/validate/intrusive-list.hxx>
namespace bitshift::validate
{
class test_case
: intrusive_list<test_case>
{
friend intrusive_list<test_case>;
public:
/// @brief Constructor.
///
/// @param name The name of the test case.
/// @param description A description of the test case.
/// @param file The file in which the test case function is defined.
/// @param line The line on which the test case function is defined.
/// @param func The test case function.
///
test_case(string name,
string description,
string file,
int line,
function<void()> func);
/// @brief Copy-construction is disabled.
///
test_case(test_case const&) = delete;
/// @brief Move-construction is disabled.
///
test_case(test_case&&) = delete;
/// @brief Destructor.
///
~test_case() noexcept;
/// @brief Get the name of the test case.
///
string const&
name() const;
/// @brief Get the description of the test case.
///
string const&
description() const;
/// @brief Get the file in which the test case function is defined.
///
string const&
file() const;
/// @brief Get the line on which the test case function is defined.
///
int
line() const;
/// @brief Get a pointer to the next test case function.
///
test_case*
next();
/// @brief Get a pointer to the previous test case function.
///
test_case*
previous();
/// @brief Run the test case function.
///
void
run();
/// @brief Copy-construction is disabled.
///
test_case& operator=(test_case const&) = delete;
/// @brief Move-construction is disabled.
///
test_case& operator=(test_case&&) = delete;
/// @brief Get a pointer to the first test case function.
///
static
test_case*
first();
/// @brief Get a pointer to the last test case function.
///
static
test_case*
last();
private:
string _name;
string _description;
string _file;
int _line{};
function<void()> _func;
};
} // namespace bitshift::validate
#endif
+23
View File
@@ -0,0 +1,23 @@
#ifndef bitshift__validate__traits_hxx_
#define bitshift__validate__traits_hxx_
#include <bitshift/validate/types.hxx>
namespace bitshift::validate
{
namespace traits
{
template<typename T>
concept has_output_operator = requires(ostream& os, T const& v)
{
{ os << v };
};
} // namespace traits
} // namespace bitshift::validate
#endif
+65
View File
@@ -0,0 +1,65 @@
#ifndef bitshift__validate__types_hxx_
#define bitshift__validate__types_hxx_
#include <cstddef>
#include <cstdint>
#include <exception>
#include <functional>
#include <iostream>
#include <list>
#include <source_location>
#include <sstream>
#include <stdexcept>
#include <string>
#include <variant>
#include <vector>
namespace bitshift::validate
{
namespace types
{
using std::size_t;
using std::int8_t;
using std::int16_t;
using std::int32_t;
using std::int64_t;
using std::uint8_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::source_location;
using std::istream;
using std::ostream;
using std::iostream;
using std::stringstream;
using std::cin;
using std::cout;
using std::cerr;
using std::variant;
using std::list;
using std::vector;
using std::string;
using strings = vector<string>;
using std::function;
using std::exception;
using std::runtime_error;
} // namespace types
using namespace types;
} // namespace bitshift::validate
#endif
+7
View File
@@ -0,0 +1,7 @@
#ifndef bitshift__validate__validate_hxx_
#define bitshift__validate__validate_hxx_
#include <bitshift/validate/macro.hxx>
#include <bitshift/validate/main.hxx>
#endif
+37
View File
@@ -0,0 +1,37 @@
#ifndef bitshift__validate__version_hxx_
#define bitshift__validate__version_hxx_
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
//
// AAAAA - major version number
// BBBBB - minor version number
// CCCCC - bugfix version number
// DDD - alpha / beta (DDD + 500) version number
// E - final (0) / snapshot (1)
//
// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
//
// Version AAAAABBBBBCCCCCDDDE
//
// 0.1.0 0000000001000000000
// 0.1.2 0000000001000020000
// 1.2.3 0000100002000030000
// 2.2.0-a.1 0000200001999990010
// 3.0.0-b.2 0000299999999995020
// 2.2.0-a.1.z 0000200001999990011
//
#define BITSHIFT_VALIDATE_VERSION $bitshift_validate.version.project_number$ULL
#define BITSHIFT_VALIDATE_VERSION_STR "$bitshift_validate.version.project$"
#define BITSHIFT_VALIDATE_VERSION_ID "$bitshift_validate.version.project_id$"
#define BITSHIFT_VALIDATE_VERSION_FULL "$bitshift_validate.version$"
#define BITSHIFT_VALIDATE_VERSION_MAJOR $bitshift_validate.version.major$
#define BITSHIFT_VALIDATE_VERSION_MINOR $bitshift_validate.version.minor$
#define BITSHIFT_VALIDATE_VERSION_PATCH $bitshift_validate.version.patch$
#define BITSHIFT_VALIDATE_PRE_RELEASE $bitshift_validate.version.pre_release$
#define BITSHIFT_VALIDATE_SNAPSHOT_SN $bitshift_validate.version.snapshot_sn$ULL
#define BITSHIFT_VALIDATE_SNAPSHOT_ID "$bitshift_validate.version.snapshot_id$"
#endif
+4
View File
@@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/
+7
View File
@@ -0,0 +1,7 @@
project = bitshift-validate
using version
using config
using test
using install
using dist
+6
View File
@@ -0,0 +1,6 @@
$out_root/
{
include bitshift/validate/
}
export $out_root/bitshift/validate/lib{validate}
+14
View File
@@ -0,0 +1,14 @@
cxx.internal.scope = current
cxx.std = latest
using cxx
hxx{*}: extension = hxx
ixx{*}: extension = ixx
txx{*}: extension = txx
cxx{*}: extension = cxx
# The test target for cross-testing (running tests under Wine, etc).
#
test.target = $cxx.target
+32
View File
@@ -0,0 +1,32 @@
./: {bitshift/ tests/} doc{README.md} legal{LICENSE} manifest
# Don't install tests.
#
tests/: install = false
define doxyfile: file
doxyfile{*}: extension = ""
doxyfile{*}: in.substitution = lax
doxyfile{Doxyfile}: in{Doxyfile} $src_root/manifest
{
PROJECT_BRIEF = "BitShift Validate API Reference Documentation"
OUTPUT_DIRECTORY = "$out_root/docs"
INTERNAL = "NO"
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_GAMMA = 140
}
./: fsdir{$out_root/docs}
fsdir{$out_root/docs}:
% clean
{{
rm -rf $out_root/docs
}}
alias{docs}: doxyfile{Doxyfile} fsdir{$out_root/docs}
{{
diag doxygen $path($<[0])
doxygen $path($<[0])
}}
+2
View File
@@ -0,0 +1,2 @@
@page bitshift-license BitShift License
@includedoc LICENSE
+27
View File
@@ -0,0 +1,27 @@
@mainpage BitShift Validate
# Introduction
Welcome to the BitShift Validate API Reference documentation.
Purpose
-------
This documentation aims to provide reference material for the BitShift Validate
unit testing library.
See the @ref readme for a quick introduction on how to import and use BitShift
Validate in your application.
License
-------
This documentation is released under the CC BY-NC-SA 4.0 license. See
[creativecommons.org][cc-by-nc-sa] for license information and the legal
details.
See @ref bitshift-license for source code license information.
[made-with-love-badge]: https://img.shields.io/badge/Made_with_%E2%9D%A4%EF%B8%8F_by-The%20BitShift%20Contributors-red
[README]: README.md
[cc-by-nc-sa]: https://creativecommons.org/licenses/by-nc-sa/4.0/
+2
View File
@@ -0,0 +1,2 @@
@page readme README
@includedoc README.md
+12
View File
@@ -0,0 +1,12 @@
: 1
name: bitshift-validate
version: 0.1.0-a.0.z
type: lib
language: c++
summary: bitshift-validate C++ unit-testing library
license: BSD-2-Clause
description-file: README.md
url: https://bitshift.helloryan.se/validate/
email: ryan@helloryan.se
depends: * build2 >= 0.18.0
depends: * bpkg >= 0.18.0
+2
View File
@@ -0,0 +1,2 @@
: 1
summary: bitshift-validate project repository
+8
View File
@@ -0,0 +1,8 @@
# Test executables.
#
driver
# Testscript output directories (can be symlinks).
#
test
test-*
+4
View File
@@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/
+6
View File
@@ -0,0 +1,6 @@
project = # Unnamed tests subproject.
using version
using config
using test
using dist
+16
View File
@@ -0,0 +1,16 @@
cxx.std = latest
using cxx
hxx{*}: extension = hxx
ixx{*}: extension = ixx
txx{*}: extension = txx
cxx{*}: extension = cxx
# Every exe{} in this subproject is by default a test.
#
exe{*}: test = true
# The test target for cross-testing (running tests under Wine, etc).
#
test.target = $cxx.target
+1
View File
@@ -0,0 +1 @@
./: {*/ -build/}
+4
View File
@@ -0,0 +1,4 @@
libs =
import libs = bitshift-validate%lib{validate}
./: exe{driver}: {hxx ixx txx cxx}{**} $libs
+21
View File
@@ -0,0 +1,21 @@
#include <bitshift/validate/validate.hxx>
BV_TEST(sanity, "basic sanity check")
{
BV_ASSERT_TRUE(true);
BV_ASSERT_FALSE(false);
BV_ASSERT_NULLPTR(nullptr);
BV_ASSERT_EQUAL(1, 1);
BV_ASSERT_NOT_EQUAL(0, 1);
}
BV_TEST(ignored, "ignored test")
{
BV_IGNORE;
}
int
main(int argc, char* argv[])
{
return bitshift::validate::main(argc, argv);
}