Hello libcode-validation

This commit is contained in:
G.H.O.S.T 2024-12-24 21:17:24 +01:00
commit 2601654936
Signed by: G.H.O.S.T
GPG Key ID: 3BD93EABD1407B82
47 changed files with 1684 additions and 0 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
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
[*.yaml]
indent_size = 2

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

View File

@ -0,0 +1,20 @@
name: on-push
on: [push]
jobs:
build-and-test:
runs-on: linux
container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest
volumes:
- /build
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Initialize
run: |
bpkg create -d /build cc config.cc.coptions="-Wall -Werror"
bdep init -A /build
- name: Build
run: b
- name: Test
run: b test

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
.bdep/
# Local default options files.
#
.build2/local/
# Compiler/linker output.
#
*.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

31
LICENSE Normal file
View File

@ -0,0 +1,31 @@
Copyright © 2024 Ryan. All rights reserved.
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.
3. All advertising materials mentioning features or use of this software must
display the following acknowledgement:
This product includes software developed by Ryan, http://helloryan.se/.
4. Neither the name(s) of the copyright holder(s) nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "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 COPYRIGHT HOLDER 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.

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# libcode-validation
![Build status](https://code.helloryan.se/code/libcode-validation/actions/workflows/on-push.yaml/badge.svg)
## Requirements
None, other than a modern C++-compiler.
## Building
See the wiki, https://code.helloryan.se/code/wiki/wiki/Build-Instructions, for
build instructions.
## Contact
Please report bugs and issues by sending an e-mail to: ryan@helloryan.se.
## Contributing
Please send an e-mail to ryan@helloryan.se to request an account and
write-access to the libcode-validation repository.

4
build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

7
build/bootstrap.build Normal file
View File

@ -0,0 +1,7 @@
project = libcode-validation
using version
using config
using test
using install
using dist

6
build/export.build Normal file
View File

@ -0,0 +1,6 @@
$out_root/
{
include code/validation/
}
export $out_root/code/validation/$import.target

16
build/root.build Normal file
View File

@ -0,0 +1,16 @@
# Uncomment to suppress warnings coming from external libraries.
#
#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

5
buildfile Normal file
View File

@ -0,0 +1,5 @@
./: {code/ tests/} doc{README.md} legal{LICENSE} manifest
# Don't install tests.
#
tests/: install = false

9
code/validation/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

View File

@ -0,0 +1,51 @@
#ifndef code__validation__assert_hxx_
#define code__validation__assert_hxx_
#include <code/validation/except.hxx>
#include <code/validation/traits.hxx>
#include <cstdint>
#include <source_location>
#include <sstream>
#include <string>
namespace code::validation::Assert
{
template<typename T>
void
assert_true(T const&,
std::string,
std::source_location const& = std::source_location::current());
template<typename T>
void
assert_false(T const&,
std::string,
std::source_location const& = std::source_location::current());
template<typename T>
void
assert_null(T const&,
std::string,
std::source_location const& = std::source_location::current());
template<typename T>
void
assert_not_null(T const&,
std::string,
std::source_location const& = std::source_location::current());
template<typename T1, typename T2 = T1>
void
assert_equal(T1 const&,
T2 const&,
std::string,
std::string,
std::source_location const& = std::source_location::current());
} // namespace code::validation::Assert
#include <code/validation/assert.txx>
#endif

210
code/validation/assert.txx Normal file
View File

@ -0,0 +1,210 @@
namespace code::validation::Assert
{
namespace
{
class Unary_expression_failure
{
public:
Unary_expression_failure(std::string message,
std::string file,
std::uint32_t line,
std::string expr,
std::string val)
: message_{std::move(message)},
file_{std::move(file)},
line_{line},
expr_{std::move(expr)},
val_{std::move(val)}
{}
void
print(std::ostream& o) const
{
o << " " << message_ << '\n';
o << " source: " << file_ << ':' << line_ << ":\n";
o << " expression: " << expr_ << '\n';
o << " value : " << val_ << '\n';
}
private:
std::string message_;
std::string file_;
std::uint32_t line_;
std::string expr_;
std::string val_;
};
class Binary_expression_failure
{
public:
Binary_expression_failure(std::string message,
std::string file,
std::uint32_t line,
std::string lhs_expr,
std::string lhs_val,
std::string rhs_expr,
std::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)}
{}
void
print(std::ostream& o) const
{
o << " " << message_ << '\n';
o << " source: " << file_ << ':' << line_ << ":\n";
o << " left-hand expression : " << lhs_expr_ << '\n';
o << " left-hand value : " << lhs_val_ << '\n';
o << " right-hand expression: " << rhs_expr_ << '\n';
o << " right-hand value : " << rhs_val_ << '\n';
}
private:
std::string message_;
std::string file_;
std::uint32_t line_;
std::string lhs_expr_;
std::string lhs_val_;
std::string rhs_expr_;
std::string rhs_val_;
};
} // namespace
template<typename T>
void
assert_true(T const& value,
std::string expr,
std::source_location const& source)
{
if (value == true) {
return;
}
std::string expr_str;
if constexpr (has_output_operator<T>) {
std::stringstream str;
str << value;
expr_str = str.str();
}
throw Fail{
Unary_expression_failure{
"expected expression to be true; was false",
source.file_name(),
source.line(),
std::move(expr),
std::move(expr_str)
}
};
}
template<typename T>
void
assert_false(T const& value,
std::string expr,
std::source_location const& source)
{
if (value == false) {
return;
}
std::string expr_str;
if constexpr (has_output_operator<T>) {
std::stringstream str;
str << value;
expr_str = str.str();
}
throw Fail{
Unary_expression_failure{
"expected expression to be false; was true",
source.file_name(),
source.line(),
std::move(expr),
std::move(expr_str)
}
};
}
template<typename T>
void
assert_null(T const& value,
std::string expr,
std::source_location const& source)
{
if (value == nullptr) {
return;
}
std::string expr_str;
if constexpr (has_output_operator<T>) {
std::stringstream str;
str << value;
expr_str = str.str();
}
throw Fail{
Unary_expression_failure{
"expected expression to be nullptr; was not",
source.file_name(),
source.line(),
std::move(expr),
std::move(expr_str)
}
};
}
template<typename T1, typename T2>
void
assert_equal(T1 const& lhs,
T2 const& rhs,
std::string lhs_expr,
std::string rhs_expr,
std::source_location const& source)
{
if (lhs == rhs) {
return;
}
std::string lhs_val;
std::string rhs_val;
if constexpr (has_output_operator<T1>) {
std::stringstream str;
str << lhs;
lhs_val = str.str();
}
if constexpr (has_output_operator<T2>) {
std::stringstream str;
str << rhs;
rhs_val = str.str();
}
throw Fail{
Binary_expression_failure{
"expected lhs to equal rhs; they did not",
source.file_name(),
source.line(),
std::move(lhs_expr),
std::move(lhs_val),
std::move(rhs_expr),
std::move(rhs_val)
}
};
}
} // namespace code::validation::Assert

