Completed unit tests for handshake request
authorKurt Pattyn <pattyn.kurt@gmail.com>
Sun, 13 Oct 2013 15:16:56 +0000 (17:16 +0200)
committerKurt Pattyn <pattyn.kurt@gmail.com>
Mon, 14 Oct 2013 09:16:07 +0000 (11:16 +0200)
Change-Id: I9558c0244e2285e7dc266bfcb3c51887fb7200d7
Reviewed-by: Steven Ceuppens <steven.ceuppens@icloud.com>
src/websockets/qwebsockethandshakerequest_p.cpp
src/websockets/qwebsockethandshakerequest_p.h
tests/auto/auto.pro
tests/auto/handshakerequest/handshakerequest.pro [new file with mode: 0644]
tests/auto/handshakerequest/tst_handshakerequest.cpp [new file with mode: 0644]

index 8026b49..ad88d12 100644 (file)
@@ -29,6 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 QT_BEGIN_NAMESPACE
 
 /*!
+    \brief Constructs a new QWebSocketHandshakeRequest given \a port and \a isSecure
     \internal
  */
 QWebSocketHandshakeRequest::QWebSocketHandshakeRequest(int port, bool isSecure) :
@@ -53,12 +54,11 @@ QWebSocketHandshakeRequest::~QWebSocketHandshakeRequest()
 }
 
 /*!
+    \brief Clears the request
     \internal
  */
 void QWebSocketHandshakeRequest::clear()
 {
-    m_port = -1;
-    m_isSecure = false;
     m_isValid = false;
     m_headers.clear();
     m_versions.clear();
@@ -189,85 +189,98 @@ QTextStream &QWebSocketHandshakeRequest::readFromStream(QTextStream &textStream)
             bool conversionOk = false;
             const float httpVersion = httpProtocol.midRef(5).toFloat(&conversionOk);
 
-            QString headerLine = textStream.readLine();
-            m_headers.clear();
-            while (!headerLine.isEmpty())
+            if (conversionOk)
             {
-                const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
-                if (headerField.length() < 2)
+                QString headerLine = textStream.readLine();
+                m_headers.clear();
+                while (!headerLine.isEmpty())
                 {
-                    m_isValid = false;
-                    clear();
-                    return textStream;
+                    const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
+                    if (headerField.length() < 2)
+                    {
+                        clear();
+                        return textStream;
+                    }
+                    m_headers.insertMulti(headerField.at(0), headerField.at(1));
+                    headerLine = textStream.readLine();
                 }
-                m_headers.insertMulti(headerField.at(0), headerField.at(1));
-                headerLine = textStream.readLine();
-            }
 
-            const QString host = m_headers.value(QStringLiteral("Host"), QStringLiteral(""));
-            m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
-            if (m_requestUrl.isRelative())
-            {
-                m_requestUrl.setHost(host);
-            }
-            if (m_requestUrl.scheme().isEmpty())
-            {
-                const QString scheme =  isSecure() ? QStringLiteral("wss://") : QStringLiteral("ws://");
-                m_requestUrl.setScheme(scheme);
-            }
+                const QString host = m_headers.value(QStringLiteral("Host"), QStringLiteral(""));
+                m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
+                if (m_requestUrl.isRelative())
+                {
+                    m_requestUrl.setHost(host);
+                }
+                if (m_requestUrl.scheme().isEmpty())
+                {
+                    const QString scheme =  isSecure() ? QStringLiteral("wss") : QStringLiteral("ws");
+                    m_requestUrl.setScheme(scheme);
+                }
 
-            const QStringList versionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Version"));
-            for (QStringList::const_iterator v = versionLines.begin(); v != versionLines.end(); ++v)
-            {
-                const QStringList versions = (*v).split(QStringLiteral(","), QString::SkipEmptyParts);
-                for (QStringList::const_iterator i = versions.begin(); i != versions.end(); ++i)
+                const QStringList versionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Version"));
+                for (QStringList::const_iterator v = versionLines.begin(); v != versionLines.end(); ++v)
                 {
-                   const QWebSocketProtocol::Version ver = QWebSocketProtocol::versionFromString((*i).trimmed());
-                   m_versions << ver;
+                    const QStringList versions = (*v).split(QStringLiteral(","), QString::SkipEmptyParts);
+                    for (QStringList::const_iterator i = versions.begin(); i != versions.end(); ++i)
+                    {
+                        bool ok = false;
+                        (void)(*i).toUInt(&ok);
+                        if (!ok)
+                        {
+                            clear();
+                            return textStream;
+                        }
+                        const QWebSocketProtocol::Version ver = QWebSocketProtocol::versionFromString((*i).trimmed());
+                        m_versions << ver;
+                    }
+                }
+                std::sort(m_versions.begin(), m_versions.end(), qGreater<QWebSocketProtocol::Version>()); //sort in descending order
+                m_key = m_headers.value(QStringLiteral("Sec-WebSocket-Key"), QStringLiteral(""));
+                const QString upgrade = m_headers.value(QStringLiteral("Upgrade"), QStringLiteral(""));           //must be equal to "websocket", case-insensitive
+                const QString connection = m_headers.value(QStringLiteral("Connection"), QStringLiteral(""));     //must contain "Upgrade", case-insensitive
+                const QStringList connectionLine = connection.split(QStringLiteral(","), QString::SkipEmptyParts);
+                QStringList connectionValues;
+                for (QStringList::const_iterator c = connectionLine.begin(); c != connectionLine.end(); ++c)
+                {
+                    connectionValues << (*c).trimmed();
                 }
-            }
-            std::sort(m_versions.begin(), m_versions.end(), qGreater<QWebSocketProtocol::Version>()); //sort in descending order
-            m_key = m_headers.value(QStringLiteral("Sec-WebSocket-Key"), QStringLiteral(""));
-            const QString upgrade = m_headers.value(QStringLiteral("Upgrade"), QStringLiteral(""));           //must be equal to "websocket", case-insensitive
-            const QString connection = m_headers.value(QStringLiteral("Connection"), QStringLiteral(""));     //must contain "Upgrade", case-insensitive
-            const QStringList connectionLine = connection.split(QStringLiteral(","), QString::SkipEmptyParts);
-            QStringList connectionValues;
-            for (QStringList::const_iterator c = connectionLine.begin(); c != connectionLine.end(); ++c)
-            {
-                connectionValues << (*c).trimmed();
-            }
 
-            //optional headers
-            m_origin = m_headers.value(QStringLiteral("Sec-WebSocket-Origin"), QStringLiteral(""));
-            const QStringList protocolLines = m_headers.values(QStringLiteral("Sec-WebSocket-Protocol"));
-            for (QStringList::const_iterator pl = protocolLines.begin(); pl != protocolLines.end(); ++pl)
-            {
-                QStringList protocols = (*pl).split(QStringLiteral(","), QString::SkipEmptyParts);
-                for (QStringList::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
+                //optional headers
+                m_origin = m_headers.value(QStringLiteral("Sec-WebSocket-Origin"), QStringLiteral(""));
+                const QStringList protocolLines = m_headers.values(QStringLiteral("Sec-WebSocket-Protocol"));
+                for (QStringList::const_iterator pl = protocolLines.begin(); pl != protocolLines.end(); ++pl)
                 {
-                    m_protocols << (*p).trimmed();
+                    QStringList protocols = (*pl).split(QStringLiteral(","), QString::SkipEmptyParts);
+                    for (QStringList::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
+                    {
+                        m_protocols << (*p).trimmed();
+                    }
                 }
-            }
-            const QStringList extensionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Extensions"));
-            for (QStringList::const_iterator el = extensionLines.begin(); el != extensionLines.end(); ++el)
-            {
-                QStringList extensions = (*el).split(QStringLiteral(","), QString::SkipEmptyParts);
-                for (QStringList::const_iterator e = extensions.begin(); e != extensions.end(); ++e)
+                const QStringList extensionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Extensions"));
+                for (QStringList::const_iterator el = extensionLines.begin(); el != extensionLines.end(); ++el)
                 {
-                    m_extensions << (*e).trimmed();
+                    QStringList extensions = (*el).split(QStringLiteral(","), QString::SkipEmptyParts);
+                    for (QStringList::const_iterator e = extensions.begin(); e != extensions.end(); ++e)
+                    {
+                        m_extensions << (*e).trimmed();
+                    }
                 }
-            }
 
-            //TODO: authentication field
+                //TODO: authentication field
 
-            m_isValid = !(host.isEmpty() ||
-                          resourceName.isEmpty() ||
-                          m_versions.isEmpty() ||
-                          m_key.isEmpty() ||
-                          (verb != QStringLiteral("GET")) ||
-                          (!conversionOk || (httpVersion < 1.1f)) ||
-                          (upgrade.toLower() != QStringLiteral("websocket")) ||
-                          (!connectionValues.contains(QStringLiteral("upgrade"), Qt::CaseInsensitive)));
+                m_isValid = !(host.isEmpty() ||
+                              resourceName.isEmpty() ||
+                              m_versions.isEmpty() ||
+                              m_key.isEmpty() ||
+                              (verb != QStringLiteral("GET")) ||
+                              (!conversionOk || (httpVersion < 1.1f)) ||
+                              (upgrade.toLower() != QStringLiteral("websocket")) ||
+                              (!connectionValues.contains(QStringLiteral("upgrade"), Qt::CaseInsensitive)));
+            }
+            if (!m_isValid)
+            {
+                clear();
+            }
         }
     }
     return textStream;
