456 lines
14 KiB
C++
456 lines
14 KiB
C++
#include "../test.h"
|
|
#include "rxcpp/operators/rx-reduce.hpp"
|
|
|
|
SCENARIO("reduce some data with seed", "[reduce][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
|
|
int seed = 42;
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.next(210, 0),
|
|
on.next(220, 1),
|
|
on.next(230, 2),
|
|
on.next(240, 3),
|
|
on.next(250, 4),
|
|
on.completed(260)
|
|
});
|
|
|
|
WHEN("mapped to ints that are one larger"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs
|
|
.reduce(seed,
|
|
[](int sum, int x) {
|
|
return sum + x;
|
|
},
|
|
[](int sum) {
|
|
return sum * 5;
|
|
})
|
|
// forget type to workaround lambda deduction bug on msvc 2013
|
|
.as_dynamic();
|
|
}
|
|
);
|
|
|
|
THEN("the output stops on completion"){
|
|
auto required = rxu::to_vector({
|
|
on.next(260, (seed + 0 + 1 + 2 + 3 + 4) * 5),
|
|
on.completed(260)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 260)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("accumulate some data with seed", "[accumulate][reduce][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
|
|
int seed = 42;
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.next(210, 0),
|
|
on.next(220, 1),
|
|
on.next(230, 2),
|
|
on.next(240, 3),
|
|
on.next(250, 4),
|
|
on.completed(260)
|
|
});
|
|
|
|
WHEN("mapped to ints that are one larger"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs
|
|
.accumulate(seed,
|
|
[](int sum, int x) {
|
|
return sum + x;
|
|
},
|
|
[](int sum) {
|
|
return sum * 5;
|
|
})
|
|
// forget type to workaround lambda deduction bug on msvc 2013
|
|
.as_dynamic();
|
|
}
|
|
);
|
|
|
|
THEN("the output stops on completion"){
|
|
auto required = rxu::to_vector({
|
|
on.next(260, (seed + 0 + 1 + 2 + 3 + 4) * 5),
|
|
on.completed(260)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 260)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("average some data", "[reduce][average][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<double> d_on;
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.next(210, 3),
|
|
on.next(220, 4),
|
|
on.next(230, 2),
|
|
on.completed(250)
|
|
});
|
|
|
|
WHEN("mapped to ints that are one larger"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.average();
|
|
}
|
|
);
|
|
|
|
THEN("the output stops on completion"){
|
|
auto required = rxu::to_vector({
|
|
d_on.next(250, 3.0),
|
|
d_on.completed(250)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("sum some data", "[reduce][sum][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.next(210, 3),
|
|
on.next(220, 4),
|
|
on.next(230, 2),
|
|
on.completed(250)
|
|
});
|
|
|
|
WHEN("sum is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.sum();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains the sum of source values"){
|
|
auto required = rxu::to_vector({
|
|
d_on.next(250, 9),
|
|
d_on.completed(250)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("max", "[reduce][max][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.next(210, 3),
|
|
on.next(220, 4),
|
|
on.next(230, 2),
|
|
on.completed(250)
|
|
});
|
|
|
|
WHEN("max is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.max();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains the max of source values"){
|
|
auto required = rxu::to_vector({
|
|
d_on.next(250, 4),
|
|
d_on.completed(250)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Does not work because calling max() on an empty stream throws an exception
|
|
// which will crash when exceptions are disabled.
|
|
//
|
|
// TODO: the max internal implementation should be rewritten not to throw exceptions.
|
|
SCENARIO("max, empty", "[reduce][max][operators][!throws]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
std::runtime_error ex("max on_error");
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.completed(250)
|
|
});
|
|
|
|
WHEN("max is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.max();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains only error message"){
|
|
auto required = rxu::to_vector({
|
|
d_on.error(250, ex)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("max, error", "[reduce][max][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
std::runtime_error ex("max on_error from source");
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.error(250, ex)
|
|
});
|
|
|
|
WHEN("max is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.max();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains only error message"){
|
|
auto required = rxu::to_vector({
|
|
d_on.error(250, ex)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("min", "[reduce][min][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.next(210, 3),
|
|
on.next(220, 4),
|
|
on.next(230, 2),
|
|
on.completed(250)
|
|
});
|
|
|
|
WHEN("min is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.min();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains the min of source values"){
|
|
auto required = rxu::to_vector({
|
|
d_on.next(250, 2),
|
|
d_on.completed(250)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Does not work with exceptions disabled, min will throw when stream is empty
|
|
// and this crashes immediately.
|
|
// TODO: min implementation should be rewritten not to throw exceptions.
|
|
SCENARIO("min, empty", "[reduce][min][operators][!throws]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
std::runtime_error ex("min on_error");
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.completed(250)
|
|
});
|
|
|
|
WHEN("min is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.min();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains only error message"){
|
|
auto required = rxu::to_vector({
|
|
d_on.error(250, ex)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("min, error", "[reduce][min][operators]"){
|
|
GIVEN("a test hot observable of ints"){
|
|
auto sc = rxsc::make_test();
|
|
auto w = sc.create_worker();
|
|
const rxsc::test::messages<int> on;
|
|
const rxsc::test::messages<int> d_on;
|
|
|
|
std::runtime_error ex("min on_error from source");
|
|
|
|
auto xs = sc.make_hot_observable({
|
|
on.next(150, 1),
|
|
on.error(250, ex)
|
|
});
|
|
|
|
WHEN("min is calculated"){
|
|
|
|
auto res = w.start(
|
|
[&]() {
|
|
return xs.min();
|
|
}
|
|
);
|
|
|
|
THEN("the output contains only error message"){
|
|
auto required = rxu::to_vector({
|
|
d_on.error(250, ex)
|
|
});
|
|
auto actual = res.get_observer().messages();
|
|
REQUIRE(required == actual);
|
|
}
|
|
|
|
THEN("there was one subscription and one unsubscription"){
|
|
auto required = rxu::to_vector({
|
|
on.subscribe(200, 250)
|
|
});
|
|
auto actual = xs.subscriptions();
|
|
REQUIRE(required == actual);
|
|
}
|
|
}
|
|
}
|
|
}
|