// krazy:excludeall=qclasses
/* This file is part of the KDE libraries
    Copyright (c) 1999-2005 Waldo Bastian <bastian@kde.org>
    Copyright (c) 2000-2005 David Faure <faure@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "kurltest.h"

#include <QTest>

QTEST_MAIN(KUrlTest)

#include "kurl.h"

#include "kuser.h"

#include <QTextCodec>
#include <QDataStream>
#include <QMap>
#include <QDebug>
#include <unistd.h> // gethostname

//QCOMPARE cannot be used to strictly check for empty or null QString as it treats QString("") == QString()
#define QSTREMPTY(_str) QVERIFY(!_str.isNull() && _str.isEmpty())

void KUrlTest::testEmptyURL()
{
    KUrl emptyURL;
    QVERIFY(!emptyURL.isValid());
    QVERIFY(emptyURL.isEmpty());
    QVERIFY(emptyURL.prettyUrl().isEmpty());
    QCOMPARE(emptyURL.url(), QString());
    QVERIFY(emptyURL.url().isEmpty());
    QVERIFY(!emptyURL.url().isNull());   // well, a null string would be correct too...
    QVERIFY(!emptyURL.isLocalFile());

    KUrl emptyStringURL("");
    QVERIFY(!emptyStringURL.isValid());
    QVERIFY(emptyStringURL.isEmpty());
    QVERIFY(emptyStringURL.url().isEmpty());
    QVERIFY(!emptyStringURL.url().isNull());
    QCOMPARE(emptyURL, emptyStringURL);

    // Roundtrip via .url()
    KUrl emptyCopy(emptyURL.url());
    QCOMPARE(emptyURL, emptyCopy);
    KUrl emptyStringCopy = KUrl(emptyStringURL.url());
    QCOMPARE(emptyStringURL, emptyCopy);

    KUrl fileURL("file:/");
    QVERIFY(!fileURL.isEmpty());

    fileURL = "file:///";
    QVERIFY(!fileURL.isEmpty());

    fileURL = QLatin1String("FILE:///");
    QVERIFY(fileURL.isLocalFile());

    KUrl udir;
    QCOMPARE(udir.url(), QString());
    QVERIFY(udir.isEmpty());
    QVERIFY(!udir.isValid());
    udir = udir.upUrl();
    QVERIFY(udir.upUrl().isEmpty());
}

void KUrlTest::testIsValid()
{
    KUrl url1("gg:www.kde.org");
    QVERIFY(url1.isValid());

    url1 = "KDE";
    QVERIFY(url1.isValid());   // KDE3 difference: was FALSE

    url1 = "$HOME/.kde/share/config";
    QVERIFY(url1.isValid());   // KDE3 difference: was FALSE
}

void KUrlTest::testSetQuery()
{
    KUrl url1 = KUrl(QByteArray("http://www.kde.org/foo.cgi?foo=bar"));
    QCOMPARE(url1.query(), QString("?foo=bar"));
    url1.setQuery("toto=titi&kde=rocks");
    QCOMPARE(url1.query(), QString("?toto=titi&kde=rocks"));
    url1.setQuery("?kde%20rocks&a=b");   // must be encoded already, as documented
    QCOMPARE(url1.query(), QString("?kde%20rocks&a=b"));   // encoded, as documented
    url1.setQuery("?");
    QCOMPARE(url1.query(), QString("?"));
    url1.setQuery("");
    QCOMPARE(url1.query(), QString("?"));
    url1.setQuery(QString());
    QCOMPARE(url1.query(), QString());

    url1.setQuery("4=2+2");
    QCOMPARE(url1.url(), QString("http://www.kde.org/foo.cgi?4=2+2"));
    QCOMPARE(url1.query(), QString("?4=2+2"));
    url1.setQuery("4=2%2B2");
    QCOMPARE(url1.url(), QString("http://www.kde.org/foo.cgi?4=2%2B2"));  // %2B should not be decoded to +
    QCOMPARE(url1.query(), QString("?4=2%2B2"));
}

void KUrlTest::testEmptyNullReference()
{
    KUrl url1 = KUrl("http://www.kde.org");
    QVERIFY(!url1.hasRef());
    QVERIFY(!url1.hasHTMLRef());
    QVERIFY(url1.ref().isNull());
    QVERIFY(url1.htmlRef().isNull());
    QVERIFY(url1.encodedHtmlRef().isNull());

    url1 = "http://www.kde.org#";
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QSTREMPTY(url1.ref());
    QSTREMPTY(url1.htmlRef());
    QSTREMPTY(url1.encodedHtmlRef());
}

void KUrlTest::testSetRef()
{
    KUrl url1 = KUrl(QByteArray("http://www.kde.org/foo.cgi#foo=bar"));
    QCOMPARE(url1.ref(), QString("foo=bar"));    // KDE3: was foo=bar, KDE4 (until 4.11): foo%3Dbar, KF5 (Qt5.2): foo=bar
#if 0// ditto (TODO)
    url1.setRef("toto=titi&kde=rocks");
    QCOMPARE(url1.ref(), QString("toto=titi&kde=rocks"));
    url1.setRef("kde=rocks&a=b");
    QCOMPARE(url1.ref(), QString("kde=rocks&a=b"));
    url1.setRef("#");
    QCOMPARE(url1.ref(), QString("#"));
#endif
    url1.setRef("");
    QSTREMPTY(url1.ref());
    QCOMPARE(url1.url(), QString("http://www.kde.org/foo.cgi#"));

    url1.setRef(QString());
    QVERIFY(url1.ref().isNull());
    QCOMPARE(url1.url(), QString("http://www.kde.org/foo.cgi"));
}

void KUrlTest::testSetHTMLRef()
{
    KUrl url1 = KUrl(QByteArray("http://www.kde.org/foo.cgi#foo=bar"));
    QCOMPARE(url1.htmlRef(), QString("foo=bar"));
    url1.setHTMLRef("toto=titi&kde=rocks");
    QCOMPARE(url1.htmlRef(), QString("toto=titi&kde=rocks"));
    url1.setHTMLRef("kde=rocks&a=b");
    QCOMPARE(url1.htmlRef(), QString("kde=rocks&a=b"));
    url1.setHTMLRef("#");
    QCOMPARE(url1.htmlRef(), QString("#"));
    QCOMPARE(url1.ref(), QString("%23"));   // it's encoded
    url1.setHTMLRef("");
    QSTREMPTY(url1.htmlRef());
    QCOMPARE(url1.url(), QString("http://www.kde.org/foo.cgi#"));

    url1.setHTMLRef(QString());
    QVERIFY(url1.htmlRef().isNull());
    QCOMPARE(url1.url(), QString("http://www.kde.org/foo.cgi"));
}

void KUrlTest::testQUrl()
{
    QUrl url1("file:///home/dfaure/my#%2f");
    // Qt 4.0 to 4.4: #/. Qt 4.5: %2f again.
    // Qt 5.0 & 5.1: #/. Qt 5.2: %2F again (probably due to 2ff719de8fe7a in qtbase)
    // See also testSimpleMethods below, for the KUrl history of it.
    QCOMPARE(url1.toString(), QString("file:///home/dfaure/my#%2F"));

#ifdef Q_OS_WIN
    QUrl url2("file:///c:/home/dfaure/my#%2f");
    QCOMPARE(url2.toString(), QString("file:///c:/home/dfaure/my#/"));
#endif

    // Show how toString() was confusing in Qt4, and fixed in Qt5
    QUrl url3 = QUrl::fromLocalFile("/home/dfaure/hash#file");
    QCOMPARE(url3.toString(), QString("file:///home/dfaure/hash%23file"));
    QString url3Str = url3.toString();
    QUrl url4(url3Str);
    QCOMPARE(url4.toString(), url3Str);
    QVERIFY(url3 == url4);
}

void KUrlTest::testIsLocalFile()
{
    KUrl trash("trash:/");
    QVERIFY(!trash.isLocalFile());

    KUrl local_file_1("file://localhost/my/file");
    QVERIFY(local_file_1.isLocalFile());

    KUrl local_file_2("file://www.kde.org/my/file");
    QVERIFY(!local_file_2.isLocalFile());

#if 0
    // exactly like in kurl.cpp, getenv("HOSTNAME") can be different...
    char hostname[ 256 ];
    hostname[ 0 ] = '\0';
    if (!gethostname(hostname, 255)) {
        hostname[sizeof(hostname) - 1] = '\0';
    }

    for (char *p = hostname; *p; p++) {
        *p = tolower(*p);
    }

    KUrl local_file_3;
    local_file_3.setHost(QString::fromLatin1(hostname));
    local_file_3.setPath("/my/file");
    qDebug("URL=%s", qPrintable(local_file_3.url()));
    QVERIFY(local_file_3.isLocalFile());
#endif

    KUrl local_file_4("file:///my/file");
    QVERIFY(local_file_4.isLocalFile());

#ifdef Q_OS_WIN
    KUrl local_file_4a("file:///c:/my/file");
    QVERIFY(local_file_4a.isLocalFile());
#endif

    KUrl local_file_5;
    local_file_5.setPath("/foo?bar");
    QCOMPARE(local_file_5.url(), QString("file:///foo%3Fbar"));

#ifdef Q_OS_WIN
    KUrl local_file_5a;
    local_file_5a.setPath("c:/foo?bar");
    QCOMPARE(local_file_5a.url(), QString("file:///c:/foo%3Fbar"));
#endif

    KUrl scummos("inf://localhost/test.txt");
    QVERIFY(!scummos.isLocalFile());
}

void KUrlTest::testSimpleMethods() // to test parsing, mostly
{
    KUrl kde("http://www.kde.org");
    QVERIFY(kde.isValid());
    QCOMPARE(kde.port(), -1);   // KDE3 DIFFERENCE: was 0.

    KUrl mlc("http://mlc:80/");
    QVERIFY(mlc.isValid());
    QCOMPARE(mlc.port(), 80);
    QCOMPARE(mlc.path(), QString("/"));

    KUrl ulong("https://swww.gad.de:443/servlet/CookieAccepted?MAIL=s@gad.de&VER=25901");
    QCOMPARE(ulong.host(), QString("swww.gad.de"));
    QCOMPARE(ulong.path(), QString("/servlet/CookieAccepted"));

    // RFC 3986 section 3.5 says:
    // fragment = *( pchar / "/" / "?" )
    // where:
    // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
    //         unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
    //         pct-encoded = "%" HEXDIG HEXDIG
    //         sub-delims  = sub-delims  = "!" / "$" / "&" / "'" / "(" / ")" /  "*" / "+" / "," / ";" / "="
    KUrl url0;
    url0 = QString("http://www.kde.org#/?:@-._~!$&'()*+,;="); // These fragment's chars should not be %-encoded
    QCOMPARE(url0.ref(),     QString("/?:@-._~!$&'()*+,;="));
    QCOMPARE(url0.htmlRef(), QString("/?:@-._~!$&'()*+,;="));
    QCOMPARE(url0.url(), QString("http://www.kde.org#/?:@-._~!$&'()*+,;="));

    url0 = QString("http://www.kde.org#/?:@-._~!$&'()*+,;=%A");
    QCOMPARE(url0.ref(),     QString("/?:@-._~!$&'()*+,;=%25A"));
    QCOMPARE(url0.htmlRef(), QString("/?:@-._~!$&'()*+,;=%A"));
    QCOMPARE(url0.url(), QString("http://www.kde.org#/?:@-._~!$&'()*+,;=%25A"));

    url0 = "http://www.kde.org/foo.cgi?4=2+2";
    QCOMPARE(url0.url(), QString("http://www.kde.org/foo.cgi?4=2+2"));
    url0 = "http://www.kde.org/foo.cgi?4=2%2B2";
    QCOMPARE(url0.url(), QString("http://www.kde.org/foo.cgi?4=2%2B2"));    // %2B should not be decoded to +

    KUrl fileURL("file:///home/dfaure/myfile");
    QCOMPARE(fileURL.url(), QString("file:///home/dfaure/myfile"));
    QCOMPARE(fileURL.path(), QString("/home/dfaure/myfile"));
    QVERIFY(!fileURL.hasRef());

    QString u1 = "file:/home/dfaure/my#myref";
    KUrl url1(u1);
    // KDE3 difference: QUrl doesn't resolve file:/ into file:///
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my#myref"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.htmlRef(), QString("myref"));
    QCOMPARE(url1.upUrl().url(), QString("file:///home/dfaure/"));

#if 0
    QUrl qurl = QUrl::fromEncoded("file:///home/dfaure/my#%23");
    printf("toString = %s\n", qurl.toString().toLatin1().constData());
    printf("toEncoded = %s\n", qurl.toEncoded().data());
    qurl = QUrl::fromEncoded("file:///home/dfaure/my#%2f");
    printf("toString = %s\n", qurl.toString().toLatin1().constData());
    printf("toEncoded = %s\n", qurl.toEncoded().data());
    qurl = QUrl::fromEncoded("file:///home/dfaure/my#/");
    printf("toString = %s\n", qurl.toString().toLatin1().constData());
    printf("toEncoded = %s\n", qurl.toEncoded().data());
#endif

    u1 = "file:///home/dfaure/my#%2f";
    url1 = u1;
    // KDE3: was %2f, Qt-4.0 to 4.4: #/. 4.5: %2f again. 5.0 to 5.1: #/. 5.2: %2F.
    // (see also testQUrl)
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my#%2F"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.ref().toLower(), QString("%2f"));
    QCOMPARE(url1.encodedHtmlRef().toLower(), QString("%2f"));
    QCOMPARE(url1.htmlRef(), QString("/"));

    u1 = "file:///home/dfaure/my#%23";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my#%23"));   // KDE4: #%23. Qt 5.0/5.1 had "##", fixed in c615dcc4416.
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.ref(), QString("%23"));
    QCOMPARE(url1.encodedHtmlRef(), QString("%23"));
    QCOMPARE(url1.htmlRef(), QString("#"));

    url1 = KUrl(url1, "#%6a");
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my#j"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.ref(), QString("j"));
    QCOMPARE(url1.encodedHtmlRef(), QString("j"));
    QCOMPARE(url1.htmlRef(), QString("j"));

    url1 = KUrl(url1, "#");
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my#"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QSTREMPTY(url1.ref());
    QSTREMPTY(url1.encodedHtmlRef());
    QSTREMPTY(url1.htmlRef());

    u1 = "file:///home/dfaure/my#myref";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my#myref"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.htmlRef(), QString("myref"));
    QCOMPARE(url1.upUrl().url(), QString("file:///home/dfaure/"));

    u1 = "file:/opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.ref(), QString("QObject::connect"));   // used to be %3A%3A but there's not point in encoding that here.
    QCOMPARE(url1.htmlRef(), QString("QObject::connect"));
    QCOMPARE(url1.upUrl().url(), QString("file:///opt/kde2/qt2/doc/html/"));

    u1 = "file:///opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.ref(), QString("QObject::connect"));   // see above
    QCOMPARE(url1.htmlRef(), QString("QObject::connect"));
    QCOMPARE(url1.upUrl().url(), QString("file:///opt/kde2/qt2/doc/html/"));

    u1 = "file:/opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject:connect";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject:connect"));
    QVERIFY(url1.hasRef());
    QVERIFY(url1.hasHTMLRef());
    QVERIFY(!url1.hasSubUrl());
    QCOMPARE(url1.ref(), QString("QObject:connect"));   // see above
    QCOMPARE(url1.htmlRef(), QString("QObject:connect"));
    QCOMPARE(url1.upUrl().url(), QString("file:///opt/kde2/qt2/doc/html/"));

    KUrl carsten;
    carsten.setPath("/home/gis/src/kde/kdelibs/kfile/.#kfiledetailview.cpp.1.18");
    QCOMPARE(carsten.path(), QString("/home/gis/src/kde/kdelibs/kfile/.#kfiledetailview.cpp.1.18"));

    KUrl longUserName("http://thisisaverylongusername@foobar.com/");
    QCOMPARE(longUserName.prettyUrl(), QString("http://thisisaverylongusername@foobar.com/"));
    QCOMPARE(KUrl(longUserName.prettyUrl()).url(), QString("http://thisisaverylongusername@foobar.com/"));
    KUrl whitespaceInUser("http://www.google.com%20%20%20%20%20@foobar.com/");
    QCOMPARE(whitespaceInUser.prettyUrl(), QString("http://www.google.com%20%20%20%20%20@foobar.com/"));
    KUrl whitespaceInPath("http://www.google.com/foo%20bar/");
    QCOMPARE(whitespaceInPath.prettyUrl(), QString("http://www.google.com/foo bar/"));
    KUrl whitespaceInPath2("http://www.google.com/foo%20%20%20%20%20%20%20bar/");
    QCOMPARE(whitespaceInPath2.prettyUrl(), QString("http://www.google.com/foo%20%20%20%20%20%20 bar/"));

    KUrl charles;
    charles.setPath("/home/charles/foo%20moo");
    QCOMPARE(charles.path(), QString("/home/charles/foo%20moo"));
    KUrl charles2("file:/home/charles/foo%20moo");
    QCOMPARE(charles2.path(), QString("/home/charles/foo moo"));

    //NOTE this test should be ran in UTF8 locale
    KUrl percentEncodedQuery("http://mail.yandex.ru/message_part/%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B8%20%D0%BE%D1%86%D0%B5%D0%BD%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%BE%D1%80%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9.rar?hid=1.1&mid=391.56424458.99241672611486679803334485488&name=%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B8%20%D0%BE%D1%86%D0%B5%D0%BD%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%BE%D1%80%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9.rar");
    QCOMPARE(percentEncodedQuery.prettyUrl(), QString::fromUtf8("http://mail.yandex.ru/message_part/Критерии оценивания орловой.rar?hid=1.1&mid=391.56424458.99241672611486679803334485488&name=%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B8%20%D0%BE%D1%86%D0%B5%D0%BD%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%BE%D1%80%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9.rar"));

#ifdef Q_OS_WIN
#ifdef Q_CC_MSVC
#pragma message ("port KUser")
#else
#warning port KUser
#endif
#else
    KUrl tilde;
    KUser currentUser;
    const QString userName = currentUser.loginName();
    QVERIFY(!userName.isEmpty());
    tilde.setPath(QString::fromUtf8("~%1/Matériel").arg(userName));
    QString homeDir = currentUser.homeDir();
    QEXPECT_FAIL("", "No more user-expansion in KUrl", Continue);
    QCOMPARE(tilde.url(), QString("file://%1/Mat%C3%A9riel").arg(homeDir));

    QUrl httpTilde("http://foo.bar/index.html");
    httpTilde.setPath("/~slajsjdlsjd/test.html");
    QVERIFY2(httpTilde.isValid(), qPrintable(httpTilde.errorString()));
    QCOMPARE(httpTilde.toString(), QString("http://foo.bar/~slajsjdlsjd/test.html"));
#endif
}

void KUrlTest::testHostName()
{
    KUrl u1("http://www.Abc.de/FR");
    QCOMPARE(u1.host(), QString("www.abc.de")); // lowercase
    QCOMPARE(u1.path(), QString("/FR")); // lowercase
    QCOMPARE(u1.url(), QString("http://www.abc.de/FR")); // hostname is lowercase
    QCOMPARE(u1.prettyUrl(), QString("http://www.abc.de/FR")); // hostname is lowercase

    KUrl u2;
    u2.setProtocol("http");
    u2.setHost("www.Abc.de");
    QCOMPARE(u2.host(), QString("www.abc.de")); // lowercase
    QCOMPARE(u2.url(), QString("http://www.abc.de")); // lowercase

    KUrl u3("donkey://Abc/DE");
    QCOMPARE(u3.host(), QString("abc")); // lowercase
    QCOMPARE(u3.url(), QString("donkey://abc/DE")); // lowercase
}

void KUrlTest::testEmptyQueryOrRef()
{
    QUrl url = QUrl::fromEncoded("http://www.kde.org");
    QCOMPARE(url.toEncoded(), QByteArray("http://www.kde.org"));
    QCOMPARE(url.encodedQuery(), QByteArray());
    url = QUrl::fromEncoded("http://www.kde.org?");
    QCOMPARE(url.toEncoded(), QByteArray("http://www.kde.org?"));
    QCOMPARE(url.encodedQuery(), QByteArray());   // note that QByteArray() == QByteArray("")

    url = QUrl::fromEncoded("http://www.kde.org");
    QVERIFY(url.encodedQuery().isEmpty());
    QVERIFY(!url.hasQuery());
    url = QUrl::fromEncoded("http://www.kde.org?");
    QVERIFY(!url.encodedQuery().isNull());
    QVERIFY(url.encodedQuery().isEmpty());
    QVERIFY(url.hasQuery());

    KUrl noQuery("http://www.kde.org");
    QCOMPARE(noQuery.query(), QString());   // query at all
    QVERIFY(!noQuery.hasQuery());
    QVERIFY(!noQuery.hasRef());
    QVERIFY(noQuery.ref().isNull());

    // Empty queries should be preserved!
    QUrl qurl = QUrl::fromEncoded("http://www.kde.org/cgi/test.cgi?", QUrl::TolerantMode);
    QCOMPARE(qurl.toEncoded(), QByteArray("http://www.kde.org/cgi/test.cgi?"));

    KUrl waba1("http://www.kde.org/cgi/test.cgi?");
    QCOMPARE(waba1.url(), QString("http://www.kde.org/cgi/test.cgi?"));
    QCOMPARE(waba1.query(), QString("?"));     // empty query
    QVERIFY(waba1.hasQuery());

    // Empty references should be preserved
    waba1 = "http://www.kde.org/cgi/test.cgi#";
    QCOMPARE(waba1.url(), QString("http://www.kde.org/cgi/test.cgi#"));
    QVERIFY(waba1.hasRef());
    QVERIFY(waba1.hasFragment());
    QVERIFY(waba1.hasHTMLRef());
    QSTREMPTY(waba1.encodedHtmlRef());
    //qurl = QUrl::fromEncoded("http://www.kde.org/cgi/test.cgi#", QUrl::TolerantMode);
    //QCOMPARE( qurl.toEncoded(), QByteArray("http://www.kde.org/cgi/test.cgi#") );

    KUrl tobi1("http://host.net/path/?#http://broken-adsfk-poij31-029mu-2890zupyc-*!*'O-+-0i");
    QCOMPARE(tobi1.query(), QString("?")); // query is empty
    QVERIFY(tobi1.hasQuery());

    tobi1 = "http://host.net/path/#no-query";
    QCOMPARE(tobi1.query(), QString("")); // no query
    QVERIFY(!tobi1.hasQuery());
}

void KUrlTest::testParsingTolerance()
{
    // URLs who forgot to encode spaces in the query - the QUrl version first
    QUrl incorrectEncoded = QUrl::fromEncoded("http://www.kde.org/cgi/qurl.cgi?hello=My Value");
    QVERIFY(incorrectEncoded.isValid());
    QVERIFY(!incorrectEncoded.toEncoded().isEmpty());
    //qDebug( "%s", incorrectEncoded.toEncoded().data() );
    QCOMPARE(incorrectEncoded.toEncoded(),
             QByteArray("http://www.kde.org/cgi/qurl.cgi?hello=My%20Value"));

    // URLs who forgot to encode spaces in the query.
    KUrl waba1("http://www.kde.org/cgi/test.cgi?hello=My Value");
    //QVERIFY( waba1.isValid() );
    QCOMPARE(waba1.url(), QString("http://www.kde.org/cgi/test.cgi?hello=My%20Value"));

    // URL with ':' in query (':' should NOT be encoded!)
    waba1 = "http://www.kde.org/cgi/test.cgi?hello:My Value";
    QCOMPARE(waba1.url(), QString("http://www.kde.org/cgi/test.cgi?hello:My%20Value"));
    QCOMPARE(waba1.upUrl().url(), QString("http://www.kde.org/cgi/test.cgi"));

    // URLs who forgot to encode spaces in the query.
    waba1 = "http://www.kde.org/cgi/test.cgi?hello=My Value+20";
    QCOMPARE(waba1.url(), QString("http://www.kde.org/cgi/test.cgi?hello=My%20Value+20"));
}

void KUrlTest::testNewLine()
{
    QUrl qurl_newline_1 = QUrl::fromEncoded("http://www.foo.bar/foo/bar\ngnork", QUrl::TolerantMode);
    QVERIFY(qurl_newline_1.isValid());
    QCOMPARE(qurl_newline_1.toEncoded(), QByteArray("http://www.foo.bar/foo/bar%0Agnork"));

    KUrl url_newline_1("http://www.foo.bar/foo/bar\ngnork");
    QCOMPARE(url_newline_1.url(), QLatin1String("http://www.foo.bar/foo/bar%0Agnork"));

    KUrl url_newline_2("http://www.foo.bar/foo?bar\ngnork");
    QCOMPARE(url_newline_2.url(), QLatin1String("http://www.foo.bar/foo?bar%0Agnork"));
}

void KUrlTest::testQueryParsing()
{
    KUrl ldap("ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)");
    QCOMPARE(ldap.host(), QString("host.com"));
    QCOMPARE(ldap.port(), 6666);
    QCOMPARE(ldap.path(), QString("/o=University of Michigan,c=US"));
    QCOMPARE(ldap.query(), QString("??sub?(cn=Babs%20Jensen)"));
    QCOMPARE(ldap.url(), QString("ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)"));
    ldap.setQuery("??sub?(cn=Karl%20Marx)");
    QCOMPARE(ldap.query(), QString("??sub?(cn=Karl%20Marx)"));
    QCOMPARE(ldap.url(), QString("ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Karl%20Marx)"));
}

void KUrlTest::testURLsWithoutPath()
{
    // Urls without path (BR21387)
    KUrl waba1("http://meine.db24.de?link=home_c_login_login");   // has query
    QCOMPARE(waba1.url(), QString("http://meine.db24.de?link=home_c_login_login"));
    QCOMPARE(waba1.path(), QString(""));
    QCOMPARE(waba1.query(), QString("?link=home_c_login_login"));

    waba1 = "http://a:389?b=c"; // has port and query
    QCOMPARE(waba1.url(), QString("http://a:389?b=c"));
    QCOMPARE(waba1.host(), QString("a"));
    QCOMPARE(waba1.port(), 389);
    QCOMPARE(waba1.path(), QString(""));
    QCOMPARE(waba1.query(), QString("?b=c"));

    // Urls without path (BR21387)
    waba1 = "http://meine.db24.de#link=home_c_login_login"; // has fragment
    QCOMPARE(waba1.url(), QString("http://meine.db24.de#link=home_c_login_login"));
    QCOMPARE(waba1.path(), QString(""));

    waba1 = "http://a:389#b=c"; // has port and fragment
    //qDebug( "%s", qPrintable( waba1.url() ) );
    QCOMPARE(waba1.scheme(), QString("http"));
    QCOMPARE(waba1.url(), QString("http://a:389#b=c"));
    QCOMPARE(waba1.host(), QString("a"));
    QCOMPARE(waba1.port(), 389);
    QCOMPARE(waba1.path(), QString(""));
    QCOMPARE(waba1.ref(), QString("b=c"));     // see testSetRef(), this changed over time
    QCOMPARE(waba1.htmlRef(), QString("b=c"));
    QCOMPARE(waba1.query(), QString());

    QUrl schemeOnly("ftp:");
    QVERIFY(schemeOnly.isValid());
    QCOMPARE(schemeOnly.scheme(), QString("ftp"));
}

void KUrlTest::testPathAndQuery()
{
#if 0
    // this KDE3 test fails, but Tobias Anton didn't say where it came from, and Andreas Hanssen (TT) says:
    // "I can't see any reason to support this; it looks like a junk artifact from older days.
    // Everything after # is the fragment. Parsing what comes after # is broken; tolerant or not."
    KUrl tobi0("http://some.host.net/path/to/file#fragmentPrecedes?theQuery");
    QCOMPARE(tobi0.ref(), QString("fragmentPrecedes"));
    QCOMPARE(tobi0.query(), QString("?theQuery"));
#else
    // So we treat it as part of the fragment
    KUrl tobi0("http://some.host.net/path/to/file#foo?bar");
    QCOMPARE(tobi0.ref(), QString("foo?bar"));   // was foo%3Fbar until KDE 4.11, but no need to encode it (Qt 5.2)
    QCOMPARE(tobi0.query(), QString());
    QCOMPARE(tobi0.prettyUrl(), QString("http://some.host.net/path/to/file#foo?bar"));
#endif

    KUrl tobi1("http://host.net/path?myfirstquery#andsomeReference");
    tobi1.setEncodedPathAndQuery("another/path/?another&query");
    QCOMPARE(tobi1.query(), QString("?another&query"));
    QCOMPARE(tobi1.path(), QString("another/path/"));   // with trailing slash
    QCOMPARE(tobi1.encodedPathAndQuery(), QString("another/path/?another&query"));
    tobi1.setEncodedPathAndQuery("another/path?another&query");
    QCOMPARE(tobi1.query(), QString("?another&query"));
    QCOMPARE(tobi1.path(), QString("another/path"));   // without trailing slash
    QCOMPARE(tobi1.encodedPathAndQuery(), QString("another/path?another&query"));
    tobi1.setEncodedPathAndQuery("and%20another%2Bpath%2E?bug=%2B258301&query%2Bword");
    QCOMPARE(tobi1.query(), QString("?bug=%2B258301&query%2Bword"));   // encoded
    QCOMPARE(tobi1.queryItem("bug"), QString("+258301"));   // decoded
    QCOMPARE(tobi1.path(), QString("and another+path."));   // decoded
    QCOMPARE(tobi1.encodedPath().constData(), "and%20another+path.");   // from QUrl. It only encodes the space.
    QCOMPARE(tobi1.encodedPathAndQuery(), QString("and%20another+path.?bug=%2B258301&query%2Bword"));

    tobi1 = "http://host.net/path/#no-query";
    QCOMPARE(tobi1.encodedPathAndQuery(), QString("/path/"));

    KUrl kde("http://www.kde.org");
    QCOMPARE(kde.encodedPathAndQuery(), QString(""));
    QCOMPARE(kde.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath), QString("/"));

    KUrl theKow("http://www.google.de/search?q=frerich&hlx=xx&hl=de&empty=&lr=lang+de&test=%2B%20%3A%25");
    QCOMPARE(theKow.encodedPathAndQuery(), QString("/search?q=frerich&hlx=xx&hl=de&empty=&lr=lang+de&test=%2B%20%3A%25"));

    KUrl uloc("file:///home/dfaure/konqtests/Mat%C3%A9riel");
    QCOMPARE(uloc.encodedPathAndQuery(), QString("/home/dfaure/konqtests/Mat%C3%A9riel"));

    KUrl urlWithAccent("file:///home/dfaure/konqtests/Matériel");
    QCOMPARE(urlWithAccent.encodedPathAndQuery(), QString("/home/dfaure/konqtests/Mat%C3%A9riel"));

    KUrl urlWithUnicodeChar(QString::fromUtf8("file:///home/dfaure/konqtests/Matériel"));
    QCOMPARE(urlWithUnicodeChar.encodedPathAndQuery(), QString("/home/dfaure/konqtests/Mat%C3%A9riel"));

    KUrl maelcum(QString::fromUtf8("http://a.b.c/äöu"));
    QCOMPARE(maelcum.encodedPathAndQuery(), QString("/%C3%A4%C3%B6u"));

    // This gives an encoded path in Qt5. TODO: find again where this came from (Gof, obviously, in 2009)
    //KUrl gof("file:%2Ftmp%2Fkde-ogoffart%2Fkmail"); // weird URL, but well ;)
    //QCOMPARE(gof.path(), QString("/tmp/kde-ogoffart/kmail"));
}

void KUrlTest::testUpUrl()
{
    KUrl url1("ftp://user%40host.com@ftp.host.com/var/www/");
    QCOMPARE(url1.user(), QString("user@host.com"));
    QCOMPARE(url1.host(), QString("ftp.host.com"));
    KUrl up = url1.upUrl();
    QCOMPARE(up.url(), QString("ftp://user%40host.com@ftp.host.com/var/"));
    up = up.upUrl();
    QCOMPARE(up.url(), QString("ftp://user%40host.com@ftp.host.com/"));
    up = up.upUrl();
    QCOMPARE(up.url(), QString("ftp://user%40host.com@ftp.host.com/"));   // unchanged

    // Going up from a relative url is not supported (#170695)
    KUrl invalid("tmp");
    QVERIFY(invalid.isValid());
    up = invalid.upUrl();
    QVERIFY(!up.isValid());
}

void KUrlTest::testSetFileName() // and addPath
{
    KUrl u2("file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/README");
    //qDebug( "* URL is %s", qPrintable( u2.url() ) );

    u2.setFileName("myfile.txt");
    QCOMPARE(u2.url(), QString("file:///home/dfaure/myfile.txt"));
    u2.setFileName("myotherfile.txt");
    QCOMPARE(u2.url(), QString("file:///home/dfaure/myotherfile.txt"));

    // more tricky, renaming a directory (kpropertiesdialog.cc)
    QString tmpurl = "file:/home/dfaure/myolddir/";
    if (tmpurl.at(tmpurl.length() - 1) == '/')
        // It's a directory, so strip the trailing slash first
    {
        tmpurl.truncate(tmpurl.length() - 1);
    }
    KUrl newUrl(tmpurl);
    newUrl.setFileName("mynewdir");
    QCOMPARE(newUrl.url(), QString("file:///home/dfaure/mynewdir"));

    // addPath tests
    newUrl.addPath("subdir");
    QCOMPARE(newUrl.url(), QString("file:///home/dfaure/mynewdir/subdir"));
    newUrl.addPath("/foo/");
    QCOMPARE(newUrl.url(), QString("file:///home/dfaure/mynewdir/subdir/foo/"));
    u2 = "http://www.kde.org"; // no path
    u2.addPath("subdir");
    QCOMPARE(u2.url(), QString("http://www.kde.org/subdir"));
    u2.addPath("");
    QCOMPARE(u2.url(), QString("http://www.kde.org/subdir"));   // unchanged

    QUrl qurl2 = QUrl::fromEncoded("print:/specials/Print%20To%20File%20(PDF%252FAcrobat)", QUrl::TolerantMode);
    QCOMPARE(qurl2.path(), QString::fromLatin1("/specials/Print To File (PDF%2FAcrobat)"));
    QCOMPARE(qurl2.path(QUrl::PrettyDecoded), QString::fromLatin1("/specials/Print To File (PDF%252FAcrobat)"));
    QCOMPARE(qurl2.toString(), QString::fromLatin1("print:/specials/Print To File (PDF%252FAcrobat)"));
    QCOMPARE(qurl2.toEncoded(), QByteArray("print:/specials/Print%20To%20File%20(PDF%252FAcrobat)"));

    // even more tricky
    u2 = "print:/specials/Print%20To%20File%20(PDF%252FAcrobat)";
    QCOMPARE(u2.path(), QString("/specials/Print To File (PDF%2FAcrobat)"));
    QCOMPARE(u2.fileName(), QString("Print To File (PDF%2FAcrobat)"));
    u2.setFileName("");
    QCOMPARE(u2.url(), QString("print:/specials/"));

    u2 = "file:/specials/Print";
    QCOMPARE(u2.path(), QString("/specials/Print"));
    QCOMPARE(u2.fileName(), QString("Print"));
    u2.setFileName("");
    QCOMPARE(u2.url(), QString("file:///specials/"));

    const char *u3 = "ftp://host/dir1/dir2/myfile.txt";
    QVERIFY(!KUrl(u3).hasSubUrl());

    KUrl::List lst = KUrl::split(KUrl(u3));
    QCOMPARE(lst.count(), 1);
    QCOMPARE(lst.first().url(), QString("ftp://host/dir1/dir2/myfile.txt"));

    // cdUp code
    KUrl lastUrl = lst.last();
    QString dir = lastUrl.directory();
    QCOMPARE(dir, QString("/dir1/dir2"));

    // files without directories
    KUrl singleFile("foo.txt");
    QCOMPARE(singleFile.path(), QString("foo.txt"));
    QCOMPARE(singleFile.pathOrUrl(), QString("foo.txt"));

    QString pre;
#ifdef Q_OS_WIN
    // On Windows we explicitly prepend a slash, see KUrl::setPath
    pre = '/';
#endif
    singleFile.setFileName("bar.bin");
    QCOMPARE(singleFile.path(), QString(pre + "bar.bin"));
    QCOMPARE(singleFile.pathOrUrl(), QString(pre + "bar.bin"));
}

void KUrlTest::testDirectory()
{
    KUrl udir;
    udir.setPath("/home/dfaure/file.txt");
    //qDebug( "URL is %s", qPrintable( udir.url() ) );
    QCOMPARE(udir.path(), QString("/home/dfaure/file.txt"));
    QCOMPARE(udir.url(), QString("file:///home/dfaure/file.txt"));
    QCOMPARE(udir.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash), QString("/home/dfaure/"));
    QCOMPARE(udir.directory(KUrl::ObeyTrailingSlash), QString("/home/dfaure"));

    KUrl u2(QByteArray("file:///home/dfaure/"));
    // not ignoring trailing slash
    QCOMPARE(u2.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash), QString("/home/dfaure/"));
    QCOMPARE(u2.directory(KUrl::ObeyTrailingSlash), QString("/home/dfaure"));
    // ignoring trailing slash
    QCOMPARE(u2.directory(KUrl::AppendTrailingSlash), QString("/home/"));
    QCOMPARE(u2.directory(), QString("/home"));

    // cleanPath() tests (before cd() since cd uses that)
    u2.cleanPath();
    QCOMPARE(u2.url(), QString("file:///home/dfaure/"));
    u2.addPath("/..//foo");
    QCOMPARE(u2.url(), QString("file:///home/dfaure/..//foo"));
    u2 = KUrl(QByteArray("file:///home/dfaure/..//foo"));
    QCOMPARE(u2.url(), QString("file:///home/dfaure/..//foo"));
    u2.cleanPath(KUrl::KeepDirSeparators);
    QCOMPARE(u2.url(), QString("file:///home//foo"));
    u2.cleanPath(KUrl::SimplifyDirSeparators);
    QCOMPARE(u2.url(), QString("file:///home/foo"));

    // cd() tests
    u2.cd("..");
    QCOMPARE(u2.url(), QString("file:///home"));
    u2.cd("thomas");
    QCOMPARE(u2.url(), QString("file:///home/thomas"));
    u2.cd("../");
    QCOMPARE(u2.url(), QString("file:///home/"));
    u2.cd("/opt/kde/bin/");
    QCOMPARE(u2.url(), QString("file:///opt/kde/bin/"));
    u2 = "ftp://ftp.kde.org/";
    u2.cd("pub");
    QCOMPARE(u2.url(), QString("ftp://ftp.kde.org/pub"));
    u2 = u2.upUrl();
    QCOMPARE(u2.url(), QString("ftp://ftp.kde.org/"));
}

void KUrlTest::testPrettyURL()
{
    KUrl tildeInPath("http://ferret.lmh.ox.ac.uk/%7Ekdecvs/");
    QCOMPARE(tildeInPath.prettyUrl(), QString("http://ferret.lmh.ox.ac.uk/~kdecvs/"));
    // Tilde should not be re-encoded, see end of 2.3 in rfc3986
    QCOMPARE(KUrl(tildeInPath.prettyUrl()).url(), QString("http://ferret.lmh.ox.ac.uk/~kdecvs/"));

    KUrl spaceInPath("file:/home/test/directory%20with%20spaces");
    QCOMPARE(spaceInPath.prettyUrl(), QString("file:///home/test/directory with spaces"));
    QCOMPARE(KUrl(spaceInPath.prettyUrl()).url(), QString("file:///home/test/directory%20with%20spaces"));

    KUrl plusInPath("http://slashdot.org/~RAMMS%2BEIN/"); // #232008
    QCOMPARE(plusInPath.prettyUrl(), QString::fromLatin1("http://slashdot.org/~RAMMS+EIN/"));
    QCOMPARE(KUrl(plusInPath.prettyUrl()).url(), QString::fromLatin1("http://slashdot.org/~RAMMS+EIN/"));

    KUrl notPretty3("fish://foo/%23README%23");
    QCOMPARE(notPretty3.path(), QString("/#README#"));
    QCOMPARE(notPretty3.prettyUrl(), QString("fish://foo/%23README%23"));

    KUrl url15581("http://alain.knaff.linux.lu/bug-reports/kde/spaces in url.html");
    QCOMPARE(url15581.prettyUrl(), QString("http://alain.knaff.linux.lu/bug-reports/kde/spaces in url.html"));
    QCOMPARE(url15581.url(), QString("http://alain.knaff.linux.lu/bug-reports/kde/spaces%20in%20url.html"));

    KUrl newLineInQuery("http://localhost/?a=foo%0A%0Abar%20baz&b=foo%0Abar%21%3F");
    QCOMPARE(newLineInQuery.prettyUrl(), QString("http://localhost/?a=foo%0A%0Abar%20baz&b=foo%0Abar%21%3F"));
    KUrl nonUtf8Query("http://kde.org/?a=test%C2%A0foo%A0%A0%A0%A0bar");
    QCOMPARE(nonUtf8Query.prettyUrl(), QString("http://kde.org/?a=test%C2%A0foo%A0%A0%A0%A0bar"));

    // KDE3 test was for parsing "percentage%in%url.html", but this is not supported; too broken.
    KUrl url15581bis("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html");
    QCOMPARE(url15581bis.prettyUrl(), QString("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html"));
    QCOMPARE(url15581bis.url(), QString("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html"));

    KUrl urlWithPass("ftp://user:password@ftp.kde.org/path");
    QCOMPARE(urlWithPass.pass(), QString::fromLatin1("password"));
    QCOMPARE(urlWithPass.prettyUrl(), QString::fromLatin1("ftp://user@ftp.kde.org/path"));

    KUrl urlWithPassAndNoUser("ftp://:password@ftp.kde.org/path");
    QCOMPARE(urlWithPassAndNoUser.pass(), QString::fromLatin1("password"));
    QCOMPARE(urlWithPassAndNoUser.prettyUrl(), QString::fromLatin1("ftp://ftp.kde.org/path"));

    KUrl xmppUri("xmpp:ogoffart@kde.org");
    QCOMPARE(xmppUri.path(), QString::fromLatin1("ogoffart@kde.org"));
    QCOMPARE(xmppUri.prettyUrl(), QString::fromLatin1("xmpp:ogoffart@kde.org"));

    QUrl offEagleqUrl;
    offEagleqUrl.setEncodedUrl("http://www.sejlsport.dk/Pr%F8v%20noget%20nyt%20dokumenter.pdf", QUrl::TolerantMode);
    const QString offEaglePath = offEagleqUrl.path();
    QCOMPARE((int)offEaglePath.at(2).unicode(), (int)'r');
#if 0 // CURRENTLY BROKEN, PENDING PRETTYURL REWRITE AND QT-4.5 (in thiago's hands)
    QCOMPARE((int)offEaglePath.at(3).unicode(), (int)0xf8);

    KUrl offEagle("http://www.sejlsport.dk/graphics/ds/DSUngdom/PDF/Pr%F8v%20noget%20nyt%20dokumenter/Invitation_Kerteminde_11.07.08.pdf");
    QCOMPARE(offEagle.path(), QString::fromLatin1("/graphics/ds/DSUngdom/PDF/Pr%F8v noget nyt dokumenter/Invitation_Kerteminde_11.07.08.pdf"));
    QCOMPARE(offEagle.url(), QString::fromLatin1("http://www.sejlsport.dk/graphics/ds/DSUngdom/PDF/Pr%F8v%20noget%20nyt%20dokumenter/Invitation_Kerteminde_11.07.08.pdf"));
    QCOMPARE(offEagle.prettyUrl(), QString::fromLatin1("http://www.sejlsport.dk/graphics/ds/DSUngdom/PDF/Pr%F8v noget nyt dokumenter/Invitation_Kerteminde_11.07.08.pdf"));
#endif

    KUrl openWithUrl("kate --use %25U");
    QCOMPARE(openWithUrl.url(), QString::fromLatin1("kate%20--use%20%25U"));
    QCOMPARE(openWithUrl.prettyUrl(), QString::fromLatin1("kate --use %25U"));
    QCOMPARE(openWithUrl.path(), QString::fromLatin1("kate --use %U"));
    QCOMPARE(openWithUrl.pathOrUrl(), QString::fromLatin1("kate --use %25U")); // caused #153894; better not use KUrl for this.

    KUrl ipv6Address("http://[::ffff:129.144.52.38]:81/index.html");
    QCOMPARE(ipv6Address.prettyUrl(), QString::fromLatin1("http://[::ffff:129.144.52.38]:81/index.html"));
}

void KUrlTest::testIsRelative()
{
    QVERIFY(!KUrl::isRelativeUrl("man:mmap"));
    QVERIFY(!KUrl::isRelativeUrl("javascript:doSomething()"));
    QVERIFY(!KUrl::isRelativeUrl("file:///blah"));
    // arguable, but necessary for KUrl( baseURL, "//www1.foo.bar" );
    QVERIFY(KUrl::isRelativeUrl("/path"));
    QVERIFY(KUrl::isRelativeUrl("something"));

    KUrl something("something");
    QCOMPARE(something.url(), QString("something"));
    QCOMPARE(something.protocol(), QString());
    QVERIFY(!something.isLocalFile());

    // Now let's test QUrl::isRelative.
    QVERIFY(!KUrl("file:///blah").isRelative());
    QVERIFY(!KUrl("/blah").isRelative());
    QVERIFY(KUrl("blah").isRelative());
    QVERIFY(!KUrl("http://www.kde.org").isRelative());
    QVERIFY(KUrl("foo/bar").isRelative());
}

void KUrlTest::testRelativePath()
{
    QString basePath = "/home/bastian";

    QCOMPARE(KUrl::relativePath(basePath, "/home/bastian"), QString("./"));
    bool b;
    QCOMPARE(KUrl::relativePath(basePath, "/home/bastian/src/plugins", &b), QString("./src/plugins"));
    QVERIFY(b);
    QCOMPARE(KUrl::relativePath(basePath, "./src/plugins"), QString("./src/plugins"));
    QCOMPARE(KUrl::relativePath(basePath, "/home/waba/src/plugins", &b), QString("../waba/src/plugins"));
    QVERIFY(!b);
    QCOMPARE(KUrl::relativePath(basePath, "/"), QString("../../"));

    QCOMPARE(KUrl::relativePath("/", "/"), QString("./"));
    QCOMPARE(KUrl::relativePath("/", "/home/bastian"), QString("./home/bastian"));
    QCOMPARE(KUrl::relativePath("", "/home/bastian"), QString("/home/bastian"));
}

void KUrlTest::testRelativeURL()
{
    KUrl baseURL("http://www.kde.org/index.html");
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("http://www.kde.org/index.html#help")), QString("#help"));
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("http://www.kde.org/index.html?help=true")), QString("index.html?help=true"));
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("http://www.kde.org/contact.html")), QString("contact.html"));
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("ftp://ftp.kde.org/pub/kde")), QString("ftp://ftp.kde.org/pub/kde"));
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("http://www.kde.org/index.html")), QString("./"));

    baseURL = "http://www.kde.org/info/index.html";
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("http://www.kde.org/bugs/contact.html")), QString("../bugs/contact.html"));

    baseURL = "ftp://ftp.kde.org/info/";
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("ftp://ftp.kde.org/info/contact.html")), QString("contact.html"));
    QCOMPARE(KUrl::relativeUrl(baseURL, KUrl("ftp://ftp.kde.org/info/A%20%20B.txt")), QString("A%20%20B.txt"));
}

void KUrlTest::testAdjustPath()
{
    KUrl url0("file:///");
    QCOMPARE(url0.url(KUrl::RemoveTrailingSlash), QString("file:///"));
    url0.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(url0.url(), QString("file:///"));

    KUrl url1("file:///home/kde/");
    url1.adjustPath(KUrl::LeaveTrailingSlash);
    QCOMPARE(url1.path(), QString("/home/kde/"));
    url1.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(url1.path(), QString("/home/kde"));
    url1.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(url1.path(), QString("/home/kde"));
    url1.adjustPath(KUrl::AddTrailingSlash);
    QCOMPARE(url1.path(), QString("/home/kde/"));

    KUrl url2("file:///home/kde//");
    url2.adjustPath(KUrl::LeaveTrailingSlash);
    QCOMPARE(url2.path(), QString("/home/kde//"));
    url2.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(url2.path(), QString("/home/kde"));
    url2.adjustPath(KUrl::AddTrailingSlash);
    QCOMPARE(url2.path(), QString("/home/kde/"));

    KUrl ftpurl1("ftp://ftp.kde.org/");
    ftpurl1.adjustPath(KUrl::LeaveTrailingSlash);
    QCOMPARE(ftpurl1.path(), QString("/"));
    ftpurl1.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(ftpurl1.path(), QString("/"));

    KUrl ftpurlNoPath("ftp://ftp.kde.org"); // #312060
    QCOMPARE(ftpurlNoPath.path(), QString());
    ftpurlNoPath.adjustPath(KUrl::LeaveTrailingSlash);
    QCOMPARE(ftpurlNoPath.path(), QString());
    ftpurlNoPath.adjustPath(KUrl::AddTrailingSlash);
    QCOMPARE(ftpurlNoPath.path(), QString());
    ftpurlNoPath.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(ftpurlNoPath.path(), QString());

    KUrl ftpurl2("ftp://ftp.kde.org///");
    ftpurl2.adjustPath(KUrl::LeaveTrailingSlash);
    QCOMPARE(ftpurl2.path(), QString("///"));
    ftpurl2.adjustPath(KUrl::RemoveTrailingSlash); // should remove all but trailing slash
    QCOMPARE(ftpurl2.path(), QString("/"));
    ftpurl2.adjustPath(KUrl::AddTrailingSlash);
    QCOMPARE(ftpurl2.path(), QString("/"));

    // Equivalent tests written by the KDirLister maintainer :)

    KUrl u3(QByteArray("ftp://brade@ftp.kde.org///"));
    u3.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(u3.url(), QString("ftp://brade@ftp.kde.org/"));

    KUrl u4(QByteArray("ftp://brade@ftp.kde.org/kde///"));
    u4.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(u4.url(), QString("ftp://brade@ftp.kde.org/kde"));

    // applying adjustPath(KUrl::RemoveTrailingSlash) twice should not yield two different urls
    // (follows from the above test)
    KUrl u5 = u4;
    u5.adjustPath(KUrl::RemoveTrailingSlash);
    QCOMPARE(u5.url(), u4.url());

    {
        KUrl remote("remote:/");
        QCOMPARE(remote.url(KUrl::RemoveTrailingSlash), QString("remote:/"));
        remote.adjustPath(KUrl::RemoveTrailingSlash);
        QCOMPARE(remote.url(), QString("remote:/"));
        remote.adjustPath(KUrl::RemoveTrailingSlash);
        QCOMPARE(remote.url(), QString("remote:/"));
    }

    {
        KUrl remote2("remote://");
        QCOMPARE(remote2.url(), QString("remote://"));
        QCOMPARE(remote2.url(KUrl::RemoveTrailingSlash), QString("remote://"));
        QCOMPARE(QUrl(remote2).toString(QUrl::StripTrailingSlash), QString("remote://"));
    }
}

void KUrlTest::testIPV6()
{
    // IPV6
    KUrl waba1("http://[::FFFF:129.144.52.38]:81/index.html");
    QCOMPARE(waba1.host(), QString("::ffff:129.144.52.38"));
    QCOMPARE(waba1.port(), 81);

    // IPV6
    waba1 = "http://waba:pass@[::FFFF:129.144.52.38]:81/index.html";
    QCOMPARE(waba1.host(), QString("::ffff:129.144.52.38"));
    QCOMPARE(waba1.user(), QString("waba"));
    QCOMPARE(waba1.pass(), QString("pass"));
    QCOMPARE(waba1.port(), 81);

    // IPV6
    waba1 = "http://www.kde.org/cgi/test.cgi";
    waba1.setHost("::ffff:129.144.52.38");
    QCOMPARE(waba1.url(), QString("http://[::ffff:129.144.52.38]/cgi/test.cgi"));
    waba1 = "http://[::ffff:129.144.52.38]/cgi/test.cgi";
    QVERIFY(waba1.isValid());

    // IPV6 without path
    waba1 = "http://[::ffff:129.144.52.38]?query";
    QVERIFY(waba1.isValid());
    QCOMPARE(waba1.url(), QString("http://[::ffff:129.144.52.38]?query"));
    QCOMPARE(waba1.query(), QString("?query"));
    waba1 = "http://[::ffff:129.144.52.38]#ref";
    QVERIFY(waba1.isValid());
    QCOMPARE(waba1.url(), QString("http://[::ffff:129.144.52.38]#ref"));
    QCOMPARE(waba1.ref(), QString("ref"));
    // IPV6 without path but with a port
    waba1 = "http://[::ffff:129.144.52.38]:81?query";
    QVERIFY(waba1.isValid());
    QCOMPARE(waba1.url(), QString("http://[::ffff:129.144.52.38]:81?query"));
    QCOMPARE(waba1.port(), 81);
    QCOMPARE(waba1.query(), QString("?query"));
    waba1 = "http://[::ffff:129.144.52.38]:81#ref";
    QVERIFY(waba1.isValid());
    QCOMPARE(waba1.url(), QString("http://[::ffff:129.144.52.38]:81#ref"));
    QCOMPARE(waba1.port(), 81);
    QCOMPARE(waba1.ref(), QString("ref"));

    KUrl weird("http://[::fff:1:23]/");
    QVERIFY(weird.isValid());
    QCOMPARE(weird.host(), QString("::fff:1:23"));
}

void KUrlTest::testBaseURL() // those are tests for the KUrl(base,relative) constructor
{
    KUrl com1(KUrl("http://server.com/dir/"), ".");
    QCOMPARE(com1.url(), QString("http://server.com/dir/"));

    KUrl com2(KUrl("http://server.com/dir/blubb/"), "blah/");
    QCOMPARE(com2.url(), QString("http://server.com/dir/blubb/blah/"));

    KUrl baseURL("http://www.foo.bar:80");
    QVERIFY(baseURL.isValid());
    QCOMPARE(baseURL.protocol(), QString("http"));     // lowercase
    QCOMPARE(baseURL.port(), 80);

    QString relativeUrl = "//www1.foo.bar";
    QVERIFY(KUrl::isRelativeUrl(relativeUrl));

    // Mimick what KUrl(2 urls) does:
    QUrl qurl("http://www.foo.bar:80");
    QCOMPARE(QString::fromLatin1(qurl.toEncoded()), QString::fromLatin1("http://www.foo.bar:80"));
    QCOMPARE(qurl.port(), 80);

    qurl.setHost(QString());
    qurl.setPath(QString());
    QCOMPARE(qurl.port(), 80);
    QCOMPARE(QString(qurl.toEncoded()), QString("http:"));   // a port without a host isn't really useful
    qurl.setPort(-1);
    QCOMPARE(QString(qurl.toEncoded()), QString("http:"));   // hmm we have no '//' anymore

    KUrl url1(baseURL, relativeUrl);
    QCOMPARE(url1.url(), QString("http://www1.foo.bar"));
    QCOMPARE(url1.host(), QString("www1.foo.bar"));

    baseURL = "http://www.foo.bar";
    KUrl rel_url(baseURL, "/top//test/../test1/file.html");
    QCOMPARE(rel_url.url(), QString("http://www.foo.bar/top//test1/file.html"));

    baseURL = "http://www.foo.bar/top//test2/file2.html";
    QCOMPARE(baseURL.url(), QString("http://www.foo.bar/top//test2/file2.html"));

    baseURL = "file:/usr/local/src/kde2/////kdelibs/kio";
    QCOMPARE(baseURL.url(), QString("file:///usr/local/src/kde2/////kdelibs/kio"));

    baseURL = "http://www.foo.bar";
    KUrl rel_url2(baseURL, "mailto:bastian@kde.org");
    QCOMPARE(rel_url2.url(), QString("mailto:bastian@kde.org"));

    baseURL = "file:/usr/local/src/kde2/kdelibs/kio/";
    KUrl url2(baseURL, "../../////kdebase/konqueror");
    QCOMPARE( url2.url(), QString("file:///usr/local/src/kde2/////kdebase/konqueror" ));


    // WABA: The following tests are to test the handling of relative URLs as
    //       found on web-pages.

    KUrl waba1("http://www.website.com/directory/?hello#ref");
    {
        KUrl waba2(waba1, "relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html"));
    }
    {
        KUrl waba2(waba1, "../relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/relative.html"));
    }
    {
        KUrl waba2(waba1, "down/relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/down/relative.html"));
    }
    {
        KUrl waba2(waba1, "/down/relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/down/relative.html"));
    }
    {
        KUrl waba2(waba1, "//www.kde.org/relative.html");
        QCOMPARE(waba2.url(), QString("http://www.kde.org/relative.html"));
    }
    {
        KUrl waba2(waba1, "relative.html?query=test&name=harry");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html?query=test&name=harry"));
        waba2.removeQueryItem("query");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html?name=harry"));
        waba2.addQueryItem("age", "18");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html?name=harry&age=18"));
        waba2.addQueryItem("age", "21");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html?name=harry&age=18&age=21"));
        waba2.addQueryItem("fullname", "Harry Potter");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html?name=harry&age=18&age=21&fullname=Harry%20Potter"));
    }
    {
        KUrl waba2(waba1, "?query=test&name=harry");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/?query=test&name=harry"));
    }
    {
        KUrl waba2(waba1, "relative.html#with_reference");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html#with_reference"));
    }

    {
        KUrl waba2(waba1, "#");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/?hello#"));
    }
    {
        KUrl waba2(waba1, "");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/?hello#ref"));
    }
    {
        KUrl waba2(waba1, "#%72%22method");  // #243217
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/?hello#r%22method"));
    }
    {
        KUrl base("http://faure@www.kde.org");   // no path
        KUrl waba2(base, "filename.html");
        QCOMPARE(waba2.url(), QString("http://faure@www.kde.org/filename.html"));
    }
    {
        KUrl base("http://faure:pass@www.kde.org:81?query");
        KUrl rel1(base, "http://www.kde.org/bleh/");  // same host
        QCOMPARE(rel1.url(), QString("http://faure:pass@www.kde.org/bleh/"));
        KUrl rel2(base, "http://www.yahoo.org");  // different host
        QCOMPARE(rel2.url(), QString("http://www.yahoo.org"));
    }

    waba1 = "http://www.website.com/directory/filename?bla#blub";
    {
        KUrl waba2(waba1, "relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html"));
    }
    {
        KUrl waba2(waba1, "./relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html"));
    }
    {
        KUrl waba2(waba1, "../relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/relative.html"));
    }
    {
        KUrl waba2(waba1, "down/relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/down/relative.html"));
    }
    {
        KUrl waba2(waba1, "down/./relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/down/relative.html"));
    }
    {
        KUrl waba2(waba1, "/down/relative.html");
        QCOMPARE(waba2.url(), QString("http://www.website.com/down/relative.html"));
    }
    {
        KUrl waba2(waba1, "relative.html?query=test&name=harry");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html?query=test&name=harry"));
    }
    {
        KUrl waba2(waba1, "?query=test&name=harry");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/filename?query=test&name=harry"));
    }
    {
        KUrl waba2(waba1, "relative.html#with_reference");
        QCOMPARE(waba2.url(), QString("http://www.website.com/directory/relative.html#with_reference"));
    }
    {
        KUrl waba2(waba1, "http:/relative.html");  // "rfc 1606 loophole"
        QCOMPARE(waba2.url(), QString("http://www.website.com/relative.html"));
    }
    waba1.setUser("waldo");
    QCOMPARE(waba1.url(), QString("http://waldo@www.website.com/directory/filename?bla#blub"));
    waba1.setUser("waldo/bastian");
    QCOMPARE(waba1.url(), QString("http://waldo%2Fbastian@www.website.com/directory/filename?bla#blub"));
    waba1.setRef(QString());
    waba1.setPass("pass");
    waba1.setDirectory("/foo");
    waba1.setProtocol("https");
    waba1.setHost("web.com");
    waba1.setPort(881);
    QCOMPARE(waba1.url(), QString("https://waldo%2Fbastian:pass@web.com:881/foo/?bla"));
    waba1.setDirectory("/foo/");
    QCOMPARE(waba1.url(), QString("https://waldo%2Fbastian:pass@web.com:881/foo/?bla"));

    QUrl sadEagleTest;
    sadEagleTest.setEncodedUrl("http://www.calorieking.com/foo.php?P0=[2006-3-8]", QUrl::TolerantMode);
    QVERIFY(sadEagleTest.isValid());
    KUrl sadEagleExpectedResult("http://www.calorieking.com/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458");
    QVERIFY(sadEagleExpectedResult.isValid());
    KUrl sadEagleBase("http://www.calorieking.com/personal/diary/");
    QVERIFY(sadEagleBase.isValid());
    KUrl sadEagleCombined(sadEagleBase, "/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458");
    QCOMPARE(sadEagleCombined.url(), sadEagleExpectedResult.url());

    KUrl dxOffEagle(KUrl("http://something/other.html"), "newpage.html?[{\"foo: bar\"}]");
    QVERIFY(dxOffEagle.isValid());
    QCOMPARE(dxOffEagle.url(), QString("http://something/newpage.html?[%7B%22foo:%20bar%22%7D]"));
    QCOMPARE(dxOffEagle.prettyUrl(), QString("http://something/newpage.html?[%7B%22foo:%20bar%22%7D]"));

    // QtSw issue 243557
    QByteArray tsdgeos("http://google.com/c?c=Translation+%C2%BB+trunk|");
    QUrl tsdgeosQUrl;
    tsdgeosQUrl.setEncodedUrl(tsdgeos, QUrl::TolerantMode);
    QVERIFY(tsdgeosQUrl.isValid()); // failed in Qt-4.4, works in Qt-4.5
    QByteArray tsdgeosExpected("http://google.com/c?c=Translation+%C2%BB+trunk%7C");
    //QCOMPARE(tsdgeosQUrl.toEncoded(), tsdgeosExpected); // unusable output from qtestlib...
    QCOMPARE(QString(tsdgeosQUrl.toEncoded()), QString(tsdgeosExpected));

    KUrl tsdgeosUrl(tsdgeos);
    QCOMPARE(tsdgeosUrl.url(), QString(tsdgeosExpected));

    QByteArray pipesAgain("http://translate.google.com/translate_t#en|uk|demo");
    QUrl pipesUrl;
    pipesUrl.setEncodedUrl(pipesAgain, QUrl::TolerantMode);
    QVERIFY(pipesUrl.isValid());
    QCOMPARE(QString(pipesUrl.toEncoded()), QString("http://translate.google.com/translate_t#en%7Cuk%7Cdemo"));

    // Shows up in nspluginviewer/flash
    QString flashRel = "javascript:window.location+\"__flashplugin_unique__\"";
    KUrl flashUrl(flashRel);
    QVERIFY(flashUrl.isValid());
    KUrl flashBase("http://www.youtube.com/?v=JvOSnRD5aNk");
    KUrl flashComposed(flashBase, flashRel);
    QCOMPARE(flashComposed.url(), QString("javascript:window.location+%22__flashplugin_unique__%22"));
}

void KUrlTest::testSetEncodedFragment_data()
{
    QTest::addColumn<QByteArray>("base");
    QTest::addColumn<QByteArray>("fragment");
    QTest::addColumn<QByteArray>("expected");
    typedef QByteArray BA;
    QTest::newRow("basic test") << BA("http://www.kde.org") << BA("abc") << BA("http://www.kde.org#abc");
    QTest::newRow("initial url has fragment") << BA("http://www.kde.org#old") << BA("new") << BA("http://www.kde.org#new");
    QTest::newRow("encoded fragment") << BA("http://www.kde.org") << BA("a%20c") << BA("http://www.kde.org#a%20c");
    QTest::newRow("with #") << BA("http://www.kde.org") << BA("a#b") << BA("http://www.kde.org#a%23b"); // see c615dcc4416 in qtbase
    QTest::newRow("empty") << BA("http://www.kde.org") << BA("") << BA("http://www.kde.org#");
}

void KUrlTest::testSetEncodedFragment()
{
    // Bug fixed in 4.5.1 by Thiago
#if QT_VERSION < 0x040501
    QSKIP("Bug in Qt-4.4/4.5-rc1: setEncodedFragment doesn't work if the initial url has no fragment");
#endif

    QFETCH(QByteArray, base);
    QFETCH(QByteArray, fragment);
    QFETCH(QByteArray, expected);
    QUrl u;
    u.setEncodedUrl(base, QUrl::TolerantMode);
    QVERIFY(u.isValid());
    u.setEncodedFragment(fragment);
    QVERIFY(u.isValid());
    QCOMPARE(QString::fromLatin1(u.toEncoded()), QString::fromLatin1(expected));
}

void KUrlTest::testSubURL()
{
    QString u1 = "file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/#myref";
    KUrl url1(u1);
    // KDE3: was #,#,#, Qt-4.0 to 4.4: #,%23,%23 . 4.5, 5.0, 5.1: #,#,#
    // Qt 5.2: #, %23, %23, see c615dcc441 in qtbase for the reasoning. We don't use sub urls anymore anyway.
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my%20tar%20file.tgz#gzip:/%23tar:/%23myref"));
    QVERIFY(url1.hasRef());
    QVERIFY(!url1.isLocalFile());    // Not strictly local!
    QVERIFY(url1.hasSubUrl());
    //QCOMPARE( url1.htmlRef(), QString("myref") );
    QCOMPARE(url1.upUrl().url(), QString("file:///home/dfaure/"));

    const KUrl::List splitList = KUrl::split(url1);
    QCOMPARE(splitList.count(), 3);
    //kDebug() << splitList.toStringList();
    QCOMPARE(splitList[0].url(), QString("file:///home/dfaure/my%20tar%20file.tgz#myref"));
    QCOMPARE(splitList[1].url(), QString("gzip:/#myref"));
    QCOMPARE(splitList[2].url(), QString("tar:/#myref"));

    KUrl rejoined = KUrl::join(splitList);
    QCOMPARE(rejoined.url(), url1.url());

    u1 = "error:/?error=14&errText=Unknown%20host%20asdfu.adgi.sdfgoi#http://asdfu.adgi.sdfgoi";
    url1 = u1;
    QCOMPARE(url1.url(), QString("error:/?error=14&errText=Unknown%20host%20asdfu.adgi.sdfgoi#http://asdfu.adgi.sdfgoi"));
    QVERIFY(url1.hasSubUrl());
    QVERIFY(url1.hasRef());
    QVERIFY(!url1.isLocalFile());
    QVERIFY(!url1.hasHTMLRef());

    u1 = "file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my%20tar%20file.tgz#gzip:/%23tar:/"));
    QVERIFY(url1.hasRef());
    QVERIFY(!url1.hasHTMLRef());
    QVERIFY(url1.hasSubUrl());
    QVERIFY(url1.htmlRef().isNull());
    QCOMPARE(url1.upUrl().url(), QString("file:///home/dfaure/"));

    u1 = "file:///home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my%20tar%20file.tgz#gzip:/%23tar:/"));
    QVERIFY(url1.hasRef());
    QVERIFY(!url1.hasHTMLRef());
    QVERIFY(url1.hasSubUrl());
    QVERIFY(url1.htmlRef().isNull());
    QCOMPARE(url1.upUrl().url(), QString("file:///home/dfaure/"));

    u1 = "file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/README";
    url1 = u1;
    QCOMPARE(url1.url(), QString("file:///home/dfaure/my%20tar%20file.tgz#gzip:/%23tar:/README"));
    QVERIFY(url1.hasRef());
    QVERIFY(!url1.hasHTMLRef());
    QVERIFY(url1.hasSubUrl());
    QVERIFY(url1.htmlRef().isNull());
    const KUrl::List url1Splitted = KUrl::split(url1);
    QCOMPARE(url1Splitted.count(), 3);
    //kDebug() << url1Splitted.toStringList();
    QCOMPARE(url1Splitted[0].url(), QString("file:///home/dfaure/my%20tar%20file.tgz"));
    QCOMPARE(url1Splitted[1].url(), QString("gzip:/"));
    QCOMPARE(url1Splitted[2].url(), QString("tar:/README"));
    const KUrl url1Rejoined = KUrl::join(url1Splitted);
    QCOMPARE(url1Rejoined.url(), url1.url());
    QCOMPARE(url1.upUrl().url(), QString("file:///home/dfaure/my%20tar%20file.tgz#gzip:/%23tar:/"));

}

void KUrlTest::testSetUser()
{
    // The KUrl equality test below works because in Qt4 null == empty.
    QString str1;
    QString str2 = "";
    QVERIFY(str1 == str2);

    KUrl emptyUserTest1("http://www.foobar.com/");
    QVERIFY(emptyUserTest1.user().isEmpty());
    QVERIFY(emptyUserTest1.user().isNull());   // Expected result. This was fixed in Qt-4.4

    KUrl emptyUserTest2("http://www.foobar.com/");
    emptyUserTest2.setUser("");
    //QVERIFY( emptyUserTest2.user().isNull() );
    // Qt 5 allows a "set but empty" username, as per the RFC.
    QCOMPARE(emptyUserTest2.url(), QString("http://@www.foobar.com/"));
    QVERIFY(emptyUserTest1 != emptyUserTest2);

    // Parsing back:
    QUrl u("http://@kde.org/");
    QVERIFY(u.userName().isEmpty());
    QVERIFY(!u.userName().isNull());
    QVERIFY(u.password().isNull());
    emptyUserTest2.setUser("foo");
    QCOMPARE(emptyUserTest2.user(), QString::fromLatin1("foo"));
    emptyUserTest2.setUser(QString());
    QVERIFY(emptyUserTest1 == emptyUserTest2);

    KUrl emptyUserTest3("http://www.foobar.com/");
    emptyUserTest3.setPass("");
    // empty password means empty user, fixed in Qt5
    QCOMPARE(emptyUserTest3.url(), QString("http://:@www.foobar.com/"));
    QVERIFY(emptyUserTest1 != emptyUserTest3);

    KUrl uga("ftp://ftp.kde.org");
    uga.setUser("foo@bar");
    QCOMPARE(uga.user(), QString::fromLatin1("foo@bar"));
    QCOMPARE(uga.url(), QString::fromLatin1("ftp://foo%40bar@ftp.kde.org"));
}

void KUrlTest::testComparisons()
{
    /* QUrl version of urlcmp */
    QUrl u1("ftp://ftp.de.kde.org/dir");
    QUrl u2("ftp://ftp.de.kde.org/dir/");
    QUrl::FormattingOptions options = QUrl::None;
    options |= QUrl::StripTrailingSlash;
    QString str1 = u1.toString(options);
    QString str2 = u2.toString(options);
    QCOMPARE(str1, u1.toString());
    QCOMPARE(str2, u1.toString());
    bool same = str1 == str2;
    QVERIFY(same);

    QString ucmp1 = "ftp://ftp.de.kde.org/dir";
    QString ucmp2 = "ftp://ftp.de.kde.org/dir/";
