1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "signalproxy.h"
28 #include "invocationspy.h"
29 #include "mockedpeer.h"
30 #include "syncableobject.h"
31 #include "testglobal.h"
33 using namespace ::testing;
36 class SignalProxyTest : public QObject, public ::testing::Test
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);
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}};
57 // -----------------------------------------------------------------------------------------------------------------------------------------
59 // Object for testing attached signals
60 class ProxyObject : public QObject
65 using Data = std::pair<int, QString>;
66 using Spy = ValueSpy<Data>;
68 ProxyObject(Spy* spy, Peer* expectedPeer)
70 , _expectedPeer{expectedPeer}
74 void sendData(int, const QString&);
75 void sendMoreData(int, const QString&);
76 void sendToFunctor(int, const QString&);
79 void receiveData(int i, const QString& s)
81 EXPECT_EQ(_expectedPeer, SignalProxy::current()->sourcePeer());
82 _spy->notify(std::make_pair(i, s));
85 void receiveExtraData(int i, const QString& s)
87 EXPECT_EQ(_expectedPeer, SignalProxy::current()->sourcePeer());
88 _spy->notify(std::make_pair(-i, s.toUpper()));
96 TEST_F(SignalProxyTest, attachSignal)
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"))));
106 ProxyObject::Spy clientSpy, serverSpy;
107 ProxyObject clientObject{&clientSpy, _clientPeer};
108 ProxyObject serverObject{&serverSpy, _serverPeer};
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);
114 _clientProxy.attachSignal(&clientObject, &ProxyObject::sendMoreData, SIGNAL(sendExtraData(int, const QString&)));
115 _serverProxy.attachSlot(SIGNAL(sendExtraData(int, const QString&)), &serverObject, &ProxyObject::receiveExtraData);
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);
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));
127 emit clientObject.sendData(42, "Hello");
128 ASSERT_TRUE(serverSpy.wait());
129 EXPECT_EQ(ProxyObject::Data(42, "Hello"), serverSpy.value());
131 emit clientObject.sendMoreData(42, "Hello");
132 ASSERT_TRUE(serverSpy.wait());
133 EXPECT_EQ(ProxyObject::Data(-42, "HELLO"), serverSpy.value());
135 emit serverObject.sendData(23, "World");
136 ASSERT_TRUE(clientSpy.wait());
137 EXPECT_EQ(ProxyObject::Data(23, "World"), clientSpy.value());
139 emit serverObject.sendToFunctor(17, "Hi Universe");
140 ASSERT_TRUE(clientSpy.wait());
141 EXPECT_EQ(ProxyObject::Data(17, "Hi Universe"), clientSpy.value());
144 // -----------------------------------------------------------------------------------------------------------------------------------------
146 class SyncObj : public SyncableObject
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)
156 int intProperty() const
161 QString stringProperty() const
163 return _stringProperty;
166 double doubleProperty() const
168 return _doubleProperty;
171 int syncedInt() const
176 QString syncedString() const
178 return _syncedString;
182 QByteArray initFooData() const
187 void setInitFooData(const QByteArray& data)
190 emit fooDataChanged(data);
193 void setIntProperty(int value)
195 _intProperty = value;
197 emit intPropertyChanged(value);
200 void setStringProperty(const QString& value)
202 _stringProperty = value;
204 emit stringPropertyChanged(value);
207 // Deliberately no sync nor changed signal
208 void setDoubleProperty(double value)
210 _doubleProperty = value;
213 void syncMethod(int intArg, const QString& stringArg)
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);
223 int requestInt(const QString& stringArg, int intArg)
225 REQUEST(ARG(stringArg), ARG(intArg));
226 REQUEST_OTHER(setIntProperty, ARG(intArg));
227 emit requestMethodCalled(stringArg, intArg);
231 QString requestString(const QString& stringArg, int intArg)
233 REQUEST(ARG(stringArg), ARG(intArg));
234 REQUEST_OTHER(setStringProperty, ARG(stringArg));
235 emit requestMethodCalled(stringArg, intArg);
236 return stringArg.toUpper();
239 // Receive methods can either have the full signature of the request method + return value, or...
240 void receiveInt(const QString&, int, int reply)
242 _intProperty = reply;
243 emit intReceived(reply);
246 // ... only the return value
247 void receiveString(const QString& reply)
249 _stringProperty = reply;
250 emit stringReceived(reply);
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&);
265 QString _stringProperty;
266 double _doubleProperty{};
267 QByteArray _fooData{"FOO"};
269 QString _syncedString;
272 TEST_F(SignalProxyTest, syncableObject)
278 EXPECT_CALL(*_clientPeer, Dispatches(InitRequest(Eq("SyncObj"), Eq("Foo"))));
279 EXPECT_CALL(*_serverPeer, Dispatches(InitData(Eq("SyncObj"), Eq("Foo"), QVariantMap{
280 {"stringProperty", "Hello"},
282 {"doubleProperty", 4.2},
287 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(23))));
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"))));
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))));
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"))));
303 // Update properties (twice)
304 EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestUpdate"), ElementsAre(QVariantMap{
305 {"stringProperty", "Quassel"},
307 {"doubleProperty", 2.3}
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"},
314 {"doubleProperty", 2.3}
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"))));
324 SyncObj clientObject;
325 SyncObj serverObject;
327 // -- Set initial data
329 serverObject.setIntProperty(42);
330 serverObject.setStringProperty("Hello");
331 serverObject.setDoubleProperty(4.2);
332 serverObject.setObjectName("Foo");
333 clientObject.setObjectName("Foo");
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());
344 // -- Check if client-side values are as expected
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());
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());
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());
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());
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());
381 // -- Property update
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());
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());
412 #include "signalproxytest.moc"