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
cd8363aa
Commit
cd8363aa
authored
Aug 10, 2023
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1518
parents
c677bf5b
faaaf3d5
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
1332 additions
and
73 deletions
+1332
-73
CHANGELOG.md
CHANGELOG.md
+3
-1
libcaf_core/caf/actor_system.cpp
libcaf_core/caf/actor_system.cpp
+4
-0
libcaf_core/caf/actor_system.hpp
libcaf_core/caf/actor_system.hpp
+9
-0
libcaf_core/caf/actor_system_config.cpp
libcaf_core/caf/actor_system_config.cpp
+4
-0
libcaf_core/caf/actor_system_config.hpp
libcaf_core/caf/actor_system_config.hpp
+6
-0
libcaf_core/caf/detail/type_traits.hpp
libcaf_core/caf/detail/type_traits.hpp
+3
-0
libcaf_core/caf/intrusive_ptr.hpp
libcaf_core/caf/intrusive_ptr.hpp
+16
-12
libcaf_core/caf/scheduled_actor.hpp
libcaf_core/caf/scheduled_actor.hpp
+5
-0
libcaf_test/CMakeLists.txt
libcaf_test/CMakeLists.txt
+2
-0
libcaf_test/caf/test/context.cpp
libcaf_test/caf/test/context.cpp
+2
-2
libcaf_test/caf/test/fixture/deterministic.cpp
libcaf_test/caf/test/fixture/deterministic.cpp
+324
-0
libcaf_test/caf/test/fixture/deterministic.hpp
libcaf_test/caf/test/fixture/deterministic.hpp
+443
-0
libcaf_test/caf/test/fixture/deterministic.test.cpp
libcaf_test/caf/test/fixture/deterministic.test.cpp
+346
-0
libcaf_test/caf/test/reporter.cpp
libcaf_test/caf/test/reporter.cpp
+17
-5
libcaf_test/caf/test/reporter.hpp
libcaf_test/caf/test/reporter.hpp
+7
-3
libcaf_test/caf/test/runnable.cpp
libcaf_test/caf/test/runnable.cpp
+19
-2
libcaf_test/caf/test/runnable.hpp
libcaf_test/caf/test/runnable.hpp
+115
-41
libcaf_test/caf/test/runner.cpp
libcaf_test/caf/test/runner.cpp
+2
-2
libcaf_test/caf/test/test.test.cpp
libcaf_test/caf/test/test.test.cpp
+5
-5
No files found.
CHANGELOG.md
View file @
cd8363aa
...
...
@@ -52,6 +52,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
`on_complete`
on its subscriber on the first activity of the source
observable. Previously, the created observable would never reach its threshold
and attempt to buffer all values indefinitely.
-
The comparison operator of
`intrusive_ptr`
no longer accidentally creates new
`intrusive_ptr`
instances when comparing to raw pointers.
## [0.19.2] - 2023-06-13
...
...
libcaf_core/caf/actor_system.cpp
View file @
cd8363aa
...
...
@@ -541,4 +541,8 @@ void actor_system::release_private_thread(detail::private_thread* ptr) {
private_threads_
.
release
(
ptr
);
}
detail
::
mailbox_factory
*
actor_system
::
mailbox_factory
()
{
return
cfg_
.
mailbox_factory
();
}
}
// namespace caf
libcaf_core/caf/actor_system.hpp
View file @
cd8363aa
...
...
@@ -398,6 +398,7 @@ public:
infer_handle_from_class_t
<
C
>
spawn
(
Ts
&&
...
xs
)
{
check_invariants
<
C
>
();
actor_config
cfg
;
cfg
.
mbox_factory
=
mailbox_factory
();
return
spawn_impl
<
C
,
Os
>
(
cfg
,
detail
::
spawn_fwd
<
Ts
>
(
xs
)...);
}
...
...
@@ -436,6 +437,7 @@ public:
static_assert
(
spawnable
,
"cannot spawn function-based actor with given arguments"
);
actor_config
cfg
;
cfg
.
mbox_factory
=
mailbox_factory
();
return
spawn_functor
<
Os
>
(
detail
::
bool_token
<
spawnable
>
{},
cfg
,
fun
,
std
::
forward
<
Ts
>
(
xs
)...);
}
...
...
@@ -500,6 +502,7 @@ public:
infer_handle_from_fun_t
<
F
>
spawn_in_groups
(
std
::
initializer_list
<
group
>
gs
,
F
fun
,
Ts
&&
...
xs
)
{
actor_config
cfg
;
cfg
.
mbox_factory
=
mailbox_factory
();
return
spawn_fun_in_groups
<
Os
>
(
cfg
,
gs
.
begin
(),
gs
.
end
(),
fun
,
std
::
forward
<
Ts
>
(
xs
)...);
}
...
...
@@ -508,6 +511,7 @@ public:
template
<
spawn_options
Os
=
no_spawn_options
,
class
Gs
,
class
F
,
class
...
Ts
>
infer_handle_from_fun_t
<
F
>
spawn_in_groups
(
const
Gs
&
gs
,
F
fun
,
Ts
&&
...
xs
)
{
actor_config
cfg
;
cfg
.
mbox_factory
=
mailbox_factory
();
return
spawn_fun_in_groups
<
Os
>
(
cfg
,
gs
.
begin
(),
gs
.
end
(),
fun
,
std
::
forward
<
Ts
>
(
xs
)...);
}
...
...
@@ -524,6 +528,7 @@ public:
infer_handle_from_class_t
<
T
>
spawn_in_groups
(
std
::
initializer_list
<
group
>
gs
,
Ts
&&
...
xs
)
{
actor_config
cfg
;
cfg
.
mbox_factory
=
mailbox_factory
();
return
spawn_class_in_groups
<
T
,
Os
>
(
cfg
,
gs
.
begin
(),
gs
.
end
(),
std
::
forward
<
Ts
>
(
xs
)...);
}
...
...
@@ -532,6 +537,7 @@ public:
template
<
class
T
,
spawn_options
Os
=
no_spawn_options
,
class
Gs
,
class
...
Ts
>
infer_handle_from_class_t
<
T
>
spawn_in_groups
(
const
Gs
&
gs
,
Ts
&&
...
xs
)
{
actor_config
cfg
;
cfg
.
mbox_factory
=
mailbox_factory
();
return
spawn_class_in_groups
<
T
,
Os
>
(
cfg
,
gs
.
begin
(),
gs
.
end
(),
std
::
forward
<
Ts
>
(
xs
)...);
}
...
...
@@ -696,6 +702,7 @@ public:
"only scheduled actors may get spawned inactively"
);
CAF_SET_LOGGER_SYS
(
this
);
actor_config
cfg
{
dummy_execution_unit
(),
nullptr
};
cfg
.
mbox_factory
=
mailbox_factory
();
auto
res
=
make_actor
<
Impl
>
(
next_actor_id
(),
node
(),
this
,
cfg
,
std
::
forward
<
Ts
>
(
xs
)...);
auto
ptr
=
static_cast
<
Impl
*>
(
actor_cast
<
abstract_actor
*>
(
res
));
...
...
@@ -790,6 +797,8 @@ private:
config_serv_
=
std
::
move
(
x
);
}
detail
::
mailbox_factory
*
mailbox_factory
();
// -- member variables -------------------------------------------------------
/// Provides system-wide callbacks for several actor operations.
...
...
libcaf_core/caf/actor_system_config.cpp
View file @
cd8363aa
...
...
@@ -478,6 +478,10 @@ actor_system_config::extract_config_file_path(string_list& args) {
}
}
detail
::
mailbox_factory
*
actor_system_config
::
mailbox_factory
()
{
return
nullptr
;
}
const
settings
&
content
(
const
actor_system_config
&
cfg
)
{
return
cfg
.
content
;
}
...
...
libcaf_core/caf/actor_system_config.hpp
View file @
cd8363aa
...
...
@@ -32,6 +32,10 @@ namespace caf {
/// Configures an `actor_system` on startup.
class
CAF_CORE_EXPORT
actor_system_config
{
public:
// -- friends ----------------------------------------------------------------
friend
class
actor_system
;
// -- member types -----------------------------------------------------------
using
hook_factory
=
std
::
function
<
io
::
hook
*
(
actor_system
&
)
>
;
...
...
@@ -303,6 +307,8 @@ protected:
config_option_set
custom_options_
;
private:
virtual
detail
::
mailbox_factory
*
mailbox_factory
();
void
set_remainder
(
string_list
args
);
mutable
std
::
vector
<
char
*>
c_args_remainder_
;
...
...
libcaf_core/caf/detail/type_traits.hpp
View file @
cd8363aa
...
...
@@ -175,6 +175,9 @@ public:
static
constexpr
bool
value
=
std
::
is_same_v
<
bool
,
result_type
>
;
};
template
<
class
T1
,
class
T2
>
constexpr
bool
is_comparable_v
=
is_comparable
<
T1
,
T2
>::
value
;
/// Checks whether `T` behaves like a forward iterator.
template
<
class
T
>
class
is_forward_iterator
{
...
...
libcaf_core/caf/intrusive_ptr.hpp
View file @
cd8363aa
...
...
@@ -231,27 +231,31 @@ bool operator!=(std::nullptr_t, const intrusive_ptr<T>& x) {
// -- comparison to raw pointer ------------------------------------------------
/// @relates intrusive_ptr
template
<
class
T
>
bool
operator
==
(
const
intrusive_ptr
<
T
>&
x
,
const
T
*
y
)
{
return
x
.
get
()
==
y
;
template
<
class
T
,
class
U
>
detail
::
enable_if_t
<
detail
::
is_comparable
<
T
*
,
U
*>::
value
,
bool
>
operator
==
(
const
intrusive_ptr
<
T
>&
lhs
,
const
U
*
rhs
)
{
return
lhs
.
get
()
==
rhs
;
}
/// @relates intrusive_ptr
template
<
class
T
>
bool
operator
==
(
const
T
*
x
,
const
intrusive_ptr
<
T
>&
y
)
{
return
x
==
y
.
get
();
template
<
class
T
,
class
U
>
detail
::
enable_if_t
<
detail
::
is_comparable
<
T
*
,
U
*>::
value
,
bool
>
operator
==
(
const
T
*
lhs
,
const
intrusive_ptr
<
U
>&
rhs
)
{
return
lhs
==
rhs
.
get
();
}
/// @relates intrusive_ptr
template
<
class
T
>
bool
operator
!=
(
const
intrusive_ptr
<
T
>&
x
,
const
T
*
y
)
{
return
x
.
get
()
!=
y
;
template
<
class
T
,
class
U
>
detail
::
enable_if_t
<
detail
::
is_comparable
<
T
*
,
U
*>::
value
,
bool
>
operator
!=
(
const
intrusive_ptr
<
T
>&
lhs
,
const
U
*
rhs
)
{
return
lhs
.
get
()
!=
rhs
;
}
/// @relates intrusive_ptr
template
<
class
T
>
bool
operator
!=
(
const
T
*
x
,
const
intrusive_ptr
<
T
>&
y
)
{
return
x
!=
y
.
get
();
template
<
class
T
,
class
U
>
detail
::
enable_if_t
<
detail
::
is_comparable
<
T
*
,
U
*>::
value
,
bool
>
operator
!=
(
const
T
*
lhs
,
const
intrusive_ptr
<
U
>&
rhs
)
{
return
lhs
!=
rhs
.
get
();
}
// -- comparison to intrusive_pointer ------------------------------------------
...
...
libcaf_core/caf/scheduled_actor.hpp
View file @
cd8363aa
...
...
@@ -241,6 +241,11 @@ public:
return
*
mailbox_
;
}
/// Checks whether this actor is fully initialized.
bool
initialized
()
const
noexcept
{
return
getf
(
is_initialized_flag
);
}
// -- event handlers ---------------------------------------------------------
/// Sets a custom handler for unexpected messages.
...
...
libcaf_test/CMakeLists.txt
View file @
cd8363aa
...
...
@@ -22,6 +22,8 @@ caf_add_component(
caf/test/block.cpp
caf/test/context.cpp
caf/test/factory.cpp
caf/test/fixture/deterministic.cpp
caf/test/fixture/deterministic.test.cpp
caf/test/fixture/flow.cpp
caf/test/fixture/flow.test.cpp
caf/test/given.cpp
...
...
libcaf_test/caf/test/context.cpp
View file @
cd8363aa
...
...
@@ -15,13 +15,13 @@ void context::on_enter(block* ptr) {
call_stack
.
push_back
(
ptr
);
unwind_stack
.
clear
();
path
.
push_back
(
ptr
);
reporter
::
instance
->
begin_step
(
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
);
reporter
::
instance
().
end_step
(
ptr
);
}
bool
context
::
activated
(
block
*
ptr
)
const
noexcept
{
...
...
libcaf_test/caf/test/fixture/deterministic.cpp
0 → 100644
View file @
cd8363aa
// 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/fixture/deterministic.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/detail/mailbox_factory.hpp"
#include "caf/detail/source_location.hpp"
#include "caf/detail/sync_request_bouncer.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/scheduled_actor.hpp"
namespace
caf
::
test
::
fixture
{
// -- mailbox ------------------------------------------------------------------
class
deterministic
::
mailbox_impl
:
public
ref_counted
,
public
abstract_mailbox
{
public:
mailbox_impl
(
deterministic
*
fix
,
scheduled_actor
*
owner
)
:
fix_
(
fix
),
owner_
(
owner
)
{
}
intrusive
::
inbox_result
push_back
(
mailbox_element_ptr
ptr
)
override
{
if
(
closed_
)
{
detail
::
sync_request_bouncer
bouncer
{
close_reason_
};
bouncer
(
*
ptr
);
return
intrusive
::
inbox_result
::
queue_closed
;
}
using
event_t
=
deterministic
::
scheduling_event
;
auto
unblocked
=
fix_
->
mail_count
(
owner_
)
==
0
;
auto
event
=
std
::
make_unique
<
event_t
>
(
owner_
,
std
::
move
(
ptr
));
fix_
->
events_
.
push_back
(
std
::
move
(
event
));
return
unblocked
?
intrusive
::
inbox_result
::
unblocked_reader
:
intrusive
::
inbox_result
::
success
;
}
void
push_front
(
mailbox_element_ptr
ptr
)
override
{
using
event_t
=
deterministic
::
scheduling_event
;
auto
event
=
std
::
make_unique
<
event_t
>
(
owner_
,
std
::
move
(
ptr
));
fix_
->
events_
.
emplace_front
(
std
::
move
(
event
));
}
mailbox_element_ptr
pop_front
()
override
{
return
fix_
->
pop_msg_impl
(
owner_
);
}
bool
closed
()
const
noexcept
override
{
return
closed_
;
}
bool
blocked
()
const
noexcept
override
{
return
blocked_
;
}
bool
try_block
()
override
{
blocked_
=
true
;
return
true
;
}
bool
try_unblock
()
override
{
if
(
!
blocked_
)
return
false
;
blocked_
=
false
;
return
true
;
}
size_t
close
(
const
error
&
reason
)
override
{
closed_
=
true
;
close_reason_
=
reason
;
auto
result
=
size_t
{
0
};
auto
envelope
=
fix_
->
pop_msg_impl
(
owner_
);
while
(
envelope
!=
nullptr
)
{
detail
::
sync_request_bouncer
bouncer
{
reason
};
++
result
;
envelope
=
fix_
->
pop_msg_impl
(
owner_
);
}
return
result
;
}
size_t
size
()
override
{
return
fix_
->
mail_count
(
owner_
);
}
void
ref_mailbox
()
noexcept
override
{
ref
();
}
void
deref_mailbox
()
noexcept
override
{
deref
();
}
mailbox_element
*
peek
(
message_id
)
override
{
// Note: this function only exists for backwards compatibility with the old
// unit testing framework. It is not used by the new test runner and thus
// not implemented.
CAF_RAISE_ERROR
(
std
::
logic_error
,
"peek not supported by this mailbox"
);
}
private:
bool
blocked_
=
false
;
bool
closed_
=
false
;
error
close_reason_
;
deterministic
*
fix_
;
scheduled_actor
*
owner_
;
};
class
deterministic
::
mailbox_factory_impl
:
public
detail
::
mailbox_factory
{
public:
explicit
mailbox_factory_impl
(
deterministic
*
fix
)
:
fix_
(
fix
)
{
// nop
}
abstract_mailbox
*
make
(
scheduled_actor
*
owner
)
override
{
return
new
deterministic
::
mailbox_impl
(
fix_
,
owner
);
}
abstract_mailbox
*
make
(
blocking_actor
*
)
override
{
return
nullptr
;
}
private:
deterministic
*
fix_
;
};
// -- scheduler ----------------------------------------------------------------
class
deterministic
::
scheduler_impl
:
public
scheduler
::
abstract_coordinator
{
public:
using
super
=
caf
::
scheduler
::
abstract_coordinator
;
scheduler_impl
(
actor_system
&
sys
,
deterministic
*
fix
)
:
super
(
sys
),
fix_
(
fix
)
{
// nop
}
bool
detaches_utility_actors
()
const
override
{
return
false
;
}
detail
::
test_actor_clock
&
clock
()
noexcept
override
{
return
clock_
;
}
protected:
void
start
()
override
{
// nop
}
void
stop
()
override
{
fix_
->
drop_events
();
}
void
enqueue
(
resumable
*
ptr
)
override
{
using
event_t
=
deterministic
::
scheduling_event
;
using
subtype_t
=
resumable
::
subtype_t
;
switch
(
ptr
->
subtype
())
{
case
subtype_t
:
:
scheduled_actor
:
case
subtype_t
:
:
io_actor
:
{
// Actors put their messages into events_ directly. However, we do run
// them right away if they aren't initialized yet.
auto
dptr
=
static_cast
<
scheduled_actor
*>
(
ptr
);
if
(
!
dptr
->
initialized
())
dptr
->
resume
(
system_
.
dummy_execution_unit
(),
0
);
break
;
}
default:
fix_
->
events_
.
push_back
(
std
::
make_unique
<
event_t
>
(
ptr
,
nullptr
));
}
// Before calling this function, CAF *always* bumps the reference count.
// Hence, we need to release one reference count here.
intrusive_ptr_release
(
ptr
);
}
private:
/// The fixture this scheduler belongs to.
deterministic
*
fix_
;
/// Allows users to fake time at will.
detail
::
test_actor_clock
clock_
;
};
// -- config -------------------------------------------------------------------
deterministic
::
config
::
config
(
deterministic
*
fix
)
{
factory_
=
std
::
make_unique
<
mailbox_factory_impl
>
(
fix
);
module_factories
.
push_back
([
fix
](
actor_system
&
sys
)
->
actor_system
::
module
*
{
return
new
scheduler_impl
(
sys
,
fix
);
});
}
deterministic
::
config
::~
config
()
{
// nop
}
detail
::
mailbox_factory
*
deterministic
::
config
::
mailbox_factory
()
{
return
factory_
.
get
();
}
// -- abstract_message_predicate -----------------------------------------------
deterministic
::
abstract_message_predicate
::~
abstract_message_predicate
()
{
// nop
}
// -- fixture ------------------------------------------------------------------
deterministic
::
deterministic
()
:
cfg
(
this
),
sys
(
cfg
)
{
// nop
}
deterministic
::~
deterministic
()
{
// Note: we need clean up all remaining messages manually. This in turn may
// clean up actors as unreachable if the test did not consume all
// messages. Otherwise, the destructor of `sys` will wait for all
// actors, potentially waiting forever.
drop_events
();
}
void
deterministic
::
drop_events
()
{
// Note: We cannot just call `events_.clear()`, because that would potentially
// cause an actor to become unreachable and close its mailbox. This
// could call `pop_msg_impl` in turn, which then tries to alter the list
// while we're clearing it.
while
(
!
events_
.
empty
())
{
std
::
list
<
std
::
unique_ptr
<
scheduling_event
>>
tmp
;
tmp
.
splice
(
tmp
.
end
(),
events_
);
}
}
bool
deterministic
::
prepone_event_impl
(
const
strong_actor_ptr
&
receiver
)
{
actor_predicate
any_sender
{
std
::
ignore
};
message_predicate
any_payload
{
std
::
ignore
};
return
prepone_event_impl
(
receiver
,
any_sender
,
any_payload
);
}
bool
deterministic
::
prepone_event_impl
(
const
strong_actor_ptr
&
receiver
,
actor_predicate
&
sender_pred
,
abstract_message_predicate
&
payload_pred
)
{
if
(
events_
.
empty
()
||
!
receiver
)
return
false
;
auto
first
=
events_
.
begin
();
auto
last
=
events_
.
end
();
auto
i
=
std
::
find_if
(
first
,
last
,
[
&
](
const
auto
&
event
)
{
auto
self
=
actor_cast
<
abstract_actor
*>
(
receiver
);
return
event
->
target
==
static_cast
<
scheduled_actor
*>
(
self
)
&&
sender_pred
(
event
->
item
->
sender
)
&&
payload_pred
(
event
->
item
->
payload
);
});
if
(
i
==
last
)
return
false
;
if
(
i
!=
first
)
{
auto
ptr
=
std
::
move
(
*
i
);
events_
.
erase
(
i
);
events_
.
insert
(
events_
.
begin
(),
std
::
move
(
ptr
));
}
return
true
;
}
deterministic
::
scheduling_event
*
deterministic
::
find_event_impl
(
const
strong_actor_ptr
&
receiver
)
{
if
(
events_
.
empty
()
||
!
receiver
)
return
nullptr
;
auto
last
=
events_
.
end
();
auto
i
=
std
::
find_if
(
events_
.
begin
(),
last
,
[
&
](
const
auto
&
event
)
{
auto
raw_ptr
=
actor_cast
<
abstract_actor
*>
(
receiver
);
return
event
->
target
==
static_cast
<
scheduled_actor
*>
(
raw_ptr
);
});
if
(
i
!=
last
)
return
i
->
get
();
return
nullptr
;
}
mailbox_element_ptr
deterministic
::
pop_msg_impl
(
scheduled_actor
*
receiver
)
{
auto
pred
=
[
&
](
const
auto
&
event
)
{
return
event
->
target
==
receiver
;
};
auto
first
=
events_
.
begin
();
auto
last
=
events_
.
end
();
auto
i
=
std
::
find_if
(
first
,
last
,
pred
);
if
(
i
==
last
)
return
nullptr
;
auto
result
=
std
::
move
((
*
i
)
->
item
);
events_
.
erase
(
i
);
return
result
;
}
size_t
deterministic
::
mail_count
(
scheduled_actor
*
receiver
)
{
if
(
receiver
==
nullptr
)
return
0
;
auto
pred
=
[
&
](
const
auto
&
event
)
{
return
event
->
target
==
receiver
;
};
return
std
::
count_if
(
events_
.
begin
(),
events_
.
end
(),
pred
);
}
size_t
deterministic
::
mail_count
(
const
strong_actor_ptr
&
receiver
)
{
auto
raw_ptr
=
actor_cast
<
abstract_actor
*>
(
receiver
);
return
mail_count
(
dynamic_cast
<
scheduled_actor
*>
(
raw_ptr
));
}
bool
deterministic
::
dispatch_message
()
{
if
(
events_
.
empty
())
return
false
;
if
(
events_
.
front
()
->
item
==
nullptr
)
{
// Regular resumable.
auto
ev
=
std
::
move
(
events_
.
front
());
events_
.
pop_front
();
ev
->
target
->
resume
(
sys
.
dummy_execution_unit
(),
0
);
return
true
;
}
// Actor: we simply resume the next actor and it will pick up its message.
auto
next
=
events_
.
front
()
->
target
;
next
->
resume
(
sys
.
dummy_execution_unit
(),
1
);
return
true
;
}
size_t
deterministic
::
dispatch_messages
()
{
size_t
result
=
0
;
while
(
dispatch_message
())
++
result
;
return
result
;
}
}
// namespace caf::test
libcaf_test/caf/test/fixture/deterministic.hpp
0 → 100644
View file @
cd8363aa
// 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/runnable.hpp"
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_actor_clock.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
#include <list>
#include <memory>
namespace
caf
::
test
::
fixture
{
/// A fixture for writing unit tests that require deterministic scheduling. The
/// fixture equips tests with an actor system that uses a deterministic
/// scheduler and provides a DSL for writing high-level tests for message
/// passing between actors.
class
CAF_TEST_EXPORT
deterministic
{
private:
// -- private member types (implementation details) --------------------------
class
mailbox_impl
;
class
scheduler_impl
;
class
mailbox_factory_impl
;
/// Wraps a resumable pointer and a mailbox element pointer.
struct
scheduling_event
{
scheduling_event
(
resumable
*
target
,
mailbox_element_ptr
payload
)
:
target
(
target
),
item
(
std
::
move
(
payload
))
{
// nop
}
/// The target of the event.
intrusive_ptr
<
resumable
>
target
;
/// The message for the event or `nullptr` if the target is not an actor.
mailbox_element_ptr
item
;
};
/// A predicate for checking of single value. When constructing from a
/// reference wrapper, the predicate assigns the found value to the reference
/// instead of checking it.
template
<
class
T
>
class
value_predicate
{
public:
value_predicate
()
{
predicate_
=
[](
const
T
&
)
{
return
true
;
};
}
explicit
value_predicate
(
decltype
(
std
::
ignore
))
:
value_predicate
()
{
// nop
}
template
<
class
U
>
explicit
value_predicate
(
U
value
,
std
::
enable_if_t
<
detail
::
is_comparable_v
<
T
,
U
>>*
=
nullptr
)
{
predicate_
=
[
value
](
const
T
&
found
)
{
return
found
==
value
;
};
}
explicit
value_predicate
(
std
::
reference_wrapper
<
T
>
ref
)
{
predicate_
=
[
ref
](
const
T
&
found
)
{
ref
.
get
()
=
found
;
return
true
;
};
}
template
<
class
Predicate
,
class
=
std
::
enable_if_t
<
std
::
is_same_v
<
bool
,
decltype
(
std
::
declval
<
Predicate
>()(
std
::
declval
<
const
T
&>
()))
>>>
explicit
value_predicate
(
Predicate
predicate
)
{
predicate_
=
std
::
move
(
predicate
);
}
value_predicate
(
value_predicate
&&
)
=
default
;
value_predicate
(
const
value_predicate
&
)
=
default
;
value_predicate
&
operator
=
(
value_predicate
&&
)
=
default
;
value_predicate
&
operator
=
(
const
value_predicate
&
)
=
default
;
bool
operator
()(
const
T
&
value
)
{
return
predicate_
(
value
);
}
private:
std
::
function
<
bool
(
const
T
&
)
>
predicate_
;
};
/// Convenience alias for predicates on strong actor pointers.
using
actor_predicate
=
value_predicate
<
strong_actor_ptr
>
;
/// Abstract base type for message predicates.
class
abstract_message_predicate
{
public:
virtual
~
abstract_message_predicate
();
virtual
bool
operator
()(
const
message
&
)
=
0
;
};
/// A predicate for checking type and (optionally) content of a message.
template
<
class
...
Ts
>
class
message_predicate
:
public
abstract_message_predicate
{
public:
template
<
class
U
,
class
...
Us
>
explicit
message_predicate
(
U
x
,
Us
...
xs
)
{
predicates_
=
std
::
make_shared
<
predicates_tuple
>
(
std
::
move
(
x
),
std
::
move
(
xs
)...);
}
explicit
message_predicate
(
decltype
(
std
::
ignore
))
{
// nop
}
message_predicate
()
{
predicates_
=
std
::
make_shared
<
predicates_tuple
>
();
}
message_predicate
(
message_predicate
&&
)
=
default
;
message_predicate
(
const
message_predicate
&
)
=
default
;
message_predicate
&
operator
=
(
message_predicate
&&
)
=
default
;
message_predicate
&
operator
=
(
const
message_predicate
&
)
=
default
;
bool
operator
()(
const
message
&
msg
)
{
if
(
!
predicates_
)
return
true
;
if
(
auto
view
=
make_const_typed_message_view
<
Ts
...
>
(
msg
))
return
do_check
(
view
,
std
::
index_sequence_for
<
Ts
...
>
{});
return
false
;
}
private:
template
<
size_t
...
Is
>
bool
do_check
([[
maybe_unused
]]
const_typed_message_view
<
Ts
...
>
view
,
//
std
::
index_sequence
<
Is
...
>
)
{
return
(((
std
::
get
<
Is
>
(
*
predicates_
))(
get
<
Is
>
(
view
)))
&&
...);
}
using
predicates_tuple
=
std
::
tuple
<
value_predicate
<
Ts
>
...
>
;
std
::
shared_ptr
<
predicates_tuple
>
predicates_
;
};
public:
// -- public member types ----------------------------------------------------
/// The configuration type for this fixture.
class
config
:
public
actor_system_config
{
public:
config
(
deterministic
*
fix
);
~
config
()
override
;
private:
detail
::
mailbox_factory
*
mailbox_factory
()
override
;
std
::
unique_ptr
<
detail
::
mailbox_factory
>
factory_
;
};
/// Configures the algorithm to evaluate for an `evaluator` instances.
enum
class
evaluator_algorithm
{
expect
,
allow
,
prepone
,
prepone_and_expect
,
prepone_and_allow
};
/// Provides a fluent interface for matching messages. The `evaluator` allows
/// setting `from` and `with` parameters for an algorithm that matches
/// messages against a predicate. When setting the only mandatory parameter
/// `to`, the `evaluator` evaluates the predicate against the next message
/// in the mailbox of the target actor.
template
<
class
...
Ts
>
class
evaluator
{
public:
evaluator
(
deterministic
*
fix
,
detail
::
source_location
loc
,
evaluator_algorithm
algorithm
)
:
fix_
(
fix
),
loc_
(
loc
),
algo_
(
algorithm
)
{
// nop
}
evaluator
()
=
delete
;
evaluator
(
evaluator
&&
)
noexcept
=
default
;
evaluator
&
operator
=
(
evaluator
&&
)
noexcept
=
default
;
evaluator
(
const
evaluator
&
)
=
delete
;
evaluator
&
operator
=
(
const
evaluator
&
)
=
delete
;
/// Matches the values of the message. The evaluator will match a message
/// only if all individual values match the corresponding predicate.
///
/// The template parameter pack `xs...` contains a list of match expressions
/// that all must evaluate to true for a message to match. For each match
/// expression:
///
/// - Passing a value creates a predicate that matches the value exactly.
/// - Passing a predicate (a function object taking one argument and
/// returning `bool`) will match any value for which the predicate returns
/// `true`.
/// - Passing `std::ignore` accepts any value at that position.
/// - Passing a `std::reference_wrapper<T>` will match any value and stores
/// the value in the reference wrapper.
template
<
class
...
Us
>
evaluator
&&
with
(
Us
...
xs
)
&&
{
static_assert
(
sizeof
...(
Ts
)
==
sizeof
...(
Us
));
static_assert
((
std
::
is_constructible_v
<
value_predicate
<
Ts
>
,
Us
>
&&
...));
with_
=
message_predicate
<
Ts
...
>
(
std
::
move
(
xs
)...);
return
std
::
move
(
*
this
);
}
/// Adds a predicate for the sender of the next message that matches only if
/// the sender is `src`.
evaluator
&&
from
(
const
strong_actor_ptr
&
src
)
&&
{
from_
=
value_predicate
<
strong_actor_ptr
>
{
std
::
move
(
src
)};
return
std
::
move
(
*
this
);
}
/// Adds a predicate for the sender of the next message that matches only if
/// the sender is `src`.
evaluator
&&
from
(
const
actor
&
src
)
&&
{
from_
=
value_predicate
<
strong_actor_ptr
>
{
std
::
move
(
src
)};
return
std
::
move
(
*
this
);
}
/// Adds a predicate for the sender of the next message that matches only if
/// the sender is `src`.
template
<
class
...
Us
>
evaluator
&&
from
(
const
typed_actor
<
Us
...
>&
src
)
&&
{
from_
=
value_predicate
<
strong_actor_ptr
>
{
std
::
move
(
src
)};
return
std
::
move
(
*
this
);
}
/// Adds a predicate for the sender of the next message that matches only
/// anonymous messages, i.e., messages without a sender.
evaluator
&&
from
(
std
::
nullptr_t
)
&&
{
return
std
::
move
(
*
this
).
from
(
strong_actor_ptr
{});
}
/// Causes the evaluator to store the sender of a matched message in `src`.
evaluator
&&
from
(
std
::
reference_wrapper
<
strong_actor_ptr
>
src
)
&&
{
from_
=
value_predicate
<
strong_actor_ptr
>
{
src
};
return
std
::
move
(
*
this
);
}
/// Sets the target actor for this evaluator and evaluate the predicate.
template
<
class
T
>
bool
to
(
const
T
&
dst
)
&&
{
auto
dst_ptr
=
actor_cast
<
strong_actor_ptr
>
(
dst
);
switch
(
algo_
)
{
case
evaluator_algorithm
:
:
expect
:
return
eval_dispatch
(
dst_ptr
,
true
);
case
evaluator_algorithm
:
:
allow
:
return
eval_dispatch
(
dst_ptr
,
false
);
case
evaluator_algorithm
:
:
prepone
:
return
eval_prepone
(
dst_ptr
);
case
evaluator_algorithm
:
:
prepone_and_expect
:
eval_prepone
(
dst_ptr
);
return
eval_dispatch
(
dst_ptr
,
true
);
case
evaluator_algorithm
:
:
prepone_and_allow
:
return
eval_prepone
(
dst_ptr
)
&&
eval_dispatch
(
dst_ptr
,
false
);
default:
CAF_RAISE_ERROR
(
std
::
logic_error
,
"invalid algorithm"
);
}
}
private:
using
predicates
=
std
::
tuple
<
value_predicate
<
Ts
>
...
>
;
bool
eval_dispatch
(
const
strong_actor_ptr
&
dst
,
bool
fail_on_mismatch
)
{
auto
&
ctx
=
runnable
::
current
();
auto
*
event
=
fix_
->
find_event_impl
(
dst
);
if
(
!
event
)
{
if
(
fail_on_mismatch
)
ctx
.
fail
({
"no matching message found"
,
loc_
});
return
false
;
}
if
(
!
from_
(
event
->
item
->
sender
)
||
!
with_
(
event
->
item
->
payload
))
{
if
(
fail_on_mismatch
)
ctx
.
fail
({
"no matching message found"
,
loc_
});
return
false
;
}
fix_
->
prepone_event_impl
(
dst
);
if
(
fail_on_mismatch
)
{
if
(
!
fix_
->
dispatch_message
())
ctx
.
fail
({
"failed to dispatch message"
,
loc_
});
reporter
::
instance
().
pass
(
loc_
);
return
true
;
}
return
fix_
->
dispatch_message
();
}
bool
eval_prepone
(
const
strong_actor_ptr
&
dst
)
{
return
fix_
->
prepone_event_impl
(
dst
,
from_
,
with_
);
}
deterministic
*
fix_
;
detail
::
source_location
loc_
;
evaluator_algorithm
algo_
;
actor_predicate
from_
;
message_predicate
<
Ts
...
>
with_
;
};
// -- friends ----------------------------------------------------------------
friend
class
mailbox_impl
;
friend
class
scheduler_impl
;
template
<
class
...
Ts
>
friend
class
evaluator
;
// -- constructors, destructors, and assignment operators --------------------
deterministic
();
~
deterministic
();
// -- properties -------------------------------------------------------------
/// Returns the number of pending messages for `receiver`.
size_t
mail_count
(
scheduled_actor
*
receiver
);
/// Returns the number of pending messages for `receiver`.
size_t
mail_count
(
const
strong_actor_ptr
&
receiver
);
/// Returns the number of pending messages for `receiver`.
size_t
mail_count
(
const
actor
&
receiver
)
{
return
mail_count
(
actor_cast
<
strong_actor_ptr
>
(
receiver
));
}
/// Returns the number of pending messages for `receiver`.
template
<
class
...
Ts
>
size_t
mail_count
(
const
typed_actor
<
Ts
...
>&
receiver
)
{
return
mail_count
(
actor_cast
<
strong_actor_ptr
>
(
receiver
));
}
// -- control flow -----------------------------------------------------------
/// Tries to dispatch a single message.
bool
dispatch_message
();
/// Dispatches all pending messages.
size_t
dispatch_messages
();
// -- message-based predicates -----------------------------------------------
/// Expects a message with types `Ts...` as the next message in the mailbox of
/// the receiver and aborts the test if the message is missing. Otherwise
/// executes the message.
template
<
class
...
Ts
>
auto
expect
(
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
return
evaluator
<
Ts
...
>
{
this
,
loc
,
evaluator_algorithm
::
expect
};
}
/// Tries to match a message with types `Ts...` and executes it if it is the
/// next message in the mailbox of the receiver.
template
<
class
...
Ts
>
auto
allow
(
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
return
evaluator
<
Ts
...
>
{
this
,
loc
,
evaluator_algorithm
::
allow
};
}
/// Tries to prepone a message, i.e., reorders the messages in the mailbox of
/// the receiver such that the next call to `dispatch_message` will run it.
/// @returns `true` if the message could be preponed, `false` otherwise.
template
<
class
...
Ts
>
auto
prepone
(
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
return
evaluator
<
Ts
...
>
{
this
,
loc
,
evaluator_algorithm
::
prepone
};
}
/// Shortcut for calling `prepone` and then `expect` with the same arguments.
template
<
class
...
Ts
>
auto
prepone_and_expect
(
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
return
evaluator
<
Ts
...
>
{
this
,
loc
,
evaluator_algorithm
::
prepone_and_expect
};
}
/// Shortcut for calling `prepone` and then `allow` with the same arguments.
template
<
class
...
Ts
>
auto
prepone_and_allow
(
const
detail
::
source_location
&
loc
=
detail
::
source_location
::
current
())
{
return
evaluator
<
Ts
...
>
{
this
,
loc
,
evaluator_algorithm
::
prepone_and_allow
};
}
// -- member variables -------------------------------------------------------
private:
// Note: this is put here because this member variable must be destroyed
// *after* the actor system (and thus must come before `sys` in
// the declaration order).
/// Stores all pending messages of scheduled actors.
std
::
list
<
std
::
unique_ptr
<
scheduling_event
>>
events_
;
public:
/// Configures the actor system with deterministic scheduling.
config
cfg
;
/// The actor system instance for the tests.
actor_system
sys
;
private:
/// Removes all events from the queue.
void
drop_events
();
/// Tries find a message in `events_` that matches the given predicate and
/// moves it to the front of the queue.
bool
prepone_event_impl
(
const
strong_actor_ptr
&
receiver
);
/// Tries find a message in `events_` that matches the given predicates and
/// moves it to the front of the queue.
bool
prepone_event_impl
(
const
strong_actor_ptr
&
receiver
,
actor_predicate
&
sender_pred
,
abstract_message_predicate
&
payload_pred
);
/// Returns the next event for `receiver` or `nullptr` if there is none.
scheduling_event
*
find_event_impl
(
const
strong_actor_ptr
&
receiver
);
/// Removes the next message for `receiver` from the queue and returns it.
mailbox_element_ptr
pop_msg_impl
(
scheduled_actor
*
receiver
);
};
}
// namespace caf::test
libcaf_test/caf/test/fixture/deterministic.test.cpp
0 → 100644
View file @
cd8363aa
// 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/fixture/deterministic.hpp"
#include "caf/test/caf_test_main.hpp"
#include "caf/test/test.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/send.hpp"
namespace
fixture
=
caf
::
test
::
fixture
;
struct
my_int
{
int
value
;
};
template
<
class
Inspector
>
auto
inspect
(
Inspector
&
f
,
my_int
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"value"
,
x
.
value
));
}
bool
operator
==
(
my_int
lhs
,
my_int
rhs
)
noexcept
{
return
lhs
.
value
==
rhs
.
value
;
}
bool
operator
==
(
my_int
lhs
,
int
rhs
)
noexcept
{
return
lhs
.
value
==
rhs
;
}
bool
operator
==
(
int
lhs
,
my_int
rhs
)
noexcept
{
return
lhs
==
rhs
.
value
;
}
bool
operator
!=
(
my_int
lhs
,
my_int
rhs
)
noexcept
{
return
lhs
.
value
!=
rhs
.
value
;
}
CAF_BEGIN_TYPE_ID_BLOCK
(
test
,
caf
::
first_custom_type_id
)
CAF_ADD_TYPE_ID
(
test
,
(
my_int
))
CAF_END_TYPE_ID_BLOCK
(
test
)
WITH_FIXTURE
(
fixture
::
deterministic
)
{
TEST
(
"the deterministic fixture provides a deterministic scheduler"
)
{
auto
initialized
=
std
::
make_shared
<
bool
>
(
false
);
auto
count
=
std
::
make_shared
<
int32_t
>
(
0
);
auto
worker
=
sys
.
spawn
([
initialized
,
count
]
{
*
initialized
=
true
;
return
caf
::
behavior
{
[
count
](
int32_t
value
)
{
*
count
+=
value
;
},
[
count
](
const
std
::
string
&
str
)
{
if
(
auto
ival
=
caf
::
get_as
<
int32_t
>
(
caf
::
config_value
{
str
}))
*
count
+=
*
ival
;
},
};
});
caf
::
scoped_actor
self
{
sys
};
check
(
*
initialized
);
check_eq
(
mail_count
(
worker
),
0u
);
anon_send
(
worker
,
1
);
check_eq
(
mail_count
(
worker
),
1u
);
self
->
send
(
worker
,
2
);
check_eq
(
mail_count
(
worker
),
2u
);
anon_send
(
worker
,
3
);
check_eq
(
mail_count
(
worker
),
3u
);
SECTION
(
"calling dispatch_message processes a single message"
)
{
check
(
dispatch_message
());
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
1
);
check
(
dispatch_message
());
check_eq
(
mail_count
(
worker
),
1u
);
check_eq
(
*
count
,
3
);
check
(
dispatch_message
());
check_eq
(
mail_count
(
worker
),
0u
);
check_eq
(
*
count
,
6
);
check
(
!
dispatch_message
());
}
SECTION
(
"calling dispatch_messages processes all messages"
)
{
check_eq
(
dispatch_messages
(),
3u
);
check_eq
(
*
count
,
6
);
}
#ifdef CAF_ENABLE_EXCEPTIONS
SECTION
(
"expect() checks for required messages"
)
{
expect
<
int32_t
>
().
to
(
worker
);
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
1
);
expect
<
int32_t
>
().
to
(
worker
);
check_eq
(
mail_count
(
worker
),
1u
);
check_eq
(
*
count
,
3
);
expect
<
int32_t
>
().
to
(
worker
);
check_eq
(
mail_count
(
worker
),
0u
);
check_eq
(
*
count
,
6
);
should_fail_with_exception
([
this
,
worker
]
{
//
expect
<
int32_t
>
().
with
(
3
).
to
(
worker
);
});
}
SECTION
(
"expect() matches the types of the next message"
)
{
anon_send
(
worker
,
"4"
);
should_fail_with_exception
([
this
,
worker
]
{
//
expect
<
std
::
string
>
().
to
(
worker
);
});
should_fail_with_exception
([
this
,
worker
]
{
//
expect
<
int32_t
,
int32_t
>
().
to
(
worker
);
});
check_eq
(
*
count
,
0
);
check_eq
(
dispatch_messages
(),
4u
);
check_eq
(
*
count
,
10
);
}
SECTION
(
"expect() optionally matches the content of the next message"
)
{
should_fail_with_exception
([
this
,
worker
]
{
//
expect
<
int32_t
>
().
with
(
3
).
to
(
worker
);
});
check_eq
(
*
count
,
0
);
expect
<
int32_t
>
().
with
(
1
).
to
(
worker
);
expect
<
int32_t
>
().
with
(
2
).
to
(
worker
);
expect
<
int32_t
>
().
with
(
3
).
to
(
worker
);
check_eq
(
*
count
,
6
);
}
SECTION
(
"expect() optionally matches the sender of the next message"
)
{
// First message has no sender.
should_fail_with_exception
([
this
,
worker
,
&
self
]
{
//
expect
<
int32_t
>
().
from
(
self
).
to
(
worker
);
});
check_eq
(
*
count
,
0
);
expect
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
);
check_eq
(
*
count
,
1
);
// Second message is from self.
should_fail_with_exception
([
this
,
worker
]
{
//
expect
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
);
});
check_eq
(
*
count
,
1
);
expect
<
int32_t
>
().
from
(
self
).
to
(
worker
);
check_eq
(
*
count
,
3
);
}
SECTION
(
"prepone_and_expect() processes out of order based on types"
)
{
anon_send
(
worker
,
"4"
);
prepone_and_expect
<
std
::
string
>
().
to
(
worker
);
check_eq
(
*
count
,
4
);
should_fail_with_exception
([
this
,
worker
]
{
//
prepone_and_expect
<
std
::
string
>
().
to
(
worker
);
});
check_eq
(
*
count
,
4
);
check_eq
(
dispatch_messages
(),
3u
);
check_eq
(
*
count
,
10
);
}
SECTION
(
"prepone_and_expect() processes out of order based on content"
)
{
prepone_and_expect
<
int32_t
>
().
with
(
3
).
to
(
worker
);
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
3
);
should_fail_with_exception
([
this
,
worker
]
{
//
prepone_and_expect
<
int32_t
>
().
with
(
3
).
to
(
worker
);
});
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
3
);
prepone_and_expect
<
int32_t
>
().
with
(
1
).
to
(
worker
);
check_eq
(
*
count
,
4
);
prepone_and_expect
<
int32_t
>
().
with
(
2
).
to
(
worker
);
check_eq
(
*
count
,
6
);
check
(
!
dispatch_message
());
}
SECTION
(
"prepone_and_expect() processes out of order based on senders"
)
{
prepone_and_expect
<
int32_t
>
().
from
(
self
).
to
(
worker
);
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
2
);
should_fail_with_exception
([
this
,
worker
,
&
self
]
{
//
prepone_and_expect
<
int32_t
>
().
from
(
self
).
to
(
worker
);
});
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
2
);
prepone_and_expect
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
);
check_eq
(
*
count
,
3
);
prepone_and_expect
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
);
check_eq
(
*
count
,
6
);
check
(
!
dispatch_message
());
}
#endif // CAF_ENABLE_EXCEPTIONS
SECTION
(
"allow() checks for optional messages"
)
{
check
(
allow
<
int32_t
>
().
to
(
worker
));
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
1
);
check
(
allow
<
int32_t
>
().
to
(
worker
));
check_eq
(
mail_count
(
worker
),
1u
);
check_eq
(
*
count
,
3
);
check
(
allow
<
int32_t
>
().
to
(
worker
));
check_eq
(
mail_count
(
worker
),
0u
);
check_eq
(
*
count
,
6
);
check
(
!
allow
<
int32_t
>
().
with
(
3
).
to
(
worker
));
}
SECTION
(
"allow() matches the types of the next message"
)
{
anon_send
(
worker
,
"4"
);
check
(
!
allow
<
std
::
string
>
().
to
(
worker
));
check
(
!
allow
<
int32_t
,
int32_t
>
().
to
(
worker
));
check_eq
(
*
count
,
0
);
check_eq
(
dispatch_messages
(),
4u
);
check_eq
(
*
count
,
10
);
}
SECTION
(
"allow() optionally matches the content of the next message"
)
{
check
(
!
allow
<
int32_t
>
().
with
(
3
).
to
(
worker
));
check_eq
(
*
count
,
0
);
check
(
allow
<
int32_t
>
().
with
(
1
).
to
(
worker
));
check
(
allow
<
int32_t
>
().
with
(
2
).
to
(
worker
));
check
(
allow
<
int32_t
>
().
with
(
3
).
to
(
worker
));
check_eq
(
*
count
,
6
);
}
SECTION
(
"allow() optionally matches the sender of the next message"
)
{
// First message has no sender.
check
(
!
allow
<
int32_t
>
().
from
(
self
).
to
(
worker
));
check_eq
(
*
count
,
0
);
check
(
allow
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
));
check_eq
(
*
count
,
1
);
// Second message is from self.
check
(
!
allow
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
));
check_eq
(
*
count
,
1
);
check
(
allow
<
int32_t
>
().
from
(
self
).
to
(
worker
));
check_eq
(
*
count
,
3
);
}
SECTION
(
"prepone_and_allow() checks for optional messages"
)
{
check
(
prepone_and_allow
<
int32_t
>
().
to
(
worker
));
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
1
);
check
(
prepone_and_allow
<
int32_t
>
().
to
(
worker
));
check_eq
(
mail_count
(
worker
),
1u
);
check_eq
(
*
count
,
3
);
check
(
prepone_and_allow
<
int32_t
>
().
to
(
worker
));
check_eq
(
mail_count
(
worker
),
0u
);
check_eq
(
*
count
,
6
);
check
(
!
prepone_and_allow
<
int32_t
>
().
with
(
3
).
to
(
worker
));
}
SECTION
(
"prepone_and_allow() processes out of order based on types"
)
{
anon_send
(
worker
,
"4"
);
check
(
prepone_and_allow
<
std
::
string
>
().
to
(
worker
));
check
(
!
prepone_and_allow
<
std
::
string
>
().
to
(
worker
));
check_eq
(
*
count
,
4
);
check_eq
(
dispatch_messages
(),
3u
);
check_eq
(
*
count
,
10
);
}
SECTION
(
"prepone_and_allow() processes out of order based on content"
)
{
check
(
prepone_and_allow
<
int32_t
>
().
with
(
3
).
to
(
worker
));
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
3
);
check
(
!
prepone_and_allow
<
int32_t
>
().
with
(
3
).
to
(
worker
));
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
3
);
check
(
prepone_and_allow
<
int32_t
>
().
with
(
1
).
to
(
worker
));
check_eq
(
*
count
,
4
);
check
(
prepone_and_allow
<
int32_t
>
().
with
(
2
).
to
(
worker
));
check_eq
(
*
count
,
6
);
check
(
!
dispatch_message
());
}
SECTION
(
"prepone_and_allow() processes out of order based on senders"
)
{
check
(
prepone_and_allow
<
int32_t
>
().
from
(
self
).
to
(
worker
));
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
2
);
check
(
!
prepone_and_allow
<
int32_t
>
().
from
(
self
).
to
(
worker
));
check_eq
(
mail_count
(
worker
),
2u
);
check_eq
(
*
count
,
2
);
check
(
prepone_and_allow
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
));
check_eq
(
*
count
,
3
);
check
(
prepone_and_allow
<
int32_t
>
().
from
(
nullptr
).
to
(
worker
));
check_eq
(
*
count
,
6
);
check
(
!
dispatch_message
());
}
SECTION
(
"prepone_and_allow() ignores non-existent combinations"
)
{
// There is no message with content (4).
check
(
!
prepone_and_allow
<
int32_t
>
().
with
(
4
).
from
(
self
).
to
(
worker
));
// There is no message with content (3) from self.
check
(
!
prepone_and_allow
<
int32_t
>
().
with
(
3
).
from
(
self
).
to
(
worker
));
// The original order should be preserved.
check
(
dispatch_message
());
check_eq
(
*
count
,
1
);
check
(
dispatch_message
());
check_eq
(
*
count
,
3
);
check
(
dispatch_message
());
check_eq
(
*
count
,
6
);
}
}
TEST
(
"evaluator expressions can check or extract individual values"
)
{
auto
worker
=
sys
.
spawn
([](
caf
::
event_based_actor
*
self
)
->
caf
::
behavior
{
self
->
set_default_handler
(
caf
::
drop
);
return
{
[](
int32_t
)
{},
};
});
SECTION
(
"omitting with() matches on the types only"
)
{
anon_send
(
worker
,
1
);
check
(
!
allow
<
std
::
string
>
().
to
(
worker
));
check
(
allow
<
int
>
().
to
(
worker
));
anon_send
(
worker
,
1
,
"two"
,
3.0
);
check
(
!
allow
<
int
>
().
to
(
worker
));
check
(
allow
<
int
,
std
::
string
,
double
>
().
to
(
worker
));
anon_send
(
worker
,
42
,
"hello world"
,
7.7
);
check
(
allow
<
int
,
std
::
string
,
double
>
().
to
(
worker
));
}
SECTION
(
"reference wrappers turn evaluators into extractors"
)
{
auto
tmp
=
0
;
anon_send
(
worker
,
1
);
check
(
allow
<
int
>
().
with
(
std
::
ref
(
tmp
)).
to
(
worker
));
check_eq
(
tmp
,
1
);
}
SECTION
(
"std::ignore matches any value"
)
{
anon_send
(
worker
,
1
);
check
(
allow
<
int
>
().
with
(
std
::
ignore
).
to
(
worker
));
anon_send
(
worker
,
2
);
check
(
allow
<
int
>
().
with
(
std
::
ignore
).
to
(
worker
));
anon_send
(
worker
,
3
);
check
(
allow
<
int
>
().
with
(
std
::
ignore
).
to
(
worker
));
anon_send
(
worker
,
1
,
2
,
3
);
check
(
!
allow
<
int
,
int
,
int
>
().
with
(
1
,
std
::
ignore
,
4
).
to
(
worker
));
check
(
!
allow
<
int
,
int
,
int
>
().
with
(
2
,
std
::
ignore
,
3
).
to
(
worker
));
check
(
allow
<
int
,
int
,
int
>
().
with
(
1
,
std
::
ignore
,
3
).
to
(
worker
));
}
SECTION
(
"value predicates allow user-defined types"
)
{
anon_send
(
worker
,
my_int
{
1
});
check
(
allow
<
my_int
>
().
to
(
worker
));
anon_send
(
worker
,
my_int
{
1
});
check
(
allow
<
my_int
>
().
with
(
std
::
ignore
).
to
(
worker
));
anon_send
(
worker
,
my_int
{
1
});
check
(
!
allow
<
my_int
>
().
with
(
my_int
{
2
}).
to
(
worker
));
check
(
allow
<
my_int
>
().
with
(
my_int
{
1
}).
to
(
worker
));
anon_send
(
worker
,
my_int
{
1
});
check
(
allow
<
my_int
>
().
with
(
1
).
to
(
worker
));
auto
tmp
=
my_int
{
0
};
anon_send
(
worker
,
my_int
{
42
});
check
(
allow
<
my_int
>
().
with
(
std
::
ref
(
tmp
)).
to
(
worker
));
check_eq
(
tmp
.
value
,
42
);
}
SECTION
(
"value predicates allow user-defined predicates"
)
{
auto
le2
=
[](
my_int
x
)
{
return
x
.
value
<=
2
;
};
anon_send
(
worker
,
my_int
{
1
});
check
(
allow
<
my_int
>
().
with
(
le2
).
to
(
worker
));
anon_send
(
worker
,
my_int
{
2
});
check
(
allow
<
my_int
>
().
with
(
le2
).
to
(
worker
));
anon_send
(
worker
,
my_int
{
3
});
check
(
!
allow
<
my_int
>
().
with
(
le2
).
to
(
worker
));
}
}
}
// WITH_FIXTURE(fixture::deterministic)
CAF_TEST_MAIN
(
caf
::
id_block
::
test
)
libcaf_test/caf/test/reporter.cpp
View file @
cd8363aa
...
...
@@ -22,8 +22,6 @@ reporter::~reporter() {
// nop
}
reporter
*
reporter
::
instance
;
namespace
{
/// Implements a mini-DSL for colored output:
...
...
@@ -334,8 +332,8 @@ public:
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
"
,
"{0:{1}} loc: $C({
2}):$Y({3
})$0
\n
"
"{0:{1}} check: {
4
}
\n
"
,
' '
,
indent_
,
location
.
file_name
(),
location
.
line
(),
arg
);
}
...
...
@@ -388,8 +386,10 @@ public:
' '
,
indent_
,
location
.
file_name
(),
location
.
line
(),
msg
);
}
void
verbosity
(
unsigned
level
)
override
{
unsigned
verbosity
(
unsigned
level
)
override
{
auto
result
=
level_
;
level_
=
level
;
return
result
;
}
void
no_colors
(
bool
new_value
)
override
{
...
...
@@ -470,8 +470,20 @@ private:
context_ptr
current_ctx_
;
};
reporter
*
global_instance
;
}
// namespace
reporter
&
reporter
::
instance
()
{
if
(
global_instance
==
nullptr
)
CAF_RAISE_ERROR
(
"no reporter instance available"
);
return
*
global_instance
;
}
void
reporter
::
instance
(
reporter
*
ptr
)
{
global_instance
=
ptr
;
}
std
::
unique_ptr
<
reporter
>
reporter
::
make_default
()
{
return
std
::
make_unique
<
default_reporter
>
();
}
...
...
libcaf_test/caf/test/reporter.hpp
View file @
cd8363aa
...
...
@@ -75,7 +75,8 @@ public:
info
(
std
::
string_view
msg
,
const
detail
::
source_location
&
location
)
=
0
;
virtual
void
verbosity
(
unsigned
level
)
=
0
;
/// Sets the verbosity level of the reporter and returns the previous value.
virtual
unsigned
verbosity
(
unsigned
level
)
=
0
;
/// Sets whether the reporter disables colored output even when writing to a
/// TTY.
...
...
@@ -93,8 +94,11 @@ public:
/// Returns statistics for the entire run.
virtual
stats
total_stats
()
=
0
;
/// Stores a pointer to the currently active reporter.
static
reporter
*
instance
;
/// Returns the registered reporter instance.
static
reporter
&
instance
();
/// Sets the reporter instance for the current test run.
static
void
instance
(
reporter
*
ptr
);
/// Creates a default reporter that writes to the standard output.
static
std
::
unique_ptr
<
reporter
>
make_default
();
...
...
libcaf_test/caf/test/runnable.cpp
View file @
cd8363aa
...
...
@@ -10,13 +10,23 @@
#include "caf/test/scope.hpp"
#include "caf/test/test.hpp"
#include "caf/detail/scope_guard.hpp"
namespace
caf
::
test
{
namespace
{
thread_local
runnable
*
current_runnable
;
}
// namespace
runnable
::~
runnable
()
{
// nop
}
void
runnable
::
run
()
{
current_runnable
=
this
;
auto
guard
=
detail
::
make_scope_guard
([]
{
current_runnable
=
nullptr
;
});
switch
(
root_type_
)
{
case
block_type
:
:
scenario
:
if
(
auto
guard
=
ctx_
->
get
<
scenario
>
(
0
,
description_
,
loc_
)
->
commit
())
{
...
...
@@ -41,13 +51,20 @@ void runnable::run() {
bool
runnable
::
check
(
bool
value
,
const
detail
::
source_location
&
location
)
{
if
(
value
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
}
else
{
reporter
::
instance
->
fail
(
"should be true"
,
location
);
reporter
::
instance
().
fail
(
"should be true"
,
location
);
}
return
value
;
}
runnable
&
runnable
::
current
()
{
auto
ptr
=
current_runnable
;
if
(
!
ptr
)
CAF_RAISE_ERROR
(
std
::
logic_error
,
"no current runnable"
);
return
*
ptr
;
}
block
&
runnable
::
current_block
()
{
if
(
ctx_
->
call_stack
.
empty
())
CAF_RAISE_ERROR
(
std
::
logic_error
,
"no current block"
);
...
...
libcaf_test/caf/test/runnable.hpp
View file @
cd8363aa
...
...
@@ -12,8 +12,10 @@
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/format.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/raise_error.hpp"
#include <string_view>
...
...
@@ -42,14 +44,26 @@ public:
/// Runs the next branch of the test.
void
run
();
/// Generates a message with the INFO severity level.
template
<
class
...
Ts
>
[[
noreturn
]]
void
fail
(
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
().
fail
(
msg
,
fwl
.
location
);
}
else
{
reporter
::
instance
().
fail
(
fwl
.
value
,
fwl
.
location
);
}
CAF_RAISE_ERROR
(
std
::
logic_error
,
"requirement failed: abort test"
);
}
/// 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
);
reporter
::
instance
().
info
(
msg
,
fwl
.
location
);
}
else
{
reporter
::
instance
->
info
(
fwl
.
value
,
fwl
.
location
);
reporter
::
instance
().
info
(
fwl
.
value
,
fwl
.
location
);
}
}
...
...
@@ -60,10 +74,10 @@ public:
=
detail
::
source_location
::
current
())
{
assert_save_comparison
<
T0
,
T1
>
();
if
(
lhs
==
rhs
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
true
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
eq
,
stringify
(
lhs
),
reporter
::
instance
().
fail
(
binary_predicate
::
eq
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
return
false
;
}
...
...
@@ -75,10 +89,10 @@ public:
=
detail
::
source_location
::
current
())
{
assert_save_comparison
<
T0
,
T1
>
();
if
(
lhs
!=
rhs
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
true
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
ne
,
stringify
(
lhs
),
reporter
::
instance
().
fail
(
binary_predicate
::
ne
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
return
false
;
}
...
...
@@ -90,10 +104,10 @@ public:
=
detail
::
source_location
::
current
())
{
assert_save_comparison
<
T0
,
T1
>
();
if
(
lhs
<
rhs
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
true
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
lt
,
stringify
(
lhs
),
reporter
::
instance
().
fail
(
binary_predicate
::
lt
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
return
false
;
}
...
...
@@ -105,10 +119,10 @@ public:
=
detail
::
source_location
::
current
())
{
assert_save_comparison
<
T0
,
T1
>
();
if
(
lhs
<=
rhs
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
true
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
le
,
stringify
(
lhs
),
reporter
::
instance
().
fail
(
binary_predicate
::
le
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
return
false
;
}
...
...
@@ -120,10 +134,10 @@ public:
=
detail
::
source_location
::
current
())
{
assert_save_comparison
<
T0
,
T1
>
();
if
(
lhs
>
rhs
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
true
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
gt
,
stringify
(
lhs
),
reporter
::
instance
().
fail
(
binary_predicate
::
gt
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
return
false
;
}
...
...
@@ -135,58 +149,118 @@ public:
=
detail
::
source_location
::
current
())
{
assert_save_comparison
<
T0
,
T1
>
();
if
(
lhs
>=
rhs
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
true
;
}
reporter
::
instance
->
fail
(
binary_predicate
::
ge
,
stringify
(
lhs
),
reporter
::
instance
().
fail
(
binary_predicate
::
ge
,
stringify
(
lhs
),
stringify
(
rhs
),
location
);
return
false
;
}
/// Checks whether `value` is `true`.
bool
check
(
bool
value
,
const
detail
::
source_location
&
location
=
detail
::
source_location
::
current
());
/// Returns the `runnable` instance that is currently running.
static
runnable
&
current
();
block
&
current_block
();
template
<
class
Expr
>
void
should_fail
(
Expr
&&
expr
,
const
caf
::
detail
::
source_location
&
location
=
caf
::
detail
::
source_location
::
current
())
{
auto
&
rep
=
reporter
::
instance
();
auto
lvl
=
rep
.
verbosity
(
CAF_LOG_LEVEL_QUIET
);
auto
before
=
rep
.
test_stats
();
{
auto
lvl_guard
=
detail
::
make_scope_guard
([
&
]
{
rep
.
verbosity
(
lvl
);
});
expr
();
}
auto
after
=
rep
.
test_stats
();
auto
passed_count_ok
=
before
.
passed
==
after
.
passed
;
auto
failed_count_ok
=
before
.
failed
+
1
==
after
.
failed
;
if
(
passed_count_ok
&&
failed_count_ok
)
{
reporter
::
instance
().
pass
(
location
);
rep
.
test_stats
({
before
.
passed
+
1
,
before
.
failed
});
}
else
{
reporter
::
instance
().
fail
(
"nested check should fail"
,
location
);
rep
.
test_stats
({
before
.
passed
,
before
.
failed
+
1
});
}
}
#ifdef CAF_ENABLE_EXCEPTIONS
template
<
class
Exception
=
void
,
class
CodeBlock
>
void
check_throws
(
CodeBlock
&&
fn
,
const
detail
::
source_location
&
location
/// Checks whether `expr()` throws an exception of type `Exception`.
template
<
class
Exception
=
void
,
class
Expr
>
void
check_throws
(
Expr
&&
expr
,
const
detail
::
source_location
&
location
=
detail
::
source_location
::
current
())
{
if
constexpr
(
std
::
is_same_v
<
Exception
,
void
>
)
{
try
{
fn
();
expr
();
}
catch
(...)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
;
}
reporter
::
instance
->
fail
(
"throws"
,
location
);
reporter
::
instance
().
fail
(
"throws"
,
location
);
}
else
{
try
{
fn
();
expr
();
}
catch
(
const
Exception
&
)
{
reporter
::
instance
->
pass
(
location
);
reporter
::
instance
().
pass
(
location
);
return
;
}
catch
(...)
{
reporter
::
instance
->
fail
(
"throws Exception"
,
location
);
reporter
::
instance
().
fail
(
"throws Exception"
,
location
);
return
;
}
reporter
::
instance
->
fail
(
"throws Exception"
,
location
);
reporter
::
instance
().
fail
(
"throws Exception"
,
location
);
}
}
#endif
template
<
class
Expr
>
void
should_fail
(
Expr
&&
expr
,
const
caf
::
detail
::
source_location
&
location
/// Checks whether `expr()` throws an exception of type `Exception` and
/// increases the failure count.
template
<
class
Exception
=
void
,
class
Expr
>
void
should_fail_with_exception
(
Expr
&&
expr
,
const
caf
::
detail
::
source_location
&
location
=
caf
::
detail
::
source_location
::
current
())
{
auto
*
rep
=
reporter
::
instance
;
auto
before
=
rep
->
test_stats
();
auto
&
rep
=
reporter
::
instance
();
auto
before
=
rep
.
test_stats
();
auto
lvl
=
rep
.
verbosity
(
CAF_LOG_LEVEL_QUIET
);
auto
caught
=
false
;
if
constexpr
(
std
::
is_same_v
<
Exception
,
void
>
)
{
try
{
expr
();
}
catch
(...)
{
caught
=
true
;
}
}
else
{
try
{
expr
();
auto
after
=
rep
->
test_stats
();
check_eq
(
before
.
passed
,
after
.
passed
,
location
);
if
(
check_eq
(
before
.
failed
+
1
,
after
.
failed
,
location
))
rep
->
test_stats
({
before
.
passed
+
1
,
before
.
failed
});
}
catch
(
const
Exception
&
)
{
caught
=
true
;
}
catch
(...)
{
// TODO: print error message
}
}
rep
.
verbosity
(
lvl
);
auto
after
=
rep
.
test_stats
();
auto
passed_count_ok
=
before
.
passed
==
after
.
passed
;
auto
failed_count_ok
=
before
.
failed
+
1
==
after
.
failed
;
if
(
caught
&&
passed_count_ok
&&
failed_count_ok
)
{
reporter
::
instance
().
pass
(
location
);
rep
.
test_stats
({
before
.
passed
+
1
,
before
.
failed
});
}
else
{
if
(
!
caught
)
{
reporter
::
instance
().
fail
(
"nested check should throw an Exception"
,
location
);
}
else
if
(
!
passed_count_ok
||
!
failed_count_ok
)
{
reporter
::
instance
().
fail
(
"nested check should fail"
,
location
);
}
rep
.
test_stats
({
before
.
passed
,
before
.
failed
+
1
});
}
}
#endif
protected:
context_ptr
ctx_
;
std
::
string_view
description_
;
...
...
libcaf_test/caf/test/runner.cpp
View file @
cd8363aa
...
...
@@ -131,7 +131,7 @@ runner::runner() : suites_(caf::test::registry::suites()) {
int
runner
::
run
(
int
argc
,
char
**
argv
)
{
auto
default_reporter
=
reporter
::
make_default
();
reporter
::
instance
=
default_reporter
.
get
(
);
reporter
::
instance
(
default_reporter
.
get
()
);
if
(
auto
[
ok
,
help_printed
]
=
parse_cli
(
argc
,
argv
);
!
ok
)
{
return
EXIT_FAILURE
;
}
else
if
(
help_printed
)
{
...
...
@@ -240,7 +240,7 @@ runner::parse_cli_result runner::parse_cli(int argc, char** argv) {
*
verbosity
);
return
{
false
,
true
};
}
reporter
::
instance
->
verbosity
(
*
level
);
reporter
::
instance
().
verbosity
(
*
level
);
}
return
{
true
,
false
};
}
...
...
libcaf_test/caf/test/test.test.cpp
View file @
cd8363aa
...
...
@@ -8,8 +8,8 @@
using
caf
::
test
::
block_type
;
TEST
(
"tests can contain different types of checks"
)
{
auto
*
rep
=
caf
::
test
::
reporter
::
instance
;
auto
stats
=
rep
->
test_stats
();
auto
&
rep
=
caf
::
test
::
reporter
::
instance
()
;
auto
stats
=
rep
.
test_stats
();
SECTION
(
"check_ne checks for inequality"
)
{
check_ne
(
0
,
1
);
should_fail
([
this
]()
{
check_ne
(
0
,
0
);
});
...
...
@@ -38,16 +38,16 @@ TEST("tests can contain different types of checks") {
should_fail
([
this
]()
{
check_lt
(
1
,
1
);
});
should_fail
([
this
]()
{
check_lt
(
2
,
1
);
});
}
info
(
"this test had {} checks"
,
rep
->
test_stats
().
total
());
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
();
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
});
caf
::
test
::
reporter
::
instance
().
test_stats
({
2
,
0
});
}
TEST
(
"each run starts with fresh local variables"
)
{
...
...
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