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
5d233fae
Commit
5d233fae
authored
Sep 27, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix and improve some issues with the testing DSL
parent
4bbbb5a2
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
912 additions
and
256 deletions
+912
-256
CHANGELOG.md
CHANGELOG.md
+24
-0
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/scheduler/test_coordinator.hpp
libcaf_core/caf/scheduler/test_coordinator.hpp
+10
-6
libcaf_core/test/dsl.cpp
libcaf_core/test/dsl.cpp
+399
-0
libcaf_test/caf/test/bdd_dsl.hpp
libcaf_test/caf/test/bdd_dsl.hpp
+10
-0
libcaf_test/caf/test/dsl.hpp
libcaf_test/caf/test/dsl.hpp
+161
-121
libcaf_test/caf/test/io_dsl.hpp
libcaf_test/caf/test/io_dsl.hpp
+13
-20
libcaf_test/caf/test/unit_test.hpp
libcaf_test/caf/test/unit_test.hpp
+209
-70
libcaf_test/caf/test/unit_test_impl.hpp
libcaf_test/caf/test/unit_test_impl.hpp
+85
-39
No files found.
CHANGELOG.md
View file @
5d233fae
...
@@ -5,10 +5,34 @@ is based on [Keep a Changelog](https://keepachangelog.com).
...
@@ -5,10 +5,34 @@ is based on [Keep a Changelog](https://keepachangelog.com).
## [Unreleased]
## [Unreleased]
### Added
-
When adding CAF with exceptions enabled (default), the unit test framework now
offers new check macros:
-
`CAF_CHECK_NOTHROW(expr)`
-
`CAF_CHECK_THROWS_AS(expr, type)`
-
`CAF_CHECK_THROWS_WITH(expr, str)`
-
`CAF_CHECK_THROWS_WITH_AS(expr, str, type)`
### Fixed
-
The DSL function
`run_until`
miscounted the number executed events, also
causing
`run_once`
to report a wrong value. Both functions now return the
correct result.
-
Using
`allow(...).with(...)`
in unit tests without a matching message crashed
the program. By adding a missing NULL-check,
`allow`
is now always safe to
use.
### Changed
### Changed
-
Since support of Qt 5 expired, we have ported the Qt examples to version 6.
-
Since support of Qt 5 expired, we have ported the Qt examples to version 6.
Hence, building the Qt examples now requires Qt in version 6.
Hence, building the Qt examples now requires Qt in version 6.
-
When compiling CAF with exceptions enabled (default),
`REQUIRE*`
macros,
`expect`
and
`disallow`
no longer call
`abort()`
. Instead, they throw an
exception that only stops the current test instead of stopping the entire test
program.
-
Reporting of several unit test macros has been improved to show correct line
numbers and provide better diagnostic of test errors.
## [0.18.5] - 2021-07-16
## [0.18.5] - 2021-07-16
...
...
libcaf_core/CMakeLists.txt
View file @
5d233fae
...
@@ -270,6 +270,7 @@ caf_add_component(
...
@@ -270,6 +270,7 @@ caf_add_component(
detail.unique_function
detail.unique_function
detail.unordered_flat_map
detail.unordered_flat_map
dictionary
dictionary
dsl
dynamic_spawn
dynamic_spawn
error
error
expected
expected
...
...
libcaf_core/caf/scheduler/test_coordinator.hpp
View file @
5d233fae
...
@@ -46,14 +46,18 @@ public:
...
@@ -46,14 +46,18 @@ public:
}
}
/// Peeks into the mailbox of `next_job<scheduled_actor>()`.
/// Peeks into the mailbox of `next_job<scheduled_actor>()`.
template
<
class
T
>
template
<
class
...
Ts
>
const
T
&
peek
()
{
decltype
(
auto
)
peek
()
{
auto
ptr
=
next_job
<
scheduled_actor
>
().
mailbox
().
peek
();
auto
ptr
=
next_job
<
scheduled_actor
>
().
mailbox
().
peek
();
CAF_ASSERT
(
ptr
!=
nullptr
);
CAF_ASSERT
(
ptr
!=
nullptr
);
auto
view
=
make_typed_message_view
<
T
>
(
ptr
->
content
());
if
(
auto
view
=
make_const_typed_message_view
<
Ts
...
>
(
ptr
->
payload
))
{
if
(
!
view
)
if
constexpr
(
sizeof
...(
Ts
)
==
1
)
CAF_RAISE_ERROR
(
"Mailbox element does not match T."
);
return
get
<
0
>
(
view
);
return
get
<
0
>
(
view
);
else
return
to_tuple
(
view
);
}
else
{
CAF_RAISE_ERROR
(
"Mailbox element does not match."
);
}
}
}
/// Puts `x` at the front of the queue unless it cannot be found in the queue.
/// Puts `x` at the front of the queue unless it cannot be found in the queue.
...
...
libcaf_core/test/dsl.cpp
0 → 100644
View file @
5d233fae
// 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.
// This test suite checks the testing DSL itself.
#define CAF_SUITE dsl
#include "core-test.hpp"
using
namespace
caf
;
namespace
{
struct
testee_state
{
behavior
make_behavior
()
{
return
{
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
[](
sub_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
-
y
;
},
};
}
};
using
testee_actor
=
stateful_actor
<
testee_state
>
;
struct
bool_list
{
std
::
vector
<
bool
>
values
;
auto
begin
()
{
return
values
.
begin
();
}
auto
end
()
{
return
values
.
end
();
}
};
bool_list
&
operator
<<
(
bool_list
&
ls
,
bool
value
)
{
ls
.
values
.
push_back
(
value
);
return
ls
;
}
}
// namespace
BEGIN_FIXTURE_SCOPE
(
test_coordinator_fixture
<>
)
SCENARIO
(
"successful checks increment the 'good' counter"
)
{
GIVEN
(
"a unit test"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
WHEN
(
"using any CHECK macro with a true statement"
)
{
THEN
(
"the 'good' counter increments by one per check"
)
{
bool_list
results
;
results
<<
CHECK_EQ
(
this_test
->
good
(),
0u
);
results
<<
CHECK_EQ
(
this_test
->
good
(),
1u
);
results
<<
CHECK_EQ
(
this_test
->
good
(),
2u
);
results
<<
CHECK
(
true
);
results
<<
CHECK_NE
(
1
,
2
);
results
<<
CHECK_LT
(
1
,
2
);
results
<<
CHECK_LE
(
1
,
2
);
results
<<
CHECK_GT
(
2
,
1
);
results
<<
CHECK_GE
(
2
,
1
);
results
<<
CHECK_EQ
(
this_test
->
good
(),
9u
);
results
<<
CHECK_EQ
(
this_test
->
bad
(),
0u
);
auto
is_true
=
[](
bool
x
)
{
return
x
;
};
CHECK
(
std
::
all_of
(
results
.
begin
(),
results
.
end
(),
is_true
));
}
}
}
}
SCENARIO
(
"unsuccessful checks increment the 'bad' counter"
)
{
GIVEN
(
"a unit test"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
WHEN
(
"using any CHECK macro with a false statement"
)
{
THEN
(
"the 'bad' counter increments by one per check"
)
{
// Run the known-to-fail tests with suppressed output.
auto
bk
=
caf
::
test
::
logger
::
instance
().
make_quiet
();
bool_list
results
;
results
<<
CHECK_EQ
(
this_test
->
good
(),
1u
);
results
<<
CHECK
(
false
);
results
<<
CHECK_NE
(
1
,
1
);
results
<<
CHECK_LT
(
2
,
1
);
results
<<
CHECK_LE
(
2
,
1
);
results
<<
CHECK_GT
(
1
,
2
);
results
<<
CHECK_GE
(
1
,
2
);
caf
::
test
::
logger
::
instance
().
levels
(
bk
);
auto
failed_checks
=
this_test
->
bad
();
this_test
->
reset
();
// Prevent the unit test from actually failing.
CHECK_EQ
(
failed_checks
,
7u
);
REQUIRE_EQ
(
results
.
values
.
size
(),
7u
);
auto
is_false
=
[](
bool
x
)
{
return
!
x
;
};
CHECK
(
std
::
all_of
(
results
.
begin
(),
results
.
end
(),
is_false
));
}
}
}
}
SCENARIO
(
"the test scheduler allows manipulating the control flow"
)
{
GIVEN
(
"some event-based actors"
)
{
auto
aut1
=
sys
.
spawn
<
testee_actor
>
();
auto
aut2
=
sys
.
spawn
<
testee_actor
>
();
auto
aut3
=
sys
.
spawn
<
testee_actor
>
();
run
();
WHEN
(
"sending messages to event-based actors"
)
{
THEN
(
"the actors become jobs in the scheduler"
)
{
CHECK
(
!
sched
.
has_job
());
self
->
send
(
aut1
,
add_atom_v
,
1
,
2
);
CHECK
(
sched
.
has_job
());
CHECK_EQ
(
sched
.
jobs
.
size
(),
1u
);
self
->
send
(
aut2
,
add_atom_v
,
2
,
3
);
CHECK_EQ
(
sched
.
jobs
.
size
(),
2u
);
self
->
send
(
aut3
,
add_atom_v
,
3
,
4
);
CHECK_EQ
(
sched
.
jobs
.
size
(),
3u
);
}
AND
(
"prioritize allows moving actors to the head of the job queue"
)
{
auto
ptr
=
[](
const
actor
&
hdl
)
{
auto
actor_ptr
=
actor_cast
<
caf
::
abstract_actor
*>
(
hdl
);
return
dynamic_cast
<
resumable
*>
(
actor_ptr
);
};
CHECK_EQ
(
&
sched
.
next_job
(),
ptr
(
aut1
));
CHECK
(
sched
.
prioritize
(
aut2
));
CHECK_EQ
(
&
sched
.
next_job
(),
ptr
(
aut2
));
CHECK
(
sched
.
prioritize
(
aut3
));
CHECK_EQ
(
&
sched
.
next_job
(),
ptr
(
aut3
));
CHECK
(
sched
.
prioritize
(
aut1
));
CHECK_EQ
(
&
sched
.
next_job
(),
ptr
(
aut1
));
}
AND
(
"peek allows inspecting the mailbox of the next job"
)
{
using
std
::
make_tuple
;
auto
peek
=
[
this
]
{
return
sched
.
peek
<
add_atom
,
int
,
int
>
();
};
CHECK_EQ
(
peek
(),
make_tuple
(
add_atom_v
,
1
,
2
));
CHECK
(
sched
.
prioritize
(
aut2
));
CHECK_EQ
(
peek
(),
make_tuple
(
add_atom_v
,
2
,
3
));
CHECK
(
sched
.
prioritize
(
aut3
));
CHECK_EQ
(
peek
(),
make_tuple
(
add_atom_v
,
3
,
4
));
}
AND
(
"run_until and run_once allows executing jobs selectively"
)
{
CHECK_EQ
(
run_until
([
this
]
{
return
sched
.
jobs
.
size
()
==
1
;
}),
2u
);
CHECK
(
run_once
());
CHECK
(
!
sched
.
has_job
());
CHECK
(
!
run_once
());
}
}
}
}
SCENARIO
(
"allow() turns into a no-op on mismatch"
)
{
GIVEN
(
"an event-based actor"
)
{
auto
aut
=
sys
.
spawn
<
testee_actor
>
();
run
();
WHEN
(
"allow()-ing a message if no message is waiting in any mailbox"
)
{
THEN
(
"allow() becomes a no-op and returns false"
)
{
CHECK
(
!
(
allow
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
))));
CHECK
(
!
(
allow
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
))));
}
}
WHEN
(
"allow()-ing a message but a different message is waiting"
)
{
THEN
(
"allow() becomes a no-op and returns false"
)
{
self
->
send
(
aut
,
sub_atom_v
,
4
,
3
);
auto
fake_sender
=
sys
.
spawn
<
testee_actor
>
();
// Wrong type.
CHECK
(
!
(
allow
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
))));
// Wrong type plus .with() check.
CHECK
(
!
(
allow
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
4
,
3
))));
// Correct type but .with() check expects different values.
CHECK
(
!
(
allow
((
sub_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
1
,
2
))));
// Correct type and matching .with() but wrong sender.
CHECK
(
!
(
allow
((
sub_atom
,
int32_t
,
int32_t
),
from
(
fake_sender
).
to
(
aut
).
with
(
_
,
4
,
3
))));
// Message must still wait in the mailbox.
auto
&
aut_dref
=
sched
.
next_job
<
testee_actor
>
();
REQUIRE_EQ
(
actor_cast
<
abstract_actor
*>
(
aut
),
&
aut_dref
);
auto
msg_ptr
=
aut_dref
.
peek_at_next_mailbox_element
();
REQUIRE_NE
(
msg_ptr
,
nullptr
);
CHECK
(
msg_ptr
->
payload
.
matches
(
sub_atom_v
,
4
,
3
));
// Drop test message.
run
();
while
(
!
self
->
mailbox
().
empty
())
self
->
dequeue
();
}
}
WHEN
(
"allow()-ing and a matching message arrives"
)
{
THEN
(
"the actor processes the message and allow() returns true"
)
{
self
->
send
(
aut
,
sub_atom_v
,
4
,
3
);
CHECK
(
sched
.
has_job
());
CHECK
(
allow
((
sub_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
4
,
3
)));
CHECK
(
!
sched
.
has_job
());
CHECK
(
allow
((
int32_t
),
from
(
aut
).
to
(
self
).
with
(
1
)));
}
}
}
}
#ifdef CAF_ENABLE_EXCEPTIONS
SCENARIO
(
"tests may check for exceptions"
)
{
GIVEN
(
"a unit test"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
WHEN
(
"using any CHECK_THROWS macro with a matching exception"
)
{
THEN
(
"the 'good' counter increments by one per check"
)
{
auto
f
=
[]
{
throw
std
::
runtime_error
(
"foo"
);
};
CHECK_THROWS_AS
(
f
(),
std
::
runtime_error
);
CHECK_THROWS_WITH
(
f
(),
"foo"
);
CHECK_THROWS_WITH_AS
(
f
(),
"foo"
,
std
::
runtime_error
);
CHECK_NOTHROW
([]
{}());
}
}
WHEN
(
"using any CHECK_THROWS macro with a unexpected exception"
)
{
THEN
(
"the 'bad' counter increments by one per check"
)
{
auto
f
=
[]
{
throw
std
::
logic_error
(
"bar"
);
};
auto
bk
=
caf
::
test
::
logger
::
instance
().
make_quiet
();
CHECK_THROWS_AS
(
f
(),
std
::
runtime_error
);
CHECK_THROWS_WITH
(
f
(),
"foo"
);
CHECK_THROWS_WITH_AS
(
f
(),
"foo"
,
std
::
runtime_error
);
CHECK_THROWS_WITH_AS
(
f
(),
"foo"
,
std
::
logic_error
);
CHECK_NOTHROW
(
f
());
caf
::
test
::
logger
::
instance
().
levels
(
bk
);
CHECK_EQ
(
this_test
->
bad
(),
5u
);
this_test
->
reset_bad
();
}
}
}
}
SCENARIO
(
"passing requirements increment the 'good' counter"
)
{
GIVEN
(
"a unit test"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
WHEN
(
"using any REQUIRE macro with a true statement"
)
{
THEN
(
"the 'good' counter increments by one per requirement"
)
{
REQUIRE_EQ
(
this_test
->
good
(),
0u
);
REQUIRE_EQ
(
this_test
->
good
(),
1u
);
REQUIRE_EQ
(
this_test
->
good
(),
2u
);
REQUIRE
(
true
);
REQUIRE_NE
(
1
,
2
);
REQUIRE_LT
(
1
,
2
);
REQUIRE_LE
(
1
,
2
);
REQUIRE_GT
(
2
,
1
);
REQUIRE_GE
(
2
,
1
);
REQUIRE_EQ
(
this_test
->
good
(),
9u
);
REQUIRE_EQ
(
this_test
->
bad
(),
0u
);
}
}
}
}
# define CHECK_FAILS(expr) \
if (true) { \
auto silent_expr = [&] { \
auto bk = caf::test::logger::instance().make_quiet(); \
auto guard = caf::detail::make_scope_guard( \
[bk] { caf::test::logger::instance().levels(bk); }); \
expr; \
}; \
CHECK_THROWS_AS(silent_expr(), test::requirement_error); \
} \
if (CHECK_EQ(this_test->bad(), 1u)) \
this_test->reset_bad()
SCENARIO
(
"failing requirements increment the 'bad' counter and throw"
)
{
GIVEN
(
"a unit test"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
WHEN
(
"using any REQUIRE macro with a true statement"
)
{
THEN
(
"the 'good' counter increments by one per requirement"
)
{
CHECK_FAILS
(
REQUIRE_EQ
(
1
,
2
));
CHECK_FAILS
(
REQUIRE_EQ
(
this_test
->
good
(),
42u
));
CHECK_FAILS
(
REQUIRE
(
false
));
CHECK_FAILS
(
REQUIRE_NE
(
1
,
1
));
CHECK_FAILS
(
REQUIRE_LT
(
2
,
1
));
CHECK_FAILS
(
REQUIRE_LE
(
2
,
1
));
CHECK_FAILS
(
REQUIRE_GT
(
1
,
2
));
CHECK_FAILS
(
REQUIRE_GE
(
1
,
2
));
}
}
}
}
SCENARIO
(
"disallow() throws when finding a prohibited message"
)
{
GIVEN
(
"an event-based actor"
)
{
auto
aut
=
sys
.
spawn
<
testee_actor
>
();
run
();
WHEN
(
"disallow()-ing a message if no message is waiting in any mailbox"
)
{
THEN
(
"disallow() becomes a no-op"
)
{
CHECK_NOTHROW
(
disallow
((
add_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut
)));
CHECK_NOTHROW
(
disallow
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
)));
}
}
WHEN
(
"disallow()-ing a message if no matching message exists"
)
{
THEN
(
"disallow() becomes a no-op"
)
{
self
->
send
(
aut
,
sub_atom_v
,
4
,
3
);
auto
fake_sender
=
sys
.
spawn
<
testee_actor
>
();
CHECK_NOTHROW
(
disallow
((
add_atom
,
int32_t
,
int32_t
),
to
(
aut
)));
CHECK_NOTHROW
(
disallow
((
add_atom
,
int32_t
,
int32_t
),
//
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_NOTHROW
(
disallow
((
add_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut
)));
CHECK_NOTHROW
(
disallow
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_NOTHROW
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
1
,
2
)));
CHECK_NOTHROW
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
//
from
(
fake_sender
).
to
(
aut
)));
CHECK_NOTHROW
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
//
from
(
fake_sender
).
to
(
aut
).
with
(
_
,
4
,
3
)));
// Drop test message.
run
();
while
(
!
self
->
mailbox
().
empty
())
self
->
dequeue
();
}
}
WHEN
(
"disallow()-ing an existing message"
)
{
THEN
(
"disallow() throws and increment the 'bad' counter"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
self
->
send
(
aut
,
sub_atom_v
,
4
,
3
);
CHECK_FAILS
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
to
(
aut
)));
CHECK_FAILS
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
//
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_FAILS
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
)));
CHECK_FAILS
(
disallow
((
sub_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
)));
}
}
}
}
SCENARIO
(
"expect() throws when not finding the required message"
)
{
GIVEN
(
"an event-based actor"
)
{
auto
this_test
=
test
::
engine
::
current_test
();
auto
aut
=
sys
.
spawn
<
testee_actor
>
();
run
();
WHEN
(
"expect()-ing a message if no message is waiting in any mailbox"
)
{
THEN
(
"expect() throws and increment the 'bad' counter"
)
{
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
to
(
aut
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
)));
auto
aut2
=
sys
.
spawn
<
testee_actor
>
();
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
to
(
aut2
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
to
(
aut2
).
with
(
_
,
_
,
_
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut2
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut2
).
with
(
_
,
_
,
_
)));
}
}
WHEN
(
"expect()-ing a message if no matching message exists"
)
{
THEN
(
"expect() throws and increment the 'bad' counter"
)
{
self
->
send
(
aut
,
sub_atom_v
,
4
,
3
);
auto
fake_sender
=
sys
.
spawn
<
testee_actor
>
();
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
to
(
aut
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut
)));
CHECK_FAILS
(
expect
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_FAILS
(
expect
((
sub_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
).
with
(
_
,
1
,
2
)));
CHECK_FAILS
(
expect
((
sub_atom
,
int32_t
,
int32_t
),
//
from
(
fake_sender
).
to
(
aut
)));
CHECK_FAILS
(
expect
((
sub_atom
,
int32_t
,
int32_t
),
//
from
(
fake_sender
).
to
(
aut
).
with
(
_
,
4
,
3
)));
// Drop test message.
run
();
while
(
!
self
->
mailbox
().
empty
())
self
->
dequeue
();
}
}
WHEN
(
"expect()-ing an existing message"
)
{
THEN
(
"expect() processes the message"
)
{
self
->
send
(
aut
,
add_atom_v
,
4
,
3
);
CHECK_NOTHROW
(
expect
((
add_atom
,
int32_t
,
int32_t
),
to
(
aut
)));
CHECK_NOTHROW
(
expect
((
int32_t
),
to
(
self
)));
self
->
send
(
aut
,
add_atom_v
,
4
,
3
);
CHECK_NOTHROW
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_NOTHROW
(
expect
((
int32_t
),
to
(
self
).
with
(
7
)));
self
->
send
(
aut
,
add_atom_v
,
4
,
3
);
CHECK_NOTHROW
(
expect
((
add_atom
,
int32_t
,
int32_t
),
from
(
self
).
to
(
aut
)));
CHECK_NOTHROW
(
expect
((
int32_t
),
from
(
aut
).
to
(
self
)));
self
->
send
(
aut
,
add_atom_v
,
4
,
3
);
CHECK_NOTHROW
(
expect
((
add_atom
,
int32_t
,
int32_t
),
//
from
(
self
).
to
(
aut
).
with
(
_
,
_
,
_
)));
CHECK_NOTHROW
(
expect
((
int32_t
),
from
(
aut
).
to
(
self
).
with
(
7
)));
}
}
}
}
#endif
END_FIXTURE_SCOPE
()
libcaf_test/caf/test/bdd_dsl.hpp
View file @
5d233fae
...
@@ -37,6 +37,16 @@
...
@@ -37,6 +37,16 @@
#define CHECK_GT(lhs, rhs) CAF_CHECK_GREATER(lhs, rhs)
#define CHECK_GT(lhs, rhs) CAF_CHECK_GREATER(lhs, rhs)
#define CHECK_GE(lhs, rhs) CAF_CHECK_GREATER_OR_EQUAL(lhs, rhs)
#define CHECK_GE(lhs, rhs) CAF_CHECK_GREATER_OR_EQUAL(lhs, rhs)
#ifdef CAF_ENABLE_EXCEPTIONS
# define CHECK_NOTHROW(expr) CAF_CHECK_NOTHROW(expr)
# define CHECK_THROWS_AS(expr, type) CAF_CHECK_THROWS_AS(expr, type)
# define CHECK_THROWS_WITH(expr, msg) CAF_CHECK_THROWS_WITH(expr, msg)
# define CHECK_THROWS_WITH_AS(expr, msg, type) \
CAF_CHECK_THROWS_WITH_AS(expr, msg, type)
#endif // CAF_ENABLE_EXCEPTIONS
#define REQUIRE(what) CAF_REQUIRE(what)
#define REQUIRE(what) CAF_REQUIRE(what)
#define REQUIRE_EQ(lhs, rhs) CAF_REQUIRE_EQUAL(lhs, rhs)
#define REQUIRE_EQ(lhs, rhs) CAF_REQUIRE_EQUAL(lhs, rhs)
#define REQUIRE_NE(lhs, rhs) CAF_REQUIRE_NOT_EQUAL(lhs, rhs)
#define REQUIRE_NE(lhs, rhs) CAF_REQUIRE_NOT_EQUAL(lhs, rhs)
...
...
libcaf_test/caf/test/dsl.hpp
View file @
5d233fae
...
@@ -86,48 +86,42 @@ T get(const has_outer_type<U>&);
...
@@ -86,48 +86,42 @@ T get(const has_outer_type<U>&);
template
<
class
T
,
class
U
>
template
<
class
T
,
class
U
>
bool
is
(
const
has_outer_type
<
U
>&
);
bool
is
(
const
has_outer_type
<
U
>&
);
template
<
class
Tup
>
template
<
class
Tup
le
>
class
elementwise_compare_inspector
{
class
elementwise_compare_inspector
{
public:
public:
using
result_type
=
bool
;
using
result_type
=
bool
;
template
<
size_t
X
>
static
constexpr
size_t
num_values
=
std
::
tuple_size_v
<
Tuple
>
;
using
pos
=
std
::
integral_constant
<
size_t
,
X
>
;
explicit
elementwise_compare_inspector
(
const
Tup
&
xs
)
:
xs_
(
xs
)
{
explicit
elementwise_compare_inspector
(
const
Tup
le
&
xs
)
:
xs_
(
xs
)
{
// nop
// nop
}
}
template
<
class
...
Ts
>
template
<
class
...
Ts
>
bool
operator
()(
const
Ts
&
...
xs
)
{
bool
operator
()(
const
Ts
&
...
xs
)
const
{
return
iterate
(
pos
<
0
>
{},
xs
...);
static_assert
(
sizeof
...(
Ts
)
==
num_values
);
return
apply
(
std
::
forward_as_tuple
(
xs
...),
std
::
index_sequence_for
<
Ts
...
>
{});
}
}
private:
private:
template
<
size_t
X
>
template
<
class
OtherTuple
,
size_t
...
Is
>
bool
iterate
(
pos
<
X
>
)
{
bool
apply
(
OtherTuple
values
,
std
::
index_sequence
<
Is
...
>
)
const
{
// end of recursion
using
std
::
get
;
return
true
;
return
(
check
(
get
<
Is
>
(
values
),
get
<
Is
>
(
xs_
))
&&
...);
}
template
<
size_t
X
,
class
T
,
class
...
Ts
>
bool
iterate
(
pos
<
X
>
,
const
T
&
y
,
const
Ts
&
...
ys
)
{
std
::
integral_constant
<
size_t
,
X
+
1
>
next
;
check
(
y
,
get
<
X
>
(
xs_
));
return
iterate
(
next
,
ys
...);
}
}
template
<
class
T
,
class
U
>
template
<
class
T
,
class
U
>
static
void
check
(
const
T
&
x
,
const
U
&
y
)
{
static
bool
check
(
const
T
&
x
,
const
U
&
y
)
{
CAF_CHECK_EQUAL
(
x
,
y
)
;
return
x
==
y
;
}
}
template
<
class
T
>
template
<
class
T
>
static
void
check
(
const
T
&
,
const
wildcard
&
)
{
static
bool
check
(
const
T
&
,
const
wildcard
&
)
{
// nop
return
true
;
}
}
const
Tup
&
xs_
;
const
Tup
le
&
xs_
;
};
};
// -- unified access to all actor handles in CAF -------------------------------
// -- unified access to all actor handles in CAF -------------------------------
...
@@ -257,16 +251,18 @@ caf::optional<std::tuple<T, Ts...>> try_extract(caf_handle x) {
...
@@ -257,16 +251,18 @@ caf::optional<std::tuple<T, Ts...>> try_extract(caf_handle x) {
/// the mailbox. Fails on an empty mailbox or if the content of the next
/// the mailbox. Fails on an empty mailbox or if the content of the next
/// element does not match `<T, Ts...>`.
/// element does not match `<T, Ts...>`.
template
<
class
T
,
class
...
Ts
>
template
<
class
T
,
class
...
Ts
>
std
::
tuple
<
T
,
Ts
...
>
extract
(
caf_handle
x
)
{
std
::
tuple
<
T
,
Ts
...
>
extract
(
caf_handle
x
,
int
src_line
)
{
auto
result
=
try_extract
<
T
,
Ts
...
>
(
x
);
if
(
auto
result
=
try_extract
<
T
,
Ts
...
>
(
x
);
result
!=
caf
::
none
)
{
if
(
result
==
caf
::
none
)
{
return
std
::
move
(
*
result
);
}
else
{
auto
ptr
=
x
->
peek_at_next_mailbox_element
();
auto
ptr
=
x
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
if
(
ptr
==
nullptr
)
CAF_FAIL
(
"Mailbox is empty"
);
CAF_FAIL
(
"cannot peek at the next message: mailbox is empty"
,
src_line
);
CAF_FAIL
(
else
"Message does not match expected pattern: "
<<
to_string
(
ptr
->
content
()));
CAF_FAIL
(
"message does not match expected types: "
<<
to_string
(
ptr
->
content
()),
src_line
);
}
}
return
std
::
move
(
*
result
);
}
}
template
<
class
T
,
class
...
Ts
>
template
<
class
T
,
class
...
Ts
>
...
@@ -277,22 +273,25 @@ bool received(caf_handle x) {
...
@@ -277,22 +273,25 @@ bool received(caf_handle x) {
template
<
class
...
Ts
>
template
<
class
...
Ts
>
class
expect_clause
{
class
expect_clause
{
public:
public:
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
:
sched_
(
sched
),
src_line_
(
src_line
)
{
peek_
=
[
=
]
{
peek_
=
[
this
]
{
/// The extractor will call CAF_FAIL on a type mismatch, essentially
/// The extractor will call CAF_FAIL on a type mismatch, essentially
/// performing a type check when ignoring the result.
/// performing a type check when ignoring the result.
extract
<
Ts
...
>
(
dest_
);
extract
<
Ts
...
>
(
dest_
,
src_line_
);
};
};
}
}
expect_clause
(
expect_clause
&&
other
)
=
default
;
expect_clause
(
expect_clause
&&
other
)
=
default
;
~
expect_clause
()
{
void
eval
(
const
char
*
type_str
,
const
char
*
fields_str
)
{
if
(
peek_
!=
nullptr
)
{
using
namespace
caf
;
peek_
();
test
::
logger
::
instance
().
verbose
()
run_once
();
<<
term
::
yellow
<<
" -> "
<<
term
::
reset
}
<<
test
::
logger
::
stream
::
reset_flags_t
{}
<<
"expect "
<<
type_str
<<
"."
<<
fields_str
<<
" [line "
<<
src_line_
<<
"]
\n
"
;
peek_
();
run_once
();
}
}
expect_clause
&
from
(
const
wildcard
&
)
{
expect_clause
&
from
(
const
wildcard
&
)
{
...
@@ -307,12 +306,15 @@ public:
...
@@ -307,12 +306,15 @@ public:
template
<
class
Handle
>
template
<
class
Handle
>
expect_clause
&
to
(
const
Handle
&
whom
)
{
expect_clause
&
to
(
const
Handle
&
whom
)
{
CAF_REQUIRE
(
sched_
.
prioritize
(
whom
));
if
(
!
sched_
.
prioritize
(
whom
))
CAF_FAIL
(
"there is no message for the designated receiver"
,
src_line_
);
dest_
=
&
sched_
.
next_job
<
caf
::
abstract_actor
>
();
dest_
=
&
sched_
.
next_job
<
caf
::
abstract_actor
>
();
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
CAF_REQUIRE
(
ptr
!=
nullptr
);
if
(
ptr
==
nullptr
)
if
(
src_
)
CAF_FAIL
(
"the designated receiver has no message in its mailbox"
,
CAF_REQUIRE_EQUAL
(
ptr
->
sender
,
src_
);
src_line_
);
if
(
src_
&&
ptr
->
sender
!=
src_
)
CAF_FAIL
(
"the found message is not from the expected sender"
,
src_line_
);
return
*
this
;
return
*
this
;
}
}
...
@@ -322,14 +324,17 @@ public:
...
@@ -322,14 +324,17 @@ public:
}
}
template
<
class
...
Us
>
template
<
class
...
Us
>
void
with
(
Us
&&
...
xs
)
{
expect_clause
&
with
(
Us
&&
...
xs
)
{
peek_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
peek_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
using
namespace
caf
::
detail
;
auto
inspector
=
elementwise_compare_inspector
<
decltype
(
tmp
)
>
{
tmp
};
elementwise_compare_inspector
<
decltype
(
tmp
)
>
inspector
{
tmp
};
auto
content
=
extract
<
Ts
...
>
(
dest_
,
src_line_
);
auto
ys
=
extract
<
Ts
...
>
(
dest_
);
if
(
!
std
::
apply
(
inspector
,
content
))
auto
ys_indices
=
get_indices
(
ys
);
CAF_FAIL
(
"message does not match expected content: "
CAF_REQUIRE
(
apply_args
(
inspector
,
ys_indices
,
ys
));
<<
caf
::
deep_to_string
(
tmp
)
<<
" vs "
<<
caf
::
deep_to_string
(
content
),
src_line_
);
};
};
return
*
this
;
}
}
protected:
protected:
...
@@ -343,26 +348,33 @@ protected:
...
@@ -343,26 +348,33 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
strong_actor_ptr
src_
;
caf
::
abstract_actor
*
dest_
;
caf
::
abstract_actor
*
dest_
=
nullptr
;
std
::
function
<
void
()
>
peek_
;
std
::
function
<
void
()
>
peek_
;
int
src_line_
;
};
};
template
<
>
template
<
>
class
expect_clause
<
void
>
{
class
expect_clause
<
void
>
{
public:
public:
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
:
sched_
(
sched
),
src_line_
(
src_line
)
{
// nop
// nop
}
}
expect_clause
(
expect_clause
&&
other
)
=
default
;
expect_clause
(
expect_clause
&&
other
)
=
default
;
~
expect_clause
()
{
void
eval
(
const
char
*
type_str
,
const
char
*
fields_str
)
{
using
namespace
caf
;
test
::
logger
::
instance
().
verbose
()
<<
term
::
yellow
<<
" -> "
<<
term
::
reset
<<
test
::
logger
::
stream
::
reset_flags_t
{}
<<
"expect(void)."
<<
fields_str
<<
" [line "
<<
src_line_
<<
"]
\n
"
;
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
if
(
ptr
==
nullptr
)
CAF_FAIL
(
"no message found"
);
CAF_FAIL
(
"no message found"
,
src_line_
);
if
(
!
ptr
->
content
().
empty
())
if
(
!
ptr
->
content
().
empty
())
CAF_FAIL
(
"non-empty message found: "
<<
to_string
(
ptr
->
content
()));
CAF_FAIL
(
"non-empty message found: "
<<
to_string
(
ptr
->
content
()),
src_line_
);
run_once
();
run_once
();
}
}
...
@@ -403,14 +415,15 @@ protected:
...
@@ -403,14 +415,15 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
strong_actor_ptr
src_
;
caf
::
abstract_actor
*
dest_
;
caf
::
abstract_actor
*
dest_
=
nullptr
;
int
src_line_
;
};
};
template
<
class
...
Ts
>
template
<
class
...
Ts
>
class
inject_clause
{
class
inject_clause
{
public:
public:
explicit
inject_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
explicit
inject_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
:
sched_
(
sched
),
src_line_
(
src_line
)
{
// nop
// nop
}
}
...
@@ -428,27 +441,39 @@ public:
...
@@ -428,27 +441,39 @@ public:
return
*
this
;
return
*
this
;
}
}
void
with
(
Ts
...
xs
)
{
inject_clause
&
with
(
Ts
...
xs
)
{
msg_
=
caf
::
make_message
(
std
::
move
(
xs
)...);
return
*
this
;
}
void
eval
(
const
char
*
type_str
,
const
char
*
fields_str
)
{
using
namespace
caf
;
test
::
logger
::
instance
().
verbose
()
<<
term
::
yellow
<<
" -> "
<<
term
::
reset
<<
test
::
logger
::
stream
::
reset_flags_t
{}
<<
"inject"
<<
type_str
<<
"."
<<
fields_str
<<
" [line "
<<
src_line_
<<
"]
\n
"
;
if
(
dest_
==
nullptr
)
if
(
dest_
==
nullptr
)
CAF_FAIL
(
"missing .to() in inject() statement"
);
CAF_FAIL
(
"missing .to() in inject() statement"
,
src_line_
);
auto
msg
=
caf
::
make_message
(
std
::
move
(
xs
)...);
else
if
(
src_
==
nullptr
)
if
(
src_
==
nullptr
)
caf
::
anon_send
(
caf
::
actor_cast
<
caf
::
actor
>
(
dest_
),
msg_
);
caf
::
anon_send
(
caf
::
actor_cast
<
caf
::
actor
>
(
dest_
),
msg
);
else
else
caf
::
send_as
(
caf
::
actor_cast
<
caf
::
actor
>
(
src_
),
caf
::
send_as
(
caf
::
actor_cast
<
caf
::
actor
>
(
src_
),
caf
::
actor_cast
<
caf
::
actor
>
(
dest_
),
msg
);
caf
::
actor_cast
<
caf
::
actor
>
(
dest_
),
msg
_
);
if
(
!
sched_
.
prioritize
(
dest_
))
if
(
!
sched_
.
prioritize
(
dest_
))
CAF_FAIL
(
"inject: failed to schedule destination actor"
);
CAF_FAIL
(
"inject: failed to schedule destination actor"
,
src_line_
);
auto
dest_ptr
=
&
sched_
.
next_job
<
caf
::
abstract_actor
>
();
auto
dest_ptr
=
&
sched_
.
next_job
<
caf
::
abstract_actor
>
();
auto
ptr
=
dest_ptr
->
peek_at_next_mailbox_element
();
auto
ptr
=
dest_ptr
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
if
(
ptr
==
nullptr
)
CAF_FAIL
(
"inject: failed to get next message from destination actor"
);
CAF_FAIL
(
"inject: failed to get next message from destination actor"
,
src_line_
);
if
(
ptr
->
sender
!=
src_
)
if
(
ptr
->
sender
!=
src_
)
CAF_FAIL
(
"inject: found unexpected sender for the next message"
);
CAF_FAIL
(
"inject: found unexpected sender for the next message"
,
if
(
ptr
->
payload
.
cptr
()
!=
msg
.
cptr
())
src_line_
);
if
(
ptr
->
payload
.
cptr
()
!=
msg_
.
cptr
())
CAF_FAIL
(
"inject: found unexpected message => "
<<
ptr
->
payload
<<
" !! "
CAF_FAIL
(
"inject: found unexpected message => "
<<
ptr
->
payload
<<
" !! "
<<
msg
);
<<
msg_
,
msg
.
reset
();
// drop local reference before running the actor
src_line_
);
msg_
.
reset
();
// drop local reference before running the actor
run_once
();
run_once
();
}
}
...
@@ -465,26 +490,25 @@ protected:
...
@@ -465,26 +490,25 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
strong_actor_ptr
src_
;
caf
::
strong_actor_ptr
dest_
;
caf
::
strong_actor_ptr
dest_
;
caf
::
message
msg_
;
int
src_line_
;
};
};
template
<
class
...
Ts
>
template
<
class
...
Ts
>
class
allow_clause
{
class
allow_clause
{
public:
public:
explicit
allow_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
explicit
allow_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
:
sched_
(
sched
),
src_line_
(
src_line
)
{
peek_
=
[
=
]
{
peek_
=
[
this
]
{
if
(
dest_
!=
nullptr
)
if
(
dest_
!=
nullptr
)
return
try_extract
<
Ts
...
>
(
dest_
)
!=
caf
::
none
;
return
try_extract
<
Ts
...
>
(
dest_
)
!=
caf
::
none
;
return
false
;
else
return
false
;
};
};
}
}
allow_clause
(
allow_clause
&&
other
)
=
default
;
allow_clause
(
allow_clause
&&
other
)
=
default
;
~
allow_clause
()
{
eval
();
}
allow_clause
&
from
(
const
wildcard
&
)
{
allow_clause
&
from
(
const
wildcard
&
)
{
return
*
this
;
return
*
this
;
}
}
...
@@ -499,31 +523,46 @@ public:
...
@@ -499,31 +523,46 @@ public:
allow_clause
&
to
(
const
Handle
&
whom
)
{
allow_clause
&
to
(
const
Handle
&
whom
)
{
if
(
sched_
.
prioritize
(
whom
))
if
(
sched_
.
prioritize
(
whom
))
dest_
=
&
sched_
.
next_job
<
caf
::
abstract_actor
>
();
dest_
=
&
sched_
.
next_job
<
caf
::
abstract_actor
>
();
else
if
(
auto
ptr
=
caf
::
actor_cast
<
caf
::
abstract_actor
*>
(
whom
))
dest_
=
dynamic_cast
<
caf
::
blocking_actor
*>
(
ptr
);
return
*
this
;
return
*
this
;
}
}
template
<
class
...
Us
>
template
<
class
...
Us
>
void
with
(
Us
&&
...
xs
)
{
allow_clause
&
with
(
Us
&&
...
xs
)
{
peek_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
peek_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
using
namespace
caf
::
detail
;
using
namespace
caf
::
detail
;
elementwise_compare_inspector
<
decltype
(
tmp
)
>
inspector
{
tmp
};
if
(
dest_
!=
nullptr
)
{
auto
ys
=
try_extract
<
Ts
...
>
(
dest_
);
if
(
auto
ys
=
try_extract
<
Ts
...
>
(
dest_
);
ys
!=
caf
::
none
)
{
if
(
ys
!=
caf
::
none
)
{
elementwise_compare_inspector
<
decltype
(
tmp
)
>
inspector
{
tmp
};
auto
ys_indices
=
get_indices
(
*
ys
);
auto
ys_indices
=
get_indices
(
*
ys
);
return
apply_args
(
inspector
,
ys_indices
,
*
ys
);
return
apply_args
(
inspector
,
ys_indices
,
*
ys
);
}
}
}
return
false
;
return
false
;
};
};
return
*
this
;
}
}
bool
eval
()
{
bool
eval
(
const
char
*
type_str
,
const
char
*
fields_str
)
{
if
(
peek_
!=
nullptr
)
{
using
namespace
caf
;
if
(
peek_
())
{
test
::
logger
::
instance
().
verbose
()
run_once
();
<<
term
::
yellow
<<
" -> "
<<
term
::
reset
return
true
;
<<
test
::
logger
::
stream
::
reset_flags_t
{}
<<
"allow"
<<
type_str
<<
"."
}
<<
fields_str
<<
" [line "
<<
src_line_
<<
"]
\n
"
;
if
(
!
dest_
)
{
return
false
;
}
if
(
auto
msg_ptr
=
dest_
->
peek_at_next_mailbox_element
();
!
msg_ptr
)
{
return
false
;
}
else
if
(
src_
&&
msg_ptr
->
sender
!=
src_
)
{
return
false
;
}
else
if
(
peek_
())
{
run_once
();
return
true
;
}
else
{
return
false
;
}
}
return
false
;
}
}
protected:
protected:
...
@@ -537,15 +576,16 @@ protected:
...
@@ -537,15 +576,16 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
strong_actor_ptr
src_
;
caf
::
abstract_actor
*
dest_
;
caf
::
abstract_actor
*
dest_
=
nullptr
;
std
::
function
<
bool
()
>
peek_
;
std
::
function
<
bool
()
>
peek_
;
int
src_line_
;
};
};
template
<
class
...
Ts
>
template
<
class
...
Ts
>
class
disallow_clause
{
class
disallow_clause
{
public:
public:
disallow_clause
()
{
disallow_clause
(
int
src_line
)
:
src_line_
(
src_line
)
{
check_
=
[
=
]
{
check_
=
[
this
]
{
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
if
(
ptr
==
nullptr
)
return
;
return
;
...
@@ -553,17 +593,13 @@ public:
...
@@ -553,17 +593,13 @@ public:
return
;
return
;
auto
res
=
try_extract
<
Ts
...
>
(
dest_
);
auto
res
=
try_extract
<
Ts
...
>
(
dest_
);
if
(
res
!=
caf
::
none
)
if
(
res
!=
caf
::
none
)
CAF_FAIL
(
"received disallowed message: "
<<
caf
::
deep_to_string
(
*
ptr
));
CAF_FAIL
(
"received disallowed message: "
<<
caf
::
deep_to_string
(
*
ptr
),
src_line_
);
};
};
}
}
disallow_clause
(
disallow_clause
&&
other
)
=
default
;
disallow_clause
(
disallow_clause
&&
other
)
=
default
;
~
disallow_clause
()
{
if
(
check_
!=
nullptr
)
check_
();
}
disallow_clause
&
from
(
const
wildcard
&
)
{
disallow_clause
&
from
(
const
wildcard
&
)
{
return
*
this
;
return
*
this
;
}
}
...
@@ -579,7 +615,7 @@ public:
...
@@ -579,7 +615,7 @@ public:
}
}
template
<
class
...
Us
>
template
<
class
...
Us
>
void
with
(
Us
&&
...
xs
)
{
disallow_clause
&
with
(
Us
&&
...
xs
)
{
check_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
check_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
if
(
ptr
==
nullptr
)
...
@@ -593,15 +629,26 @@ public:
...
@@ -593,15 +629,26 @@ public:
auto
&
ys
=
*
res
;
auto
&
ys
=
*
res
;
auto
ys_indices
=
get_indices
(
ys
);
auto
ys_indices
=
get_indices
(
ys
);
if
(
apply_args
(
inspector
,
ys_indices
,
ys
))
if
(
apply_args
(
inspector
,
ys_indices
,
ys
))
CAF_FAIL
(
"received disallowed message: "
<<
CAF_ARG
(
*
res
));
CAF_FAIL
(
"received disallowed message: "
<<
CAF_ARG
(
*
res
)
,
src_line_
);
}
}
};
};
return
*
this
;
}
void
eval
(
const
char
*
type_str
,
const
char
*
fields_str
)
{
using
namespace
caf
;
test
::
logger
::
instance
().
verbose
()
<<
term
::
yellow
<<
" -> "
<<
term
::
reset
<<
test
::
logger
::
stream
::
reset_flags_t
{}
<<
"disallow"
<<
type_str
<<
"."
<<
fields_str
<<
" [line "
<<
src_line_
<<
"]
\n
"
;
check_
();
}
}
protected:
protected:
caf_handle
src_
;
caf_handle
src_
;
caf_handle
dest_
;
caf_handle
dest_
;
std
::
function
<
void
()
>
check_
;
std
::
function
<
void
()
>
check_
;
int
src_line_
;
};
};
template
<
class
...
Ts
>
template
<
class
...
Ts
>
...
@@ -747,6 +794,7 @@ public:
...
@@ -747,6 +794,7 @@ public:
size_t
progress
=
0
;
size_t
progress
=
0
;
while
(
consume_message
())
{
while
(
consume_message
())
{
++
progress
;
++
progress
;
++
events
;
if
(
predicate
())
{
if
(
predicate
())
{
CAF_LOG_DEBUG
(
"stop due to predicate:"
<<
CAF_ARG
(
events
));
CAF_LOG_DEBUG
(
"stop due to predicate:"
<<
CAF_ARG
(
events
));
return
events
;
return
events
;
...
@@ -754,18 +802,20 @@ public:
...
@@ -754,18 +802,20 @@ public:
}
}
while
(
handle_io_event
())
{
while
(
handle_io_event
())
{
++
progress
;
++
progress
;
++
events
;
if
(
predicate
())
{
if
(
predicate
())
{
CAF_LOG_DEBUG
(
"stop due to predicate:"
<<
CAF_ARG
(
events
));
CAF_LOG_DEBUG
(
"stop due to predicate:"
<<
CAF_ARG
(
events
));
return
events
;
return
events
;
}
}
}
}
if
(
trigger_timeout
())
if
(
trigger_timeout
())
{
++
progress
;
++
progress
;
++
events
;
}
if
(
progress
==
0
)
{
if
(
progress
==
0
)
{
CAF_LOG_DEBUG
(
"no activity left:"
<<
CAF_ARG
(
events
));
CAF_LOG_DEBUG
(
"no activity left:"
<<
CAF_ARG
(
events
));
return
events
;
return
events
;
}
}
events
+=
progress
;
}
}
}
}
...
@@ -875,32 +925,22 @@ T unbox(T* x) {
...
@@ -875,32 +925,22 @@ T unbox(T* x) {
/// Convenience macro for defining expect clauses.
/// Convenience macro for defining expect clauses.
#define expect(types, fields) \
#define expect(types, fields) \
do { \
(expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched, __LINE__}.fields.eval( \
CAF_MESSAGE("expect" << #types << "." << #fields); \
#types, #fields))
expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched}.fields; \
} while (false)
#define inject(types, fields) \
#define inject(types, fields) \
do { \
(inject_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched, __LINE__}.fields.eval( \
CAF_MESSAGE("inject" << #types << "." << #fields); \
#types, #fields))
inject_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched}.fields; \
} while (false)
/// Convenience macro for defining allow clauses.
/// Convenience macro for defining allow clauses.
#define allow(types, fields) \
#define allow(types, fields) \
([&] { \
(allow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched, __LINE__}.fields.eval( \
CAF_MESSAGE("allow" << #types << "." << #fields); \
#types, #fields))
allow_clause<CAF_EXPAND(CAF_DSL_LIST types)> x{sched}; \
x.fields; \
return x.eval(); \
})()
/// Convenience macro for defining disallow clauses.
/// Convenience macro for defining disallow clauses.
#define disallow(types, fields) \
#define disallow(types, fields) \
do { \
(disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{__LINE__}.fields.eval( \
CAF_MESSAGE("disallow" << #types << "." << #fields); \
#types, #fields))
disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{}.fields; \
} while (false)
/// Defines the required base type for testee states in the current namespace.
/// Defines the required base type for testee states in the current namespace.
#define TESTEE_SETUP() \
#define TESTEE_SETUP() \
...
...
libcaf_test/caf/test/io_dsl.hpp
View file @
5d233fae
...
@@ -55,9 +55,9 @@ public:
...
@@ -55,9 +55,9 @@ public:
/// @param fun A function object for delegating to the parent's `exec_all`.
/// @param fun A function object for delegating to the parent's `exec_all`.
test_node_fixture
(
run_all_nodes_fun
fun
)
test_node_fixture
(
run_all_nodes_fun
fun
)
:
mm
(
this
->
sys
.
middleman
()),
:
mm
(
this
->
sys
.
middleman
()),
mpx
(
dynamic_cast
<
caf
::
io
::
network
::
test_multiplexer
&>
(
mm
.
backend
())),
mpx
(
dynamic_cast
<
caf
::
io
::
network
::
test_multiplexer
&>
(
mm
.
backend
())),
run_all_nodes
(
std
::
move
(
fun
))
{
run_all_nodes
(
std
::
move
(
fun
))
{
bb
=
mm
.
named_broker
<
caf
::
io
::
basp_broker
>
(
"BASP"
);
bb
=
mm
.
named_broker
<
caf
::
io
::
basp_broker
>
(
"BASP"
);
}
}
...
@@ -124,9 +124,7 @@ void exec_all_fixtures(Iterator first, Iterator last) {
...
@@ -124,9 +124,7 @@ void exec_all_fixtures(Iterator first, Iterator last) {
return
x
->
sched
.
try_run_once
()
||
x
->
mpx
.
read_data
()
return
x
->
sched
.
try_run_once
()
||
x
->
mpx
.
read_data
()
||
x
->
mpx
.
try_exec_runnable
()
||
x
->
mpx
.
try_accept_connection
();
||
x
->
mpx
.
try_exec_runnable
()
||
x
->
mpx
.
try_accept_connection
();
};
};
auto
trigger_timeouts
=
[](
fixture_ptr
x
)
{
auto
trigger_timeouts
=
[](
fixture_ptr
x
)
{
x
->
sched
.
trigger_timeouts
();
};
x
->
sched
.
trigger_timeouts
();
};
for
(;;)
{
for
(;;)
{
// Exhaust all messages in the system.
// Exhaust all messages in the system.
while
(
std
::
any_of
(
first
,
last
,
advance
))
while
(
std
::
any_of
(
first
,
last
,
advance
))
...
@@ -167,9 +165,8 @@ public:
...
@@ -167,9 +165,8 @@ public:
/// (calls `publish`).
/// (calls `publish`).
/// @returns randomly picked connection handles for the server and the client.
/// @returns randomly picked connection handles for the server and the client.
std
::
pair
<
connection_handle
,
connection_handle
>
std
::
pair
<
connection_handle
,
connection_handle
>
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
std
::
string
host
,
std
::
string
host
,
uint16_t
port
,
uint16_t
port
,
accept_handle
server_accept_hdl
)
{
accept_handle
server_accept_hdl
)
{
auto
server_hdl
=
next_connection_handle
();
auto
server_hdl
=
next_connection_handle
();
auto
client_hdl
=
next_connection_handle
();
auto
client_hdl
=
next_connection_handle
();
server
.
mpx
.
prepare_connection
(
server_accept_hdl
,
server_hdl
,
client
.
mpx
,
server
.
mpx
.
prepare_connection
(
server_accept_hdl
,
server_hdl
,
client
.
mpx
,
...
@@ -181,8 +178,8 @@ public:
...
@@ -181,8 +178,8 @@ public:
/// (calls `publish`).
/// (calls `publish`).
/// @returns randomly picked connection handles for the server and the client.
/// @returns randomly picked connection handles for the server and the client.
std
::
pair
<
connection_handle
,
connection_handle
>
std
::
pair
<
connection_handle
,
connection_handle
>
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
std
::
string
host
,
std
::
string
host
,
uint16_t
port
)
{
uint16_t
port
)
{
return
prepare_connection
(
server
,
client
,
std
::
move
(
host
),
port
,
return
prepare_connection
(
server
,
client
,
std
::
move
(
host
),
port
,
next_accept_handle
());
next_accept_handle
());
}
}
...
@@ -206,7 +203,7 @@ public:
...
@@ -206,7 +203,7 @@ public:
}
}
/// Type-erased callback for calling `exec_all`.
/// Type-erased callback for calling `exec_all`.
std
::
function
<
void
()
>
exec_all_callback
()
{
std
::
function
<
void
()
>
exec_all_callback
()
{
return
[
&
]
{
exec_all
();
};
return
[
&
]
{
exec_all
();
};
}
}
...
@@ -273,13 +270,9 @@ public:
...
@@ -273,13 +270,9 @@ public:
};
};
#define expect_on(where, types, fields) \
#define expect_on(where, types, fields) \
do { \
(expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched, __LINE__} \
CAF_MESSAGE(#where << ": expect" << #types << "." << #fields); \
.fields.eval(#types, #fields))
expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched}.fields; \
} while (false)
#define disallow_on(where, types, fields) \
#define disallow_on(where, types, fields) \
do { \
(disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched, __LINE__} \
CAF_MESSAGE(#where << ": disallow" << #types << "." << #fields); \
.fields.eval(#types, #fields))
disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched}.fields; \
} while (false)
libcaf_test/caf/test/unit_test.hpp
View file @
5d233fae
...
@@ -15,10 +15,12 @@
...
@@ -15,10 +15,12 @@
#include <thread>
#include <thread>
#include <vector>
#include <vector>
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/fwd.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/logger.hpp"
#include "caf/optional.hpp"
#include "caf/optional.hpp"
#include "caf/raise_error.hpp"
#include "caf/term.hpp"
#include "caf/term.hpp"
#include "caf/variant.hpp"
#include "caf/variant.hpp"
...
@@ -27,6 +29,20 @@
...
@@ -27,6 +29,20 @@
namespace
caf
::
test
{
namespace
caf
::
test
{
#ifdef CAF_ENABLE_EXCEPTIONS
class
requirement_error
:
public
std
::
exception
{
public:
requirement_error
(
std
::
string
msg
);
const
char
*
what
()
const
noexcept
override
;
private:
std
::
string
what_
;
};
#endif // CAF_ENABLE_EXCEPTIONS
// -- Function objects for implementing CAF_CHECK_* macros ---------------------
// -- Function objects for implementing CAF_CHECK_* macros ---------------------
template
<
class
F
>
template
<
class
F
>
...
@@ -235,6 +251,16 @@ public:
...
@@ -235,6 +251,16 @@ public:
return
bad_
;
return
bad_
;
}
}
void
reset
()
{
expected_failures_
=
0
;
good_
=
0
;
bad_
=
0
;
}
void
reset_bad
()
{
bad_
=
0
;
}
bool
disabled
()
const
noexcept
{
bool
disabled
()
const
noexcept
{
return
disabled_
;
return
disabled_
;
}
}
...
@@ -339,6 +365,25 @@ public:
...
@@ -339,6 +365,25 @@ public:
level
lvl_
;
level
lvl_
;
};
};
auto
levels
()
{
return
std
::
make_pair
(
level_console_
,
level_file_
);
}
void
levels
(
std
::
pair
<
level
,
level
>
values
)
{
std
::
tie
(
level_console_
,
level_file_
)
=
values
;
}
void
levels
(
level
console_lvl
,
level
file_lvl
)
{
level_console_
=
console_lvl
;
level_file_
=
file_lvl
;
}
auto
make_quiet
()
{
auto
res
=
levels
();
levels
(
level
::
quiet
,
level
::
quiet
);
return
res
;
}
stream
error
();
stream
error
();
stream
info
();
stream
info
();
stream
verbose
();
stream
verbose
();
...
@@ -474,21 +519,42 @@ bool check_un(bool result, const char* file, size_t line, const char* expr);
...
@@ -474,21 +519,42 @@ bool check_un(bool result, const char* file, size_t line, const char* expr);
bool
check_bin
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
,
bool
check_bin
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
,
const
std
::
string
&
lhs
,
const
std
::
string
&
rhs
);
const
std
::
string
&
lhs
,
const
std
::
string
&
rhs
);
void
require_un
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
);
void
require_bin
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
,
const
std
::
string
&
lhs
,
const
std
::
string
&
rhs
);
}
// namespace detail
}
// namespace detail
}
// namespace caf::test
}
// namespace caf::test
// on the global namespace so that it can hidden via namespace-scoping
// on the global namespace so that it can hidden via namespace-scoping
using
caf_test_case_auto_fixture
=
caf
::
test
::
dummy_fixture
;
using
caf_test_case_auto_fixture
=
caf
::
test
::
dummy_fixture
;
#define CAF_TEST_
PRINT(level, msg, colorcode)
\
#define CAF_TEST_
LOG_MSG_4(level, colorcode, msg, line)
\
(::caf::test::logger::instance().level() \
(::caf::test::logger::instance().level() \
<< ::caf::term::colorcode << " -> " << ::caf::term::reset \
<< ::caf::term::colorcode << " -> " << ::caf::term::reset \
<< ::caf::test::logger::stream::reset_flags_t{} << msg << " [line " \
<< ::caf::test::logger::stream::reset_flags_t{} << msg << " [line " << line \
<< __LINE__ << "]\n")
<< "]\n")
#define CAF_TEST_LOG_MSG_3(level, colorcode, msg) \
CAF_TEST_LOG_MSG_4(level, colorcode, msg, __LINE__)
#ifdef CAF_MSVC
# define CAF_TEST_LOG_MSG(...) \
CAF_PP_CAT(CAF_PP_OVERLOAD(CAF_TEST_LOG_MSG_, __VA_ARGS__)(__VA_ARGS__), \
CAF_PP_EMPTY())
#else
# define CAF_TEST_LOG_MSG(...) \
CAF_PP_OVERLOAD(CAF_TEST_LOG_MSG_, __VA_ARGS__)(__VA_ARGS__)
#endif
#define CAF_TEST_PRINT_ERROR(...) CAF_TEST_LOG_MSG(error, red, __VA_ARGS__)
#define CAF_TEST_PRINT_INFO(...) CAF_TEST_LOG_MSG(info, yellow, __VA_ARGS__)
#define CAF_TEST_PRINT_VERBOSE(...) \
CAF_TEST_LOG_MSG(verbose, yellow, __VA_ARGS__)
#define CAF_TEST_PRINT_ERROR(msg) CAF_TEST_PRINT(info, msg, red)
#define CAF_TEST_PRINT(level, msg, colorcode) \
#define CAF_TEST_PRINT_INFO(msg) CAF_TEST_PRINT(info, msg, yellow)
CAF_TEST_LOG_MSG(level, colorcode, msg)
#define CAF_TEST_PRINT_VERBOSE(msg) CAF_TEST_PRINT(verbose, msg, yellow)
#define CAF_PASTE_CONCAT(lhs, rhs) lhs##rhs
#define CAF_PASTE_CONCAT(lhs, rhs) lhs##rhs
...
@@ -514,54 +580,21 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
...
@@ -514,54 +580,21 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
::caf::test::engine::last_check_line(__LINE__); \
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
} while (false)
#define CAF_CHECK(...) \
#define CAF_CHECK(expr) \
[](bool expr_result) { \
::caf::test::detail::check_un(static_cast<bool>(expr), __FILE__, __LINE__, \
return ::caf::test::detail::check_un(expr_result, __FILE__, __LINE__, \
#expr)
#__VA_ARGS__); \
}(static_cast<bool>(__VA_ARGS__))
#define CAF_REQUIRE(expr) \
::caf::test::detail::require_un(static_cast<bool>(expr), __FILE__, __LINE__, \
#define CAF_CHECK_FUNC(func, x_expr, y_expr) \
#expr)
[](auto&& x_val, auto&& y_val) { \
func comparator; \
auto caf_check_res \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \
__FILE__, __LINE__, \
CAF_FUNC_EXPR(func, x_expr, y_expr), false, \
comparator(x_val, y_val), x_val, y_val); \
return caf_check_res; \
}(x_expr, y_expr)
#define CAF_FAIL(
msg
) \
#define CAF_FAIL(
...
) \
do { \
do { \
CAF_TEST_PRINT_ERROR(
msg);
\
CAF_TEST_PRINT_ERROR(
__VA_ARGS__);
\
::caf::test::engine::current_test()->fail(false); \
::caf::test::engine::current_test()->fail(false); \
::caf::test::detail::requirement_failed("test failure"); \
::caf::test::detail::requirement_failed("test failure"); \
} while (false)
} while (false)
#define CAF_REQUIRE(...) \
do { \
auto CAF_UNIQUE(__result) \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \
__FILE__, __LINE__, #__VA_ARGS__, false, \
static_cast<bool>(__VA_ARGS__)); \
if (!CAF_UNIQUE(__result)) \
::caf::test::detail::requirement_failed(#__VA_ARGS__); \
} while (false)
#define CAF_REQUIRE_FUNC(func, x_expr, y_expr) \
do { \
func comparator; \
auto&& x_val___ = x_expr; \
auto&& y_val___ = y_expr; \
auto CAF_UNIQUE(__result) = ::caf::test::detail::check( \
::caf::test::engine::current_test(), __FILE__, __LINE__, \
CAF_FUNC_EXPR(func, x_expr, y_expr), false, \
comparator(x_val___, y_val___), x_val___, y_val___); \
if (!CAF_UNIQUE(__result)) \
::caf::test::detail::requirement_failed( \
CAF_FUNC_EXPR(func, x_expr, y_expr)); \
} while (false)
#define CAF_TEST_IMPL(name, disabled_by_default) \
#define CAF_TEST_IMPL(name, disabled_by_default) \
namespace { \
namespace { \
struct CAF_UNIQUE(test) : caf_test_case_auto_fixture { \
struct CAF_UNIQUE(test) : caf_test_case_auto_fixture { \
...
@@ -590,7 +623,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
...
@@ -590,7 +623,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
CAF_TEST_PRINT_VERBOSE(msg); \
CAF_TEST_PRINT_VERBOSE(msg); \
} while (false)
} while (false)
// -- CAF_CHECK*
predicate family
----------------------------------------------
// -- CAF_CHECK*
macro family ----
----------------------------------------------
#define CAF_CHECK_EQUAL(x_expr, y_expr) \
#define CAF_CHECK_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
[](const auto& x_val, const auto& y_val) { \
...
@@ -672,33 +705,139 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
...
@@ -672,33 +705,139 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
caf::detail::stringification_inspector::render(y_val)); \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
}(x_expr, y_expr)
// -- CAF_CHECK* predicate family ----------------------------------------------
#ifdef CAF_ENABLE_EXCEPTIONS
#define CAF_REQUIRE_EQUAL(x, y) CAF_REQUIRE_FUNC(::caf::test::equal_to, x, y)
# define CAF_CHECK_NOTHROW(expr) \
[&] { \
bool got_exception = false; \
try { \
static_cast<void>(expr); \
} catch (...) { \
got_exception = true; \
} \
::caf::test::detail::check_un(!got_exception, __FILE__, __LINE__, \
#expr " does not throw"); \
return !got_exception; \
}()
# define CAF_CHECK_THROWS_AS(expr, type) \
[&] { \
bool got_exception = false; \
try { \
static_cast<void>(expr); \
} catch (type&) { \
got_exception = true; \
} catch (...) { \
} \
::caf::test::detail::check_un(got_exception, __FILE__, __LINE__, \
#expr " throws " #type); \
return got_exception; \
}()
# define CAF_CHECK_THROWS_WITH(expr, msg) \
[&] { \
std::string ex_what = "EX-NOT-FOUND"; \
try { \
static_cast<void>(expr); \
} catch (std::exception & ex) { \
ex_what = ex.what(); \
} catch (...) { \
} \
return CHECK_EQ(ex_what, msg); \
}()
# define CAF_CHECK_THROWS_WITH_AS(expr, msg, type) \
[&] { \
std::string ex_what = "EX-NOT-FOUND"; \
try { \
static_cast<void>(expr); \
} catch (type & ex) { \
ex_what = ex.what(); \
} catch (...) { \
} \
return CHECK_EQ(ex_what, msg); \
}()
#endif // CAF_ENABLE_EXCEPTIONS
// -- CAF_REQUIRE* macro family ------------------------------------------------
#define CAF_REQUIRE_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
x_val == y_val, __FILE__, __LINE__, #x_expr " == " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_NOT_EQUAL(x, y) \
#define CAF_REQUIRE_NOT_EQUAL(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::not_equal_to, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
x_val != y_val, __FILE__, __LINE__, #x_expr " != " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_LESS(x, y) CAF_REQUIRE_FUNC(::caf::test::less_than, x, y)
#define CAF_REQUIRE_LESS(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
x_val < y_val, __FILE__, __LINE__, #x_expr " < " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_NOT_LESS(x, y) \
#define CAF_REQUIRE_NOT_LESS(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::less_than>, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
!(x_val < y_val), __FILE__, __LINE__, "not " #x_expr " < " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_LESS_OR_EQUAL(x, y) \
#define CAF_REQUIRE_LESS_OR_EQUAL(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::less_than_or_equal, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
x_val <= y_val, __FILE__, __LINE__, #x_expr " <= " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_NOT_LESS_OR_EQUAL(x, y) \
#define CAF_REQUIRE_NOT_LESS_OR_EQUAL(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::less_than_or_equal>, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
!(x_val <= y_val), __FILE__, __LINE__, "not " #x_expr " <= " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_GREATER(x, y) \
#define CAF_REQUIRE_GREATER(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::greater_than, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
x_val > y_val, __FILE__, __LINE__, #x_expr " > " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_NOT_GREATER(x, y) \
#define CAF_REQUIRE_NOT_GREATER(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::greater_than>, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
!(x_val > y_val), __FILE__, __LINE__, "not " #x_expr " > " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_GREATER_OR_EQUAL(x, y) \
#define CAF_REQUIRE_GREATER_OR_EQUAL(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::greater_than_or_equal, x, y)
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::require_bin( \
x_val >= y_val, __FILE__, __LINE__, #x_expr " >= " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
#define CAF_REQUIRE_NOT_GREATER_OR_EQUAL(x, y) \
#define CAF_REQUIRE_NOT_GREATER_OR_EQUAL(x_expr, y_expr) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::greater_than_or_equal>, \
[](const auto& x_val, const auto& y_val) { \
x, y)
return ::caf::test::detail::require_bin( \
!(x_val >= y_val), __FILE__, __LINE__, "not " #x_expr " >= " #y_expr, \
caf::detail::stringification_inspector::render(x_val), \
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
libcaf_test/caf/test/unit_test_impl.hpp
View file @
5d233fae
...
@@ -29,6 +29,14 @@
...
@@ -29,6 +29,14 @@
namespace
caf
::
test
{
namespace
caf
::
test
{
requirement_error
::
requirement_error
(
std
::
string
msg
)
:
what_
(
std
::
move
(
msg
))
{
// nop
}
const
char
*
requirement_error
::
what
()
const
noexcept
{
return
what_
.
c_str
();
}
class
watchdog
{
class
watchdog
{
public:
public:
static
void
start
(
int
secs
);
static
void
start
(
int
secs
);
...
@@ -121,6 +129,7 @@ namespace detail {
...
@@ -121,6 +129,7 @@ namespace detail {
<<
term
::
yellow
<<
":"
<<
term
::
cyan
<<
engine
::
last_check_line
()
<<
term
::
yellow
<<
":"
<<
term
::
cyan
<<
engine
::
last_check_line
()
<<
term
::
reset
<<
detail
::
fill
(
engine
::
last_check_line
())
<<
term
::
reset
<<
detail
::
fill
(
engine
::
last_check_line
())
<<
"had last successful check"
<<
'\n'
;
<<
"had last successful check"
<<
'\n'
;
CAF_RAISE_ERROR
(
requirement_error
,
msg
.
c_str
());
abort
();
abort
();
}
}
...
@@ -200,6 +209,59 @@ bool check_bin(bool result, const char* file, size_t line, const char* expr,
...
@@ -200,6 +209,59 @@ bool check_bin(bool result, const char* file, size_t line, const char* expr,
return
result
;
return
result
;
}
}
void
require_un
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
)
{
string_view
rel_up
=
"../"
;
while
(
strncmp
(
file
,
rel_up
.
data
(),
rel_up
.
size
())
==
0
)
file
+=
rel_up
.
size
();
auto
parent
=
engine
::
current_test
();
auto
out
=
logger
::
instance
().
massive
();
if
(
result
)
{
out
<<
term
::
green
<<
"** "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
reset
<<
"passed"
<<
'\n'
;
parent
->
pass
();
engine
::
last_check_file
(
file
);
engine
::
last_check_line
(
line
);
}
else
{
out
<<
term
::
red
<<
"!! "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
red
<<
"requirement failed: "
<<
expr
<<
term
::
reset
<<
'\n'
;
parent
->
fail
(
false
);
std
::
string
msg
=
"requirement failed in "
;
msg
+=
file
;
msg
+=
" line "
;
msg
+=
std
::
to_string
(
line
);
requirement_failed
(
std
::
move
(
msg
));
}
}
void
require_bin
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
,
const
std
::
string
&
lhs
,
const
std
::
string
&
rhs
)
{
string_view
rel_up
=
"../"
;
while
(
strncmp
(
file
,
rel_up
.
data
(),
rel_up
.
size
())
==
0
)
file
+=
rel_up
.
size
();
auto
parent
=
engine
::
current_test
();
auto
out
=
logger
::
instance
().
massive
();
if
(
result
)
{
out
<<
term
::
green
<<
"** "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
reset
<<
"passed"
<<
'\n'
;
parent
->
pass
();
engine
::
last_check_file
(
file
);
engine
::
last_check_line
(
line
);
}
else
{
out
<<
term
::
red
<<
"!! "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
red
<<
"requirement failed: "
<<
expr
<<
term
::
reset
<<
'\n'
<<
" lhs: "
<<
lhs
<<
'\n'
<<
" rhs: "
<<
rhs
<<
'\n'
;
parent
->
fail
(
false
);
std
::
string
msg
=
"requirement failed in "
;
msg
+=
file
;
msg
+=
" line "
;
msg
+=
std
::
to_string
(
line
);
requirement_failed
(
std
::
move
(
msg
));
}
}
}
// namespace detail
}
// namespace detail
logger
::
stream
::
stream
(
logger
&
parent
,
logger
::
level
lvl
)
logger
::
stream
::
stream
(
logger
&
parent
,
logger
::
level
lvl
)
...
@@ -319,36 +381,6 @@ bool engine::run(bool colorize, const std::string& log_file,
...
@@ -319,36 +381,6 @@ bool engine::run(bool colorize, const std::string& log_file,
size_t
total_bad
=
0
;
size_t
total_bad
=
0
;
size_t
total_bad_expected
=
0
;
size_t
total_bad_expected
=
0
;
auto
bar
=
'+'
+
std
::
string
(
70
,
'-'
)
+
'+'
;
auto
bar
=
'+'
+
std
::
string
(
70
,
'-'
)
+
'+'
;
#if (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 4 \
&& __GNUC_MINOR__ < 9) \
|| (defined(__clang__) && !defined(_LIBCPP_VERSION))
// regex implementation is broken prior to 4.9
using
strvec
=
std
::
vector
<
std
::
string
>
;
using
whitelist_type
=
strvec
;
using
blacklist_type
=
strvec
;
auto
from_psv
=
[](
const
std
::
string
&
psv
)
->
strvec
{
// psv == pipe-separated-values
strvec
result
;
if
(
psv
!=
".*"
)
{
split
(
result
,
psv
,
"|"
,
token_compress_on
);
std
::
sort
(
result
.
begin
(),
result
.
end
());
}
return
result
;
};
auto
suites
=
from_psv
(
suites_str
);
auto
not_suites
=
from_psv
(
not_suites_str
);
auto
tests
=
from_psv
(
tests_str
);
auto
not_tests
=
from_psv
(
not_tests_str
);
auto
enabled
=
[](
const
strvec
&
whitelist
,
const
strvec
&
blacklist
,
const
std
::
string
&
x
)
{
// an empty whitelist means original input was ".*", i.e., enable all
return
!
std
::
binary_search
(
blacklist
.
begin
(),
blacklist
.
end
(),
x
)
&&
(
whitelist
.
empty
()
||
std
::
binary_search
(
whitelist
.
begin
(),
whitelist
.
end
(),
x
));
};
#else
using
whitelist_type
=
std
::
regex
;
using
blacklist_type
=
std
::
regex
;
std
::
regex
suites
{
suites_str
};
std
::
regex
suites
{
suites_str
};
std
::
regex
tests
{
tests_str
};
std
::
regex
tests
{
tests_str
};
std
::
regex
not_suites
;
std
::
regex
not_suites
;
...
@@ -358,18 +390,16 @@ bool engine::run(bool colorize, const std::string& log_file,
...
@@ -358,18 +390,16 @@ bool engine::run(bool colorize, const std::string& log_file,
not_suites
.
assign
(
not_suites_str
);
not_suites
.
assign
(
not_suites_str
);
if
(
!
not_tests_str
.
empty
())
if
(
!
not_tests_str
.
empty
())
not_tests
.
assign
(
not_tests_str
);
not_tests
.
assign
(
not_tests_str
);
auto
enabled
=
[](
const
std
::
regex
&
whitelist
,
const
std
::
regex
&
blacklist
,
auto
enabled
=
[](
const
std
::
regex
&
selected
,
const
std
::
regex
&
blocked
,
const
std
::
string
&
x
)
{
const
std
::
string
&
x
)
{
// an empty whitelist means original input was "*", i.e., enable all
return
std
::
regex_search
(
x
,
selected
)
&&
!
std
::
regex_search
(
x
,
blocked
);
return
std
::
regex_search
(
x
,
whitelist
)
&&
!
std
::
regex_search
(
x
,
blacklist
);
};
};
#endif
auto
test_enabled
=
[
&
](
const
std
::
regex
&
selected
,
const
std
::
regex
&
blocked
,
auto
test_enabled
=
[
&
](
const
whitelist_type
&
whitelist
,
const
test
&
x
)
{
const
blacklist_type
&
blacklist
,
const
test
&
x
)
{
// Disabled tests run if explicitly requested by the user, i.e.,
// Disabled tests run if explicitly requested by the user, i.e.,
// tests_str is not the ".*" catch-all default.
// tests_str is not the ".*" catch-all default.
return
(
!
x
.
disabled
()
||
tests_str
!=
".*"
)
return
(
!
x
.
disabled
()
||
tests_str
!=
".*"
)
&&
enabled
(
whitelist
,
blacklist
,
x
.
name
());
&&
enabled
(
selected
,
blocked
,
x
.
name
());
};
};
std
::
vector
<
std
::
string
>
failed_tests
;
std
::
vector
<
std
::
string
>
failed_tests
;
for
(
auto
&
p
:
instance
().
suites_
)
{
for
(
auto
&
p
:
instance
().
suites_
)
{
...
@@ -393,7 +423,23 @@ bool engine::run(bool colorize, const std::string& log_file,
...
@@ -393,7 +423,23 @@ bool engine::run(bool colorize, const std::string& log_file,
log
.
verbose
()
<<
term
::
yellow
<<
"- "
<<
term
::
reset
<<
t
->
name
()
<<
'\n'
;
log
.
verbose
()
<<
term
::
yellow
<<
"- "
<<
term
::
reset
<<
t
->
name
()
<<
'\n'
;
auto
start
=
std
::
chrono
::
high_resolution_clock
::
now
();
auto
start
=
std
::
chrono
::
high_resolution_clock
::
now
();
watchdog
::
start
(
max_runtime
());
watchdog
::
start
(
max_runtime
());
#ifdef CAF_ENABLE_EXCEPTIONS
try
{
t
->
run_test_impl
();
}
catch
(
requirement_error
&
)
{
// nop
}
catch
(
std
::
exception
&
ex
)
{
t
->
fail
(
false
);
log
.
error
()
<<
term
::
red
<<
"!! uncaught exception, what: "
<<
ex
.
what
()
<<
term
::
reset_endl
;
}
catch
(...)
{
t
->
fail
(
false
);
log
.
error
()
<<
term
::
red
<<
"!! uncaught exception of unknown type"
<<
term
::
reset_endl
;
}
#else
t
->
run_test_impl
();
t
->
run_test_impl
();
#endif // CAF_ENABLE_EXCEPTIONS
watchdog
::
stop
();
watchdog
::
stop
();
auto
stop
=
std
::
chrono
::
high_resolution_clock
::
now
();
auto
stop
=
std
::
chrono
::
high_resolution_clock
::
now
();
auto
elapsed
auto
elapsed
...
@@ -515,8 +561,8 @@ std::string engine::render(std::chrono::microseconds t) {
...
@@ -515,8 +561,8 @@ std::string engine::render(std::chrono::microseconds t) {
return
t
.
count
()
>
1000000
return
t
.
count
()
>
1000000
?
(
std
::
to_string
(
t
.
count
()
/
1000000
)
+
'.'
?
(
std
::
to_string
(
t
.
count
()
/
1000000
)
+
'.'
+
std
::
to_string
((
t
.
count
()
%
1000000
)
/
10000
)
+
" s"
)
+
std
::
to_string
((
t
.
count
()
%
1000000
)
/
10000
)
+
" s"
)
:
t
.
count
()
>
1000
?
(
std
::
to_string
(
t
.
count
()
/
1000
)
+
" ms"
)
:
t
.
count
()
>
1000
?
(
std
::
to_string
(
t
.
count
()
/
1000
)
+
" ms"
)
:
(
std
::
to_string
(
t
.
count
())
+
" us"
);
:
(
std
::
to_string
(
t
.
count
())
+
" us"
);
}
}
int
main
(
int
argc
,
char
**
argv
)
{
int
main
(
int
argc
,
char
**
argv
)
{
...
...
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