src: Yearly copyright bump
[quassel.git] / tests / common / signalproxytest.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2020 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "signalproxy.h"
22
23 #include <utility>
24
25 #include <QByteArray>
26 #include <QTest>
27
28 #include "invocationspy.h"
29 #include "mockedpeer.h"
30 #include "syncableobject.h"
31 #include "testglobal.h"
32
33 using namespace ::testing;
34 using namespace test;
35
36 class SignalProxyTest : public QObject, public ::testing::Test
37 {
38     Q_OBJECT
39
40 public:
41     void SetUp() override
42     {
43         // Set up peers and connect the signal proxies so they're ready to use
44         _clientPeer->setPeer(_serverPeer);
45         _serverPeer->setPeer(_clientPeer);
46         _clientProxy.addPeer(_clientPeer);
47         _serverProxy.addPeer(_serverPeer);
48     }
49
50 protected:
51     SignalProxy _clientProxy{SignalProxy::ProxyMode::Client, this};
52     SignalProxy _serverProxy{SignalProxy::ProxyMode::Server, this};
53     MockedPeer* _clientPeer{new MockedPeer{this}};
54     MockedPeer* _serverPeer{new MockedPeer{this}};
55 };
56
57 // -----------------------------------------------------------------------------------------------------------------------------------------
58
59 // Object for testing attached signals
60 class ProxyObject : public QObject
61 {
62     Q_OBJECT
63
64 public:
65     using Data = std::pair<int, QString>;
66     using Spy = ValueSpy<Data>;
67
68     ProxyObject(Spy* spy, Peer* expectedPeer)
69         : _spy{spy}
70         , _expectedPeer{expectedPeer}
71     {}
72
73 signals:
74     void sendData(int, const QString&);
75     void sendMoreData(int, const QString&);
76     void sendToFunctor(int, const QString&);
77
78 public slots:
79     void receiveData(int i, const QString& s)
80     {
81         EXPECT_EQ(_expectedPeer, SignalProxy::current()->sourcePeer());
82         _spy->notify(std::make_pair(i, s));
83     }
84
85     void receiveExtraData(int i, const QString& s)
86     {
87         EXPECT_EQ(_expectedPeer, SignalProxy::current()->sourcePeer());
88         _spy->notify(std::make_pair(-i, s.toUpper()));
89     }
90
91 private:
92     Spy* _spy;
93     Peer* _expectedPeer;
94 };
95
96 TEST_F(SignalProxyTest, attachSignal)
97 {
98     {
99         InSequence s;
100         EXPECT_CALL(*_clientPeer, Dispatches(RpcCall(Eq(SIGNAL(sendData(int,QString))), ElementsAre(42, "Hello"))));
101         EXPECT_CALL(*_clientPeer, Dispatches(RpcCall(Eq(SIGNAL(sendExtraData(int,QString))), ElementsAre(42, "Hello"))));
102         EXPECT_CALL(*_serverPeer, Dispatches(RpcCall(Eq("2sendData(int,QString)"), ElementsAre(23, "World"))));
103         EXPECT_CALL(*_serverPeer, Dispatches(RpcCall(Eq("2sendToFunctor(int,QString)"), ElementsAre(17, "Hi Universe"))));
104     }
105
106     ProxyObject::Spy clientSpy, serverSpy;
107     ProxyObject clientObject{&clientSpy, _clientPeer};
108     ProxyObject serverObject{&serverSpy, _serverPeer};
109
110     // Deliberately not normalize some of the macro invocations
111     _clientProxy.attachSignal(&clientObject, &ProxyObject::sendData);
112     _serverProxy.attachSlot(SIGNAL(sendData(int,QString)), &serverObject, &ProxyObject::receiveData);
113
114     _clientProxy.attachSignal(&clientObject, &ProxyObject::sendMoreData, SIGNAL(sendExtraData(int, const QString&)));
115     _serverProxy.attachSlot(SIGNAL(sendExtraData(int, const QString&)), &serverObject, &ProxyObject::receiveExtraData);
116
117     _serverProxy.attachSignal(&serverObject, &ProxyObject::sendData);
118     //_clientProxy.attachSlot(SIGNAL(sendData(int, const QString&)), &clientObject, SLOT(receiveData(int,QString)));
119     _clientProxy.attachSlot(SIGNAL(sendData(int, const QString&)), &clientObject, &ProxyObject::receiveData);
120
121     _serverProxy.attachSignal(&serverObject, &ProxyObject::sendToFunctor);
122     _clientProxy.attachSlot(SIGNAL(sendToFunctor(int, const QString&)), this, [this, &clientSpy](int i, const QString& s) {
123         EXPECT_EQ(_clientPeer, SignalProxy::current()->sourcePeer());
124         clientSpy.notify(std::make_pair(i, s));
125     });
126
127     emit clientObject.sendData(42, "Hello");
128     ASSERT_TRUE(serverSpy.wait());
129     EXPECT_EQ(ProxyObject::Data(42, "Hello"), serverSpy.value());
130
131     emit clientObject.sendMoreData(42, "Hello");
132     ASSERT_TRUE(serverSpy.wait());
133     EXPECT_EQ(ProxyObject::Data(-42, "HELLO"), serverSpy.value());
134
135     emit serverObject.sendData(23, "World");
136     ASSERT_TRUE(clientSpy.wait());
137     EXPECT_EQ(ProxyObject::Data(23, "World"), clientSpy.value());
138
139     emit serverObject.sendToFunctor(17, "Hi Universe");
140     ASSERT_TRUE(clientSpy.wait());
141     EXPECT_EQ(ProxyObject::Data(17, "Hi Universe"), clientSpy.value());
142 }
143
144 // -----------------------------------------------------------------------------------------------------------------------------------------
145
146 class SyncObj : public SyncableObject
147 {
148     Q_OBJECT
149     SYNCABLE_OBJECT
150
151     Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intPropertyChanged)
152     Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringPropertyChanged)
153     Q_PROPERTY(double doubleProperty READ doubleProperty WRITE setDoubleProperty)
154
155 public:
156     int intProperty() const
157     {
158         return _intProperty;
159     }
160
161     QString stringProperty() const
162     {
163         return _stringProperty;
164     }
165
166     double doubleProperty() const
167     {
168         return _doubleProperty;
169     }
170
171     int syncedInt() const
172     {
173         return _syncedInt;
174     }
175
176     QString syncedString() const
177     {
178         return _syncedString;
179     }
180
181 public slots:
182     QByteArray initFooData() const
183     {
184         return _fooData;
185     }
186
187     void setInitFooData(const QByteArray& data)
188     {
189         _fooData = data;
190         emit fooDataChanged(data);
191     }
192
193     void setIntProperty(int value)
194     {
195         _intProperty = value;
196         SYNC(ARG(value));
197         emit intPropertyChanged(value);
198     }
199
200     void setStringProperty(const QString& value)
201     {
202         _stringProperty = value;
203         SYNC(ARG(value));
204         emit stringPropertyChanged(value);
205     }
206
207     // Deliberately no sync nor changed signal
208     void setDoubleProperty(double value)
209     {
210         _doubleProperty = value;
211     }
212
213     void syncMethod(int intArg, const QString& stringArg)
214     {
215         _syncedInt = intArg;
216         _syncedString = stringArg;
217         SYNC(ARG(intArg), ARG(stringArg));
218         SYNC_OTHER(setIntProperty, ARG(intArg));
219         SYNC_OTHER(setStringProperty, ARG(stringArg));
220         emit syncMethodCalled(intArg, stringArg);
221     }
222
223     int requestInt(const QString& stringArg, int intArg)
224     {
225         REQUEST(ARG(stringArg), ARG(intArg));
226         REQUEST_OTHER(setIntProperty, ARG(intArg));
227         emit requestMethodCalled(stringArg, intArg);
228         return -intArg;
229     }
230
231     QString requestString(const QString& stringArg, int intArg)
232     {
233         REQUEST(ARG(stringArg), ARG(intArg));
234         REQUEST_OTHER(setStringProperty, ARG(stringArg));
235         emit requestMethodCalled(stringArg, intArg);
236         return stringArg.toUpper();
237     }
238
239     // Receive methods can either have the full signature of the request method + return value, or...
240     void receiveInt(const QString&, int, int reply)
241     {
242         _intProperty = reply;
243         emit intReceived(reply);
244     }
245
246     // ... only the return value
247     void receiveString(const QString& reply)
248     {
249         _stringProperty = reply;
250         emit stringReceived(reply);
251     }
252
253
254 signals:
255     void intPropertyChanged(int);
256     void stringPropertyChanged(const QString&);
257     void fooDataChanged(const QByteArray&);
258     void syncMethodCalled(int, const QString&);
259     void requestMethodCalled(const QString&, int);
260     void intReceived(int);
261     void stringReceived(const QString&);
262
263 private:
264     int _intProperty{};
265     QString _stringProperty;
266     double _doubleProperty{};
267     QByteArray _fooData{"FOO"};
268     int _syncedInt{};
269     QString _syncedString;
270 };
271
272 TEST_F(SignalProxyTest, syncableObject)
273 {
274     {
275         InSequence s;
276
277         // Synchronize
278         EXPECT_CALL(*_clientPeer, Dispatches(InitRequest(Eq("SyncObj"), Eq("Foo"))));
279         EXPECT_CALL(*_serverPeer, Dispatches(InitData(Eq("SyncObj"), Eq("Foo"), QVariantMap{
280                                                           {"stringProperty", "Hello"},
281                                                           {"intProperty", 42},
282                                                           {"doubleProperty", 4.2},
283                                                           {"FooData", "FOO"}
284                                                       })));
285
286         // Set int property
287         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(23))));
288
289         // Sync method
290         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("syncMethod"), ElementsAre(42, "World"))));
291         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(42))));
292         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("World"))));
293
294         // Request method
295         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestInt"), ElementsAre("Hello", 23))));
296         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("receiveInt"), ElementsAre("Hello", 23, -23))));
297         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(23))));
298
299         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestString"), ElementsAre("Hello", 23))));
300         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("receiveString"), ElementsAre("HELLO"))));
301         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("Hello"))));
302
303         // Update properties (twice)
304         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestUpdate"), ElementsAre(QVariantMap{
305                                                              {"stringProperty", "Quassel"},
306                                                              {"intProperty", 17},
307                                                              {"doubleProperty", 2.3}
308                                                          })))).Times(2);
309         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(17))));
310         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("Quassel"))));
311         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("update"), ElementsAre(QVariantMap{
312                                                              {"stringProperty", "Quassel"},
313                                                              {"intProperty", 17},
314                                                              {"doubleProperty", 2.3}
315                                                          }))));
316
317         // Rename object
318         EXPECT_CALL(*_serverPeer, Dispatches(RpcCall(Eq("__objectRenamed__"), ElementsAre("SyncObj", "Bar", "Foo"))));
319         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Bar"), Eq("setStringProperty"), ElementsAre("Hi Universe"))));
320     }
321
322     SignalSpy spy;
323
324     SyncObj clientObject;
325     SyncObj serverObject;
326
327     // -- Set initial data
328
329     serverObject.setIntProperty(42);
330     serverObject.setStringProperty("Hello");
331     serverObject.setDoubleProperty(4.2);
332     serverObject.setObjectName("Foo");
333     clientObject.setObjectName("Foo");
334
335     // -- Synchronize
336
337     spy.connect(&serverObject, &SyncableObject::initDone);
338     _serverProxy.synchronize(&serverObject);
339     ASSERT_TRUE(spy.wait());
340     spy.connect(&clientObject, &SyncableObject::initDone);
341     _clientProxy.synchronize(&clientObject);
342     ASSERT_TRUE(spy.wait());
343
344     // -- Check if client-side values are as expected
345
346     EXPECT_EQ(42, clientObject.intProperty());
347     EXPECT_EQ("Hello", clientObject.stringProperty());
348     EXPECT_EQ(4.2, clientObject.doubleProperty());
349     EXPECT_EQ("FOO", clientObject.initFooData());
350
351     // -- Set int property
352     spy.connect(&clientObject, &SyncObj::intPropertyChanged);
353     serverObject.setIntProperty(23);
354     ASSERT_TRUE(spy.wait());
355     EXPECT_EQ(23, clientObject.intProperty());
356
357     // -- Sync method
358
359     spy.connect(&clientObject, &SyncObj::syncMethodCalled);
360     serverObject.syncMethod(42, "World");
361     ASSERT_TRUE(spy.wait());
362     EXPECT_EQ(42, clientObject.syncedInt());
363     EXPECT_EQ(42, clientObject.intProperty());
364     EXPECT_EQ("World", clientObject.syncedString());
365     EXPECT_EQ("World", clientObject.stringProperty());
366
367     // -- Request method
368
369     spy.connect(&clientObject, &SyncObj::intReceived);
370     clientObject.requestInt("Hello", 23);
371     ASSERT_TRUE(spy.wait());
372     EXPECT_EQ(23, serverObject.intProperty());
373     EXPECT_EQ(-23, clientObject.intProperty());
374
375     spy.connect(&clientObject, &SyncObj::stringReceived);
376     clientObject.requestString("Hello", 23);
377     ASSERT_TRUE(spy.wait());
378     EXPECT_EQ("Hello", serverObject.stringProperty());
379     EXPECT_EQ("HELLO", clientObject.stringProperty());
380
381     // -- Property update
382
383     QVariantMap propMap{{"intProperty", 17}, {"stringProperty", "Quassel"}, {"doubleProperty", 2.3}};
384     spy.connect(&serverObject, &SyncableObject::updatedRemotely);
385     clientObject.requestUpdate(propMap);
386     ASSERT_TRUE(spy.wait());
387     // We don't allow client updates yet, so the values shouldn't have changed
388     EXPECT_EQ(23, serverObject.intProperty());
389     EXPECT_EQ("Hello", serverObject.stringProperty());
390     EXPECT_EQ(4.2, serverObject.doubleProperty());
391     // Allow client updates, try again
392     serverObject.setAllowClientUpdates(true);
393     spy.connect(&clientObject, &SyncableObject::updated);
394     clientObject.requestUpdate(propMap);
395     ASSERT_TRUE(spy.wait());
396     EXPECT_EQ(17, serverObject.intProperty());
397     EXPECT_EQ("Quassel", serverObject.stringProperty());
398     EXPECT_EQ(2.3, serverObject.doubleProperty());
399     EXPECT_EQ(17, clientObject.intProperty());
400     EXPECT_EQ("Quassel", clientObject.stringProperty());
401     EXPECT_EQ(2.3, clientObject.doubleProperty());
402
403     // -- Rename object
404     spy.connect(&clientObject, &SyncObj::stringPropertyChanged);
405     serverObject.setObjectName("Bar");
406     serverObject.setStringProperty("Hi Universe");
407     ASSERT_TRUE(spy.wait());
408     EXPECT_EQ("Bar", clientObject.objectName());
409     EXPECT_EQ("Hi Universe", clientObject.stringProperty());
410 }
411
412 #include "signalproxytest.moc"