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).
## [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
-
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.
-
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
...
...
libcaf_core/CMakeLists.txt
View file @
5d233fae
...
...
@@ -270,6 +270,7 @@ caf_add_component(
detail.unique_function
detail.unordered_flat_map
dictionary
dsl
dynamic_spawn
error
expected
...
...
libcaf_core/caf/scheduler/test_coordinator.hpp
View file @
5d233fae
...
...
@@ -46,14 +46,18 @@ public:
}
/// Peeks into the mailbox of `next_job<scheduled_actor>()`.
template
<
class
T
>
const
T
&
peek
()
{
template
<
class
...
Ts
>
decltype
(
auto
)
peek
()
{
auto
ptr
=
next_job
<
scheduled_actor
>
().
mailbox
().
peek
();
CAF_ASSERT
(
ptr
!=
nullptr
);
auto
view
=
make_typed_message_view
<
T
>
(
ptr
->
content
());
if
(
!
view
)
CAF_RAISE_ERROR
(
"Mailbox element does not match T."
);
return
get
<
0
>
(
view
);
if
(
auto
view
=
make_const_typed_message_view
<
Ts
...
>
(
ptr
->
payload
))
{
if
constexpr
(
sizeof
...(
Ts
)
==
1
)
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.
...
...
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 @@
#define CHECK_GT(lhs, rhs) CAF_CHECK_GREATER(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_EQ(lhs, rhs) CAF_REQUIRE_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>&);
template
<
class
T
,
class
U
>
bool
is
(
const
has_outer_type
<
U
>&
);
template
<
class
Tup
>
template
<
class
Tup
le
>
class
elementwise_compare_inspector
{
public:
using
result_type
=
bool
;
template
<
size_t
X
>
using
pos
=
std
::
integral_constant
<
size_t
,
X
>
;
static
constexpr
size_t
num_values
=
std
::
tuple_size_v
<
Tuple
>
;
explicit
elementwise_compare_inspector
(
const
Tup
&
xs
)
:
xs_
(
xs
)
{
explicit
elementwise_compare_inspector
(
const
Tup
le
&
xs
)
:
xs_
(
xs
)
{
// nop
}
template
<
class
...
Ts
>
bool
operator
()(
const
Ts
&
...
xs
)
{
return
iterate
(
pos
<
0
>
{},
xs
...);
bool
operator
()(
const
Ts
&
...
xs
)
const
{
static_assert
(
sizeof
...(
Ts
)
==
num_values
);
return
apply
(
std
::
forward_as_tuple
(
xs
...),
std
::
index_sequence_for
<
Ts
...
>
{});
}
private:
template
<
size_t
X
>
bool
iterate
(
pos
<
X
>
)
{
// end of recursion
return
true
;
}
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
OtherTuple
,
size_t
...
Is
>
bool
apply
(
OtherTuple
values
,
std
::
index_sequence
<
Is
...
>
)
const
{
using
std
::
get
;
return
(
check
(
get
<
Is
>
(
values
),
get
<
Is
>
(
xs_
))
&&
...);
}
template
<
class
T
,
class
U
>
static
void
check
(
const
T
&
x
,
const
U
&
y
)
{
CAF_CHECK_EQUAL
(
x
,
y
)
;
static
bool
check
(
const
T
&
x
,
const
U
&
y
)
{
return
x
==
y
;
}
template
<
class
T
>
static
void
check
(
const
T
&
,
const
wildcard
&
)
{
// nop
static
bool
check
(
const
T
&
,
const
wildcard
&
)
{
return
true
;
}
const
Tup
&
xs_
;
const
Tup
le
&
xs_
;
};
// -- unified access to all actor handles in CAF -------------------------------
...
...
@@ -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
/// element does not match `<T, Ts...>`.
template
<
class
T
,
class
...
Ts
>
std
::
tuple
<
T
,
Ts
...
>
extract
(
caf_handle
x
)
{
auto
result
=
try_extract
<
T
,
Ts
...
>
(
x
);
if
(
result
==
caf
::
none
)
{
std
::
tuple
<
T
,
Ts
...
>
extract
(
caf_handle
x
,
int
src_line
)
{
if
(
auto
result
=
try_extract
<
T
,
Ts
...
>
(
x
);
result
!=
caf
::
none
)
{
return
std
::
move
(
*
result
);
}
else
{
auto
ptr
=
x
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
CAF_FAIL
(
"Mailbox is empty"
);
CAF_FAIL
(
"Message does not match expected pattern: "
<<
to_string
(
ptr
->
content
()));
CAF_FAIL
(
"cannot peek at the next message: mailbox is empty"
,
src_line
);
else
CAF_FAIL
(
"message does not match expected types: "
<<
to_string
(
ptr
->
content
()),
src_line
);
}
return
std
::
move
(
*
result
);
}
template
<
class
T
,
class
...
Ts
>
...
...
@@ -277,22 +273,25 @@ bool received(caf_handle x) {
template
<
class
...
Ts
>
class
expect_clause
{
public:
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
peek_
=
[
=
]
{
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
src_line_
(
src_line
)
{
peek_
=
[
this
]
{
/// The extractor will call CAF_FAIL on a type mismatch, essentially
/// performing a type check when ignoring the result.
extract
<
Ts
...
>
(
dest_
);
extract
<
Ts
...
>
(
dest_
,
src_line_
);
};
}
expect_clause
(
expect_clause
&&
other
)
=
default
;
~
expect_clause
()
{
if
(
peek_
!=
nullptr
)
{
peek_
();
run_once
();
}
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 "
<<
type_str
<<
"."
<<
fields_str
<<
" [line "
<<
src_line_
<<
"]
\n
"
;
peek_
();
run_once
();
}
expect_clause
&
from
(
const
wildcard
&
)
{
...
...
@@ -307,12 +306,15 @@ public:
template
<
class
Handle
>
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
>
();
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
CAF_REQUIRE
(
ptr
!=
nullptr
);
if
(
src_
)
CAF_REQUIRE_EQUAL
(
ptr
->
sender
,
src_
);
if
(
ptr
==
nullptr
)
CAF_FAIL
(
"the designated receiver has no message in its mailbox"
,
src_line_
);
if
(
src_
&&
ptr
->
sender
!=
src_
)
CAF_FAIL
(
"the found message is not from the expected sender"
,
src_line_
);
return
*
this
;
}
...
...
@@ -322,14 +324,17 @@ public:
}
template
<
class
...
Us
>
void
with
(
Us
&&
...
xs
)
{
expect_clause
&
with
(
Us
&&
...
xs
)
{
peek_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
using
namespace
caf
::
detail
;
elementwise_compare_inspector
<
decltype
(
tmp
)
>
inspector
{
tmp
};
auto
ys
=
extract
<
Ts
...
>
(
dest_
);
auto
ys_indices
=
get_indices
(
ys
);
CAF_REQUIRE
(
apply_args
(
inspector
,
ys_indices
,
ys
));
auto
inspector
=
elementwise_compare_inspector
<
decltype
(
tmp
)
>
{
tmp
};
auto
content
=
extract
<
Ts
...
>
(
dest_
,
src_line_
);
if
(
!
std
::
apply
(
inspector
,
content
))
CAF_FAIL
(
"message does not match expected content: "
<<
caf
::
deep_to_string
(
tmp
)
<<
" vs "
<<
caf
::
deep_to_string
(
content
),
src_line_
);
};
return
*
this
;
}
protected:
...
...
@@ -343,26 +348,33 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
abstract_actor
*
dest_
;
caf
::
abstract_actor
*
dest_
=
nullptr
;
std
::
function
<
void
()
>
peek_
;
int
src_line_
;
};
template
<
>
class
expect_clause
<
void
>
{
public:
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
explicit
expect_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
src_line_
(
src_line
)
{
// nop
}
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
();
if
(
ptr
==
nullptr
)
CAF_FAIL
(
"no message found"
);
CAF_FAIL
(
"no message found"
,
src_line_
);
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
();
}
...
...
@@ -403,14 +415,15 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
abstract_actor
*
dest_
;
caf
::
abstract_actor
*
dest_
=
nullptr
;
int
src_line_
;
};
template
<
class
...
Ts
>
class
inject_clause
{
public:
explicit
inject_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
explicit
inject_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
src_line_
(
src_line
)
{
// nop
}
...
...
@@ -428,27 +441,39 @@ public:
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
)
CAF_FAIL
(
"missing .to() in inject() statement"
);
auto
msg
=
caf
::
make_message
(
std
::
move
(
xs
)...);
if
(
src_
==
nullptr
)
caf
::
anon_send
(
caf
::
actor_cast
<
caf
::
actor
>
(
dest_
),
msg
);
CAF_FAIL
(
"missing .to() in inject() statement"
,
src_line_
);
else
if
(
src_
==
nullptr
)
caf
::
anon_send
(
caf
::
actor_cast
<
caf
::
actor
>
(
dest_
),
msg_
);
else
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_
))
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
ptr
=
dest_ptr
->
peek_at_next_mailbox_element
();
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_
)
CAF_FAIL
(
"inject: found unexpected sender for the next message"
);
if
(
ptr
->
payload
.
cptr
()
!=
msg
.
cptr
())
CAF_FAIL
(
"inject: found unexpected sender for the next message"
,
src_line_
);
if
(
ptr
->
payload
.
cptr
()
!=
msg_
.
cptr
())
CAF_FAIL
(
"inject: found unexpected message => "
<<
ptr
->
payload
<<
" !! "
<<
msg
);
msg
.
reset
();
// drop local reference before running the actor
<<
msg_
,
src_line_
);
msg_
.
reset
();
// drop local reference before running the actor
run_once
();
}
...
...
@@ -465,26 +490,25 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
strong_actor_ptr
dest_
;
caf
::
message
msg_
;
int
src_line_
;
};
template
<
class
...
Ts
>
class
allow_clause
{
public:
explicit
allow_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
)
:
sched_
(
sched
),
dest_
(
nullptr
)
{
peek_
=
[
=
]
{
explicit
allow_clause
(
caf
::
scheduler
::
test_coordinator
&
sched
,
int
src_line
)
:
sched_
(
sched
),
src_line_
(
src_line
)
{
peek_
=
[
this
]
{
if
(
dest_
!=
nullptr
)
return
try_extract
<
Ts
...
>
(
dest_
)
!=
caf
::
none
;
return
false
;
else
return
false
;
};
}
allow_clause
(
allow_clause
&&
other
)
=
default
;
~
allow_clause
()
{
eval
();
}
allow_clause
&
from
(
const
wildcard
&
)
{
return
*
this
;
}
...
...
@@ -499,31 +523,46 @@ public:
allow_clause
&
to
(
const
Handle
&
whom
)
{
if
(
sched_
.
prioritize
(
whom
))
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
;
}
template
<
class
...
Us
>
void
with
(
Us
&&
...
xs
)
{
allow_clause
&
with
(
Us
&&
...
xs
)
{
peek_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
using
namespace
caf
::
detail
;
elementwise_compare_inspector
<
decltype
(
tmp
)
>
inspector
{
tmp
};
auto
ys
=
try_extract
<
Ts
...
>
(
dest_
);
if
(
ys
!=
caf
::
none
)
{
auto
ys_indices
=
get_indices
(
*
ys
);
return
apply_args
(
inspector
,
ys_indices
,
*
ys
);
if
(
dest_
!=
nullptr
)
{
if
(
auto
ys
=
try_extract
<
Ts
...
>
(
dest_
);
ys
!=
caf
::
none
)
{
elementwise_compare_inspector
<
decltype
(
tmp
)
>
inspector
{
tmp
};
auto
ys_indices
=
get_indices
(
*
ys
);
return
apply_args
(
inspector
,
ys_indices
,
*
ys
);
}
}
return
false
;
};
return
*
this
;
}
bool
eval
()
{
if
(
peek_
!=
nullptr
)
{
if
(
peek_
())
{
run_once
();
return
true
;
}
bool
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
{}
<<
"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:
...
...
@@ -537,15 +576,16 @@ protected:
caf
::
scheduler
::
test_coordinator
&
sched_
;
caf
::
strong_actor_ptr
src_
;
caf
::
abstract_actor
*
dest_
;
caf
::
abstract_actor
*
dest_
=
nullptr
;
std
::
function
<
bool
()
>
peek_
;
int
src_line_
;
};
template
<
class
...
Ts
>
class
disallow_clause
{
public:
disallow_clause
()
{
check_
=
[
=
]
{
disallow_clause
(
int
src_line
)
:
src_line_
(
src_line
)
{
check_
=
[
this
]
{
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
return
;
...
...
@@ -553,17 +593,13 @@ public:
return
;
auto
res
=
try_extract
<
Ts
...
>
(
dest_
);
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
()
{
if
(
check_
!=
nullptr
)
check_
();
}
disallow_clause
&
from
(
const
wildcard
&
)
{
return
*
this
;
}
...
...
@@ -579,7 +615,7 @@ public:
}
template
<
class
...
Us
>
void
with
(
Us
&&
...
xs
)
{
disallow_clause
&
with
(
Us
&&
...
xs
)
{
check_
=
[
this
,
tmp
=
std
::
make_tuple
(
std
::
forward
<
Us
>
(
xs
)...)]
{
auto
ptr
=
dest_
->
peek_at_next_mailbox_element
();
if
(
ptr
==
nullptr
)
...
...
@@ -593,15 +629,26 @@ public:
auto
&
ys
=
*
res
;
auto
ys_indices
=
get_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:
caf_handle
src_
;
caf_handle
dest_
;
std
::
function
<
void
()
>
check_
;
int
src_line_
;
};
template
<
class
...
Ts
>
...
...
@@ -747,6 +794,7 @@ public:
size_t
progress
=
0
;
while
(
consume_message
())
{
++
progress
;
++
events
;
if
(
predicate
())
{
CAF_LOG_DEBUG
(
"stop due to predicate:"
<<
CAF_ARG
(
events
));
return
events
;
...
...
@@ -754,18 +802,20 @@ public:
}
while
(
handle_io_event
())
{
++
progress
;
++
events
;
if
(
predicate
())
{
CAF_LOG_DEBUG
(
"stop due to predicate:"
<<
CAF_ARG
(
events
));
return
events
;
}
}
if
(
trigger_timeout
())
if
(
trigger_timeout
())
{
++
progress
;
++
events
;
}
if
(
progress
==
0
)
{
CAF_LOG_DEBUG
(
"no activity left:"
<<
CAF_ARG
(
events
));
return
events
;
}
events
+=
progress
;
}
}
...
...
@@ -875,32 +925,22 @@ T unbox(T* x) {
/// Convenience macro for defining expect clauses.
#define expect(types, fields) \
do { \
CAF_MESSAGE("expect" << #types << "." << #fields); \
expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched}.fields; \
} while (false)
(expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched, __LINE__}.fields.eval( \
#types, #fields))
#define inject(types, fields) \
do { \
CAF_MESSAGE("inject" << #types << "." << #fields); \
inject_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched}.fields; \
} while (false)
(inject_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched, __LINE__}.fields.eval( \
#types, #fields))
/// Convenience macro for defining allow clauses.
#define allow(types, fields) \
([&] { \
CAF_MESSAGE("allow" << #types << "." << #fields); \
allow_clause<CAF_EXPAND(CAF_DSL_LIST types)> x{sched}; \
x.fields; \
return x.eval(); \
})()
(allow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{sched, __LINE__}.fields.eval( \
#types, #fields))
/// Convenience macro for defining disallow clauses.
#define disallow(types, fields) \
do { \
CAF_MESSAGE("disallow" << #types << "." << #fields); \
disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{}.fields; \
} while (false)
(disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{__LINE__}.fields.eval( \
#types, #fields))
/// Defines the required base type for testee states in the current namespace.
#define TESTEE_SETUP() \
...
...
libcaf_test/caf/test/io_dsl.hpp
View file @
5d233fae
...
...
@@ -55,9 +55,9 @@ public:
/// @param fun A function object for delegating to the parent's `exec_all`.
test_node_fixture
(
run_all_nodes_fun
fun
)
:
mm
(
this
->
sys
.
middleman
()),
mpx
(
dynamic_cast
<
caf
::
io
::
network
::
test_multiplexer
&>
(
mm
.
backend
())),
run_all_nodes
(
std
::
move
(
fun
))
{
:
mm
(
this
->
sys
.
middleman
()),
mpx
(
dynamic_cast
<
caf
::
io
::
network
::
test_multiplexer
&>
(
mm
.
backend
())),
run_all_nodes
(
std
::
move
(
fun
))
{
bb
=
mm
.
named_broker
<
caf
::
io
::
basp_broker
>
(
"BASP"
);
}
...
...
@@ -124,9 +124,7 @@ void exec_all_fixtures(Iterator first, Iterator last) {
return
x
->
sched
.
try_run_once
()
||
x
->
mpx
.
read_data
()
||
x
->
mpx
.
try_exec_runnable
()
||
x
->
mpx
.
try_accept_connection
();
};
auto
trigger_timeouts
=
[](
fixture_ptr
x
)
{
x
->
sched
.
trigger_timeouts
();
};
auto
trigger_timeouts
=
[](
fixture_ptr
x
)
{
x
->
sched
.
trigger_timeouts
();
};
for
(;;)
{
// Exhaust all messages in the system.
while
(
std
::
any_of
(
first
,
last
,
advance
))
...
...
@@ -167,9 +165,8 @@ public:
/// (calls `publish`).
/// @returns randomly picked connection handles for the server and the client.
std
::
pair
<
connection_handle
,
connection_handle
>
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
std
::
string
host
,
uint16_t
port
,
accept_handle
server_accept_hdl
)
{
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
std
::
string
host
,
uint16_t
port
,
accept_handle
server_accept_hdl
)
{
auto
server_hdl
=
next_connection_handle
();
auto
client_hdl
=
next_connection_handle
();
server
.
mpx
.
prepare_connection
(
server_accept_hdl
,
server_hdl
,
client
.
mpx
,
...
...
@@ -181,8 +178,8 @@ public:
/// (calls `publish`).
/// @returns randomly picked connection handles for the server and the client.
std
::
pair
<
connection_handle
,
connection_handle
>
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
std
::
string
host
,
uint16_t
port
)
{
prepare_connection
(
PlanetType
&
server
,
PlanetType
&
client
,
std
::
string
host
,
uint16_t
port
)
{
return
prepare_connection
(
server
,
client
,
std
::
move
(
host
),
port
,
next_accept_handle
());
}
...
...
@@ -206,7 +203,7 @@ public:
}
/// Type-erased callback for calling `exec_all`.
std
::
function
<
void
()
>
exec_all_callback
()
{
std
::
function
<
void
()
>
exec_all_callback
()
{
return
[
&
]
{
exec_all
();
};
}
...
...
@@ -273,13 +270,9 @@ public:
};
#define expect_on(where, types, fields) \
do { \
CAF_MESSAGE(#where << ": expect" << #types << "." << #fields); \
expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched}.fields; \
} while (false)
(expect_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched, __LINE__} \
.fields.eval(#types, #fields))
#define disallow_on(where, types, fields) \
do { \
CAF_MESSAGE(#where << ": disallow" << #types << "." << #fields); \
disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched}.fields; \
} while (false)
(disallow_clause<CAF_EXPAND(CAF_DSL_LIST types)>{where.sched, __LINE__} \
.fields.eval(#types, #fields))
libcaf_test/caf/test/unit_test.hpp
View file @
5d233fae
...
...
@@ -15,10 +15,12 @@
#include <thread>
#include <vector>
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/optional.hpp"
#include "caf/raise_error.hpp"
#include "caf/term.hpp"
#include "caf/variant.hpp"
...
...
@@ -27,6 +29,20 @@
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 ---------------------
template
<
class
F
>
...
...
@@ -235,6 +251,16 @@ public:
return
bad_
;
}
void
reset
()
{
expected_failures_
=
0
;
good_
=
0
;
bad_
=
0
;
}
void
reset_bad
()
{
bad_
=
0
;
}
bool
disabled
()
const
noexcept
{
return
disabled_
;
}
...
...
@@ -339,6 +365,25 @@ public:
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
info
();
stream
verbose
();
...
...
@@ -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
,
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 caf::test
// on the global namespace so that it can hidden via namespace-scoping
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::term::colorcode << " -> " << ::caf::term::reset \
<< ::caf::test::logger::stream::reset_flags_t{} << msg << " [line " \
<< __LINE__ << "]\n")
<< ::caf::test::logger::stream::reset_flags_t{} << msg << " [line " << line \
<< "]\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_INFO(msg) CAF_TEST_PRINT(info, msg, yellow)
#define CAF_TEST_PRINT_VERBOSE(msg) CAF_TEST_PRINT(verbose, msg, yellow)
#define CAF_TEST_PRINT(level, msg, colorcode) \
CAF_TEST_LOG_MSG(level, colorcode, msg)
#define CAF_PASTE_CONCAT(lhs, rhs) lhs##rhs
...
...
@@ -514,54 +580,21 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
#define CAF_CHECK(...) \
[](bool expr_result) { \
return ::caf::test::detail::check_un(expr_result, __FILE__, __LINE__, \
#__VA_ARGS__); \
}(static_cast<bool>(__VA_ARGS__))
#define CAF_CHECK_FUNC(func, x_expr, y_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_CHECK(expr) \
::caf::test::detail::check_un(static_cast<bool>(expr), __FILE__, __LINE__, \
#expr)
#define CAF_REQUIRE(expr) \
::caf::test::detail::require_un(static_cast<bool>(expr), __FILE__, __LINE__, \
#expr)
#define CAF_FAIL(
msg
) \
#define CAF_FAIL(
...
) \
do { \
CAF_TEST_PRINT_ERROR(
msg);
\
CAF_TEST_PRINT_ERROR(
__VA_ARGS__);
\
::caf::test::engine::current_test()->fail(false); \
::caf::test::detail::requirement_failed("test failure"); \
} 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) \
namespace { \
struct CAF_UNIQUE(test) : caf_test_case_auto_fixture { \
...
...
@@ -590,7 +623,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
CAF_TEST_PRINT_VERBOSE(msg); \
} while (false)
// -- CAF_CHECK*
predicate family
----------------------------------------------
// -- CAF_CHECK*
macro family ----
----------------------------------------------
#define CAF_CHECK_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
...
...
@@ -672,33 +705,139 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
caf::detail::stringification_inspector::render(y_val)); \
}(x_expr, y_expr)
// -- CAF_CHECK* predicate family ----------------------------------------------
#define CAF_REQUIRE_EQUAL(x, y) CAF_REQUIRE_FUNC(::caf::test::equal_to, x, y)
#ifdef CAF_ENABLE_EXCEPTIONS
# 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) \
CAF_REQUIRE_FUNC(::caf::test::not_equal_to, x, y)
#define CAF_REQUIRE_NOT_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_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) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::less_than>, x, y)
#define CAF_REQUIRE_NOT_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__, "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) \
CAF_REQUIRE_FUNC(::caf::test::less_than_or_equal, x, y)
#define CAF_REQUIRE_LESS_OR_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_LESS_OR_EQUAL(x, y) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::less_than_or_equal>, x, y)
#define CAF_REQUIRE_NOT_LESS_OR_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__, "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) \
CAF_REQUIRE_FUNC(::caf::test::greater_than, x, y)
#define CAF_REQUIRE_GREATER(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_GREATER(x, y) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::greater_than>, x, y)
#define CAF_REQUIRE_NOT_GREATER(x_expr, y_expr) \
[](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) \
CAF_REQUIRE_FUNC(::caf::test::greater_than_or_equal, x, y)
#define CAF_REQUIRE_GREATER_OR_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_GREATER_OR_EQUAL(x, y) \
CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::greater_than_or_equal>, \
x, y)
#define CAF_REQUIRE_NOT_GREATER_OR_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__, "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 @@
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
{
public:
static
void
start
(
int
secs
);
...
...
@@ -121,6 +129,7 @@ namespace detail {
<<
term
::
yellow
<<
":"
<<
term
::
cyan
<<
engine
::
last_check_line
()
<<
term
::
reset
<<
detail
::
fill
(
engine
::
last_check_line
())
<<
"had last successful check"
<<
'\n'
;
CAF_RAISE_ERROR
(
requirement_error
,
msg
.
c_str
());
abort
();
}
...
...
@@ -200,6 +209,59 @@ bool check_bin(bool result, const char* file, size_t line, const char* expr,
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
logger
::
stream
::
stream
(
logger
&
parent
,
logger
::
level
lvl
)
...
...
@@ -319,36 +381,6 @@ bool engine::run(bool colorize, const std::string& log_file,
size_t
total_bad
=
0
;
size_t
total_bad_expected
=
0
;
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
tests
{
tests_str
};
std
::
regex
not_suites
;
...
...
@@ -358,18 +390,16 @@ bool engine::run(bool colorize, const std::string& log_file,
not_suites
.
assign
(
not_suites_str
);
if
(
!
not_tests_str
.
empty
())
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
)
{
// an empty whitelist means original input was "*", i.e., enable all
return
std
::
regex_search
(
x
,
whitelist
)
&&
!
std
::
regex_search
(
x
,
blacklist
);
return
std
::
regex_search
(
x
,
selected
)
&&
!
std
::
regex_search
(
x
,
blocked
);
};
#endif
auto
test_enabled
=
[
&
](
const
whitelist_type
&
whitelist
,
const
blacklist_type
&
blacklist
,
const
test
&
x
)
{
auto
test_enabled
=
[
&
](
const
std
::
regex
&
selected
,
const
std
::
regex
&
blocked
,
const
test
&
x
)
{
// Disabled tests run if explicitly requested by the user, i.e.,
// tests_str is not the ".*" catch-all default.
return
(
!
x
.
disabled
()
||
tests_str
!=
".*"
)
&&
enabled
(
whitelist
,
blacklist
,
x
.
name
());
&&
enabled
(
selected
,
blocked
,
x
.
name
());
};
std
::
vector
<
std
::
string
>
failed_tests
;
for
(
auto
&
p
:
instance
().
suites_
)
{
...
...
@@ -393,7 +423,23 @@ bool engine::run(bool colorize, const std::string& log_file,
log
.
verbose
()
<<
term
::
yellow
<<
"- "
<<
term
::
reset
<<
t
->
name
()
<<
'\n'
;
auto
start
=
std
::
chrono
::
high_resolution_clock
::
now
();
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
();
#endif // CAF_ENABLE_EXCEPTIONS
watchdog
::
stop
();
auto
stop
=
std
::
chrono
::
high_resolution_clock
::
now
();
auto
elapsed
...
...
@@ -515,8 +561,8 @@ std::string engine::render(std::chrono::microseconds t) {
return
t
.
count
()
>
1000000
?
(
std
::
to_string
(
t
.
count
()
/
1000000
)
+
'.'
+
std
::
to_string
((
t
.
count
()
%
1000000
)
/
10000
)
+
" s"
)
:
t
.
count
()
>
1000
?
(
std
::
to_string
(
t
.
count
()
/
1000
)
+
" ms"
)
:
(
std
::
to_string
(
t
.
count
())
+
" us"
);
:
t
.
count
()
>
1000
?
(
std
::
to_string
(
t
.
count
()
/
1000
)
+
" ms"
)
:
(
std
::
to_string
(
t
.
count
())
+
" us"
);
}
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