62
code/validation/buildfile Normal file
View File

@ -0,0 +1,62 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
./: lib{code-validation}: libul{code-validation}
libul{code-validation}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version}
libul{code-validation}: $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{code-validation}: bin.whole = false
}
hxx{version}: in{version} $src_root/manifest
{
dist = true
clean = ($src_root != $out_root)
}
# Build options.
#
cxx.poptions =+ "-I$out_root" "-I$src_root"
# Export options.
#
lib{code-validation}:
{
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{code-validation}: bin.lib.version = "-$version.project_id"
else
lib{code-validation}: bin.lib.version = "-$version.major.$version.minor"
# Install into the code/validation/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/code/validation/
install.subdirs = true
}

View File

@ -0,0 +1,29 @@
#ifndef code__validation__except_hxx_
#define code__validation__except_hxx_
#include <code/validation/test-fail-extras.hxx>
namespace code::validation
{
class Fail
{
public:
Fail(Test_fail_extras extras)
: extras_{std::move(extras)}
{}
Test_fail_extras const&
extras() const
{
return extras_;
}
private:
Test_fail_extras extras_;
};
} // namespace code::validation
#endif

118
code/validation/exec.cxx Normal file
View File

@ -0,0 +1,118 @@
#include <code/validation/exec.hxx>
#include <code/validation/except.hxx>
#include <code/validation/setup.hxx>
#include <code/validation/teardown.hxx>
#include <code/validation/test-case.hxx>
#include <code/validation/test-result.hxx>
#include <code/validation/utility.hxx>
namespace code::validation
{
static
void
exec_setup()
{
auto current = Setup::first();
if (!current) {
return;
}
do {
// fixme: report setup exceptions. fail entire unit test on exception.
//
current->run();
current = current->next();
} while (current && current != Setup::first());
}
static
void
exec_teardown()
{
auto current = Teardown::first();
if (!current) {
return;
}
do {
// fixme: report teardown exceptions. fail entire unit test on exception.
//
current->run();
current = current->next();
} while (current && current != Teardown::first());
}
static
bool
exec_test_case(Test_summary& summary, Test_case& test_case)
{
try {
Utility::Current<Test_case> current{&test_case};
exec_setup();
try {
test_case.run();
}
catch (...) {
exec_teardown();
throw;
}
exec_teardown();
summary.append({
test_case.description(),
test_case.file(),
test_case.line(),
"passed"
});
summary.inc_pass();
return true;
}
catch (Fail const& fail) {
summary.append({
test_case.description(),
test_case.file(),
test_case.line(),
"failed",
fail.extras()
});
summary.inc_fail();
}
return false;
}
bool
exec(Test_summary& summary)
{
auto current = &Test_case::first();
if (!current) {
return true;
}
bool all_passed{true};
do {
bool passed = exec_test_case(summary, *current);
if (!passed) {
all_passed = false;
}
current = &current->next();
} while (current != &Test_case::first());
return all_passed;
}
} // namespace code::validation

