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
Expand all
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 diff is collapsed.
Click to expand it.
libcaf_test/caf/test/fixture/deterministic.test.cpp
0 → 100644
View file @
cd8363aa
This diff is collapsed.
Click to expand it.
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