index 88025e9..fc292af 100644 (file)
@@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE
 
 class QTextStream;
 
-class QWebSocketHandshakeRequest
+class Q_AUTOTEST_EXPORT QWebSocketHandshakeRequest
 {
     Q_DISABLE_COPY(QWebSocketHandshakeRequest)
 
@@ -80,7 +80,7 @@ private:
     QUrl m_requestUrl;
 };
 
-QTextStream &operator >>(QTextStream &stream, QWebSocketHandshakeRequest &request);
+QTextStream & Q_AUTOTEST_EXPORT operator >>(QTextStream &stream, QWebSocketHandshakeRequest &request);
 
 QT_END_NAMESPACE
 
index fe88c37..21f22a5 100644 (file)
@@ -1,9 +1,10 @@
 TEMPLATE = subdirs
 
 SUBDIRS += \
+   websocketcorsauthenticator
 
 contains(QT_CONFIG, private_tests): SUBDIRS += \
-   dataprocessor \
    websocketprotocol \
+   dataprocessor \
    websocketframe \
-   websocketcorsauthenticator
+   handshakerequest
diff --git a/tests/auto/handshakerequest/handshakerequest.pro b/tests/auto/handshakerequest/handshakerequest.pro
new file mode 100644 (file)
index 0000000..0f4401e
--- /dev/null
@@ -0,0 +1,15 @@
+CONFIG += console
+CONFIG += c++11
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_handshakerequest
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_handshakerequest.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/handshakerequest/tst_handshakerequest.cpp b/tests/auto/handshakerequest/tst_handshakerequest.cpp
new file mode 100644 (file)
index 0000000..001391b
--- /dev/null
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QDebug>
+#include <QByteArray>
+#include <QtEndian>
+
+#include "private/qwebsockethandshakerequest_p.h"
+#include "qwebsocketprotocol.h"
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
+
+class tst_HandshakeRequest : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_HandshakeRequest();
+
+private Q_SLOTS:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+    void cleanup();
+
+    void tst_initialization();
+
+    void tst_invalidStream_data();
+    void tst_invalidStream();
+
+    void tst_multipleValuesInConnectionHeader();
+    void tst_multipleVersions();
+};
+
+tst_HandshakeRequest::tst_HandshakeRequest()
+{}
+
+void tst_HandshakeRequest::initTestCase()
+{
+}
+
+void tst_HandshakeRequest::cleanupTestCase()
+{}
+
+void tst_HandshakeRequest::init()
+{
+    qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
+    qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+}
+
+void tst_HandshakeRequest::cleanup()
+{
+}
+
+void tst_HandshakeRequest::tst_initialization()
+{
+    {
+        QWebSocketHandshakeRequest request(0, false);
+        QCOMPARE(request.port(), 0);
+        QVERIFY(!request.isSecure());
+        QVERIFY(!request.isValid());
+        QCOMPARE(request.extensions().length(), 0);
+        QCOMPARE(request.protocols().length(), 0);
+        QCOMPARE(request.headers().size(), 0);
+        QCOMPARE(request.key().length(), 0);
+        QCOMPARE(request.origin().length(), 0);
+        QVERIFY(request.requestUrl().isEmpty());
+        QCOMPARE(request.resourceName().length(), 0);
+        QCOMPARE(request.versions().length(), 0);
+    }
+    {
+        QWebSocketHandshakeRequest request(80, true);
+        QCOMPARE(request.port(), 80);
+        QVERIFY(request.isSecure());
+        QVERIFY(!request.isValid());
+        QCOMPARE(request.extensions().length(), 0);
+        QCOMPARE(request.protocols().length(), 0);
+        QCOMPARE(request.headers().size(), 0);
+        QCOMPARE(request.key().length(), 0);
+        QCOMPARE(request.origin().length(), 0);
+        QVERIFY(request.requestUrl().isEmpty());
+        QCOMPARE(request.resourceName().length(), 0);
+        QCOMPARE(request.versions().length(), 0);
+    }
+    {
+        QWebSocketHandshakeRequest request(80, true);
+        request.clear();
+        QCOMPARE(request.port(), 80);
+        QVERIFY(request.isSecure());
+        QVERIFY(!request.isValid());
+        QCOMPARE(request.extensions().length(), 0);
+        QCOMPARE(request.protocols().length(), 0);
+        QCOMPARE(request.headers().size(), 0);
+        QCOMPARE(request.key().length(), 0);
+        QCOMPARE(request.origin().length(), 0);
+        QVERIFY(request.requestUrl().isEmpty());
+        QCOMPARE(request.resourceName().length(), 0);
+        QCOMPARE(request.versions().length(), 0);
+    }
+}
+
+void tst_HandshakeRequest::tst_invalidStream_data()
+{
+    QTest::addColumn<QString>("dataStream");
+
+    QTest::newRow("garbage on 2 lines") << "foofoofoo\r\nfoofoo\r\n\r\n";
+    QTest::newRow("garbage on 1 line") << "foofoofoofoofoo";
+    QTest::newRow("Correctly formatted but invalid fields") << "VERB RESOURCE PROTOCOL";
+
+    //internally the fields are parsed and indexes are used to convert to a http version for instance
+    //this test checks if there doesn't occur an out-of-bounds exception
+    QTest::newRow("Correctly formatted but invalid short fields") << "V R P";
+    QTest::newRow("Invalid \\0 character in header") << "V R\0 P";
+    QTest::newRow("Invalid http version in header") << "V R HTTP/invalid";
+    QTest::newRow("Empty header field") << "GET . HTTP/1.1\r\nHEADER: ";
+    QTest::newRow("All zeros") << QString(QByteArray(10, char(0)));
+    QTest::newRow("Invalid hostname") << "GET . HTTP/1.1\r\nHost: \xFF\xFF";
+    QTest::newRow("Complete heaer - Invalid websocket version") << "GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: \xFF\xFF\r\n" \
+                                                                   "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                                                                   "Upgrade: websocket\r\n" \
+                                                                   "Connection: Upgrade\r\n\r\n";
+    QTest::newRow("Complete header - Invalid verb") << "XXX . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n" \
+                                                       "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                                                       "Upgrade: websocket\r\n" \
+                                                       "Connection: Upgrade\r\n\r\n";
+    QTest::newRow("Complete header - Invalid http version") << "GET . HTTP/a.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n" \
+                                                               "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                                                               "Upgrade: websocket\r\n" \
+                                                               "Connection: Upgrade\r\n\r\n";
+    QTest::newRow("Complete header - Invalid connection") << "GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n" \
+                                                             "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                                                             "Upgrade: websocket\r\n" \
+                                                             "Connection: xxxxxxx\r\n\r\n";
+    QTest::newRow("Complete header - Invalid upgrade") << "GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n" \
+                                                          "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                                                          "Upgrade: wabsocket\r\n" \
+                                                          "Connection: Upgrade\r\n\r\n";
+    QTest::newRow("Complete header - Upgrade contains too many values") << "GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n" \
+                                                          "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                                                          "Upgrade: websocket,ftp\r\n" \
+                                                          "Connection: Upgrade\r\n\r\n";
+}
+
+void tst_HandshakeRequest::tst_invalidStream()
+{
+    QFETCH(QString, dataStream);
+
+    QByteArray data;
+    QTextStream textStream(&data);
+    QWebSocketHandshakeRequest request(80, true);
+
+    textStream << dataStream;
+    textStream.seek(0);
+    textStream >> request;
+
+    QVERIFY(!request.isValid());
+    QCOMPARE(request.port(), 80);
+    QVERIFY(request.isSecure());
+    QCOMPARE(request.extensions().length(), 0);
+    QCOMPARE(request.protocols().length(), 0);
+    QCOMPARE(request.headers().size(), 0);
+    QCOMPARE(request.key().length(), 0);
+    QCOMPARE(request.origin().length(), 0);
+    QVERIFY(request.requestUrl().isEmpty());
+    QCOMPARE(request.resourceName().length(), 0);
+    QCOMPARE(request.versions().length(), 0);
+}
+
+/*
+ * This is a regression test
+ * Checks for validity when more than one value is present in Connection
+ */
+void tst_HandshakeRequest::tst_multipleValuesInConnectionHeader()
+{
+    QString header = "GET /test HTTP/1.1\r\nHost: foo.com\r\nSec-WebSocket-Version: 13\r\n" \
+                     "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                     "Upgrade: websocket\r\n" \
+                     "Connection: Upgrade,keepalive\r\n\r\n";
+    QByteArray data;
+    QTextStream textStream(&data);
+    QWebSocketHandshakeRequest request(80, false);
+
+    textStream << header;
+    textStream.seek(0);
+    textStream >> request;
+
+    QVERIFY(request.isValid());
+    QCOMPARE(request.port(), 80);
+    QVERIFY(!request.isSecure());
+    QCOMPARE(request.extensions().length(), 0);
+    QCOMPARE(request.protocols().length(), 0);
+    QCOMPARE(request.headers().size(), 5);
+    QCOMPARE(request.key().length(), 9);
+    QCOMPARE(request.origin().length(), 0);
+    QCOMPARE(request.requestUrl(), QUrl("ws://foo.com/test"));
+    QCOMPARE(request.resourceName().length(), 5);
+    QCOMPARE(request.versions().length(), 1);
+    QCOMPARE(request.versions().at(0), QWebSocketProtocol::V_13);
+}
+
+void tst_HandshakeRequest::tst_multipleVersions()
+{
+    QString header = "GET /test HTTP/1.1\r\nHost: foo.com\r\n" \
+                     "Sec-WebSocket-Version: 4, 5, 6, 7, 8, 13\r\n" \
+                     "Sec-WebSocket-Key: AVDFBDDFF\r\n" \
+                     "Upgrade: websocket\r\n" \
+                     "Connection: Upgrade,keepalive\r\n\r\n";
+    QByteArray data;
+    QTextStream textStream(&data);
+    QWebSocketHandshakeRequest request(80, false);
+
+    textStream << header;
+    textStream.seek(0);
+    textStream >> request;
+
+    QVERIFY(request.isValid());
+    QCOMPARE(request.port(), 80);
+    QVERIFY(!request.isSecure());
+    QCOMPARE(request.extensions().length(), 0);
+    QCOMPARE(request.protocols().length(), 0);
+    QCOMPARE(request.headers().size(), 5);
+    QVERIFY(request.headers().contains(QStringLiteral("Host")));
+    QVERIFY(request.headers().contains(QStringLiteral("Sec-WebSocket-Version")));
+    QVERIFY(request.headers().contains(QStringLiteral("Sec-WebSocket-Key")));
+    QVERIFY(request.headers().contains(QStringLiteral("Upgrade")));
+    QVERIFY(request.headers().contains(QStringLiteral("Connection")));
+    QCOMPARE(request.key(), QStringLiteral("AVDFBDDFF"));
+    QCOMPARE(request.origin().length(), 0);
+    QCOMPARE(request.requestUrl(), QUrl("ws://foo.com/test"));
+    QCOMPARE(request.resourceName().length(), 5);
+    QCOMPARE(request.versions().length(), 6);
+    //should be 13 since the list is ordered in decreasing order
+    QCOMPARE(request.versions().at(0), QWebSocketProtocol::V_13);
+}
+
+QTEST_MAIN(tst_HandshakeRequest)
+
+#include "tst_handshakerequest.moc"