File indexing completed on 2024-05-19 04:39:59
0001 /* 0002 SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de> 0003 SPDX-FileCopyrightText: 2015 Kevin Funk <kfunk@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "test_path.h" 0009 0010 #include <util/path.h> 0011 0012 #include <KIO/Global> 0013 0014 #include <QTest> 0015 #include <QStandardPaths> 0016 0017 #include <type_traits> 0018 #include <utility> 0019 0020 QTEST_MAIN(TestPath) 0021 0022 using namespace KDevelop; 0023 0024 static_assert(std::is_nothrow_move_assignable<Path>(), "Why would a move assignment operator throw?"); 0025 static_assert(std::is_nothrow_move_constructible<Path>(), "Why would a move constructor throw?"); 0026 0027 static const int FILES_PER_FOLDER = 10; 0028 static const int FOLDERS_PER_FOLDER = 5; 0029 static const int TREE_DEPTH = 5; 0030 0031 template<typename T> 0032 T stringToUrl(const QString& path) 0033 { 0034 return T(path); 0035 } 0036 0037 template<> 0038 QStringList stringToUrl(const QString& path) 0039 { 0040 return path.split('/'); 0041 } 0042 0043 template<typename T> 0044 T childUrl(const T& parent, const QString& child) 0045 { 0046 return T(parent, child); 0047 } 0048 0049 template<> 0050 QStringList childUrl(const QStringList& parent, const QString& child) 0051 { 0052 QStringList ret = parent; 0053 ret << child; 0054 return ret; 0055 } 0056 0057 template<> 0058 QUrl childUrl(const QUrl& parent, const QString& child) 0059 { 0060 QUrl ret = parent; 0061 ret.setPath(ret.path() + '/' + child); 0062 return ret; 0063 } 0064 0065 template<typename T> 0066 QVector<T> generateData(const T& parent, int level) 0067 { 0068 QVector<T> ret; 0069 // files per folder 0070 for (int i = 0; i < FILES_PER_FOLDER; ++i) { 0071 const QString fileName = QStringLiteral("file%1.txt").arg(i); 0072 const T file = childUrl<T>(parent, fileName); 0073 Q_ASSERT(!ret.contains(file)); 0074 ret << file; 0075 } 0076 0077 // nesting depth 0078 if (level < TREE_DEPTH) { 0079 // folders per folder 0080 for (int i = 0; i < FOLDERS_PER_FOLDER; ++i) { 0081 const QString folderName = QStringLiteral("folder%1").arg(i); 0082 const T folder = childUrl<T>(parent, folderName); 0083 Q_ASSERT(!ret.contains(folder)); 0084 ret << folder; 0085 ret += generateData<T>(folder, level + 1); 0086 } 0087 } 0088 return ret; 0089 } 0090 0091 template<typename T> 0092 void runBenchmark() 0093 { 0094 QBENCHMARK { 0095 const T base = stringToUrl<T>("/tmp/foo/bar"); 0096 generateData(base, 0); 0097 } 0098 } 0099 0100 void TestPath::initTestCase() 0101 { 0102 QStandardPaths::setTestModeEnabled(true); 0103 } 0104 0105 void TestPath::bench_qurl() 0106 { 0107 runBenchmark<QUrl>(); 0108 } 0109 0110 void TestPath::bench_qstringlist() 0111 { 0112 runBenchmark<QStringList>(); 0113 } 0114 0115 void TestPath::bench_path() 0116 { 0117 runBenchmark<Path>(); 0118 } 0119 0120 void TestPath::bench_fromLocalPath() 0121 { 0122 QFETCH(int, variant); 0123 0124 const QString input(QStringLiteral("/foo/bar/asdf/bla/blub.h")); 0125 const int repeat = 1000; 0126 if (variant == 1) { 0127 QBENCHMARK { 0128 for (int i = 0; i < repeat; ++i) { 0129 Path path = Path(QUrl::fromLocalFile(input)); 0130 Q_UNUSED(path); 0131 } 0132 } 0133 } else if (variant == 2) { 0134 QBENCHMARK { 0135 for (int i = 0; i < repeat; ++i) { 0136 Path path = Path(input); 0137 Q_UNUSED(path); 0138 } 0139 } 0140 } else { 0141 QFAIL("unexpected variant"); 0142 } 0143 } 0144 0145 void TestPath::bench_fromLocalPath_data() 0146 { 0147 QTest::addColumn<int>("variant"); 0148 0149 QTest::newRow("QUrl::fromLocalFile") << 1; 0150 QTest::newRow("QString") << 2; 0151 } 0152 0153 void TestPath::bench_swap() 0154 { 0155 Path a(QStringLiteral("/foo/a.cpp")); 0156 Path b(QStringLiteral("/bar/b.cc")); 0157 QBENCHMARK { 0158 using std::swap; 0159 swap(a, b); 0160 } 0161 } 0162 0163 void TestPath::bench_hash() 0164 { 0165 const Path path(QStringLiteral("/my/very/long/path/to/a/file.cpp")); 0166 QBENCHMARK { 0167 auto hash = qHash(path); 0168 Q_UNUSED(hash); 0169 } 0170 } 0171 0172 /// Invoke @p op on URL @p base, but preserve drive letter if @p op removes it 0173 template<typename Func> 0174 QUrl preserveWindowsDriveLetter(const QUrl& base, Func op) 0175 { 0176 #ifndef Q_OS_WIN 0177 return op(base); 0178 #else 0179 // only apply to local files 0180 if (!base.isLocalFile()) { 0181 return op(base); 0182 } 0183 0184 // save drive letter 0185 const QString windowsDriveLetter = base.toLocalFile().mid(0, 2); 0186 QUrl url = op(base); 0187 // restore drive letter 0188 if (url.toLocalFile().startsWith('/')) { 0189 url = QUrl::fromLocalFile(windowsDriveLetter + url.toLocalFile()); 0190 } 0191 return url; 0192 #endif 0193 } 0194 0195 QUrl resolvedUrl(const QUrl& base, const QUrl& relative) 0196 { 0197 return preserveWindowsDriveLetter(base, [&](const QUrl& url) { 0198 return url.resolved(relative); 0199 }); 0200 } 0201 0202 QUrl comparableUpUrl(const QUrl& url) 0203 { 0204 QUrl ret = preserveWindowsDriveLetter(url, [&](const QUrl& url) { 0205 return KIO::upUrl(url).adjusted(QUrl::RemovePassword); 0206 }); 0207 return ret.adjusted(QUrl::StripTrailingSlash); 0208 } 0209 0210 void TestPath::testPath() 0211 { 0212 QFETCH(QString, input); 0213 0214 QUrl url = QUrl::fromUserInput(input); 0215 url = url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments); 0216 0217 Path optUrl(input); 0218 0219 if (!url.password().isEmpty()) { 0220 QUrl urlNoPass = url.adjusted(QUrl::RemovePassword); 0221 QCOMPARE(optUrl.toUrl(), urlNoPass); 0222 } else { 0223 QCOMPARE(optUrl.toUrl(), url); 0224 } 0225 QCOMPARE(optUrl.isLocalFile(), url.isLocalFile()); 0226 QCOMPARE(optUrl.pathOrUrl(), toUrlOrLocalFile(url, QUrl::RemovePassword)); 0227 QCOMPARE(optUrl.isValid(), url.isValid()); 0228 QCOMPARE(optUrl.isEmpty(), url.isEmpty()); 0229 QCOMPARE(optUrl.lastPathSegment(), url.fileName()); 0230 QCOMPARE(optUrl.path(), url.isLocalFile() ? url.toLocalFile() : url.path()); 0231 QCOMPARE(optUrl.parent().toUrl(), comparableUpUrl(url)); 0232 QCOMPARE(optUrl.toLocalFile(), url.toLocalFile()); 0233 0234 QCOMPARE(optUrl, Path(input)); 0235 QCOMPARE(optUrl, Path(optUrl)); 0236 QVERIFY(optUrl != Path(input + "/asdf")); 0237 0238 if (url.isLocalFile() && !input.startsWith(QLatin1String("file://"))) { 0239 QCOMPARE(optUrl, Path(QUrl::fromLocalFile(input))); 0240 } 0241 0242 QCOMPARE(optUrl, Path(url)); 0243 0244 if (url.isValid()) { 0245 QVERIFY(optUrl.relativePath(optUrl).isEmpty()); 0246 0247 Path relativePath(optUrl, QStringLiteral("foo/bar")); 0248 QCOMPARE(optUrl.relativePath(relativePath), QLatin1String("foo/bar")); 0249 QCOMPARE(relativePath.relativePath(optUrl), QLatin1String("../../")); 0250 QVERIFY(optUrl.isParentOf(relativePath)); 0251 QVERIFY(!relativePath.isParentOf(optUrl)); 0252 0253 #ifndef Q_OS_WIN 0254 Path absolutePath(optUrl, QStringLiteral("/laa/loo")); 0255 QCOMPARE(absolutePath.path(), QLatin1String("/laa/loo")); 0256 QCOMPARE(url.resolved(QUrl(QStringLiteral("/laa/loo"))).path(), QLatin1String("/laa/loo")); 0257 0258 Path absolutePath2(optUrl, QStringLiteral("/")); 0259 QCOMPARE(absolutePath2.path(), QLatin1String("/")); 0260 QCOMPARE(url.resolved(QUrl(QStringLiteral("/"))).path(), QLatin1String("/")); 0261 #endif 0262 0263 Path unrelatedPath(QStringLiteral("https://test@blubasdf.com:12345/")); 0264 QCOMPARE(optUrl.relativePath(unrelatedPath), unrelatedPath.pathOrUrl()); 0265 QCOMPARE(unrelatedPath.relativePath(optUrl), optUrl.pathOrUrl()); 0266 QVERIFY(!unrelatedPath.isParentOf(optUrl)); 0267 QVERIFY(!optUrl.isParentOf(unrelatedPath)); 0268 } 0269 0270 QCOMPARE(Path().relativePath(optUrl), optUrl.pathOrUrl()); 0271 QVERIFY(optUrl.relativePath(Path()).isEmpty()); 0272 QVERIFY(Path().relativePath(Path()).isEmpty()); 0273 QVERIFY(!optUrl.isParentOf(Path())); 0274 QVERIFY(!Path().isParentOf(optUrl)); 0275 QVERIFY(!Path().isParentOf(Path())); 0276 QVERIFY(!optUrl.isParentOf(optUrl)); 0277 0278 QCOMPARE(optUrl.isRemote(), optUrl.isValid() && !optUrl.isLocalFile()); 0279 QCOMPARE(optUrl.isRemote(), optUrl.isValid() && !optUrl.remotePrefix().isEmpty()); 0280 0281 if (url.path() == QLatin1String("/")) { 0282 url.setPath("/test/foo/bar"); 0283 } else { 0284 url.setPath(url.path() + "/test/foo/bar"); 0285 } 0286 if (url.scheme().isEmpty()) { 0287 url.setScheme(QStringLiteral("file")); 0288 } 0289 optUrl.addPath(QStringLiteral("test/foo/bar")); 0290 QCOMPARE(optUrl.lastPathSegment(), url.fileName()); 0291 QCOMPARE(optUrl.path(), url.isLocalFile() ? url.toLocalFile() : url.path()); 0292 0293 url = url.adjusted(QUrl::RemoveFilename); 0294 url.setPath(url.path() + "lalalala_adsf.txt"); 0295 optUrl.setLastPathSegment(QStringLiteral("lalalala_adsf.txt")); 0296 QCOMPARE(optUrl.lastPathSegment(), url.fileName()); 0297 QCOMPARE(optUrl.path(), url.isLocalFile() ? url.toLocalFile() : url.path()); 0298 0299 QCOMPARE(optUrl.parent().toUrl(), comparableUpUrl(url)); 0300 0301 QVERIFY(optUrl.parent().isDirectParentOf(optUrl)); 0302 QVERIFY(!optUrl.parent().parent().isDirectParentOf(optUrl)); 0303 0304 #ifndef Q_OS_WIN 0305 Path a(QStringLiteral("/foo/bar/asdf/")); 0306 Path b(QStringLiteral("/foo/bar/")); 0307 QVERIFY(b.isDirectParentOf(a)); 0308 Path c(QStringLiteral("/foo/bar")); 0309 QVERIFY(c.isDirectParentOf(a)); 0310 #endif 0311 0312 optUrl = Path{}; 0313 url.clear(); 0314 QCOMPARE(optUrl.toUrl(), url); 0315 } 0316 0317 void TestPath::testPath_data() 0318 { 0319 QTest::addColumn<QString>("input"); 0320 0321 #ifndef Q_OS_WIN 0322 QTest::newRow("invalid") << ""; 0323 QTest::newRow("path") << "/tmp/foo/asdf.txt"; 0324 QTest::newRow("path-folder") << "/tmp/foo/asdf/"; 0325 QTest::newRow("root") << "/"; 0326 QTest::newRow("clean-path") << "/tmp/..///asdf/"; 0327 QTest::newRow("file") << "file:///tmp/foo/asdf.txt"; 0328 QTest::newRow("file-folder") << "file:///tmp/foo/bar/"; 0329 #else 0330 QTest::newRow("path") << "C:/tmp/foo/asdf.txt"; 0331 QTest::newRow("path-folder") << "C:/tmp/foo/asdf/"; 0332 QTest::newRow("root") << "C:/"; 0333 QTest::newRow("clean-path") << "C:/tmp/..///asdf/"; 0334 QTest::newRow("file") << "file:///C:/tmp/foo/asdf.txt"; 0335 QTest::newRow("file-folder") << "file:///C:/tmp/foo/bar/"; 0336 #endif 0337 QTest::newRow("remote-root") << "http://www.test.com/"; 0338 QTest::newRow("http") << "http://www.test.com/tmp/asdf.txt"; 0339 QTest::newRow("ftps") << "ftps://user@host.com/tmp/foo/asdf.txt"; 0340 QTest::newRow("password") << "ftps://user:password@host.com/tmp/asdf.txt"; 0341 QTest::newRow("port") << "http://localhost:8080/foo/bar/test.txt"; 0342 } 0343 0344 void TestPath::testPathInvalid() 0345 { 0346 QFETCH(QString, input); 0347 Path url(input); 0348 QVERIFY(!url.isValid()); 0349 QVERIFY(url.isEmpty()); 0350 } 0351 0352 void TestPath::testPathInvalid_data() 0353 { 0354 QTest::addColumn<QString>("input"); 0355 0356 QTest::newRow("empty") << ""; 0357 QTest::newRow("fragment") << "http://test.com/#hello"; 0358 QTest::newRow("query") << "http://test.com/?hello"; 0359 QTest::newRow("suburl") << "file:///home/weis/kde.tgz#gzip:/#tar:/kdebase"; 0360 QTest::newRow("relative") << "../foo/bar"; 0361 QTest::newRow("name") << "asdfasdf"; 0362 QTest::newRow("remote-nopath") << "http://www.test.com"; 0363 } 0364 0365 void TestPath::testPathComparison() 0366 { 0367 QFETCH(Path, left); 0368 QFETCH(Path, right); 0369 QFETCH(int, leftCompareRight); 0370 QFETCH(int, leftCompareRightCi); 0371 0372 const bool equal = leftCompareRight == 0; 0373 const bool less = leftCompareRight < 0; 0374 const bool greater = leftCompareRight > 0; 0375 0376 QVERIFY(left == left); 0377 QVERIFY(right == right); 0378 QCOMPARE(left == right, equal); 0379 QCOMPARE(right == left, equal); 0380 0381 QVERIFY(!(left != left)); 0382 QVERIFY(!(right != right)); 0383 QCOMPARE(left != right, !equal); 0384 QCOMPARE(right != left, !equal); 0385 0386 QCOMPARE(left < right, less); 0387 QCOMPARE(left <= right, less || equal); 0388 QCOMPARE(left > right, greater); 0389 QCOMPARE(left >= right, greater || equal); 0390 0391 QCOMPARE(right < left, greater); 0392 QCOMPARE(right <= left, greater || equal); 0393 QCOMPARE(right > left, less); 0394 QCOMPARE(right >= left, less || equal); 0395 0396 QCOMPARE(left.compare(left), 0); 0397 QCOMPARE(right.compare(right), 0); 0398 QCOMPARE(left.compare(right) < 0, leftCompareRight < 0); 0399 QCOMPARE(right.compare(left) < 0, leftCompareRight > 0); 0400 0401 QCOMPARE(left.compare(left, Qt::CaseSensitive), 0); 0402 QCOMPARE(right.compare(right, Qt::CaseSensitive), 0); 0403 QCOMPARE(left.compare(right, Qt::CaseSensitive) < 0, leftCompareRight < 0); 0404 QCOMPARE(right.compare(left, Qt::CaseSensitive) < 0, leftCompareRight > 0); 0405 0406 QCOMPARE(left.compare(left, Qt::CaseInsensitive), 0); 0407 QCOMPARE(right.compare(right, Qt::CaseInsensitive), 0); 0408 QCOMPARE(left.compare(right, Qt::CaseInsensitive) < 0, leftCompareRightCi < 0); 0409 QCOMPARE(right.compare(left, Qt::CaseInsensitive) < 0, leftCompareRightCi > 0); 0410 } 0411 0412 void TestPath::testPathComparison_data() 0413 { 0414 QTest::addColumn<Path>("left"); 0415 QTest::addColumn<Path>("right"); 0416 QTest::addColumn<int>("leftCompareRight"); 0417 QTest::addColumn<int>("leftCompareRightCi"); 0418 0419 Path a(QStringLiteral("/tmp/a")); 0420 Path b(QStringLiteral("/tmp/b")); 0421 Path c(QStringLiteral("/tmp/ac")); 0422 Path d(QStringLiteral("/d")); 0423 Path e(QStringLiteral("/tmp")); 0424 Path f(QStringLiteral("/tmp/")); 0425 Path invalid; 0426 0427 QTest::newRow("a-b") << a << b << -1 << -1; 0428 QTest::newRow("a-copy") << a << Path(a) << 0 << 0; 0429 QTest::newRow("c-a") << c << a << 1 << 1; 0430 QTest::newRow("c-invalid") << c << invalid << 1 << 1; 0431 QTest::newRow("c-d") << c << d << 1 << 1; 0432 QTest::newRow("e-f") << e << f << 0 << 0; 0433 0434 Path A(QStringLiteral("/tmp/A")); 0435 Path B(QStringLiteral("/tmp/B")); 0436 Path C(QStringLiteral("/tmp/aC")); 0437 Path D(QStringLiteral("/D")); 0438 Path E(QStringLiteral("/TMP")); 0439 Path F(QStringLiteral("/TmP/F")); 0440 0441 QTest::newRow("a-A") << a << A << 1 << 0; 0442 QTest::newRow("a-B") << a << B << 1 << -1; 0443 QTest::newRow("A-b") << A << b << -1 << -1; 0444 QTest::newRow("A-C") << A << C << -1 << -1; 0445 QTest::newRow("c-C") << c << C << 1 << 0; 0446 QTest::newRow("d-D") << d << D << 1 << 0; 0447 QTest::newRow("F-A") << F << A << -1 << 1; 0448 QTest::newRow("f-E") << f << E << 1 << 0; 0449 QTest::newRow("E-F") << E << F << -1 << -1; 0450 } 0451 0452 void TestPath::testPathSwap() 0453 { 0454 Path a(QStringLiteral("/foo/22/x.C")); 0455 Path b(QStringLiteral("/home/bar/at.7z")); 0456 QVERIFY(!(a == b)); 0457 0458 const auto aCopy = a; 0459 const auto bCopy = b; 0460 QCOMPARE(aCopy, a); 0461 QCOMPARE(bCopy, b); 0462 0463 using std::swap; 0464 swap(a, b); 0465 QCOMPARE(a, bCopy); 0466 QCOMPARE(b, aCopy); 0467 } 0468 0469 void TestPath::testPathAddData() 0470 { 0471 QFETCH(QString, pathToAdd); 0472 0473 const QStringList bases = { 0474 #ifndef Q_OS_WIN 0475 QStringLiteral("/"), 0476 QStringLiteral("/foo/bar/asdf/"), 0477 QStringLiteral("file:///foo/bar/asdf/"), 0478 #else 0479 "C:/", 0480 "C:/foo/bar/asdf/", 0481 "file:///C:/foo/bar/asdf/", 0482 #endif 0483 QStringLiteral("http://www.asdf.com/foo/bar/asdf/"), 0484 }; 0485 0486 for (const QString& base : bases) { 0487 QUrl baseUrl = QUrl::fromUserInput(base); 0488 if (QDir::isRelativePath(pathToAdd)) { 0489 baseUrl = resolvedUrl(baseUrl, QUrl(pathToAdd)); 0490 } else if (QDir::isRelativePath(pathToAdd) || baseUrl.path() != QLatin1String("/")) { 0491 // if pathToAdd == /absolute && baseUrl == "/", below call would lead to an invalid QUrl 0492 // with qtbase.git/f62768d046528636789f901ac79e2cfa1843a7b7 0493 baseUrl.setPath(baseUrl.path() + pathToAdd); 0494 } else { 0495 baseUrl.setPath(pathToAdd); 0496 } 0497 0498 baseUrl = baseUrl.adjusted(QUrl::NormalizePathSegments); 0499 if (baseUrl.path().contains(QLatin1String("//"))) { 0500 // odd, this should have been normalized, no? 0501 auto path = baseUrl.path(); 0502 path.replace(QLatin1String("//"), QLatin1String("/")); 0503 baseUrl.setPath(path); 0504 } 0505 // QUrl::StripTrailingSlash converts file:/// to file: which is not what we want 0506 if (baseUrl.path() != QLatin1String("/")) { 0507 baseUrl = baseUrl.adjusted(QUrl::StripTrailingSlash); 0508 } 0509 0510 Path basePath(base); 0511 basePath.addPath(pathToAdd); 0512 0513 QCOMPARE(basePath.pathOrUrl(), toUrlOrLocalFile(baseUrl)); 0514 QCOMPARE(basePath.toUrl(), baseUrl); 0515 } 0516 } 0517 0518 void TestPath::testPathAddData_data() 0519 { 0520 QTest::addColumn<QString>("pathToAdd"); 0521 0522 const QStringList paths = QStringList() 0523 << QStringLiteral("file.txt") 0524 << QStringLiteral("path/file.txt") 0525 << QStringLiteral("path//file.txt") 0526 << QStringLiteral("/absolute") 0527 << QStringLiteral("../") 0528 << QStringLiteral("..") 0529 << QStringLiteral("../../../") 0530 << QStringLiteral("./foo") 0531 << QStringLiteral("../relative") 0532 << QStringLiteral("../../relative") 0533 << QStringLiteral("../foo/../bar") 0534 << QStringLiteral("../foo/./bar") 0535 << QStringLiteral("../../../../../../../invalid"); 0536 for (const QString& path : paths) { 0537 QTest::addRow("%s", qPrintable(path)) << path; 0538 } 0539 } 0540 0541 void TestPath::testPathBaseCtor() 0542 { 0543 QFETCH(QString, base); 0544 QFETCH(QString, subPath); 0545 QFETCH(QString, expected); 0546 0547 const Path basePath(base); 0548 0549 const Path path(basePath, subPath); 0550 0551 QCOMPARE(path.pathOrUrl(), expected); 0552 } 0553 0554 void TestPath::testPathBaseCtor_data() 0555 { 0556 QTest::addColumn<QString>("base"); 0557 QTest::addColumn<QString>("subPath"); 0558 QTest::addColumn<QString>("expected"); 0559 0560 QTest::newRow("empty") << "" << "" << ""; 0561 QTest::newRow("empty-relative") << "" << "foo" << ""; 0562 #ifndef Q_OS_WIN 0563 QTest::newRow("root-empty") << "/" << "" << "/"; 0564 QTest::newRow("root-root") << "/" << "/" << "/"; 0565 QTest::newRow("root-relative") << "/" << "bar" << "/bar"; 0566 QTest::newRow("root-relative-dirty") << "/" << "bar//foo/a/.." << "/bar/foo"; 0567 QTest::newRow("path-relative") << "/foo/bar" << "bar/foo" << "/foo/bar/bar/foo"; 0568 QTest::newRow("path-absolute") << "/foo/bar" << "/bar/foo" << "/bar/foo"; 0569 #else 0570 QTest::newRow("root-empty") << "C:/" << "" << "C:"; 0571 QTest::newRow("root1-empty") << "C:" << "" << "C:"; 0572 QTest::newRow("root-root") << "C:/" << "C:/" << "C:"; 0573 QTest::newRow("root-relative") << "C:/" << "bar" << "C:/bar"; 0574 QTest::newRow("root1-relative") << "C:" << "bar" << "C:/bar"; 0575 QTest::newRow("root-relative-dirty") << "C:/" << "bar//foo/a/.." << "C:/bar/foo"; 0576 QTest::newRow("path-relative") << "C:/foo/bar" << "bar/foo" << "C:/foo/bar/bar/foo"; 0577 QTest::newRow("path-absolute") << "C:/foo/bar" << "C:/bar/foo" << "C:/bar/foo"; 0578 #endif 0579 QTest::newRow("remote-path-absolute") << "http://foo.com/foo/bar" << "/bar/foo" << "http://foo.com/bar/foo"; 0580 QTest::newRow("remote-path-relative") << "http://foo.com/foo/bar" << "bar/foo" << "http://foo.com/foo/bar/bar/foo"; 0581 } 0582 0583 // there is no cd() in QUrl, emulate what KUrl did 0584 static bool cdQUrl(QUrl& url, const QString& path) 0585 { 0586 if (path.isEmpty() || !url.isValid()) { 0587 return false; 0588 } 0589 // have to append slash otherwise last segment is treated as a file name and not a directory 0590 if (!url.path().endsWith('/')) { 0591 url.setPath(url.path() + '/'); 0592 } 0593 url = url.resolved(QUrl(path)).adjusted(QUrl::RemoveFragment | QUrl::RemoveQuery); 0594 return true; 0595 } 0596 0597 void TestPath::testPathCd() 0598 { 0599 QFETCH(QString, base); 0600 QFETCH(QString, change); 0601 0602 Path path = base.isEmpty() ? Path() : Path(base); 0603 QUrl url = QUrl::fromUserInput(base); 0604 0605 Path changed = path.cd(change); 0606 if (cdQUrl(url, change)) { 0607 QVERIFY(changed.isValid()); 0608 } 0609 url = url.adjusted(QUrl::NormalizePathSegments); 0610 0611 QCOMPARE(changed.pathOrUrl(), toUrlOrLocalFile(url, QUrl::StripTrailingSlash)); 0612 } 0613 0614 void TestPath::testPathCd_data() 0615 { 0616 QTest::addColumn<QString>("base"); 0617 QTest::addColumn<QString>("change"); 0618 0619 const QVector<QString> bases{ 0620 QString(), 0621 #ifndef Q_OS_WIN 0622 QStringLiteral("/foo"), QStringLiteral("/foo/bar/asdf"), 0623 #else 0624 "C:/foo", "C:/foo/bar/asdf", 0625 #endif 0626 QStringLiteral("http://foo.com/"), QStringLiteral("http://foo.com/foo"), QStringLiteral( 0627 "http://foo.com/foo/bar/asdf") 0628 }; 0629 0630 const QVector<QString> suffixes { 0631 QString(), 0632 QStringLiteral(".."), 0633 QStringLiteral("../"), 0634 QStringLiteral("../foo"), 0635 QStringLiteral("."), 0636 QStringLiteral("./"), 0637 QStringLiteral("./foo"), 0638 QStringLiteral("./foo/bar"), 0639 QStringLiteral("./foo/.."), 0640 QStringLiteral("./foo/"), 0641 QStringLiteral("./foo/../bar"), 0642 }; 0643 0644 const QVector<QString> extraSuffixes { 0645 QStringLiteral("/foo"), 0646 QStringLiteral("/foo/../bar"), 0647 }; 0648 0649 for (const QString& base : bases) { 0650 for (const auto& suffix : suffixes) { 0651 QTest::addRow("%s-%s", qPrintable(base), qPrintable(suffix)) << base << suffix; 0652 } 0653 #ifdef Q_OS_WIN 0654 // only add next rows for remote URLs on Windows 0655 if (base.startsWith("C:/")) { 0656 continue; 0657 } 0658 #endif 0659 for (const auto& suffix : extraSuffixes) { 0660 QTest::addRow("%s-%s", qPrintable(base), qPrintable(suffix)) << base << suffix; 0661 } 0662 } 0663 } 0664 0665 void TestPath::testHasParent_data() 0666 { 0667 QTest::addColumn<QString>("input"); 0668 QTest::addColumn<bool>("hasParent"); 0669 0670 QTest::newRow("empty") << QString() << false; 0671 #ifdef Q_OS_WIN 0672 QTest::newRow("\\") << QStringLiteral("\\") << true; // true b/c parent could be e.g. 'C:' 0673 #else 0674 QTest::newRow("/") << QStringLiteral("/") << false; 0675 QTest::newRow("/foo") << QStringLiteral("/foo") << true; 0676 QTest::newRow("/foo/bar") << QStringLiteral("/foo/bar") << true; 0677 QTest::newRow("//foo/bar") << QStringLiteral("//foo/bar") << true; 0678 #endif 0679 QTest::newRow("http://foo.bar") << QStringLiteral("http://foo.bar") << false; 0680 QTest::newRow("http://foo.bar/") << QStringLiteral("http://foo.bar/") << false; 0681 QTest::newRow("http://foo.bar/asdf") << QStringLiteral("http://foo.bar/asdf") << true; 0682 QTest::newRow("http://foo.bar/asdf/asdf") << QStringLiteral("http://foo.bar/asdf/asdf") << true; 0683 } 0684 0685 void TestPath::testHasParent() 0686 { 0687 QFETCH(QString, input); 0688 Path path(input); 0689 QTEST(path.hasParent(), "hasParent"); 0690 } 0691 0692 void TestPath::QUrl_acceptance() 0693 { 0694 const QUrl baseLocal = QUrl(QStringLiteral("file:///foo.h")); 0695 QCOMPARE(baseLocal.isValid(), true); 0696 QCOMPARE(baseLocal.isRelative(), false); 0697 QCOMPARE(baseLocal, QUrl::fromLocalFile(QStringLiteral("/foo.h"))); 0698 QCOMPARE(baseLocal, QUrl::fromUserInput(QStringLiteral("/foo.h"))); 0699 0700 QUrl relative(QStringLiteral("bar.h")); 0701 QCOMPARE(relative.isRelative(), true); 0702 QCOMPARE(baseLocal.resolved(relative), QUrl(QStringLiteral("file:///bar.h"))); 0703 QUrl relative2(QStringLiteral("/foo/bar.h")); 0704 QCOMPARE(relative2.isRelative(), true); 0705 QCOMPARE(baseLocal.resolved(relative2), QUrl(QStringLiteral("file:///foo/bar.h"))); 0706 0707 const QUrl baseRemote = QUrl(QStringLiteral("http://foo.org/asdf/foo/asdf")); 0708 QCOMPARE(baseRemote.resolved(relative), QUrl(QStringLiteral("http://foo.org/asdf/foo/bar.h"))); 0709 } 0710 0711 #include "moc_test_path.cpp"