Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtConcurrent/qtconcurrentrun.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qmap.h>
#include <QtCore/qthreadpool.h>
#include <QtCore/qurl.h>
#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QtTest/private/qemulationdetector_p.h>
using namespace Qt::StringLiterals;
Q_DECLARE_METATYPE(QUrl::FormattingOptions)
class tst_QUrl : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void getSetCheck();
void constructing();
void hashInPath();
void unc();
void assignment();
void orderingCompiles();
void comparison();
void comparison2_data();
void comparison2();
void copying();
void setUrl();
void i18n_data();
void i18n();
void resolving_data();
void resolving();
void toString_data();
void toString();
void toString_PreferLocalFile_data();
void toString_PreferLocalFile();
void toString_constructed_data();
void toString_constructed();
void toDisplayString_PreferLocalFile_data();
void toDisplayString_PreferLocalFile();
void toAndFromStringList_data();
void toAndFromStringList();
void isParentOf_data();
void isParentOf();
void toLocalFile_data();
void toLocalFile();
void toLocalFileUnsafe_data();
void toLocalFileUnsafe();
void fromLocalFile_data();
void fromLocalFile();
void fromLocalFileNormalize_data();
void fromLocalFileNormalize();
void fromLocalFileNormalizeNonRoundtrip_data();
void fromLocalFileNormalizeNonRoundtrip();
void macTypes_data();
void macTypes();
void relative();
void compat_legacy();
void compat_constructor_01_data();
void compat_constructor_01();
void compat_constructor_02_data();
void compat_constructor_02();
void compat_constructor_03_data();
void compat_constructor_03();
void compat_isValid_01_data();
void compat_isValid_01();
void compat_isValid_02_data();
void compat_isValid_02();
void compat_path_data();
void compat_path();
void compat_fileName_data();
void compat_fileName();
void compat_decode_data();
void compat_decode();
void compat_encode_data();
void compat_encode();
void percentEncoding_data();
void percentEncoding();
void swap();
void symmetry();
void ipvfuture_data();
void ipvfuture();
void ipv6_data();
void ipv6();
void ipv6_2_data();
void ipv6_2();
void moreIpv6();
void toPercentEncoding_data();
void toPercentEncoding();
void isRelative_data();
void isRelative();
void hasQuery_data();
void hasQuery();
void nameprep();
void isValid();
void schemeValidator_data();
void schemeValidator();
void setScheme_data();
void setScheme();
void strictParser_data();
void strictParser();
void tolerantParser();
void correctEncodedMistakes_data();
void correctEncodedMistakes();
void correctDecodedMistakes_data();
void correctDecodedMistakes();
void emptyQueryOrFragment();
void hasFragment_data();
void hasFragment();
void setFragment_data();
void setFragment();
void fromEncoded();
void stripTrailingSlash_data();
void stripTrailingSlash();
void hosts_data();
void hosts();
void hostFlags_data();
void hostFlags();
void setPort();
void port_data();
void port();
void toEncoded_data();
void toEncoded();
void setAuthority_data();
void setAuthority();
void setEmptyAuthority_data();
void setEmptyAuthority();
void clear();
void resolvedWithAbsoluteSchemes() const;
void resolvedWithAbsoluteSchemes_data() const;
void binaryData_data();
void binaryData();
void fromUserInput_data();
void fromUserInput();
void fromUserInputWithCwd_data();
void fromUserInputWithCwd();
void fileName_data();
void fileName();
void isEmptyForEncodedUrl();
void toEncodedNotUsingUninitializedPath();
void emptyAuthorityRemovesExistingAuthority_data();
void emptyAuthorityRemovesExistingAuthority();
void acceptEmptyAuthoritySegments();
void lowercasesScheme();
void componentEncodings_data();
void componentEncodings();
void setComponents_data();
void setComponents();
void streaming_data();
void streaming();
void detach();
void testThreading();
void matches_data();
void matches();
void ipv6_zoneId_data();
void ipv6_zoneId();
void normalizeRemotePaths_data();
void normalizeRemotePaths();
private:
void testThreadingHelper();
const QString m_currentPath = QDir::currentPath();
QTemporaryDir m_tempDir;
};
void tst_QUrl::initTestCase()
{
QVERIFY2(m_tempDir.isValid(), qPrintable(m_tempDir.errorString()));
}
void tst_QUrl::cleanupTestCase()
{
// Restore working directory changed in fromUserInputWithCwd()
QDir::setCurrent(m_currentPath);
}
// Testing get/set functions
void tst_QUrl::getSetCheck()
{
QUrl obj1;
// int QUrl::port()
// void QUrl::setPort(int)
obj1.setPort(0);
QCOMPARE(0, obj1.port());
obj1.setPort(INT_MIN);
QCOMPARE(-1, obj1.port()); // Out of range, -1
obj1.setPort(INT_MAX);
QCOMPARE(-1, obj1.port()); // Out of range, -1
obj1.setPort(1234);
QCOMPARE(1234, obj1.port());
// static QStringList QUrl::idnWhitelist()
// static void QUrl::setIdnWhitelist(QStringList)
QStringList original = QUrl::idnWhitelist(); // save for later
QUrl::setIdnWhitelist(QStringList());
QCOMPARE(QUrl::idnWhitelist(), QStringList());
QStringList norway; norway << "no";
QUrl::setIdnWhitelist(norway);
QCOMPARE(QUrl::idnWhitelist(), norway);
QStringList modified = original;
modified << "foo";
QUrl::setIdnWhitelist(modified);
QCOMPARE(QUrl::idnWhitelist(), modified);
// reset to the original
QUrl::setIdnWhitelist(original);
QCOMPARE(QUrl::idnWhitelist(), original);
}
void tst_QUrl::constructing()
{
QUrl url;
QVERIFY(!url.isValid());
QVERIFY(url.isEmpty());
QCOMPARE(url.port(), -1);
QCOMPARE(url.toString(), QString());
QCOMPARE(url, url);
QVERIFY(!(url < url));
QUrl fromLocal = QUrl::fromLocalFile(QString());
QVERIFY(!fromLocal.isValid());
QVERIFY(fromLocal.isEmpty());
QCOMPARE(fromLocal.toString(), QString());
QUrl justHost("qt-project.org");
QVERIFY(!justHost.isEmpty());
QVERIFY(justHost.host().isEmpty());
QCOMPARE(justHost.path(), QString::fromLatin1("qt-project.org"));
QUrl hostWithSlashes("//qt-project.org");
QVERIFY(hostWithSlashes.path().isEmpty());
QCOMPARE(hostWithSlashes.host(), QString::fromLatin1("qt-project.org"));
}
void tst_QUrl::hashInPath()
{
QUrl withHashInPath;
withHashInPath.setPath(QString::fromLatin1("hi#mum.txt"));
QCOMPARE(withHashInPath.path(), QString::fromLatin1("hi#mum.txt"));
QCOMPARE(withHashInPath.toEncoded(), QByteArray("hi%23mum.txt"));
QCOMPARE(withHashInPath.toString(), QString("hi%23mum.txt"));
QCOMPARE(withHashInPath.toDisplayString(QUrl::PreferLocalFile), QString("hi%23mum.txt"));
QUrl fromHashInPath = QUrl::fromEncoded(withHashInPath.toEncoded());
QCOMPARE(withHashInPath, fromHashInPath);
const QUrl localWithHash = QUrl::fromLocalFile("/hi#mum.txt");
QCOMPARE(localWithHash.path(), QString::fromLatin1("/hi#mum.txt"));
QCOMPARE(localWithHash.path(), QString::fromLatin1("/hi#mum.txt"));
QCOMPARE(localWithHash.toString(QUrl::PreferLocalFile), QString("/hi#mum.txt"));
QCOMPARE(localWithHash.toDisplayString(QUrl::PreferLocalFile), QString("/hi#mum.txt"));
}
void tst_QUrl::unc()
{
QUrl buildUNC;
buildUNC.setScheme(QString::fromLatin1("file"));
buildUNC.setHost(QString::fromLatin1("somehost"));
buildUNC.setPath(QString::fromLatin1("somepath"));
QCOMPARE(buildUNC.toLocalFile(), QString::fromLatin1("//somehost/somepath"));
buildUNC.toEncoded();
QVERIFY(!buildUNC.isEmpty());
}
void tst_QUrl::assignment()
{
QVERIFY(url.isValid());
QUrl copy;
copy = url;
QCOMPARE(url, copy);
}
void tst_QUrl::orderingCompiles()
{
QTestPrivate::testAllComparisonOperatorsCompile<QUrl>();
}
void tst_QUrl::comparison()
{
QVERIFY(url1.isValid());
QVERIFY(url2.isValid());
QCOMPARE(url1, url2);
QVERIFY(!(url1 < url2));
QVERIFY(!(url2 < url1));
QVERIFY(url1.matches(url2, QUrl::None));
QVERIFY(url1.matches(url2, QUrl::StripTrailingSlash));
// 6.2.2 Syntax-based Normalization
QEXPECT_FAIL("", "Normalization not implemented, will probably not be implemented like this", Continue);
QCOMPARE(url3, url4);
QCOMPARE(url4bis.adjusted(QUrl::NormalizePathSegments), url3bis);
QCOMPARE(url4bis.adjusted(QUrl::NormalizePathSegments | QUrl::StripTrailingSlash), url3bisNoSlash);
QVERIFY(url3bis.matches(url4bis, QUrl::NormalizePathSegments));
QVERIFY(!url3bisNoSlash.matches(url4bis, QUrl::NormalizePathSegments));
QVERIFY(url3bisNoSlash.matches(url4bis, QUrl::NormalizePathSegments | QUrl::StripTrailingSlash));
QCOMPARE(url4EncodedDots.path(QUrl::PrettyDecoded), QString("/.//b/..%2F/b/c/"));
QCOMPARE(url4EncodedDots.path(QUrl::FullyDecoded), QString("/.//b/..//b/c/"));
QCOMPARE(QString::fromLatin1(url4EncodedDots.toEncoded()), QString::fromLatin1("example://a/.//b/..%2F/b/c/"));
QCOMPARE(url4EncodedDots.adjusted(QUrl::NormalizePathSegments).toString(), QString("example://a//b/..%2F/b/c/"));
QUrl urlPathSetEncoded = QUrl("ws://localhost:12345/segment/with%20spaces/%3C%c3%a9%3E", QUrl::TolerantMode);
QCOMPARE(urlPathSetDecoded, urlPathSetEncoded);
urlSetPathEncodedWithPercent.setPath("/segment/with%20spaces/%3C%c3%a9%3E", QUrl::TolerantMode);
urlSetPathEncodedWithLiterals.setPath("/segment/with spaces/<é>", QUrl::TolerantMode);
urlSetPathDecoded.setPath("/segment/with spaces/<é>", QUrl::DecodedMode);
QCOMPARE(urlSetPathEncodedWithPercent, urlPathSetEncoded);
QCOMPARE(urlSetPathEncodedWithPercent, urlPathSetDecoded);
QCOMPARE(urlSetPathEncodedWithLiterals, urlPathSetEncoded);
QCOMPARE(urlSetPathEncodedWithLiterals, urlPathSetDecoded);
QCOMPARE(urlSetPathEncodedWithLiterals, urlSetPathEncodedWithPercent);
QCOMPARE(urlSetPathDecoded, urlPathSetEncoded);
QCOMPARE(urlSetPathDecoded, urlPathSetDecoded);
QCOMPARE(urlSetPathDecoded, urlSetPathEncodedWithPercent);
QCOMPARE(urlSetPathDecoded, urlSetPathEncodedWithLiterals);
QCOMPARE(qHash(urlSetPathEncodedWithPercent), qHash(urlPathSetEncoded));
QCOMPARE(qHash(urlSetPathEncodedWithPercent), qHash(urlPathSetDecoded));
QCOMPARE(qHash(urlSetPathEncodedWithLiterals), qHash(urlPathSetEncoded));
QCOMPARE(qHash(urlSetPathEncodedWithLiterals), qHash(urlPathSetDecoded));
QCOMPARE(qHash(urlSetPathEncodedWithLiterals), qHash(urlSetPathEncodedWithPercent));
QCOMPARE(qHash(urlSetPathDecoded), qHash(urlPathSetEncoded));
QCOMPARE(qHash(urlSetPathDecoded), qHash(urlPathSetDecoded));
QCOMPARE(qHash(urlSetPathDecoded), qHash(urlSetPathEncodedWithPercent));
QCOMPARE(qHash(urlSetPathDecoded), qHash(urlSetPathEncodedWithLiterals));
// 6.2.2.1 Make sure hexdecimal characters in percent encoding are
// treated case-insensitively
QUrl url5;
url5.setQuery(QLatin1String("a=%2a"));
QUrl url6;
url6.setQuery(QLatin1String("a=%2A"));
QCOMPARE(url5, url6);
QUrl url7;
url7.setQuery(QLatin1String("a=C"));
QUrl url8;
url8.setQuery(QLatin1String("a=c"));
QVERIFY(url7 != url8);
QVERIFY(url7 < url8);
// Trailing slash difference
QVERIFY(!(url9 == url9NoSlash));
QVERIFY(!url9.matches(url9NoSlash, QUrl::None));
QVERIFY(url9.matches(url9NoSlash, QUrl::StripTrailingSlash));
// RemoveFilename
QVERIFY(!(url10 == url10bis));
QVERIFY(!url10.matches(url10bis, QUrl::None));
QVERIFY(!url10.matches(url10bis, QUrl::StripTrailingSlash));
QVERIFY(url10.matches(url10bis, QUrl::RemoveFilename));
// RemoveAuthority
QUrl authUrl3("x:/a/b");
QVERIFY(authUrl1.matches(authUrl2, QUrl::RemoveFilename));
QCOMPARE(authUrl1.adjusted(QUrl::RemoveAuthority), authUrl3.adjusted(QUrl::RemoveAuthority));
QVERIFY(authUrl1.matches(authUrl3, QUrl::RemoveAuthority));
QCOMPARE(authUrl2.adjusted(QUrl::RemoveAuthority | QUrl::RemoveFilename), authUrl3.adjusted(QUrl::RemoveAuthority | QUrl::RemoveFilename));
QVERIFY(authUrl2.matches(authUrl3, QUrl::RemoveAuthority | QUrl::RemoveFilename));
QVERIFY(authUrl3.matches(authUrl2, QUrl::RemoveAuthority | QUrl::RemoveFilename));
QUrl hostUrl1("file:/foo");
QCOMPARE(hostUrl1, hostUrl2);
QVERIFY(hostUrl1.matches(hostUrl2, QUrl::None));
QVERIFY(hostUrl1.matches(hostUrl2, QUrl::RemoveAuthority));
// RemovePassword
QVERIFY(!(passUrl1 == passUrl2));
QVERIFY(passUrl1 != passUrl2);
QVERIFY(!passUrl1.matches(passUrl2, QUrl::None));
QVERIFY(passUrl1.matches(passUrl2, QUrl::RemovePassword));
// RemovePassword, null vs empty
QVERIFY(!(emptyPassUrl1 == emptyPassUrl2));
QVERIFY(emptyPassUrl1 != emptyPassUrl2);
QVERIFY(!emptyPassUrl1.matches(emptyPassUrl2, QUrl::None));
QVERIFY(emptyPassUrl1.matches(emptyPassUrl2, QUrl::RemovePassword));
// RemoveQuery, RemoveFragment
QVERIFY(!(queryFragUrl1 == queryFragUrl2));
QVERIFY(queryFragUrl1 != queryFragUrl2);
QVERIFY(!queryFragUrl1.matches(queryFragUrl2, QUrl::None));
QVERIFY(!queryFragUrl1.matches(queryFragUrl2, QUrl::RemoveQuery));
QVERIFY(!queryFragUrl1.matches(queryFragUrl2, QUrl::RemoveFragment));
QVERIFY(queryFragUrl1.matches(queryFragUrl2, QUrl::RemoveQuery | QUrl::RemoveFragment));
QVERIFY(queryFragUrl1.matches(queryFragUrl3, QUrl::RemoveQuery | QUrl::RemoveFragment));
QVERIFY(queryFragUrl3.matches(queryFragUrl1, QUrl::RemoveQuery | QUrl::RemoveFragment));
}
void tst_QUrl::comparison2_data()
{
QTest::addColumn<QUrl>("url1");
QTest::addColumn<QUrl>("url2");
QTest::addColumn<int>("ordering"); // like strcmp
QTest::newRow("null-null") << QUrl() << QUrl() << 0;
QUrl empty;
empty.setPath("/hello"); // ensure it has detached
empty.setPath(QString());
QTest::newRow("null-empty") << QUrl() << empty << 0;
QTest::newRow("scheme-null") << QUrl("x:") << QUrl() << 1;
QTest::newRow("samescheme") << QUrl("x:") << QUrl("x:") << 0;
QTest::newRow("no-fragment-empty-fragment") << QUrl("http://kde.org/dir/") << QUrl("http://kde.org/dir/#") << -1;
QTest::newRow("no-query-empty-query") << QUrl("http://kde.org/dir/") << QUrl("http://kde.org/dir/?") << -1;
QTest::newRow("simple-file-url") << QUrl("file:///home/dfaure/file") << QUrl("file:///home/dfaure/file") << 0;
QTest::newRow("fromLocalFile-vs-ctor") << QUrl::fromLocalFile("/home/dfaure/file") << QUrl("file:///home/dfaure/file") << 0;
// the following three are by choice
// the order could be the opposite and it would still be correct
QTest::newRow("scheme-path") << QUrl("x:") << QUrl("/tmp") << +1;
QTest::newRow("fragment-path") << QUrl("#foo") << QUrl("/tmp") << -1;
QTest::newRow("fragment-scheme") << QUrl("#foo") << QUrl("x:") << -1;
QTest::newRow("noport-zeroport") << QUrl("http://example.com") << QUrl("http://example.com:0") << -1;
// check that nothing is remembered
auto addEmptiedUrl = [](const char *label, const QUrl &url) {
QUrl copy = url;
copy.setUrl(QString());
QTest::addRow("null-latent-%s", label) << QUrl() << copy << 0;
copy = url;
copy.setUrl(nonempty);
QTest::addRow("nonnull-latent-%s", label) << QUrl(nonempty) << copy << 0;
};
addEmptiedUrl("scheme", QUrl("x:"));
addEmptiedUrl("username", QUrl("//user@"));
addEmptiedUrl("password", QUrl("//:pass@"));
addEmptiedUrl("userinfo", QUrl("//user:pass@"));
addEmptiedUrl("host", QUrl("//foo"));
addEmptiedUrl("username-host", QUrl("//user@bar"));
addEmptiedUrl("password-host", QUrl("//:pass@bar"));
addEmptiedUrl("userinfo-host", QUrl("//user:pass@bar"));
addEmptiedUrl("host-port", QUrl("//bar:1"));
addEmptiedUrl("abpath", QUrl("/"));
addEmptiedUrl("relpath", QUrl("hello"));
addEmptiedUrl("abpath-local", QUrl("file:/"));
addEmptiedUrl("relpath-local", QUrl("file:hello"));
addEmptiedUrl("query", QUrl("?boop"));
addEmptiedUrl("fragment", QUrl("#meep"));
}
void tst_QUrl::comparison2()
{
QFETCH(QUrl, url1);
QFETCH(QUrl, url2);
QFETCH(int, ordering);
const Qt::weak_ordering expectedOrdering = [&ordering] {
if (ordering > 0)
return Qt::weak_ordering::greater;
else if (ordering < 0)
return Qt::weak_ordering::less;
return Qt::weak_ordering::equivalent;
}();
if (ordering == 0) {
QCOMPARE(url1.toString(), url2.toString());
QCOMPARE(url1, url2);
QCOMPARE(qHash(url1), qHash(url2));
QCOMPARE(qHash(url1, 1), qHash(url2, 1));
} else if (ordering < 0) {
QCOMPARE_LT(url1.toString(), url2.toString());
QCOMPARE_NE(url1, url2);
QCOMPARE_LT(url1, url2);
} else {
QCOMPARE_GT(url1.toString(), url2.toString());
QCOMPARE_NE(url1, url2);
QCOMPARE_GT(url1, url2);
}
QT_TEST_ALL_COMPARISON_OPS(url1, url2, expectedOrdering);
// redundant checks (the above should catch these)
QCOMPARE(url1 < url2 || url2 < url1, ordering != 0);
QVERIFY(!(url1 < url2 && url2 < url1));
QVERIFY(url1 < url2 || url1 == url2 || url2 < url1);
}
void tst_QUrl::copying()
{
QVERIFY(url.isValid());
QUrl copy(url);
QCOMPARE(url, copy);
}
void tst_QUrl::setUrl()
{
{
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString::fromLatin1("http"));
QCOMPARE(url.path(), QString());
QCOMPARE(url.host(), QString::fromLatin1("0.foo.com"));
}
{
QUrl url("file:/");
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString::fromLatin1("file"));
QCOMPARE(url.path(), QString::fromLatin1("/"));
QVERIFY(url.query().isEmpty());
QVERIFY(url.userInfo().isEmpty());
QVERIFY(url.authority().isEmpty());
QVERIFY(url.fragment().isEmpty());
QCOMPARE(url.port(), -1);
QCOMPARE(url.toDisplayString(QUrl::PreferLocalFile), QString::fromLatin1("/"));
}
{
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString::fromLatin1("http"));
QCOMPARE(url.path(), QString());
QVERIFY(url.query().isEmpty());
QVERIFY(url.userInfo().isEmpty());
QVERIFY(url.fragment().isEmpty());
QCOMPARE(url.host(), QString::fromLatin1("www.foo.bar"));
QCOMPARE(url.authority(), QString::fromLatin1("www.foo.bar:80"));
QCOMPARE(url.port(), 80);
QUrl url2("//www1.foo.bar");
}
{
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString::fromLatin1("http"));
QCOMPARE(url.path(), QString());
QVERIFY(url.query().isEmpty());
QCOMPARE(url.userName(), QString::fromLatin1("user:"));
QCOMPARE(url.password(), QString::fromLatin1("pass@"));
QCOMPARE(url.userInfo(), QString::fromLatin1("user%3A:pass@"));
QVERIFY(url.fragment().isEmpty());
QCOMPARE(url.host(), QString::fromLatin1("56::56:56:56:7f00:1"));
QCOMPARE(url.authority(), QString::fromLatin1("user%3A:pass%40@[56::56:56:56:7f00:1]:99"));
QCOMPARE(url.port(), 99);
}
{
QUrl url("http://user@localhost:8000/xmlhttprequest/resources/basic-auth-nouserpass/basic-auth-nouserpass.php");
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString::fromLatin1("http"));
QCOMPARE(url.userName(), QString::fromLatin1("user"));
QCOMPARE(url.password(), QString());
QCOMPARE(url.userInfo(), QString::fromLatin1("user"));
QCOMPARE(url.port(), 8000);
}
{
QVERIFY(url.isValid());
QUrl url2("/top//test/../test1/file.html");
QCOMPARE(url.resolved(url2).toString(), QString::fromLatin1("http://www.foo.bar/top//test1/file.html"));
}
{
QVERIFY(url.isValid());
QUrl url2("/top//test/../test1/file.html");
QCOMPARE(url.resolved(url2).toString(), QString::fromLatin1("http://www.foo.bar/top//test1/file.html"));
}
{
QVERIFY(url.isValid());
}
{
QVERIFY(url.isValid());
}
{
QUrl url("file:/usr/local/src/kde2/////kdelibs/kio");
QVERIFY(url.isValid());
}
{
QVERIFY(url.isValid());
QUrl url2("mailto:bastian@kde.org");
QVERIFY(url2.isValid());
QCOMPARE(url.resolved(url2).toString(), QString::fromLatin1("mailto:bastian@kde.org"));
}
{
QUrl url("mailto:bastian@kde.org?subject=hello");
QCOMPARE(url.toString(), QString::fromLatin1("mailto:bastian@kde.org?subject=hello"));
}
{
QUrl url("file:/usr/local/src/kde2/kdelibs/kio/");
QVERIFY(url.isValid());
QUrl url2("../../////kdebase/konqueror");
QCOMPARE(url.resolved(url2).toString(),
}
{
QString u1 = "file:/home/dfaure/my#myref";
QUrl url = u1;
QVERIFY(url.isValid());
QCOMPARE(url.fragment(), QString::fromLatin1("myref"));
}
{
QUrl url("gg:www.kde.org");
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString::fromLatin1("gg"));
QVERIFY(url.host().isEmpty());
QCOMPARE(url.path(), QString::fromLatin1("www.kde.org"));
}
{
QUrl url("KDE");
QVERIFY(url.isValid());
QCOMPARE(url.path(), QString::fromLatin1("KDE"));
QVERIFY(url.scheme().isEmpty());
}
{
QUrl url("$HOME/.kde/share/config");
QVERIFY(url.isValid());
QCOMPARE(url.path(), QString::fromLatin1("$HOME/.kde/share/config"));
QVERIFY(url.scheme().isEmpty());
}
{
QUrl url("file:/opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect");
QVERIFY(url.isValid());
QCOMPARE(url.fragment(), QString::fromLatin1("QObject::connect"));
}
{
QUrl url("file:/opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject:connect");
QVERIFY(url.isValid());
QCOMPARE(url.fragment(), QString::fromLatin1("QObject:connect"));
}
{
// suburls
QUrl url("file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/#myref");
QVERIFY(url.isValid());
// or simply 'myref?'
QCOMPARE(url.fragment(), QString::fromLatin1("gzip:/#tar:/#myref"));
}
{
QVERIFY(url.isValid());
}
{
// suburls
QUrl url("file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/");
QVERIFY(url.isValid());
}
{
QUrl url("file:/home/dfaure/cdrdao-1.1.5/dao/#CdrDriver.cc#");
QVERIFY(url.isValid());
}
{
QUrl url("file:/home/dfaure/my%20tar%20file.tgz#gzip:/#tar:/README");
QVERIFY(url.isValid());
QCOMPARE(url.toString(), QString::fromLatin1("file:///home/dfaure/my tar file.tgz#gzip:/#tar:/README"));
}
{
QVERIFY(notPretty.isValid());
QUrl notPretty2("file:/home/test/directory%20with%20spaces");
QVERIFY(notPretty2.isValid());
QVERIFY(notPretty3.isValid());
QUrl url15581;
QCOMPARE(url15581.toString(), QString::fromLatin1("http://alain.knaff.linux.lu/bug-reports/kde/spaces in url.html"));
QCOMPARE(url15581.toEncoded().constData(), QByteArray("http://alain.knaff.linux.lu/bug-reports/kde/spaces%20in%20url.html").constData());
QCOMPARE(url15582.toString(), QString::fromLatin1("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html"));
QCOMPARE(url15582.toEncoded(), QByteArray("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html"));
QCOMPARE(url15582.toString(QUrl::FullyEncoded), QString("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html"));
}
{
QUrl carsten;
carsten.setPath("/home/gis/src/kde/kdelibs/kfile/.#kfiledetailview.cpp.1.18");
QCOMPARE(carsten.path(), QString::fromLatin1("/home/gis/src/kde/kdelibs/kfile/.#kfiledetailview.cpp.1.18"));
QUrl charles;
charles.setPath("/home/charles/foo%20moo");
QCOMPARE(charles.path(), QString::fromLatin1("/home/charles/foo%20moo"));
QCOMPARE(charles.path(QUrl::FullyEncoded), QString::fromLatin1("/home/charles/foo%2520moo"));
QUrl charles2("file:/home/charles/foo%20moo");
QCOMPARE(charles2.path(), QString::fromLatin1("/home/charles/foo moo"));
QCOMPARE(charles2.path(QUrl::FullyEncoded), QString::fromLatin1("/home/charles/foo%20moo"));
}
{
QUrl udir;
QCOMPARE(udir.toEncoded(), QByteArray());
QCOMPARE(udir.toString(QUrl::FullyEncoded), QString());
QVERIFY(!udir.isValid());
udir = QUrl::fromLocalFile("/home/dfaure/file.txt");
QCOMPARE(udir.path(), QString::fromLatin1("/home/dfaure/file.txt"));
}
{
QUrl url;
url.setUrl("hello.com#?");
QVERIFY(url.isValid());
url.setUrl("hello.com");
QVERIFY(!url.toString().contains(QLatin1Char('#')));
QVERIFY(!url.toString().contains(QLatin1Char('?')));
}
{
QUrl url;
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString("http"));
QCOMPARE(url.host(), QString("1.2.3.4.example.com"));
}
{
QUrl url;
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString("http"));
QCOMPARE(url.host(), QString("1.2.3.4"));
}
{
QUrl url;
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString("http"));
QCOMPARE(url.host(), QString("1.2.3.4"));
QCOMPARE(url.path(), QString("/"));
}
{
QUrl url;
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString("http"));
QCOMPARE(url.host(), QString("1.2.3.4"));
QCOMPARE(url.query(QUrl::FullyEncoded), QLatin1String("foo"));
}
{
QUrl url;
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString("http"));
QCOMPARE(url.host(), QString("1.2.3.4"));
QCOMPARE(url.fragment(), QString("bar"));
}
{
QUrl url("data:text/javascript,d5%20%3D%20'five\\u0027s'%3B");
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), QString("data"));
QCOMPARE(url.host(), QString());
QCOMPARE(url.path(), QString("text/javascript,d5 = 'five\\u0027s';"));
QCOMPARE(url.path(QUrl::FullyEncoded), QLatin1String("text/javascript,d5%20%3D%20'five%5Cu0027s'%3B"));
}
{
// invalid port number
QUrl url;
QVERIFY(!url.isValid());
}
{ //check it calls detach
QUrl u2 = u1;
QCOMPARE(u1.host(), QString::fromLatin1("aaa.com"));
QCOMPARE(u2.host(), QString::fromLatin1("bbb.com"));
}
}
void tst_QUrl::i18n_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QByteArray>("punyOutput");
QTest::newRow("øl") << QString::fromUtf8("http://ole:passord@www.øl.no/index.html?ole=æsemann&ilder gud=hei#top")
<< QByteArray("http://ole:passord@www.xn--l-4ga.no/index.html?ole=%C3%A6semann&ilder%20gud=hei#top");
QTest::newRow("non-latin1")
}
void tst_QUrl::i18n()
{
QFETCH(QString, input);
QFETCH(QByteArray, punyOutput);
QUrl url(input);
QVERIFY(url.isValid());
QCOMPARE(url.toEncoded().constData(), punyOutput.constData());
QCOMPARE(QUrl::fromEncoded(punyOutput), url);
QCOMPARE(QUrl::fromEncoded(punyOutput).toString(), input);
}
void tst_QUrl::resolving_data()
{
QTest::addColumn<QString>("baseUrl");
QTest::addColumn<QString>("relativeUrl");
QTest::addColumn<QString>("resolvedUrl");
// boundary cases
QTest::newRow("/-on-empty-no-authority") << "scheme:" << "/" << "scheme:/";
QTest::newRow(".-on-empty-no-authority") << "scheme:" << "." << "scheme:";
QTest::newRow("./-on-empty-no-authority") << "scheme:" << "./" << "scheme:";
QTest::newRow(".//-on-empty-no-authority") << "scheme:" << ".//" << "scheme:";
QTest::newRow("..-on-empty-no-authority") << "scheme:" << ".." << "scheme:";
QTest::newRow("../-on-empty-no-authority") << "scheme:" << "../" << "scheme:";
// URL paths not ending in /
QTest::newRow("g:h") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g:h") << QString::fromLatin1("g:h");
QTest::newRow("g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g") << QString::fromLatin1("http://a/b/c/g");
QTest::newRow("./g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("./g") << QString::fromLatin1("http://a/b/c/g");
QTest::newRow("g/") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g/") << QString::fromLatin1("http://a/b/c/g/");
QTest::newRow("/g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("/g") << QString::fromLatin1("http://a/g");
QTest::newRow("//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("//g") << QString::fromLatin1("http://g");
QTest::newRow("?y") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("?y") << QString::fromLatin1("http://a/b/c/d;p?y");
QTest::newRow("g?y") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g?y") << QString::fromLatin1("http://a/b/c/g?y");
QTest::newRow("#s") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("#s") << QString::fromLatin1("http://a/b/c/d;p?q#s");
QTest::newRow("g#s") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g#s") << QString::fromLatin1("http://a/b/c/g#s");
QTest::newRow("g?y#s") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g?y#s") << QString::fromLatin1("http://a/b/c/g?y#s");
QTest::newRow(";x") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1(";x") << QString::fromLatin1("http://a/b/c/;x");
QTest::newRow("g;x") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g;x") << QString::fromLatin1("http://a/b/c/g;x");
QTest::newRow("g;x?y#s") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g;x?y#s") << QString::fromLatin1("http://a/b/c/g;x?y#s");
QTest::newRow("[empty]") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("") << QString::fromLatin1("http://a/b/c/d;p?q");
QTest::newRow(".") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1(".") << QString::fromLatin1("http://a/b/c/");
QTest::newRow("./") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("./") << QString::fromLatin1("http://a/b/c/");
QTest::newRow(".//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1(".//") << QString::fromLatin1("http://a/b/c//");
QTest::newRow("..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..") << QString::fromLatin1("http://a/b/");
QTest::newRow("../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../") << QString::fromLatin1("http://a/b/");
QTest::newRow("..//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..//") << QString::fromLatin1("http://a/b//");
QTest::newRow("../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../g") << QString::fromLatin1("http://a/b/g");
QTest::newRow("..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..//g") << QString::fromLatin1("http://a/b//g");
QTest::newRow("../..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../..") << QString::fromLatin1("http://a/");
QTest::newRow("../../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../") << QString::fromLatin1("http://a/");
QTest::newRow("../..//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../..//") << QString::fromLatin1("http://a//");
QTest::newRow("../../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../g") << QString::fromLatin1("http://a/g");
QTest::newRow("../..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../..//g") << QString::fromLatin1("http://a//g");
// URL paths ending in /
QTest::newRow("g:h-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g:h") << QString::fromLatin1("g:h");
QTest::newRow("g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g") << QString::fromLatin1("http://a/b/c/g");
QTest::newRow("./g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("./g") << QString::fromLatin1("http://a/b/c/g");
QTest::newRow("g/-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g/") << QString::fromLatin1("http://a/b/c/g/");
QTest::newRow("/g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("/g") << QString::fromLatin1("http://a/g");
QTest::newRow("//g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("//g") << QString::fromLatin1("http://g");
QTest::newRow("?y-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("?y") << QString::fromLatin1("http://a/b/c/;p?y");
QTest::newRow("g?y-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g?y") << QString::fromLatin1("http://a/b/c/g?y");
QTest::newRow("#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("#s") << QString::fromLatin1("http://a/b/c/;p?q#s");
QTest::newRow("g#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g#s") << QString::fromLatin1("http://a/b/c/g#s");
QTest::newRow("g?y#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g?y#s") << QString::fromLatin1("http://a/b/c/g?y#s");
QTest::newRow(";x-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1(";x") << QString::fromLatin1("http://a/b/c/;x");
QTest::newRow("g;x-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g;x") << QString::fromLatin1("http://a/b/c/g;x");
QTest::newRow("g;x?y#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g;x?y#s") << QString::fromLatin1("http://a/b/c/g;x?y#s");
QTest::newRow("[empty]-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("") << QString::fromLatin1("http://a/b/c/;p?q");
QTest::newRow(".-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1(".") << QString::fromLatin1("http://a/b/c/");
QTest::newRow("./-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("./") << QString::fromLatin1("http://a/b/c/");
QTest::newRow(".//-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1(".//") << QString::fromLatin1("http://a/b/c//");
QTest::newRow("..-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("..") << QString::fromLatin1("http://a/b/");
QTest::newRow("../-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../") << QString::fromLatin1("http://a/b/");
QTest::newRow("..//-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("..//") << QString::fromLatin1("http://a/b//");
QTest::newRow("../g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../g") << QString::fromLatin1("http://a/b/g");
QTest::newRow("..//g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("..//g") << QString::fromLatin1("http://a/b//g");
QTest::newRow("../..-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../..") << QString::fromLatin1("http://a/");
QTest::newRow("../../-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../../") << QString::fromLatin1("http://a/");
QTest::newRow("../..//-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../..//") << QString::fromLatin1("http://a//");
QTest::newRow("../../g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../../g") << QString::fromLatin1("http://a/g");
QTest::newRow("../..//g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../..//g") << QString::fromLatin1("http://a//g");
// URL paths ending in //
// Parsers must be careful in handling cases where there are more
// relative path ".." segments than there are hierarchical levels in the
// base URI's path. Note that the ".." syntax cannot be used to change
// the authority component of a URI.
QTest::newRow("../../../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../") << QString::fromLatin1("http://a/");
QTest::newRow("../../../..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../..") << QString::fromLatin1("http://a/");
QTest::newRow("../../../..//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../..//") << QString::fromLatin1("http://a//");
QTest::newRow("../../../../..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../../..") << QString::fromLatin1("http://a/");
QTest::newRow("../../../../../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../../../") << QString::fromLatin1("http://a/");
QTest::newRow("../../../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../g") << QString::fromLatin1("http://a/g");
QTest::newRow("../../..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../..//g") << QString::fromLatin1("http://a//g");
QTest::newRow("../../../../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../../g") << QString::fromLatin1("http://a/g");
QTest::newRow("../../../..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../..//g") << QString::fromLatin1("http://a//g");
// Similarly, parsers must remove the dot-segments "." and ".." when
// they are complete components of a path, but not when they are only
// part of a segment.
QTest::newRow("/./g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("/./g") << QString::fromLatin1("http://a/g");
QTest::newRow("/../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("/../g") << QString::fromLatin1("http://a/g");
QTest::newRow("g.") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g.") << QString::fromLatin1("http://a/b/c/g.");
QTest::newRow(".g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1(".g") << QString::fromLatin1("http://a/b/c/.g");
QTest::newRow("g..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g..") << QString::fromLatin1("http://a/b/c/g..");
QTest::newRow("..g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..g") << QString::fromLatin1("http://a/b/c/..g");
// Less likely are cases where the relative URI reference uses
// unnecessary or nonsensical forms of the "." and ".." complete path
// segments.
QTest::newRow("./../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("./../g") << QString::fromLatin1("http://a/b/g");
QTest::newRow("./g/.") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("./g/.") << QString::fromLatin1("http://a/b/c/g/");
QTest::newRow("g/./h") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g/./h") << QString::fromLatin1("http://a/b/c/g/h");
QTest::newRow("g/../h") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g/../h") << QString::fromLatin1("http://a/b/c/h");
QTest::newRow("g;x=1/./y") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g;x=1/./y") << QString::fromLatin1("http://a/b/c/g;x=1/y");
QTest::newRow("g;x=1/../y") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g;x=1/../y") << QString::fromLatin1("http://a/b/c/y");
// Some applications fail to separate the reference's query and/or
// fragment components from a relative path before merging it with the
// base path and removing dot-segments. This error is rarely noticed,
// since typical usage of a fragment never includes the hierarchy ("/")
// character, and the query component is not normally used within
// relative references.
QTest::newRow("g?y/./x") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g?y/./x") << QString::fromLatin1("http://a/b/c/g?y/./x");
QTest::newRow("g?y/../x") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g?y/../x") << QString::fromLatin1("http://a/b/c/g?y/../x");
QTest::newRow("g#s/./x") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g#s/./x") << QString::fromLatin1("http://a/b/c/g#s/./x");
QTest::newRow("g#s/../x") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g#s/../x") << QString::fromLatin1("http://a/b/c/g#s/../x");
// Some parsers allow the scheme name to be present in a relative URI
// reference if it is the same as the base URI scheme. This is
// considered to be a loophole in prior specifications of partial URI [RFC1630],
//QTest::newRow("http:g [for backward compatibility]") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("http:g") << QString::fromLatin1("http://a/b/c/g");
// However we don't do that anymore, as per RFC3986, in order for the data:subpage testcase below to work.
QTest::newRow("http:g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("http:g") << QString::fromLatin1("http:g");
QTest::newRow("data:subpage") << QString::fromLatin1("data:text/plain, main page") << QString::fromLatin1("data:text/plain, subpage") << QString::fromLatin1("data:text/plain, subpage");
// Resolve relative with relative
QTest::newRow("../a (1)") << QString::fromLatin1("b") << QString::fromLatin1("../a") << QString::fromLatin1("a");
QTest::newRow("../a (2)") << QString::fromLatin1("b/a") << QString::fromLatin1("../a") << QString::fromLatin1("a");
QTest::newRow("../a (3)") << QString::fromLatin1("b/c/a") << QString::fromLatin1("../a") << QString::fromLatin1("b/a");
QTest::newRow("../a (4)") << QString::fromLatin1("b") << QString::fromLatin1("/a") << QString::fromLatin1("/a");
QTest::newRow("relative+.") << "scheme:" << "." << "scheme:";
QTest::newRow("relative+./") << "scheme:" << "./" << "scheme:";
QTest::newRow("relative+.//") << "scheme:" << ".//" << "scheme:";
QTest::newRow("relative+.///") << "scheme:" << ".///" << "scheme:";
QTest::newRow("relative+./.") << "scheme:" << "./." << "scheme:";
QTest::newRow("relative+././") << "scheme:" << "././" << "scheme:";
QTest::newRow("relative+..") << "scheme:" << ".." << "scheme:";
QTest::newRow("relative+../") << "scheme:" << "../" << "scheme:";
QTest::newRow("relative+..//") << "scheme:" << "..//" << "scheme:";
QTest::newRow("relative+..///") << "scheme:" << "..///" << "scheme:";
QTest::newRow("relative+../.") << "scheme:" << "../." << "scheme:";
QTest::newRow("relative+.././") << "scheme:" << ".././" << "scheme:";
QTest::newRow("relative+.././/") << "scheme:" << ".././/" << "scheme:";
QTest::newRow("relative+.././//") << "scheme:" << ".././//" << "scheme:";
QTest::newRow("relative+../../../..") << "scheme:b/c/d" << "../../../.." << "scheme:";
QTest::newRow("relative+../../../../") << "scheme:b/c/d" << "../../../../" << "scheme:";
QTest::newRow("relative+../../../..//") << "scheme:b/c/d" << "../../../..//" << "scheme:";
QTest::newRow("relative+../../d/../..") << "scheme:b/c/d" << "../../d/../.." << "scheme:";
QTest::newRow("relative+../../d/../../") << "scheme:b/c/d" << "../../d/../../" << "scheme:";
QTest::newRow("relative+endslash+../../../..") << "scheme:b/c/d/" << "../../../.." << "scheme:";
QTest::newRow("relative+endslash+../../../../") << "scheme:b/c/d/" << "../../../../" << "scheme:";
QTest::newRow("relative+endslash+../../../..//") << "scheme:b/c/d/" << "../../../..//" << "scheme:";
// Resolve absolute without authority with relative
QTest::newRow("../a (5)") << QString::fromLatin1("/b") << QString::fromLatin1("../a") << QString::fromLatin1("/a");
QTest::newRow("../a (6)") << QString::fromLatin1("/b/a") << QString::fromLatin1("../a") << QString::fromLatin1("/a");
QTest::newRow("../a (7)") << QString::fromLatin1("/b/c/a") << QString::fromLatin1("../a") << QString::fromLatin1("/b/a");
QTest::newRow("../a (8)") << QString::fromLatin1("/b") << QString::fromLatin1("/a") << QString::fromLatin1("/a");
QTest::newRow("noauthority+.") << "scheme:/a/b" << "." << "scheme:/a/";
QTest::newRow("noauthority+./") << "scheme:/a/b" << "./" << "scheme:/a/";
QTest::newRow("noauthority+.//") << "scheme:/a/b" << ".//" << "scheme:/a//";
QTest::newRow("noauthority+./d") << "scheme:/a/b" << "./d" << "scheme:/a/d";
QTest::newRow("noauthority+.//d") << "scheme:/a/b" << ".//d" << "scheme:/a//d";
QTest::newRow("noauthority+..") << "scheme:/a/b" << ".." << "scheme:/";
QTest::newRow("noauthority+../") << "scheme:/a/b" << "../" << "scheme:/";
QTest::newRow("noauthority+..//") << "scheme:/a/b" << "..//" << "scheme:/";
QTest::newRow("noauthority+../d") << "scheme:/a/b" << "../d" << "scheme:/d";
QTest::newRow("noauthority+..//d") << "scheme:/a/b" << "..//d" << "scheme:/d"; // no double slash!
QTest::newRow("noauthority+../..") << "scheme:/a/b" << "../.." << "scheme:/";
QTest::newRow("noauthority+../../") << "scheme:/a/b" << "../../" << "scheme:/";
QTest::newRow("noauthority+../..//") << "scheme:/a/b" << "../..//" << "scheme:/";
QTest::newRow("noauthority+../../d") << "scheme:/a/b" << "../../d" << "scheme:/d";
QTest::newRow("noauthority+../..//d") << "scheme:/a/b" << "../..//d" << "scheme:/d"; // no double slash!
// More tests from KDE
QTest::newRow("brackets") << QString::fromLatin1("http://www.calorieking.com/personal/diary/") << QString::fromLatin1("/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458") << QString::fromLatin1("http://www.calorieking.com/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458");
QTest::newRow("javascript")<< QString::fromLatin1("http://www.youtube.com/?v=JvOSnRD5aNk") << QString::fromLatin1("javascript:window.location+\"__flashplugin_unique__\"") << QString::fromLatin1("javascript:window.location+%22__flashplugin_unique__%22");
}
void tst_QUrl::resolving()
{
QFETCH(QString, baseUrl);
QFETCH(QString, relativeUrl);
QFETCH(QString, resolvedUrl);
QUrl url(baseUrl);
QCOMPARE(url.resolved(relativeUrl).toString(), resolvedUrl);
}
void tst_QUrl::toString_data()
{
QTest::addColumn<QString>("urlString");
QTest::addColumn<uint>("options");
QTest::addColumn<QString>("string");
QTest::newRow("data0") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme)
<< QString::fromLatin1("//ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top");
QTest::newRow("data2") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemovePassword)
// show that QUrl keeps the empty-but-present username if you remove the password
// see data3-bis for another case
QTest::newRow("data2-bis") << QString::fromLatin1("http://:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemovePassword)
QTest::newRow("data3") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveUserInfo)
// show that QUrl keeps the empty-but-preset hostname if you remove the userinfo
QTest::newRow("data3-bis") << QString::fromLatin1("http://ole:password@/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveUserInfo)
QTest::newRow("data4") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemovePort)
QTest::newRow("data5") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveAuthority)
<< QString::fromLatin1("http:/index.html?ole=semann&gud=hei#top");
QTest::newRow("data6") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemovePath)
QTest::newRow("data7") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveQuery)
QTest::newRow("data8") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveFragment)
QTest::newRow("data9") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemovePassword)
<< QString::fromLatin1("//ole@www.troll.no:9090/index.html?ole=semann&gud=hei#top");
QTest::newRow("data10") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemoveUserInfo)
<< QString::fromLatin1("//www.troll.no:9090/index.html?ole=semann&gud=hei#top");
QTest::newRow("data11") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemovePort)
<< QString::fromLatin1("//ole:password@www.troll.no/index.html?ole=semann&gud=hei#top");
QTest::newRow("data12") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemoveAuthority)
<< QString::fromLatin1("/index.html?ole=semann&gud=hei#top");
QTest::newRow("data13") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemovePath)
<< QString::fromLatin1("//ole:password@www.troll.no:9090?ole=semann&gud=hei#top");
QTest::newRow("data14") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemoveAuthority | QUrl::RemoveFragment)
<< QString::fromLatin1("/index.html?ole=semann&gud=hei");
QTest::newRow("data15") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveAuthority | QUrl::RemoveQuery)
<< QString::fromLatin1("http:/index.html#top");
QTest::newRow("data16") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemovePassword | QUrl::RemovePort
| QUrl::RemovePath | QUrl::RemoveQuery
| QUrl::RemoveFragment)
QTest::newRow("data17") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top")
<< uint(QUrl::RemoveScheme | QUrl::RemovePassword
| QUrl::RemovePort | QUrl::RemovePath
| QUrl::RemoveQuery | QUrl::RemoveFragment)
<< QString::fromLatin1("//ole@www.troll.no");
QTest::newRow("data18") << QString::fromLatin1("http://andreas:hemmelig@www.vg.no/?my=query&your=query#yougotfragged")
<< uint(QUrl::None)
<< uint(QUrl::None)
<< uint(QUrl::None)
QTest::newRow("mailto-brackets") << QString::fromLatin1("mailto:test[at]gmail[dot]com")
<< uint(QUrl::PrettyDecoded)
<< QString::fromLatin1("mailto:test[at]gmail[dot]com");
QTest::newRow("mailto-query") << QString::fromLatin1("mailto:?to=test@example.com")
<< uint(QUrl::PrettyDecoded)
<< QString::fromLatin1("mailto:?to=test@example.com");
}
void tst_QUrl::toString()
{
QFETCH(QString, urlString);
QFETCH(uint, options);
QFETCH(QString, string);
QUrl::FormattingOptions opt(options);
QUrl url(urlString);
QCOMPARE(url.toString(opt), string);
QCOMPARE(url.adjusted(opt).toString(), string);
}
void tst_QUrl::toString_PreferLocalFile_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QString>("string");
#ifdef Q_OS_WIN
<< QString::fromLatin1("c:/windows/regedit.exe");
QTest::newRow("win-share") << QUrl(QString::fromLatin1("//Anarki/homes"))
<< QString::fromLatin1("//anarki/homes");
#else
<< QString::fromLatin1("/tmp");
#endif
}
void tst_QUrl::toString_PreferLocalFile()
{
QFETCH(QUrl, url);
QFETCH(QString, string);
QCOMPARE(url.toString(QUrl::PreferLocalFile), string);
}
void tst_QUrl::toAndFromStringList_data()
{
QTest::addColumn<QStringList>("strings");
QTest::newRow("empty") << QStringList();
}
void tst_QUrl::toAndFromStringList()
{
QFETCH(QStringList, strings);
const QList<QUrl> urls = QUrl::fromStringList(strings);
QCOMPARE(urls.size(), strings.size());
const QStringList converted = QUrl::toStringList(urls);
QCOMPARE(converted, strings);
}
//### more tests ... what do we expect ...
void tst_QUrl::isParentOf_data()
{
QTest::addColumn<QString>("parent");
QTest::addColumn<QString>("child");
QTest::addColumn<bool>("trueFalse");
}
void tst_QUrl::toString_constructed_data()
{
QTest::addColumn<QString>("scheme");
QTest::addColumn<QString>("userName");
QTest::addColumn<QString>("password");
QTest::addColumn<QString>("host");
QTest::addColumn<int>("port");
QTest::addColumn<QString>("path");
QTest::addColumn<QString>("query");
QTest::addColumn<QString>("fragment");
QTest::addColumn<QString>("asString");
QTest::addColumn<QByteArray>("asEncoded");
QTest::addColumn<uint>("options");
QString n("");
QTest::newRow("data1") << n << n << n << QString::fromLatin1("qt-project.org") << -1 << QString::fromLatin1("/index.html")
<< QString() << n << QString::fromLatin1("//qt-project.org/index.html")
<< QByteArray("//qt-project.org/index.html") << 0u;
QTest::newRow("data2") << QString::fromLatin1("file") << n << n << n << -1 << QString::fromLatin1("/root") << QString()
QTest::newRow("userAndPass") << QString::fromLatin1("http") << QString::fromLatin1("dfaure") << QString::fromLatin1("kde")
<< "kde.org" << 443 << QString::fromLatin1("/") << QString() << n
<< QString::fromLatin1("http://dfaure:kde@kde.org:443/") << QByteArray("http://dfaure:kde@kde.org:443/")
<< 0u;
QTest::newRow("PassWithoutUser") << QString::fromLatin1("http") << n << QString::fromLatin1("kde")
<< "kde.org" << 443 << QString::fromLatin1("/") << QString() << n
QTest::newRow("PassWithoutUser-RemovePassword") << QString::fromLatin1("http") << n << QString::fromLatin1("kde")
<< "kde.org" << 443 << QString::fromLatin1("/") << QString() << n
<< uint(QUrl::RemovePassword);
}
void tst_QUrl::toString_constructed()
{
QFETCH(QString, scheme);
QFETCH(QString, userName);
QFETCH(QString, password);
QFETCH(QString, host);
QFETCH(int, port);
QFETCH(QString, path);
QFETCH(QString, query);
QFETCH(QString, fragment);
QFETCH(QString, asString);
QFETCH(QByteArray, asEncoded);
QFETCH(uint, options);
QUrl url;
if (!scheme.isEmpty())
url.setScheme(scheme);
if (!userName.isEmpty())
url.setUserName(userName);
if (!password.isEmpty())
url.setPassword(password);
if (!host.isEmpty())
url.setHost(host);
if (port != -1)
url.setPort(port);
if (!path.isEmpty())
url.setPath(path);
if (!query.isEmpty())
url.setQuery(query, QUrl::StrictMode);
if (!fragment.isEmpty())
url.setFragment(fragment);
QVERIFY(url.isValid());
QUrl::FormattingOptions formattingOptions(options);
QCOMPARE(url.toString(formattingOptions), asString);
QCOMPARE(QString::fromLatin1(url.toEncoded(formattingOptions)), QString::fromLatin1(asEncoded)); // readable in case of differences
QCOMPARE(url.toEncoded(formattingOptions), asEncoded);
if (options == QUrl::UrlFormattingOption::None) {
QUrl parsed(asString);
QCOMPARE(url, parsed);
QCOMPARE(qHash(url), qHash(parsed));
QCOMPARE(qHash(url, 1), qHash(parsed, 1));
}
// clear it and ensure no memory of the previous state remains
url.setUrl(QString());
QCOMPARE(url, QUrl());
QCOMPARE(qHash(url), qHash(QUrl()));
QCOMPARE(qHash(url, 1), qHash(QUrl(), 1));
}
void tst_QUrl::toDisplayString_PreferLocalFile_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QString>("string");
QTest::newRow("basic") << QUrl::fromLocalFile("/home/charles/foomoo")
<< QString::fromLatin1("/home/charles/foomoo");
QTest::newRow("with%") << QUrl::fromLocalFile("/home/charles/foo%20moo")
<< QString::fromLatin1("/home/charles/foo%20moo");
<< QString::fromLatin1("//host/foo");
QTest::newRow("query-and-fragment") << QUrl("file://user:pass@example.org/a?b=c%20d%23e#frag%23ment")
}
void tst_QUrl::toDisplayString_PreferLocalFile()
{
QFETCH(QUrl, url);
QFETCH(QString, string);
if (url.isLocalFile() && url.query().isEmpty() && url.fragment().isEmpty())
QCOMPARE(url.toLocalFile(), string);
QCOMPARE(url.toDisplayString(QUrl::PreferLocalFile), string);
}
void tst_QUrl::isParentOf()
{
QFETCH(QString, parent);
QFETCH(QString, child);
QFETCH(bool, trueFalse);
QUrl url(parent);
QCOMPARE(url.isParentOf(QUrl(child)), trueFalse);
}
void tst_QUrl::toLocalFile_data()
{
QTest::addColumn<QString>("theUrl");
QTest::addColumn<QString>("theFile");
QTest::newRow("file:/") << QString::fromLatin1("file:/a.txt") << QString::fromLatin1("/a.txt");
QTest::newRow("file:////") << QString::fromLatin1("file:////somehost/somedir/somefile") << QString::fromLatin1("//somehost/somedir/somefile");
QTest::newRow("FILE:/") << QString::fromLatin1("FILE:/a.txt") << QString::fromLatin1("/a.txt");
QTest::newRow("path-delimiter") << QString::fromLatin1("file:///Mambo <%235>.mp3") << QString::fromLatin1("/Mambo <#5>.mp3");
QTest::newRow("path-percent") << QString::fromLatin1("file:///a%25.txt") << QString::fromLatin1("/a%.txt");
QTest::newRow("path-percent-percent") << QString::fromLatin1("file:///a%25%25.txt") << QString::fromLatin1("/a%%.txt");
QTest::newRow("path-percent-a-percent") << QString::fromLatin1("file:///a%25a%25.txt") << QString::fromLatin1("/a%a%.txt");
QTest::newRow("path-control-char") << QString::fromLatin1("file:///a%1f.txt") << QString::fromLatin1("/a\x1f.txt");
QTest::newRow("path-percent-hex-hex") << QString::fromLatin1("file:///%2580.txt") << QString::fromLatin1("/%80.txt");
#ifdef Q_OS_WIN // QTBUG-42346, WebDAV is visible as local file on Windows only.
<< QString::fromLatin1("//somewebdavhost@SSL/somedir/somefile");
#else
<< QString();
#endif
#ifdef Q_OS_WIN
QTest::newRow("windows-drive-absolute") << QString::fromLatin1("file:///c:/a.txt") << QString::fromLatin1("c:/a.txt");
#else
QTest::newRow("windows-drive-absolute") << QString::fromLatin1("file:///c:/a.txt") << QString::fromLatin1("/c:/a.txt");
#endif
QTest::newRow("windows-unc-path") << QString::fromLatin1("file://somehost/somedir/somefile") << QString::fromLatin1("//somehost/somedir/somefile");
QTest::newRow("windows-unc-root") << QString::fromLatin1("file://somehost/") << QString::fromLatin1("//somehost/");
QTest::newRow("windows-unc-nopath") << QString::fromLatin1("file://somehost") << QString::fromLatin1("//somehost");
QTest::newRow("windows-extlen-path") << QString::fromLatin1("file:////%3F/somedir/somefile") << QString::fromLatin1("//?/somedir/somefile");
QTest::newRow("windows-wsl-path") << QString::fromLatin1("file:////wsl$/somedir/somefile") << QString::fromLatin1("//wsl$/somedir/somefile");
QTest::newRow("windows-device-path") << QString::fromLatin1("file:////./somedir/somefile") << QString::fromLatin1("//./somedir/somefile");
// and some that result in empty (i.e., not local)
QTest::newRow("noscheme-absolute") << QString::fromLatin1("/a.txt") << QString();
QTest::newRow("noscheme-host") << QString::fromLatin1("//a.txt") << QString();
QTest::newRow("noscheme-host-path") << QString::fromLatin1("///a.txt") << QString();
QTest::newRow("fooscheme-absolute") << QString::fromLatin1("foo:/a.txt") << QString();
}
void tst_QUrl::toLocalFile()
{
QFETCH(QString, theUrl);
QFETCH(QString, theFile);
QUrl url(theUrl);
QCOMPARE(url.toLocalFile(), theFile);
QCOMPARE(url.isLocalFile(), !theFile.isEmpty());
// set the path to the same (encoded) thing - nothing should change
url.setPath(url.path(QUrl::FullyEncoded), QUrl::TolerantMode);
QCOMPARE(url.toLocalFile(), theFile);
QCOMPARE(url.isLocalFile(), !theFile.isEmpty());
// QUrl::PrettyDecoded is still URL-encoded and lossless
url.setPath(url.path(QUrl::PrettyDecoded), QUrl::TolerantMode);
QCOMPARE(url.toLocalFile(), theFile);
QCOMPARE(url.isLocalFile(), !theFile.isEmpty());
// local file paths can be fully decoded without loss
url.setPath(url.path());
QCOMPARE(url.toLocalFile(), theFile);
QCOMPARE(url.isLocalFile(), !theFile.isEmpty());
}
void tst_QUrl::toLocalFileUnsafe_data()
{
QTest::addColumn<QString>("theUrl");
QTest::addColumn<QString>("theFile");
QTest::addColumn<bool>("unsafe");
// Safe: toLocalFile() decodes without loss and returns theFile.
// No normalization happens in toLocalFile(), so "../" survives verbatim.
// Empty path: local file, but toLocalFile() is the empty (not null) string.
QTest::newRow("empty-path") << u"file:"_s << QString() << false;
// Unsafe: the fully-decoded path would contain a byte that is dangerous for
// a local-file path, so toLocalFile() must return a null QString().
#ifdef Q_OS_WIN
// A backslash is a directory separator on Windows only.
#endif
}
void tst_QUrl::toLocalFileUnsafe()
{
QFETCH(QString, theUrl);
QFETCH(QString, theFile);
QFETCH(bool, unsafe);
QUrl url(theUrl);
QVERIFY(url.isValid());
QVERIFY(url.isLocalFile());
const QString localFile = url.toLocalFile();
if (unsafe) {
// toLocalFile() refuses to produce a lossy/dangerous path.
QVERIFY(localFile.isNull());
// path() and normalization must NOT fail: they keep the percent-encoding
// and are still usable for inspection.
QVERIFY(!url.path(QUrl::FullyDecoded).isEmpty());
QVERIFY(!url.path(QUrl::NormalizePathSegments | QUrl::FullyDecoded).isEmpty());
// toString(PreferLocalFile) can't produce a local path either, so it
// falls back to a full "file:" URL.
QVERIFY(url.toString(QUrl::PreferLocalFile).startsWith("file:"_L1));
} else {
QCOMPARE(localFile, theFile);
// toString(PreferLocalFile) produces the same local path.
QCOMPARE(url.toString(QUrl::PreferLocalFile), localFile);
}
}
void tst_QUrl::fromLocalFile_data()
{
QTest::addColumn<QString>("theFile");
QTest::addColumn<QString>("theUrl");
QTest::addColumn<QString>("thePath");
QTest::newRow("absolute-path") << QString::fromLatin1("/a.txt") << QString::fromLatin1("file:///a.txt") << QString::fromLatin1("/a.txt");
QTest::newRow("relative-path") << QString::fromLatin1("a.txt") << QString::fromLatin1("file:a.txt") << QString::fromLatin1("a.txt");
QTest::newRow("absolute-two-path") << QString::fromLatin1("/a/b.txt") << QString::fromLatin1("file:///a/b.txt") << QString::fromLatin1("/a/b.txt");
QTest::newRow("path-delimiters") << QString::fromLatin1("/Mambo <#5>.mp3") << QString::fromLatin1("file:///Mambo <%235>.mp3")
<< QString::fromLatin1("/Mambo <#5>.mp3");
QTest::newRow("path-brackets") << u"/tmp/[test].txt"_s << u"file:///tmp/%5Btest%5D.txt"_s << u"/tmp/[test].txt"_s;
// Windows absolute details
QTest::newRow("windows-drive") << QString::fromLatin1("c:/a.txt") << QString::fromLatin1("file:///c:/a.txt") << QString::fromLatin1("/c:/a.txt");
// Handling of Windows roots with relative - note, no normalization!
QTest::newRow("windows-drive-above-root")
<< QString::fromLatin1("c:/../a.txt") << QString::fromLatin1("file:///c:/../a.txt") << QString::fromLatin1("/c:/../a.txt");
// Windows UNC paths
for (const char *suffix : { "", "/", "/somedir/somefile" }) {
const char *pathDescription =
strlen(suffix) == 0 ? "nopath" :
strlen(suffix) > 1 ? "path" : "root";
QTest::addRow("windows-unc-%s", pathDescription)
<< QString("//somehost") + suffix
<< QString(suffix);
#ifdef Q_OS_WIN32
// debackslashification only happens on Windows
QString suffixWithBackslashes(suffix);
suffixWithBackslashes.replace('/', '\\');
QTest::addRow("windows-backslash-unc-%s", pathDescription)
<< QString(QString("\\\\somehost") + suffixWithBackslashes)
<< QString(suffix);
QTest::addRow("windows-backslash-extlen-%s", pathDescription)
<< QString(QString("\\\\?") + suffixWithBackslashes)
<< QString("//?") + suffix;
#endif
QTest::addRow("windows-extlen-%s", pathDescription)
<< QString("//?") + suffix
<< QString("//?") + suffix;
QTest::addRow("windows-wsl-%s", pathDescription)
<< QString("//wsl$") + suffix
<< QString("//wsl$") + suffix;
QTest::addRow("windows-device--%s", pathDescription)
<< QString("//.") + suffix
<< QString("//.") + suffix;
}
QTest::newRow("windows-webdav")
<< QString::fromLatin1("//somewebdavhost@SSL/somedir/somefile")
<< QString::fromLatin1("/somedir/somefile");
}
void tst_QUrl::fromLocalFile()
{
QFETCH(QString, theFile);
QFETCH(QString, theUrl);
QFETCH(QString, thePath);
QUrl url = QUrl::fromLocalFile(theFile);
QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl);
QCOMPARE(url.path(), thePath);
// set the path to the same (encoded) thing - nothing should change
url.setPath(url.path(QUrl::FullyEncoded), QUrl::TolerantMode);
QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl);
QCOMPARE(url.path(), thePath);
// QUrl::PrettyDecoded is still URL-encoded and lossless
url.setPath(url.path(QUrl::PrettyDecoded), QUrl::TolerantMode);
QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl);
QCOMPARE(url.path(), thePath);
// local file paths can be fully decoded without loss
url.setPath(url.path());
QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl);
QCOMPARE(url.path(), thePath);
}
void tst_QUrl::fromLocalFileNormalize_data()
{
QTest::addColumn<QString>("theFile"); // should support the fromLocalFile/toLocalFile roundtrip (so no //host or windows path)
QTest::addColumn<QString>("theUrl");
QTest::addColumn<QString>("urlWithNormalizedPath");
QTest::newRow("absolute-path") << QString::fromLatin1("/a.txt") << QString::fromLatin1("file:///a.txt") << QString::fromLatin1("file:///a.txt");
QTest::newRow("relative-path") << QString::fromLatin1("a.txt") << QString::fromLatin1("file:a.txt") << QString::fromLatin1("file:a.txt");
QTest::newRow("absolute-path-2-trailing-slashes") << u"/b//"_s << u"file:///b//"_s << u"file:///b/"_s;
QTest::newRow("percent") << QString::fromLatin1("/a%.txt") << QString::fromLatin1("file:///a%25.txt")
QTest::newRow("percent25") << QString::fromLatin1("/a%25.txt") << QString::fromLatin1("file:///a%2525.txt")
QTest::newRow("percent80") << QString::fromLatin1("/%80.txt") << QString::fromLatin1("file:///%2580.txt")
QTest::newRow("relative-dot") << QString::fromLatin1("./a.txt") << QString::fromLatin1("file:./a.txt") << QString::fromLatin1("file:a.txt");
QTest::newRow("relative-dot-dot") << QString::fromLatin1("././a.txt") << QString::fromLatin1("file:././a.txt") << QString::fromLatin1("file:a.txt");
QTest::newRow("relative-path-dotdot") << QString::fromLatin1("b/../a.txt") << QString::fromLatin1("file:b/../a.txt") << QString::fromLatin1("file:a.txt");
QTest::newRow("relative-path-dotdot-dotdot") << QString::fromLatin1("b/../../a.txt") << QString::fromLatin1("file:b/../../a.txt") << QString::fromLatin1("file:../a.txt");
QTest::newRow("absolute-path-dotdot") << QString::fromLatin1("/b/../a.txt") << QString::fromLatin1("file:///b/../a.txt") << QString::fromLatin1("file:///a.txt");
QTest::newRow("absolute-path-dotdot-dotdot") << QString::fromLatin1("/b/../../a.txt") << QString::fromLatin1("file:///b/../../a.txt") << QString::fromLatin1("file:///../a.txt");
QTest::newRow("absolute-path-slash") << QString::fromLatin1("/b/") << QString::fromLatin1("file:///b/") << QString::fromLatin1("file:///b/");
QTest::newRow("absolute-path-slahs-dot") << QString::fromLatin1("/b/.") << QString::fromLatin1("file:///b/.") << QString::fromLatin1("file:///b/");
QTest::newRow("absolute-path-slahs-dot-slash") << QString::fromLatin1("/b/./") << QString::fromLatin1("file:///b/./") << QString::fromLatin1("file:///b/");
QTest::newRow("absolute-path-dotdot-slashslash") << QString::fromLatin1("/b/..//") << QString::fromLatin1("file:///b/..//") << QString::fromLatin1("file:////");
}
void tst_QUrl::fromLocalFileNormalize()
{
QFETCH(QString, theFile);
QFETCH(QString, theUrl);
QFETCH(QString, urlWithNormalizedPath);
QUrl url = QUrl::fromLocalFile(theFile);
QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl);
QCOMPARE(url.toLocalFile(), theFile); // roundtrip
QCOMPARE(url.path(), theFile); // works as well as long as we don't test windows paths
QCOMPARE(url.toString(QUrl::NormalizePathSegments), urlWithNormalizedPath);
}
void tst_QUrl::fromLocalFileNormalizeNonRoundtrip_data()
{
#ifdef Q_OS_WIN32
static constexpr bool IsWindows = true;
#else
static constexpr bool IsWindows = false;
#endif
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("theUrl");
QTest::addColumn<QString>("thePath");
QTest::addColumn<QString>("urlWithNormalizedPath");
QTest::newRow("server/..") << u"//server/.."_s << u"file://server/.."_s << u"/.."_s << u"file://server/.."_s;
QTest::newRow("server/share") << u"//server/share"_s << u"file://server/share"_s << u"/share"_s << u"file://server/share"_s;
QTest::newRow("server/share/..") << u"//server/share/.."_s << u"file://server/share/.."_s << u"/share/.."_s << u"file://server/"_s;
auto addAbsoluteWindowsPathRow = [](const char *name, const QString &input,
const QString &unixNormalized, const QString &windowsNormalized) {
QString thePath = '/' + input; // fromPercentEncoding, but works for now
QString theUrl = "file://" + thePath;
const QString &normalized = IsWindows ? windowsNormalized : unixNormalized;
QTest::newRow(name) << input << theUrl << thePath << normalized;
};
addAbsoluteWindowsPathRow("relative-drive/path", "c:autoexec.bat",
addAbsoluteWindowsPathRow("absolute-drive/path", "c:/config.sys",
addAbsoluteWindowsPathRow("absolute-drive/path/..", "c:/dos/..",
addAbsoluteWindowsPathRow("absolute-drive/path/../", "c:/dos/../",
// The drive root should remain for the normalized URLs on Windows
addAbsoluteWindowsPathRow("absolute-drive/..", "c:/..",
addAbsoluteWindowsPathRow("relative-drive/path/..", "c:dos/..",
addAbsoluteWindowsPathRow("relative-drive/path/../", "c:dos/../",
addAbsoluteWindowsPathRow("relative-drive/path/../..", "c:dos/../..",
addAbsoluteWindowsPathRow("relative-drive/path/../../", "c:dos/../../",
}
void tst_QUrl::fromLocalFileNormalizeNonRoundtrip()
{
QFETCH(QString, input);
QFETCH(QString, theUrl);
QFETCH(QString, thePath);
QFETCH(QString, urlWithNormalizedPath);
QUrl url = QUrl::fromLocalFile(input);
QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl);
QCOMPARE(url.path(), thePath);
QCOMPARE(url.toString(QUrl::NormalizePathSegments), urlWithNormalizedPath);
}
void tst_QUrl::macTypes_data()
{
#ifndef Q_OS_DARWIN
QSKIP("This is a Mac-only test");
#else
extern void tst_QUrl_mactypes_data();
tst_QUrl_mactypes_data();
#endif
}
void tst_QUrl::macTypes()
{
#ifdef Q_OS_DARWIN
extern void tst_QUrl_mactypes();
tst_QUrl_mactypes();
#endif
}
void tst_QUrl::compat_legacy()
{
{
QUrl u( "file:bar" );
QCOMPARE( u.toString(QUrl::RemoveScheme), QString("bar") );
}
/* others
*/
{
QCOMPARE( u.path(), QString("/images/ban/pgs_front.jpg") );
}
{
QUrl u = tmp.resolved(QString("pgs_front.jpg"));
QCOMPARE( u.path(), QString("/images/ban/pgs_front.jpg") );
}
{
QUrl tmp;
QCOMPARE( u.path(), QString("/images/ban/pgs_front.jpg") );
}
{
QUrl tmp;
QFileInfo fi(u.path());
u.setPath(fi.path());
QCOMPARE( u.path(), QString("/images/ban") );
}
}
void tst_QUrl::compat_constructor_01_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<QString>("res");
//next we fill it with data
QTest::newRow( "data0" ) << QString("Makefile") << QString("Makefile"); // nolonger add file by default
QTest::newRow( "data1" ) << QString("Makefile") << QString("Makefile");
QTest::newRow( "data2" ) << QString("ftp://ftp.qt-project.org/qt/INSTALL") << QString("ftp://ftp.qt-project.org/qt/INSTALL");
QTest::newRow( "data3" ) << QString("ftp://ftp.qt-project.org/qt/INSTALL") << QString("ftp://ftp.qt-project.org/qt/INSTALL");
}
void tst_QUrl::compat_constructor_01()
{
QFETCH( QString, urlStr );
{
QUrl empty;
QUrl u = empty.resolved(urlStr);
QTEST( u.toString(), "res" );
}
{
QUrl empty;
QUrl u = empty.resolved(urlStr);
QTEST( u.toString(), "res" );
}
}
void tst_QUrl::compat_constructor_02_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<QString>("fileName");
QTest::addColumn<QString>("res");
//next we fill it with data
QTest::newRow( "data0" ) << QString("ftp://ftp.qt-project.org/qt") << QString("INSTALL") << QString("ftp://ftp.qt-project.org/INSTALL");
QTest::newRow( "data1" ) << QString("ftp://ftp.qt-project.org/qt/") << QString("INSTALL") << QString("ftp://ftp.qt-project.org/qt/INSTALL");
}
void tst_QUrl::compat_constructor_02()
{
QFETCH( QString, urlStr );
QFETCH( QString, fileName );
QUrl tmp( urlStr );
QUrl u = tmp.resolved(fileName);
QTEST( u.toString(), "res" );
}
void tst_QUrl::compat_constructor_03_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<QString>("res");
//next we fill it with data
QTest::newRow( "protocol00" ) << QString( "http://qt-project.org/index.html" ) << QString( "http://qt-project.org/index.html" );
QTest::newRow( "protocol01" ) << QString( "http://qt-project.org" ) << QString( "http://qt-project.org" );
QTest::newRow( "protocol02" ) << QString( "http://qt-project.org/" ) << QString( "http://qt-project.org/" );
QTest::newRow( "protocol03" ) << QString( "http://qt-project.org/foo" ) << QString( "http://qt-project.org/foo" );
QTest::newRow( "protocol04" ) << QString( "http://qt-project.org/foo/" ) << QString( "http://qt-project.org/foo/" );
QTest::newRow( "protocol05" ) << QString( "ftp://ftp.qt-project.org/foo/index.txt" ) << QString( "ftp://ftp.qt-project.org/foo/index.txt" );
QTest::newRow( "local00" ) << QString( "/foo" ) << QString( "/foo" );
QTest::newRow( "local01" ) << QString( "/foo/" ) << QString( "/foo/" );
QTest::newRow( "local02" ) << QString( "/foo/bar" ) << QString( "/foo/bar" );
QTest::newRow( "local03" ) << QString( "/foo/bar/" ) << QString( "/foo/bar/" );
QTest::newRow( "local04" ) << QString( "foo" ) << QString( "foo" );
QTest::newRow( "local05" ) << QString( "foo/" ) << QString( "foo/" );
QTest::newRow( "local06" ) << QString( "foo/bar" ) << QString( "foo/bar" );
QTest::newRow( "local07" ) << QString( "foo/bar/" ) << QString( "foo/bar/" );
QTest::newRow( "local09" ) << QString( "" ) << QString( "" );
QTest::newRow( "relProtocol00" ) << QString( "foo:bar" ) << QString( "foo:bar" );
QTest::newRow( "relProtocol01" ) << QString( "foo:/bar" ) << QString( "foo:/bar" );
QTest::newRow( "windowsDrive00" ) << QString( "c:/" ) << QString( "c:/" );
QTest::newRow( "windowsDrive01" ) << QString( "c:" ) << QString( "c:" );
QTest::newRow( "windowsDrive02" ) << QString( "c:/WinNT/" ) << QString( "c:/WinNT/" );
QTest::newRow( "windowsDrive03" ) << QString( "c:/autoexec.bat" ) << QString( "c:/autoexec.bat" );
QTest::newRow( "windowsDrive04" ) << QString( "c:WinNT/" ) << QString( "c:WinNT/" );
QTest::newRow( "windowsDrive05" ) << QString( "c:autoexec.bat" ) << QString( "c:autoexec.bat" );
}
void tst_QUrl::compat_constructor_03()
{
QFETCH( QString, urlStr );
QUrl u( urlStr );
QTEST( u.toString(), "res" );
}
void tst_QUrl::compat_isValid_01_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<bool>("res");
QTest::newRow( "ok_02" ) << QString( "file:/foo") << (bool)true;
QTest::newRow( "ok_03" ) << QString( "file:foo") << (bool)true;
QTest::newRow( "err_02" ) << QString( "file:/::foo") << (bool)true;
}
void tst_QUrl::compat_isValid_01()
{
QFETCH( QString, urlStr );
QFETCH( bool, res );
QUrl url( urlStr );
QCOMPARE( url.isValid(), res );
if (!res)
QVERIFY(url.toString().isEmpty());
}
void tst_QUrl::compat_isValid_02_data()
{
QTest::addColumn<QString>("protocol");
QTest::addColumn<QString>("user");
QTest::addColumn<QString>("password");
QTest::addColumn<QString>("host");
QTest::addColumn<int>("port");
QTest::addColumn<QString>("path");
QTest::addColumn<bool>("res");
QString n = "";
QTest::newRow( "ok_01" ) << n << n << n << n << -1 << QString("path") << (bool)true;
QTest::newRow( "ok_02" ) << QString("ftp") << n << n << QString("ftp.qt-project.org") << -1 << n << (bool)true;
QTest::newRow( "ok_03" ) << QString("ftp") << QString("foo") << n << QString("ftp.qt-project.org") << -1 << n << (bool)true;
QTest::newRow( "ok_04" ) << QString("ftp") << QString("foo") << QString("bar") << QString("ftp.qt-project.org") << -1 << n << (bool)true;
QTest::newRow( "ok_05" ) << QString("ftp") << n << n << QString("ftp.qt-project.org") << -1 << QString("/path")<< (bool)true;
QTest::newRow( "ok_06" ) << QString("ftp") << QString("foo") << n << QString("ftp.qt-project.org") << -1 << QString("/path") << (bool)true;
QTest::newRow( "ok_07" ) << QString("ftp") << QString("foo") << QString("bar") << QString("ftp.qt-project.org") << -1 << QString("/path")<< (bool)true;
QTest::newRow( "err_01" ) << n << n << n << n << -1 << n << (bool)false;
QTest::newRow( "err_02" ) << QString("ftp") << n << n << n << -1 << n << (bool)true;
QTest::newRow( "err_03" ) << n << QString("foo") << n << n << -1 << n << (bool)true;
QTest::newRow( "err_04" ) << n << n << QString("bar") << n << -1 << n << (bool)true;
QTest::newRow( "err_05" ) << n << n << n << QString("ftp.qt-project.org") << -1 << n << (bool)true;
QTest::newRow( "err_06" ) << n << n << n << n << 80 << n << (bool)true;
QTest::newRow( "err_07" ) << QString("ftp") << QString("foo") << n << n << -1 << n << (bool)true;
QTest::newRow( "err_08" ) << QString("ftp") << n << QString("bar") << n << -1 << n << (bool)true;
QTest::newRow( "err_09" ) << QString("ftp") << QString("foo") << QString("bar") << n << -1 << n << (bool)true;
}
void tst_QUrl::compat_isValid_02()
{
QFETCH( QString, protocol );
QFETCH( QString, user );
QFETCH( QString, password );
QFETCH( QString, host );
QFETCH( int, port );
QFETCH( QString, path );
QFETCH( bool, res );
QUrl url;
if ( !protocol.isEmpty() )
url.setScheme( protocol );
if ( !user.isEmpty() )
url.setUserName( user );
if ( !password.isEmpty() )
url.setPassword( password );
if ( !host.isEmpty() )
url.setHost( host );
if ( port != -1 )
url.setPort( port );
if ( !path.isEmpty() )
url.setPath( path );
QCOMPARE( url.isValid(), res );
if (!res)
QVERIFY(url.toString().isEmpty());
}
void tst_QUrl::compat_path_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<QString>("res");
QTest::newRow( "protocol00" ) << "http://qt-project.org/images/ban/pgs_front.jpg" << "/images/ban/pgs_front.jpg";
#if defined( Q_OS_WIN32 )
QTest::newRow( "winShare00" ) << "//Anarki/homes" << "/homes";
#endif
}
void tst_QUrl::compat_path()
{
QFETCH( QString, url );
QUrl u( url );
QTEST( u.path(), "res" );
}
void tst_QUrl::compat_fileName_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<QString>("fileName");
#ifdef Q_OS_WIN32
QTest::newRow( "Windows - DrivePathFileName - \\" ) << QString("c:\\windows\\tmp\\filename.txt")<< QString("filename.txt");
QTest::newRow( "Windows - DrivePathFileName - /" ) << QString("c:/windows/tmp/filename.txt") << QString("filename.txt");
QTest::newRow( "Windows - DrivePathWithSlash - \\" ) << QString("c:\\windows\\tmp\\") << QString();
QTest::newRow( "Windows - DrivePathWithSlash - /" ) << QString("c:/windows/tmp/") << QString();
QTest::newRow( "Windows - DrivePathWithoutSlash - \\" ) << QString("c:/windows/tmp") << QString("tmp");
QTest::newRow( "Windows - DrivePathWithoutSlash - /" ) << QString("c:/windows/tmp") << QString("tmp");
#endif
QTest::newRow( "Path00" ) << QString("/") << QString();
QTest::newRow( "Path01" ) << QString("/home/dev/test/") << QString();
QTest::newRow( "PathFileName00" ) << QString("/home/dev/test") << QString("test");
}
void tst_QUrl::compat_fileName()
{
QFETCH( QString, url );
QFETCH( QString, fileName );
QUrl fileUrl = QUrl::fromLocalFile(url);
QFileInfo fi(fileUrl.toLocalFile());
QCOMPARE( fi.fileName(), fileName );
}
void tst_QUrl::compat_decode_data()
{
QTest::addColumn<QByteArray>("encodedString");
QTest::addColumn<QString>("decodedString");
QTest::newRow("NormalString") << QByteArray("filename") << QString("filename");
QTest::newRow("NormalStringEncoded") << QByteArray("file%20name") << QString("file name");
QTest::newRow("JustEncoded") << QByteArray("%20") << QString(" ");
QTest::newRow("HTTPUrl") << QByteArray("http://qt-project.org") << QString("http://qt-project.org");
QTest::newRow("HTTPUrlEncoded") << QByteArray("http://qt-project%20org") << QString("http://qt-project org");
QTest::newRow("EmptyString") << QByteArray("") << QString("");
QTest::newRow("NulByte") << QByteArray("C%00%0A") << QString::fromLatin1("C\0\n", 3);
QTest::newRow("Task27166") << QByteArray("Fran%C3%A7aise") << QString::fromUtf8("Française");
}
void tst_QUrl::compat_decode()
{
QFETCH(QByteArray, encodedString);
QFETCH(QString, decodedString);
QCOMPARE(QUrl::fromPercentEncoding(encodedString), decodedString);
}
void tst_QUrl::compat_encode_data()
{
QTest::addColumn<QString>("decodedString");
QTest::addColumn<QByteArray>("encodedString");
QTest::newRow("NormalString") << QString("filename") << QByteArray("filename");
QTest::newRow("NormalStringEncoded") << QString("file name") << QByteArray("file%20name");
QTest::newRow("JustEncoded") << QString(" ") << QByteArray("%20");
QTest::newRow("HTTPUrl") << QString("http://qt-project.org") << QByteArray("http%3A//qt-project.org");
QTest::newRow("HTTPUrlEncoded") << QString("http://qt-project org") << QByteArray("http%3A//qt-project%20org");
QTest::newRow("EmptyString") << QString("") << QByteArray("");
QTest::newRow("NulByte") << QString::fromLatin1("C\0\n", 3) << QByteArray("C%00%0A");
QTest::newRow("Task27166") << QString::fromUtf8("Française") << QByteArray("Fran%C3%A7aise");
}
void tst_QUrl::compat_encode()
{
QFETCH(QString, decodedString);
QFETCH(QByteArray, encodedString);
QCOMPARE(QUrl::toPercentEncoding(decodedString, "/."), encodedString);
}
void tst_QUrl::relative()
{
QUrl url("../ole");
QCOMPARE(url.path(), QString::fromLatin1("../ole"));
QUrl url2("./");
QCOMPARE(url2.path(), QString::fromLatin1("./"));
QUrl url3("..");
QCOMPARE(url3.path(), QString::fromLatin1(".."));
QUrl url4("../..");
QCOMPARE(url4.path(), QString::fromLatin1("../.."));
}
void tst_QUrl::percentEncoding_data()
{
// This test is limited. It's superseded by componentEncodings below
QTest::addColumn<QString>("original");
QTest::addColumn<QByteArray>("encoded");
QTest::newRow("test_01") << QString::fromLatin1("sdfsdf") << QByteArray("sdfsdf");
QTest::newRow("test_02") << QString::fromUtf8("æss") << QByteArray("%C3%A6ss");
}
void tst_QUrl::percentEncoding()
{
// This test is limited. It's superseded by componentEncodings below
QFETCH(QString, original);
QFETCH(QByteArray, encoded);
QCOMPARE(QUrl(original).toEncoded().constData(), encoded.constData());
QCOMPARE(QUrl::fromEncoded(QUrl(original).toEncoded()), QUrl(original));
QCOMPARE(QUrl::fromEncoded(QUrl(original).toEncoded()).toString(), original);
QCOMPARE(QUrl::fromEncoded(encoded), QUrl(original));
QCOMPARE(QUrl(QUrl(original).toString()).toString(), original);
}
void tst_QUrl::toPercentEncoding_data()
{
QTest::addColumn<QString>("original");
QTest::addColumn<QByteArray>("encoded");
QTest::addColumn<QByteArray>("excludeInEncoding");
QTest::addColumn<QByteArray>("includeInEncoding");
QTest::newRow("test_01") << QString::fromLatin1("abcdevghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678-._~")
<< QByteArray("abcdevghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678-._~")
<< QByteArray("")
<< QByteArray("");
QTest::newRow("test_02") << QString::fromLatin1("{\t\n\r^\"abc}")
<< QByteArray("%7B%09%0A%0D%5E%22abc%7D")
<< QByteArray("")
<< QByteArray("");
QTest::newRow("test_03") << QString::fromLatin1("://?#[]@!$&'()*+,;=")
<< QByteArray("%3A%2F%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D")
<< QByteArray("")
<< QByteArray("");
QTest::newRow("test_04") << QString::fromLatin1("://?#[]@!$&'()*+,;=")
<< QByteArray("%3A%2F%2F%3F%23%5B%5D%40!$&'()*+,;=")
<< QByteArray("!$&'()*+,;=")
<< QByteArray("");
QTest::newRow("test_05") << QString::fromLatin1("abcd")
<< QByteArray("a%62%63d")
<< QByteArray("")
<< QByteArray("bc");
}
void tst_QUrl::toPercentEncoding()
{
QFETCH(QString, original);
QFETCH(QByteArray, encoded);
QFETCH(QByteArray, excludeInEncoding);
QFETCH(QByteArray, includeInEncoding);
QByteArray encodedUrl = QUrl::toPercentEncoding(original, excludeInEncoding, includeInEncoding);
QCOMPARE(encodedUrl.constData(), encoded.constData());
QCOMPARE(original, QUrl::fromPercentEncoding(encodedUrl));
}
void tst_QUrl::swap()
{
u1.swap(u2);
QCOMPARE(u2.host(),QLatin1String("qt-project.org"));
QCOMPARE(u1.host(),QLatin1String("www.kdab.com"));
}
void tst_QUrl::symmetry()
{
QCOMPARE(url.scheme(), QString::fromLatin1("http"));
QCOMPARE(url.host(), QString::fromUtf8("www.räksmörgås.se"));
QCOMPARE(url.host(QUrl::EncodeSpaces), QString::fromUtf8("www.räksmörgås.se"));
QCOMPARE(url.host(QUrl::EncodeUnicode), QString::fromUtf8("www.xn--rksmrgs-5wao1o.se"));
QCOMPARE(url.host(QUrl::EncodeUnicode | QUrl::EncodeSpaces), QString::fromUtf8("www.xn--rksmrgs-5wao1o.se"));
QCOMPARE(url.path(), QString::fromLatin1("/pub"));
// this will be encoded ...
QCOMPARE(url.query(QUrl::FullyEncoded), QLatin1String("a=b&a=d%C3%B8&a=f"));
QCOMPARE(url.fragment(), QString::fromUtf8("vræl"));
QUrl onlyHost("//qt-project.org");
QCOMPARE(onlyHost.toString(), QString::fromLatin1("//qt-project.org"));
{
QString urlString = QString::fromLatin1("http://desktop:33326/upnp/{32f525a6-6f31-426e-91ca-01c2e6c2c57e}");
QString encodedUrlString = QString("http://desktop:33326/upnp/%7B32f525a6-6f31-426e-91ca-01c2e6c2c57e%7D");
QUrl urlPreviewList(urlString);
QCOMPARE(urlPreviewList.toString(), encodedUrlString);
QByteArray b = urlPreviewList.toEncoded();
QCOMPARE(b.constData(), encodedUrlString.toLatin1().constData());
QCOMPARE(QUrl::fromEncoded(b).toString(), encodedUrlString);
QCOMPARE(QUrl(b).toString(), encodedUrlString);
}
{
QString urlString = QString::fromLatin1("http://desktop:53423/deviceDescription?uuid={7977c17b-00bf-4af9-894e-fed28573c3a9}");
QString encodedUrlString = QString("http://desktop:53423/deviceDescription?uuid=%7B7977c17b-00bf-4af9-894e-fed28573c3a9%7D");
QUrl urlPreviewList(urlString);
QCOMPARE(urlPreviewList.toString(), encodedUrlString);
QByteArray b = urlPreviewList.toEncoded();
QCOMPARE(b.constData(), encodedUrlString.toLatin1().constData());
QCOMPARE(QUrl::fromEncoded(b).toString(), encodedUrlString);
QCOMPARE(QUrl(b).toString(), encodedUrlString);
}
}
void tst_QUrl::ipvfuture_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<bool>("isValid");
QTest::addColumn<QString>("output");
// No one uses IPvFuture yet, so we have no clue what it might contain
// We're just testing that it can hold what the RFC says it should hold:
// IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
QTest::newRow("data-unreserved") << "x://[v7.hello~-WORLD_.com]" << true << "x://[v7.hello~-WORLD_.com]";
QTest::newRow("data-sub-delims-colon") << "x://[v7.!$&'()*+,;=:]" << true << "x://[v7.!$&'()*+,;=:]";
// we're using the tolerant parser
QTest::newRow("data-encoded-sub-delims-colon") << "x://[v7.%21%24%26%27%28%29%2A%2B%2C%3B%3D%3A]" << true << "x://[v7.!$&'()*+,;=:]";
// should we test "[%76%37%2ex]" -> "[v7.x]" ?
}
void tst_QUrl::ipvfuture()
{
QFETCH(QString, input);
QFETCH(bool, isValid);
QUrl url(input);
if (isValid) {
QVERIFY2(url.isValid(), qPrintable(url.errorString()));
QFETCH(QString, output);
QCOMPARE(url.toString(), output);
QUrl url2(output);
QCOMPARE(url2, url);
} else {
QVERIFY(!url.isValid());
}
}
void tst_QUrl::ipv6_data()
{
QTest::addColumn<QString>("ipv6Auth");
QTest::addColumn<bool>("isValid");
QTest::addColumn<QString>("output");
QTest::newRow("empty") << "//[]" << false << "";
QTest::newRow("case 1") << QString::fromLatin1("//[56:56:56:56:56:56:56:56]") << true
<< "//[56:56:56:56:56:56:56:56]";
QTest::newRow("case 2") << QString::fromLatin1("//[::56:56:56:56:56:56:56]") << true
<< "//[0:56:56:56:56:56:56:56]";
QTest::newRow("case 3") << QString::fromLatin1("//[56::56:56:56:56:56:56]") << true
<< "//[56:0:56:56:56:56:56:56]";
QTest::newRow("case 4") << QString::fromLatin1("//[56:56::56:56:56:56:56]") << true
<< "//[56:56:0:56:56:56:56:56]";
QTest::newRow("case 5") << QString::fromLatin1("//[56:56:56::56:56:56:56]") << true
<< "//[56:56:56:0:56:56:56:56]";
QTest::newRow("case 6") << QString::fromLatin1("//[56:56:56:56::56:56:56]") << true
<< "//[56:56:56:56:0:56:56:56]";
QTest::newRow("case 7") << QString::fromLatin1("//[56:56:56:56:56::56:56]") << true
<< "//[56:56:56:56:56:0:56:56]";
QTest::newRow("case 8") << QString::fromLatin1("//[56:56:56:56:56:56::56]") << true
<< "//[56:56:56:56:56:56:0:56]";
QTest::newRow("case 9") << QString::fromLatin1("//[56:56:56:56:56:56:56::]") << true
<< "//[56:56:56:56:56:56:56:0]";
QTest::newRow("case 4 with one less") << QString::fromLatin1("//[56::56:56:56:56:56]") << true
<< "//[56::56:56:56:56:56]";
QTest::newRow("case 4 with less and ip4") << QString::fromLatin1("//[56::56:56:56:127.0.0.1]") << true
<< "//[56::56:56:56:7f00:1]";
QTest::newRow("case 7 with one and ip4") << QString::fromLatin1("//[56::255.0.0.0]") << true
<< "//[56::ff00:0]";
QTest::newRow("case 2 with ip4") << QString::fromLatin1("//[::56:56:56:56:56:0.0.0.255]") << true
<< "//[0:56:56:56:56:56:0:ff]";
QTest::newRow("case 2 with half ip4") << QString::fromLatin1("//[::56:56:56:56:56:56:0.255]") << false << "";
QTest::newRow("case 4 with less and ip4 and port and useinfo")
<< QString::fromLatin1("//user:pass@[56::56:56:56:127.0.0.1]:99") << true
<< "//user:pass@[56::56:56:56:7f00:1]:99";
QTest::newRow("case :,") << QString::fromLatin1("//[:,]") << false << "";
QTest::newRow("case ::bla") << QString::fromLatin1("//[::bla]") << false << "";
QTest::newRow("case v4-mapped") << "//[0:0:0:0:0:ffff:7f00:1]" << true << "//[::ffff:127.0.0.1]";
QTest::newRow("encoded-digit") << "//[::%31]" << true << "//[::1]";
QTest::newRow("encoded-colon") << "//[%3A%3A]" << true << "//[::]";
QTest::newRow("full ipv6 with zone id (decoded %)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%eth0]") << true
<< "//[56:56:56:56:56:56:56:56%25eth0]";
QTest::newRow("full ipv6 with zone id (encoded %)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%25eth0]") << true
<< "//[56:56:56:56:56:56:56:56%25eth0]";
QTest::newRow("full ipv6 with invalid zone id") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%]") << false << "";
QTest::newRow("full ipv6 with invalid zone id (encoded)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%25]") << false << "";
QTest::newRow("full ipv6 with zone id 25 (encoded)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%2525]") << true << "//[56:56:56:56:56:56:56:56%2525]";
QTest::newRow("case 4 with less and ip4 and port and useinfo and zone id")
<< QString::fromLatin1("//user:pass@[56::56:56:56:127.0.0.1%ethernet_1]:99") << true
<< "//user:pass@[56::56:56:56:7f00:1%25ethernet_1]:99";
QTest::newRow("encoded-digit including zone id") << "//[::%31%25eth0]" << true << "//[::1%25eth0]";
}
void tst_QUrl::ipv6()
{
QFETCH(QString, ipv6Auth);
QFETCH(bool, isValid);
QFETCH(QString, output);
QUrl url(ipv6Auth);
QCOMPARE(url.isValid(), isValid);
QCOMPARE(url.toString().isEmpty(), !isValid);
if (url.isValid()) {
QCOMPARE(url.toString(), output);
url.setHost(url.host());
QVERIFY(url.isValid());
QCOMPARE(url.toString(), output);
}
}
void tst_QUrl::ipv6_2_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::newRow("[::ffff:129.144.52.38]")
QTest::newRow("[::FFFF:129.144.52.38]")
}
void tst_QUrl::ipv6_2()
{
QFETCH(QString, input);
QFETCH(QString, output);
QUrl url(input);
QCOMPARE(url.toString(), output);
url.setHost(url.host());
QCOMPARE(url.toString(), output);
}
void tst_QUrl::moreIpv6()
{
waba1.setHost("::ffff:129.144.52.38");
QCOMPARE(QString::fromLatin1(waba1.toEncoded()), QString::fromLatin1("http://[::ffff:129.144.52.38]/cgi/test.cgi"));
QUrl withInvalidHost;
withInvalidHost.setHost("[:]"); // This had an UB (ptr[-1] read)
QCOMPARE(withInvalidHost.host(), QString());
}
void tst_QUrl::isRelative_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<bool>("trueFalse");
QTest::newRow("55288") << QString::fromLatin1("node64.html#fig:form:ana") << true;
// kde
QTest::newRow("mailto: URL, is relative") << "mailto:faure@kde.org" << false;
QTest::newRow("man: URL, is relative") << "man:mmap" << false;
QTest::newRow("javascript: URL, is relative") << "javascript:doSomething()" << false;
QTest::newRow("file: URL, is relative") << "file:/blah" << false;
QTest::newRow("/path, is relative") << "/path" << true;
QTest::newRow("something, is relative") << "something" << true;
// end kde
}
void tst_QUrl::isRelative()
{
QFETCH(QString, url);
QFETCH(bool, trueFalse);
QCOMPARE(QUrl(url).isRelative(), trueFalse);
}
void tst_QUrl::hasQuery_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<bool>("trueFalse");
}
void tst_QUrl::hasQuery()
{
QFETCH(QString, url);
QFETCH(bool, trueFalse);
QUrl qurl(url);
QCOMPARE(qurl.hasQuery(), trueFalse);
QCOMPARE(qurl.query().isNull(), !trueFalse);
}
void tst_QUrl::nameprep()
{
// U+FB01 LATIN SMALL LIGATURE FI
}
void tst_QUrl::isValid()
{
{
QUrl url(QString("A=B"));
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
QCOMPARE(url.path(), QString("A=B"));
}
{
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
// < and > are not allowed in userinfo in strict mode
url.setUserName("normal_username");
QVERIFY(url.isValid());
}
{
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
// < and > are allowed in tolerant mode
}
{
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QCOMPARE(url.path(), QString("/here"));
url.setAuthority("strange;hostname");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
url.setAuthority("foobar@bar");
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
url.setAuthority("strange;hostname");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY2(url.errorString().contains("Invalid hostname"),
qPrintable(url.errorString()));
}
{
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QCOMPARE(url.path(), QString("/g"));
url.setHost("stuff;1");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
url.setHost("stuff-1");
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
url.setHost("stuff;1");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY2(url.errorString().contains("Invalid hostname"),
qPrintable(url.errorString()));
}
{
// U+1F100 DIGIT ZERO FULL STOP
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QCOMPARE(url.path(), QString("/g"));
url.setHost("%f0%9f%84%80.example.la/");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
url.setHost("\xf0\x9f\x84\x80.example.la/");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY2(url.errorString().contains("Invalid hostname"),
qPrintable(url.errorString()));
}
{
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
url.setPath("relative");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY(url.errorString().contains("Path component is relative and authority is present"));
}
{
QUrl url("http:");
url.setPath("//example.com");
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY(url.errorString().contains("Path component starts with '//' and authority is absent"));
// should disappear if we set a port
url.setPort(80);
QVERIFY(url.isValid());
}
{
QUrl url;
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY(url.errorString().contains("':' before any '/'"));
// this specific error disappears if we set anything in the authority,
// but then we run into another error
url.setPort(80);
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY(url.errorString().contains("Path component is relative and authority is present"));
}
{
QVERIFY(!url.isValid());
}
}
void tst_QUrl::schemeValidator_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<bool>("result");
QTest::addColumn<QString>("scheme");
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
QTest::newRow("empty") << QString() << false << QString();
// uncontroversial ones
QTest::newRow("mailto") << "mailto:smith@example.com" << true << "mailto";
QTest::newRow("file-1slash") << "file:/etc/passwd" << true << "file";
QTest::newRow("mailto+subject") << "mailto:smith@example.com?subject=Hello%20World" << true << "mailto";
// valid, but unexpected
QTest::newRow("ftp-nohost") << "ftp:/etc/passwd" << true << "ftp";
QTest::newRow("http-nohost") << "http:/etc/passwd" << true << "http";
// schemes with numbers
QTest::newRow("digits") << "proto2://" << true << "proto2";
// schemes with dots, dashes, and pluses
// lowercasing
QTest::newRow("MAILTO") << "MAILTO:smith@example.com" << true << "mailto";
QTest::newRow("FILE") << "FILE:/etc/passwd" << true << "file";
// invalid entries
QTest::newRow("start-digit") << "1http://example.com" << false << "1http";
QTest::newRow("with-space") << "a b://" << false << "a b";
QTest::newRow("with-non-ascii") << "\304\245\305\243\305\245\321\200://example.com" << false << "\304\245\305\243\305\245\321\200";
QTest::newRow("with-control1") << "http\1://example.com" << false << "http\1";
QTest::newRow("with-control127") << "http\177://example.com" << false << "http\177";
QTest::newRow("with-null") << QString::fromLatin1("http\0://example.com", 19) << false << QString::fromLatin1("http\0", 5);
QTest::newRow("percent-encoded") << "%68%74%%74%70://example.com" << false << "%68%74%%74%70";
static const char controls[] = "!\"$&'()*,;<=>[\\]^_`{|}~";
for (char control : controls) {
const QString scheme = QLatin1String("pre") + QLatin1Char(control) + QLatin1String("post");
QTest::newRow((QByteArrayLiteral("with-") + control).constData())
<< (scheme + QLatin1String("://example.com/"))
<< false << scheme;
}
}
void tst_QUrl::schemeValidator()
{
QFETCH(QString, input);
QFETCH(bool, result);
QUrl url(input);
QCOMPARE(url.isValid(), result);
if (result) {
QFETCH(QString, scheme);
QCOMPARE(url.scheme(), scheme);
// reconstruct with just the scheme:
url.setUrl(scheme + ':');
QVERIFY(url.isValid());
QCOMPARE(url.scheme(), scheme);
} else {
QVERIFY(url.toString().isEmpty());
}
}
void tst_QUrl::setScheme_data()
{
schemeValidator_data();
// a couple more which wouldn't work in parsing a full URL
QTest::newRow("with-slash") << QString() << false << "http/";
QTest::newRow("with-question") << QString() << false << "http?";
QTest::newRow("with-hash") << QString() << false << "http#";
}
void tst_QUrl::setScheme()
{
QFETCH(QString, scheme);
QFETCH(bool, result);
QString expectedScheme;
if (result)
expectedScheme = scheme;
QUrl url;
url.setScheme(scheme);
QCOMPARE(url.isValid(), result);
QCOMPARE(url.scheme(), expectedScheme);
url.setScheme(scheme.toUpper());
QCOMPARE(url.isValid(), result);
QCOMPARE(url.scheme(), expectedScheme);
}
void tst_QUrl::strictParser_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("needle");
// QUrl doesn't detect an error in the scheme when parsing because
// it falls back to parsing as a path. So, these errors are path errors
QTest::newRow("invalid-scheme") << "ht%://example.com" << "character '%' not permitted";
QTest::newRow("empty-scheme") << ":/" << "':' before any '/'";
QTest::newRow("invalid-user1") << "http://bad<user_name>@ok-hostname" << "Invalid user name (character '<' not permitted)";
QTest::newRow("invalid-user2") << "http://bad%@ok-hostname" << "Invalid user name (character '%' not permitted)";
QTest::newRow("invalid-password") << "http://user:pass\x7F@ok-hostname" << "Invalid password (character '\x7F' not permitted)";
QTest::newRow("invalid-regname") << "http://bad<hostname>" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-regname-2") << "http://b%61d" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-ipv6-char1") << "http://[::g]" << "Invalid IPv6 address (character 'g' not permitted)";
QTest::newRow("invalid-ipv6-char2") << "http://[z::]" << "Invalid IPv6 address (character 'z' not permitted)";
QTest::newRow("invalid-encoded-ipvfuture") << "x://[v7.%7E%2D%54%5f%2E]" << "Invalid IPvFuture address";
QTest::newRow("unbalanced-brackets") << "http://[ff02::1" << "Expected ']' to match '[' in hostname";
// invalid hostnames happen in TolerantMode too
QTest::newRow("invalid-hostname-leading-dot") << "http://.co.uk" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-hostname-double-dot") << "http://co..uk" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-hostname-non-LDH") << "http://foo,bar.example.com" << "Invalid hostname (contains invalid characters)";
// a backslash is not an authority delimiter, so it lands in the host
QTest::newRow("invalid-hostname-backslash") << "http://foo\\bar" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-hostname-encoded-slash") << "http://foo%2Fbar" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-hostname-encoded-backslash") << "http://foo%5Cbar" << "Invalid hostname (contains invalid characters)";
QTest::newRow("invalid-hostname-encoded-nul") << "http://foo%00bar" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-space") << "http:// " << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-nbsp") << "http://\xc2\xa0" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-control-1f") << "http://\x1f" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-control-7f") << "http://\x7f" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-control-80") << "http://\xc2\x80" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-private-bmp") << "http://\xee\x80\x80" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-private-plane15") << "http://\xf3\xb0\x80\x80" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-private-plane16") << "http://\xf4\x80\x80\x80" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-ffff") << "http://\xef\xbf\xbf" << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-surrogate-1") << "http://" + QString(QChar(0xD800)) << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-surrogate-2") << "http://" + QString(QChar(0xDC00)) << "Invalid hostname (contains invalid characters)";
QTest::newRow("idn-prohibited-char-surrogate-3") << "http://" + QString(QChar(0xD800)) + "a" << "Invalid hostname (contains invalid characters)";
// FIXME: add some tests for prohibited BiDi (RFC 3454 section 6)
// port errors happen in TolerantMode too
QTest::newRow("invalid-path") << "foo:/path%\x1F" << "Invalid path (character '%' not permitted)";
QTest::newRow("invalid-query") << "foo:?\\#" << "Invalid query (character '\\' not permitted)";
QTest::newRow("invalid-fragment") << "#{}" << "Invalid fragment (character '{' not permitted)";
}
void tst_QUrl::strictParser()
{
QFETCH(QString, input);
QFETCH(QString, needle);
QUrl url(input, QUrl::StrictMode);
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
QVERIFY(!url.errorString().isEmpty());
QVERIFY2(url.errorString().contains(input),
"Error string does not contain the original input (\"" +
input.toLatin1() + "\"): " + url.errorString().toLatin1());
// Note: if the following fails due to changes in the parser, simply update the test data
QVERIFY2(url.errorString().contains(needle),
"Error string changed and does not contain \"" +
needle.toLatin1() + "\" anymore: " + url.errorString().toLatin1());
}
void tst_QUrl::tolerantParser()
{
{
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
QCOMPARE(url.path(), QString("/path with spaces.html"));
QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://www.example.com/path%20with%20spaces.html"));
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
}
{
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
QCOMPARE(url.path(), QString("/path with spaces.html"));
QVERIFY(!url.isValid());
QVERIFY(url.toString().isEmpty());
}
{
QVERIFY(url15581.isValid());
QCOMPARE(url15581.toEncoded().constData(), "http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.htm%3E");
}
{
QUrl webkit22616 =
QUrl::fromEncoded("http://example.com/testya.php?browser-info=s:1400x1050x24:f:9.0%20r152:t:%u0442%u0435%u0441%u0442");
QVERIFY(webkit22616.isValid());
// Qt 5 behaviour change: one broken % means all % are considered broken
// QCOMPARE(webkit22616.toEncoded().constData(),
QCOMPARE(webkit22616.toEncoded().constData(),
}
{
QUrl url;
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
url.setUrl("//[::56:56:56:56:56:56:56]");
QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]"));
QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]"));
QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]"));
// invoke the tolerant parser's error correction
url.setUrl("%hello.com/f%");
QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%25hello.com/f%25"));
QCOMPARE(url.toEncoded(), QByteArray("%25hello.com/f%25"));
QCOMPARE(url.toString(), QString("%25hello.com/f%25"));
QVERIFY(url.isValid());
QVERIFY(!url.toString().isEmpty());
QVERIFY(url.isValid());
QVERIFY(url.isValid());
url.setUrl(QLatin1String("//[::56:56:56:56:56:56:56]"), QUrl::StrictMode);
QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]"));
QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]"));
url.setUrl(QLatin1String("data:text/css,div%20{%20border-right:%20solid;%20}"), QUrl::TolerantMode);
QCOMPARE(url.toString(QUrl::FullyEncoded), QString("data:text/css,div%20%7B%20border-right:%20solid;%20%7D"));
QCOMPARE(url.toEncoded(), QByteArray("data:text/css,div%20%7B%20border-right:%20solid;%20%7D"));
QCOMPARE(url.toString(), QString("data:text/css,div %7B border-right: solid; %7D"));
}
{
QUrl tsdgeosQUrl;
tsdgeosQUrl.setUrl(tsdgeos, QUrl::TolerantMode);
QVERIFY(tsdgeosQUrl.isValid()); // failed in Qt-4.4, works in Qt-4.5
QCOMPARE(QString(tsdgeosQUrl.toEncoded()), QString(tsdgeosExpected));
}
{
QUrl url;
QVERIFY(url.isValid());
}
{
QUrl url;
QVERIFY(url.isValid());
}
}
void tst_QUrl::correctEncodedMistakes_data()
{
QTest::addColumn<QByteArray>("encodedUrl");
QTest::addColumn<bool>("result");
QTest::addColumn<QString>("toDecoded");
QTest::newRow("%") << QByteArray("%") << true << QString("%25");
QTest::newRow("3%") << QByteArray("3%") << true << QString("3%25");
QTest::newRow("13%") << QByteArray("13%") << true << QString("13%25");
QTest::newRow("13%!") << QByteArray("13%!") << true << QString("13%25!");
QTest::newRow("13%!!") << QByteArray("13%!!") << true << QString("13%25!!");
QTest::newRow("13%a") << QByteArray("13%a") << true << QString("13%25a");
QTest::newRow("13%az") << QByteArray("13%az") << true << QString("13%25az");
QTest::newRow("13%25") << QByteArray("13%25") << true << QString("13%25");
}
void tst_QUrl::correctEncodedMistakes()
{
QFETCH(QByteArray, encodedUrl);
QFETCH(bool, result);
QFETCH(QString, toDecoded);
QUrl url = QUrl::fromEncoded(encodedUrl);
QCOMPARE(url.isValid(), result);
if (url.isValid()) {
QCOMPARE(url.toString(), toDecoded);
} else {
QVERIFY(url.toString().isEmpty());
}
}
void tst_QUrl::correctDecodedMistakes_data()
{
QTest::addColumn<QString>("decodedUrl");
QTest::addColumn<bool>("result");
QTest::addColumn<QString>("toDecoded");
QTest::newRow("%") << QString("%") << true << QString("%25");
QTest::newRow("3%") << QString("3%") << true << QString("3%25");
QTest::newRow("13%") << QString("13%") << true << QString("13%25");
QTest::newRow("13%!") << QString("13%!") << true << QString("13%25!");
QTest::newRow("13%!!") << QString("13%!!") << true << QString("13%25!!");
QTest::newRow("13%a") << QString("13%a") << true << QString("13%25a");
QTest::newRow("13%az") << QString("13%az") << true << QString("13%25az");
}
void tst_QUrl::correctDecodedMistakes()
{
QFETCH(QString, decodedUrl);
QFETCH(bool, result);
QFETCH(QString, toDecoded);
QUrl url(decodedUrl);
QCOMPARE(url.isValid(), result);
if (url.isValid()) {
QCOMPARE(url.toString(), toDecoded);
} else {
QVERIFY(url.toString().isEmpty());
}
}
void tst_QUrl::emptyQueryOrFragment()
{
QCOMPARE(qurl.toEncoded().constData(), "http://www.kde.org/cgi/test.cgi?"); // Empty refs should be preserved
{
// start with an empty one
QVERIFY(!url.hasFragment());
QVERIFY(url.fragment().isNull());
// add fragment
url.setFragment(QLatin1String("abc"));
QVERIFY(url.hasFragment());
QCOMPARE(url.fragment(), QString(QLatin1String("abc")));
// remove fragment
url.setFragment(QString());
QVERIFY(!url.hasFragment());
QVERIFY(url.fragment().isNull());
// add empty fragment
url.setFragment(QLatin1String(""));
QVERIFY(url.hasFragment());
QVERIFY(url.fragment().isEmpty());
QVERIFY(!url.fragment().isNull());
}
{
// start with an empty one
QVERIFY(!url.hasQuery());
QVERIFY(url.query().isNull());
// add encodedQuery
url.setQuery("abc=def");
QVERIFY(url.hasQuery());
QCOMPARE(url.query(), QString(QLatin1String("abc=def")));
url.setQuery(QLatin1String("abc=def"));
// remove encodedQuery
url.setQuery(QString());
QVERIFY(!url.hasQuery());
QVERIFY(url.query().isNull());
url.setQuery(QString());
// add empty encodedQuery
url.setQuery("");
QVERIFY(url.hasQuery());
QVERIFY(url.query().isEmpty());
QVERIFY(!url.query().isNull());
url.setQuery(QLatin1String(""));
}
}
void tst_QUrl::hasFragment_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<bool>("trueFalse");
}
void tst_QUrl::hasFragment()
{
QFETCH(QString, url);
QFETCH(bool, trueFalse);
QUrl qurl(url);
QCOMPARE(qurl.hasFragment(), trueFalse);
QCOMPARE(qurl.fragment().isNull(), !trueFalse);
}
void tst_QUrl::setFragment_data()
{
QTest::addColumn<QString>("base");
QTest::addColumn<QString>("fragment");
QTest::addColumn<QString>("expected");
QTest::newRow("null") << QString::fromLatin1("http://www.kde.org") << QString() << QString::fromLatin1("http://www.kde.org");
QTest::newRow("empty") << QString::fromLatin1("http://www.kde.org") << QString::fromLatin1("") << QString::fromLatin1("http://www.kde.org#");
QTest::newRow("basic test") << QString::fromLatin1("http://www.kde.org") << QString::fromLatin1("abc") << QString::fromLatin1("http://www.kde.org#abc");
QTest::newRow("initial url has fragment") << QString::fromLatin1("http://www.kde.org#old") << QString::fromLatin1("new") << QString::fromLatin1("http://www.kde.org#new");
QTest::newRow("encoded fragment") << QString::fromLatin1("http://www.kde.org") << QString::fromLatin1("a%20c") << QString::fromLatin1("http://www.kde.org#a%20c");
QTest::newRow("with #") << QString::fromLatin1("http://www.kde.org") << QString::fromLatin1("a#b") << QString::fromLatin1("http://www.kde.org#a%23b"); // toString uses "a#b"
QTest::newRow("unicode") << QString::fromLatin1("http://www.kde.org") << QString::fromUtf8("\xc3\xa9") << QString::fromLatin1("http://www.kde.org#%C3%A9");
}
void tst_QUrl::setFragment()
{
QFETCH(QString, base);
QFETCH(QString, fragment);
QFETCH(QString, expected);
QUrl u;
u.setUrl(base, QUrl::TolerantMode);
QVERIFY(u.isValid());
u.setFragment(fragment);
QVERIFY(u.isValid());
QCOMPARE(!fragment.isNull(), u.hasFragment());
QCOMPARE(QString::fromUtf8(u.toEncoded()), expected);
}
void tst_QUrl::fromEncoded()
{
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(QFileInfo(qurl2.path()).fileName(), QString::fromLatin1("Print To File (PDF%2FAcrobat)"));
QCOMPARE(qurl2.fileName(), QString::fromLatin1("Print To File (PDF%2FAcrobat)"));
QCOMPARE(qurl2.toEncoded().constData(), "print:/specials/Print%20To%20File%20(PDF%252FAcrobat)");
QVERIFY(qurl.isValid());
QVERIFY(qurltest.isValid());
QVERIFY(qurl_newline_1.isValid());
}
void tst_QUrl::stripTrailingSlash_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<QString>("expectedStrip"); // toString(Strip)
QTest::addColumn<QString>("expectedDir"); // toString(RemoveFilename)
QTest::addColumn<QString>("expectedDirStrip"); // toString(RemoveFilename|Strip)
QTest::newRow("subdir no slash") << "ftp://kde.org/dir/subdir" << "ftp://kde.org/dir/subdir" << "ftp://kde.org/dir/" << "ftp://kde.org/dir";
QTest::newRow("ftp no slash") << "ftp://kde.org/dir" << "ftp://kde.org/dir" << "ftp://kde.org/" << "ftp://kde.org/";
QTest::newRow("ftp slash") << "ftp://kde.org/dir/" << "ftp://kde.org/dir" << "ftp://kde.org/dir/" << "ftp://kde.org/dir";
QTest::newRow("ftp_two_slashes") << "ftp://kde.org/dir//" << "ftp://kde.org/dir" << "ftp://kde.org/dir//" << "ftp://kde.org/dir";
QTest::newRow("no path") << "remote://" << "remote://" << "remote://" << "remote://";
QTest::newRow("no authority") << "/root/test/../foo/bar" << "/root/test/../foo/bar" << "/root/test/../foo/" << "/root/test/../foo";
// The URL spec distinguishes literal slashes from their counterparts
// encoded as %2F: the former are path separators, the latter aren't.
QTest::newRow("ftp encoded-slash") << "ftp://kde.org/dir%2F" << "ftp://kde.org/dir%2F" << "ftp://kde.org/" << "ftp://kde.org/";
QTest::newRow("ftp encoded-slash then slash") << "ftp://kde.org/dir%2F/" << "ftp://kde.org/dir%2F" << "ftp://kde.org/dir%2F/" << "ftp://kde.org/dir%2F";
QTest::newRow("file encoded-slash") << "file:///dir%2F" << "file:///dir%2F" << "file:///" << "file:///";
}
void tst_QUrl::stripTrailingSlash()
{
QFETCH(QString, url);
QFETCH(QString, expectedStrip);
QFETCH(QString, expectedDir);
QFETCH(QString, expectedDirStrip);
QUrl u(url);
QCOMPARE(u.toString(QUrl::StripTrailingSlash), expectedStrip);
QCOMPARE(u.toString(QUrl::RemoveFilename), expectedDir);
QCOMPARE(u.toString(QUrl::RemoveFilename | QUrl::StripTrailingSlash), expectedDirStrip);
// Same thing, using QUrl::adjusted()
QCOMPARE(u.adjusted(QUrl::StripTrailingSlash).toString(), expectedStrip);
QCOMPARE(u.adjusted(QUrl::RemoveFilename).toString(), expectedDir);
QCOMPARE(u.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toString(), expectedDirStrip);
}
void tst_QUrl::hosts_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<QString>("host");
QTest::newRow("empty") << QString("") << QString("");
QTest::newRow("empty2") << QString("file:/file") << QString("");
QTest::newRow("empty4") << QString("http:/file") << QString("");
// numeric hostnames -> decoded as IPv4 as per inet_aton(3)
// IP literals
<< QString("1.2.3.4");
<< QString("2001:200:0:8002:203:47ff:fea5:3085");
QTest::newRow("long-ipv6-literal-with-port") << QString("http://[2001:200:0:8002:203:47ff:fea5:3085]:80")
<< QString("2001:200:0:8002:203:47ff:fea5:3085");
<< QString("::255.254.253.252");
<< QString("::ffff:255.254.253.252");
<< QString("::ffff:255.254.253.252");
// no embedded v4 unless the cases above
<< QString("1000::ffff:7f80:8101");
QTest::newRow("long-ipv6-literal-v4decoded") << QString("http://[fec0:8000::8002:1000:ffff:200.100.50.250]")
<< QString("fec0:8000:0:8002:1000:ffff:c864:32fa");
QTest::newRow("longer-ipv6-literal-v4decoded") << QString("http://[fec0:8000:4000:8002:1000:ffff:200.100.50.250]")
<< QString("fec0:8000:4000:8002:1000:ffff:c864:32fa");
// normal hostnames
// IDN hostnames
QTest::newRow("idn") << QString(QLatin1String("http://\345r.no")) << QString(QLatin1String("\345r.no"));
}
void tst_QUrl::hosts()
{
QFETCH(QString, url);
QUrl u(url);
QTEST(u.host(), "host");
QVERIFY(u.isEmpty() || u.isValid());
}
void tst_QUrl::hostFlags_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<QUrl::FormattingOptions>("options");
QTest::addColumn<QString>("expectedHost");
QTest::newRow("se_fullydecoded") << swedish << QUrl::FormattingOptions(QUrl::FullyDecoded) << QString::fromUtf8("www.räksmörgås.se");
QTest::newRow("se_fullyencoded") << swedish << QUrl::FormattingOptions(QUrl::FullyEncoded) << QString::fromUtf8("www.xn--rksmrgs-5wao1o.se");
QTest::newRow("se_prettydecoded") << swedish << QUrl::FormattingOptions(QUrl::PrettyDecoded) << QString::fromUtf8("www.räksmörgås.se");
QTest::newRow("se_encodespaces") << swedish << QUrl::FormattingOptions(QUrl::EncodeSpaces) << QString::fromUtf8("www.räksmörgås.se");
}
void tst_QUrl::hostFlags()
{
QFETCH(QString, urlStr);
QFETCH(QUrl::FormattingOptions, options);
QFETCH(QString, expectedHost);
QUrl url(urlStr);
QCOMPARE(url.host(options), expectedHost);
}
void tst_QUrl::setPort()
{
{
QUrl url;
QVERIFY(url.toString().isEmpty());
url.setHost("a");
url.setPort(80);
QCOMPARE(url.port(), 80);
QCOMPARE(url.toString(), QString::fromLatin1("//a:80"));
url.setPort(-1);
url.setHost(QString());
QCOMPARE(url.port(), -1);
QCOMPARE(url.toString(), QString());
url.setPort(80);
url.setPort(65536);
QCOMPARE(url.port(), -1);
QVERIFY(url.errorString().contains("out of range"));
}
{
QUrl reference("//:80");
QUrl piecewise;
piecewise.setPort(80);
QCOMPARE(piecewise, reference);
}
{
// setAuthority must clear the port
url.setAuthority("example.org");
QCOMPARE(url.port(), -1);
}
{
// setAuthority must clear the port
url.setAuthority(QString());
QCOMPARE(url.port(), -1);
QCOMPARE(url.toString(), QString("http:"));
}
}
void tst_QUrl::port_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("port");
}
void tst_QUrl::port()
{
QFETCH(QString, input);
QFETCH(int, port);
QUrl url(input);
QVERIFY(url.isValid());
QCOMPARE(url.port(), port);
}
void tst_QUrl::toEncoded_data()
{
QTest::addColumn<QByteArray>("url");
QTest::addColumn<QUrl::FormattingOptions>("options");
QTest::addColumn<QByteArray>("encoded");
<< QUrl::FormattingOptions(QUrl::StripTrailingSlash)
}
void tst_QUrl::toEncoded()
{
QFETCH(QByteArray, url);
QFETCH(QUrl::FormattingOptions, options);
QFETCH(QByteArray, encoded);
QCOMPARE(QUrl::fromEncoded(url).toEncoded(options), encoded);
}
void tst_QUrl::setAuthority_data()
{
QTest::addColumn<QString>("authority");
QTest::addColumn<QString>("url");
QTest::newRow("Plain auth") << QString("62.70.27.22:21") << QString("//62.70.27.22:21");
QTest::newRow("Yet another plain auth") << QString("192.168.1.1:21") << QString("//192.168.1.1:21");
QTest::newRow("Auth without port") << QString("192.168.1.1") << QString("//192.168.1.1");
QTest::newRow("Auth w/full hostname without port") << QString("shusaku.troll.no") << QString("//shusaku.troll.no");
QTest::newRow("Auth w/hostname without port") << QString("shusaku") << QString("//shusaku");
QTest::newRow("Auth w/full hostname that ends with number, without port") << QString("shusaku.troll.no.2") << QString("//shusaku.troll.no.2");
QTest::newRow("Auth w/hostname that ends with number, without port") << QString("shusaku2") << QString("//shusaku2");
QTest::newRow("Empty auth") << QString() << QString();
}
void tst_QUrl::setAuthority()
{
QUrl u;
QFETCH(QString, authority);
QFETCH(QString, url);
u.setAuthority(authority);
QCOMPARE(u.toString(), url);
}
void tst_QUrl::setEmptyAuthority_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("authority");
QTest::addColumn<QString>("expectedUrlString");
QTest::newRow("null host and authority") << QString() << QString() << QString("");
QTest::newRow("empty host and authority") << QString("") << QString("") << QString("//");
}
void tst_QUrl::setEmptyAuthority()
{
QFETCH(QString, host);
QFETCH(QString, authority);
QFETCH(QString, expectedUrlString);
QUrl u;
u.setHost(host);
QCOMPARE(u.toString(), expectedUrlString);
u.setAuthority(authority);
QCOMPARE(u.toString(), expectedUrlString);
}
void tst_QUrl::clear()
{
QUrl url("a");
QUrl url2("a");
QCOMPARE(url, url2);
url.clear();
QVERIFY(url != url2);
}
void tst_QUrl::binaryData_data()
{
QTest::addColumn<QString>("url");
}
void tst_QUrl::binaryData()
{
QFETCH(QString, url);
QUrl u = QUrl::fromEncoded(url.toUtf8());
QVERIFY(u.isValid());
QVERIFY(!u.isEmpty());
QString url2 = QString::fromUtf8(u.toEncoded());
//QCOMPARE(url2.length(), url.length());
QCOMPARE(url2, url);
}
void tst_QUrl::fromUserInput_data()
{
//
// most of this test is:
// Copyright (C) Research In Motion Limited 2009. All rights reserved.
// Distributed under the BSD license.
// See qurl.cpp
//
QTest::addColumn<QString>("string");
QTest::addColumn<QUrl>("guessUrlFromString");
// Null
QTest::newRow("null") << QString() << QUrl();
// File
QDirIterator it(QDir::homePath());
int c = 0;
while (it.hasNext()) {
it.next();
QTest::newRow(("file-" + QByteArray::number(c++)).constData())
<< it.filePath() << QUrl::fromLocalFile(it.filePath());
}
// basic latin1
QTest::newRow("unicode-0") << QString::fromUtf8("\xc3\xa5.com/") << QUrl::fromEncoded(QString::fromUtf8("http://\xc3\xa5.com/").toUtf8(), QUrl::TolerantMode);
QTest::newRow("unicode-0b") << QString::fromUtf8("\xc3\xa5.com/") << QUrl::fromEncoded("http://%C3%A5.com/", QUrl::TolerantMode);
QTest::newRow("unicode-0c") << QString::fromUtf8("\xc3\xa5.com/") << QUrl::fromEncoded("http://xn--5ca.com/", QUrl::TolerantMode);
// unicode
QTest::newRow("unicode-1") << QString::fromUtf8("\xce\xbb.com/") << QUrl::fromEncoded(QString::fromUtf8("http://\xce\xbb.com/").toUtf8(), QUrl::TolerantMode);
QTest::newRow("unicode-1b") << QString::fromUtf8("\xce\xbb.com/") << QUrl::fromEncoded("http://%CE%BB.com/", QUrl::TolerantMode);
QTest::newRow("unicode-1c") << QString::fromUtf8("\xce\xbb.com/") << QUrl::fromEncoded("http://xn--wxa.com/", QUrl::TolerantMode);
// no scheme
// no host
QTest::newRow("nohost-1") << "http://" << QUrl("http://");
QTest::newRow("nohost-2") << "smb:" << QUrl("smb:");
// QUrl's tolerant parser should already handle this
QTest::newRow("not-encoded-0") << "http://example.org/test page.html" << QUrl::fromEncoded("http://example.org/test%20page.html");
// Make sure the :80, i.e. port doesn't screw anything up
portUrl.setPort(80);
QTest::newRow("port-0") << "example.org:80" << portUrl;
portUrl.setPath("/path");
QTest::newRow("port-2") << "example.org:80/path" << portUrl;
// mailto doesn't have a ://, but is valid
QUrl mailto("ben@example.net");
mailto.setScheme("mailto");
QTest::newRow("mailto") << "mailto:ben@example.net" << mailto;
// misc
QTest::newRow("spaces-0") << " http://example.org/test page.html " << QUrl("http://example.org/test%20page.html");
QTest::newRow("trash-0") << "example.org/test?someData=42%&someOtherData=abcde#anchor" << QUrl::fromEncoded("http://example.org/test?someData=42%25&someOtherData=abcde#anchor");
QTest::newRow("other-scheme-0") << "spotify:track:0hO542doVbfGDAGQULMORT" << QUrl("spotify:track:0hO542doVbfGDAGQULMORT");
QTest::newRow("other-scheme-1") << "weirdscheme:80:otherstuff" << QUrl("weirdscheme:80:otherstuff");
QTest::newRow("number-path-0") << "tel:2147483648" << QUrl("tel:2147483648");
// FYI: The scheme in the resulting url user
QUrl authUrl("user:pass@domain.com");
QTest::newRow("misc-1") << "user:pass@domain.com" << authUrl;
// FTP with double slashes in path
QTest::newRow("ftp-double-slash-no-scheme")
QTest::newRow("ftp-double-slash-scheme")
}
void tst_QUrl::fromUserInput()
{
QFETCH(QString, string);
QFETCH(QUrl, guessUrlFromString);
QUrl url = QUrl::fromUserInput(string);
QCOMPARE(url, guessUrlFromString);
}
void tst_QUrl::fromUserInputWithCwd_data()
{
QTest::addColumn<QString>("string");
QTest::addColumn<QString>("directory");
QTest::addColumn<QUrl>("guessedUrlDefault");
QTest::addColumn<QUrl>("guessedUrlAssumeLocalFile");
// Null
QTest::newRow("null") << QString() << QString() << QUrl() << QUrl();
// Use a tempdir with files, for testing specific file names
// We use canonicalPath() on the dir path because ::getcwd() canonicalizes,
// so we get a canonical base path for URLs with "." as working directory.
const QString base = QDir(m_tempDir.path()).canonicalPath();
QDir::setCurrent(base); // for the tests that use "." as working dir
// "."
{
const QUrl url = QUrl::fromLocalFile(base); // fromUserInput cleans the path
QTest::newRow("dot-in-path") << "." << base << url << url;
QTest::newRow("dot-in-dot") << "." << QStringLiteral(".") << url << url;
}
// Existing files
for (const char *fileName : {"file.txt", "file#a.txt", "file .txt", "file.txt "
#ifndef Q_OS_WIN
, "file:colon.txt"
#endif
}) {
const QString filePath = base + '/' + fileName;
QFile file(filePath);
QVERIFY2(file.open(QIODevice::WriteOnly), qPrintable(filePath));
file.write("Hello world\n");
const QUrl url = QUrl::fromLocalFile(filePath);
QTest::newRow(fileName) << fileName << base << url << url;
QTest::newRow(QByteArray(fileName) + "-in-dot") << fileName << QStringLiteral(".") << url << url;
}
QDir parent(base);
QVERIFY(parent.cdUp());
QUrl parentUrl = QUrl::fromLocalFile(parent.path());
QTest::newRow("dotdot") << ".." << base << parentUrl << parentUrl;
QTest::newRow("nonexisting") << "nonexisting" << base << QUrl("http://nonexisting") << QUrl::fromLocalFile(base + "/nonexisting");
QTest::newRow("short-url") << "example.org" << base << QUrl("http://example.org") << QUrl::fromLocalFile(base + "/example.org");
QTest::newRow("full-url") << "http://example.org" << base << QUrl("http://example.org") << QUrl("http://example.org");
QTest::newRow("absolute") << "/doesnotexist.txt" << base << QUrl("file:///doesnotexist.txt") << QUrl("file:///doesnotexist.txt");
#ifdef Q_OS_WIN
QTest::newRow("windows-absolute") << "c:/doesnotexist.txt" << base << QUrl("file:///c:/doesnotexist.txt") << QUrl("file:///c:/doesnotexist.txt");
#endif
// IPv4 & IPv6
// same as fromUserInput, but needs retesting
QTest::newRow("ipv4-1") << "127.0.0.1" << base << QUrl("http://127.0.0.1") << QUrl::fromLocalFile(base + "/127.0.0.1");
QTest::newRow("ipv6-5") << "c:f00:ba4::" << base << QUrl("http://[c:f00:ba4::]") << QUrl("http://[c:f00:ba4::]");
}
void tst_QUrl::fromUserInputWithCwd()
{
QFETCH(QString, string);
QFETCH(QString, directory);
QFETCH(QUrl, guessedUrlDefault);
QFETCH(QUrl, guessedUrlAssumeLocalFile);
QUrl url = QUrl::fromUserInput(string, directory);
QCOMPARE(url, guessedUrlDefault);
url = QUrl::fromUserInput(string, directory, QUrl::AssumeLocalFile);
QCOMPARE(url, guessedUrlAssumeLocalFile);
}
void tst_QUrl::fileName_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<QString>("expectedDirPath");
QTest::addColumn<QString>("expectedPrettyDecodedFileName");
QTest::addColumn<QString>("expectedFullyDecodedFileName");
<< "/support/" << "file.html" << "file.html";
<< "/temp/" << "tmp.txt" << "tmp.txt";
<< "/temp/" << QString() << QString();
<< "/" << "temp" << "temp";
QTest::newRow("relative") << "temp/tmp.txt"
<< "temp/" << "tmp.txt" << "tmp.txt";
QTest::newRow("relativeNoSlash") << "tmp.txt"
<< QString() << "tmp.txt" << "tmp.txt";
QTest::newRow("encoded") << "print:/specials/Print%20To%20File%20(PDF%252FAcrobat)"
<< "/specials/" << "Print To File (PDF%252FAcrobat)" << "Print To File (PDF%2FAcrobat)";
<< "/temp/" << "." << ".";
}
void tst_QUrl::fileName()
{
QFETCH(QString, urlStr);
QFETCH(QString, expectedDirPath);
QFETCH(QString, expectedPrettyDecodedFileName);
QFETCH(QString, expectedFullyDecodedFileName);
QUrl url(urlStr);
QVERIFY(url.isValid());
QCOMPARE(url.adjusted(QUrl::RemoveFilename).path(), expectedDirPath);
QCOMPARE(url.fileName(QUrl::PrettyDecoded), expectedPrettyDecodedFileName);
QCOMPARE(url.fileName(QUrl::FullyDecoded), expectedFullyDecodedFileName);
}
// This is a regression test for a previously fixed bug where isEmpty didn't
// work for an encoded URL that was yet to be decoded. The test checks that
// isEmpty works for an encoded URL both after and before decoding.
void tst_QUrl::isEmptyForEncodedUrl()
{
{
QUrl url;
url.setUrl(QLatin1String("LABEL=USB_STICK"), QUrl::TolerantMode);
QVERIFY( url.isValid() );
QCOMPARE( url.path(), QString("LABEL=USB_STICK") );
QVERIFY( !url.isEmpty() );
}
{
QUrl url;
url.setUrl(QLatin1String("LABEL=USB_STICK"), QUrl::TolerantMode);
QVERIFY( url.isValid() );
QVERIFY( !url.isEmpty() );
QCOMPARE( url.path(), QString("LABEL=USB_STICK") );
}
}
// This test verifies that the QUrl::toEncoded() does not rely on the
// potentially uninitialized unencoded path.
void tst_QUrl::toEncodedNotUsingUninitializedPath()
{
QUrl url;
url.setPath(QLatin1String("/test.txt"));
url.setHost("example.com");
QCOMPARE(url.toEncoded().constData(), "//example.com/test.txt");
url.path();
QCOMPARE(url.toEncoded().constData(), "//example.com/test.txt");
}
void tst_QUrl::resolvedWithAbsoluteSchemes() const
{
QFETCH(QUrl, base);
QFETCH(QUrl, relative);
QFETCH(QUrl, expected);
/* Check our input. */
QVERIFY(relative.isValid());
QVERIFY(base.isValid());
QVERIFY(expected.isValid());
const QUrl result(base.resolved(relative));
QVERIFY(result.isValid());
QCOMPARE(result, expected);
}
void tst_QUrl::resolvedWithAbsoluteSchemes_data() const
{
QTest::addColumn<QUrl>("base");
QTest::addColumn<QUrl>("relative");
QTest::addColumn<QUrl>("expected");
<< QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
<< QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E");
QTest::newRow("Resolve with base url and port.")
<< QUrl::fromEncoded("newfile.html")
QTest::newRow("Resolve with relative path")
}
void tst_QUrl::emptyAuthorityRemovesExistingAuthority_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("expected");
}
void tst_QUrl::emptyAuthorityRemovesExistingAuthority()
{
QFETCH(QString, input);
QFETCH(QString, expected);
QUrl url(input);
QUrl orig = url;
url.setAuthority(QString());
QCOMPARE(url.authority(), QString());
QVERIFY(url != orig);
QCOMPARE(url.toString(), expected);
QCOMPARE(url, QUrl(expected));
}
void tst_QUrl::acceptEmptyAuthoritySegments()
{
QCOMPARE(QUrl("remote://").toString(), QString::fromLatin1("remote://"));
QVERIFY(QUrl(foo_triple_bar) != QUrl(foo_uni_bar));
QCOMPARE(QUrl(foo_triple_bar).toString(), foo_triple_bar);
QCOMPARE(QUrl(foo_triple_bar).toEncoded(), foo_triple_bar.toLatin1());
QCOMPARE(QUrl(foo_uni_bar).toString(), foo_uni_bar);
QCOMPARE(QUrl(foo_uni_bar).toEncoded(), foo_uni_bar.toLatin1());
QCOMPARE(QUrl(foo_triple_bar, QUrl::StrictMode).toString(), foo_triple_bar);
QCOMPARE(QUrl(foo_triple_bar, QUrl::StrictMode).toEncoded(), foo_triple_bar.toLatin1());
QCOMPARE(QUrl(foo_uni_bar, QUrl::StrictMode).toString(), foo_uni_bar);
QCOMPARE(QUrl(foo_uni_bar, QUrl::StrictMode).toEncoded(), foo_uni_bar.toLatin1());
QCOMPARE(QUrl(file_triple_bar), QUrl(file_uni_bar));
QCOMPARE(QUrl(file_uni_bar).toString(), file_triple_bar);
QCOMPARE(QUrl(file_uni_bar, QUrl::StrictMode).toString(), file_triple_bar);
}
void tst_QUrl::lowercasesScheme()
{
QUrl url;
url.setScheme("HELLO");
QCOMPARE(url.scheme(), QString("hello"));
}
void tst_QUrl::componentEncodings_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<int>("encoding");
QTest::addColumn<QString>("userName");
QTest::addColumn<QString>("password");
QTest::addColumn<QString>("userInfo");
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("authority");
QTest::addColumn<QString>("path");
QTest::addColumn<QString>("query");
QTest::addColumn<QString>("fragment");
QTest::addColumn<QString>("toString");
const int MostDecoded = QUrl::DecodeReserved; // the most decoded mode without being fully decoded
QTest::newRow("empty") << QUrl() << int(QUrl::FullyEncoded)
<< QString() << QString() << QString()
<< QString() << QString()
<< QString() << QString() << QString() << QString();
// hostname cannot contain spaces
QTest::newRow("encoded-space") << QUrl("x://user name:pass word@host/path name?query value#fragment value")
<< int(QUrl::EncodeSpaces)
<< "user%20name" << "pass%20word" << "user%20name:pass%20word"
<< "host" << "user%20name:pass%20word@host"
<< "/path%20name" << "query%20value" << "fragment%20value"
QTest::newRow("decoded-space") << QUrl("x://user%20name:pass%20word@host/path%20name?query%20value#fragment%20value")
<< MostDecoded
<< "user name" << "pass word" << "user name:pass word"
<< "host" << "user name:pass word@host"
<< "/path name" << "query value" << "fragment value"
// binary data is always encoded
// this is also testing non-UTF8 data
<< MostDecoded
<< "%C0%00" << "%C1%01" << "%C0%00:%C1%01"
<< "host" << "%C0%00:%C1%01@host"
<< "/%C2%02" << "%C3%03" << "%D4%04"
// unicode tests
// hostnames can participate in this test, but we need a top-level domain that accepts Unicode
QTest::newRow("encoded-unicode") << QUrl(QString::fromUtf8("x://\xc2\x80:\xc3\x90@smørbrød.example.no/\xe0\xa0\x80?\xf0\x90\x80\x80#é"))
<< int(QUrl::EncodeUnicode)
<< "%C2%80" << "%C3%90" << "%C2%80:%C3%90"
<< "xn--smrbrd-cyad.example.no" << "%C2%80:%C3%90@xn--smrbrd-cyad.example.no"
<< "/%E0%A0%80" << "%F0%90%80%80" << "%C3%A9"
QTest::newRow("decoded-unicode") << QUrl("x://%C2%80:%C3%90@XN--SMRBRD-cyad.example.NO/%E0%A0%80?%F0%90%80%80#%C3%A9")
<< MostDecoded
<< QString::fromUtf8("\xc2\x80") << QString::fromUtf8("\xc3\x90")
<< QString::fromUtf8("\xc2\x80:\xc3\x90")
<< QString::fromUtf8("smørbrød.example.no")
<< QString::fromUtf8("\xc2\x80:\xc3\x90@smørbrød.example.no")
<< QString::fromUtf8("/\xe0\xa0\x80")
<< QString::fromUtf8("\xf0\x90\x80\x80") << QString::fromUtf8("é")
<< QString::fromUtf8("x://\xc2\x80:\xc3\x90@smørbrød.example.no/\xe0\xa0\x80?\xf0\x90\x80\x80#é");
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// these are always decoded
<< int(QUrl::FullyEncoded)
<< "a" << "q" << "a:q"
<< "a0.example.com" << "a:q@a0.example.com"
<< "/~" << "_" << "Q"
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// these are always left alone
<< int(QUrl::FullyEncoded)
<< "!$&" << "'()" << "!$&:'()"
<< "host" << "!$&:'()@host"
<< "/*+," << "$=(+)" << ";="
QTest::newRow("encoded-subdelims") << QUrl("x://%21%24%26:%27%28%29@host/%2a%2b%2c?%26=%26&%3d=%3d#%3b%3d")
<< MostDecoded
<< "%21%24%26" << "%27%28%29" << "%21%24%26:%27%28%29"
<< "host" << "%21%24%26:%27%28%29@host"
<< "/%2A%2B%2C" << "%26=%26&%3D=%3D" << "%3B%3D"
// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
// these are the separators between fields
// they must appear encoded in certain positions in the full URL, no exceptions
// when in those positions, they appear decoded in the isolated parts
// in other positions and the other delimiters are always left untransformed
// 1) test the delimiters that must appear encoded
// (if they were decoded, they'd would change the URL parsing)
QTest::newRow("encoded-gendelims-changing") << QUrl("x://%5b%3a%2f%3f%23%40%5d:%5b%2f%3f%23%40%5d@host/%2f%3f%23?%23")
<< MostDecoded
<< "[:/?#@]" << "[/?#@]" << "[%3A/?#@]:[/?#@]"
<< "host" << "%5B%3A/?#%40%5D:%5B/?#%40%5D@host"
<< "/%2F?#" << "#" << ""
// 2) test that the other delimiters remain decoded
<< int(QUrl::FullyEncoded)
<< "" << ":" << "::"
<< "host" << "::@host"
<< "/:@/[]" << ":/?@[]?" << ":/?@[]"
// 3) and test that the same encoded sequences remain encoded
QTest::newRow("encoded-gendelims-unchanging") << QUrl("x://:%3A@host/%3A%40%5B%5D?%3A%2F%3F%40%5B%5D#%23%3A%2F%3F%40%5B%5D")
<< MostDecoded
<< "" << "%3A" << ":%3A"
<< "host" << ":%3A@host"
<< "/%3A%40%5B%5D" << "%3A%2F%3F%40%5B%5D" << "%23%3A%2F%3F%40%5B%5D"
// test the query
// since QUrl doesn't know what chars the user wants to use for the pair and value delimiters,
// it keeps the delimiters alone except for "#", which must always be encoded.
// In the following test, all delimiter characters appear both as encoded and as decoded (except for "#")
QTest::newRow("unencoded-delims-query") << QUrl("?!$()*+,;=:/?[]@%21%24%26%27%28%29%2a%2b%2c%2f%3a%3b%3d%3f%40%5b%5d")
<< int(QUrl::FullyEncoded)
<< QString() << QString() << QString()
<< QString() << QString()
<< QString() << "!$()*+,;=:/?[]@%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" << QString()
<< "?!$()*+,;=:/?[]@%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D";
QTest::newRow("undecoded-delims-query") << QUrl("?!$()*+,;=:/?[]@%21%24%26%27%28%29%2a%2b%2c%2f%3a%3b%3d%3f%40%5b%5d")
<< MostDecoded
<< QString() << QString() << QString()
<< QString() << QString()
<< QString() << "!$()*+,;=:/?[]@%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" << QString()
<< "?!$()*+,;=:/?[]@%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D";
// reserved characters: '"' / "<" / ">" / "^" / "\" / "{" / "|" "}"
// the RFC does not allow them undecoded anywhere, but we do
QTest::newRow("encoded-reserved") << QUrl("x://\"<>^\\{|}:\"<>^\\{|}@host/\"<>^\\{|}?\"<>^\\{|}#\"<>^\\{|}")
<< int(QUrl::FullyEncoded)
<< "%22%3C%3E%5E%5C%7B%7C%7D" << "%22%3C%3E%5E%5C%7B%7C%7D"
<< "%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D"
<< "host" << "%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host"
<< "/%22%3C%3E%5E%5C%7B%7C%7D" << "%22%3C%3E%5E%5C%7B%7C%7D"
<< "%22%3C%3E%5E%5C%7B%7C%7D"
"?%22%3C%3E%5E%5C%7B%7C%7D#%22%3C%3E%5E%5C%7B%7C%7D";
QTest::newRow("decoded-reserved") << QUrl("x://%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host"
"/%22%3C%3E%5E%5C%7B%7C%7D?%22%3C%3E%5E%5C%7B%7C%7D#%22%3C%3E%5E%5C%7B%7C%7D")
<< int(QUrl::DecodeReserved)
<< "\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}:\"<>^\\{|}"
<< "host" << "\"<>^\\{|}:\"<>^\\{|}@host"
<< "/\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}"
// Beauty is in the eye of the beholder
// Test PrettyDecoder against our expectations
// spaces and unicode are considered pretty and are decoded
// this includes hostnames
QTest::newRow("pretty-spaces-unicode") << QUrl("x://%20%c3%a9:%c3%a9%20@XN--SMRBRD-cyad.example.NO/%c3%a9%20?%20%c3%a9#%c3%a9%20")
<< int(QUrl::PrettyDecoded)
<< QString::fromUtf8(" é") << QString::fromUtf8("é ")
<< QString::fromUtf8(" é:é ")
<< QString::fromUtf8("smørbrød.example.no")
<< QString::fromUtf8(" é:é @smørbrød.example.no")
<< QString::fromUtf8("/é ") << QString::fromUtf8(" é")
<< QString::fromUtf8("é ")
<< QString::fromUtf8("x:// é:é @smørbrød.example.no/é ? é#é ");
// the pretty form decodes all unambiguous gen-delims in the individual parts
QTest::newRow("pretty-gendelims") << QUrl("x://%5b%3a%40%2f%3f%23%5d:%5b%40%2f%3f%23%5d@host/%3f%23?%23")
<< int(QUrl::PrettyDecoded)
<< "[:@/?#]" << "[@/?#]" << "[%3A@/?#]:[@/?#]"
<< "host" << "%5B%3A%40/?#%5D:%5B%40/?#%5D@host"
<< "/?#" << "#" << ""
// the pretty form keeps the other characters decoded everywhere
// except when rebuilding the full URL, when we only allow "{}" to remain decoded
QTest::newRow("pretty-reserved") << QUrl("x://\"<>^\\{|}:\"<>^\\{|}@host/\"<>^\\{|}?\"<>^\\{|}#\"<>^\\{|}")
<< int(QUrl::PrettyDecoded)
<< "\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}:\"<>^\\{|}"
<< "host" << "\"<>^\\{|}:\"<>^\\{|}@host"
<< "/\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}"
"?%22%3C%3E%5E%5C%7B%7C%7D#%22%3C%3E%5E%5C%7B%7C%7D";
}
void tst_QUrl::componentEncodings()
{
QFETCH(QUrl, url);
QFETCH(int, encoding);
QFETCH(QString, userName);
QFETCH(QString, password);
QFETCH(QString, userInfo);
QFETCH(QString, host);
QFETCH(QString, authority);
QFETCH(QString, path);
QFETCH(QString, query);
QFETCH(QString, fragment);
QFETCH(QString, toString);
QUrl::ComponentFormattingOptions formatting(encoding);
QCOMPARE(url.userName(formatting), userName);
QCOMPARE(url.password(formatting), password);
QCOMPARE(url.userInfo(formatting), userInfo);
QCOMPARE(url.host(formatting), host);
QCOMPARE(url.authority(formatting), authority);
QCOMPARE(url.path(formatting), path);
QCOMPARE(url.query(formatting), query);
QCOMPARE(url.fragment(formatting), fragment);
QCOMPARE(url.toString(formatting),
(((QString(toString ))))); // the weird () and space is to align the output
// repeat with the URL we got from toString
QUrl url2(toString);
QCOMPARE(url2.userName(formatting), userName);
QCOMPARE(url2.password(formatting), password);
QCOMPARE(url2.userInfo(formatting), userInfo);
QCOMPARE(url2.host(formatting), host);
QCOMPARE(url2.authority(formatting), authority);
QCOMPARE(url2.path(formatting), path);
QCOMPARE(url2.query(formatting), query);
QCOMPARE(url2.fragment(formatting), fragment);
QCOMPARE(url2.toString(formatting), toString);
// and use the comparison operator
QCOMPARE(url2, url);
}
enum Component {
Scheme = 0x01,
UserName = 0x02,
Password = 0x04,
UserInfo = UserName | Password,
Host = 0x08,
Port = 0x10,
Authority = UserInfo | Host | Port,
Path = 0x20,
Hierarchy = Authority | Path,
Query = 0x40,
Fragment = 0x80,
FullUrl = 0xff
};
void tst_QUrl::setComponents_data()
{
QTest::addColumn<QUrl>("original");
QTest::addColumn<int>("component");
QTest::addColumn<QString>("newValue");
QTest::addColumn<int>("parsingMode");
QTest::addColumn<bool>("isValid");
QTest::addColumn<int>("encoding");
QTest::addColumn<QString>("output");
QTest::addColumn<QString>("toString");
const int Tolerant = QUrl::TolerantMode;
const int Strict = QUrl::StrictMode;
const int Decoded = QUrl::DecodedMode;
const int PrettyDecoded = QUrl::PrettyDecoded;
const int FullyDecoded = QUrl::FullyDecoded;
const int Normalized = QUrl::PrettyDecoded | QUrl::NormalizePathSegments;
const int NoFilename = QUrl::PrettyDecoded | QUrl::RemoveFilename;
// -- test empty vs null --
// there's no empty-but-present scheme or path
// a URL with an empty scheme is a "URI reference"
// and the path is always non-empty if it's present
<< int(Scheme) << QString() << Tolerant << true
<< PrettyDecoded << QString() << "//example.com";
<< int(Scheme) << "" << Tolerant << true
<< PrettyDecoded << "" << "//example.com";
<< int(Path) << QString() << Tolerant << true
<< int(Path) << "" << Tolerant << true
QTest::newRow("path-encoded-slash-pretty")
<< int(Path) << "/a%2Fb" << Tolerant << true
<< int(Path) << "/a%2Fb" << Tolerant << true
<< int(Path) << u"/a\0b"_s << Decoded << true
<< int(Path) << u"/a\0b"_s << Decoded << true
// If the %3A gets decoded to ":", the URL becomes invalid;
// see test path-invalid-1 below
QTest::newRow("path-%3A-before-slash") << QUrl()
<< int(Path) << "c%3A/" << Tolerant << true
<< PrettyDecoded << "c%3A/" << "c%3A/";
<< int(Path) << "//path" << Tolerant << true
<< int(Path) << "//tmp/..///root/." << Tolerant << true
// Normalization and RemoveFilename treat an encoded slash as a literal
// character, not a separator, so "%2F..%2F" is not a traversal.
<< int(Path) << "/a/../b" << Tolerant << true
QTest::newRow("path-normalize-encoded-slash-dotdot")
<< int(Path) << "/a/%2F..%2F/b" << Tolerant << true
QTest::newRow("path-removefilename-encoded-slash")
<< int(Path) << "/a/b%2Fc" << Tolerant << true
QTest::newRow("path-removefilename-encoded-slash-name")
<< int(Path) << "/a%2Fb/c" << Tolerant << true
// the other fields can be present and be empty
// that is, their delimiters would be present, but there would be nothing to one side
<< int(UserInfo) << QString() << Tolerant << true
<< int(UserInfo) << "" << Tolerant << true
<< int(UserInfo) << ":" << Tolerant << true
<< int(UserName) << QString() << Tolerant << true
<< int(UserName) << "" << Tolerant << true
<< int(UserName) << "" << Tolerant << true
<< int(UserName) << "" << Tolerant << true
<< int(Password) << QString() << Tolerant << true
<< int(Password) << "" << Tolerant << true
<< int(Host) << QString() << Tolerant << true
<< PrettyDecoded << QString() << "foo:/path";
<< int(Host) << "" << Tolerant << true
<< int(Authority) << QString() << Tolerant << true
<< PrettyDecoded << QString() << "foo:/path";
<< int(Authority) << "" << Tolerant << true
<< int(Query) << QString() << Tolerant << true
<< int(Query) << "" << Tolerant << true
<< int(Fragment) << QString() << Tolerant << true
<< int(Fragment) << "" << Tolerant << true
// -- test some non-valid components --
<< int(Scheme) << "1http" << Tolerant << false
<< PrettyDecoded << "" << "";
<< int(Scheme) << "http%40" << Tolerant << false
<< PrettyDecoded << "" << "";
<< int(Scheme) << "http%61" << Strict << false
<< PrettyDecoded << "" << "";
<< int(UserName) << "{}" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserName) << "foo/bar" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserName) << "foo:bar" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Password) << "{}" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Password) << "foo/bar" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Password) << "foo:bar" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserInfo) << "{}" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserInfo) << "foo/bar" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Host) << "-not-valid-" << Tolerant << false
<< PrettyDecoded << QString() << QString();
<< int(Host) << "%31%30.%30.%30.%31" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Host) << "foo/bar" << Tolerant << false
<< PrettyDecoded << QString() << QString();
<< int(Host) << "foo\\bar" << Tolerant << false
<< PrettyDecoded << QString() << QString();
<< int(Host) << u"foo\0bar"_s << Tolerant << false
<< PrettyDecoded << QString() << QString();
<< int(Authority) << "-not-valid-" << Tolerant << false
<< PrettyDecoded << "" << QString();
<< int(Authority) << "%31%30.%30.%30.%31" << Strict << false
<< PrettyDecoded << "" << QString();
<< int(Path) << "{}" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Query) << "{}" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Fragment) << "{}" << Strict << false
<< PrettyDecoded << QString() << QString();
// these test cases are "compound invalid":
// they produces isValid == false, but the original is still available
QTest::newRow("invalid-path-1") << QUrl("/relative")
<< int(Path) << "c:/" << Strict << false
<< PrettyDecoded << "c:/" << QString();
<< int(Path) << "relative" << Strict << false
<< PrettyDecoded << "relative" << QString();
QTest::newRow("invalid-path-3") << QUrl("trash:/")
<< int(Path) << "//path" << Tolerant << false
<< PrettyDecoded << "//path" << QString();
// -- test bad percent encoding --
// unnecessary to test the scheme, since percent-decoding is not performed in it;
// see tests above
<< int(UserName) << "bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Password) << "bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserInfo) << "bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserInfo) << "bar%:foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(UserInfo) << "bar:%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Authority) << "bar%foo@example.org" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Authority) << "bar%:foo@example.org" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Authority) << "bar:%foo@example.org" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Authority) << "bar:foo@bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Host) << "bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Path) << "/bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Query) << "bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
<< int(Fragment) << "bar%foo" << Strict << false
<< PrettyDecoded << QString() << QString();
// -- test decoded behaviour --
// '%' characters are not permitted in the scheme, this tests that it fails to set anything
<< int(Scheme) << "http%61" << Decoded << false
<< PrettyDecoded << "" << "";
<< int(UserName) << "h%61llo[:]world" << Decoded << true
<< int(Password) << "h%61llo[:]world@" << Decoded << true
// '%' characters are not permitted in the hostname, these test that it fails to set anything
<< int(Host) << "ex%61mple.com" << Decoded << false
<< PrettyDecoded << QString() << QString();
// square brackets are force-encoded from decoded forms in the path, query, and fragment
<< int(Path) << "/ba[r]%23" << Decoded << true
<< int(Query) << "ba[r]%23" << Decoded << true
<< int(Fragment) << "ba[r]%23" << Decoded << true
// force decoding
<< int(UserName) << "hello%3Aworld%25" << Tolerant << true
<< int(Password) << "}}>b9o%25kR(" << Tolerant << true
<< int(Path) << "/bar%25[foo]" << Tolerant << true
<< int(Query) << "bar%25foo" << Tolerant << true
<< int(Fragment) << "bar%25foo" << Tolerant << true
}
void tst_QUrl::setComponents()
{
QFETCH(QUrl, original);
QUrl copy(original);
QFETCH(int, component);
QFETCH(int, parsingMode);
QFETCH(QString, newValue);
QFETCH(int, encoding);
QFETCH(QString, output);
#define QNULLCOMPARE(a, b) \
do { QCOMPARE(a, b); QCOMPARE(a.isNull(), b.isNull()); } while (false)
switch (component) {
case Scheme:
// scheme is only parsed in strict mode
copy.setScheme(newValue);
QCOMPARE(copy.scheme(), output); // schemes don't become null
break;
case Path:
copy.setPath(newValue, QUrl::ParsingMode(parsingMode));
QNULLCOMPARE(copy.path(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case UserInfo:
copy.setUserInfo(newValue, QUrl::ParsingMode(parsingMode));
QNULLCOMPARE(copy.userInfo(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case UserName:
copy.setUserName(newValue, QUrl::ParsingMode(parsingMode));
QNULLCOMPARE(copy.userName(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case Password:
copy.setPassword(newValue, QUrl::ParsingMode(parsingMode));
QNULLCOMPARE(copy.password(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case Host:
copy.setHost(newValue, QUrl::ParsingMode(parsingMode));
QNULLCOMPARE(copy.host(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case Authority:
copy.setAuthority(newValue, QUrl::ParsingMode(parsingMode));
QNULLCOMPARE(copy.authority(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case Query:
copy.setQuery(newValue, QUrl::ParsingMode(parsingMode));
QCOMPARE(copy.hasQuery(), !newValue.isNull());
QCOMPARE(copy.query(QUrl::ComponentFormattingOptions(encoding)), output);
break;
case Fragment:
copy.setFragment(newValue, QUrl::ParsingMode(parsingMode));
QCOMPARE(copy.hasFragment(), !newValue.isNull());
QCOMPARE(copy.fragment(QUrl::ComponentFormattingOptions(encoding)), output);
break;
}
#undef QNULLCOMPARE
QFETCH(bool, isValid);
QCOMPARE(copy.isValid(), isValid);
if (isValid) {
QFETCH(QString, toString);
QCOMPARE(copy.toString(), toString);
// Check round-tripping
QCOMPARE(QUrl(copy.toString()).toString(), toString);
// check comparisons
QUrl recreated(toString);
QCOMPARE(copy, recreated);
QCOMPARE(qHash(copy), qHash(recreated));
QCOMPARE(qHash(copy, 1), qHash(recreated, 1));
} else {
QVERIFY(copy.toString().isEmpty());
}
}
void tst_QUrl::streaming_data()
{
QTest::addColumn<QString>("urlStr");
QTest::newRow("accentuated") << QString::fromUtf8("trash:/été");
QTest::newRow("empty") << "";
}
void tst_QUrl::streaming()
{
QFETCH(QString, urlStr);
QUrl url(urlStr);
QByteArray buffer;
QDataStream writeStream( &buffer, QIODevice::WriteOnly );
writeStream << url;
QDataStream stream( buffer );
QUrl restored;
stream >> restored;
if (url.isValid())
QCOMPARE(restored.url(), url.url());
else
QVERIFY(!restored.isValid());
}
void tst_QUrl::detach()
{
QUrl empty;
empty.detach();
QUrl foo2 = foo;
foo2.detach(); // not that it's needed, given that setHost detaches, of course. But this increases coverage :)
foo2.setHost("www.gnome.org");
QCOMPARE(foo2.host(), QString("www.gnome.org"));
QCOMPARE(foo.host(), QString("www.kde.org"));
}
// Test accessing the same QUrl from multiple threads concurrently
// To maximize the chances of a race (and of a report from helgrind), we actually use
// 10 urls rather than one.
class UrlStorage
{
public:
UrlStorage() {
m_urls.resize(10);
for (int i = 0 ; i < m_urls.size(); ++i)
}
QList<QUrl> m_urls;
};
static const UrlStorage * s_urlStorage = nullptr;
void tst_QUrl::testThreadingHelper()
{
const UrlStorage* storage = s_urlStorage;
for (const auto &u : storage->m_urls) {
// QVERIFY/QCOMPARE trigger race conditions in helgrind
if (!u.isValid())
qFatal("invalid url");
if (u.scheme() != QLatin1String("http"))
qFatal("invalid scheme");
if (!u.toString().startsWith('h'))
qFatal("invalid toString");
QUrl copy(u);
copy.setHost("www.new-host.com");
QUrl copy2(u);
copy2.setUserName("dfaure");
QUrl copy3(u);
QUrl copy4(u);
copy4.detach();
QUrl copy5(u);
QUrl resolved1 = u.resolved(QUrl("index.html"));
Q_UNUSED(resolved1);
Q_UNUSED(resolved2);
QString local = u.toLocalFile();
Q_UNUSED(local);
QTest::qWait(10); // give time for the other threads to start
}
}
void tst_QUrl::testThreading()
{
enum { Count = 100 };
if (QTestPrivate::isRunningArmOnX86())
QSKIP("This test fails in QEMU and looks like because of a data race, QTBUG-93176");
s_urlStorage = new UrlStorage;
QThreadPool::globalInstance()->setMaxThreadCount(Count);
// Written this way because wasm need the eventloop
QList<QFuture<void>> futures;
futures.reserve(Count);
for (int i = 0; i < Count; ++i)
futures.push_back(QtConcurrent::run(&tst_QUrl::testThreadingHelper, this));
QEventLoop loop;
std::atomic<int> remaining = Count;
for (int i = 0; i < Count; ++i) {
futures[i].then([&]() {
if (!--remaining)
loop.quit();
});
}
loop.exec();
delete s_urlStorage;
}
void tst_QUrl::matches_data()
{
QTest::addColumn<QString>("urlStrOne");
QTest::addColumn<QString>("urlStrTwo");
QTest::addColumn<uint>("options");
QTest::addColumn<bool>("matches");
<< uint(QUrl::None) << true;
<< uint(QUrl::None) << false;
<< uint(QUrl::RemovePath) << true;
<< uint(QUrl::RemovePath) << false;
<< uint(QUrl::RemovePath | QUrl::RemoveAuthority)
<< true;
QTest::newRow("nonMatchingHost-removePathAuthority") << "http://user:pass@www.website.com/directory"
<< uint(QUrl::RemovePath | QUrl::RemoveAuthority)
<< true;
QTest::newRow("matchingHostAuthority-removePathAuthority")
<< uint(QUrl::RemovePath | QUrl::RemoveAuthority) << true;
QTest::newRow("nonMatchingAuthority-removePathAuthority")
<< uint(QUrl::RemovePath | QUrl::RemoveAuthority) << true;
<< uint(QUrl::RemovePort) << true;
<< uint(QUrl::RemovePort) << false;
<< uint(QUrl::RemovePassword) << true;
<< uint(QUrl::RemovePassword) << false;
QTest::newRow("matchingUserName-removePassword") << "http://user@example.com" << "http://user@example.com"
<< uint(QUrl::RemovePassword) << true;
QTest::newRow("nonMatchingUserName-removePassword") << "http://user@example.com" << "http://user2@example.com"
<< uint(QUrl::RemovePassword) << false;
}
void tst_QUrl::matches()
{
QFETCH(QString, urlStrOne);
QFETCH(QString, urlStrTwo);
QFETCH(uint, options);
QFETCH(bool, matches);
QUrl urlOne(urlStrOne);
QUrl urlTwo(urlStrTwo);
QCOMPARE(urlOne.matches(urlTwo, QUrl::FormattingOptions(options)), matches);
}
void tst_QUrl::ipv6_zoneId_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QString>("decodedHost");
QTest::addColumn<QString>("prettyHost");
QTest::addColumn<QString>("encodedHost");
QTest::newRow("subdelims") << QUrl("x://[::%25eth%2B]") << "::%eth+" << "::%25eth%2B" << "::%25eth%2B";
QTest::newRow("unicode") << QUrl("x://[::%25wlán0]") << "::%wlán0" << "::%25wlán0" << "::%25wl%C3%A1n0";
QTest::newRow("non-utf8") << QUrl("x://[::%25%80]") << QString("::%") + QChar(QChar::ReplacementCharacter) << "::%25%80" << "::%25%80";
}
void tst_QUrl::ipv6_zoneId()
{
QFETCH(QUrl, url);
QFETCH(QString, decodedHost);
QFETCH(QString, prettyHost);
QFETCH(QString, encodedHost);
QVERIFY2(url.isValid(), qPrintable(url.errorString()));
QCOMPARE(url.host(QUrl::FullyDecoded), decodedHost);
QCOMPARE(url.host(), decodedHost);
QCOMPARE(url.host(QUrl::FullyEncoded), encodedHost);
QCOMPARE(url.toString(), "x://[" + prettyHost + "]");
QCOMPARE(url.toString(QUrl::FullyEncoded), "x://[" + encodedHost + "]");
}
void tst_QUrl::normalizeRemotePaths_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QString>("expected");
QTest::addColumn<QString>("expectedNoFilename");
// An encoded slash (%2F) is not a path separator: it is part of the segment
// it appears in and must survive normalization untouched.
QTest::newRow("encoded-slash-in-both") << QUrl("http://qt-project.org/pre%2Fvious/path/../file%2Fname")
}
void tst_QUrl::normalizeRemotePaths()
{
QFETCH(QUrl, url);
QFETCH(QString, expected);
QFETCH(QString, expectedNoFilename);
QCOMPARE(url.adjusted(QUrl::NormalizePathSegments).toString(), expected);
QCOMPARE(url.adjusted(QUrl::NormalizePathSegments | QUrl::RemoveFilename).toString(),
expectedNoFilename);
QCOMPARE(url.resolved(QUrl(".")).toString(), expectedNoFilename);
}
QTEST_MAIN(tst_QUrl)
#include "tst_qurl.moc"