/ test / tst_msgpackiodevice.cpp
tst_msgpackiodevice.cpp
  1  #include <QTest>
  2  #include <QSignalSpy>
  3  #include <QTcpServer>
  4  #include <QTcpSocket>
  5  #include <QRegularExpression>
  6  #include <QBuffer>
  7  
  8  #include <msgpackiodevice.h>
  9  #include <msgpackrequest.h>
 10  #include "common.h"
 11  
 12  namespace NeovimQt {
 13  
 14  /**
 15   * A dummy request handler to help testing, it responds to all calls
 16   * with response
 17   */
 18  class RequestHandler: public QObject, public MsgpackRequestHandler
 19  {
 20  	Q_OBJECT
 21  public:
 22  	RequestHandler(QObject *parent=0):QObject(parent) {}
 23  	virtual void handleRequest(MsgpackIODevice* dev, quint32 msgid, const QByteArray& method, const QVariantList& params) {
 24  		dev->sendResponse(msgid, QVariant(), response);
 25  		emit receivedRequest(msgid, method, params);
 26  	}
 27  	QVariant response;
 28  signals:
 29  	void receivedRequest(quint32 msgid, const QByteArray&, const QVariantList&);
 30  
 31  };
 32  
 33  class Test: public QObject
 34  {
 35  	Q_OBJECT
 36  private:
 37  	MsgpackIODevice *one, *two;
 38  	void resetLoop() {
 39  		QTcpServer *server = new QTcpServer();
 40  		QVERIFY(server->listen(QHostAddress::LocalHost));
 41  
 42  		QVERIFY(server->isListening());
 43  
 44  		QTcpSocket *client = new QTcpSocket(this);
 45  		QSignalSpy onConnected(client, SIGNAL(connected()));
 46  		QVERIFY(onConnected.isValid());
 47  		QSignalSpy onNewConnection(server, SIGNAL(newConnection()));
 48  		QVERIFY(onNewConnection.isValid());
 49  
 50  		client->connectToHost(server->serverAddress(), server->serverPort());
 51  		QVERIFY(SPYWAIT(onConnected));
 52  		QVERIFY(SPYWAIT(onNewConnection));
 53  
 54  		QTcpSocket *other = server->nextPendingConnection();
 55  
 56  		one = new MsgpackIODevice(client);
 57  		two = new MsgpackIODevice(other);
 58  		QVERIFY(one->isOpen());
 59  		QVERIFY(two->isOpen());
 60  	}
 61  private slots:
 62  	void init() {
 63  		resetLoop();
 64  	}
 65  
 66  	void cleanup() {
 67  		delete one;
 68  		delete two;
 69  	}
 70  
 71  	void invalidDevice() {
 72  		MsgpackIODevice *io = new MsgpackIODevice(new QBuffer);
 73  		QCOMPARE(io->errorCause(), MsgpackIODevice::InvalidDevice);
 74  	}
 75  
 76  	void defaultValues() { QCOMPARE(one->errorCause(), MsgpackIODevice::NoError); }
 77  
 78  	/**
 79  	 * These errors are not fatal but increase coverage
 80  	 */
 81  	void recvError() {
 82  		one->send(QByteArray("Hello!"));
 83  		
 84  		// An array of size 1 is an invalid msgpack-rpc
 85  		QVariantList brokenRequest;
 86  		brokenRequest << 42;
 87  		one->send(brokenRequest);
 88  
 89  		// Invalid method
 90  		QVariantList brokenRequest2;
 91  		brokenRequest2 << "X" << 42 << 42 << 42;
 92  		one->send(brokenRequest2);
 93  
 94  		// Invalid method
 95  		QVariantList brokenRequest3;
 96  		brokenRequest2 << 0 << 42 << 42 << 42;
 97  		one->send(brokenRequest3);
 98  
 99  		// Just to finish
100  		auto req = one->startRequestUnchecked("testRequest", 0);
101  		QSignalSpy gotResp(req, SIGNAL(error(quint32, quint64, QVariant)));
102  		QVERIFY(gotResp.isValid());
103  		QVERIFY2(SPYWAIT(gotResp), "By default all requests get an error");
104  	}
105  
106  	void notification() {
107  		QVariantList list;
108  		list << 44 << 45;
109  
110  		QVariantList params;
111  		params << 1 << QByteArray("one") << -3 << list;
112  		QByteArray method("testNotification");
113  
114  		QSignalSpy onNotification(two, SIGNAL(notification(QByteArray, QVariantList)));
115  		QVERIFY(onNotification.isValid());
116  
117  		one->sendNotification(method, params);
118  		QVERIFY(SPYWAIT(onNotification));
119  
120  		QVariantList n = onNotification.at(0);
121  		QCOMPARE(n.at(0).toByteArray(), method);
122  		QCOMPARE(n.at(1).toList(), params);
123  	}
124  
125  	void request() {
126  		auto req = one->startRequestUnchecked("testRequest", 0);
127  		
128  		QSignalSpy gotResp(req, SIGNAL(error(quint32, quint64, QVariant)));
129  		QVERIFY(gotResp.isValid());
130  
131  		QVERIFY2(SPYWAIT(gotResp), "By default all requests get an error");
132  
133  		// Now test with an actual response
134  		RequestHandler *handler = new RequestHandler(two);
135  		handler->response = 42;
136  		two->setRequestHandler(handler);
137  
138  		QSignalSpy gotReq(handler, SIGNAL(receivedRequest(quint32, QByteArray, QVariantList)));
139  		QVERIFY(gotReq.isValid());
140  
141  		auto req2 = one->startRequestUnchecked("testRequest2", 1);
142  		one->send(QByteArray("hello"));
143  		QVERIFY(SPYWAIT(gotReq));
144  
145  		// Make sure the request msgid/method/params matches the ones we sent
146  		QVariantList signal = gotReq.at(0);
147  		QCOMPARE(signal.at(0).toUInt(), req2->id);
148  		QCOMPARE(signal.at(1).toByteArray(), QByteArray("testRequest2"));
149  		QCOMPARE(signal.at(2).toList().at(0).toByteArray(), QByteArray("hello"));
150  
151  		QSignalSpy gotResp2(req2, SIGNAL(finished(quint32, quint64, QVariant)));
152  		QVERIFY(gotResp2.isValid());
153  		QVERIFY2(SPYWAIT(gotResp2), "RequestHandler sends back a response");
154  	}
155  
156  	void checkVariant()
157  	{
158  		// Some Unsupported types
159  		QVERIFY(!one->checkVariant(QRect()));
160  
161  		// Supported types
162  		QVERIFY(one->checkVariant(QVariant()));
163  		QVERIFY(one->checkVariant(true));
164  		QVERIFY(one->checkVariant(42));
165  		QVERIFY(one->checkVariant(4.4));
166  		QVERIFY(one->checkVariant(QStringLiteral("test")));
167  		QVERIFY(one->checkVariant(QByteArray()));
168  		QVERIFY(one->checkVariant(QVariantList() << "test"));
169  		QVERIFY(one->checkVariant(QVariantMap()));
170  		QVERIFY(one->checkVariant(QPoint(1,1)));
171  	}
172  
173  	void msgId() {
174  		QCOMPARE(one->msgId(), (quint32)0);
175  		QCOMPARE(one->msgId(), (quint32)1);
176  		// Sending a request increases the id
177  		one->startRequestUnchecked("testRequestMsgId", 0);
178  		QCOMPARE(one->msgId(), (quint32)3);
179  		// Notifications dont
180  		one->sendNotification("testNotificationMsgId", QVariantList());
181  		QCOMPARE(one->msgId(), (quint32)4);
182  	}
183  
184  	void timeout() {
185  		QBuffer buf;
186  		buf.open(QBuffer::ReadWrite);
187  
188  		// This will actually trigger a fatal error because the buffer
189  		// is not a valid device, but is still enough to get the timeout
190  		// below
191  		MsgpackIODevice *dev = new MsgpackIODevice(&buf);
192  
193  		MsgpackRequest *r = dev->startRequestUnchecked("testTimeout", 0);
194  		r->setTimeout(3);
195  
196  		QSignalSpy timedOut(r, SIGNAL(timeout(quint32)));
197  		QVERIFY(timedOut.isValid());
198  		QVERIFY(SPYWAIT(timedOut));
199  		QVariantList params = timedOut.at(0);
200  		QCOMPARE(params.at(0).toUInt(), r->id);
201  	}
202  
203  };
204  
205  } // Namespace NeovimQt
206  QTEST_MAIN(NeovimQt::Test)
207  #include "tst_msgpackiodevice.moc"