/* * This file is part of KDevelop * Copyright 2012 Milian Wolff * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_path.h" #include #include #include #include #include QTEST_MAIN(TestPath); using namespace KDevelop; static const int FILES_PER_FOLDER = 10; static const int FOLDERS_PER_FOLDER = 5; static const int TREE_DEPTH = 5; namespace QTest { template<> char *toString(const KUrl &url) { return qstrdup(qPrintable(url.pathOrUrl())); } } template T stringToUrl(const QString& path) { return T(path); } template<> QStringList stringToUrl(const QString& path) { return path.split('/'); } template T childUrl(const T& parent, const QString& child) { return T(parent, child); } template<> QStringList childUrl(const QStringList& parent, const QString& child) { QStringList ret = parent; ret << child; return ret; } template<> QUrl childUrl(const QUrl& parent, const QString& child) { QUrl ret = parent; ret.setPath(ret.path() + '/' + child); return ret; } template<> KUrl childUrl(const KUrl& parent, const QString& child) { KUrl ret = parent; ret.addPath(child); return ret; } template QVector generateData(const T& parent, int level) { QVector ret; // files per folder for (int i = 0; i < FILES_PER_FOLDER; ++i) { const QString fileName = QString("file%1.txt").arg(i); const T file = childUrl(parent, fileName); Q_ASSERT(!ret.contains(file)); ret << file; } // nesting depth if (level < TREE_DEPTH) { // folders per folder for (int i = 0; i < FOLDERS_PER_FOLDER; ++i) { const QString folderName = QString("folder%1").arg(i); const T folder = childUrl(parent, folderName); Q_ASSERT(!ret.contains(folder)); ret << folder; ret += generateData(folder, level + 1); } } return ret; } template void runBenchmark() { QBENCHMARK { const T base = stringToUrl("/tmp/foo/bar"); generateData(base, 0); } } void TestPath::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); } void TestPath::cleanupTestCase() { TestCore::shutdown(); } void TestPath::bench_kurl() { runBenchmark(); } void TestPath::bench_qurl() { runBenchmark(); } void TestPath::bench_qstringlist() { runBenchmark(); } void TestPath::bench_path() { runBenchmark(); } void TestPath::bench_fromLocalPath() { QFETCH(int, variant); const QString input("/foo/bar/asdf/bla/blub.h"); const int repeat = 1000; if (variant == 1) { QBENCHMARK { for(int i = 0; i < repeat; ++i) { Path path = Path(KUrl(input)); Q_UNUSED(path); } } } else if (variant == 2) { QBENCHMARK { for(int i = 0; i < repeat; ++i) { Path path = Path(KUrl::fromPath(input)); Q_UNUSED(path); } } } else if (variant == 3) { QBENCHMARK { for(int i = 0; i < repeat; ++i) { Path path = Path(QUrl::fromLocalFile(input)); Q_UNUSED(path); } } } else { QFAIL("unexpected variant"); } } void TestPath::bench_fromLocalPath_data() { QTest::addColumn("variant"); QTest::newRow("KUrl::KUrl") << 1; QTest::newRow("KUrl::fromPath") << 2; QTest::newRow("QUrl::fromLocalFile") << 3; } void TestPath::bench_hash() { const Path path("/my/very/long/path/to/a/file.cpp"); QBENCHMARK { auto hash = qHash(path); Q_UNUSED(hash); } } KUrl comparableUpUrl(const KUrl& url) { KUrl ret = url.upUrl(); ret.adjustPath(KUrl::RemoveTrailingSlash); if (ret.hasPass()) { ret.setPass(QString()); } return ret; } void TestPath::testPath() { QFETCH(QString, input); KUrl url(input); url.cleanPath(); url.adjustPath(KUrl::RemoveTrailingSlash); Path optUrl(input); if (url.hasPass()) { KUrl urlNoPass = url; urlNoPass.setPass(QString()); QCOMPARE(optUrl.toUrl(), urlNoPass); } else { QCOMPARE(optUrl.toUrl(), url); } QCOMPARE(optUrl.isLocalFile(), url.isLocalFile()); QCOMPARE(optUrl.pathOrUrl(), url.pathOrUrl()); QCOMPARE(optUrl.isValid(), url.isValid()); QCOMPARE(optUrl.lastPathSegment(), url.fileName()); QCOMPARE(optUrl.path(), url.path()); QCOMPARE(optUrl.parent().toUrl(), comparableUpUrl(url)); QCOMPARE(optUrl.toLocalFile(), url.toLocalFile()); QCOMPARE(optUrl, Path(input)); QCOMPARE(optUrl, Path(optUrl)); QVERIFY(optUrl != Path(input + "/asdf")); if (url.isLocalFile() && !input.startsWith("file://")) { QCOMPARE(optUrl, Path(KUrl::fromPath(input))); } QCOMPARE(optUrl, Path(url)); if (url.isValid()) { QVERIFY(optUrl.relativePath(optUrl).isEmpty()); Path relativePath(optUrl, "foo/bar"); QCOMPARE(optUrl.relativePath(relativePath), QLatin1String("foo/bar")); QCOMPARE(relativePath.relativePath(optUrl), QLatin1String("../../")); QVERIFY(optUrl.isParentOf(relativePath)); QVERIFY(!relativePath.isParentOf(optUrl)); Path absolutePath(optUrl, "/laa/loo"); QCOMPARE(absolutePath.path(), QLatin1String("/laa/loo")); QCOMPARE(KUrl(url, "/laa/loo").path(), QLatin1String("/laa/loo")); Path absolutePath2(optUrl, "/"); QCOMPARE(absolutePath2.path(), QLatin1String("/")); QCOMPARE(KUrl(url, "/").path(), QLatin1String("/")); Path unrelatedPath("https://test@blubasdf.com:12345/"); QCOMPARE(optUrl.relativePath(unrelatedPath), unrelatedPath.pathOrUrl()); QCOMPARE(unrelatedPath.relativePath(optUrl), optUrl.pathOrUrl()); QVERIFY(!unrelatedPath.isParentOf(optUrl)); QVERIFY(!optUrl.isParentOf(unrelatedPath)); } QCOMPARE(Path().relativePath(optUrl), optUrl.pathOrUrl()); QVERIFY(optUrl.relativePath(Path()).isEmpty()); QVERIFY(Path().relativePath(Path()).isEmpty()); QVERIFY(!optUrl.isParentOf(Path())); QVERIFY(!Path().isParentOf(optUrl)); QVERIFY(!Path().isParentOf(Path())); QVERIFY(!optUrl.isParentOf(optUrl)); QCOMPARE(optUrl.isRemote(), optUrl.isValid() && !optUrl.isLocalFile()); QCOMPARE(optUrl.isRemote(), optUrl.isValid() && !optUrl.remotePrefix().isEmpty()); url.addPath("test/foo/bar"); optUrl.addPath("test/foo/bar"); QCOMPARE(optUrl.lastPathSegment(), url.fileName()); QCOMPARE(optUrl.path(), url.path()); url.setFileName("lalalala_adsf.txt"); optUrl.setLastPathSegment("lalalala_adsf.txt"); QCOMPARE(optUrl.lastPathSegment(), url.fileName()); QCOMPARE(optUrl.path(), url.path()); QCOMPARE(optUrl.parent().toUrl(), comparableUpUrl(url)); QVERIFY(optUrl.parent().isDirectParentOf(optUrl)); QVERIFY(!optUrl.parent().parent().isDirectParentOf(optUrl)); Path a("/foo/bar/asdf/"); Path b("/foo/bar/"); QVERIFY(b.isDirectParentOf(a)); Path c("/foo/bar"); QVERIFY(c.isDirectParentOf(a)); optUrl.clear(); url.clear(); QCOMPARE(optUrl.toUrl(), url); } void TestPath::testPath_data() { QTest::addColumn("input"); QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"\""); QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"\""); QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"\""); QTest::newRow("invalid") << ""; QTest::newRow("path") << "/tmp/foo/asdf.txt"; QTest::newRow("path-folder") << "/tmp/foo/asdf/"; QTest::newRow("root") << "/"; QTest::newRow("clean-path") << "/tmp/..///asdf/"; QTest::newRow("remote-root") << "http://www.test.com/"; QTest::newRow("http") << "http://www.test.com/tmp/asdf.txt"; QTest::newRow("file") << "file:///tmp/foo/asdf.txt"; QTest::newRow("file-folder") << "file:///tmp/foo/bar/"; QTest::newRow("ftps") << "ftps://user@host.com/tmp/foo/asdf.txt"; QTest::newRow("password") << "ftps://user:password@host.com/tmp/asdf.txt"; QTest::newRow("port") << "http://localhost:8080/foo/bar/test.txt"; } void TestPath::testPathInvalid() { QFETCH(QString, input); Path url(input); QVERIFY(!url.isValid()); } void TestPath::testPathInvalid_data() { QTest::addColumn("input"); QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"\""); QTest::newRow("empty") << ""; QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"http://test.com/#hello\""); QTest::newRow("fragment") << "http://test.com/#hello"; QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"http://test.com/?hello\""); QTest::newRow("query") << "http://test.com/?hello"; QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"file:///home/weis/kde.tgz#gzip:/%23tar:/kdebase\""); QTest::newRow("suburl") << "file:///home/weis/kde.tgz#gzip:/#tar:/kdebase"; QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"../foo/bar\""); QTest::newRow("relative") << "../foo/bar"; QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"asdfasdf\""); QTest::newRow("name") << "asdfasdf"; QTest::ignoreMessage(QtWarningMsg, "Path::init: invalid/unsupported Path encountered: \"http://www.test.com\""); QTest::newRow("remote-nopath") << "http://www.test.com"; } void TestPath::testPathOperators() { QFETCH(Path, left); QFETCH(Path, right); QFETCH(bool, equal); QFETCH(bool, less); bool greater = !equal && !less; QVERIFY(left == left); QVERIFY(right == right); QCOMPARE(left == right, equal); QCOMPARE(right == left, equal); QCOMPARE(left < right, less); QCOMPARE(left <= right, less || equal); QCOMPARE(left > right, greater); QCOMPARE(left >= right, greater || equal); QCOMPARE(right < left, greater); QCOMPARE(right <= left, greater || equal); QCOMPARE(right > left, less); QCOMPARE(right >= left, less || equal); } void TestPath::testPathOperators_data() { QTest::addColumn("left"); QTest::addColumn("right"); QTest::addColumn("equal"); QTest::addColumn("less"); Path a("/tmp/a"); Path b("/tmp/b"); Path c("/tmp/ac"); Path d("/d"); Path e("/tmp"); Path f("/tmp/"); Path invalid; QTest::newRow("a-b") << a << b << false << true; QTest::newRow("a-copy") << a << Path(a) << true << false; QTest::newRow("c-a") << c << a << false << false; QTest::newRow("c-invalid") << c << invalid << false << false; QTest::newRow("c-d") << c << d << false << false; QTest::newRow("e-f") << e << f << true << false; } void TestPath::testPathAddData() { QFETCH(QString, pathToAdd); const QStringList bases = QStringList() << "/foo/bar/asdf/" << "file:///foo/bar/asdf/" << "http://www.asdf.com/foo/bar/asdf/" << "/" ; foreach(const QString& base, bases) { KUrl baseUrl(base); baseUrl.addPath(pathToAdd); baseUrl.cleanPath(); baseUrl.adjustPath(KUrl::RemoveTrailingSlash); Path basePath(base); basePath.addPath(pathToAdd); QCOMPARE(basePath.toUrl(), baseUrl); QCOMPARE(basePath.pathOrUrl(), baseUrl.pathOrUrl()); } } void TestPath::testPathAddData_data() { QTest::addColumn("pathToAdd"); const QStringList paths = QStringList() << "file.txt" << "path/file.txt" << "path//file.txt" << "/absolute" << "../" << ".." << "../../../" << "./foo" << "../relative" << "../../relative" << "../foo/../bar" << "../foo/./bar" << "../../../../../../../invalid"; foreach(const QString &path, paths) { QTest::newRow(qstrdup(path.toUtf8().constData())) << path; } } void TestPath::testPathBaseCtor() { QFETCH(QString, base); QFETCH(QString, subPath); const Path basePath(base); const Path path(basePath, subPath); KUrl url(base); if (KUrl(subPath).isRelative()) { url.addPath(subPath); } else { url.setPath(subPath); } url.cleanPath(); QCOMPARE(path.pathOrUrl(), url.pathOrUrl(KUrl::RemoveTrailingSlash)); } void TestPath::testPathBaseCtor_data() { QTest::addColumn("base"); QTest::addColumn("subPath"); QTest::newRow("empty") << "" << ""; QTest::newRow("root-empty") << "/" << ""; QTest::newRow("root-root") << "/" << "/"; QTest::newRow("root-relative") << "/" << "bar"; QTest::newRow("root-relative-dirty") << "/" << "bar//foo/a/.."; QTest::newRow("empty-relative") << "" << "bar/foo/"; QTest::newRow("path-relative") << "/foo/bar" << "bar/foo"; QTest::newRow("path-absolute") << "/foo/bar" << "/bar/foo"; QTest::newRow("remote-path-absolute") << "http://foo.com/foo/bar" << "/bar/foo"; QTest::newRow("remote-path-relative") << "http://foo.com/foo/bar" << "bar/foo"; } void TestPath::testPathCd() { QFETCH(QString, base); QFETCH(QString, change); Path path = base.isEmpty() ? Path() : Path(base); KUrl url(base); Path changed = path.cd(change); if (url.cd(change)) { QVERIFY(changed.isValid()); } url.cleanPath(); QCOMPARE(changed.pathOrUrl(), url.pathOrUrl(KUrl::RemoveTrailingSlash)); } void TestPath::testPathCd_data() { QTest::addColumn("base"); QTest::addColumn("change"); const QVector bases{"", "/foo", "/foo/bar/asdf", "http://foo.com/", "http://foo.com/foo", "http://foo.com/foo/bar/asdf"}; foreach (const QString& base, bases) { QTest::newRow(qstrdup(qPrintable(base + "-"))) << base << ""; QTest::newRow(qstrdup(qPrintable(base + "-.."))) << base << ".."; QTest::newRow(qstrdup(qPrintable(base + "-../"))) << base << "../"; QTest::newRow(qstrdup(qPrintable(base + "v../foo"))) << base << "../foo"; QTest::newRow(qstrdup(qPrintable(base + "-."))) << base << "."; QTest::newRow(qstrdup(qPrintable(base + "-./"))) << base << "./"; QTest::newRow(qstrdup(qPrintable(base + "-./foo"))) << base << "./foo"; QTest::newRow(qstrdup(qPrintable(base + "-./foo/bar"))) << base << "./foo/bar"; QTest::newRow(qstrdup(qPrintable(base + "-foo/.."))) << base << "foo/.."; QTest::newRow(qstrdup(qPrintable(base + "-foo/"))) << base << "foo/"; QTest::newRow(qstrdup(qPrintable(base + "-foo/../bar"))) << base << "foo/../bar"; QTest::newRow(qstrdup(qPrintable(base + "-/foo"))) << base << "/foo"; QTest::newRow(qstrdup(qPrintable(base + "-/foo/../bar"))) << base << "/foo/../bar"; } } #include "moc_test_path.cpp"