Loading [MathJax]/jax/output/HTML-CSS/config.js
Radium Engine  1.5.29
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
observer.cpp
1#include <Core/Utils/Observable.hpp>
2#include <catch2/catch_test_macros.hpp>
3
5
6// This class can notify observers with no args
7class ObservableVoid : public Observable<>
8{};
9
10// This class can notify observers with a int
11class ObservableInt : public Observable<int>
12{};
13
14// This class have two observable "hooks"
15class PeterPan
16{
17
18 public:
19 void setName( std::string arg ) {
20 m_name = arg;
21 m_hookString.notify( m_name );
22 m_hook.notify();
23 }
24 void setBestFriendNameAndAge( std::string name, int age ) {
25 m_best = name;
26 m_bestAge = age;
27 m_hookIntString.notify( m_bestAge, m_best );
28 m_hook.notify();
29 }
30 Observable<>& getHook() { return m_hook; }
31 Observable<std::string>& getHookName() { return m_hookString; }
32 Observable<int, std::string>& getHookBest() { return m_hookIntString; }
33
34 private:
35 Observable<> m_hook;
36 Observable<std::string> m_hookString;
37 Observable<int, std::string> m_hookIntString;
38 std::string m_name {};
39 std::string m_best {};
40 int m_bestAge;
41};
42
43class Spy
44{
45 public:
46 void spyPeter( PeterPan& p ) {
47 p.getHook().attach( [this]() { m_cpt++; } );
48 p.getHookName().attachMember( this, &Spy::addName );
49 p.getHookBest().attachMember( this, &Spy::addBest );
50 }
51
52 private:
53 void addName( std::string n ) { m_hisName.push_back( n ); }
54 void addBest( int a, std::string n ) { m_hisBests.emplace_back( n, a ); }
55
56 // public to allow easy tests
57 public:
60 int m_cpt { 0 };
61};
62
63class A
64{
65 public:
66 void f() { m_a++; }
67 void f2( int a ) { m_a = a; }
68 void f3( int a, int b ) {
69 m_a = a;
70 m_b = b;
71 }
72 static void g() { m_b++; }
73 static void g2( int b ) { m_b = b; }
74
75 int m_a { 0 };
76 static int m_b;
77};
78
79int A::m_b = 0;
80
81TEST_CASE( "Core/Utils/Observable", "[unittests][Core][Core/Utils][Observable]" ) {
82 ObservableVoid observableVoid;
83 ObservableInt observableInt;
84
85 A a;
86 int c = 0;
87 A::m_b = 0;
88
89 SECTION( "attach member to hook" ) {
90 // this one is just to check it compiles ... we can add some REQUIREs in a near futur.
91 Observable<> hookVoid;
92 Observable<int> hookInt;
93 Observable<int, int> hookInt2;
94
95 hookVoid.attachMember( &a, &A::f );
96 hookInt.attachMember( &a, &A::f2 );
97 hookInt2.attachMember( &a, &A::f3 );
98 hookVoid.notify();
99 hookInt.notify( 7 );
100 }
101
102 SECTION( "Spy Peter Pan" ) {
103 PeterPan peter;
104 Spy badGuy;
105 badGuy.spyPeter( peter );
106
107 peter.setName( "PeterPan !" );
108 REQUIRE( "PeterPan !" == badGuy.m_hisName.back() );
109 peter.setBestFriendNameAndAge( "Tinker Bell", 150 );
110 peter.setBestFriendNameAndAge( "John", 11 );
111 peter.setBestFriendNameAndAge( "Michael", 8 );
112 peter.setName( "PeterPan" );
113
114 REQUIRE( "PeterPan" == badGuy.m_hisName.back() );
115 peter.setBestFriendNameAndAge( "Wendy", 12 );
116 peter.setName( "Mr. Pan" );
117
118 REQUIRE( "Mr. Pan" == badGuy.m_hisName.back() );
119 peter.setBestFriendNameAndAge( "Wendy", 18 );
120
121 REQUIRE( badGuy.m_hisBests.size() == 5 );
122 }
123
124 SECTION( "observe me" ) {
125 using Observer = std::function<void( void )>;
127 // using Observer2 = std::function<void( int )>;
128
129 auto bf = std::bind( &A::f, &a );
130 std::bind( &A::f2, &a, std::placeholders::_1 );
131
132 Observer obf = bf;
133 auto& observerTarget = obf.target_type();
134 // "Type failed."
135 REQUIRE( obf.target_type() == observerTarget );
136
137 auto gid = observableVoid.attach( A::g );
138 observableVoid.attach( bf );
139 observableVoid.attach( [&c]() { c++; } );
140 observableVoid.notify();
141
142 REQUIRE( c == 1 );
143 REQUIRE( a.m_a == 1 );
144 REQUIRE( A::m_b == 1 );
145
146 observableVoid.detach( gid );
147 observableVoid.notify();
148
149 REQUIRE( c == 2 );
150 REQUIRE( a.m_a == 2 );
151 // Test detach
152 REQUIRE( A::m_b == 1 );
153
154 observableVoid.attach( A::g );
155 observableVoid.attach( A::g );
156 observableVoid.attach( bf );
157 observableVoid.attach( [&c]() { c++; } );
158 observableVoid.notify();
159
160 REQUIRE( c == 4 );
161 REQUIRE( a.m_a == 4 );
162 REQUIRE( A::m_b == 3 );
163
164 observableVoid.detachAll();
165 observableVoid.notify();
166
167 // Test detach
168 REQUIRE( c == 4 );
169 REQUIRE( a.m_a == 4 );
170 REQUIRE( A::m_b == 3 );
171
172 observableInt.attachMember( &a, &A::f2 );
173 observableInt.attach( &A::g2 );
174 observableInt.attach( [&c]( int pc ) { c = pc; } );
175
176 observableInt.notify( 5 );
177
178 REQUIRE( c == 5 );
179 REQUIRE( a.m_a == 5 );
180 REQUIRE( A::m_b == 5 );
181
182 observableInt.notify( 6 );
183
184 REQUIRE( c == 6 );
185 REQUIRE( a.m_a == 6 );
186 REQUIRE( A::m_b == 6 );
187
188 ObservableInt test2copy;
189 observableInt.copyObserversTo( test2copy );
190
191 observableInt.detachAll();
192 observableInt.notify( 7 );
193
194 REQUIRE( c == 6 );
195 REQUIRE( a.m_a == 6 );
196 REQUIRE( A::m_b == 6 );
197
198 test2copy.notify( 7 );
199
200 REQUIRE( c == 7 );
201 REQUIRE( a.m_a == 7 );
202 REQUIRE( A::m_b == 7 );
203 }
204}
T back(T... args)
T bind(T... args)
int attach(Observer observer)
void detachAll()
Detach all observers.
void notify(Args... p) const
Notify (i.e. call) each attached observer with argument p.
void detach(int observerId)
int attachMember(T *object, void(T::*observer)(Args...))
void copyObserversTo(Observable &other) const
explicit copy of all attached observers the other Observable
T emplace_back(T... args)
T push_back(T... args)
T size(T... args)