| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the Qt Linguist of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | |
| 29 | #include <QtCore/QDir> |
| 30 | #include <QtCore/QDebug> |
| 31 | #include <QtCore/QFile> |
| 32 | #include <QtCore/QByteArray> |
| 33 | |
| 34 | #include <QtTest/QtTest> |
| 35 | |
| 36 | class tst_lrelease : public QObject |
| 37 | { |
| 38 | Q_OBJECT |
| 39 | |
| 40 | public: |
| 41 | tst_lrelease() |
| 42 | : lrelease(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/lrelease" ) |
| 43 | , dataDir(QFINDTESTDATA("testdata/" )) |
| 44 | {} |
| 45 | |
| 46 | private: |
| 47 | |
| 48 | private slots: |
| 49 | void translate(); |
| 50 | void compressed(); |
| 51 | void idbased(); |
| 52 | void markuntranslated(); |
| 53 | void dupes(); |
| 54 | void noTranslations(); |
| 55 | |
| 56 | private: |
| 57 | void doCompare(const QStringList &actual, const QString &expectedFn); |
| 58 | |
| 59 | QString lrelease; |
| 60 | QString dataDir; |
| 61 | }; |
| 62 | |
| 63 | void tst_lrelease::doCompare(const QStringList &actual, const QString &expectedFn) |
| 64 | { |
| 65 | QFile file(expectedFn); |
| 66 | QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); |
| 67 | QStringList expected = QString(file.readAll()).trimmed().split(sep: '\n'); |
| 68 | |
| 69 | int i = 0, ei = expected.size(), gi = actual.size(); |
| 70 | for (; ; i++) { |
| 71 | if (i == gi) { |
| 72 | if (i == ei) |
| 73 | return; |
| 74 | gi = 0; |
| 75 | break; |
| 76 | } else if (i == ei) { |
| 77 | ei = 0; |
| 78 | break; |
| 79 | } else if (!QRegExp(expected.at(i)).exactMatch(str: actual.at(i))) { |
| 80 | while ((ei - 1) >= i && (gi - 1) >= i && |
| 81 | (QRegExp(expected.at(i: ei - 1)).exactMatch(str: actual.at(i: gi - 1)))) |
| 82 | ei--, gi--; |
| 83 | break; |
| 84 | } |
| 85 | } |
| 86 | QString diff; |
| 87 | for (int j = qMax(a: 0, b: i - 3); j < i; j++) |
| 88 | diff += expected.at(i: j) + '\n'; |
| 89 | diff += "<<<<<<< got\n" ; |
| 90 | for (int j = i; j < gi; j++) { |
| 91 | diff += actual.at(i: j) + '\n'; |
| 92 | if (j >= i + 5) { |
| 93 | diff += "...\n" ; |
| 94 | break; |
| 95 | } |
| 96 | } |
| 97 | diff += "=========\n" ; |
| 98 | for (int j = i; j < ei; j++) { |
| 99 | diff += expected.at(i: j) + '\n'; |
| 100 | if (j >= i + 5) { |
| 101 | diff += "...\n" ; |
| 102 | break; |
| 103 | } |
| 104 | } |
| 105 | diff += ">>>>>>> expected\n" ; |
| 106 | for (int j = ei; j < qMin(a: ei + 3, b: expected.size()); j++) |
| 107 | diff += expected.at(i: j) + '\n'; |
| 108 | QFAIL(qPrintable("Output for " + expectedFn + " does not meet expectations:\n" + diff)); |
| 109 | } |
| 110 | |
| 111 | void tst_lrelease::translate() |
| 112 | { |
| 113 | QVERIFY(!QProcess::execute(lrelease, QStringList() << (dataDir + "translate.ts" ))); |
| 114 | |
| 115 | QTranslator translator; |
| 116 | QVERIFY(translator.load(dataDir + "translate.qm" )); |
| 117 | qApp->installTranslator(messageFile: &translator); |
| 118 | |
| 119 | QCOMPARE(QObject::tr("\nnewline at the start" ), QString("\nNEWLINE AT THE START" )); |
| 120 | QCOMPARE(QObject::tr("newline at the end\n" ), QString("NEWLINE AT THE END\n" )); |
| 121 | QCOMPARE(QObject::tr("newline and space at the end\n " ), QString("NEWLINE AND SPACE AT THE END\n " )); |
| 122 | QCOMPARE(QObject::tr("space and newline at the end \n" ), QString("SPACE AND NEWLINE AT THE END \n" )); |
| 123 | QCOMPARE(QObject::tr("\ttab at the start and newline at the end\n" ), QString("\tTAB AT THE START AND NEWLINE AT THE END\n" )); |
| 124 | QCOMPARE(QObject::tr("\n\tnewline and tab at the start" ), QString("\n\tNEWLINE AND TAB AT THE START" )); |
| 125 | QCOMPARE(QObject::tr(" \tspace and tab at the start" ), QString(" \tSPACE AND TAB AT THE START" )); |
| 126 | QCOMPARE(QObject::tr(" string that does not exist" ), QString(" string that does not exist" )); |
| 127 | |
| 128 | QCOMPARE(QCoreApplication::translate("CubeForm" , "Test" ), QString::fromLatin1("BBBB" )); |
| 129 | QCOMPARE(QCoreApplication::translate("" , "Test" , "Empty context" ), QString("AAAA" )); |
| 130 | |
| 131 | // Test plurals |
| 132 | QString txed = QCoreApplication::translate(context: "Plurals" , key: "There are %n houses" , disambiguation: 0, n: 0); |
| 133 | QCOMPARE(QString::fromLatin1("[%1]" ).arg(txed), QString("[There are 0 houses]" )); |
| 134 | QCOMPARE(QCoreApplication::translate("Plurals" , "There are %n houses" , 0, 1), QString("There is 1 house" )); |
| 135 | QCOMPARE(QCoreApplication::translate("Plurals" , "There are %n houses" , 0, 2), QString("There are 2 houses" )); |
| 136 | QCOMPARE(QCoreApplication::translate("Plurals" , "There are %n houses" , 0, 3), QString("There are 3 houses" )); |
| 137 | |
| 138 | |
| 139 | // More plurals |
| 140 | QCOMPARE(tr("There are %n cars" , "More Plurals" , 0) , QString("There are 0 cars" )); |
| 141 | QCOMPARE(tr("There are %n cars" , "More Plurals" , 1) , QString("There is 1 car" )); |
| 142 | QCOMPARE(tr("There are %n cars" , "More Plurals" , 2) , QString("There are 2 cars" )); |
| 143 | QCOMPARE(tr("There are %n cars" , "More Plurals" , 3) , QString("There are 3 cars" )); |
| 144 | |
| 145 | |
| 146 | QCOMPARE(QCoreApplication::translate("no_en" , "Kj\xc3\xb8r K\xc3\xa5re, kj\xc3\xa6re" ), QString::fromUtf8("Drive K\xc3\xa5re, dear" )); |
| 147 | QCOMPARE(QCoreApplication::translate("en_no" , "Drive K\xc3\xa5re, dear" ), QString::fromUtf8("Kj\xc3\xb8r K\xc3\xa5re, kj\xc3\xa6re" )); |
| 148 | QCOMPARE(QCoreApplication::translate("en_ch" , "Chinese symbol:" ), QString::fromUtf8("Chinese symbol:\xe7\xb0\x9f" )); |
| 149 | |
| 150 | // printf("halo\r\nhallo"); |
| 151 | // QCOMPARE(tr("This\r\nwill fail"), QString("THIS\nWILL FAIL")); // \r\n = 0d 0a |
| 152 | |
| 153 | QCOMPARE(tr("Completely random string" ), |
| 154 | QString::fromLatin1("Super-lange Uebersetzung mit Schikanen\x9c" |
| 155 | "Mittlere Uebersetung\x9c" |
| 156 | "Kurze Uebers." )); |
| 157 | |
| 158 | qApp->removeTranslator(messageFile: &translator); |
| 159 | } |
| 160 | |
| 161 | void tst_lrelease::compressed() |
| 162 | { |
| 163 | QVERIFY(!QProcess::execute(lrelease, QStringList() << "-compress" << (dataDir + "compressed.ts" ))); |
| 164 | |
| 165 | QTranslator translator; |
| 166 | QVERIFY(translator.load(dataDir + "compressed.qm" )); |
| 167 | qApp->installTranslator(messageFile: &translator); |
| 168 | |
| 169 | QCOMPARE(QCoreApplication::translate("Context1" , "Foo" ), QString::fromLatin1("in first context" )); |
| 170 | QCOMPARE(QCoreApplication::translate("Context2" , "Bar" ), QString::fromLatin1("in second context" )); |
| 171 | |
| 172 | QCOMPARE(QCoreApplication::translate("Action1" , "Component Name" ), QString::fromLatin1("translation in first context" )); |
| 173 | QCOMPARE(QCoreApplication::translate("Action2" , "Component Name" ), QString::fromLatin1("translation in second context" )); |
| 174 | QCOMPARE(QCoreApplication::translate("Action3" , "Component Name" ), QString::fromLatin1("translation in third context" )); |
| 175 | |
| 176 | } |
| 177 | |
| 178 | void tst_lrelease::idbased() |
| 179 | { |
| 180 | QVERIFY(!QProcess::execute(lrelease, QStringList() << "-idbased" << (dataDir + "idbased.ts" ))); |
| 181 | |
| 182 | QTranslator translator; |
| 183 | QVERIFY(translator.load(dataDir + "idbased.qm" )); |
| 184 | qApp->installTranslator(messageFile: &translator); |
| 185 | |
| 186 | QCOMPARE(qtTrId("test_id" ), QString::fromLatin1("This is a test string." )); |
| 187 | QCOMPARE(qtTrId("untranslated_id" ), QString::fromLatin1("This has no translation." )); |
| 188 | } |
| 189 | |
| 190 | void tst_lrelease::markuntranslated() |
| 191 | { |
| 192 | QVERIFY(!QProcess::execute(lrelease, QStringList() << "-markuntranslated" << "#" << "-idbased" << (dataDir + "idbased.ts" ))); |
| 193 | |
| 194 | QTranslator translator; |
| 195 | QVERIFY(translator.load(dataDir + "idbased.qm" )); |
| 196 | qApp->installTranslator(messageFile: &translator); |
| 197 | |
| 198 | QCOMPARE(qtTrId("test_id" ), QString::fromLatin1("This is a test string." )); |
| 199 | QCOMPARE(qtTrId("untranslated_id" ), QString::fromLatin1("#This has no translation." )); |
| 200 | } |
| 201 | |
| 202 | void tst_lrelease::dupes() |
| 203 | { |
| 204 | QProcess proc; |
| 205 | proc.start(program: lrelease, arguments: QStringList() << (dataDir + "dupes.ts" ), mode: QIODevice::ReadWrite | QIODevice::Text); |
| 206 | QVERIFY(proc.waitForFinished()); |
| 207 | QVERIFY(proc.exitStatus() == QProcess::NormalExit); |
| 208 | doCompare(actual: QString(proc.readAllStandardError()).trimmed().split(sep: '\n'), expectedFn: dataDir + "dupes.errors" ); |
| 209 | } |
| 210 | |
| 211 | void tst_lrelease::noTranslations() |
| 212 | { |
| 213 | QProcess proc; |
| 214 | proc.start(program: lrelease, arguments: { dataDir + "no-translations.pro" }); |
| 215 | QVERIFY(proc.waitForFinished()); |
| 216 | QCOMPARE(proc.exitStatus(), QProcess::NormalExit); |
| 217 | QCOMPARE(proc.exitCode(), 0); |
| 218 | auto stderrOutput = proc.readAllStandardError(); |
| 219 | QVERIFY(stderrOutput.contains("lrelease warning: Met no 'TRANSLATIONS' entry in project file" )); |
| 220 | } |
| 221 | |
| 222 | QTEST_MAIN(tst_lrelease) |
| 223 | #include "tst_lrelease.moc" |
| 224 | |