1 /***************************************************************************
2 * Copyright (C) 2005-2018 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>;
73 void sendData(int, const QString&);
74 void sendMoreData(int, const QString&);
77 void receiveData(int i, const QString& s) { _spy->notify(std::make_pair(i, s)); }
78 void receiveExtraData(int i, const QString& s) { _spy->notify(std::make_pair(-i, s.toUpper())); }
84 TEST_F(SignalProxyTest, attachSignal)
88 EXPECT_CALL(*_clientPeer, Dispatches(RpcCall(Eq(SIGNAL(sendData(int,QString))), ElementsAre(42, "Hello"))));
89 EXPECT_CALL(*_clientPeer, Dispatches(RpcCall(Eq(SIGNAL(sendExtraData(int,QString))), ElementsAre(42, "Hello"))));
90 EXPECT_CALL(*_serverPeer, Dispatches(RpcCall(Eq("2sendData(int,QString)"), ElementsAre(23, "World"))));
93 ProxyObject::Spy clientSpy, serverSpy;
94 ProxyObject clientObject{&clientSpy};
95 ProxyObject serverObject{&serverSpy};
97 // Deliberately not normalize some of the macro invocations
98 _clientProxy.attachSignal(&clientObject, SIGNAL(sendData(int, const QString&)));
99 _serverProxy.attachSlot(SIGNAL(sendData(int,QString)), &serverObject, SLOT(receiveData(int, const QString&)));
101 _clientProxy.attachSignal(&clientObject, SIGNAL(sendMoreData(int,QString)), SIGNAL(sendExtraData(int,QString)));
102 _serverProxy.attachSlot(SIGNAL(sendExtraData(int, const QString&)), &serverObject, SLOT(receiveExtraData(int,QString)));
104 _serverProxy.attachSignal(&serverObject, SIGNAL(sendData(int,QString)));
105 _clientProxy.attachSlot(SIGNAL(sendData(int, const QString&)), &clientObject, SLOT(receiveData(int,QString)));
107 emit clientObject.sendData(42, "Hello");
108 ASSERT_TRUE(serverSpy.wait());
109 EXPECT_EQ(ProxyObject::Data(42, "Hello"), serverSpy.value());
111 emit clientObject.sendMoreData(42, "Hello");
112 ASSERT_TRUE(serverSpy.wait());
113 EXPECT_EQ(ProxyObject::Data(-42, "HELLO"), serverSpy.value());
115 emit serverObject.sendData(23, "World");
116 ASSERT_TRUE(clientSpy.wait());
117 EXPECT_EQ(ProxyObject::Data(23, "World"), clientSpy.value());
120 // -----------------------------------------------------------------------------------------------------------------------------------------
122 class SyncObj : public SyncableObject
127 Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intPropertyChanged)
128 Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringPropertyChanged)
129 Q_PROPERTY(double doubleProperty READ doubleProperty WRITE setDoubleProperty)
133 int intProperty() const
138 QString stringProperty() const
140 return _stringProperty;
143 double doubleProperty() const
145 return _doubleProperty;
148 int syncedInt() const
153 QString syncedString() const
155 return _syncedString;
159 QByteArray initFooData() const
164 void setInitFooData(const QByteArray& data)
167 emit fooDataChanged(data);
170 void setIntProperty(int value)
172 _intProperty = value;
174 emit intPropertyChanged(value);
177 void setStringProperty(const QString& value)
179 _stringProperty = value;
181 emit stringPropertyChanged(value);
184 // Deliberately no sync nor changed signal
185 void setDoubleProperty(double value)
187 _doubleProperty = value;
190 void syncMethod(int intArg, const QString& stringArg)
193 _syncedString = stringArg;
194 SYNC(ARG(intArg), ARG(stringArg));
195 SYNC_OTHER(setIntProperty, ARG(intArg));
196 SYNC_OTHER(setStringProperty, ARG(stringArg));
197 emit syncMethodCalled(intArg, stringArg);
200 void requestMethod(const QString& stringArg, int intArg)
202 REQUEST(ARG(stringArg), ARG(intArg));
203 REQUEST_OTHER(setStringProperty, ARG(stringArg));
204 emit requestMethodCalled(stringArg, intArg);
208 void intPropertyChanged(int);
209 void stringPropertyChanged(const QString&);
210 void fooDataChanged(const QByteArray&);
211 void syncMethodCalled(int, const QString&);
212 void requestMethodCalled(const QString&, int);
216 QString _stringProperty;
217 double _doubleProperty{};
218 QByteArray _fooData{"FOO"};
220 QString _syncedString;
223 TEST_F(SignalProxyTest, syncableObject)
229 EXPECT_CALL(*_clientPeer, Dispatches(InitRequest(Eq("SyncObj"), Eq("Foo"))));
230 EXPECT_CALL(*_serverPeer, Dispatches(InitData(Eq("SyncObj"), Eq("Foo"), QVariantMap{
231 {"stringProperty", "Hello"},
233 {"doubleProperty", 4.2},
238 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(23))));
241 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("syncMethod"), ElementsAre(42, "World"))));
242 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(42))));
243 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("World"))));
246 EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestMethod"), ElementsAre("Hello", 23))));
247 EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("Hello"))));
249 // Update properties (twice)
250 EXPECT_CALL(*_clientPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("requestUpdate"), ElementsAre(QVariantMap{
251 {"stringProperty", "Quassel"},
253 {"doubleProperty", 2.3}
255 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setIntProperty"), ElementsAre(17))));
256 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("setStringProperty"), ElementsAre("Quassel"))));
257 EXPECT_CALL(*_serverPeer, Dispatches(SyncMessage(Eq("SyncObj"), Eq("Foo"), Eq("update"), ElementsAre(QVariantMap{
258 {"stringProperty", "Quassel"},
260 {"doubleProperty", 2.3}
266 SyncObj clientObject;
267 SyncObj serverObject;
269 // -- Set initial data
271 serverObject.setIntProperty(42);
272 serverObject.setStringProperty("Hello");
273 serverObject.setDoubleProperty(4.2);
274 serverObject.setObjectName("Foo");
275 clientObject.setObjectName("Foo");
279 spy.connect(&serverObject, &SyncableObject::initDone);
280 _serverProxy.synchronize(&serverObject);
281 ASSERT_TRUE(spy.wait());
282 spy.connect(&clientObject, &SyncableObject::initDone);
283 _clientProxy.synchronize(&clientObject);
284 ASSERT_TRUE(spy.wait());
286 // -- Check if client-side values are as expected
288 EXPECT_EQ(42, clientObject.intProperty());
289 EXPECT_EQ("Hello", clientObject.stringProperty());
290 EXPECT_EQ(4.2, clientObject.doubleProperty());
291 EXPECT_EQ("FOO", clientObject.initFooData());
293 // -- Set int property
294 spy.connect(&clientObject, &SyncObj::intPropertyChanged);
295 serverObject.setIntProperty(23);
296 ASSERT_TRUE(spy.wait());
297 EXPECT_EQ(23, clientObject.intProperty());
301 spy.connect(&clientObject, &SyncObj::syncMethodCalled);
302 serverObject.syncMethod(42, "World");
303 ASSERT_TRUE(spy.wait());
304 EXPECT_EQ(42, clientObject.syncedInt());
305 EXPECT_EQ(42, clientObject.intProperty());
306 EXPECT_EQ("World", clientObject.syncedString());
307 EXPECT_EQ("World", clientObject.stringProperty());
311 spy.connect(&serverObject, &SyncObj::requestMethodCalled);
312 clientObject.requestMethod("Hello", 23);
313 ASSERT_TRUE(spy.wait());
314 EXPECT_EQ("Hello", serverObject.stringProperty());
316 // -- Property update
318 QVariantMap propMap{{"intProperty", 17}, {"stringProperty", "Quassel"}, {"doubleProperty", 2.3}};
319 spy.connect(&serverObject, &SyncableObject::updatedRemotely);
320 clientObject.requestUpdate(propMap);
321 ASSERT_TRUE(spy.wait());
322 // We don't allow client updates yet, so the values shouldn't have changed
323 EXPECT_EQ(23, serverObject.intProperty());
324 EXPECT_EQ("Hello", serverObject.stringProperty());
325 EXPECT_EQ(4.2, serverObject.doubleProperty());
326 // Allow client updates, try again
327 serverObject.setAllowClientUpdates(true);
328 spy.connect(&clientObject, &SyncableObject::updated);
329 clientObject.requestUpdate(propMap);
330 ASSERT_TRUE(spy.wait());
331 EXPECT_EQ(17, serverObject.intProperty());
332 EXPECT_EQ("Quassel", serverObject.stringProperty());
333 EXPECT_EQ(2.3, serverObject.doubleProperty());
334 EXPECT_EQ(17, clientObject.intProperty());
335 EXPECT_EQ("Quassel", clientObject.stringProperty());
336 EXPECT_EQ(2.3, clientObject.doubleProperty());
339 #include "signalproxytest.moc"