// Copyright Sebastian Jeckel 2017.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "gtest/gtest.h"
#include "react/algorithm.h"
#include "react/observer.h"
#include
#include
#include
#include
#include
#include
using namespace react;
TEST(AlgorithmTest, Hold)
{
// Hold last value of event source in state.
Group g;
auto evt1 = EventSource::Create(g);
State st = Hold(1, evt1);
int output = 0;
int turns = 0;
auto obs = Observer::Create([&] (int v)
{
++turns;
output = v;
}, st);
// Initial call. Output should take the value of initial value.
EXPECT_EQ(1, output);
EXPECT_EQ(1, turns);
// Event changes value.
evt1.Emit(10);
EXPECT_EQ(10, output);
EXPECT_EQ(2, turns);
// New event, but same value, observer should not be called.
evt1.Emit(10);
EXPECT_EQ(10, output);
EXPECT_EQ(2, turns);
}
TEST(AlgorithmTest, Monitor1)
{
// Emit events when value of state changes.
Group g;
auto st = StateVar::Create(g, 1);
Event evt = Monitor( st );
int output = 0;
int turns = 0;
auto obs = Observer::Create([&] (const auto& events)
{
++turns;
for (int e : events)
output += e;
}, evt);
EXPECT_EQ(0, output);
EXPECT_EQ(0, turns);
// Change from 1 -> 10: Event.
st.Set(10);
// Change from 10 -> 20: Event.
st.Set(20);
// Change from 20 -> 20: No event.
st.Set(20);
// 10 + 20 were the changes.
EXPECT_EQ(30, output);
EXPECT_EQ(2, turns);
}
TEST(AlgorithmTest, Monitor2)
{
// Monitor state changes and filter the resulting events.
Group g;
auto target = StateVar::Create(g, 10);
std::vector results;
auto filterFunc = [] (int v) { return v > 10; };
{
// Observer is created in a nested scope so it gets destructed before the end of this function.
auto obs = Observer::Create([&] (const auto& events)
{
for (int e : events)
results.push_back(e);
}, Filter(filterFunc, Monitor(target)));
// Change the value a couple of times.
target.Set(10); // Change, but <= 10
target.Set(20); // Change
target.Set(20); // No change
target.Set(10); // Change, but <= 10
// Only 1 non-filtered change should go through.
EXPECT_EQ(results.size(), 1);
EXPECT_EQ(results[0], 20);
}
target.Set(100); // Change, >100, but observer is gone.
// No changes to results without the observer.
ASSERT_EQ(results.size(), 1);
}
TEST(AlgorithmTest, Snapshot)
{
Group g;
auto sv = StateVar::Create(g, 1);
auto es = EventSource<>::Create(g);
State st = Snapshot( sv, es );
int output = 0;
int turns = 0;
auto obs = Observer::Create([&] (int v)
{
++turns;
output = v;
}, st);
EXPECT_EQ(1, output);
EXPECT_EQ(1, turns);
sv.Set(10);
EXPECT_EQ(1, output);
EXPECT_EQ(1, turns);
es.Emit();
EXPECT_EQ(10, output);
EXPECT_EQ(2, turns);
}
TEST(AlgorithmTest, Pulse)
{
Group g;
auto sv = StateVar::Create(g, 1);
auto es = EventSource<>::Create(g);
Event st = Pulse( sv, es );
int output = 0;
int turns = 0;
auto obs = Observer::Create([&] (const auto& events)
{
for (int e : events)
{
++turns;
output += e;
}
}, st);
EXPECT_EQ(0, output);
EXPECT_EQ(0, turns);
sv.Set(10);
EXPECT_EQ(0, output);
EXPECT_EQ(0, turns);
es.Emit();
EXPECT_EQ(10, output);
EXPECT_EQ(1, turns);
}
TEST(AlgorithmTest, Iterate1)
{
Group g;
auto numSrc = EventSource::Create(g);
State numFold = Iterate(0, [] (const auto& events, int v)
{
for (int e : events)
v += e;
return v;
}, numSrc);
for (int i=1; i<=100; ++i)
numSrc << i;
int output = 0;
auto obs = Observer::Create([&] (int v) { output = v; }, numFold);
EXPECT_EQ(output, 5050);
}
TEST(AlgorithmTest, Iterate2)
{
Group g;
auto charSrc = EventSource::Create(g);
State<:string> strFold = Iterate<:string>(std::string(""), [] (const auto& events, std::string s)
{
for (char c : events)
s += c;
return s;
}, charSrc);
std::string output;
auto obs = Observer::Create([&] (const auto& v) { output = v; }, strFold);
charSrc << 'T' << 'e' << 's' << 't';
EXPECT_EQ(output, "Test");
}
TEST(AlgorithmTest, Iterate3)
{
Group g;
auto numSrc = EventSource::Create(g);
State numFold = Iterate(0, [] (const auto& events, int v)
{
for (int e : events)
v += e;
return v;
}, numSrc);
int turns = 0;
int output = 0;
auto obs = Observer::Create([&] (const auto& v)
{
++turns;
output = v;
}, numFold);
g.DoTransaction([&]
{
for (int i=1; i<=100; i++)
numSrc << i;
});
EXPECT_EQ(turns, 2);
EXPECT_EQ(output, 5050);
}
template
struct Incrementer
{
T operator()(const EventValueList& events, T v) const
{
for (auto e : events)
++v;
return v;
}
};
template
struct Decrementer
{
T operator()(const EventValueList& events, T v) const
{
for (auto e : events)
--v;
return v;
}
};
TEST(AlgorithmTest, Iterate4)
{
Group g;
auto trigger = EventSource<>::Create(g);
{
State inc = Iterate(0, Incrementer{ }, trigger);
for (int i=1; i<=100; i++)
trigger.Emit();
int output = 0;
auto obs = Observer::Create([&] (int v) { output = v; }, inc);
EXPECT_EQ(output, 100);
}
{
State dec = Iterate(200, Decrementer{ }, trigger);
for (int i=1; i<=100; i++)
trigger.Emit();
int output = 0;
auto obs = Observer::Create([&] (int v) { output = v; }, dec);
ASSERT_EQ(output, 100);
}
}
TEST(AlgorithmTest, IterateByRef1)
{
Group g;
auto src = EventSource::Create(g);
auto x = IterateByRef<:vector>>(std::vector{ }, [] (const auto& events, auto& v)
{
for (int e : events)
v.push_back(e);
}, src);
std::vector output;
auto obs = Observer::Create([&] (const auto& v) { output = v; }, x);
// Push
for (int i=1; i<=100; i++)
src << i;
EXPECT_EQ(output.size(), 100);
// Check
for (int i=1; i<=100; i++)
EXPECT_EQ(output[i-1], i);
}
TEST(AlgorithmTest, IterateByRef2)
{
Group g;
auto src = EventSource<>::Create(g);
auto x = IterateByRef<:vector>>(std::vector{ }, [] (const auto& events, std::vector& v)
{
for (Token e : events)
v.push_back(123);
}, src);
std::vector output;
auto obs = Observer::Create([&] (const auto& v) { output = v; }, x);
// Push
for (auto i=0; i<100; i++)
src.Emit();
EXPECT_EQ(output.size(), 100);
// Check
for (auto i=0; i<100; i++)
EXPECT_EQ(output[i], 123);
}
namespace {
template
T Sum(T a, T b) { return a + b; }
template
T Prod(T a, T b) { return a * b; }
template
T Diff(T a, T b) { return a - b; }
} //~namespace
TEST(AlgorithmTest, TransformWithState)
{
Group g;
auto in1 = StateVar::Create(g);
auto in2 = StateVar::Create(g);
auto sum = State::Create(Sum, in1, in2);
auto prod = State::Create(Prod, in1, in2);
auto diff = State::Create(Diff, in1, in2);
auto src1 = EventSource<>::Create(g);
auto src2 = EventSource::Create(g);
auto out1 = Transform<:tuple int>>([] (Token, int sum, int prod, int diff)
{
return std::make_tuple(sum, prod, diff);
}, src1, sum, prod, diff);
auto out2 = Transform<:tuple int>>([] (int e, int sum, int prod, int diff)
{
return std::make_tuple(e, sum, prod, diff);
}, src2, sum, prod, diff);
int turns1 = 0;
int turns2 = 0;
{
std::tuple output1;
auto obs1 = Observer::Create([&] (const auto& events)
{
for (const auto& e : events)
{
++turns1;
output1 = e;
}
}, out1);
std::tuple output2;
auto obs2 = Observer::Create([&] (const auto& events)
{
for (const auto& e : events)
{
++turns2;
output2 = e;
}
}, out2);
in1.Set(22);
in2.Set(11);
src1.Emit();
src2.Emit(42);
EXPECT_EQ(std::get<0>(output1), 33);
EXPECT_EQ(std::get<1>(output1), 242);
EXPECT_EQ(std::get<2>(output1), 11);
EXPECT_EQ(std::get<0>(output2), 42);
EXPECT_EQ(std::get<1>(output2), 33);
EXPECT_EQ(std::get<2>(output2), 242);
EXPECT_EQ(std::get<3>(output2), 11);
EXPECT_EQ(turns1, 1);
EXPECT_EQ(turns2, 1);
}
{
std::tuple output1;
auto obs1 = Observer::Create([&] (const auto& events)
{
for (const auto& e : events)
{
++turns1;
output1 = e;
}
}, out1);
std::tuple output2;
auto obs2 = Observer::Create([&] (const auto& events)
{
for (const auto& e : events)
{
++turns2;
output2 = e;
}
}, out2);
in1.Set(220);
in2.Set(110);
src1.Emit();
src2.Emit(420);
EXPECT_EQ(std::get<0>(output1), 330);
EXPECT_EQ(std::get<1>(output1), 24200);
EXPECT_EQ(std::get<2>(output1), 110);
EXPECT_EQ(std::get<0>(output2), 420);
EXPECT_EQ(std::get<1>(output2), 330);
EXPECT_EQ(std::get<2>(output2), 24200);
EXPECT_EQ(std::get<3>(output2), 110);
EXPECT_EQ(turns1, 2);
EXPECT_EQ(turns2, 2);
}
}
TEST(AlgorithmTest, IterateWithState)
{
Group g;
auto in1 = StateVar::Create(g);
auto in2 = StateVar::Create(g);
auto op1 = State::Create(Sum, in1, in2);
auto op2 = State::Create([] (int a, int b) { return (a + b) * 10; }, in1, in2);
auto src1 = EventSource<>::Create(g);
auto src2 = EventSource::Create(g);
auto out1 = Iterate<:tuple int>>(std::make_tuple(0, 0), [] (const auto& events, std::tuple t, int op1, int op2)
{
for (const auto& e : events)
t = std::make_tuple(std::get<0>(t) + op1, std::get<1>(t) + op2);
return t;
}, src1, op1, op2);
auto out2 = Iterate<:tuple int>>(std::make_tuple(0, 0, 0), [] (const auto& events, std::tuple t, int op1, int op2)
{
for (const auto& e : events)
t = std::make_tuple(std::get<0>(t) + e, std::get<1>(t) + op1, std::get<2>(t) + op2);
return t;
}, src2, op1, op2);
int turns1 = 0;
int turns2 = 0;
{
std::tuple output1;
auto obs1 = Observer::Create([&] (const auto& v)
{
++turns1;
output1 = v;
}, out1);
std::tuple output2;
auto obs2 = Observer::Create([&] (const auto& v)
{
++turns2;
output2 = v;
}, out2);
in1.Set(22);
in2.Set(11);
src1.Emit();
src2.Emit(42);
EXPECT_EQ(std::get<0>(output1), 33);
EXPECT_EQ(std::get<1>(output1), 330);
EXPECT_EQ(std::get<0>(output2), 42);
EXPECT_EQ(std::get<1>(output2), 33);
EXPECT_EQ(std::get<2>(output2), 330);
EXPECT_EQ(turns1, 2);
EXPECT_EQ(turns2, 2);
}
{
std::tuple output1;
auto obs1 = Observer::Create([&] (const auto& v)
{
++turns1;
output1 = v;
}, out1);
std::tuple output2;
auto obs2 = Observer::Create([&] (const auto& v)
{
++turns2;
output2 = v;
}, out2);
in1.Set(220);
in2.Set(110);
src1.Emit();
src2.Emit(420);
EXPECT_EQ(std::get<0>(output1), 33 + 330);
EXPECT_EQ(std::get<1>(output1), 330 + 3300);
EXPECT_EQ(std::get<0>(output2), 42 + 420);
EXPECT_EQ(std::get<1>(output2), 33 + 330);
EXPECT_EQ(std::get<2>(output2), 330 + 3300);
EXPECT_EQ(turns1, 4);
EXPECT_EQ(turns2, 4);
}
}
TEST(AlgorithmTest, IterateByRefWithState)
{
Group g;
auto in1 = StateVar::Create(g);
auto in2 = StateVar::Create(g);
auto op1 = State::Create(Sum, in1, in2);
auto op2 = State::Create([] (int a, int b) { return (a + b) * 10; }, in1, in2);
auto src1 = EventSource<>::Create(g);
auto src2 = EventSource::Create(g);
auto out1 = IterateByRef<:vector>>(std::vector{ }, [] (const auto& events, std::vector& v, int op1, int op2)
{
for (const auto& e : events)
{
v.push_back(op1);
v.push_back(op2);
}
}, src1, op1, op2);
auto out2 = IterateByRef<:vector>>(std::vector{ }, [] (const auto& events, std::vector& v, int op1, int op2)
{
for (const auto& e : events)
{
v.push_back(e);
v.push_back(op1);
v.push_back(op2);
}
}, src2, op1, op2);
int turns1 = 0;
int turns2 = 0;
{
std::vector output1;
auto obs1 = Observer::Create([&] (const std::vector& v)
{
++turns1;
output1 = v;
}, out1);
std::vector output2;
auto obs2 = Observer::Create([&] (const std::vector& v)
{
++turns2;
output2 = v;
}, out2);
in1.Set(22);
in2.Set(11);
src1.Emit();
src2.Emit(42);
EXPECT_EQ(output1.size(), 2);
EXPECT_EQ(output1[0], 33);
EXPECT_EQ(output1[1], 330);
EXPECT_EQ(output2.size(), 3);
EXPECT_EQ(output2[0], 42);
EXPECT_EQ(output2[1], 33);
EXPECT_EQ(output2[2], 330);
EXPECT_EQ(turns1, 2);
EXPECT_EQ(turns2, 2);
}
{
std::vector output1;
auto obs1 = Observer::Create([&] (const std::vector& v)
{
++turns1;
output1 = v;
}, out1);
std::vector output2;
auto obs2 = Observer::Create([&] (const std::vector& v)
{
++turns2;
output2 = v;
}, out2);
in1.Set(220);
in2.Set(110);
src1.Emit();
src2.Emit(420);
EXPECT_EQ(output1.size(), 4);
EXPECT_EQ(output1[0], 33);
EXPECT_EQ(output1[1], 330);
EXPECT_EQ(output1[2], 330);
EXPECT_EQ(output1[3], 3300);
EXPECT_EQ(output2.size(), 6);
EXPECT_EQ(output2[0], 42);
EXPECT_EQ(output2[1], 33);
EXPECT_EQ(output2[2], 330);
EXPECT_EQ(output2[3], 420);
EXPECT_EQ(output2[4], 330);
EXPECT_EQ(output2[5], 3300);
EXPECT_EQ(turns1, 4);
EXPECT_EQ(turns2, 4);
}
}
Group flattenGroup;
class FlattenDummy
{
public:
StateVar value1 = StateVar::Create(flattenGroup, 10);
StateVar value2 = StateVar::Create(flattenGroup, 20);
bool operator==(const FlattenDummy& other) const
{ return value1 == other.value1 && value2 == other.value2; }
struct Flat;
};
struct FlattenDummy::Flat : public Flattened
{
using Flattened::Flattened;
Ref value1 = this->Flatten(FlattenDummy::value1);
Ref value2 = this->Flatten(FlattenDummy::value2);
};
TEST(AlgorithmTest, FlattenObject1)
{
Group g;
FlattenDummy o1;
FlattenDummy o2;
auto outer = StateVar::Create(flattenGroup, o1);
auto flat = FlattenObject(outer);
int turns = 0;
int output1 = 0;
int output2 = 0;
auto obs = Observer::Create([&] (const FlattenDummy::Flat& v)
{
++turns;
output1 = v.value1;
output2 = v.value2;
}, flat);
EXPECT_EQ(turns, 1);
EXPECT_EQ(output1, 10);
EXPECT_EQ(output2, 20);
o1.value1.Set(30);
o1.value2.Set(40);
EXPECT_EQ(turns, 3);
EXPECT_EQ(output1, 30);
EXPECT_EQ(output2, 40);
outer.Set(o2);
EXPECT_EQ(turns, 4);
EXPECT_EQ(output1, 10);
EXPECT_EQ(output2, 20);
o1.value1.Set(300);
o1.value2.Set(400);
o2.value1.Set(500);
o2.value2.Set(600);
EXPECT_EQ(turns, 6);
EXPECT_EQ(output1, 500);
EXPECT_EQ(output2, 600);
}