16
code/validation/exec.hxx Normal file
View File

@ -0,0 +1,16 @@
#ifndef code__validation__exec_hxx_
#define code__validation__exec_hxx_
#include <code/validation/test-summary.hxx>
#include <iostream>
namespace code::validation
{
bool
exec(Test_summary& summary);
} // namespace code::validation
#endif

View File

@ -0,0 +1,54 @@
#ifndef code__validation__macros_hxx_
#define code__validation__macros_hxx_
#include <code/validation/assert.hxx>
#include <code/validation/setup.hxx>
#include <code/validation/teardown.hxx>
#include <code/validation/test-case.hxx>
#define VALIDATION_TEST_SETUP_2(file, line) \
static void test_setup_func_##line(); \
code::validation::Setup test_setup_##line(file, line, &test_setup_func_##line); \
void test_setup_func_##line()
#define VALIDATION_TEST_SETUP_1(file, line) \
VALIDATION_TEST_SETUP_2(file, line)
#define VALIDATION_TEST_SETUP \
VALIDATION_TEST_SETUP_1(__FILE__, __LINE__)
#define VALIDATION_TEST_TEARDOWN_2(file, line) \
static void test_teardown_func_##line(); \
code::validation::Teardown test_teardown_##line(file, line, &test_teardown_func_##line); \
void test_teardown_func_##line()
#define VALIDATION_TEST_TEARDOWN_1(file, line) \
VALIDATION_TEST_TEARDOWN_2(file, line)
#define VALIDATION_TEST_TEARDOWN \
VALIDATION_TEST_TEARDOWN_1(__FILE__, __LINE__)
#define VALIDATION_TEST_2(name, file, line) \
void test_func_##line(); \
code::validation::Test_case test_##line(#name, (file), (line), &test_func_##line); \
void test_func_##line()
#define VALIDATION_TEST_1(name, file, line) \
VALIDATION_TEST_2(name, file, line)
#define VALIDATION_TEST(name) \
VALIDATION_TEST_1(name, __FILE__, __LINE__)
#define VALIDATION_ASSERT_TRUE(expr) \
::code::validation::Assert::assert_true((expr), #expr)
#define VALIDATION_ASSERT_FALSE(expr) \
::code::validation::Assert::assert_false((expr), #expr)
#define VALIDATION_ASSERT_NULL(expr) \
::code::validation::Assert::assert_null((expr), #expr)
#define VALIDATION_ASSERT_EQUAL(lhs, rhs) \
::code::validation::Assert::assert_equal((lhs), (rhs), #lhs, #rhs)
#endif

43
code/validation/main.cxx Normal file
View File

@ -0,0 +1,43 @@
#include <code/validation/main.hxx>
#include <code/validation/exec.hxx>
#include <code/validation/test-summary.hxx>
#include <cstring>
namespace code::validation
{
int
main(int argc, char* argv[])
{
int verbosity{0};
bool print_stats{false};
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--verbose") == 0) {
++verbosity;
}
else if (strcmp(argv[i], "-v") == 0) {
++verbosity;
}
else if (strcmp(argv[i], "--stats") == 0) {
print_stats = true;
}
}
Test_summary summary;
bool success = exec(summary);
bool print_summary = verbosity > 0;
bool print_extras = verbosity > 1;
if (print_summary) {
summary.print(std::cout, print_extras, print_stats);
}
return success ? 0 : -1;
}
} // namespace code::validation

15
code/validation/main.hxx Normal file
View File

@ -0,0 +1,15 @@
#ifndef code__validation__main_hxx_
#define code__validation__main_hxx_
#include <code/validation/macros.hxx>
#include <code/validation/assert.hxx>
namespace code::validation
{
int
main(int argc, char* argv[]);
} // namespace code::validation
#endif

60
code/validation/setup.cxx Normal file
View File

@ -0,0 +1,60 @@
#include <code/validation/setup.hxx>
namespace code::validation
{
Setup::
Setup(std::string file, int line, std::function<void()> function)
: file_{std::move(file)}, line_{line}, function_{function}
{}
std::string const&
Setup::
file() const
{
return file_;
}
int
Setup::
line() const
{
return line_;
}
Setup*
Setup::
next()
{
return Utility::Intrusive_list<Setup>::next();
}
Setup*
Setup::
previous()
{
return Utility::Intrusive_list<Setup>::previous();
}
void
Setup::
run()
{
function_();
}
Setup*
Setup::
first()
{
return Utility::Intrusive_list<Setup>::first();
}
Setup*
Setup::
last()
{
return Utility::Intrusive_list<Setup>::last();
}
} // namespace code::validation

54
code/validation/setup.hxx Normal file
View File

@ -0,0 +1,54 @@
#ifndef code__validation__setup_hxx_
#define code__validation__setup_hxx_
#include <code/validation/utility.hxx>
#include <functional>
#include <source_location>
#include <string>
namespace code::validation
{
class Setup
: Utility::Intrusive_list<Setup>
{
friend Utility::Intrusive_list<Setup>;
public:
Setup(std::string, int, std::function<void()> function);
std::string const&
file() const;
int
line() const;
Setup*
next();
Setup*
previous();
void
run();
static
Setup*
first();
static
Setup*
last();
private:
std::string file_;
int line_;
std::function<void()> function_;
};
} // namespace code::validation
#endif

View File

@ -0,0 +1,60 @@
#include <code/validation/teardown.hxx>
namespace code::validation
{
Teardown::
Teardown(std::string file, int line, std::function<void()> function)
: file_{std::move(file)}, line_{line}, function_{function}
{}
std::string const&
Teardown::
file() const
{
return file_;
}
int
Teardown::
line() const
{
return line_;
}
Teardown*
Teardown::
next()
{
return Utility::Intrusive_list<Teardown>::next();
}
Teardown*
Teardown::
previous()
{
return Utility::Intrusive_list<Teardown>::previous();
}
void
Teardown::
run()
{
function_();
}
Teardown*
Teardown::
first()
{
return Utility::Intrusive_list<Teardown>::first();
}
Teardown*
Teardown::
last()
{
return Utility::Intrusive_list<Teardown>::last();
}
} // namespace code::validation

View File

@ -0,0 +1,54 @@
#ifndef code__validation__teardown_hxx_
#define code__validation__teardown_hxx_
#include <code/validation/utility.hxx>
#include <functional>
#include <source_location>
#include <string>
namespace code::validation
{
class Teardown
: Utility::Intrusive_list<Teardown>
{
friend Utility::Intrusive_list<Teardown>;
public:
Teardown(std::string, int, std::function<void()> function);
std::string const&
file() const;
int
line() const;
Teardown*
next();
Teardown*
previous();
void
run();
static
Teardown*
first();
static
Teardown*
last();
private:
std::string file_;
int line_;
std::function<void()> function_;
};
} // namespace code::validation
#endif

View File

@ -0,0 +1,73 @@
#include <code/validation/test-case.hxx>
namespace code::validation
{
Test_case::
Test_case(std::string description,
std::string file,
int line,
std::function<void()> function)
: description_{std::move(description)},
file_{std::move(file)},
line_{line},
function_{function}
{}
std::string const&
Test_case::
description() const
{
return description_;
}
std::string const&
Test_case::
file() const
{
return file_;
}
int
Test_case::
line() const
{
return line_;
}
Test_case&
Test_case::
next()
{
return *Utility::Intrusive_list<Test_case>::next();
}
Test_case&
Test_case::
previous()
{
return *Utility::Intrusive_list<Test_case>::previous();
}
void
Test_case::
run()
{
function_();
}
Test_case&
Test_case::
first()
{
return *Utility::Intrusive_list<Test_case>::first();
}
Test_case&
Test_case::
last()
{
return *Utility::Intrusive_list<Test_case>::last();
}
} // namespace code::validation

View File

@ -0,0 +1,57 @@
#ifndef code__validation__test_case_hxx_
#define code__validation__test_case_hxx_
#include <code/validation/utility.hxx>
#include <functional>
#include <source_location>
#include <string>
namespace code::validation
{
class Test_case
: Utility::Intrusive_list<Test_case>
{
friend Utility::Intrusive_list<Test_case>;
public:
Test_case(std::string, std::string, int, std::function<void()> function);
std::string const&
description() const;
std::string const&
file() const;
int
line() const;
Test_case&
next();
Test_case&
previous();
void
run();
static
Test_case&
first();
static
Test_case&
last();
private:
std::string description_;
std::string file_;
int line_;
std::function<void()> function_;
};
} // namespace code::validation
#endif

View File

@ -0,0 +1,13 @@
#include <code/validation/test-result.hxx>
namespace code::validation
{
void
Test_fail_extras::
print(std::ostream& o) const
{
extras_->do_print(o);
}
}

View File

@ -0,0 +1,58 @@
#ifndef code__validation__test_fail_extras_hxx_
#define code__validation__test_fail_extras_hxx_
#include <cstdint>
#include <memory>
namespace code::validation
{
class Test_fail_extras
{
public:
template<typename T>
Test_fail_extras(T extras)
: extras_{std::make_shared<Wrapper<T>>(std::move(extras))}
{}
void
print(std::ostream&) const;
private:
struct Abstraction
{
virtual
~Abstraction() noexcept = default;
virtual
void
do_print(std::ostream&) const = 0;
};
template<typename T>
struct Wrapper
: Abstraction
{
template<typename... Args>
Wrapper(Args&&... args)
: extras{std::forward<Args>(args)...}
{}
void
do_print(std::ostream& o) const override
{
extras.print(o);
}
T extras;
};
std::shared_ptr<Abstraction const> extras_;
};
} // namespace code::validation
#endif

View File

@ -0,0 +1,69 @@
#include <code/validation/test-result.hxx>
namespace code::validation
{
Test_result::
Test_result(std::string description,
std::string file,
int line,
std::string message)
: description_{std::move(description)},
file_{std::move(file)},
line_{line_},
message_{std::move(message)}
{}
Test_result::
Test_result(std::string description,
std::string file,
int line,
std::string message,
Test_fail_extras extras)
: description_{std::move(description)},
file_{std::move(file)},
line_{line_},
message_{std::move(message)},
extras_{extras}
{}
std::string const&
Test_result::
description() const
{
return description_;
}
std::string const&
Test_result::
file() const
{
return file_;
}
int
Test_result::
line() const
{
return line_;
}
std::string const&
Test_result::
message() const
{
return message_;
}
void
Test_result::
print(std::ostream& o, bool print_extras) const
{
o << "test: " << description() << ": " << message() << '\n';
if (print_extras && extras_) {
extras_->print(o);
}
}
} // namespace code::validation

View File

@ -0,0 +1,45 @@
#ifndef code__validation__test_result_hxx_
#define code__validation__test_result_hxx_
#include <code/validation/test-fail-extras.hxx>
#include <optional>
#include <string>
namespace code::validation
{
class Test_result
{
public:
Test_result(std::string, std::string, int, std::string);
Test_result(std::string, std::string, int, std::string, Test_fail_extras);
std::string const&
description() const;
std::string const&
file() const;
int
line() const;
std::string const&
message() const;
void
print(std::ostream&, bool) const;
private:
std::string description_;
std::string file_;
int line_;
std::string message_;
std::optional<Test_fail_extras> extras_;
};
} // namespace code::validation
#endif

View File

@ -0,0 +1,48 @@
#include <code/validation/test-summary.hxx>
#include <iostream>
namespace code::validation
{
void
Test_summary::
append(Test_result result)
{
results_.emplace_back(std::move(result));
}
void
Test_summary::
inc_pass()
{
++pass_;
}
void
Test_summary::
inc_fail()
{
++fail_;
}
void
Test_summary::
print(std::ostream& o, bool print_extras, bool stats) const
{
for (auto const& j : results_) {
j.print(o, print_extras);
}
if (stats) {
auto total = pass_ + fail_;
o << "test statistics:\n"
<< "tests : " << total << '\n'
<< "tests passed: " << pass_ << '\n'
<< "tests failed: " << fail_ << '\n'
;
}
}
} // namespace code::validation

View File

@ -0,0 +1,38 @@
#ifndef code__validation__test_summary_hxx_
#define code__validation__test_summary_hxx_
#include <code/validation/test-result.hxx>
#include <cstdint>
#include <vector>
namespace code::validation
{
class Test_summary
{
public:
Test_summary() = default;
void
append(Test_result);
void
inc_pass();
void
inc_fail();
void
print(std::ostream&, bool, bool) const;
private:
std::vector<Test_result> results_;
int pass_{};
int fail_{};
};
} // namespace code::validation
#endif

View File

@ -0,0 +1,23 @@
#ifndef code__validation__traits_hxx_
#define code__validation__traits_hxx_
#include <concepts>
#include <iostream>
namespace code::validation
{
template<typename T>
concept has_output_operator = requires(std::ostream& o, T const& value)
{
// check if T can be written to an std::ostream.
//
{
o << value
};
};
} // namespace code::validation
#endif

138
code/validation/utility.hxx Normal file
View File

@ -0,0 +1,138 @@
#ifndef code__validation__utility_hxx_
#define code__validation__utility_hxx_
#include <cstddef>
namespace code::validation::Utility
{
template<typename T>
class Intrusive_list
{
protected:
Intrusive_list()
{
if (!first_ || !last_) {
first_ = this;
last_ = this;
}
first_->prev_ = this;
last_->next_ = this;
prev_ = last_;
next_ = first_;
last_ = this;
++counter_;
}
Intrusive_list(Intrusive_list const&) = delete;
Intrusive_list(Intrusive_list&&) = delete;
~Intrusive_list() noexcept
{
if (first_ == this) {
first_ = next_;
}
if (last_ == this) {
last_ = prev_;
}
prev_->next_ = next_;
next_->prev_ = prev_;
--counter_;
}
Intrusive_list& operator=(Intrusive_list const&) = delete;
Intrusive_list& operator=(Intrusive_list&&) = delete;
T*
previous()
{
return static_cast<T*>(prev_);
}
T*
next()
{
return static_cast<T*>(next_);
}
static
T*
first()
{
return static_cast<T*>(first_);
}
static
T*
last()
{
return static_cast<T*>(last_);
}
static
std::size_t
count()
{
return counter_;
}
private:
Intrusive_list<T>* prev_{nullptr};
Intrusive_list<T>* next_{nullptr};
static Intrusive_list<T>* first_;
static Intrusive_list<T>* last_;
static std::size_t counter_;
};
template<typename T>
Intrusive_list<T>* Intrusive_list<T>::first_{nullptr};
template<typename T>
Intrusive_list<T>* Intrusive_list<T>::last_{nullptr};
template<typename T>
std::size_t Intrusive_list<T>::counter_{0};
template<typename T>
class Current
{
public:
explicit
Current(T* current)
{
current_ = current;
}
~Current() noexcept
{
current_ = nullptr;
}
static
T*
get()
{
return current_;
}
private:
static T* current_;
};
template<typename T>
T* Current<T>::current_{nullptr};
} // namespace code::validation::Utility
#endif

View File

@ -0,0 +1,8 @@
#ifndef code__validation__validation_hxx_
#define code__validation__validation_hxx_
#include <code/validation/exec.hxx>
#include <code/validation/macros.hxx>
#include <code/validation/main.hxx>
#endif

View File

@ -0,0 +1,37 @@
#ifndef code__validation___version_hxx_
#define code__validation___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 LIBCODE_VALIDATION_VERSION $libcode_validation.version.project_number$ULL
#define LIBCODE_VALIDATION_VERSION_STR "$libcode_validation.version.project$"
#define LIBCODE_VALIDATION_VERSION_ID "$libcode_validation.version.project_id$"
#define LIBCODE_VALIDATION_VERSION_FULL "$libcode_validation.version$"
#define LIBCODE_VALIDATION_VERSION_MAJOR $libcode_validation.version.major$
#define LIBCODE_VALIDATION_VERSION_MINOR $libcode_validation.version.minor$
#define LIBCODE_VALIDATION_VERSION_PATCH $libcode_validation.version.patch$
#define LIBCODE_VALIDATION_PRE_RELEASE $libcode_validation.version.pre_release$
#define LIBCODE_VALIDATION_SNAPSHOT_SN $libcode_validation.version.snapshot_sn$ULL
#define LIBCODE_VALIDATION_SNAPSHOT_ID "$libcode_validation.version.snapshot_id$"
#endif

11
manifest Normal file
View File

@ -0,0 +1,11 @@
: 1
name: libcode-validation
version: 0.1.0-a.0.z
language: c++
summary: libcode-validation C++ library
license: BSD-4-Clause
description-file: README.md
url: https://helloryan.se/code/
email: ryan@helloryan.se
depends: * build2 >= 0.17.0
depends: * bpkg >= 0.17.0

2
repositories.manifest Normal file
View File

@ -0,0 +1,2 @@
: 1
summary: libcode-validation project repository

8
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Test executables.
#
driver
# Testscript output directories (can be symlinks).
#
test
test-*

1
tests/basics/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
basics

31
tests/basics/basics.cxx Normal file
View File

@ -0,0 +1,31 @@
#include <code/validation/main.hxx>
VALIDATION_TEST_SETUP
{
}
VALIDATION_TEST_TEARDOWN
{
}
VALIDATION_TEST(test_true)
{
VALIDATION_ASSERT_TRUE(true == true);
}
VALIDATION_TEST(test_false)
{
VALIDATION_ASSERT_FALSE(true == false);
}
VALIDATION_TEST(test_null)
{
int* ptr{nullptr};
VALIDATION_ASSERT_NULL(ptr);
}
int
main(int argc, char* argv[])
{
return code::validation::main(argc, argv);
}

4
tests/basics/buildfile Normal file
View File

@ -0,0 +1,4 @@
import libs = libcode-validation%lib{code-validation}
exe{basics}: {hxx ixx txx cxx}{**} $libs testscript{**}
exe{basics}: test.options = -v -v

4
tests/build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

View File

@ -0,0 +1,5 @@
project = # Unnamed tests subproject.
using config
using test
using dist

16
tests/build/root.build Normal file
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
tests/buildfile Normal file
View File

@ -0,0 +1 @@
./: {*/ -build/}