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
f2c9d7e3
Commit
f2c9d7e3
authored
Mar 24, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Restructure broadcast observables
parent
a6583d46
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
905 additions
and
688 deletions
+905
-688
libcaf_core/caf/flow/concat.hpp
libcaf_core/caf/flow/concat.hpp
+1
-2
libcaf_core/caf/flow/observable.hpp
libcaf_core/caf/flow/observable.hpp
+606
-655
libcaf_core/caf/flow/observable_builder.hpp
libcaf_core/caf/flow/observable_builder.hpp
+2
-3
libcaf_core/caf/flow/observer.hpp
libcaf_core/caf/flow/observer.hpp
+6
-18
libcaf_core/caf/flow/observer_state.hpp
libcaf_core/caf/flow/observer_state.hpp
+12
-0
libcaf_core/caf/flow/step.hpp
libcaf_core/caf/flow/step.hpp
+43
-3
libcaf_core/caf/flow/zip_with.hpp
libcaf_core/caf/flow/zip_with.hpp
+4
-4
libcaf_core/test/flow/broadcaster.cpp
libcaf_core/test/flow/broadcaster.cpp
+3
-3
libcaf_core/test/flow/concat.cpp
libcaf_core/test/flow/concat.cpp
+96
-0
libcaf_core/test/flow/merge.cpp
libcaf_core/test/flow/merge.cpp
+132
-0
No files found.
libcaf_core/caf/flow/concat.hpp
View file @
f2c9d7e3
...
@@ -18,8 +18,7 @@ concat(Observable x, Observables... xs) {
...
@@ -18,8 +18,7 @@ concat(Observable x, Observables... xs) {
static_assert
(
static_assert
(
(
std
::
is_same_v
<
output_type
,
output_type_t
<
Observables
>>
&&
...));
(
std
::
is_same_v
<
output_type
,
output_type_t
<
Observables
>>
&&
...));
auto
hdl
=
std
::
move
(
x
).
as_observable
();
auto
hdl
=
std
::
move
(
x
).
as_observable
();
auto
ptr
=
make_counted
<
merger_impl
<
output_type
>>
(
hdl
.
ptr
()
->
ctx
());
auto
ptr
=
make_counted
<
concat_impl
<
output_type
>>
(
hdl
.
ptr
()
->
ctx
());
ptr
->
concat_mode
(
true
);
ptr
->
add
(
std
::
move
(
hdl
));
ptr
->
add
(
std
::
move
(
hdl
));
(
ptr
->
add
(
std
::
move
(
xs
).
as_observable
()),
...);
(
ptr
->
add
(
std
::
move
(
xs
).
as_observable
()),
...);
return
observable
<
output_type
>
{
std
::
move
(
ptr
)};
return
observable
<
output_type
>
{
std
::
move
(
ptr
)};
...
...
libcaf_core/caf/flow/observable.hpp
View file @
f2c9d7e3
This diff is collapsed.
Click to expand it.
libcaf_core/caf/flow/observable_builder.hpp
View file @
f2c9d7e3
...
@@ -296,10 +296,9 @@ private:
...
@@ -296,10 +296,9 @@ private:
/// a single @ref observable object.
/// a single @ref observable object.
template
<
class
Generator
,
class
...
Steps
>
template
<
class
Generator
,
class
...
Steps
>
class
generation
final
class
generation
final
:
public
observable_def
<
:
public
observable_def
<
steps_output_type_t
<
Generator
,
Steps
...
>>
{
transform_processor_output_type_t
<
Generator
,
Steps
...
>>
{
public:
public:
using
output_type
=
transform_processor
_output_type_t
<
Generator
,
Steps
...
>
;
using
output_type
=
steps
_output_type_t
<
Generator
,
Steps
...
>
;
class
impl
:
public
observable_impl_base
<
output_type
>
{
class
impl
:
public
observable_impl_base
<
output_type
>
{
public:
public:
...
...
libcaf_core/caf/flow/observer.hpp
View file @
f2c9d7e3
...
@@ -477,42 +477,33 @@ private:
...
@@ -477,42 +477,33 @@ private:
// -- utility observer ---------------------------------------------------------
// -- utility observer ---------------------------------------------------------
/// Forwards all events to a parent.
/// Forwards all events to a parent.
template
<
class
T
,
class
Parent
,
class
Token
=
unit_t
>
template
<
class
T
,
class
Parent
,
class
Token
>
class
forwarder
:
public
ref_counted
,
public
observer_impl
<
T
>
{
class
forwarder
:
public
ref_counted
,
public
observer_impl
<
T
>
{
public:
public:
CAF_INTRUSIVE_PTR_FRIENDS
(
forwarder
)
CAF_INTRUSIVE_PTR_FRIENDS
(
forwarder
)
explicit
forwarder
(
intrusive_ptr
<
Parent
>
parent
,
Token
token
=
Token
{}
)
explicit
forwarder
(
intrusive_ptr
<
Parent
>
parent
,
Token
token
)
:
parent
(
std
::
move
(
parent
)),
token
(
std
::
move
(
token
))
{
:
parent
(
std
::
move
(
parent
)),
token
(
std
::
move
(
token
))
{
// nop
// nop
}
}
void
on_complete
()
override
{
void
on_complete
()
override
{
if
(
parent
)
{
if
(
parent
)
{
if
constexpr
(
std
::
is_same_v
<
Token
,
unit_t
>
)
parent
->
fwd_on_complete
(
token
);
parent
->
fwd_on_complete
(
this
);
else
parent
->
fwd_on_complete
(
this
,
token
);
parent
=
nullptr
;
parent
=
nullptr
;
}
}
}
}
void
on_error
(
const
error
&
what
)
override
{
void
on_error
(
const
error
&
what
)
override
{
if
(
parent
)
{
if
(
parent
)
{
if
constexpr
(
std
::
is_same_v
<
Token
,
unit_t
>
)
parent
->
fwd_on_error
(
token
,
what
);
parent
->
fwd_on_error
(
this
,
what
);
else
parent
->
fwd_on_error
(
this
,
token
,
what
);
parent
=
nullptr
;
parent
=
nullptr
;
}
}
}
}
void
on_subscribe
(
subscription
new_sub
)
override
{
void
on_subscribe
(
subscription
new_sub
)
override
{
if
(
parent
)
{
if
(
parent
)
{
if
constexpr
(
std
::
is_same_v
<
Token
,
unit_t
>
)
parent
->
fwd_on_subscribe
(
token
,
std
::
move
(
new_sub
));
parent
->
fwd_on_subscribe
(
this
,
std
::
move
(
new_sub
));
else
parent
->
fwd_on_subscribe
(
this
,
token
,
std
::
move
(
new_sub
));
}
else
{
}
else
{
new_sub
.
cancel
();
new_sub
.
cancel
();
}
}
...
@@ -520,10 +511,7 @@ public:
...
@@ -520,10 +511,7 @@ public:
void
on_next
(
span
<
const
T
>
items
)
override
{
void
on_next
(
span
<
const
T
>
items
)
override
{
if
(
parent
)
{
if
(
parent
)
{
if
constexpr
(
std
::
is_same_v
<
Token
,
unit_t
>
)
parent
->
fwd_on_next
(
token
,
items
);
parent
->
fwd_on_next
(
this
,
items
);
else
parent
->
fwd_on_next
(
this
,
token
,
items
);
}
}
}
}
...
...
libcaf_core/caf/flow/observer_state.hpp
View file @
f2c9d7e3
...
@@ -23,6 +23,18 @@ enum class observer_state {
...
@@ -23,6 +23,18 @@ enum class observer_state {
disposed
,
disposed
,
};
};
/// Returns whether `x` represents a final state, i.e., `completed`, `aborted`
/// or `disposed`.
constexpr
bool
is_final
(
observer_state
x
)
noexcept
{
return
static_cast
<
int
>
(
x
)
>=
static_cast
<
int
>
(
observer_state
::
completed
);
}
/// Returns whether `x` represents an active state, i.e., `idle` or
/// `subscribed`.
constexpr
bool
is_active
(
observer_state
x
)
noexcept
{
return
static_cast
<
int
>
(
x
)
<=
static_cast
<
int
>
(
observer_state
::
subscribed
);
}
/// @relates sec
/// @relates sec
CAF_CORE_EXPORT
std
::
string
to_string
(
observer_state
);
CAF_CORE_EXPORT
std
::
string
to_string
(
observer_state
);
...
...
libcaf_core/caf/flow/step.hpp
View file @
f2c9d7e3
...
@@ -16,6 +16,28 @@
...
@@ -16,6 +16,28 @@
namespace
caf
::
flow
{
namespace
caf
::
flow
{
template
<
class
T
>
struct
identity_step
{
using
input_type
=
T
;
using
output_type
=
T
;
template
<
class
Next
,
class
...
Steps
>
bool
on_next
(
const
input_type
&
item
,
Next
&
next
,
Steps
&
...
steps
)
{
return
next
.
on_next
(
item
,
steps
...);
}
template
<
class
Next
,
class
...
Steps
>
void
on_complete
(
Next
&
next
,
Steps
&
...
steps
)
{
next
.
on_complete
(
steps
...);
}
template
<
class
Next
,
class
...
Steps
>
void
on_error
(
const
error
&
what
,
Next
&
next
,
Steps
&
...
steps
)
{
next
.
on_error
(
what
,
steps
...);
}
};
template
<
class
T
>
template
<
class
T
>
struct
limit_step
{
struct
limit_step
{
size_t
remaining
;
size_t
remaining
;
...
@@ -464,12 +486,15 @@ public:
...
@@ -464,12 +486,15 @@ public:
/// Calls `on_complete` on all observers and drops any pending data.
/// Calls `on_complete` on all observers and drops any pending data.
void
close
()
{
void
close
()
{
buf_
.
clear
();
buf_
.
clear
();
if
(
!
err_
)
if
(
!
err_
)
{
for
(
auto
&
out
:
outputs_
)
for
(
auto
&
out
:
outputs_
)
out
.
sink
.
on_complete
();
out
.
sink
.
on_complete
();
else
state_
=
observable_state
::
completed
;
}
else
{
for
(
auto
&
out
:
outputs_
)
for
(
auto
&
out
:
outputs_
)
out
.
sink
.
on_error
(
err_
);
out
.
sink
.
on_error
(
err_
);
state_
=
observable_state
::
aborted
;
}
outputs_
.
clear
();
outputs_
.
clear
();
}
}
...
@@ -502,7 +527,6 @@ public:
...
@@ -502,7 +527,6 @@ public:
if
(
is_active
(
state_
))
{
if
(
is_active
(
state_
))
{
if
(
idle
())
{
if
(
idle
())
{
close
();
close
();
state_
=
err_
?
observable_state
::
aborted
:
observable_state
::
completed
;
}
else
{
}
else
{
state_
=
observable_state
::
completing
;
state_
=
observable_state
::
completing
;
}
}
...
@@ -577,4 +601,20 @@ private:
...
@@ -577,4 +601,20 @@ private:
#endif
#endif
};
};
/// Utility for the observables that use one or more steps.
template
<
class
...
Steps
>
struct
steps_output_oracle
;
template
<
class
Step
>
struct
steps_output_oracle
<
Step
>
{
using
type
=
typename
Step
::
output_type
;
};
template
<
class
Step1
,
class
Step2
,
class
...
Steps
>
struct
steps_output_oracle
<
Step1
,
Step2
,
Steps
...
>
:
steps_output_oracle
<
Step2
,
Steps
...
>
{};
template
<
class
...
Steps
>
using
steps_output_type_t
=
typename
steps_output_oracle
<
Steps
...
>::
type
;
}
// namespace caf::flow
}
// namespace caf::flow
libcaf_core/caf/flow/zip_with.hpp
View file @
f2c9d7e3
...
@@ -166,7 +166,7 @@ private:
...
@@ -166,7 +166,7 @@ private:
}
}
template
<
size_t
I
>
template
<
size_t
I
>
void
fwd_on_subscribe
(
ref_counted
*
,
zipper_index
<
I
>
index
,
subscription
sub
)
{
void
fwd_on_subscribe
(
zipper_index
<
I
>
index
,
subscription
sub
)
{
if
(
!
term_
.
finalized
())
{
if
(
!
term_
.
finalized
())
{
auto
&
in
=
at
(
index
);
auto
&
in
=
at
(
index
);
if
(
!
in
.
sub
)
{
if
(
!
in
.
sub
)
{
...
@@ -182,7 +182,7 @@ private:
...
@@ -182,7 +182,7 @@ private:
}
}
template
<
size_t
I
>
template
<
size_t
I
>
void
fwd_on_complete
(
ref_counted
*
,
zipper_index
<
I
>
index
)
{
void
fwd_on_complete
(
zipper_index
<
I
>
index
)
{
if
(
!
broken_
)
{
if
(
!
broken_
)
{
broken_
=
true
;
broken_
=
true
;
if
(
at
(
index
).
buf
.
empty
())
if
(
at
(
index
).
buf
.
empty
())
...
@@ -192,7 +192,7 @@ private:
...
@@ -192,7 +192,7 @@ private:
}
}
template
<
size_t
I
>
template
<
size_t
I
>
void
fwd_on_error
(
ref_counted
*
,
zipper_index
<
I
>
index
,
const
error
&
what
)
{
void
fwd_on_error
(
zipper_index
<
I
>
index
,
const
error
&
what
)
{
if
(
!
term_
.
err
())
if
(
!
term_
.
err
())
term_
.
err
(
what
);
term_
.
err
(
what
);
if
(
!
broken_
)
{
if
(
!
broken_
)
{
...
@@ -204,7 +204,7 @@ private:
...
@@ -204,7 +204,7 @@ private:
}
}
template
<
size_t
I
,
class
T
>
template
<
size_t
I
,
class
T
>
void
fwd_on_next
(
ref_counted
*
,
zipper_index
<
I
>
index
,
span
<
const
T
>
items
)
{
void
fwd_on_next
(
zipper_index
<
I
>
index
,
span
<
const
T
>
items
)
{
if
(
!
term_
.
finalized
())
{
if
(
!
term_
.
finalized
())
{
auto
&
buf
=
at
(
index
).
buf
;
auto
&
buf
=
at
(
index
).
buf
;
buf
.
insert
(
buf
.
end
(),
items
.
begin
(),
items
.
end
());
buf
.
insert
(
buf
.
end
(),
items
.
begin
(),
items
.
end
());
...
...
libcaf_core/test/flow/broadcaster.cpp
View file @
f2c9d7e3
...
@@ -27,7 +27,7 @@ BEGIN_FIXTURE_SCOPE(fixture)
...
@@ -27,7 +27,7 @@ BEGIN_FIXTURE_SCOPE(fixture)
SCENARIO
(
"a broadcaster pushes items to all subscribers at the same time"
)
{
SCENARIO
(
"a broadcaster pushes items to all subscribers at the same time"
)
{
GIVEN
(
"a broadcaster with one source and three sinks"
)
{
GIVEN
(
"a broadcaster with one source and three sinks"
)
{
auto
uut
=
make_counted
<
flow
::
broadcaster_impl
<
int
>
>
(
ctx
.
get
());
auto
uut
=
flow
::
make_broadcaster_impl
<
int
>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
auto
snk1
=
flow
::
make_passive_observer
<
int
>
();
auto
snk1
=
flow
::
make_passive_observer
<
int
>
();
auto
snk2
=
flow
::
make_passive_observer
<
int
>
();
auto
snk2
=
flow
::
make_passive_observer
<
int
>
();
...
@@ -71,7 +71,7 @@ SCENARIO("a broadcaster pushes items to all subscribers at the same time") {
...
@@ -71,7 +71,7 @@ SCENARIO("a broadcaster pushes items to all subscribers at the same time") {
SCENARIO
(
"a broadcaster emits values before propagating completion"
)
{
SCENARIO
(
"a broadcaster emits values before propagating completion"
)
{
GIVEN
(
"a broadcaster with one source and three sinks"
)
{
GIVEN
(
"a broadcaster with one source and three sinks"
)
{
auto
uut
=
make_counted
<
flow
::
broadcaster_impl
<
int
>
>
(
ctx
.
get
());
auto
uut
=
flow
::
make_broadcaster_impl
<
int
>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
auto
snk1
=
flow
::
make_passive_observer
<
int
>
();
auto
snk1
=
flow
::
make_passive_observer
<
int
>
();
auto
snk2
=
flow
::
make_passive_observer
<
int
>
();
auto
snk2
=
flow
::
make_passive_observer
<
int
>
();
...
@@ -114,7 +114,7 @@ SCENARIO("a broadcaster emits values before propagating completion") {
...
@@ -114,7 +114,7 @@ SCENARIO("a broadcaster emits values before propagating completion") {
SCENARIO
(
"a broadcaster emits values before propagating errors"
)
{
SCENARIO
(
"a broadcaster emits values before propagating errors"
)
{
GIVEN
(
"a broadcaster with one source and three sinks"
)
{
GIVEN
(
"a broadcaster with one source and three sinks"
)
{
auto
uut
=
make_counted
<
flow
::
broadcaster_impl
<
int
>
>
(
ctx
.
get
());
auto
uut
=
flow
::
make_broadcaster_impl
<
int
>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
auto
snk1
=
flow
::
make_passive_observer
<
int
>
();
auto
snk1
=
flow
::
make_passive_observer
<
int
>
();
auto
snk2
=
flow
::
make_passive_observer
<
int
>
();
auto
snk2
=
flow
::
make_passive_observer
<
int
>
();
...
...
libcaf_core/test/flow/concat.cpp
View file @
f2c9d7e3
...
@@ -17,12 +17,108 @@ namespace {
...
@@ -17,12 +17,108 @@ namespace {
struct
fixture
:
test_coordinator_fixture
<>
{
struct
fixture
:
test_coordinator_fixture
<>
{
flow
::
scoped_coordinator_ptr
ctx
=
flow
::
make_scoped_coordinator
();
flow
::
scoped_coordinator_ptr
ctx
=
flow
::
make_scoped_coordinator
();
template
<
class
...
Ts
>
std
::
vector
<
int
>
ls
(
Ts
...
xs
)
{
return
std
::
vector
<
int
>
{
xs
...};
}
};
};
}
// namespace
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"concatenate processes inputs sequentially"
)
{
GIVEN
(
"a concatenation with no inputs and shutdown-on-last-complete ON"
)
{
auto
uut
=
make_counted
<
flow
::
concat_impl
<
int
>>
(
ctx
.
get
());
WHEN
(
"subscribing to the concatenation"
)
{
THEN
(
"the concatenation immediately closes"
)
{
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
aborted
);
CHECK_EQ
(
snk
->
err
,
sec
::
disposed
);
CHECK
(
snk
->
buf
.
empty
());
}
}
}
GIVEN
(
"a concatenation with no inputs and shutdown-on-last-complete OFF"
)
{
auto
uut
=
make_counted
<
flow
::
concat_impl
<
int
>>
(
ctx
.
get
());
uut
->
shutdown_on_last_complete
(
false
);
WHEN
(
"subscribing to the concatenation"
)
{
THEN
(
"the concatenation accepts the subscription and does nothing else"
)
{
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK
(
snk
->
buf
.
empty
());
}
}
}
GIVEN
(
"a concatenation with one input that completes"
)
{
WHEN
(
"subscribing and requesting before the first push"
)
{
auto
uut
=
make_counted
<
flow
::
concat_impl
<
int
>>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
uut
->
add
(
src
->
as_observable
());
ctx
->
run
();
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
THEN
(
"the concatenation forwards all items from the source"
)
{
MESSAGE
(
"the observer enters the state subscribed"
);
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
());
MESSAGE
(
"when requesting data, no data is received yet"
);
snk
->
sub
.
request
(
2
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
());
MESSAGE
(
"after pushing, the observer immediately receives them"
);
src
->
push
(
1
,
2
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
));
MESSAGE
(
"when requesting more data, the observer gets the remainder"
);
snk
->
sub
.
request
(
20
);
ctx
->
run
();
src
->
push
(
3
,
4
,
5
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
MESSAGE
(
"the concatenation closes if the source closes"
);
src
->
complete
();
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
completed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
}
}
}
GIVEN
(
"a concatenation with one input that aborts after some items"
)
{
WHEN
(
"subscribing to the concatenation"
)
{
auto
uut
=
make_counted
<
flow
::
concat_impl
<
int
>>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
uut
->
add
(
src
->
as_observable
());
ctx
->
run
();
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
THEN
(
"the concatenation forwards all items until the error"
)
{
MESSAGE
(
"after the source pushed five items, it emits an error"
);
snk
->
sub
.
request
(
20
);
ctx
->
run
();
src
->
push
(
1
,
2
,
3
,
4
,
5
);
ctx
->
run
();
src
->
abort
(
make_error
(
sec
::
runtime_error
));
ctx
->
run
();
MESSAGE
(
"the observer obtains the and then the error"
);
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
aborted
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
CHECK_EQ
(
snk
->
err
,
make_error
(
sec
::
runtime_error
));
}
}
}
}
SCENARIO
(
"concat operators combine inputs"
)
{
SCENARIO
(
"concat operators combine inputs"
)
{
GIVEN
(
"two observables"
)
{
GIVEN
(
"two observables"
)
{
WHEN
(
"merging them to a single publisher via concat"
)
{
WHEN
(
"merging them to a single publisher via concat"
)
{
...
...
libcaf_core/test/flow/merge.cpp
View file @
f2c9d7e3
...
@@ -17,12 +17,144 @@ namespace {
...
@@ -17,12 +17,144 @@ namespace {
struct
fixture
:
test_coordinator_fixture
<>
{
struct
fixture
:
test_coordinator_fixture
<>
{
flow
::
scoped_coordinator_ptr
ctx
=
flow
::
make_scoped_coordinator
();
flow
::
scoped_coordinator_ptr
ctx
=
flow
::
make_scoped_coordinator
();
template
<
class
...
Ts
>
std
::
vector
<
int
>
ls
(
Ts
...
xs
)
{
return
std
::
vector
<
int
>
{
xs
...};
}
};
};
}
// namespace
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"mergers round-robin over their inputs"
)
{
GIVEN
(
"a merger with no inputs and shutdown-on-last-complete ON"
)
{
auto
uut
=
make_counted
<
flow
::
merger_impl
<
int
>>
(
ctx
.
get
());
WHEN
(
"subscribing to the merger"
)
{
THEN
(
"the merger immediately closes"
)
{
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
aborted
);
CHECK_EQ
(
snk
->
err
,
sec
::
disposed
);
CHECK
(
snk
->
buf
.
empty
());
}
}
}
GIVEN
(
"a merger with no inputs and shutdown-on-last-complete OFF"
)
{
auto
uut
=
make_counted
<
flow
::
merger_impl
<
int
>>
(
ctx
.
get
());
uut
->
shutdown_on_last_complete
(
false
);
WHEN
(
"subscribing to the merger"
)
{
THEN
(
"the merger accepts the subscription and does nothing else"
)
{
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK
(
snk
->
buf
.
empty
());
}
}
}
GIVEN
(
"a round-robin merger with one input that completes"
)
{
WHEN
(
"subscribing to the merger and requesting before the first push"
)
{
auto
uut
=
make_counted
<
flow
::
merger_impl
<
int
>>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
uut
->
add
(
src
->
as_observable
());
ctx
->
run
();
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
THEN
(
"the merger forwards all items from the source"
)
{
MESSAGE
(
"the observer enters the state subscribed"
);
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
());
MESSAGE
(
"when requesting data, no data is received yet"
);
snk
->
sub
.
request
(
2
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
());
MESSAGE
(
"after pushing, the observer immediately receives them"
);
src
->
push
(
1
,
2
,
3
,
4
,
5
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
));
MESSAGE
(
"when requesting more data, the observer gets the remainder"
);
snk
->
sub
.
request
(
20
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
MESSAGE
(
"the merger closes if the source closes"
);
src
->
complete
();
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
completed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
}
}
AND_WHEN
(
"subscribing to the merger pushing before the first request"
)
{
auto
uut
=
make_counted
<
flow
::
merger_impl
<
int
>>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
uut
->
add
(
src
->
as_observable
());
ctx
->
run
();
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
THEN
(
"the merger forwards all items from the source"
)
{
MESSAGE
(
"the observer enters the state subscribed"
);
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
());
MESSAGE
(
"after pushing, the observer receives nothing yet"
);
src
->
push
(
1
,
2
,
3
,
4
,
5
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
());
MESSAGE
(
"the observer get the first items immediately when requesting"
);
snk
->
sub
.
request
(
2
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
));
MESSAGE
(
"when requesting more data, the observer gets the remainder"
);
snk
->
sub
.
request
(
20
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
MESSAGE
(
"the merger closes if the source closes"
);
src
->
complete
();
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
completed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
}
}
}
GIVEN
(
"a round-robin merger with one input that aborts after some items"
)
{
WHEN
(
"subscribing to the merger"
)
{
auto
uut
=
make_counted
<
flow
::
merger_impl
<
int
>>
(
ctx
.
get
());
auto
src
=
flow
::
make_passive_observable
<
int
>
(
ctx
.
get
());
uut
->
add
(
src
->
as_observable
());
ctx
->
run
();
auto
snk
=
flow
::
make_passive_observer
<
int
>
();
uut
->
subscribe
(
snk
->
as_observer
());
ctx
->
run
();
THEN
(
"the merger forwards all items from the source until the error"
)
{
MESSAGE
(
"after the source pushed five items, it emits an error"
);
src
->
push
(
1
,
2
,
3
,
4
,
5
);
ctx
->
run
();
src
->
abort
(
make_error
(
sec
::
runtime_error
));
ctx
->
run
();
MESSAGE
(
"when requesting, the observer still obtains the items first"
);
snk
->
sub
.
request
(
2
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
subscribed
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
));
snk
->
sub
.
request
(
20
);
ctx
->
run
();
CHECK_EQ
(
snk
->
state
,
flow
::
observer_state
::
aborted
);
CHECK_EQ
(
snk
->
buf
,
ls
(
1
,
2
,
3
,
4
,
5
));
CHECK_EQ
(
snk
->
err
,
make_error
(
sec
::
runtime_error
));
}
}
}
}
SCENARIO
(
"merge operators combine inputs"
)
{
SCENARIO
(
"merge operators combine inputs"
)
{
GIVEN
(
"two observables"
)
{
GIVEN
(
"two observables"
)
{
WHEN
(
"merging them to a single publisher"
)
{
WHEN
(
"merging them to a single publisher"
)
{
...
...
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