#ifndef KDELIBS4SUPPORT_NO_DEPRECATED
    QVERIFY(!urlcmp(ucmp1, ucmp2));
    QVERIFY(urlcmp(ucmp1, ucmp2, KUrl::CompareWithoutTrailingSlash)); //only slash difference, ignore_trailing
#endif

    {
        KUrl u1(ucmp1);
        KUrl u2(ucmp2);
        QVERIFY(!u1.equals(u2));
        QVERIFY(u1.equals(u2, KUrl::CompareWithoutTrailingSlash));
        QVERIFY(u1.equals(u2, KUrl::CompareWithoutTrailingSlash | KUrl::AllowEmptyPath));
    }

    // Special case: no path vs '/'.
    {
        QString str1 = QString::fromLatin1("ftp://ftp.de.kde.org");
        QString str2 = QString::fromLatin1("ftp://ftp.de.kde.org/");
#ifndef KDELIBS4SUPPORT_NO_DEPRECATED
        QVERIFY(!urlcmp(str1, str2));
        QVERIFY(!urlcmp(str1, str2, KUrl::CompareWithoutTrailingSlash)); // empty path != '/'
        QVERIFY(urlcmp(str1, str2, KUrl::CompareWithoutTrailingSlash | KUrl::AllowEmptyPath));
#endif
        KUrl u1(str1);
        KUrl u2(str2);
        QVERIFY(!u1.equals(u2));
        QVERIFY(!u1.equals(u2, KUrl::CompareWithoutTrailingSlash));
        QVERIFY(u1.equals(u2, KUrl::CompareWithoutTrailingSlash | KUrl::AllowEmptyPath));
    }

    QString ucmp3 = "ftp://ftp.de.kde.org/dir/#";
