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 test suite 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 <QtTest/QtTest>
30#include <qtranslator.h>
31#include <qfile.h>
32#include <qtemporarydir.h>
33
34class tst_QTranslator : public QObject
35{
36 Q_OBJECT
37
38public:
39 tst_QTranslator();
40protected:
41 bool eventFilter(QObject *obj, QEvent *event);
42private slots:
43 void initTestCase();
44 void init();
45
46 void load_data();
47 void load();
48 void loadLocale();
49 void threadLoad();
50 void testLanguageChange();
51 void plural();
52 void translate_qm_file_generated_with_msgfmt();
53 void loadDirectory();
54 void dependencies();
55 void translationInThreadWhileInstallingTranslator();
56
57private:
58 int languageChangeEventCounter;
59 QSharedPointer<QTemporaryDir> dataDir;
60};
61
62tst_QTranslator::tst_QTranslator()
63 : languageChangeEventCounter(0)
64{
65 qApp->installEventFilter(filterObj: this);
66}
67
68void tst_QTranslator::initTestCase()
69{
70 dataDir = QEXTRACTTESTDATA(QStringLiteral("/tst_qtranslator"));
71 QVERIFY2(!dataDir.isNull(), qPrintable("Could not extract test data"));
72}
73
74void tst_QTranslator::init()
75{
76 QVERIFY2(QDir::setCurrent(dataDir->path()),
77 qPrintable("Could not chdir to " + dataDir->path()));
78}
79
80bool tst_QTranslator::eventFilter(QObject *, QEvent *event)
81{
82 if (event->type() == QEvent::LanguageChange)
83 ++languageChangeEventCounter;
84 return false;
85}
86
87void tst_QTranslator::load_data()
88{
89 QTest::addColumn<QString>(name: "filepath");
90 QTest::addColumn<bool>(name: "isEmpty");
91 QTest::addColumn<QString>(name: "translation");
92 QTest::addColumn<QString>(name: "language");
93
94 QTest::newRow(dataTag: "hellotr_la") << "hellotr_la.qm" << false << "Hallo Welt!" << "de";
95 QTest::newRow(dataTag: "hellotr_empty") << "hellotr_empty.qm" << true << "" << "";
96}
97
98void tst_QTranslator::load()
99{
100 QFETCH(QString, filepath);
101 QFETCH(bool, isEmpty);
102 QFETCH(QString, translation);
103 QFETCH(QString, language);
104
105 {
106 QTranslator tor;
107 QVERIFY(tor.load(QFileInfo(filepath).baseName()));
108 QCOMPARE(tor.isEmpty(), isEmpty);
109 QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation);
110 QCOMPARE(tor.filePath(), filepath);
111 QCOMPARE(tor.language(), language);
112 }
113
114 {
115 QFile file(filepath);
116 file.open(flags: QFile::ReadOnly);
117 QByteArray data = file.readAll();
118 QTranslator tor;
119 QVERIFY(tor.load((const uchar *)data.constData(), data.length()));
120 QCOMPARE(tor.isEmpty(), isEmpty);
121 QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation);
122 QCOMPARE(tor.filePath(), "");
123 QCOMPARE(tor.language(), language);
124 }
125
126 {
127 QTranslator tor;
128 QString path = QString(":/tst_qtranslator/%1").arg(a: filepath);
129 QVERIFY(tor.load(path));
130 QCOMPARE(tor.isEmpty(), isEmpty);
131 QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation);
132 QCOMPARE(tor.filePath(), path);
133 QCOMPARE(tor.language(), language);
134 }
135}
136
137void tst_QTranslator::loadLocale()
138{
139 QLocale locale;
140 auto localeName = locale.uiLanguages().value(i: 0).replace(before: '-', after: '_');
141 if (localeName.isEmpty())
142 QSKIP("This test requires at least one available UI language.");
143
144 QByteArray ba;
145 {
146 QFile file(":/tst_qtranslator/hellotr_la.qm");
147 QVERIFY2(file.open(QFile::ReadOnly), qPrintable(file.errorString()));
148 ba = file.readAll();
149 QVERIFY(!ba.isEmpty());
150 }
151
152 QTemporaryDir dir;
153 QVERIFY(dir.isValid());
154
155 auto path = dir.path();
156 QFile file(path + "/dummy");
157 QVERIFY2(file.open(QFile::WriteOnly), qPrintable(file.errorString()));
158 QCOMPARE(file.write(ba), ba.size());
159 file.close();
160
161 /*
162 Test the following order:
163
164 /tmp/tmpDir/foo-en_US.qm
165 /tmp/tmpDir/foo-en_US
166 /tmp/tmpDir/foo-en.qm
167 /tmp/tmpDir/foo-en
168 /tmp/tmpDir/foo.qm
169 /tmp/tmpDir/foo-
170 /tmp/tmpDir/foo
171 */
172
173 QStringList files;
174 while (true) {
175 files.append(t: path + "/foo-" + localeName + ".qm");
176 QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
177
178 files.append(t: path + "/foo-" + localeName);
179 QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
180
181 int rightmost = localeName.lastIndexOf(c: QLatin1Char('_'));
182 if (rightmost <= 0)
183 break;
184 localeName.truncate(pos: rightmost);
185 }
186
187 files.append(t: path + "/foo.qm");
188 QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
189
190 files.append(t: path + "/foo-");
191 QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
192
193 files.append(t: path + "/foo");
194 QVERIFY2(file.rename(files.last()), qPrintable(file.errorString()));
195
196 QTranslator tor;
197 for (const auto &filePath : files) {
198 QVERIFY(tor.load(locale, "foo", "-", path, ".qm"));
199 QCOMPARE(tor.filePath(), filePath);
200 QVERIFY2(file.remove(filePath), qPrintable(file.errorString()));
201 }
202}
203
204class TranslatorThread : public QThread
205{
206 void run() {
207 QTranslator tor( 0 );
208 tor.load(filename: "hellotr_la");
209
210 if (tor.isEmpty())
211 qFatal(msg: "Could not load translation");
212 if (tor.translate(context: "QPushButton", sourceText: "Hello world!") != QLatin1String("Hallo Welt!"))
213 qFatal(msg: "Test string was not translated correctlys");
214 }
215};
216
217
218void tst_QTranslator::threadLoad()
219{
220 TranslatorThread thread;
221 thread.start();
222 QVERIFY(thread.wait(10 * 1000));
223}
224
225void tst_QTranslator::testLanguageChange()
226{
227 languageChangeEventCounter = 0;
228
229 QTranslator *tor = new QTranslator;
230 tor->load(filename: "hellotr_la.qm");
231 qApp->sendPostedEvents();
232 qApp->sendPostedEvents();
233 QCOMPARE(languageChangeEventCounter, 0);
234
235 tor->load(filename: "doesn't exist, same as clearing");
236 qApp->sendPostedEvents();
237 qApp->sendPostedEvents();
238 QCOMPARE(languageChangeEventCounter, 0);
239
240 tor->load(filename: "hellotr_la.qm");
241 qApp->sendPostedEvents();
242 qApp->sendPostedEvents();
243 QCOMPARE(languageChangeEventCounter, 0);
244
245 qApp->installTranslator(messageFile: tor);
246 qApp->sendPostedEvents();
247 qApp->sendPostedEvents();
248 QCOMPARE(languageChangeEventCounter, 1);
249
250 tor->load(filename: "doesn't exist, same as clearing");
251 qApp->sendPostedEvents();
252 qApp->sendPostedEvents();
253 QCOMPARE(languageChangeEventCounter, 2);
254
255 tor->load(filename: "hellotr_la.qm");
256 qApp->sendPostedEvents();
257 qApp->sendPostedEvents();
258 QCOMPARE(languageChangeEventCounter, 3);
259
260 qApp->removeTranslator(messageFile: tor);
261 qApp->sendPostedEvents();
262 qApp->sendPostedEvents();
263 QCOMPARE(languageChangeEventCounter, 4);
264
265 tor->load(filename: "doesn't exist, same as clearing");
266 qApp->sendPostedEvents();
267 qApp->sendPostedEvents();
268 QCOMPARE(languageChangeEventCounter, 4);
269
270 qApp->installTranslator(messageFile: tor);
271 qApp->sendPostedEvents();
272 qApp->sendPostedEvents();
273 QCOMPARE(languageChangeEventCounter, 4);
274
275 tor->load(filename: "hellotr_la.qm");
276 qApp->sendPostedEvents();
277 qApp->sendPostedEvents();
278 QCOMPARE(languageChangeEventCounter, 5);
279
280 delete tor;
281 tor = 0;
282 qApp->sendPostedEvents();
283 qApp->sendPostedEvents();
284 QCOMPARE(languageChangeEventCounter, 6);
285}
286
287
288void tst_QTranslator::plural()
289{
290
291 QTranslator tor( 0 );
292 tor.load(filename: "hellotr_la");
293 QVERIFY(!tor.isEmpty());
294 QCoreApplication::installTranslator(messageFile: &tor);
295 QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 0), QLatin1String("Hallo 0 Welten!"));
296 QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 1), QLatin1String("Hallo 1 Welt!"));
297 QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 2), QLatin1String("Hallo 2 Welten!"));
298}
299
300void tst_QTranslator::translate_qm_file_generated_with_msgfmt()
301{
302 QTranslator translator;
303 translator.load(filename: "msgfmt_from_po");
304 qApp->installTranslator(messageFile: &translator);
305
306 QCOMPARE(QCoreApplication::translate("", "Intro"), QLatin1String("Einleitung"));
307 // The file is converted from a po file, thus it does not have any context info.
308 // The following should then not be translated
309 QCOMPARE(QCoreApplication::translate("contekst", "Intro"), QLatin1String("Intro"));
310 QCOMPARE(QCoreApplication::translate("contekst", "Intro\0\0"), QLatin1String("Intro"));
311 QCOMPARE(QCoreApplication::translate("contekst", "Intro\0x"), QLatin1String("Intro"));
312 QCOMPARE(QCoreApplication::translate("", "Intro\0\0"), QLatin1String("Einleitung"));
313 QCOMPARE(QCoreApplication::translate("", "Intro\0x"), QLatin1String("Einleitung"));
314
315 qApp->removeTranslator(messageFile: &translator);
316}
317
318void tst_QTranslator::loadDirectory()
319{
320 QString current_base = QDir::current().dirName();
321 QVERIFY(QFileInfo("../" + current_base).isDir());
322
323 QTranslator tor;
324 tor.load(filename: current_base, directory: "..");
325 QVERIFY(tor.isEmpty());
326}
327
328void tst_QTranslator::dependencies()
329{
330 {
331 // load
332 QTranslator tor;
333 tor.load(filename: "dependencies_la");
334 QVERIFY(!tor.isEmpty());
335 QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!"));
336
337 // plural
338 QCoreApplication::installTranslator(messageFile: &tor);
339 QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 0), QLatin1String("Hallo 0 Welten!"));
340 QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 1), QLatin1String("Hallo 1 Welt!"));
341 QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 2), QLatin1String("Hallo 2 Welten!"));
342
343 // pick up translation from the file with dependencies
344 QCOMPARE(tor.translate("QPushButton", "It's a small world"), QLatin1String("Es ist eine kleine Welt"));
345 }
346
347 {
348 QTranslator tor( 0 );
349 QFile file("dependencies_la.qm");
350 file.open(flags: QFile::ReadOnly);
351 QByteArray data = file.readAll();
352 tor.load(data: (const uchar *)data.constData(), len: data.length());
353 QVERIFY(!tor.isEmpty());
354 QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!"));
355 }
356
357 {
358 // Test resolution of paths relative to main file
359 const QString absoluteFile = QFileInfo("dependencies_la").absoluteFilePath();
360 QDir::setCurrent(QDir::tempPath());
361 QTranslator tor;
362 QVERIFY(tor.load(absoluteFile));
363 QVERIFY(!tor.isEmpty());
364 }
365}
366
367struct TranslateThread : public QThread
368{
369 bool ok = false;
370 QAtomicInt terminate;
371 QMutex startupLock;
372 QWaitCondition runningCondition;
373
374 void run() {
375 bool startSignalled = false;
376
377 while (terminate.loadRelaxed() == 0) {
378 const QString result = QCoreApplication::translate(context: "QPushButton", key: "Hello %n world(s)!", disambiguation: 0, n: 0);
379
380 if (!startSignalled) {
381 QMutexLocker startupLocker(&startupLock);
382 runningCondition.wakeAll();
383 startSignalled = true;
384 }
385
386 ok = (result == QLatin1String("Hallo 0 Welten!"))
387 || (result == QLatin1String("Hello 0 world(s)!"));
388 if (!ok)
389 break;
390 }
391 }
392};
393
394void tst_QTranslator::translationInThreadWhileInstallingTranslator()
395{
396 TranslateThread thread;
397
398 QMutexLocker startupLocker(&thread.startupLock);
399
400 thread.start();
401
402 thread.runningCondition.wait(lockedMutex: &thread.startupLock);
403
404 QTranslator tor;
405 tor.load(filename: "hellotr_la");
406 QCoreApplication::installTranslator(messageFile: &tor);
407
408 ++thread.terminate;
409
410 QVERIFY(thread.wait());
411 QVERIFY(thread.ok);
412}
413
414QTEST_MAIN(tst_QTranslator)
415#include "tst_qtranslator.moc"
416

source code of qtbase/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp