diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index a37a3f9b0bf..348386e550c 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -210,6 +210,18 @@ bool FilePath::isNewerThan(const QDateTime &timeStamp) const return false; } +Qt::CaseSensitivity FilePath::caseSensitivity() const +{ + if (m_scheme.isEmpty()) + return HostOsInfo::fileNameCaseSensitivity(); + + // FIXME: This could or possibly should the target device's file name case sensitivity + // into account by diverting to IDevice. However, as this is expensive and we are + // in time-critical path here, we go with "good enough" for now: + // The first approximation is "Anything unusual is not case sensitive" + return Qt::CaseSensitive; +} + /*! Recursively resolves symlinks if \a filePath is a symlink. To resolve symlinks anywhere in the path, see canonicalPath. @@ -994,12 +1006,9 @@ QDir FilePath::toDir() const bool FilePath::operator==(const FilePath &other) const { - if (m_scheme.isEmpty()) - return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) == 0; - - // FIXME: This should take the host's file name case sensitivity into account. - // The first approximation here is "Anything unusual is not case sensitive" - return m_data == other.m_data && m_host == other.m_host && m_scheme == other.m_scheme; + return QString::compare(m_data, other.m_data, caseSensitivity()) == 0 + && m_host == other.m_host + && m_scheme == other.m_scheme; } bool FilePath::operator!=(const FilePath &other) const @@ -1009,13 +1018,9 @@ bool FilePath::operator!=(const FilePath &other) const bool FilePath::operator<(const FilePath &other) const { - if (m_scheme.isEmpty()) - return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) < 0; - - // FIXME: This should take the host's file name case sensitivity into account. - // The first approximation here is "Anything unusual is not case sensitive" - if (m_data != other.m_data) - return m_data < other.m_data; + const int cmp = QString::compare(m_data, other.m_data, caseSensitivity()); + if (cmp != 0) + return cmp < 0; if (m_host != other.m_host) return m_host < other.m_host; return m_scheme < other.m_scheme; @@ -1046,7 +1051,7 @@ bool FilePath::isChildOf(const FilePath &s) const { if (s.isEmpty()) return false; - if (!m_data.startsWith(s.m_data, HostOsInfo::fileNameCaseSensitivity())) + if (!m_data.startsWith(s.m_data, caseSensitivity())) return false; if (m_data.size() <= s.m_data.size()) return false; @@ -1066,13 +1071,13 @@ bool FilePath::isChildOf(const QDir &dir) const /// \returns whether FilePath startsWith \a s bool FilePath::startsWith(const QString &s) const { - return m_data.startsWith(s, HostOsInfo::fileNameCaseSensitivity()); + return m_data.startsWith(s, caseSensitivity()); } /// \returns whether FilePath endsWith \a s bool FilePath::endsWith(const QString &s) const { - return m_data.endsWith(s, HostOsInfo::fileNameCaseSensitivity()); + return m_data.endsWith(s, caseSensitivity()); } bool FilePath::isDir() const @@ -1307,7 +1312,7 @@ void withNtfsPermissions(const std::function &task) std::hash::result_type std::hash::operator()(const std::hash::argument_type &fn) const { - if (Utils::HostOsInfo::fileNameCaseSensitivity() == Qt::CaseInsensitive) + if (fn.caseSensitivity() == Qt::CaseInsensitive) return hash()(fn.toString().toUpper().toStdString()); return hash()(fn.toString().toStdString()); } diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 8da4f64fdcb..67b40c4eff0 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -146,6 +146,8 @@ public: bool isDir() const; bool isNewerThan(const QDateTime &timeStamp) const; + Qt::CaseSensitivity caseSensitivity() const; + FilePath relativeChildPath(const FilePath &parent) const; FilePath relativePath(const FilePath &anchor) const; FilePath pathAppended(const QString &str) const; diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index 258422305fd..381f359982c 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -52,6 +52,8 @@ private slots: void relativePath(); void fromToString_data(); void fromToString(); + void comparison_data(); + void comparison(); private: QTemporaryDir tempDir; @@ -329,5 +331,38 @@ void tst_fileutils::fromToString() QCOMPARE(copy.toString(), full); } +void tst_fileutils::comparison() +{ + QFETCH(QString, left); + QFETCH(QString, right); + QFETCH(bool, hostSensitive); + QFETCH(bool, expected); + + HostOsInfo::setOverrideFileNameCaseSensitivity( + hostSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + + FilePath l = FilePath::fromString(left); + FilePath r = FilePath::fromString(right); + QCOMPARE(l == r, expected); +} + +void tst_fileutils::comparison_data() +{ + QTest::addColumn("left"); + QTest::addColumn("right"); + QTest::addColumn("hostSensitive"); + QTest::addColumn("expected"); + + QTest::newRow("r1") << "Abc" << "abc" << true << false; + QTest::newRow("r2") << "Abc" << "abc" << false << true; + QTest::newRow("r3") << "x://y/Abc" << "x://y/abc" << true << false; + QTest::newRow("r4") << "x://y/Abc" << "x://y/abc" << false << false; + + QTest::newRow("s1") << "abc" << "abc" << true << true; + QTest::newRow("s2") << "abc" << "abc" << false << true; + QTest::newRow("s3") << "x://y/abc" << "x://y/abc" << true << true; + QTest::newRow("s4") << "x://y/abc" << "x://y/abc" << false << true; +} + QTEST_APPLESS_MAIN(tst_fileutils) #include "tst_fileutils.moc"