#ifndef KDELIBS4SUPPORT_NO_DEPRECATED
    QVERIFY(!urlcmp(ucmp2, ucmp3));  // (only hash difference)
    QVERIFY(urlcmp(ucmp2, ucmp3, KUrl::CompareWithoutFragment)); // (only hash difference, ignore_ref)
    QVERIFY(urlcmp(ucmp2, ucmp3, KUrl::CompareWithoutTrailingSlash | KUrl::CompareWithoutFragment)); // (slash and hash difference, ignore_trailing, ignore_ref)
    QVERIFY(urlcmp("", "", KUrl::CompareWithoutFragment)); // (empty, empty)
    QVERIFY(urlcmp("", ""));  // (empty, empty)
    QVERIFY(!urlcmp("", ucmp1));  // (empty, not empty)
    QVERIFY(!urlcmp("", ucmp1, KUrl::CompareWithoutFragment)); // (empty, not empty)
    QVERIFY(!urlcmp("file", ucmp1));  // (malformed, not empty)
    QVERIFY(!urlcmp("file", ucmp1, KUrl::CompareWithoutFragment)); // (malformed, not empty)
#endif

    KUrl ftpUrl("ftp://ftp.de.kde.org");
    QCOMPARE(ftpUrl.path(), QString());
    ftpUrl = "ftp://ftp.de.kde.org/";
    QVERIFY(ftpUrl.isParentOf(KUrl("ftp://ftp.de.kde.org/host/subdir/")));
    ftpUrl = "ftp://ftp/host/subdir/";
    QVERIFY(ftpUrl.isParentOf(KUrl("ftp://ftp/host/subdir/")));
    QVERIFY(ftpUrl.isParentOf(KUrl("ftp://ftp/host/subdir")));
    QVERIFY(!ftpUrl.isParentOf(KUrl("ftp://ftp/host/subdi")));
    QVERIFY(ftpUrl.isParentOf(KUrl("ftp://ftp/host/subdir/blah/")));
    QVERIFY(!ftpUrl.isParentOf(KUrl("ftp://ftp/blah/subdir")));
    QVERIFY(!ftpUrl.isParentOf(KUrl("file:////ftp/host/subdir/")));
    QVERIFY(ftpUrl.isParentOf(KUrl("ftp://ftp/host/subdir/subsub")));
}

void KUrlTest::testStreaming_data()
{
    QTest::addColumn<QString>("urlStr");

    QTest::newRow("origURL") << "http://www.website.com/directory/?#ref";
    QTest::newRow("urlWithPassAndNoUser") << "ftp://:password@ftp.kde.org/path";
    QTest::newRow("accentuated") << QString::fromUtf8("trash:/été");
    QTest::newRow("empty") << "";
    QVERIFY(!KUrl("ptal://mlc:usb").isValid());
    QTest::newRow("invalid") << "ptal://mlc:usb";
    QTest::newRow("ipv6") << "http://[::ffff:129.144.52.38]:81?query";
}

void KUrlTest::testStreaming()
{
    QFETCH(QString, urlStr);
    KUrl url(urlStr);

    QByteArray buffer;
    QDataStream writeStream(&buffer, QIODevice::WriteOnly);
    writeStream << url;

    QDataStream stream(buffer);
    KUrl restored;
    stream >> restored; // streaming valid url
    QCOMPARE(restored.isValid(), url.isValid());
    if (url.isValid()) {
        QCOMPARE(restored.url(), url.url());
    }
}

void KUrlTest::testBrokenStuff()
{
    // Broken stuff
    KUrl waba1("file:a");
    QCOMPARE(waba1.path(), QString("a"));
    QCOMPARE(waba1.fileName(KUrl::ObeyTrailingSlash), QString("a"));
    QCOMPARE(waba1.fileName(), QString("a"));
    QCOMPARE(waba1.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash), QString(""));
    QCOMPARE(waba1.directory(KUrl::ObeyTrailingSlash), QString(""));
    QCOMPARE(waba1.directory(), QString(""));

    waba1 = "file:a/";
    QCOMPARE(waba1.path(), QString("a/"));
    QCOMPARE(waba1.fileName(KUrl::ObeyTrailingSlash), QString(""));
    QCOMPARE(waba1.fileName(), QString("a"));
    QCOMPARE(waba1.directory(KUrl::ObeyTrailingSlash | KUrl::AppendTrailingSlash), QString("a/"));
    QCOMPARE(waba1.directory(KUrl::ObeyTrailingSlash), QString("a"));
    QCOMPARE(waba1.directory(), QString(""));

    waba1 = "file:";
    QVERIFY(!waba1.isEmpty());
    QVERIFY(waba1.isValid());   // KDE3: was invalid. Now it's qurl with scheme="file".
    QCOMPARE(waba1.path(), QString(""));
    QCOMPARE(waba1.fileName(KUrl::ObeyTrailingSlash), QString(""));
    QCOMPARE(waba1.fileName(), QString(""));
    QCOMPARE(waba1.directory(KUrl::ObeyTrailingSlash | KUrl::AppendTrailingSlash), QString(""));
    QCOMPARE(waba1.directory(KUrl::AppendTrailingSlash), QString(""));
    QCOMPARE(waba1.directory(), QString(""));
    KUrl broken;
    broken.setPath(QString());
    QVERIFY(!broken.isEmpty());
    // It's valid: because isValid refers to parsing, not to what happens afterwards.
    QVERIFY(broken.isValid());
    QCOMPARE(broken.path(), QString(""));
    broken = "file://"; // just because coolo wondered
    QVERIFY(!broken.isEmpty());
    QVERIFY(broken.isValid());   // KDE3: was invalid; same as above
    QCOMPARE(broken.path(), QString(""));
    broken = "file";
    QVERIFY(broken.isValid());   // KDE3: was invalid; now it's path="file"

