Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
7a77a40e
Commit
7a77a40e
authored
Jul 06, 2023
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add initial version for the new testing framework
parent
ff9b5667
Changes
51
Hide whitespace changes
Inline
Side-by-side
Showing
51 changed files
with
2902 additions
and
24 deletions
+2902
-24
.clang-format
.clang-format
+2
-1
CMakeLists.txt
CMakeLists.txt
+16
-22
libcaf_core/caf/detail/format.hpp
libcaf_core/caf/detail/format.hpp
+30
-0
libcaf_core/caf/detail/source_location.hpp
libcaf_core/caf/detail/source_location.hpp
+47
-0
libcaf_test/CMakeLists.txt
libcaf_test/CMakeLists.txt
+42
-0
libcaf_test/caf/test/and_given.hpp
libcaf_test/caf/test/and_given.hpp
+27
-0
libcaf_test/caf/test/and_then.hpp
libcaf_test/caf/test/and_then.hpp
+21
-0
libcaf_test/caf/test/and_when.hpp
libcaf_test/caf/test/and_when.hpp
+27
-0
libcaf_test/caf/test/binary_predicate.hpp
libcaf_test/caf/test/binary_predicate.hpp
+67
-0
libcaf_test/caf/test/block.hpp
libcaf_test/caf/test/block.hpp
+112
-0
libcaf_test/caf/test/block_type.hpp
libcaf_test/caf/test/block_type.hpp
+117
-0
libcaf_test/caf/test/caf_test_main.hpp
libcaf_test/caf/test/caf_test_main.hpp
+18
-0
libcaf_test/caf/test/context.hpp
libcaf_test/caf/test/context.hpp
+84
-0
libcaf_test/caf/test/factory.hpp
libcaf_test/caf/test/factory.hpp
+43
-0
libcaf_test/caf/test/fwd.hpp
libcaf_test/caf/test/fwd.hpp
+37
-0
libcaf_test/caf/test/given.hpp
libcaf_test/caf/test/given.hpp
+27
-0
libcaf_test/caf/test/nesting_error.hpp
libcaf_test/caf/test/nesting_error.hpp
+90
-0
libcaf_test/caf/test/registry.hpp
libcaf_test/caf/test/registry.hpp
+58
-0
libcaf_test/caf/test/reporter.hpp
libcaf_test/caf/test/reporter.hpp
+98
-0
libcaf_test/caf/test/runnable.hpp
libcaf_test/caf/test/runnable.hpp
+122
-0
libcaf_test/caf/test/runner.hpp
libcaf_test/caf/test/runner.hpp
+32
-0
libcaf_test/caf/test/scenario.hpp
libcaf_test/caf/test/scenario.hpp
+97
-0
libcaf_test/caf/test/scope.hpp
libcaf_test/caf/test/scope.hpp
+52
-0
libcaf_test/caf/test/section.hpp
libcaf_test/caf/test/section.hpp
+23
-0
libcaf_test/caf/test/step_type.hpp
libcaf_test/caf/test/step_type.hpp
+2
-0
libcaf_test/caf/test/suite.hpp
libcaf_test/caf/test/suite.hpp
+32
-0
libcaf_test/caf/test/test.hpp
libcaf_test/caf/test/test.hpp
+54
-0
libcaf_test/caf/test/then.hpp
libcaf_test/caf/test/then.hpp
+24
-0
libcaf_test/caf/test/when.hpp
libcaf_test/caf/test/when.hpp
+29
-0
libcaf_test/src/test/and_given.cpp
libcaf_test/src/test/and_given.cpp
+42
-0
libcaf_test/src/test/and_then.cpp
libcaf_test/src/test/and_then.cpp
+26
-0
libcaf_test/src/test/and_when.cpp
libcaf_test/src/test/and_when.cpp
+50
-0
libcaf_test/src/test/block.cpp
libcaf_test/src/test/block.cpp
+85
-0
libcaf_test/src/test/context.cpp
libcaf_test/src/test/context.cpp
+43
-0
libcaf_test/src/test/factory.cpp
libcaf_test/src/test/factory.cpp
+13
-0
libcaf_test/src/test/given.cpp
libcaf_test/src/test/given.cpp
+42
-0
libcaf_test/src/test/nesting_error.cpp
libcaf_test/src/test/nesting_error.cpp
+42
-0
libcaf_test/src/test/registry.cpp
libcaf_test/src/test/registry.cpp
+29
-0
libcaf_test/src/test/reporter.cpp
libcaf_test/src/test/reporter.cpp
+450
-0
libcaf_test/src/test/runnable.cpp
libcaf_test/src/test/runnable.cpp
+56
-0
libcaf_test/src/test/runner.cpp
libcaf_test/src/test/runner.cpp
+148
-0
libcaf_test/src/test/scenario.cpp
libcaf_test/src/test/scenario.cpp
+48
-0
libcaf_test/src/test/scope.cpp
libcaf_test/src/test/scope.cpp
+31
-0
libcaf_test/src/test/section.cpp
libcaf_test/src/test/section.cpp
+29
-0
libcaf_test/src/test/test.cpp
libcaf_test/src/test/test.cpp
+30
-0
libcaf_test/src/test/then.cpp
libcaf_test/src/test/then.cpp
+24
-0
libcaf_test/src/test/when.cpp
libcaf_test/src/test/when.cpp
+48
-0
libcaf_test/test/test-test.cpp
libcaf_test/test/test-test.cpp
+3
-1
libcaf_test/test/test/block_type.cpp
libcaf_test/test/test/block_type.cpp
+31
-0
libcaf_test/test/test/scenario.cpp
libcaf_test/test/test/scenario.cpp
+142
-0
libcaf_test/test/test/test.cpp
libcaf_test/test/test/test.cpp
+60
-0
No files found.
.clang-format
View file @
7a77a40e
...
...
@@ -30,12 +30,13 @@ IndentCaseLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks:
fals
e
KeepEmptyLinesAtTheStartOfBlocks:
tru
e
Language: Cpp
MacroBlockBegin: "^BEGIN_STATE$|CAF_BEGIN_TYPE_ID_BLOCK"
MacroBlockEnd: "^END_STATE$|CAF_END_TYPE_ID_BLOCK"
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
NamespaceMacros: ["SUITE", "WITH_FIXTURE"]
PenaltyBreakAssignment: 25
PenaltyBreakBeforeFirstCallParameter: 50
PenaltyReturnTypeOnItsOwnLine: 25
...
...
CMakeLists.txt
View file @
7a77a40e
...
...
@@ -207,25 +207,9 @@ else()
CACHE INTERNAL
"The version string used for shared library objects"
)
endif
()
# --
install testing DSL headers -----------------------------
------------------
# --
create the libcaf_test target ahead of time for caf_core
------------------
add_library
(
libcaf_test INTERFACE
)
set_target_properties
(
libcaf_test PROPERTIES EXPORT_NAME test
)
target_include_directories
(
libcaf_test INTERFACE
$<BUILD_INTERFACE:
${
PROJECT_SOURCE_DIR
}
/libcaf_test>
)
add_library
(
CAF::test ALIAS libcaf_test
)
install
(
DIRECTORY libcaf_test/caf/ DESTINATION
${
CMAKE_INSTALL_INCLUDEDIR
}
/caf
FILES_MATCHING PATTERN
"*.hpp"
)
install
(
TARGETS libcaf_test
EXPORT CAFTargets
ARCHIVE DESTINATION
${
CMAKE_INSTALL_LIBDIR
}
COMPONENT test
RUNTIME DESTINATION
${
CMAKE_INSTALL_BINDIR
}
COMPONENT test
LIBRARY DESTINATION
${
CMAKE_INSTALL_LIBDIR
}
COMPONENT test
)
add_library
(
libcaf_test
)
# -- add uninstall target if it does not exist yet -----------------------------
...
...
@@ -356,16 +340,24 @@ function(caf_add_component name)
set_property
(
TARGET
${
obj_lib_target
}
PROPERTY POSITION_INDEPENDENT_CODE ON
)
caf_target_link_libraries
(
${
obj_lib_target
}
${
CAF_ADD_COMPONENT_DEPENDENCIES
}
)
add_library
(
${
pub_lib_target
}
"
${
PROJECT_SOURCE_DIR
}
/cmake/dummy.cpp"
$<TARGET_OBJECTS:
${
obj_lib_target
}
>
)
if
(
NOT TARGET
${
pub_lib_target
}
)
add_library
(
${
pub_lib_target
}
"
${
PROJECT_SOURCE_DIR
}
/cmake/dummy.cpp"
$<TARGET_OBJECTS:
${
obj_lib_target
}
>
)
else
()
target_sources
(
${
pub_lib_target
}
PRIVATE
"
${
PROJECT_SOURCE_DIR
}
/cmake/dummy.cpp"
$<TARGET_OBJECTS:
${
obj_lib_target
}
>
)
endif
()
if
(
CAF_ENABLE_TESTING AND CAF_ADD_COMPONENT_TEST_SOURCES
)
set
(
tst_bin_target
"caf-
${
name
}
-test"
)
list
(
APPEND targets
${
tst_bin_target
}
)
add_executable
(
${
tst_bin_target
}
${
CAF_ADD_COMPONENT_TEST_SOURCES
}
$<TARGET_OBJECTS:
${
obj_lib_target
}
>
)
target_link_libraries
(
${
tst_bin_target
}
PRIVATE
CAF::
test
target_link_libraries
(
${
tst_bin_target
}
PRIVATE
libcaf_
test
${
CAF_ADD_COMPONENT_DEPENDENCIES
}
)
target_include_directories
(
${
tst_bin_target
}
PRIVATE
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/test"
)
...
...
@@ -401,6 +393,8 @@ endfunction()
add_subdirectory
(
libcaf_core
)
add_subdirectory
(
libcaf_test
)
if
(
CAF_ENABLE_NET_MODULE
)
add_subdirectory
(
libcaf_net
)
endif
()
...
...
libcaf_core/caf/detail/format.hpp
View file @
7a77a40e
...
...
@@ -12,6 +12,7 @@
// `inspect` overload.
#include "caf/detail/build_config.hpp"
#include "caf/detail/source_location.hpp"
#include <string>
...
...
@@ -113,3 +114,32 @@ std::string format(std::string_view fstr, Args&&... args) {
}
// namespace caf::detail
#endif
namespace
caf
::
detail
{
/// Wraps a format string and its source location. Useful for logging functions
/// that have a variadic list of arguments and thus cannot use the usual way of
/// passing in a source location via default argument.
struct
format_string_with_location
{
constexpr
format_string_with_location
(
std
::
string_view
str
,
const
source_location
&
loc
=
source_location
::
current
())
:
value
(
str
),
location
(
loc
)
{
// nop
}
constexpr
format_string_with_location
(
const
char
*
str
,
const
source_location
&
loc
=
source_location
::
current
())
:
value
(
str
),
location
(
loc
)
{
// nop
}
/// The format string.
std
::
string_view
value
;
/// The source location of the format string.
source_location
location
;
};
}
// namespace caf::detail
libcaf_core/caf/detail/source_location.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include <cstdint>
namespace
caf
::
detail
{
// Backport of C++20's source_location. Remove when upgrading to C+20.
class
source_location
{
public:
constexpr
uint_least32_t
line
()
const
noexcept
{
return
line_
;
}
constexpr
uint_least32_t
column
()
const
noexcept
{
return
0
;
}
constexpr
const
char
*
file_name
()
const
noexcept
{
return
file_
;
}
constexpr
const
char
*
function_name
()
const
noexcept
{
return
fn_
;
}
static
constexpr
source_location
current
(
const
char
*
file_name
=
__builtin_FILE
(),
const
char
*
fn_name
=
__builtin_FUNCTION
(),
int
line
=
__builtin_LINE
())
noexcept
{
source_location
loc
;
loc
.
file_
=
file_name
;
loc
.
fn_
=
fn_name
;
loc
.
line_
=
line
;
return
loc
;
}
private:
const
char
*
file_
=
"invalid"
;
const
char
*
fn_
=
"invalid"
;
uint_least32_t
line_
=
0
;
};
}
// namespace caf::detail
libcaf_test/CMakeLists.txt
0 → 100644
View file @
7a77a40e
# -- collect header files ------------------------------------------------------
file
(
GLOB_RECURSE CAF_TEST_HEADERS
"caf/*.hpp"
)
# -- add targets ---------------------------------------------------------------
caf_add_component
(
test
DEPENDENCIES
PUBLIC
CAF::core
PRIVATE
CAF::internal
ENUM_TYPES
test.block_type
HEADERS
${
CAF_TEST_HEADERS
}
SOURCES
src/test/and_given.cpp
src/test/and_then.cpp
src/test/and_when.cpp
src/test/block.cpp
src/test/context.cpp
src/test/factory.cpp
src/test/given.cpp
src/test/nesting_error.cpp
src/test/registry.cpp
src/test/reporter.cpp
src/test/runnable.cpp
src/test/runner.cpp
src/test/scenario.cpp
src/test/scope.cpp
src/test/section.cpp
src/test/test.cpp
src/test/then.cpp
src/test/when.cpp
TEST_SOURCES
test/test-test.cpp
TEST_SUITES
test.block_type
test.scenario
test.test
)
libcaf_test/caf/test/and_given.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
namespace
caf
::
test
{
/// Represents an `AND_GIVEN` block.
class
and_given
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
when
*
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
and_when
*
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/caf/test/and_then.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
namespace
caf
::
test
{
/// Represents an `AND_THEN` block.
class
and_then
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/caf/test/and_when.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
namespace
caf
::
test
{
/// Represents an `AND_WHEN` block.
class
and_when
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
then
*
get_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
and_then
*
get_and_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/caf/test/binary_predicate.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include <string_view>
namespace
caf
::
test
{
/// Describes a binary predicate.
enum
class
binary_predicate
{
/// Equal.
eq
,
/// Not equal.
ne
,
/// Less than.
lt
,
/// Less than or equal.
le
,
/// Greater than.
gt
,
/// Greater than or equal.
ge
,
};
/// Returns a string representation of `predicate` in C++ syntax.
constexpr
std
::
string_view
str
(
binary_predicate
predicate
)
noexcept
{
switch
(
predicate
)
{
default:
return
"???"
;
case
binary_predicate
:
:
eq
:
return
"=="
;
case
binary_predicate
:
:
ne
:
return
"!="
;
case
binary_predicate
:
:
lt
:
return
"<"
;
case
binary_predicate
:
:
le
:
return
"<="
;
case
binary_predicate
:
:
gt
:
return
">"
;
case
binary_predicate
:
:
ge
:
return
">="
;
}
}
/// Returns the negated predicate.
constexpr
binary_predicate
negate
(
binary_predicate
predicate
)
noexcept
{
switch
(
predicate
)
{
default:
return
predicate
;
case
binary_predicate
:
:
eq
:
return
binary_predicate
::
ne
;
case
binary_predicate
:
:
ne
:
return
binary_predicate
::
eq
;
case
binary_predicate
:
:
lt
:
return
binary_predicate
::
ge
;
case
binary_predicate
:
:
le
:
return
binary_predicate
::
gt
;
case
binary_predicate
:
:
gt
:
return
binary_predicate
::
le
;
case
binary_predicate
:
:
ge
:
return
binary_predicate
::
lt
;
}
}
}
// namespace caf::test
libcaf_test/caf/test/block.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/fwd.hpp"
#include <string_view>
#include <vector>
namespace
caf
::
test
{
/// Represents a block of test logic. Blocks can be nested to form a tree-like
/// structure.
class
CAF_TEST_EXPORT
block
{
public:
block
(
context_ptr
ctx
,
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
);
virtual
~
block
();
/// Returns the type of this block.
virtual
block_type
type
()
const
noexcept
=
0
;
/// Returns the user-defined description of this block.
std
::
string_view
description
()
const
noexcept
{
return
description_
;
}
/// Returns the source location of this block.
const
detail
::
source_location
&
location
()
const
noexcept
{
return
loc_
;
}
/// Called at scope entry.
void
enter
();
/// Called from the root block to clean up a branch of the test.
void
leave
();
/// Customization point for performing sanity checks before leaving the block.
virtual
void
on_leave
();
/// Checks whether this block can run. This is used to skip blocks that were
/// executed in a previous run or are scheduled to run in a future run.
bool
can_run
()
const
noexcept
;
/// Checks whether this block is active. A block is active if it is currently
/// executed.
bool
active
()
const
noexcept
{
return
active_
;
}
template
<
class
T
>
T
*
get_nested
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
&
result
=
get_nested_or_construct
(
id
);
if
(
!
result
)
{
result
=
std
::
make_unique
<
T
>
(
ctx_
,
id
,
description
,
loc
);
nested_
.
push_back
(
result
.
get
());
}
return
static_cast
<
T
*>
(
result
.
get
());
}
virtual
section
*
get_section
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
given
*
get_given
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
and_given
*
get_and_given
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
when
*
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
and_when
*
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
then
*
get_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
and_then
*
get_and_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
virtual
but
*
get_but
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
());
protected:
std
::
unique_ptr
<
block
>&
get_nested_or_construct
(
int
id
);
context_ptr
ctx_
;
int
id_
=
0
;
std
::
string_view
description_
;
bool
active_
=
false
;
bool
executed_
=
false
;
std
::
vector
<
block
*>
nested_
;
detail
::
source_location
loc_
;
};
}
// namespace caf::test
libcaf_test/caf/test/block_type.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/test_export.hpp"
#include <string>
#include <string_view>
#include <type_traits>
namespace
caf
::
test
{
/// Identifies a code block in a test definition.
enum
class
block_type
{
/// Identifies a TEST block.
test
,
/// Identifies a SECTION block in a TEST.
section
,
/// Identifies a BDD-style SCENARIO block.
scenario
,
/// Identifies a BDD-style GIVEN block.
given
,
/// Identifies a BDD-style AND_GIVEN block.
and_given
,
/// Identifies a BDD-style WHEN block.
when
,
/// Identifies a BDD-style AND_WHEN block.
and_when
,
/// Identifies a BDD-style THEN block.
then
,
/// Identifies a BDD-style AND_THEN block.
and_then
,
/// Identifies a BDD-style BUT block.
but
,
};
/// Checks whether `type` is an extension type, i.e., AND_GIVEN, AND_WHEN,
/// AND_THEN, or BUT.
/// @relates block_type
constexpr
bool
is_extension
(
block_type
type
)
noexcept
{
switch
(
type
)
{
default:
return
false
;
case
block_type
:
:
and_given
:
case
block_type
:
:
and_when
:
case
block_type
:
:
and_then
:
case
block_type
:
:
but
:
return
true
;
}
}
/// @relates block_type
constexpr
std
::
string_view
macro_name
(
block_type
type
)
noexcept
{
switch
(
type
)
{
case
block_type
:
:
test
:
return
"TEST"
;
case
block_type
:
:
section
:
return
"SECTION"
;
case
block_type
:
:
scenario
:
return
"SCENARIO"
;
case
block_type
:
:
given
:
return
"GIVEN"
;
case
block_type
:
:
and_given
:
return
"AND_GIVEN"
;
case
block_type
:
:
when
:
return
"WHEN"
;
case
block_type
:
:
and_when
:
return
"AND_WHEN"
;
case
block_type
:
:
then
:
return
"THEN"
;
case
block_type
:
:
and_then
:
return
"AND_THEN"
;
case
block_type
:
:
but
:
return
"BUT"
;
}
return
"???"
;
}
/// @relates block_type
constexpr
std
::
string_view
as_prefix
(
block_type
type
)
noexcept
{
switch
(
type
)
{
case
block_type
:
:
test
:
return
"Test"
;
case
block_type
:
:
section
:
return
"Section"
;
case
block_type
:
:
scenario
:
return
"Scenario"
;
case
block_type
:
:
given
:
return
"Given"
;
case
block_type
:
:
when
:
return
"When"
;
case
block_type
:
:
then
:
return
"Then"
;
case
block_type
:
:
and_given
:
case
block_type
:
:
and_when
:
case
block_type
:
:
and_then
:
return
"And"
;
case
block_type
:
:
but
:
return
"But"
;
}
return
"???"
;
}
/// @relates block_type
CAF_TEST_EXPORT
std
::
string
to_string
(
block_type
);
/// @relates block_type
CAF_TEST_EXPORT
bool
from_string
(
std
::
string_view
,
block_type
&
);
/// @relates block_type
CAF_TEST_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
block_type
>
,
block_type
&
);
}
// namespace caf::test
libcaf_test/caf/test/caf_test_main.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/exec_main.hpp"
#include "caf/test/runner.hpp"
#define CAF_TEST_MAIN(...) \
int main(int argc, char** argv) { \
[[maybe_unused]] auto host_init_guard = caf::detail::do_init_host_system( \
caf::detail::type_list<>{}, caf::detail::type_list<__VA_ARGS__>{}); \
caf::exec_main_init_meta_objects<__VA_ARGS__>(); \
caf::core::init_global_meta_objects(); \
caf::test::runner runner; \
return runner.run(argc, argv); \
}
libcaf_test/caf/test/context.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
#include "caf/test/fwd.hpp"
#include <map>
#include <memory>
#include <string_view>
#include <vector>
namespace
caf
::
test
{
/// Represents the exeuction context of a test. The context stores all steps of
/// the test and the current execution stack. The context persists across
/// multiple runs of the test in order to select one execution path per run.
class
CAF_TEST_EXPORT
context
:
public
std
::
enable_shared_from_this
<
context
>
{
public:
/// Returns whether the test is still active. A test is active as long as
/// no unwinding is in progress.
bool
active
()
const
noexcept
{
return
unwind_stack
.
empty
();
}
/// Clears the call and unwind stacks.
void
clear_stacks
()
{
call_stack
.
clear
();
unwind_stack
.
clear
();
path
.
clear
();
}
bool
can_run
();
void
on_enter
(
block
*
ptr
);
void
on_leave
(
block
*
ptr
);
/// Checks whether `ptr` has been activated this run, i.e., whether we can
/// find it in `unwind_stack`.
bool
activated
(
block
*
ptr
)
{
return
std
::
find
(
path
.
begin
(),
path
.
end
(),
ptr
)
!=
path
.
end
();
}
/// Stores the current execution stack for the run.
std
::
vector
<
block
*>
call_stack
;
/// Stores the steps that finished execution this run.
std
::
vector
<
block
*>
unwind_stack
;
/// Stores all steps that we have reached at least once during the run.
std
::
vector
<
block
*>
path
;
/// Stores all steps of the test with their run-time ID.
std
::
map
<
int
,
std
::
unique_ptr
<
block
>>
steps
;
template
<
class
T
>
T
*
get
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
&
result
=
steps
[
id
];
if
(
!
result
)
{
result
=
std
::
make_unique
<
T
>
(
shared_from_this
(),
id
,
description
,
loc
);
}
return
static_cast
<
T
*>
(
result
.
get
());
}
template
<
class
T
>
T
*
find_predecessor
(
int
caller_id
)
{
return
static_cast
<
T
*>
(
find_predecessor_block
(
caller_id
,
T
::
type_token
));
}
private:
block
*
find_predecessor_block
(
int
caller_id
,
block_type
type
);
};
/// A smart pointer to the execution context of a test.
using
context_ptr
=
std
::
shared_ptr
<
context
>
;
}
// namespace caf::test
libcaf_test/caf/test/factory.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block_type.hpp"
#include "caf/test/fwd.hpp"
#include <memory>
#include <string_view>
namespace
caf
::
test
{
/// A factory for creating runnable test definitions.
class
factory
{
public:
friend
class
registry
;
factory
(
std
::
string_view
suite_name
,
std
::
string_view
description
,
block_type
type
)
:
suite_name_
(
suite_name
),
description_
(
description
),
type_
(
type
)
{
// nop
}
virtual
~
factory
();
/// Returns the name of the suite this factory belongs to.
std
::
string_view
suite_name
()
const
noexcept
{
return
suite_name_
;
}
/// Creates a new runnable definition for the test.
virtual
std
::
unique_ptr
<
runnable
>
make
(
context_ptr
state
)
=
0
;
protected:
std
::
unique_ptr
<
factory
>
next_
;
std
::
string_view
suite_name_
;
std
::
string_view
description_
;
block_type
type_
;
};
}
// namespace caf::test
libcaf_test/caf/test/fwd.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include <memory>
namespace
caf
::
test
{
enum
class
block_type
;
enum
class
binary_predicate
;
enum
class
unary_predicate
;
class
and_given
;
class
and_then
;
class
and_when
;
class
block
;
class
but
;
class
context
;
class
factory
;
class
given
;
class
nesting_error
;
class
registry
;
class
reporter
;
class
runnable
;
class
runner
;
class
scenario
;
class
scope
;
class
section
;
class
test
;
class
then
;
class
when
;
using
context_ptr
=
std
::
shared_ptr
<
context
>
;
}
// namespace caf::test
libcaf_test/caf/test/given.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
namespace
caf
::
test
{
/// Represents a `GIVEN` block.
class
given
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
when
*
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
and_when
*
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/caf/test/nesting_error.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/block_type.hpp"
namespace
caf
::
test
{
/// Thrown when a block is opened in an invalid parent. When
/// `CAF_ENABLE_EXCEPTIONS` is off, the `raise` functions terminate the program
/// instead.
class
CAF_TEST_EXPORT
nesting_error
{
public:
constexpr
nesting_error
(
const
nesting_error
&
)
noexcept
=
default
;
constexpr
nesting_error
&
operator
=
(
const
nesting_error
&
)
noexcept
=
default
;
/// Returns a human-readable error message.
std
::
string
message
()
const
;
/// Returns the type of the parent in which the error occurred.
constexpr
block_type
parent
()
const
noexcept
{
return
parent_
;
}
/// Returns the type of the block that caused the error.
constexpr
block_type
child
()
const
noexcept
{
return
child_
;
}
/// Returns the source location of the error.
constexpr
const
detail
::
source_location
&
location
()
const
noexcept
{
return
loc_
;
}
/// Throws a `nesting_error` to indicate that `child` is not allowed in
/// `parent`.
[[
noreturn
]]
static
void
raise_not_allowed
(
block_type
parent
,
block_type
child
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
raise_impl
(
code
::
not_allowed
,
parent
,
child
,
loc
);
}
/// Throws a `nesting_error` to indicate that `parent` is not allowing
/// additional blocks of type `child`.
[[
noreturn
]]
static
void
raise_too_many
(
block_type
parent
,
block_type
child
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
raise_impl
(
code
::
too_many
,
parent
,
child
,
loc
);
}
/// Throws a `nesting_error` to indicate that `child` expected a `parent`
/// block prior to it.
[[
noreturn
]]
static
void
raise_invalid_sequence
(
block_type
parent
,
block_type
child
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
raise_impl
(
code
::
invalid_sequence
,
parent
,
child
,
loc
);
}
private:
enum
class
code
{
not_allowed
,
too_many
,
invalid_sequence
,
};
constexpr
nesting_error
(
code
what
,
block_type
parent
,
block_type
child
,
const
detail
::
source_location
&
loc
)
noexcept
:
code_
(
what
),
parent_
(
parent
),
child_
(
child
),
loc_
(
loc
)
{
// nop
}
[[
noreturn
]]
static
void
raise_impl
(
code
what
,
block_type
parent
,
block_type
child
,
const
detail
::
source_location
&
loc
);
code
code_
;
block_type
parent_
;
block_type
child_
;
detail
::
source_location
loc_
;
};
}
// namespace caf::test
libcaf_test/caf/test/registry.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block_type.hpp"
#include "caf/test/factory.hpp"
#include "caf/test/fwd.hpp"
#include "caf/unordered_flat_map.hpp"
#include <cstddef>
#include <map>
#include <string_view>
namespace
caf
::
test
{
/// A registry for our factories.
class
registry
{
public:
/// Maps test names to factories. Elements are sorted by the order of their
/// registration.
using
tests_map
=
unordered_flat_map
<
std
::
string_view
,
factory
*>
;
/// Maps suite names to suites.
using
suites_map
=
std
::
map
<
std
::
string_view
,
tests_map
>
;
static
suites_map
suites
();
template
<
class
TestImpl
>
static
ptrdiff_t
add
(
std
::
string_view
suite_name
,
std
::
string_view
description
,
block_type
type
)
{
struct
impl
:
factory
{
using
factory
::
factory
;
std
::
unique_ptr
<
runnable
>
make
(
context_ptr
state
)
override
{
return
std
::
make_unique
<
TestImpl
>
(
std
::
move
(
state
),
this
->
description_
,
this
->
type_
);
}
};
auto
ptr
=
std
::
make_unique
<
impl
>
(
suite_name
,
description
,
type
);
auto
result
=
reinterpret_cast
<
ptrdiff_t
>
(
ptr
.
get
());
if
(
head_
==
nullptr
)
{
head_
=
std
::
move
(
ptr
);
tail_
=
head_
.
get
();
}
else
{
tail_
->
next_
=
std
::
move
(
ptr
);
tail_
=
tail_
->
next_
.
get
();
}
return
result
;
}
private:
static
std
::
unique_ptr
<
factory
>
head_
;
static
factory
*
tail_
;
};
}
// namespace caf::test
libcaf_test/caf/test/reporter.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/fwd.hpp"
#include <cstddef>
#include <string_view>
namespace
caf
::
test
{
/// Observes the execution of test suites and reports the results.
class
CAF_TEST_EXPORT
reporter
{
public:
struct
stats
{
size_t
passed
;
size_t
failed
;
size_t
total
()
const
noexcept
{
return
passed
+
failed
;
}
stats
&
operator
+=
(
const
stats
&
other
)
{
passed
+=
other
.
passed
;
failed
+=
other
.
failed
;
return
*
this
;
}
};
virtual
~
reporter
();
virtual
bool
success
()
const
noexcept
=
0
;
virtual
void
start
()
=
0
;
virtual
void
stop
()
=
0
;
virtual
void
begin_suite
(
std
::
string_view
name
)
=
0
;
virtual
void
end_suite
(
std
::
string_view
name
)
=
0
;
virtual
void
begin_test
(
context_ptr
state
,
std
::
string_view
)
=
0
;
virtual
void
end_test
()
=
0
;
virtual
void
begin_step
(
block
*
ptr
)
=
0
;
virtual
void
end_step
(
block
*
ptr
)
=
0
;
virtual
void
pass
(
const
detail
::
source_location
&
location
)
=
0
;
/// Reports a failed check with a binary predicate.
virtual
void
fail
(
binary_predicate
type
,
std
::
string_view
lhs
,
std
::
string_view
rhs
,
const
detail
::
source_location
&
location
)
=
0
;
/// Reports a failed check (unary predicate).
virtual
void
fail
(
std
::
string_view
arg
,
const
detail
::
source_location
&
location
)
=
0
;
virtual
void
unhandled_exception
(
std
::
string_view
msg
)
=
0
;
virtual
void
unhandled_exception
(
std
::
string_view
msg
,
const
detail
::
source_location
&
location
)
=
0
;
virtual
void
info
(
std
::
string_view
msg
,
const
detail
::
source_location
&
location
)
=
0
;
virtual
void
verbosity
(
unsigned
level
)
=
0
;
/// Returns statistics for the current test.
virtual
stats
test_stats
()
=
0
;
/// Overrides the statistics for the current test.
virtual
void
test_stats
(
stats
)
=
0
;
/// Returns statistics for the current suite.
virtual
stats
suite_stats
()
=
0
;
/// Returns statistics for the entire run.
virtual
stats
total_stats
()
=
0
;
/// Stores a pointer to the currently active reporter.
static
reporter
*
instance
;
/// Creates a default reporter that writes to the standard output.
static
std
::
unique_ptr
<
reporter
>
make_default
();
};
}
// namespace caf::test
libcaf_test/caf/test/runnable.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/format.hpp"
#include "caf/detail/source_location.hpp"
#include "caf/test/binary_predicate.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/fwd.hpp"
#include "caf/test/reporter.hpp"
#include <string_view>
namespace
caf
::
test
{
/// A runnable definition of a test case or scenario.
class
runnable
{
public:
/// Creates a new runnable.
/// @param ctx The test context.
/// @param description A description of the test or scenario.
/// @param root_type The type of the root block.
/// @param loc The source location of the test or scenario.
runnable
(
context_ptr
ctx
,
std
::
string_view
description
,
block_type
root_type
,
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
:
ctx_
(
std
::
move
(
ctx
)),
description_
(
description
),
root_type_
(
root_type
),
loc_
(
loc
)
{
// nop
}
virtual
~
runnable
();
/// Runs the next branch of the test.
void
run
();
/// Generates a message with the INFO severity level.
template
<
class
...
Ts
>
void
info
(
detail
::
format_string_with_location
fwl
,
Ts
&&
...
xs
)
{
if
constexpr
(
sizeof
...(
Ts
)
>
0
)
{
auto
msg
=
detail
::
format
(
fwl
.
value
,
std
::
forward
<
Ts
>
(
xs
)...);
reporter
::
instance
->
info
(
msg
,
fwl
.
location
);
}
else
{
reporter
::
instance
->
info
(
fwl
.
value
,
fwl
.
location
);
}
}
/// Checks whether `lhs` and `rhs` are equal.
template
<
class
T0
,
class
T1
>
void
check_eq
(
const
T0
&
lhs
,
const
T1
&
rhs
,
const
detail
::
source_location
&
location
=
detail
::
source_location
::
current
())
{
if
(
std
::
is_integral_v
<
T0
>
&&
std
::
is_integral_v
<
T1
>
)
{
static_assert
(
std
::
is_signed_v
<
T0
>
==
std
::
is_signed_v
<
T1
>
,
"comparing signed and unsigned integers is unsafe"
);
}
if
(
lhs
==
rhs
)
{
reporter
::
instance
->
pass
(
location
);
return
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
eq
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
}
void
check
(
bool
value
,
const
detail
::
source_location
&
location
=
detail
::
source_location
::
current
());
block
&
current_block
();
#ifdef CAF_ENABLE_EXCEPTIONS
template
<
class
Exception
=
void
,
class
CodeBlock
>
void
check_throws
(
CodeBlock
&&
fn
,
const
detail
::
source_location
&
location
=
detail
::
source_location
::
current
())
{
if
constexpr
(
std
::
is_same_v
<
Exception
,
void
>
)
{
try
{
fn
();
}
catch
(...)
{
reporter
::
instance
->
pass
(
location
);
return
;
}
reporter
::
instance
->
fail
(
"throws"
,
location
);
}
else
{
try
{
fn
();
}
catch
(
const
Exception
&
)
{
reporter
::
instance
->
pass
(
location
);
return
;
}
catch
(...)
{
reporter
::
instance
->
fail
(
"throws Exception"
,
location
);
return
;
}
reporter
::
instance
->
fail
(
"throws Exception"
,
location
);
}
}
#endif
protected:
context_ptr
ctx_
;
std
::
string_view
description_
;
block_type
root_type_
;
detail
::
source_location
loc_
;
private:
template
<
class
T
>
std
::
string
stringify
(
const
T
&
value
)
{
if
constexpr
(
std
::
is_convertible_v
<
T
,
std
::
string
>
)
{
return
std
::
string
{
value
};
}
else
{
return
caf
::
deep_to_string
(
value
);
}
}
virtual
void
do_run
()
=
0
;
};
}
// namespace caf::test
libcaf_test/caf/test/runner.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/registry.hpp"
namespace
caf
::
test
{
/// Implements the main loop for running tests.
class
runner
{
public:
/// Bundles the result of a command line parsing operation.
struct
parse_cli_result
{
/// Stores whether parsing the command line arguments was successful.
bool
ok
;
/// Stores whether a help text was printed.
bool
help_printed
;
};
runner
();
int
run
(
int
argc
,
char
**
argv
);
private:
parse_cli_result
parse_cli
(
int
argc
,
char
**
argv
);
registry
::
suites_map
suites_
;
};
}
// namespace caf::test
libcaf_test/caf/test/scenario.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/pp.hpp"
#include "caf/test/and_given.hpp"
#include "caf/test/and_then.hpp"
#include "caf/test/and_when.hpp"
#include "caf/test/block.hpp"
#include "caf/test/context.hpp"
#include "caf/test/factory.hpp"
#include "caf/test/given.hpp"
#include "caf/test/registry.hpp"
#include "caf/test/runnable.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/suite.hpp"
#include "caf/test/then.hpp"
#include "caf/test/when.hpp"
namespace
caf
::
test
{
class
scenario
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
given
*
get_given
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
and_given
*
get_and_given
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
when
*
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
and_when
*
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
scope
commit
();
};
}
// namespace caf::test
#define GIVEN(description) \
for (auto given_scope \
= this->current_block().get_given(__COUNTER__, description)->commit(); \
given_scope; given_scope.leave())
#define AND_GIVEN(description) \
static_cast<void>(0); \
for (auto and_given_scope = this->current_block() \
.get_and_given(__COUNTER__, description) \
->commit(); \
and_given_scope.leave())
#define WHEN(description) \
for (auto when_scope \
= this->current_block().get_when(__COUNTER__, description)->commit(); \
when_scope; when_scope.leave())
#define AND_WHEN(description) \
static_cast<void>(0); \
for (auto and_when_scope = this->current_block() \
.get_and_when(__COUNTER__, description) \
->commit(); \
and_when_scope; and_when_scope.leave())
#define THEN(description) \
for (auto then_scope \
= this->current_block().get_then(__COUNTER__, description)->commit(); \
then_scope; then_scope.leave())
#define AND_THEN(description) \
static_cast<void>(0); \
for (auto and_then_scope = this->current_block() \
.get_and_then(__COUNTER__, description) \
->commit(); \
and_then_scope; and_then_scope.leave())
#define SCENARIO(description) \
struct CAF_PP_UNIFYN(scenario_) \
: caf::test::runnable, caf_test_case_auto_fixture { \
using super = caf::test::runnable; \
using super::super; \
void do_run() override; \
static ptrdiff_t register_id; \
using caf_test_suite_name_t = std::decay_t<decltype(caf_test_suite_name)>; \
static_assert(!std::is_same_v<caf::unit_t, caf_test_suite_name_t>, \
"SCENARIO must be nested in a SUITE block"); \
}; \
ptrdiff_t CAF_PP_UNIFYN(scenario_)::register_id \
= caf::test::registry::add<CAF_PP_UNIFYN(scenario_)>( \
caf_test_suite_name, description, caf::test::block_type::scenario); \
void CAF_PP_UNIFYN(scenario_)::do_run()
libcaf_test/caf/test/scope.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/fwd.hpp"
namespace
caf
::
test
{
/// Represents an execution scope for a test block.
class
CAF_TEST_EXPORT
scope
{
public:
scope
()
noexcept
=
default
;
explicit
scope
(
block
*
ptr
)
noexcept
:
ptr_
(
ptr
)
{
// nop
}
scope
(
scope
&&
other
)
noexcept
:
ptr_
(
other
.
release
())
{
// nop
}
scope
&
operator
=
(
scope
&&
other
)
noexcept
;
scope
(
const
scope
&
)
=
delete
;
scope
&
operator
=
(
const
scope
&
)
=
delete
;
~
scope
();
/// Leave the scope with calling `on_leave` before `leave`. This allows the
/// block to perform sanity checks and potentially throws.
void
leave
();
/// Checks whether this scope is active.
explicit
operator
bool
()
const
noexcept
{
return
ptr_
!=
nullptr
;
}
private:
block
*
release
()
noexcept
{
auto
tmp
=
ptr_
;
ptr_
=
nullptr
;
return
tmp
;
}
block
*
ptr_
=
nullptr
;
};
}
// namespace caf::test
libcaf_test/caf/test/section.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
namespace
caf
::
test
{
class
section
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
section
*
get_section
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/caf/test/step_type.hpp
0 → 100644
View file @
7a77a40e
block_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_typeblock_type
libcaf_test/caf/test/suite.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/pp.hpp"
#include "caf/unit.hpp"
#include <string_view>
[[
maybe_unused
]]
constexpr
caf
::
unit_t
caf_test_suite_name
=
caf
::
unit
;
struct
caf_test_case_auto_fixture
{};
#define SUITE(name) \
namespace { \
static_assert( \
std::is_same_v<std::decay_t<decltype(caf_test_suite_name)>, caf::unit_t>, \
"only one SUITE per translation unit is supported"); \
constexpr std::string_view caf_test_suite_name = name; \
} \
namespace
#define WITH_FIXTURE(name) \
namespace CAF_PP_UNIFYN(caf_fixture_) { \
static_assert( \
!std::is_same_v<std::decay_t<decltype(caf_test_suite_name)>, caf::unit_t>, \
"WITH_FIXTURE must be placed into a SUITE scope"); \
using caf_test_case_auto_fixture = name; \
} \
namespace CAF_PP_UNIFYN(caf_fixture_)
libcaf_test/caf/test/test.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/pp.hpp"
#include "caf/test/block.hpp"
#include "caf/test/context.hpp"
#include "caf/test/factory.hpp"
#include "caf/test/registry.hpp"
#include "caf/test/runnable.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/section.hpp"
#include "caf/test/suite.hpp"
namespace
caf
::
test
{
/// Represents a `TEST` block.
class
test
:
public
block
{
public:
using
block
::
block
;
block_type
type
()
const
noexcept
override
;
section
*
get_section
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
scope
commit
();
};
}
// namespace caf::test
#define SECTION(description) \
for (auto CAF_PP_UNIFYN(scope_) = this->current_block() \
.get_section(__COUNTER__, description) \
->commit(); \
CAF_PP_UNIFYN(scope_); CAF_PP_UNIFYN(scope_).leave())
#define TEST(description) \
struct CAF_PP_UNIFYN(test_) \
: caf::test::runnable, caf_test_case_auto_fixture { \
using super = caf::test::runnable; \
using super::super; \
void do_run() override; \
static ptrdiff_t register_id; \
using caf_test_suite_name_t = std::decay_t<decltype(caf_test_suite_name)>; \
static_assert(!std::is_same_v<caf::unit_t, caf_test_suite_name_t>, \
"TEST must be nested in a SUITE block"); \
}; \
ptrdiff_t CAF_PP_UNIFYN(test_)::register_id \
= caf::test::registry::add<CAF_PP_UNIFYN(test_)>( \
caf_test_suite_name, description, caf::test::block_type::test); \
void CAF_PP_UNIFYN(test_)::do_run()
libcaf_test/caf/test/then.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
#include "caf/test/block_type.hpp"
namespace
caf
::
test
{
/// Represents a `THEN` block.
class
then
:
public
block
{
public:
using
block
::
block
;
static
constexpr
block_type
type_token
=
block_type
::
then
;
block_type
type
()
const
noexcept
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/caf/test/when.hpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/test/block.hpp"
#include "caf/test/block_type.hpp"
namespace
caf
::
test
{
class
when
:
public
block
{
public:
using
block
::
block
;
static
constexpr
block_type
type_token
=
block_type
::
when
;
then
*
get_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
and_then
*
get_and_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
override
;
block_type
type
()
const
noexcept
override
;
scope
commit
();
};
}
// namespace caf::test
libcaf_test/src/test/and_given.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/and_given.hpp"
#include "caf/test/and_when.hpp"
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/when.hpp"
namespace
caf
::
test
{
block_type
and_given
::
type
()
const
noexcept
{
return
block_type
::
and_given
;
}
when
*
and_given
::
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
when
>
(
id
,
description
,
loc
);
}
and_when
*
and_given
::
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
*
result
=
ctx_
->
get
<
and_when
>
(
id
,
description
,
loc
);
if
(
nested_
.
empty
())
{
nesting_error
::
raise_invalid_sequence
(
block_type
::
when
,
block_type
::
and_when
,
loc
);
}
nested_
.
push_back
(
result
);
return
result
;
}
scope
and_given
::
commit
()
{
if
(
!
ctx_
->
active
()
||
!
can_run
())
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/and_then.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/and_then.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/then.hpp"
namespace
caf
::
test
{
block_type
and_then
::
type
()
const
noexcept
{
return
block_type
::
and_then
;
}
scope
and_then
::
commit
()
{
// An AND_THEN block is only executed if the previous THEN block was executed.
if
(
!
can_run
()
||
!
ctx_
->
activated
(
ctx_
->
find_predecessor
<
then
>
(
id_
)))
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/and_when.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/and_when.hpp"
#include "caf/test/and_then.hpp"
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/then.hpp"
#include "caf/test/when.hpp"
namespace
caf
::
test
{
block_type
and_when
::
type
()
const
noexcept
{
return
block_type
::
and_when
;
}
then
*
and_when
::
get_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
*
result
=
ctx_
->
get
<
then
>
(
id
,
description
,
loc
);
if
(
nested_
.
empty
())
{
nested_
.
emplace_back
(
result
);
}
else
if
(
nested_
.
front
()
!=
result
)
{
nesting_error
::
raise_too_many
(
type
(),
block_type
::
then
,
loc
);
}
return
result
;
}
and_then
*
and_when
::
get_and_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
*
result
=
ctx_
->
get
<
and_then
>
(
id
,
description
,
loc
);
if
(
nested_
.
empty
())
{
nesting_error
::
raise_invalid_sequence
(
block_type
::
then
,
block_type
::
and_then
,
loc
);
}
nested_
.
push_back
(
result
);
return
result
;
}
scope
and_when
::
commit
()
{
// An AND_WHEN block is only executed if the previous WHEN block was executed.
if
(
!
can_run
()
||
!
ctx_
->
activated
(
ctx_
->
find_predecessor
<
when
>
(
id_
)))
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/block.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/block.hpp"
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
namespace
caf
::
test
{
block
::
block
(
context_ptr
ctx
,
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
:
ctx_
(
ctx
),
id_
(
id
),
description_
(
description
),
loc_
(
loc
)
{
// nop
}
block
::~
block
()
{
// nop
}
void
block
::
enter
()
{
executed_
=
true
;
ctx_
->
on_enter
(
this
);
}
void
block
::
leave
()
{
ctx_
->
on_leave
(
this
);
}
void
block
::
on_leave
()
{
// nop
}
bool
block
::
can_run
()
const
noexcept
{
auto
pred
=
[](
const
auto
*
nested
)
{
return
nested
->
can_run
();
};
return
!
executed_
||
std
::
any_of
(
nested_
.
begin
(),
nested_
.
end
(),
pred
);
}
section
*
block
::
get_section
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
section
,
loc
);
}
given
*
block
::
get_given
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
given
,
loc
);
}
and_given
*
block
::
get_and_given
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
given
,
loc
);
}
when
*
block
::
get_when
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
when
,
loc
);
}
and_when
*
block
::
get_and_when
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
and_when
,
loc
);
}
then
*
block
::
get_then
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
then
,
loc
);
}
and_then
*
block
::
get_and_then
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
and_then
,
loc
);
}
but
*
block
::
get_but
(
int
,
std
::
string_view
,
const
detail
::
source_location
&
loc
)
{
nesting_error
::
raise_not_allowed
(
type
(),
block_type
::
but
,
loc
);
}
std
::
unique_ptr
<
block
>&
block
::
get_nested_or_construct
(
int
id
)
{
// Note: we only need this trivial getter to have avoid including
// "context.hpp" from "block.hpp".
return
ctx_
->
steps
[
id
];
}
}
// namespace caf::test
libcaf_test/src/test/context.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/context.hpp"
#include "caf/test/block.hpp"
#include "caf/test/reporter.hpp"
namespace
caf
::
test
{
void
context
::
on_enter
(
block
*
ptr
)
{
call_stack
.
push_back
(
ptr
);
unwind_stack
.
clear
();
path
.
push_back
(
ptr
);
reporter
::
instance
->
begin_step
(
ptr
);
}
void
context
::
on_leave
(
block
*
ptr
)
{
call_stack
.
pop_back
();
unwind_stack
.
push_back
(
ptr
);
reporter
::
instance
->
end_step
(
ptr
);
}
bool
context
::
can_run
()
{
auto
pred
=
[](
auto
&
kvp
)
{
return
kvp
.
second
->
can_run
();
};
return
!
steps
.
empty
()
&&
std
::
any_of
(
steps
.
begin
(),
steps
.
end
(),
pred
);
}
block
*
context
::
find_predecessor_block
(
int
caller_id
,
block_type
type
)
{
// Find the caller.
auto
i
=
steps
.
find
(
caller_id
);
if
(
i
==
steps
.
end
())
return
nullptr
;
// Find the first step of type `T` that precedes the caller.
auto
pred
=
[
type
](
auto
&
kvp
)
{
return
kvp
.
second
->
type
()
==
type
;
};
auto
j
=
std
::
find_if
(
std
::
reverse_iterator
{
i
},
steps
.
rend
(),
pred
);
if
(
j
==
steps
.
rend
())
return
nullptr
;
return
j
->
second
.
get
();
}
}
// namespace caf::test
libcaf_test/src/test/factory.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/factory.hpp"
namespace
caf
::
test
{
factory
::~
factory
()
{
// nop
}
}
// namespace caf::test
libcaf_test/src/test/given.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/given.hpp"
#include "caf/test/and_when.hpp"
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/when.hpp"
namespace
caf
::
test
{
block_type
given
::
type
()
const
noexcept
{
return
block_type
::
given
;
}
when
*
given
::
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
when
>
(
id
,
description
,
loc
);
}
and_when
*
given
::
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
*
result
=
ctx_
->
get
<
and_when
>
(
id
,
description
,
loc
);
if
(
nested_
.
empty
())
{
nesting_error
::
raise_invalid_sequence
(
block_type
::
when
,
block_type
::
and_when
,
loc
);
}
nested_
.
push_back
(
result
);
return
result
;
}
scope
given
::
commit
()
{
if
(
!
ctx_
->
active
()
||
!
can_run
())
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/nesting_error.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/nesting_error.hpp"
#include "caf/config.hpp"
#include "caf/detail/format.hpp"
#include <cstdio>
namespace
caf
::
test
{
std
::
string
nesting_error
::
message
()
const
{
switch
(
code_
)
{
default:
// not allowed
return
detail
::
format
(
"cannot nest a {} in a {} block"
,
macro_name
(
child_
),
macro_name
(
parent_
));
case
code
:
:
too_many
:
return
detail
::
format
(
"too many {} blocks in a {} block"
,
macro_name
(
child_
),
macro_name
(
parent_
));
case
code
:
:
invalid_sequence
:
return
detail
::
format
(
"need a {} block before a {} block"
,
macro_name
(
parent_
),
macro_name
(
child_
));
}
}
[[
noreturn
]]
void
nesting_error
::
raise_impl
(
nesting_error
::
code
what
,
block_type
parent
,
block_type
child
,
const
detail
::
source_location
&
loc
)
{
#ifdef CAF_ENABLE_EXCEPTIONS
throw
nesting_error
{
what
,
parent
,
child
,
loc
};
#else
auto
msg
=
nesting_error
{
code
::
not_allowed
,
parent
,
child
}.
message
();
fprintf
(
stderr
,
"[FATAL] critical error (%s:%d): %s
\n
"
,
msg
.
c_str
(),
loc
.
file_name
,
static_cast
<
int
>
(
loc
.
line
));
abort
();
#endif
}
}
// namespace caf::test
libcaf_test/src/test/registry.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/registry.hpp"
#include "caf/detail/format.hpp"
#include "caf/raise_error.hpp"
namespace
caf
::
test
{
registry
::
suites_map
registry
::
suites
()
{
suites_map
result
;
for
(
auto
ptr
=
head_
.
get
();
ptr
!=
nullptr
;
ptr
=
ptr
->
next_
.
get
())
{
auto
&
suite
=
result
[
ptr
->
suite_name_
];
if
(
auto
[
iter
,
ok
]
=
suite
.
emplace
(
ptr
->
description_
,
ptr
);
!
ok
)
{
auto
msg
=
detail
::
format
(
"duplicate test name in suite {}: {}"
,
ptr
->
suite_name
(),
ptr
->
description_
);
CAF_RAISE_ERROR
(
std
::
logic_error
,
msg
.
c_str
());
}
}
return
result
;
}
std
::
unique_ptr
<
factory
>
registry
::
head_
;
factory
*
registry
::
tail_
;
}
// namespace caf::test
libcaf_test/src/test/reporter.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/reporter.hpp"
#include "caf/detail/format.hpp"
#include "caf/detail/log_level.hpp"
#include "caf/raise_error.hpp"
#include "caf/term.hpp"
#include "caf/test/binary_predicate.hpp"
#include "caf/test/block.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include <iostream>
namespace
caf
::
test
{
reporter
::~
reporter
()
{
// nop
}
reporter
*
reporter
::
instance
;
namespace
{
/// Implements a mini-DSL for colored output:
/// - $R(red text)
/// - $G(green text)
/// - $B(blue text)
/// - $Y(yellow text)
/// - $M(magenta text)
/// - $C(cyan text)
/// - $0 turns off coloring completely (enter verbatim mode)
struct
colorizing_iterator
{
enum
mode_t
{
normal
,
read_color
,
escape
,
color
,
off
,
off_read_color
,
off_escape
,
off_color
,
verbatim
,
};
mode_t
mode
=
normal
;
std
::
ostream
*
out
;
void
put
(
char
c
)
{
switch
(
mode
)
{
case
normal
:
if
(
c
==
'$'
)
mode
=
read_color
;
else
out
->
put
(
c
);
break
;
case
read_color
:
out
->
flush
();
switch
(
c
)
{
case
'R'
:
*
out
<<
term
::
red
;
break
;
case
'G'
:
*
out
<<
term
::
green
;
break
;
case
'B'
:
*
out
<<
term
::
blue
;
break
;
case
'Y'
:
*
out
<<
term
::
yellow
;
break
;
case
'M'
:
*
out
<<
term
::
magenta
;
break
;
case
'C'
:
*
out
<<
term
::
cyan
;
break
;
case
'0'
:
mode
=
verbatim
;
return
;
default:
CAF_RAISE_ERROR
(
"invalid color code"
);
break
;
}
mode
=
escape
;
break
;
case
escape
:
if
(
c
!=
'('
)
CAF_RAISE_ERROR
(
"expected ( after color code"
);
mode
=
color
;
break
;
case
color
:
if
(
c
==
')'
)
{
out
->
flush
();
*
out
<<
term
::
reset
;
mode
=
normal
;
break
;
}
out
->
put
(
c
);
break
;
case
off
:
if
(
c
==
'$'
)
mode
=
off_read_color
;
else
out
->
put
(
c
);
break
;
case
off_read_color
:
mode
=
off_escape
;
break
;
case
off_escape
:
if
(
c
!=
'('
)
CAF_RAISE_ERROR
(
"expected ( after color code"
);
mode
=
color
;
break
;
case
off_color
:
if
(
c
==
')'
)
{
mode
=
off
;
break
;
}
out
->
put
(
c
);
break
;
default:
// verbatim
out
->
put
(
c
);
}
}
colorizing_iterator
&
operator
++
()
{
return
*
this
;
}
colorizing_iterator
operator
++
(
int
)
{
return
*
this
;
}
colorizing_iterator
&
operator
*
()
{
return
*
this
;
}
colorizing_iterator
&
operator
=
(
char
c
)
{
put
(
c
);
return
*
this
;
}
};
class
default_reporter
:
public
reporter
{
public:
using
clock_type
=
std
::
chrono
::
steady_clock
;
using
time_point
=
clock_type
::
time_point
;
using
fractional_seconds
=
std
::
chrono
::
duration
<
double
>
;
bool
success
()
const
noexcept
override
{
return
total_stats_
.
failed
==
0
;
}
void
start
()
override
{
start_time_
=
clock_type
::
now
();
}
auto
plain
()
{
return
std
::
ostream_iterator
<
char
>
{
std
::
cout
};
}
auto
colored
()
{
return
colorizing_iterator
{
colorizing_iterator
::
normal
,
&
std
::
cout
};
}
void
stop
()
override
{
using
detail
::
format_to
;
using
std
::
chrono
::
duration_cast
;
auto
elapsed
=
clock_type
::
now
()
-
start_time_
;
format_to
(
colored
(),
"$B(Summary):
\n
"
" $B(Time): $Y({0:.3f}s)
\n
"
" $B(Suites): ${1}({2} / {3})
\n
"
" $B(Checks): ${1}({4} / {5})
\n
"
" $B(Status): ${1}({6})
\n
"
,
duration_cast
<
fractional_seconds
>
(
elapsed
).
count
(),
// {0}
total_stats_
.
failed
>
0
?
'R'
:
'G'
,
// {1}
num_suites_
-
failed_suites_
.
size
(),
// {2}
num_suites_
,
// {3}
total_stats_
.
passed
,
// {4}
total_stats_
.
total
(),
// {5}
total_stats_
.
failed
>
0
?
"failed"
:
"passed"
);
// {6}
if
(
!
failed_suites_
.
empty
())
{
format_to
(
colored
(),
" $B(Failed Suites):
\n
"
);
for
(
auto
name
:
failed_suites_
)
format_to
(
colored
(),
" - $R({})
\n
"
,
name
);
}
std
::
cout
<<
std
::
endl
;
}
void
begin_suite
(
std
::
string_view
name
)
override
{
failed_tests_
.
clear
();
current_suite_
=
name
;
suite_start_time_
=
clock_type
::
now
();
++
num_suites_
;
suite_stats_
=
{
0
,
0
};
}
void
end_suite
(
std
::
string_view
name
)
override
{
using
detail
::
format_to
;
using
std
::
chrono
::
duration_cast
;
total_stats_
+=
suite_stats_
;
if
(
suite_stats_
.
failed
>
0
)
failed_suites_
.
push_back
(
name
);
else
if
(
level_
<
CAF_LOG_LEVEL_DEBUG
)
return
;
auto
elapsed
=
clock_type
::
now
()
-
suite_start_time_
;
format_to
(
colored
(),
"$B(Suite Summary): $C({0})
\n
"
" $B(Time): $Y({1:.3f}s)
\n
"
" $B(Checks): ${2}({3} / {4})
\n
"
" $B(Status): ${2}({5})
\n
"
,
name
,
// {0}
duration_cast
<
fractional_seconds
>
(
elapsed
).
count
(),
// {1}
suite_stats_
.
failed
>
0
?
'R'
:
'G'
,
// {2}
suite_stats_
.
passed
,
// {3}
suite_stats_
.
total
(),
// {4}
suite_stats_
.
failed
>
0
?
"failed"
:
"passed"
);
// {5}
if
(
!
failed_tests_
.
empty
())
{
format_to
(
colored
(),
" $B(Failed tests):
\n
"
);
for
(
auto
name
:
failed_tests_
)
format_to
(
colored
(),
" - $R({})
\n
"
,
name
);
}
std
::
cout
<<
std
::
endl
;
}
void
begin_test
(
context_ptr
state
,
std
::
string_view
name
)
override
{
live_
=
false
;
test_stats_
=
{
0
,
0
};
current_test_
=
name
;
current_ctx_
=
std
::
move
(
state
);
}
void
end_test
()
override
{
if
(
test_stats_
.
failed
>
0
)
failed_tests_
.
push_back
(
current_test_
);
suite_stats_
+=
test_stats_
;
current_ctx_
.
reset
();
if
(
live_
)
std
::
cout
<<
std
::
endl
;
}
void
set_live
()
{
if
(
live_
)
return
;
using
detail
::
format_to
;
if
(
current_ctx_
==
nullptr
)
CAF_RAISE_ERROR
(
std
::
logic_error
,
"begin_test was not called"
);
format_to
(
colored
(),
"$C(Suite): $0{}
\n
"
,
current_suite_
);
indent_
=
2
;
live_
=
true
;
for
(
auto
*
frame
:
current_ctx_
->
call_stack
)
begin_step
(
frame
);
}
void
begin_step
(
block
*
ptr
)
override
{
using
detail
::
format_to
;
if
(
!
live_
)
return
;
if
(
indent_
>
0
&&
is_extension
(
ptr
->
type
()))
indent_
-=
2
;
format_to
(
colored
(),
"{0:{1}}$C({2}): $0{3}
\n
"
,
' '
,
indent_
,
as_prefix
(
ptr
->
type
()),
ptr
->
description
());
indent_
+=
2
;
}
void
end_step
(
block
*
ptr
)
override
{
if
(
!
live_
)
return
;
if
(
indent_
==
0
)
CAF_RAISE_ERROR
(
"unbalanced (begin|end)_step calls"
);
if
(
!
is_extension
(
ptr
->
type
()))
indent_
-=
2
;
}
void
pass
(
const
detail
::
source_location
&
location
)
override
{
using
detail
::
format_to
;
test_stats_
.
passed
++
;
if
(
level_
<
CAF_LOG_LEVEL_DEBUG
)
return
;
set_live
();
format_to
(
colored
(),
"{0:{1}}$G(pass) $C({2}):$Y({3})$0
\n
"
,
' '
,
indent_
,
location
.
file_name
(),
location
.
line
());
}
void
fail
(
binary_predicate
type
,
std
::
string_view
lhs
,
std
::
string_view
rhs
,
const
detail
::
source_location
&
location
)
override
{
using
detail
::
format_to
;
test_stats_
.
failed
++
;
if
(
level_
<
CAF_LOG_LEVEL_ERROR
)
return
;
set_live
();
format_to
(
colored
(),
"{0:{1}}$R(error): lhs {2} rhs
\n
"
"{0:{1}} loc: $C({3}):$Y({4})$0
\n
"
"{0:{1}} lhs: {5}
\n
"
"{0:{1}} rhs: {6}
\n
"
,
' '
,
indent_
,
str
(
negate
(
type
)),
location
.
file_name
(),
location
.
line
(),
lhs
,
rhs
);
}
void
fail
(
std
::
string_view
arg
,
const
detail
::
source_location
&
location
)
override
{
using
detail
::
format_to
;
test_stats_
.
failed
++
;
if
(
level_
<
CAF_LOG_LEVEL_ERROR
)
return
;
set_live
();
format_to
(
colored
(),
"{0:{1}}$R(error): check failed
\n
"
"{0:{1}} loc: $C({3}):$Y({4})$0
\n
"
"{0:{1}} check: {5}
\n
"
,
' '
,
indent_
,
location
.
file_name
(),
location
.
line
(),
arg
);
}
void
unhandled_exception
(
std
::
string_view
msg
)
override
{
using
detail
::
format_to
;
test_stats_
.
failed
++
;
if
(
level_
<
CAF_LOG_LEVEL_ERROR
)
return
;
set_live
();
if
(
current_ctx_
==
nullptr
||
current_ctx_
->
unwind_stack
.
empty
())
{
format_to
(
colored
(),
"{0:{1}}$R(unhandled exception): abort test run
\n
"
"{0:{1}} loc: $R(unknown)$0
\n
"
"{0:{1}} msg: {2}
\n
"
,
' '
,
indent_
,
msg
);
}
else
{
auto
&
location
=
current_ctx_
->
unwind_stack
.
front
()
->
location
();
format_to
(
colored
(),
"{0:{1}}$R(unhandled exception): abort test run
\n
"
"{0:{1}} loc: in block starting at $C({2}):$Y({3})$0
\n
"
"{0:{1}} msg: {4}
\n
"
,
' '
,
indent_
,
location
.
file_name
(),
location
.
line
(),
msg
);
}
}
void
unhandled_exception
(
std
::
string_view
msg
,
const
detail
::
source_location
&
location
)
override
{
using
detail
::
format_to
;
test_stats_
.
failed
++
;
if
(
level_
<
CAF_LOG_LEVEL_ERROR
)
return
;
set_live
();
format_to
(
colored
(),
"{0:{1}}$R(unhandled exception): abort test run
\n
"
"{0:{1}} loc: $C({2}):$Y({3})$0
\n
"
"{0:{1}} msg: {4}
\n
"
,
' '
,
indent_
,
location
.
file_name
(),
location
.
line
(),
msg
);
}
void
info
(
std
::
string_view
msg
,
const
detail
::
source_location
&
location
)
override
{
using
detail
::
format_to
;
if
(
level_
<
CAF_LOG_LEVEL_INFO
)
return
;
set_live
();
format_to
(
colored
(),
"{0:{1}}$M(info):
\n
"
"{0:{1}} loc: $C({2}):$Y({3})$0
\n
"
"{0:{1}} msg: {4}
\n
"
,
' '
,
indent_
,
location
.
file_name
(),
location
.
line
(),
msg
);
}
void
verbosity
(
unsigned
level
)
override
{
level_
=
level
;
}
stats
test_stats
()
override
{
return
test_stats_
;
}
void
test_stats
(
stats
new_value
)
override
{
test_stats_
=
new_value
;
}
stats
suite_stats
()
override
{
return
suite_stats_
;
}
stats
total_stats
()
override
{
return
total_stats_
;
}
private:
void
print_indent
(
size_t
indent
)
{
for
(
size_t
i
=
0
;
i
<
indent
;
++
i
)
std
::
cout
<<
' '
;
}
void
print_indent
()
{
print_indent
(
indent_
);
}
/// Configures the number of spaces to print before each line.
size_t
indent_
=
0
;
/// Stores statistics for the current test.
stats
test_stats_
=
{
0
,
0
};
/// Stores statistics for the current suite.
stats
suite_stats_
=
{
0
,
0
};
/// Stores statistics for all suites.
stats
total_stats_
=
{
0
,
0
};
/// Counts the number of test suites.
size_t
num_suites_
=
0
;
/// Stores the time point when the test runner started.
time_point
start_time_
;
/// Stores the time point when the current test suite started.
time_point
suite_start_time_
;
/// Configures the verbosity of the reporter.
unsigned
level_
=
CAF_LOG_LEVEL_INFO
;
/// Stores the names of failed test suites.
std
::
vector
<
std
::
string_view
>
failed_suites_
;
/// Stores the names of failed tests in a suite.
std
::
vector
<
std
::
string_view
>
failed_tests_
;
/// Stores whether we render the current test as live. We start off with
/// `false` and switch to `true` as soon as the current test generates any
/// output.
bool
live_
=
false
;
/// Stores the name of the current test suite.
std
::
string_view
current_suite_
;
/// Stores the name of the current test suite.
std
::
string_view
current_test_
;
/// Stores the state for the current test.
context_ptr
current_ctx_
;
};
}
// namespace
std
::
unique_ptr
<
reporter
>
reporter
::
make_default
()
{
return
std
::
make_unique
<
default_reporter
>
();
}
}
// namespace caf::test
libcaf_test/src/test/runnable.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/runnable.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include "caf/test/scenario.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/test.hpp"
namespace
caf
::
test
{
runnable
::~
runnable
()
{
// nop
}
void
runnable
::
run
()
{
switch
(
root_type_
)
{
case
block_type
:
:
scenario
:
if
(
auto
guard
=
ctx_
->
get
<
scenario
>
(
0
,
description_
,
loc_
)
->
commit
())
{
do_run
();
return
;
}
CAF_RAISE_ERROR
(
std
::
logic_error
,
"failed to select the root block for the scenario"
);
break
;
case
block_type
:
:
test
:
if
(
auto
guard
=
ctx_
->
get
<
test
>
(
0
,
description_
,
loc_
)
->
commit
())
{
do_run
();
return
;
}
CAF_RAISE_ERROR
(
std
::
logic_error
,
"failed to select the root block for the test"
);
break
;
default:
CAF_RAISE_ERROR
(
std
::
logic_error
,
"invalid root type"
);
}
}
void
runnable
::
check
(
bool
value
,
const
detail
::
source_location
&
location
)
{
if
(
value
)
{
reporter
::
instance
->
pass
(
location
);
return
;
}
reporter
::
instance
->
fail
(
"should be true"
,
location
);
}
block
&
runnable
::
current_block
()
{
if
(
ctx_
->
call_stack
.
empty
())
CAF_RAISE_ERROR
(
std
::
logic_error
,
"no current block"
);
return
*
ctx_
->
call_stack
.
back
();
}
}
// namespace caf::test
libcaf_test/src/test/runner.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/runner.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_option_set.hpp"
#include "caf/detail/log_level.hpp"
#include "caf/settings.hpp"
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
#include "caf/test/reporter.hpp"
#include "caf/test/runnable.hpp"
#include <iostream>
#include <optional>
#include <string>
namespace
caf
::
test
{
namespace
{
config_option_set
make_option_set
()
{
config_option_set
result
;
config_option_adder
{
result
,
"global"
}
.
add
<
bool
>
(
"help,h?"
,
"print this help text"
)
.
add
<
bool
>
(
"available-suites,a"
,
"print all available suites"
)
.
add
<
std
::
string
>
(
"available-tests,A"
,
"print tests for a suite"
)
.
add
<
std
::
string
>
(
"verbosity,v"
,
"set verbosity level of the reporter"
);
return
result
;
}
std
::
optional
<
unsigned
>
parse_log_level
(
std
::
string_view
x
)
{
if
(
x
==
"quiet"
)
return
CAF_LOG_LEVEL_QUIET
;
if
(
x
==
"error"
)
return
CAF_LOG_LEVEL_ERROR
;
if
(
x
==
"warning"
)
return
CAF_LOG_LEVEL_WARNING
;
if
(
x
==
"info"
)
return
CAF_LOG_LEVEL_INFO
;
if
(
x
==
"debug"
)
return
CAF_LOG_LEVEL_DEBUG
;
if
(
x
==
"trace"
)
return
CAF_LOG_LEVEL_TRACE
;
return
{};
}
}
//
runner
::
runner
()
:
suites_
(
caf
::
test
::
registry
::
suites
())
{
// nop
}
int
runner
::
run
(
int
argc
,
char
**
argv
)
{
auto
default_reporter
=
reporter
::
make_default
();
reporter
::
instance
=
default_reporter
.
get
();
if
(
auto
[
ok
,
help_printed
]
=
parse_cli
(
argc
,
argv
);
!
ok
)
{
return
EXIT_FAILURE
;
}
else
if
(
help_printed
)
{
return
EXIT_SUCCESS
;
}
default_reporter
->
start
();
for
(
auto
&
[
suite_name
,
suite
]
:
suites_
)
{
default_reporter
->
begin_suite
(
suite_name
);
for
(
auto
[
test_name
,
factory_instance
]
:
suite
)
{
auto
state
=
std
::
make_shared
<
context
>
();
try
{
do
{
default_reporter
->
begin_test
(
state
,
test_name
);
auto
def
=
factory_instance
->
make
(
state
);
def
->
run
();
default_reporter
->
end_test
();
state
->
clear_stacks
();
}
while
(
state
->
can_run
());
}
catch
(
const
nesting_error
&
ex
)
{
default_reporter
->
unhandled_exception
(
ex
.
message
(),
ex
.
location
());
default_reporter
->
end_test
();
}
catch
(
const
std
::
exception
&
ex
)
{
default_reporter
->
unhandled_exception
(
ex
.
what
());
default_reporter
->
end_test
();
}
catch
(...)
{
default_reporter
->
unhandled_exception
(
"unknown exception type"
);
default_reporter
->
end_test
();
}
}
default_reporter
->
end_suite
(
suite_name
);
}
default_reporter
->
stop
();
return
default_reporter
->
success
()
>
0
?
EXIT_SUCCESS
:
EXIT_FAILURE
;
}
runner
::
parse_cli_result
runner
::
parse_cli
(
int
argc
,
char
**
argv
)
{
using
detail
::
format_to
;
caf
::
settings
cfg
;
std
::
vector
<
std
::
string
>
args_cpy
{
argv
+
1
,
argv
+
argc
};
auto
options
=
make_option_set
();
auto
res
=
options
.
parse
(
cfg
,
args_cpy
);
auto
err
=
std
::
ostream_iterator
<
char
>
{
std
::
cerr
};
if
(
res
.
first
!=
caf
::
pec
::
success
)
{
format_to
(
err
,
"error while parsing argument '{}': {}
\n\n
{}
\n
"
,
*
res
.
second
,
to_string
(
res
.
first
),
options
.
help_text
());
return
{
false
,
true
};
}
if
(
get_or
(
cfg
,
"help"
,
false
))
{
format_to
(
err
,
"{}
\n
"
,
options
.
help_text
());
return
{
true
,
true
};
}
if
(
get_or
(
cfg
,
"available-suites"
,
false
))
{
format_to
(
err
,
"available suites:
\n
"
);
for
(
auto
&
[
suite_name
,
suite
]
:
suites_
)
format_to
(
err
,
"- {}
\n
"
,
suite_name
);
return
{
true
,
true
};
}
if
(
auto
suite_name
=
get_as
<
std
::
string
>
(
cfg
,
"available-tests"
))
{
auto
i
=
suites_
.
find
(
*
suite_name
);
if
(
i
==
suites_
.
end
())
{
format_to
(
err
,
"no such suite: {}
\n
"
,
*
suite_name
);
return
{
false
,
true
};
}
format_to
(
err
,
"available tests in suite {}:
\n
"
,
i
->
first
);
for
(
const
auto
&
[
test_name
,
factory_instance
]
:
i
->
second
)
format_to
(
err
,
"- {}
\n
"
,
test_name
);
return
{
true
,
true
};
}
if
(
auto
verbosity
=
get_as
<
std
::
string
>
(
cfg
,
"verbosity"
))
{
auto
level
=
parse_log_level
(
*
verbosity
);
if
(
!
level
)
{
format_to
(
err
,
"unrecognized verbosity level: '{}'
\n
"
"expected one of:
\n
"
"- quiet
\n
"
"- error
\n
"
"- warning
\n
"
"- info
\n
"
"- debug
\n
"
"- trace
\n
"
,
*
verbosity
);
return
{
false
,
true
};
}
reporter
::
instance
->
verbosity
(
*
level
);
}
return
{
true
,
false
};
}
}
// namespace caf::test
libcaf_test/src/test/scenario.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/scenario.hpp"
#include "caf/test/and_given.hpp"
#include "caf/test/and_when.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include "caf/test/given.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/when.hpp"
namespace
caf
::
test
{
block_type
scenario
::
type
()
const
noexcept
{
return
block_type
::
scenario
;
}
given
*
scenario
::
get_given
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
given
>
(
id
,
description
,
loc
);
}
and_given
*
scenario
::
get_and_given
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
and_given
>
(
id
,
description
,
loc
);
}
when
*
scenario
::
get_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
when
>
(
id
,
description
,
loc
);
}
and_when
*
scenario
::
get_and_when
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
and_when
>
(
id
,
description
,
loc
);
}
scope
scenario
::
commit
()
{
if
(
!
ctx_
->
active
()
||
!
can_run
())
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/scope.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/scope.hpp"
#include "caf/test/block.hpp"
namespace
caf
::
test
{
scope
&
scope
::
operator
=
(
scope
&&
other
)
noexcept
{
if
(
ptr_
)
ptr_
->
leave
();
ptr_
=
other
.
release
();
return
*
this
;
}
scope
::~
scope
()
{
if
(
ptr_
)
ptr_
->
leave
();
}
void
scope
::
leave
()
{
if
(
ptr_
)
{
ptr_
->
on_leave
();
ptr_
->
leave
();
ptr_
=
nullptr
;
}
}
}
// namespace caf::test
libcaf_test/src/test/section.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/section.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include "caf/test/scope.hpp"
namespace
caf
::
test
{
block_type
section
::
type
()
const
noexcept
{
return
block_type
::
section
;
}
section
*
section
::
get_section
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
section
>
(
id
,
description
,
loc
);
}
scope
section
::
commit
()
{
if
(
!
ctx_
->
active
()
||
!
can_run
())
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/test.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/test.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/section.hpp"
namespace
caf
::
test
{
block_type
test
::
type
()
const
noexcept
{
return
block_type
::
test
;
}
section
*
test
::
get_section
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
return
get_nested
<
section
>
(
id
,
description
,
loc
);
}
scope
test
::
commit
()
{
if
(
!
ctx_
->
active
()
||
!
can_run
())
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/then.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/then.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/context.hpp"
#include "caf/test/scope.hpp"
namespace
caf
::
test
{
block_type
then
::
type
()
const
noexcept
{
return
block_type
::
then
;
}
scope
then
::
commit
()
{
if
(
!
ctx_
->
active
()
||
executed_
)
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/src/test/when.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/when.hpp"
#include "caf/test/and_then.hpp"
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
#include "caf/test/scope.hpp"
#include "caf/test/then.hpp"
namespace
caf
::
test
{
then
*
when
::
get_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
*
result
=
ctx_
->
get
<
then
>
(
id
,
description
,
loc
);
if
(
nested_
.
empty
())
{
nested_
.
emplace_back
(
result
);
}
else
if
(
nested_
.
front
()
!=
result
)
{
nesting_error
::
raise_too_many
(
type
(),
block_type
::
then
,
loc
);
}
return
result
;
}
and_then
*
when
::
get_and_then
(
int
id
,
std
::
string_view
description
,
const
detail
::
source_location
&
loc
)
{
auto
*
result
=
ctx_
->
get
<
and_then
>
(
id
,
description
,
loc
);
if
(
nested_
.
empty
())
{
nesting_error
::
raise_invalid_sequence
(
block_type
::
then
,
block_type
::
and_then
,
loc
);
}
nested_
.
push_back
(
result
);
return
result
;
}
block_type
when
::
type
()
const
noexcept
{
return
block_type
::
when
;
}
scope
when
::
commit
()
{
if
(
!
ctx_
->
active
()
||
!
can_run
())
return
{};
enter
();
return
scope
{
this
};
}
}
// namespace caf::test
libcaf_test/
src/caf
-test.cpp
→
libcaf_test/
test/test
-test.cpp
View file @
7a77a40e
...
...
@@ -2,4 +2,6 @@
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/unit_test_impl.hpp"
#include "caf/test/caf_test_main.hpp"
CAF_TEST_MAIN
()
libcaf_test/test/test/block_type.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/block_type.hpp"
#include "caf/test/test.hpp"
using
caf
::
test
::
block_type
;
SUITE
(
"caf.test.block_type"
)
{
TEST
(
"is_extension checks whether a block type needs a predecessor"
)
{
using
caf
::
test
::
is_extension
;
SECTION
(
"is_extension is true for all AND_* types and BUT"
)
{
check
(
is_extension
(
block_type
::
and_given
));
check
(
is_extension
(
block_type
::
and_when
));
check
(
is_extension
(
block_type
::
and_then
));
check
(
is_extension
(
block_type
::
but
));
}
SECTION
(
"is_extension is false for regular types"
)
{
check
(
!
is_extension
(
block_type
::
test
));
check
(
!
is_extension
(
block_type
::
section
));
check
(
!
is_extension
(
block_type
::
scenario
));
check
(
!
is_extension
(
block_type
::
given
));
check
(
!
is_extension
(
block_type
::
when
));
check
(
!
is_extension
(
block_type
::
then
));
}
}
}
// SUITE("caf.test.block_type")
libcaf_test/test/test/scenario.cpp
0 → 100644
View file @
7a77a40e
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/scenario.hpp"
#include "caf/config.hpp"
#include "caf/test/nesting_error.hpp"
#include "caf/test/test.hpp"
SUITE
(
"caf.test.scenario"
)
{
#ifdef CAF_ENABLE_EXCEPTIONS
SCENARIO
(
"a scenario may not contain a section"
)
{
auto
entered_section
=
false
;
check_throws
<
caf
::
test
::
nesting_error
>
([
this
,
&
entered_section
]
{
GIVEN
(
"given-1"
)
{
WHEN
(
"when-1"
)
{
SECTION
(
"nesting error"
)
{
entered_section
=
true
;
}
}
}
});
check
(
!
entered_section
);
}
#endif
SCENARIO
(
"each run starts with fresh local variables"
)
{
GIVEN
(
"a my_int variable"
)
{
auto
my_int
=
0
;
WHEN
(
"entering a WHEN block"
)
{
THEN
(
"the local variable has its default value"
)
{
check_eq
(
my_int
,
0
);
my_int
=
42
;
check_eq
(
my_int
,
42
);
}
}
WHEN
(
"entering another WHEN block"
)
{
THEN
(
"previous writes to the local variable are gone"
)
{
check_eq
(
my_int
,
0
);
}
}
}
}
struct
int_fixture
{
int
my_int
=
0
;
};
WITH_FIXTURE
(
int_fixture
)
{
SCENARIO
(
"each run starts with a fresh fixture"
)
{
GIVEN
(
"a fixture with a my_int member variable"
)
{
WHEN
(
"entering a WHEN block"
)
{
THEN
(
"the fixture is default-constructed"
)
{
check_eq
(
my_int
,
0
);
my_int
=
42
;
check_eq
(
my_int
,
42
);
}
}
WHEN
(
"entering another WHEN block"
)
{
THEN
(
"previous writes to the fixture are gone"
)
{
check_eq
(
my_int
,
0
);
}
}
}
}
}
// WITH_FIXTURE(int_fixture)
SCENARIO
(
"scenario-1"
)
{
auto
render
=
[
this
]()
->
std
::
string
{
if
(
ctx_
->
call_stack
.
empty
())
return
"nil"
;
std
::
string
result
;
auto
i
=
ctx_
->
call_stack
.
begin
();
result
+=
(
*
i
++
)
->
description
();
while
(
i
!=
ctx_
->
call_stack
.
end
())
{
result
+=
"/"
;
result
+=
(
*
i
++
)
->
description
();
}
return
result
;
};
auto
counter
=
0
;
check_eq
(
render
(),
"scenario-1"
);
GIVEN
(
"given-1"
)
{
check_eq
(
++
counter
,
1
);
check_eq
(
render
(),
"scenario-1/given-1"
);
WHEN
(
"when-1"
)
{
check_eq
(
++
counter
,
2
);
check_eq
(
render
(),
"scenario-1/given-1/when-1"
);
THEN
(
"then-1"
)
{
check_eq
(
++
counter
,
3
);
check_eq
(
render
(),
"scenario-1/given-1/when-1/then-1"
);
}
AND_THEN
(
"and-then-1"
)
{
check_eq
(
++
counter
,
4
);
check_eq
(
render
(),
"scenario-1/given-1/when-1/and-then-1"
);
}
AND_THEN
(
"and-then-2"
)
{
check_eq
(
++
counter
,
5
);
check_eq
(
render
(),
"scenario-1/given-1/when-1/and-then-2"
);
}
}
AND_WHEN
(
"and-when-1-1"
)
{
check_eq
(
++
counter
,
6
);
check_eq
(
render
(),
"scenario-1/given-1/and-when-1-1"
);
}
AND_WHEN
(
"and-when-1-2"
)
{
check_eq
(
++
counter
,
7
);
check_eq
(
render
(),
"scenario-1/given-1/and-when-1-2"
);
}
WHEN
(
"when-2"
)
{
check_eq
(
++
counter
,
2
);
check_eq
(
render
(),
"scenario-1/given-1/when-2"
);
THEN
(
"then-1"
)
{
check_eq
(
++
counter
,
3
);
check_eq
(
render
(),
"scenario-1/given-1/when-2/then-1"
);
}
AND_THEN
(
"and-then-1"
)
{
check_eq
(
++
counter
,
4
);
check_eq
(
render
(),
"scenario-1/given-1/when-2/and-then-1"
);
}
AND_THEN
(
"and-then-2"
)
{
check_eq
(
++
counter
,
5
);
check_eq
(
render
(),
"scenario-1/given-1/when-2/and-then-2"
);
}
}
AND_WHEN
(
"and-when-2-1"
)
{
check_eq
(
++
counter
,
6
);
check_eq
(
render
(),
"scenario-1/given-1/and-when-2-1"
);
}
AND_WHEN
(
"and-when-2-2"
)
{
check_eq
(
++
counter
,
7
);
check_eq
(
render
(),
"scenario-1/given-1/and-when-2-2"
);
}
}
}
}
// SUITE("caf.test.scenario")
libcaf_test/test/test/test.cpp
0 → 100644
View file @
7a77a40e
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/test.hpp"
using
caf
::
test
::
block_type
;
SUITE
(
"caf.test.test"
)
{
TEST
(
"tests can contain checks"
)
{
auto
*
rep
=
caf
::
test
::
reporter
::
instance
;
for
(
int
i
=
0
;
i
<
3
;
++
i
)
check_eq
(
i
,
i
);
auto
stats
=
rep
->
test_stats
();
check_eq
(
stats
.
passed
,
3u
);
check_eq
(
stats
.
failed
,
0u
);
info
(
"this test had {} checks"
,
rep
->
test_stats
().
total
());
}
TEST
(
"failed checks increment the failed counter"
)
{
check_eq
(
1
,
2
);
auto
stats
=
caf
::
test
::
reporter
::
instance
->
test_stats
();
check_eq
(
stats
.
passed
,
0u
);
check_eq
(
stats
.
failed
,
1u
);
info
(
"reset error count to not fail the test"
);
caf
::
test
::
reporter
::
instance
->
test_stats
({
2
,
0
});
}
TEST
(
"each run starts with fresh local variables"
)
{
auto
my_int
=
0
;
SECTION
(
"block 1 reads my_int as 0"
)
{
check_eq
(
my_int
,
0
);
my_int
=
42
;
check_eq
(
my_int
,
42
);
}
SECTION
(
"block 2 also reads my_int as 0"
)
{
check_eq
(
my_int
,
0
);
}
}
struct
int_fixture
{
int
my_int
=
0
;
};
WITH_FIXTURE
(
int_fixture
)
{
TEST
(
"each run starts with a fresh fixture"
)
{
SECTION
(
"block 1 reads my_int as 0"
)
{
check_eq
(
my_int
,
0
);
my_int
=
42
;
check_eq
(
my_int
,
42
);
}
SECTION
(
"block 2 also reads my_int as 0"
)
{
check_eq
(
my_int
,
0
);
}
}
}
// WITH_FIXTURE(int_fixture)
}
// SUITE("caf.test.test")
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment