sigproxy: Modernize RPC calls (remote signals)
[quassel.git] / tests / common / signalproxytest.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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     void requestMethod(const QString& stringArg, int intArg)
224     {
225         REQUEST(ARG(stringArg), ARG(intArg));
226         REQUEST_OTHER(setStringProperty, ARG(stringArg));
227         emit requestMethodCalled(stringArg, intArg);
228     }
229
230 signals:
231     void intPropertyChanged(int);
232     void stringPropertyChanged(const QString&);
233     void fooDataChanged(const QByteArray&);
234     void syncMethodCalled(int, const QString&);
235     void requestMethodCalled(const QString&, int);
236
237 private:
238     int _intProperty{};
239     QString _stringProperty;
240     double _doubleProperty{};
241     QByteArray _fooData{"FOO"};
242     int _syncedInt{};
243     QString _syncedString;
244 };
245
246 TEST_F(SignalProxyTest, syncableObject)
247 {
248     {
249         InSequence s;
250
251         // Synchronize
252         EXPECT_CALL(*_clientPeer, Dispatches(InitRequest(Eq("SyncObj"), Eq("Foo"))));
253         EXPECT_CALL(*_serverPeer, Dispatches(InitData(Eq("SyncObj"), Eq("Foo"), QVariantMap{
254                                                           {"stringProperty", "Hello"},
255                                                           {"intProperty", 42},
256                                                           {"doubleProperty", 4.2},
257                                                           {"FooData", "FOO"}
258                                                       })));
259
260         // Set int property
261         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(23))));
262
263         // Sync method
264         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("syncMethod"), ElementsAre(42, "World"))));
265         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(42))));
266         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("World"))));
267
268         // Request method
269         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestMethod"), ElementsAre("Hello", 23))));
270         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("Hello"))));
271
272         // Update properties (twice)
273         EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestUpdate"), ElementsAre(QVariantMap{
274                                                              {"stringProperty", "Quassel"},
275                                                              {"intProperty", 17},
276                                                              {"doubleProperty", 2.3}
277                                                          })))).Times(2);
278         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(17))));
279         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("Quassel"))));
280         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("update"), ElementsAre(QVariantMap{
281                                                              {"stringProperty", "Quassel"},
282                                                              {"intProperty", 17},
283                                                              {"doubleProperty", 2.3}
284                                                          }))));
285
286         // Rename object
287         EXPECT_CALL(*_serverPeer, Dispatches(RpcCall(Eq("__objectRenamed__"), ElementsAre("SyncObj", "Bar", "Foo"))));
288         EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Bar"), Eq("setStringProperty"), ElementsAre("Hi Universe"))));
289     }
290
291     SignalSpy spy;
292
293     SyncObj clientObject;
294     SyncObj serverObject;
295
296     // -- Set initial data
297
298     serverObject.setIntProperty(42);
299     serverObject.setStringProperty("Hello");
300     serverObject.setDoubleProperty(4.2);
301     serverObject.setObjectName("Foo");
302     clientObject.setObjectName("Foo");
303
304     // -- Synchronize
305
306     spy.connect(&serverObject, &SyncableObject::initDone);
307     _serverProxy.synchronize(&serverObject);
308     ASSERT_TRUE(spy.wait());
309     spy.connect(&clientObject, &SyncableObject::initDone);
310     _clientProxy.synchronize(&clientObject);
311     ASSERT_TRUE(spy.wait());
312
313     // -- Check if client-side values are as expected
314
315     EXPECT_EQ(42, clientObject.intProperty());
316     EXPECT_EQ("Hello", clientObject.stringProperty());
317     EXPECT_EQ(4.2, clientObject.doubleProperty());
318     EXPECT_EQ("FOO", clientObject.initFooData());
319
320     // -- Set int property
321     spy.connect(&clientObject, &SyncObj::intPropertyChanged);
322     serverObject.setIntProperty(23);
323     ASSERT_TRUE(spy.wait());
324     EXPECT_EQ(23, clientObject.intProperty());
325
326     // -- Sync method
327
328     spy.connect(&clientObject, &SyncObj::syncMethodCalled);
329     serverObject.syncMethod(42, "World");
330     ASSERT_TRUE(spy.wait());
331     EXPECT_EQ(42, clientObject.syncedInt());
332     EXPECT_EQ(42, clientObject.intProperty());
333     EXPECT_EQ("World", clientObject.syncedString());
334     EXPECT_EQ("World", clientObject.stringProperty());
335
336     // -- Request method
337
338     spy.connect(&serverObject, &SyncObj::requestMethodCalled);
339     clientObject.requestMethod("Hello", 23);
340     ASSERT_TRUE(spy.wait());
341     EXPECT_EQ("Hello", serverObject.stringProperty());
342
343     // -- Property update
344
345     QVariantMap propMap{{"intProperty", 17}, {"stringProperty", "Quassel"}, {"doubleProperty", 2.3}};
346     spy.connect(&serverObject, &SyncableObject::updatedRemotely);
347     clientObject.requestUpdate(propMap);
348     ASSERT_TRUE(spy.wait());
349     // We don't allow client updates yet, so the values shouldn't have changed
350     EXPECT_EQ(23, serverObject.intProperty());
351     EXPECT_EQ("Hello", serverObject.stringProperty());
352     EXPECT_EQ(4.2, serverObject.doubleProperty());
353     // Allow client updates, try again
354     serverObject.setAllowClientUpdates(true);
355     spy.connect(&clientObject, &SyncableObject::updated);
356     clientObject.requestUpdate(propMap);
357     ASSERT_TRUE(spy.wait());
358     EXPECT_EQ(17, serverObject.intProperty());
359     EXPECT_EQ("Quassel", serverObject.stringProperty());
360     EXPECT_EQ(2.3, serverObject.doubleProperty());
361     EXPECT_EQ(17, clientObject.intProperty());
362     EXPECT_EQ("Quassel", clientObject.stringProperty());
363     EXPECT_EQ(2.3, clientObject.doubleProperty());
364
365     // -- Rename object
366     spy.connect(&clientObject, &SyncObj::stringPropertyChanged);
367     serverObject.setObjectName("Bar");
368     serverObject.setStringProperty("Hi Universe");
369     ASSERT_TRUE(spy.wait());
370     EXPECT_EQ("Bar", clientObject.objectName());
371     EXPECT_EQ("Hi Universe", clientObject.stringProperty());
372 }
373
374 #include "signalproxytest.moc"