#if 0
    // KUrl has a Q_ASSERT on this now, so we can't test it.
    broken = "/";
    QVERIFY(broken.isValid());
    QCOMPARE(broken.path(), QString("/"));
    QCOMPARE(broken.url(), QString("/"));   // KDE3: was resolved to "file:///". QUrl supports urls without a protocol.
    QCOMPARE(broken.protocol(), QString(""));   // KDE3: was "file"
#endif

    {
        QUrl url;
        url.setEncodedUrl("LABEL=USB_STICK", QUrl::TolerantMode);
        QVERIFY(url.isValid());
        QCOMPARE(url.path(), QString("LABEL=USB_STICK"));
        QVERIFY(!url.isEmpty());
    }
    {
        QUrl url;
        url.setEncodedUrl("LABEL=USB_STICK", QUrl::TolerantMode);
        QVERIFY(url.isValid());
        QVERIFY(!url.isEmpty());   // Qt-4.4-snapshot20080213 bug, reported to TT
        QCOMPARE(url.path(), QString("LABEL=USB_STICK"));
    }

    broken = "LABEL=USB_STICK"; // 71430, can we use KUrl for this?
    QVERIFY(broken.isValid());   // KDE3 difference: QUrl likes this one too
    QVERIFY(!broken.isEmpty());
    QCOMPARE(broken.path(), QString("LABEL=USB_STICK"));   // was "" in KDE3
}

void KUrlTest::testMoreBrokenStuff()
{
#if 0 // BROKEN?
    // UNC like names
    KUrl unc1("FILE://localhost/home/root");
    QCOMPARE(unc1.path(), QString("/home/root"));
    QCOMPARE(unc1.url(), QString("file:///home/root"));
#endif
    KUrl unc2("file:///home/root"); // with empty host
    QCOMPARE(unc2.path(), QString("/home/root"));
    QCOMPARE(unc2.url(), QString("file:///home/root"));

    {
        KUrl unc3("FILE://remotehost/home/root");
#if 0 // BROKEN?
        QCOMPARE(unc3.path(), QString("//remotehost/home/root"));
#endif
        QCOMPARE(unc3.url(), QString("file://remotehost/home/root"));   // kde3, qt5: lowercase. kde4/qt4: uppercase.
        KUrl url2("file://atlas/dfaure");
        QCOMPARE(url2.host(), QString("atlas"));
        QCOMPARE(url2.path(), QString("/dfaure"));
        //QCOMPARE( url3.path(), QString("//atlas/dfaure")); // says Waba
        //KUrl url3("file:////atlas/dfaure");
        //QCOMPARE( url3.path(), QString("//atlas/dfaure")); // says Waba

        KUrl url4(url2, "//remotehost/home/root");
        QCOMPARE(url4.host(), QString("remotehost"));
        QCOMPARE(url4.path(), QString("/home/root"));
    }

    KUrl weird;
    weird = "http://strange<hostname>/";
    QVERIFY(!weird.isValid());

    weird = "http://strange<username>@strange<hostname>/";
    QVERIFY(!weird.isValid());

    {
        QUrl url;
        url.setUrl("http://strange<username>@hostname/", QUrl::TolerantMode);
        QVERIFY(url.isValid());
    }
    weird = "http://strange<username>@hostname/";
    QVERIFY(weird.isValid());   // KDE3: was valid. Fixed by _setEncodedUrl.
    QCOMPARE(weird.host(), QString("hostname"));

    weird = "http://strange;hostname/";
    QVERIFY(!weird.isValid());

    weird = "http://strange;username@strange;hostname/";
    QVERIFY(!weird.isValid());

    weird = "http://strange;username@hostname/";
    QVERIFY(weird.isValid());
    QCOMPARE(weird.host(), QString("hostname"));

    weird = "http://strange;username:password@strange;hostname/";
    QVERIFY(!weird.isValid());

    weird = "http://strange;username:password@hostname/";
    QVERIFY(weird.isValid());
    QCOMPARE(weird.host(), QString("hostname"));

    weird = "http://[strange;hostname]/";
    QVERIFY(!weird.isValid());

    weird = "ssh://user@machine?cmd='echo $HOSTNAME'";
    QVERIFY(weird.isValid());
    QCOMPARE(weird.host(), QString("machine"));
    //qDebug("%s",qPrintable( weird.query() ) );
    QCOMPARE(weird.queryItem("cmd"), QString("'echo $HOSTNAME'"));

    weird = ":pictures"; // for KFileDialog's startDir, long ago. Nowadays it uses a proper URL, kfiledialog:///pictures
    QVERIFY(!weird.isValid());
    QVERIFY(weird.protocol().isEmpty());
    QVERIFY(weird.host().isEmpty());
    QCOMPARE(weird.path(), QString(":pictures"));
    QCOMPARE(weird.url(), QString());

    weird = "::keyword"; // for KFileDialog's startDir, long ago.
    QVERIFY(!weird.isValid());
    QVERIFY(weird.protocol().isEmpty());
    QVERIFY(weird.host().isEmpty());
    QCOMPARE(weird.path(), QString("::keyword"));
    QCOMPARE(weird.url(), QString());

    KUrl broken;
    broken = "ptal://mlc:usb:PC_970";
    QVERIFY(!broken.isValid());
    QVERIFY(!broken.errorString().isEmpty());
    //QCOMPARE( broken.url(), QString("ptal://mlc:usb:PC_970") ); // QUrl doesn't provide the initial string if it's an invalid url
    QUrl brokenUrl("ptal://mlc:usb:PC_970");
    QVERIFY(!brokenUrl.isValid());

    QUrl dxOffEagle("http://something/newpage.html?[{\"foo: bar\"}]", QUrl::TolerantMode);
    QVERIFY(dxOffEagle.isValid());
    QCOMPARE(QString(dxOffEagle.toEncoded()), QString("http://something/newpage.html?[%7B%22foo:%20bar%22%7D]"));
    QUrl dxOffEagle2;
    dxOffEagle2.setUrl("http://something/newpage.html?[{\"foo: bar\"}]", QUrl::TolerantMode);
    QVERIFY(dxOffEagle2.isValid());
    QCOMPARE(dxOffEagle.toEncoded(), dxOffEagle2.toEncoded());

    QUrl dxOffEagle3;
    dxOffEagle3.setEncodedUrl("http://something/newpage.html?[{\"foo: bar\"}]", QUrl::TolerantMode);
#if QT_VERSION < 0x040500
    QEXPECT_FAIL("", "Issue N183630, task ID 183874; works with setUrl so we do that in _setEncodedUrl now", Continue);
#endif
    QVERIFY(dxOffEagle3.isValid());
    QCOMPARE(dxOffEagle.toEncoded(), dxOffEagle3.toEncoded());

    QUrl javascript;
    javascript.setUrl("javascript:window.location+\"__flashplugin_unique__\"", QUrl::TolerantMode);
    QVERIFY(javascript.isValid());
    javascript.setEncodedUrl("javascript:window.location+\"__flashplugin_unique__\"", QUrl::TolerantMode);
#if QT_VERSION < 0x040500
    QEXPECT_FAIL("", "Issue N183630, task ID 183874", Continue);
#endif
    QVERIFY(javascript.isValid());
}

