diff options
Diffstat (limited to 'tests/auto/corelib/io/qurluts46/tst_qurluts46.cpp')
-rw-r--r-- | tests/auto/corelib/io/qurluts46/tst_qurluts46.cpp | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/tests/auto/corelib/io/qurluts46/tst_qurluts46.cpp b/tests/auto/corelib/io/qurluts46/tst_qurluts46.cpp new file mode 100644 index 0000000000..d163ed19bf --- /dev/null +++ b/tests/auto/corelib/io/qurluts46/tst_qurluts46.cpp @@ -0,0 +1,154 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/QUrl> +#include <QtCore/QFile> +#include <QTest> +#include <QSet> +#include <QByteArray> +#include <algorithm> + +class tst_QUrlUts46 : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void idnaTestV2_data(); + void idnaTestV2(); + +private: + // All error codes in UTR #46 revision 31 (Unicode 15.1): + // A4_1, A4_2, + // B1, B2, B3, B4, B5, B6, + // C1, C2, + // P4, + // V1, V2, V3, V5, V6, + // X4_2 + // + // NOTE: moving this inside idnaTestV2_data() results in ICE with MSVC 2019 + static const QSet<QByteArray> fatalErrors; +}; + +const QSet<QByteArray> tst_QUrlUts46::fatalErrors = { + "A4_2", // Empty ASCII label +}; + +/** + * Replace \uXXXX escapes in test case fields. + */ +static QString unescapeField(const QString &field) +{ + static const QRegularExpression re(R"(\\u([[:xdigit:]]{4}))"); + + QString result; + qsizetype lastIdx = 0; + + for (const auto &match : re.globalMatch(field)) { + // Add stuff before the match + result.append(field.mid(lastIdx, match.capturedStart() - lastIdx)); + bool ok = false; + auto c = match.captured(1).toUInt(&ok, 16); + if (!ok) { + qFatal("Failed to parse a Unicode escape: %s", qPrintable(match.captured(1))); + } + + result.append(QChar(c)); + lastIdx = match.capturedEnd(); + } + + // Append the unescaped end + result.append(field.mid(lastIdx)); + + return result; +} + +void tst_QUrlUts46::idnaTestV2_data() +{ + QTest::addColumn<QString>("source"); + QTest::addColumn<QString>("toUnicode"); + QTest::addColumn<bool>("toUnicodeOk"); + QTest::addColumn<QString>("toAsciiN"); + QTest::addColumn<bool>("toAsciiNOk"); + QTest::addColumn<QString>("toAsciiT"); + QTest::addColumn<bool>("toAsciiTOk"); + + QFile dataFile(QFINDTESTDATA("testdata/IdnaTestV2.txt")); + qDebug() << "Data file:" << dataFile.fileName(); + QVERIFY(dataFile.open(QFile::ReadOnly)); + + auto isToAsciiOk = [](const QByteArray &s, bool ifEmpty) { + if (s.isEmpty()) + return ifEmpty; + + Q_ASSERT(s.startsWith('[') && s.endsWith(']')); + + const auto errors = s.sliced(1, s.size() - 2).split(','); + // NOTE: empty string is not in fatalErrors and it's ok + return std::all_of(errors.begin(), errors.end(), + [](auto &e) { return !fatalErrors.contains(e.trimmed()); }); + }; + + for (unsigned int lineNo = 1; !dataFile.atEnd(); lineNo++) { + auto line = dataFile.readLine().trimmed(); + + int commentIdx = line.indexOf('#'); + if (commentIdx != -1) + line = line.left(commentIdx).trimmed(); + if (line.isEmpty()) + continue; + + auto fields = line.split(';'); + Q_ASSERT(fields.size() == 7); + + for (auto &field : fields) + field = unescapeField(field.trimmed()).toUtf8(); + + const QString &source = fields[0]; + QString toUnicode = fields[1].isEmpty() ? source : fields[1]; + bool toUnicodeOk = fields[2].isEmpty(); + bool toUnicodeOkForAscii = isToAsciiOk(fields[2], true); + QString toAsciiN = fields[3].isEmpty() ? toUnicode : fields[3]; + bool toAsciiNOk = isToAsciiOk(fields[4], toUnicodeOkForAscii); + QString toAsciiT = fields[5].isEmpty() ? toAsciiN : fields[5]; + bool toAsciiTOk = isToAsciiOk(fields[6], toAsciiNOk); + + QTest::addRow("line %u", lineNo) << source << toUnicode << toUnicodeOk << toAsciiN + << toAsciiNOk << toAsciiT << toAsciiTOk; + } +} + +void tst_QUrlUts46::idnaTestV2() +{ + QFETCH(QString, source); + QFETCH(QString, toUnicode); + QFETCH(bool, toUnicodeOk); + QFETCH(QString, toAsciiN); + QFETCH(bool, toAsciiNOk); + QFETCH(QString, toAsciiT); + QFETCH(bool, toAsciiTOk); + + QString toAceN = QUrl::toAce(source); + if (toUnicodeOk && toAsciiNOk) + QCOMPARE(toAceN, toAsciiN); + else if (toAsciiNOk) + QVERIFY(toAceN.isEmpty() || toAceN == toAsciiN); + else + QCOMPARE(toAceN, QString()); + + QString toAceT = QUrl::toAce(source, QUrl::AceTransitionalProcessing); + if (toUnicodeOk && toAsciiTOk) + QCOMPARE(toAceT, toAsciiT); + else if (toAsciiTOk) + QVERIFY(toAceT.isEmpty() || toAceT == toAsciiT); + else + QCOMPARE(toAceT, QString()); + + QString normalized = QUrl::fromAce(toAceN.toUtf8(), QUrl::IgnoreIDNWhitelist); + if (toUnicodeOk && !toAceN.isEmpty()) + QCOMPARE(normalized, toUnicode); + else + QCOMPARE(normalized, toAceN); +} + +QTEST_APPLESS_MAIN(tst_QUrlUts46) + +#include "tst_qurluts46.moc" |