summaryrefslogtreecommitdiffstats
path: root/rbutil/rbutilqt/test/test-httpget.cpp
diff options
context:
space:
mode:
authorDominik Riebeling <Dominik.Riebeling@gmail.com>2013-01-27 10:40:09 +0100
committerDominik Riebeling <Dominik.Riebeling@gmail.com>2013-01-27 20:11:18 +0100
commite96df430c4def5c2a17a54d28b42857a48bd83fa (patch)
tree4d352684a5385f4f7c45ac1f80ea9d65b72b3128 /rbutil/rbutilqt/test/test-httpget.cpp
parent3144c2c74c474f50aa42c9f2d569f347ecf08306 (diff)
downloadrockbox-e96df430c4def5c2a17a54d28b42857a48bd83fa.tar.gz
rockbox-e96df430c4def5c2a17a54d28b42857a48bd83fa.tar.bz2
rockbox-e96df430c4def5c2a17a54d28b42857a48bd83fa.zip
Implement test for HttpGet class.
Change-Id: I1df793fd32dafdea999b875452ee832b773d8156
Diffstat (limited to 'rbutil/rbutilqt/test/test-httpget.cpp')
-rw-r--r--rbutil/rbutilqt/test/test-httpget.cpp512
1 files changed, 512 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/test/test-httpget.cpp b/rbutil/rbutilqt/test/test-httpget.cpp
new file mode 100644
index 0000000000..c9ae86bc6c
--- /dev/null
+++ b/rbutil/rbutilqt/test/test-httpget.cpp
@@ -0,0 +1,512 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2013 Dominik Riebeling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtCore/QObject>
+#include "httpget.h"
+
+#define TEST_USER_AGENT "TestAgent/2.3"
+#define TEST_HTTP_TIMEOUT 1000
+#define TEST_BINARY_BLOB "\x01\x10\x20\x30\x40\x50\x60\x70" \
+ "\x80\x90\xff\xee\xdd\xcc\xbb\xaa"
+
+ // HttpDaemon is the the class that implements the simple HTTP server.
+ class HttpDaemon : public QTcpServer
+{
+ Q_OBJECT
+ public:
+ HttpDaemon(quint16 port, QObject* parent = 0) : QTcpServer(parent)
+ {
+ listen(QHostAddress::Any, port);
+ }
+
+#if QT_VERSION < 0x050000
+ void incomingConnection(int socket)
+#else
+ // Qt 5 uses a different prototype for this function!
+ void incomingConnection(qintptr socket)
+#endif
+ {
+ // When a new client connects, the server constructs a QTcpSocket and all
+ // communication with the client is done over this QTcpSocket. QTcpSocket
+ // works asynchronously, this means that all the communication is done
+ // in the two slots readClient() and discardClient().
+ QTcpSocket* s = new QTcpSocket(this);
+ connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
+ connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
+ s->setSocketDescriptor(socket);
+ }
+ QList<QString> lastRequestData(void)
+ {
+ return m_lastRequestData;
+ }
+ void setResponsesToSend(QList<QByteArray> response)
+ {
+ m_requestNumber = 0;
+ m_responsesToSend = response;
+ }
+ void reset(void)
+ {
+ m_requestNumber = 0;
+ m_lastRequestData.clear();
+ QString now =
+ QDateTime::currentDateTime().toString("ddd, d MMM yyyy hh:mm:ss");
+ m_defaultResponse = QByteArray(
+ "HTTP/1.1 404 Not Found\r\n"
+ "Date: " + now.toLatin1() + "\r\n"
+ "Last-Modified: " + now.toLatin1() + "\r\n"
+ "Connection: close\r\n"
+ "\r\n");
+ }
+
+ private slots:
+ void readClient()
+ {
+ // This slot is called when the client sent data to the server.
+ QTcpSocket* socket = (QTcpSocket*)sender();
+ // read whole request
+ QString request;
+ while(socket->canReadLine()) {
+ QString line = socket->readLine();
+ request.append(line);
+ if(request.endsWith("\r\n\r\n")) {
+ m_lastRequestData.append(request);
+
+ if(m_requestNumber < m_responsesToSend.size())
+ socket->write(m_responsesToSend.at(m_requestNumber));
+ else
+ socket->write(m_defaultResponse);
+ socket->close();
+ m_requestNumber++;
+ }
+ if (socket->state() == QTcpSocket::UnconnectedState)
+ delete socket;
+ }
+ }
+ void discardClient()
+ {
+ QTcpSocket* socket = (QTcpSocket*)sender();
+ socket->deleteLater();
+ }
+
+ private:
+ int m_requestNumber;
+ QList<QByteArray> m_responsesToSend;
+ QList<QString> m_lastRequestData;
+ QByteArray m_defaultResponse;
+};
+
+
+class TestHttpGet : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testCachedRequest(void);
+ void testUncachedRepeatedRequest(void);
+ void testUncachedMovedRequest(void);
+ void testUserAgent(void);
+ void testResponseCode(void);
+ void testContentToBuffer(void);
+ void testContentToFile(void);
+ void testNoServer(void);
+ void testServerTimestamp(void);
+ void testMovedQuery(void);
+ void init(void);
+ void cleanup(void);
+
+ public slots:
+ void waitTimeout(void) { m_waitTimeoutOccured = true; }
+ QDir temporaryFolder(void)
+ {
+ // Qt unfortunately doesn't support creating temporary folders so
+ // we need to do that ourselves.
+ QString tempdir;
+ for(int i = 0; i < 100000; i++) {
+ tempdir = QDir::tempPath() + QString("/qttest-temp-%1").arg(i);
+ if(!QFileInfo(tempdir).exists()) break;
+ }
+ QDir().mkpath(tempdir);
+ return QDir(tempdir);
+ }
+ void rmTree(QString folder)
+ {
+ // no function in Qt to recursively delete a folder :(
+ QDir dir(folder);
+ Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot
+ | QDir::System | QDir::Hidden | QDir::AllDirs
+ | QDir::Files, QDir::DirsFirst)) {
+ if(info.isDir()) rmTree(info.absoluteFilePath());
+ else QFile::remove(info.absoluteFilePath());
+ }
+ dir.rmdir(folder);
+ }
+ private:
+ HttpDaemon *m_daemon;
+ bool m_waitTimeoutOccured;
+ QString m_now;
+ QDir m_cachedir;
+ HttpGet *m_getter;
+ QSignalSpy *m_doneSpy;
+};
+
+
+void TestHttpGet::init(void)
+{
+ m_now = QDateTime::currentDateTime().toString("ddd, d MMM yyyy hh:mm:ss");
+ m_daemon = new HttpDaemon(8080, this);
+ m_daemon->reset();
+ m_cachedir = temporaryFolder();
+ m_getter = new HttpGet(this);
+ m_doneSpy = new QSignalSpy(m_getter, SIGNAL(done(bool)));
+ m_waitTimeoutOccured = false;
+}
+
+void TestHttpGet::cleanup(void)
+{
+ rmTree(m_cachedir.absolutePath());
+ if(m_getter) {
+ m_getter->abort(); delete m_getter;
+ }
+ if(m_daemon) delete m_daemon;
+ if(m_doneSpy) delete m_doneSpy;
+}
+
+/* On uncached requests, HttpGet is supposed to sent a GET request only.
+ */
+void TestHttpGet::testUncachedRepeatedRequest(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "\r\n\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ "<html></html>\r\n\r\n");
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_daemon->lastRequestData().size(), 1);
+ QCOMPARE(m_daemon->lastRequestData().at(0).startsWith("GET"), true);
+
+ // request second time
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() < 2 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+ QCOMPARE(m_doneSpy->count(), 2);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_daemon->lastRequestData().size(), 2);
+ QCOMPARE(m_daemon->lastRequestData().at(1).startsWith("GET"), true);
+ QCOMPARE(m_getter->httpResponse(), 200);
+}
+
+/* With enabled cache HttpGet is supposed to check the server file using a HEAD
+ * request first, then request the file using GET if the server file is newer
+ * than the cached one (or the file does not exist in the cache)
+ */
+void TestHttpGet::testCachedRequest(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 302 Found\r\n"
+ "Location: http://localhost:8080/test2.txt\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ "<html></html>\r\n\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: 1 Jan 2000 00:00:00\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n");
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->setCache(m_cachedir);
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QList<QString> requests = m_daemon->lastRequestData();
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_doneSpy->at(0).at(0).toBool(), false);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(requests.size(), 2);
+ QCOMPARE(requests.at(0).startsWith("GET"), true);
+ QCOMPARE(requests.at(1).startsWith("GET"), true);
+ QCOMPARE(m_getter->httpResponse(), 200);
+
+ // request real file, this time the response should come from cache.
+ m_getter->getFile(QUrl("http://localhost:8080/test2.txt"));
+ while(m_doneSpy->count() < 2 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+ QCOMPARE(m_doneSpy->count(), 2); // 2 requests, 2 times done()
+ QCOMPARE(m_doneSpy->at(1).at(0).toBool(), false);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_daemon->lastRequestData().size(), 3);
+ // redirect will not cache as the redirection target file.
+ QCOMPARE(m_daemon->lastRequestData().at(2).startsWith("GET"), true);
+ QCOMPARE(m_getter->httpResponse(), 200);
+}
+
+/* When a custom user agent is set all requests are supposed to contain it.
+ * Enable cache to make HttpGet performs a HEAD request. Answer with 302, so
+ * HttpGet follows and sends another HEAD request before finally doing a GET.
+ */
+void TestHttpGet::testUserAgent(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "\r\n\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ "<html></html>\r\n\r\n");
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->setGlobalUserAgent(TEST_USER_AGENT);
+ m_getter->setCache(m_cachedir);
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QList<QString> requests = m_daemon->lastRequestData();
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(requests.size(), 1);
+ QCOMPARE(requests.at(0).startsWith("GET"), true);
+
+ for(int i = 0; i < requests.size(); ++i) {
+ QRegExp rx("User-Agent:[\t ]+([a-zA-Z0-9\\./]+)");
+ bool userAgentFound = rx.indexIn(requests.at(i)) > 0 ? true : false;
+ QCOMPARE(userAgentFound, true);
+ QString userAgentString = rx.cap(1);
+ QCOMPARE(userAgentString, QString(TEST_USER_AGENT));
+ }
+}
+
+void TestHttpGet::testUncachedMovedRequest(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 302 Found\r\n"
+ "Location: http://localhost:8080/test2.txt\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ "<html></html>\r\n\r\n");
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->getFile(QUrl("http://localhost:8080/test1.php?var=1&b=foo"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_daemon->lastRequestData().size(), 2);
+ QCOMPARE(m_daemon->lastRequestData().at(0).startsWith("GET"), true);
+ QCOMPARE(m_daemon->lastRequestData().at(1).startsWith("GET"), true);
+}
+
+void TestHttpGet::testResponseCode(void)
+{
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_doneSpy->at(0).at(0).toBool(), true);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_daemon->lastRequestData().size(), 1);
+ QCOMPARE(m_daemon->lastRequestData().at(0).startsWith("GET"), true);
+ QCOMPARE(m_getter->httpResponse(), 404);
+}
+
+void TestHttpGet::testContentToBuffer(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ TEST_BINARY_BLOB);
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_getter->readAll(), QByteArray(TEST_BINARY_BLOB));
+ // sizeof(TEST_BINARY_BLOB) will include an additional terminating NULL.
+ QCOMPARE((unsigned long)m_getter->readAll().size(), sizeof(TEST_BINARY_BLOB) - 1);
+}
+
+void TestHttpGet::testContentToFile(void)
+{
+ QTemporaryFile tf(this);
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ TEST_BINARY_BLOB);
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->setFile(&tf);
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_waitTimeoutOccured, false);
+
+ tf.open();
+ QByteArray data = tf.readAll();
+ QCOMPARE(data, QByteArray(TEST_BINARY_BLOB));
+ QCOMPARE((unsigned long)data.size(), sizeof(TEST_BINARY_BLOB) - 1);
+ tf.close();
+}
+
+void TestHttpGet::testNoServer(void)
+{
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+ m_getter->getFile(QUrl("http://localhost:8081/test1.txt"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_doneSpy->at(0).at(0).toBool(), true);
+ QCOMPARE(m_waitTimeoutOccured, false);
+}
+
+void TestHttpGet::testServerTimestamp(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: Wed, 20 Jan 2010 10:20:30\r\n" // RFC 822
+ "Date: Wed, 20 Jan 2010 10:20:30\r\n"
+ "\r\n"
+ "\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: Sat Feb 19 09:08:07 2011\r\n" // asctime
+ "Date: Sat Feb 19 09:08:07 2011\r\n"
+ "\r\n"
+ "\r\n");
+
+ QList<QDateTime> times;
+ times << QDateTime::fromString("2010-01-20T11:20:30", Qt::ISODate);
+ times << QDateTime::fromString("2011-02-19T10:08:07", Qt::ISODate);
+
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ int count = m_doneSpy->count();
+ for(int i = 0; i < responses.size(); ++i) {
+ m_getter->getFile(QUrl("http://localhost:8080/test1.txt"));
+ while(m_doneSpy->count() == count && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+ count = m_doneSpy->count();
+ QCOMPARE(m_getter->timestamp(), times.at(i));
+ }
+}
+
+void TestHttpGet::testMovedQuery(void)
+{
+ QList<QByteArray> responses;
+ responses << QByteArray(
+ "HTTP/1.1 302 Found\r\n"
+ "Location: http://localhost:8080/test2.php\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "\r\n");
+ responses << QByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Last-Modified: " + m_now.toLatin1() + "\r\n"
+ "Date: " + m_now.toLatin1() + "\r\n"
+ "\r\n"
+ "<html></html>\r\n\r\n");
+ m_daemon->setResponsesToSend(responses);
+
+ QTimer::singleShot(TEST_HTTP_TIMEOUT, this, SLOT(waitTimeout(void)));
+
+ m_getter->getFile(QUrl("http://localhost:8080/test1.php?var=1&b=foo"));
+ while(m_doneSpy->count() == 0 && m_waitTimeoutOccured == false)
+ QCoreApplication::processEvents();
+
+ QCOMPARE(m_doneSpy->count(), 1);
+ QCOMPARE(m_waitTimeoutOccured, false);
+ QCOMPARE(m_getter->httpResponse(), 200);
+ QCOMPARE(m_daemon->lastRequestData().size(), 2);
+ QCOMPARE(m_daemon->lastRequestData().at(0).startsWith("GET"), true);
+ QCOMPARE(m_daemon->lastRequestData().at(1).startsWith("GET"), true);
+ // current implementation keeps order of query items.
+ QCOMPARE((bool)m_daemon->lastRequestData().at(1).contains("/test2.php?var=1&b=foo"), true);
+}
+
+QTEST_MAIN(TestHttpGet)
+
+// this include is needed because we don't use a separate header file for the
+// test class. It also needs to be at the end.
+#include "test-httpget.moc"
+