void KUrlTest::testMailto()
{
    const QString faure = "faure@kde.org";
    const QString mailtoFaure = "mailto:" + faure;
    KUrl umail1(mailtoFaure);
    QCOMPARE(umail1.protocol(), QString("mailto"));
    QCOMPARE(umail1.path(), QString(faure));
    QVERIFY(!KUrl::isRelativeUrl(mailtoFaure));

#if 0 // TODO in Qt
    // Make sure populateMimeData() works correct:
    // 1. the text/plain part of the mimedata should not contain the mailto: part
    // 2. the uri-list part of the mimedata should contain the mailto: part
    QMimeData md;
    umail1.populateMimeData(&md);
    QCOMPARE(md.text(), faure);
    KUrl::List uriList = KUrl::List::fromMimeData(&md);
    QCOMPARE(uriList.size(), 1);
    KUrl first = uriList.first();
    QCOMPARE(first.protocol(), QString("mailto"));
    QCOMPARE(first.path(), faure);
#endif

    KUrl mailtoOnly("mailto:");
    QVERIFY(mailtoOnly.isValid());   // KDE3 said invalid, QUrl is more tolerant

    KUrl url1("mailto:user@host.com");
    QCOMPARE(url1.url(), QString("mailto:user@host.com"));
    QCOMPARE(url1.url(KUrl::LeaveTrailingSlash), QString("mailto:user@host.com"));

    KUrl mailtoUrl("mailto:null@kde.org?subject=hello");
    QCOMPARE(mailtoUrl.url(), QString("mailto:null@kde.org?subject=hello"));

    QUrl qurl("mailto:null@kde.org?subject=hello#world"); // #80165: is #world part of fragment or query? RFC-3986 says: fragment.
    QCOMPARE(QString::fromLatin1(qurl.encodedQuery()), QString("subject=hello"));

    {
        QUrl mailtoUrl;
        mailtoUrl.setScheme("mailto");
        mailtoUrl.setPath("a%b");
        QCOMPARE(mailtoUrl.path(), QString("a%b"));
        QCOMPARE(mailtoUrl.path(QUrl::FullyEncoded), QString("a%25b"));
        QCOMPARE(mailtoUrl.toString(), QString("mailto:a%25b"));
        QCOMPARE(QString::fromLatin1(mailtoUrl.toEncoded()), QString::fromLatin1("mailto:a%25b"));
    }
    {
        KUrl mailtoUrl;
        mailtoUrl.setScheme("mailto");
        mailtoUrl.setPath("a%b");
        QCOMPARE(mailtoUrl.url(), QString("mailto:a%25b")); // KUrl takes care of not changing behavior with Qt5.
    }

#if 0
    // I wrote this test in the very first kurltest, but there's no proof that it's actually valid.
    // Andreas says this is broken, i.e. against rfc2368.
    // Let's see if the need ever comes up.
    KUrl umail2("mailto:Faure David <faure@kde.org>");
    QCOMPARE(umail2.protocol(), QString("mailto"));
    QCOMPARE(umail2.path(), QString("Faure David <faure@kde.org>"));
    QVERIFY(!KUrl::isRelativeUrl("mailto:faure@kde.org"));
#endif

    KUrl url183433("mailto:test[at]gmail[dot]com");
    QCOMPARE(url183433.prettyUrl(), QString("mailto:test[at]gmail[dot]com"));
    QCOMPARE(url183433.url(), QString("mailto:test[at]gmail[dot]com"));
}

void KUrlTest::testSmb()
{
    KUrl smb("smb://domain;username:password@server/share");
    QVERIFY(smb.isValid());
    QCOMPARE(smb.user(), QString("domain;username"));
    smb = "smb:/";
    QVERIFY(smb.isValid());
    QCOMPARE(smb.url(), QString::fromLatin1("smb:/"));
    QCOMPARE(smb.prettyUrl(), QString::fromLatin1("smb:/"));
    smb = "smb://"; // KDE3: kurl.cpp rev 1.106 made it invalid. Valid again with QUrl.
    QVERIFY(smb.isValid());
    QCOMPARE(smb.prettyUrl(), QString::fromLatin1("smb://"));
    smb = "smb://host";
    QVERIFY(smb.isValid());
    QCOMPARE(smb.url(), QString::fromLatin1("smb://host"));
    QCOMPARE(smb.prettyUrl(), QString::fromLatin1("smb://host"));
    smb = "smb:///";
    QVERIFY(smb.isValid());
    QCOMPARE(smb.prettyUrl(), QString::fromLatin1("smb:/"));

    KUrl implicitSmb("file://host/path");
    QVERIFY(!implicitSmb.isLocalFile()); // -> kio_file will redirect to smb (by default)
    QCOMPARE(implicitSmb.host(), QString("host"));

    KUrl noImplicitSmb("//path1/path2");
    QVERIFY(noImplicitSmb.isLocalFile());
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) // see qtbase commit f62768d046
    QCOMPARE(noImplicitSmb.path(), QString("//path1/path2"));
#else
    QCOMPARE(noImplicitSmb.path(), QString("/path1/path2"));
#endif
}

void KUrlTest::testOtherProtocols()
{
    KUrl about("about:");
    QCOMPARE(about.path(), QString());
    QCOMPARE(about.protocol(), QString("about"));

    KUrl aboutKonqueror("about:konqueror");
    QCOMPARE(aboutKonqueror.path(), QString("konqueror"));

    KUrl leo("data:text/html,http://www.invalid/");
    QVERIFY(leo.isValid());
    QCOMPARE(leo.protocol(), QString("data"));
    QCOMPARE(leo.url(), QString("data:text/html,http://www.invalid/"));
    QCOMPARE(leo.path(), QString("text/html,http://www.invalid/"));

    KUrl testFrag("data:,test#foo");
    QVERIFY(testFrag.isValid());
    QCOMPARE(testFrag.protocol(), QString("data"));
    QCOMPARE(testFrag.url(), QString("data:,test#foo"));
    QCOMPARE(testFrag.path(), QString(",test"));
    QVERIFY(testFrag.hasFragment());
    QCOMPARE(testFrag.fragment(), QString("foo"));

    KUrl ptal("ptal://mlc:usb@PC_970");   // User=mlc, password=usb, host=PC_970
#if QT_VERSION >= 0x040600 && QT_VERSION <= 0x040602 // Hostnames with underscores were invalid in 4.6.0 to 4.6.2, then allowed again in e301c82693c33c0f96c6a756d15fe35a9d877443
    QCOMPARE(ptal.url(), QString("ptal://mlc:usb@")); // The host "PC_970" is invalid according to STD3 validation
    KUrl ptalSimpler("ptal://mlc:usb@pc123");
    QCOMPARE(ptalSimpler.url(), QString("ptal://mlc:usb@pc123"));
#else
    QUrl ptal_qurl;
    ptal_qurl.setUrl("ptal://mlc:usb@PC_970", QUrl::TolerantMode);
    QVERIFY(ptal_qurl.isValid());
    QCOMPARE(QString::fromLatin1(ptal_qurl.toEncoded()), QString::fromLatin1("ptal://mlc:usb@pc_970"));
    QCOMPARE(ptal_qurl.host(), QString("pc_970"));

    QVERIFY(ptal.isValid());
    QCOMPARE(ptal.host(), QString("pc_970"));
    QCOMPARE(ptal.user(), QString("mlc"));
    QCOMPARE(ptal.pass(), QString("usb"));
#endif
}

void KUrlTest::testUtf8()
{
    QTextCodec *codec = QTextCodec::codecForName("ISO-8859-1");
    QVERIFY(codec != nullptr);
    QTextCodec::setCodecForLocale(codec);

#if 0
    {
        QUrl utest;
        utest.setScheme("file");
        utest.setPath(QString::fromUtf8("/home/dfaure/Matériel"));
        printf("utest.toString()=%s\n", utest.toString().toLatin1().constData());
        printf("utest.path()=%s\n", utest.path().toLatin1().constData());
        printf("utest.toEncoded()=%s\n", utest.toEncoded().data());
    }
#endif

    // UTF8 tests
    KUrl uloc;
    uloc.setPath(QString::fromUtf8("/home/dfaure/Matériel"));
    QCOMPARE(uloc.url(), QString("file:///home/dfaure/Mat%C3%A9riel"));    // KDE3 would say %E9 here; but from now on URLs are always utf8 encoded.
    QCOMPARE(uloc.path(), QString::fromUtf8("/home/dfaure/Matériel"));
    QCOMPARE(uloc.prettyUrl(), QString::fromUtf8("file:///home/dfaure/Matériel"));
    QCOMPARE(uloc.pathOrUrl(), QString::fromUtf8("/home/dfaure/Matériel"));                // ... but that's why pathOrUrl is nicer.
    QCOMPARE(uloc.url(), QString("file:///home/dfaure/Mat%C3%A9riel"));
    uloc = KUrl("file:///home/dfaure/Mat%C3%A9riel");
    QCOMPARE(uloc.path(), QString::fromUtf8("/home/dfaure/Matériel"));
    QCOMPARE(uloc.url(), QString("file:///home/dfaure/Mat%C3%A9riel"));

    KUrl umlaut1("http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel");
    QCOMPARE(umlaut1.url(), QString("http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel"));

    KUrl umlaut2("http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel"); // was ,106
    QCOMPARE(umlaut2.url(), QString("http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel"));

    KUrl urlWithUnicodeChar(QString::fromUtf8("file:///home/dfaure/Matériel"));
    QCOMPARE(uloc.url(), QString("file:///home/dfaure/Mat%C3%A9riel"));

    KUrl wkai(QString::fromUtf8("/tmp/魔"));
    QCOMPARE(wkai.url(), QString("file:///tmp/%E9%AD%94"));
    QCOMPARE(wkai.prettyUrl(), QString::fromUtf8("file:///tmp/魔"));

    // Show that the character "fraction slash" (U+2044) cannot appear in url(),
    // so it's ok to use that to encode urls as filenames (e.g. in kio_http cache)
    KUrl fractionSlash(QString("http://kde.org/a") + QChar(0x2044) + "b");
    QCOMPARE(fractionSlash.url(), QString("http://kde.org/a%E2%81%84b"));
    QCOMPARE(fractionSlash.prettyUrl(), QString(QString("http://kde.org/a") + QChar(0x2044) + "b"));
}

void KUrlTest::testOtherEncodings()
{
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("koi8-r"));
    KUrl baseURL("file:/home/coolo");
    KUrl russian = QUrl::fromLocalFile(baseURL.directory(KUrl::AppendTrailingSlash) + QString::fromUtf8("фгн7"));
    //QCOMPARE( russian.url(), QString("file:///home/%C6%C7%CE7" ) ); // KDE3: was not using utf8
    QCOMPARE(russian.url(), QString("file:///home/%D1%84%D0%B3%D0%BD7"));   // QUrl uses utf8

    KUrl utf8_1("audiocd:/By%20Name/15%20Geantra%C3%AE.wav");
    QCOMPARE(utf8_1.fileName(), QString::fromUtf8("15 Geantraî.wav"));

    // KDE3: url had %2F, and fileName had '/'. But this is wrong, %2F means '/',
    // and filenames have to use %2F, so the url needs to have %252F.
    // KIO::encodeFileName takes care of that.
    KUrl utf8_2("audiocd:/By%20Name/15%252FGeantra%C3%AE.wav");
    QCOMPARE(utf8_2.path(), QString::fromUtf8("/By Name/15%2FGeantraî.wav"));
    QCOMPARE(utf8_2.fileName(), QString::fromUtf8("15%2FGeantraî.wav"));
}

void KUrlTest::testPathOrURL()
{
    QUrl quloc = QUrl::fromLocalFile("/home/dfaure/konqtests/Mat%C3%A9riel");
    QCOMPARE(quloc.toLocalFile(), QString("/home/dfaure/konqtests/Mat%C3%A9riel"));

    // passing path or url to the constructor: both work
    KUrl uloc("/home/dfaure/konqtests/Mat%C3%A9riel");
    QCOMPARE(uloc.url(), QString("file:///home/dfaure/konqtests/Mat%25C3%25A9riel"));
    QCOMPARE(uloc.toLocalFile(), QString("/home/dfaure/konqtests/Mat%C3%A9riel"));
    uloc = KUrl("http://www.kde.org");
    QCOMPARE(uloc.pathOrUrl(), uloc.url());
    QCOMPARE(uloc.pathOrUrl(KUrl::AddTrailingSlash), QString("http://www.kde.org/"));
    uloc = KUrl(QString("www.kde.org"));
    QVERIFY(uloc.isValid());   // KDE3: was invalid. But it's now a url with path="www.kde.org", ok.
    uloc = KUrl("index.html");
    QVERIFY(uloc.isValid());   // KDE3: was invalid; same as above
    uloc = KUrl("");
    QVERIFY(!uloc.isValid());
#ifdef Q_OS_WIN
#ifdef Q_CC_MSVC
#pragma message ("port KUser")
#else
#warning port KUser
#endif
#else
    KUser currentUser;
    const QString userName = currentUser.loginName();
    QVERIFY(!userName.isEmpty());
    uloc = KUrl(QString::fromUtf8("~%1/konqtests/Matériel").arg(userName));
    QEXPECT_FAIL("", "No more user-expansion in KUrl", Continue);
    QCOMPARE(uloc.path(), QString::fromUtf8("%1/konqtests/Matériel").arg(currentUser.homeDir()));
#endif

    // pathOrUrl tests
    uloc = KUrl("/home/dfaure/konqtests/Mat%C3%A9riel");
    QCOMPARE(uloc.pathOrUrl(), uloc.path());
    uloc = "http://www.kde.org";
    QCOMPARE(uloc.url(), QString("http://www.kde.org"));
    uloc = "file:///home/dfaure/konq%20tests/Mat%C3%A9riel#ref";
    QCOMPARE(uloc.pathOrUrl(), QString::fromUtf8("file:///home/dfaure/konq tests/Matériel#ref"));
    uloc = "file:///home/dfaure/konq%20tests/Mat%C3%A9riel?query";
    QCOMPARE(uloc.pathOrUrl(), QString::fromUtf8("file:///home/dfaure/konq tests/Matériel?query"));
    uloc = KUrl("/home/dfaure/file#with#hash");
    QCOMPARE(uloc.pathOrUrl(), QString("/home/dfaure/file#with#hash"));

    // test creation of url from pathOrUrl
    uloc = KUrl(QString::fromUtf8("http://www.kde.org/home/andreas/täst"));
    QCOMPARE(KUrl(uloc.pathOrUrl()), uloc);
    uloc = KUrl("http://www.kde.org/home/andreas/t%C3%A4st");
    QCOMPARE(KUrl(uloc.pathOrUrl()), uloc);
    uloc = KUrl(QString::fromUtf8("file:///home/andreas/täst"));
    QCOMPARE(KUrl(uloc.pathOrUrl()), uloc);
    uloc = KUrl("file:///home/andreas/t%C3%A4st");
    QCOMPARE(KUrl(uloc.pathOrUrl()), uloc);
    uloc = KUrl("http://www.kde.org/home/kde?foobar#test");
    QCOMPARE(KUrl(uloc.pathOrUrl()), uloc);
    uloc = KUrl("http://www.kde.org/home/%andreas");
    QCOMPARE(KUrl(uloc.pathOrUrl()), uloc);
}

void KUrlTest::testAssignment()
{
    // passing path or url to the constructor: both work
    KUrl uloc;
    uloc = "/home/dfaure/konqtests/Mat%C3%A9riel";
    QCOMPARE(uloc.toLocalFile(), QString::fromUtf8("/home/dfaure/konqtests/Mat%C3%A9riel"));
    KUrl u2;
    u2 = uloc;
    QCOMPARE(u2.toLocalFile(), QString::fromUtf8("/home/dfaure/konqtests/Mat%C3%A9riel"));
    uloc = "http://www.kde.org";
    QCOMPARE(uloc.pathOrUrl(), uloc.url());
    uloc = QString("www.kde.org");
    QVERIFY(uloc.isValid());
    uloc = KUrl("index.html");
    QVERIFY(uloc.isValid());
    uloc = KUrl("");
    QVERIFY(!uloc.isValid());
#ifdef Q_OS_WIN
#ifdef Q_CC_MSVC
#pragma message ("port KUser")
#else
#warning port KUser
#endif
#else
    KUser currentUser;
    const QString userName = currentUser.loginName();
    QVERIFY(!userName.isEmpty());
    uloc = QString::fromUtf8("~%1/konqtests/Matériel").arg(userName);
    QEXPECT_FAIL("", "No more user-expansion in KUrl", Continue);
    QCOMPARE(uloc.path(), QString::fromUtf8("%1/konqtests/Matériel").arg(currentUser.homeDir()));
    uloc = QByteArray('~' + userName.toUtf8() + "/konqtests/Matériel");
    QEXPECT_FAIL("", "No more user-expansion in KUrl", Continue);
    QCOMPARE(uloc.path(), QString::fromUtf8("%1/konqtests/Matériel").arg(currentUser.homeDir()));

    // Assigning a KUrl to a QUrl and back
    QUrl qurl = uloc;
    QCOMPARE(qurl.toEncoded(), uloc.toEncoded());
    uloc = KUrl(qurl);
    QCOMPARE(qurl.toEncoded(), uloc.toEncoded());
    QEXPECT_FAIL("", "No more user-expansion in KUrl", Continue);
    QCOMPARE(uloc.path(), QString::fromUtf8("%1/konqtests/Matériel").arg(currentUser.homeDir()));
#endif
}

void KUrlTest::testQueryItem()
{
    KUrl theKow("http://www.google.de/search?q=frerich&hlx=xx&hl=de&empty=&lr=lang+de&test=%2B%20%3A%25");
    QCOMPARE(theKow.queryItem("q"), QString("frerich"));
    QCOMPARE(theKow.queryItem("hl"), QString("de"));
    QCOMPARE(theKow.queryItem("lr"), QString("lang de"));   // the '+' got decoded
    QCOMPARE(theKow.queryItem("InterstellarCounselor"), QString());
    QCOMPARE(theKow.queryItem("empty"), QString(""));
    QCOMPARE(theKow.queryItem("test"), QString("+ :%"));
    theKow.addQueryItem("a", "b+c");
    QCOMPARE(theKow.url(), QString("http://www.google.de/search?q=frerich&hlx=xx&hl=de&empty=&lr=lang+de&test=%2B%20%3A%25&a=b%2Bc"));   // KDE3 would use b%2Bc, but this is more correct
    QCOMPARE(theKow.queryItem("a"), QString("b+c"));   // note that the '+' remained

    // checks for queryItems(), which returns a QMap<QString,QString>:
    KUrl queryUrl("mailto:Marc%20Mutz%20%3cmutz@kde.org%3E?"
                  "Subject=subscribe+me&"
                  "body=subscribe+mutz%40kde.org&"
                  "Cc=majordomo%40lists.kde.org");
    QCOMPARE(QStringList(queryUrl.queryItems(nullptr).keys()).join(", "),
             QString("Cc, Subject, body"));
    QCOMPARE(QStringList(queryUrl.queryItems(KUrl::CaseInsensitiveKeys).keys()).join(", "),
             QString("body, cc, subject"));
    QCOMPARE(QStringList(queryUrl.queryItems(nullptr).values()).join(", "),
             QString("majordomo@lists.kde.org, subscribe me, subscribe mutz@kde.org"));
    QCOMPARE(QStringList(queryUrl.queryItems(KUrl::CaseInsensitiveKeys).values()).join(", "),
             QString("subscribe mutz@kde.org, majordomo@lists.kde.org, subscribe me"));
    // TODO check for QUrl::queryItems

}

void KUrlTest::testEncodeString()
{
    // Needed for #49616
    QCOMPARE(QUrl::toPercentEncoding("C++"), QByteArray("C%2B%2B"));
    QCOMPARE(QUrl::fromPercentEncoding("C%2B%2B"), QString("C++"));
    QCOMPARE(QUrl::toPercentEncoding("%"), QByteArray("%25"));
    QCOMPARE(QUrl::toPercentEncoding(":"), QByteArray("%3A"));
    QCOMPARE(QUrl::fromPercentEncoding("C%A"), QString("C%A"));     // % A is not percent-encoding  (pct-encoded = "%" HEXDIG HEXDIG)

    QString output = QUrl::fromPercentEncoding("C%00%0A");
    QString expected = QString::fromLatin1("C\0\n", 3); // no reason to stop at %00, in fact
    QCOMPARE(output.size(), expected.size());
    QCOMPARE(output, expected);
}

void KUrlTest::testIdn()
{
    //qDebug( "trying QUrl with fromPercentEncoding" );
    QUrl qurltest(QUrl::fromPercentEncoding("http://\303\244.de"));     // a+trema in utf8
    QVERIFY(qurltest.isValid());

    //qDebug( "trying QUrl with fromEncoded" );
    QUrl qurl = QUrl::fromEncoded("http://\303\244.de");   // a+trema in utf8
    QVERIFY(qurl.isValid());
    QCOMPARE(qurl.toEncoded(), QByteArray("http://xn--4ca.de"));

    //qDebug( "and now trying KUrl" );
    KUrl thiago(QString::fromUtf8("http://\303\244.de"));     // a+trema in utf8
    QVERIFY(thiago.isValid());
    QCOMPARE(thiago.url(), QString("http://xn--4ca.de"));     // Non-ascii is allowed in IDN domain names.

#if 0
    // A broken test - not using utf8. and amantia forgot the real-world testcase.
    KUrl amantia("http://%E1.foo.de");
    QVERIFY(amantia.isValid());
    QCOMPARE(amantia.url(), QString("http://xn--80a.foo.de"));     // Non-ascii is allowed in IDN domain names.
#endif

    // A more valid test for % in hostnames:
    KUrl uwp("http://%C3%A4.de");
    QVERIFY(uwp.isValid());
    QCOMPARE(thiago.url(), QString("http://xn--4ca.de"));   // as above
}

void KUrlTest::testUriMode()
{
    KUrl url1;
#if 0 // ###### TODO KUri
    url1 = "http://www.foobar.com/";
    QCOMPARE(url1.uriMode(), KUrl::URL);
    url1 = "mailto:user@host.com";
    QCOMPARE(url1.uriMode(), KUrl::Mailto);

    url1 = "data:text/plain,foobar?gazonk=flarp";
    QCOMPARE(url1.uriMode(), KUrl::RawURI);

    QCOMPARE(url1.path(), QString("text/plain,foobar?gazonk=flarp"));
#endif
    url1 = "mailto:User@Host.COM?subject=Hello";
    QCOMPARE(url1.path(), QString("User@Host.COM"));   // KDE3: "User@host.com". Does it matter?
}

void KUrlTest::testToLocalFile()
{
    const QString localFile("/tmp/print.pdf");

    const KUrl urlWithHost("file://localhost/tmp/print.pdf");
    const KUrl urlWithoutHost("file:///tmp/print.pdf");

    QCOMPARE(urlWithHost.toLocalFile(), localFile);
    QCOMPARE(urlWithoutHost.toLocalFile(), localFile);
}

void KUrlTest::testUrl_data()
{
    QTest::addColumn<KUrl>("url");
    QTest::addColumn<QString>("urlLTS");
    QTest::addColumn<QString>("urlRTS");
    QTest::addColumn<QString>("urlATS");

    QTest::newRow("local file 1")
            << KUrl("file:///")
            << QString::fromLatin1("file:///")
            << QString::fromLatin1("file:///")
            << QString::fromLatin1("file:///");
    QTest::newRow("local file 2")
            << KUrl("file:///home/kde/")
            << QString::fromLatin1("file:///home/kde/")
            << QString::fromLatin1("file:///home/kde")
            << QString::fromLatin1("file:///home/kde/");
    QTest::newRow("local file 3")
            << KUrl("file:///home/kde//")
            << QString::fromLatin1("file:///home/kde//")
            << QString::fromLatin1("file:///home/kde")
            << QString::fromLatin1("file:///home/kde//");

    QTest::newRow("ftp url")
            << KUrl("ftp://ftp.kde.org/")
            << QString::fromLatin1("ftp://ftp.kde.org/")
            << QString::fromLatin1("ftp://ftp.kde.org/")
            << QString::fromLatin1("ftp://ftp.kde.org/");
    QTest::newRow("ftp url - 3 trailing slashes")
            << KUrl("ftp://ftp.kde.org///")
            << QString::fromLatin1("ftp://ftp.kde.org///")
            << QString::fromLatin1("ftp://ftp.kde.org/")
            << QString::fromLatin1("ftp://ftp.kde.org///");
}

void KUrlTest::testUrl()
{
    QFETCH(KUrl, url);
    QFETCH(QString, urlLTS);
    QFETCH(QString, urlRTS);
    QFETCH(QString, urlATS);

    QCOMPARE(url.url(KUrl::LeaveTrailingSlash), urlLTS);
    QCOMPARE(url.url(KUrl::RemoveTrailingSlash), urlRTS);
    QCOMPARE(url.url(KUrl::AddTrailingSlash), urlATS);
}

void KUrlTest::testToStringList()
{
    KUrl::List urls;
    urls << KUrl("file:///")
         << KUrl("file:///home/kde/")
         << KUrl("file:///home/kde//")
         << KUrl("ftp://ftp.kde.org/")
         << KUrl("ftp://ftp.kde.org///");

    //kDebug() << urls.toStringList(KUrl::LeaveTrailingSlash);
    QCOMPARE(urls.toStringList(KUrl::LeaveTrailingSlash),
             QStringList()
             << QLatin1String("file:///")
             << QLatin1String("file:///home/kde/")
             << QLatin1String("file:///home/kde//")
             << QLatin1String("ftp://ftp.kde.org/")
             << QLatin1String("ftp://ftp.kde.org///"));

    //kDebug() << urls.toStringList(KUrl::RemoveTrailingSlash);
    QCOMPARE(urls.toStringList(KUrl::RemoveTrailingSlash),
             QStringList()
             << QLatin1String("file:///")
             << QLatin1String("file:///home/kde")
             << QLatin1String("file:///home/kde")
             << QLatin1String("ftp://ftp.kde.org/")
             << QLatin1String("ftp://ftp.kde.org/"));

    //kDebug() << urls.toStringList(KUrl::AddTrailingSlash);
    QCOMPARE(urls.toStringList(KUrl::AddTrailingSlash),
             QStringList()
             << QLatin1String("file:///")
             << QLatin1String("file:///home/kde/")
             << QLatin1String("file:///home/kde//")
             << QLatin1String("ftp://ftp.kde.org/")
             << QLatin1String("ftp://ftp.kde.org///"));
}

void KUrlTest::testHashing()
{
    QHash<KUrl, QUrl> hash;
    hash.insert(KUrl("http://www.kde.org/foo%23bar"), QUrl("http://www.kde.org/foo%23bar"));
    hash.insert(KUrl("http://www.kde.org/foo#ref?query"), QUrl("http://www.kde.org/foo#ref?query"));

    QHashIterator<KUrl, QUrl> it(hash);
    while (it.hasNext()) {
        it.next();
        QCOMPARE(it.key().url(), it.value().toString());
    }
}
