1/****************************************************************************
2**
3** Copyright (C) 2020 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
30#include <QtTest/QtTest>
31#include <math.h>
32#include <qdebug.h>
33#include <qdir.h>
34#include <qfileinfo.h>
35#include <QScopedArrayPointer>
36#include <qtextcodec.h>
37#include <qdatetime.h>
38#if QT_CONFIG(process)
39# include <qprocess.h>
40#endif
41#include <float.h>
42#include <locale.h>
43
44#include <qlocale.h>
45#include <private/qlocale_p.h>
46#include <private/qlocale_tools_p.h>
47#include <qnumeric.h>
48
49#if defined(Q_OS_LINUX) && !defined(__UCLIBC__)
50# define QT_USE_FENV
51#endif
52
53#ifdef QT_USE_FENV
54# include <fenv.h>
55#endif
56
57#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
58# include <stdlib.h>
59#endif
60
61Q_DECLARE_METATYPE(QLocale::FormatType)
62
63class tst_QLocale : public QObject
64{
65 Q_OBJECT
66
67public:
68 tst_QLocale();
69
70private slots:
71 void initTestCase();
72 void cleanupTestCase();
73#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
74 void windowsDefaultLocale();
75#endif
76#ifdef Q_OS_MAC
77 void macDefaultLocale();
78#endif
79
80 void ctor();
81 void emptyCtor_data();
82 void emptyCtor();
83 void consistentC();
84 void matchingLocales();
85 void stringToDouble_data();
86 void stringToDouble();
87 void stringToFloat_data();
88 void stringToFloat();
89 void doubleToString_data();
90 void doubleToString();
91 void strtod_data();
92 void strtod();
93 void long_long_conversion_data();
94 void long_long_conversion();
95 void long_long_conversion_extra();
96 void testInfAndNan();
97 void fpExceptions();
98 void negativeZero();
99 void dayOfWeek();
100 void dayOfWeek_data();
101 void formatDate();
102 void formatDate_data();
103 void formatTime();
104 void formatTime_data();
105 void formatDateTime();
106 void formatDateTime_data();
107 void formatTimeZone();
108 void toDateTime_data();
109 void toDateTime();
110 void negativeNumbers();
111 void numberOptions();
112 void dayName_data();
113 void dayName();
114 void standaloneDayName_data();
115 void standaloneDayName();
116 void underflowOverflow();
117
118 void dateFormat();
119 void timeFormat();
120 void dateTimeFormat();
121 void monthName();
122 void standaloneMonthName();
123
124 void defaultNumberingSystem_data();
125 void defaultNumberingSystem();
126
127 void ampm_data();
128 void ampm();
129 void currency();
130 void quoteString();
131 void uiLanguages();
132 void weekendDays();
133 void listPatterns();
134
135 void measurementSystems_data();
136 void measurementSystems();
137 void QTBUG_26035_positivesign();
138
139 void textDirection_data();
140 void textDirection();
141
142 void formattedDataSize_data();
143 void formattedDataSize();
144 void bcp47Name_data();
145 void bcp47Name();
146
147 void systemLocale_data();
148 void systemLocale();
149
150 void IndianNumberGrouping();
151
152 // *** ORDER-DEPENDENCY *** (This Is Bad.)
153 // Test order is determined by order of declaration here: *all* tests that
154 // QLocale::setDefault() *must* appear *after* all other tests !
155 void defaulted_ctor(); // This one must be the first of these.
156 void legacyNames();
157 void unixLocaleName_data();
158 void unixLocaleName();
159 void testNames_data();
160 void testNames();
161 // DO NOT add tests here unless they QLocale::setDefault(); see above.
162private:
163 QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
164 QString m_sysapp;
165 QStringList cleanEnv;
166 bool europeanTimeZone;
167 void toReal_data();
168
169 class TransientLocale
170 {
171 const int m_category;
172 const char *const m_prior;
173 public:
174 TransientLocale(int category, const char *locale)
175 : m_category(category), m_prior(setlocale(category: category, locale: locale)) {}
176 ~TransientLocale() { setlocale(category: m_category, locale: m_prior); }
177 };
178};
179
180tst_QLocale::tst_QLocale()
181{
182 qRegisterMetaType<QLocale::FormatType>(typeName: "QLocale::FormatType");
183
184 // Test if in Central European Time zone
185 uint x1 = QDateTime(QDate(1990, 1, 1), QTime()).toSecsSinceEpoch();
186 uint x2 = QDateTime(QDate(1990, 6, 1), QTime()).toSecsSinceEpoch();
187 europeanTimeZone = (x1 == 631148400 && x2 == 644191200);
188}
189
190void tst_QLocale::initTestCase()
191{
192#if QT_CONFIG(process)
193# ifdef Q_OS_ANDROID
194 m_sysapp = QCoreApplication::applicationDirPath() + "/libsyslocaleapp.so";
195# else // !defined(Q_OS_ANDROID)
196 const QString syslocaleapp_dir = QFINDTESTDATA("syslocaleapp");
197 QVERIFY2(!syslocaleapp_dir.isEmpty(),
198 qPrintable(QStringLiteral("Cannot find 'syslocaleapp' starting from ")
199 + QDir::toNativeSeparators(QDir::currentPath())));
200 m_sysapp = syslocaleapp_dir + QStringLiteral("/syslocaleapp");
201# ifdef Q_OS_WIN
202 m_sysapp += QStringLiteral(".exe");
203# endif
204# endif // Q_OS_ANDROID
205 const QFileInfo fi(m_sysapp);
206 QVERIFY2(fi.exists() && fi.isExecutable(),
207 qPrintable(QDir::toNativeSeparators(m_sysapp)
208 + QStringLiteral(" does not exist or is not executable.")));
209
210 // Get an environment free of any locale-related variables
211 cleanEnv.clear();
212 foreach (QString const& entry, QProcess::systemEnvironment()) {
213 if (entry.startsWith(s: "LANG=") || entry.startsWith(s: "LC_") || entry.startsWith(s: "LANGUAGE="))
214 continue;
215 cleanEnv << entry;
216 }
217#endif // QT_CONFIG(process)
218}
219
220void tst_QLocale::cleanupTestCase()
221{}
222
223void tst_QLocale::ctor()
224{
225 QLocale default_locale = QLocale::system();
226 QLocale::Language default_lang = default_locale.language();
227 QLocale::Country default_country = default_locale.country();
228
229 qDebug(msg: "Default: %s/%s", QLocale::languageToString(language: default_lang).toLatin1().constData(),
230 QLocale::countryToString(country: default_country).toLatin1().constData());
231
232 {
233 QLocale l;
234 QVERIFY(l.language() == default_lang);
235 QVERIFY(l.country() == default_country);
236 }
237
238#define TEST_CTOR(req_lang, req_script, req_country, exp_lang, exp_script, exp_country) \
239 { \
240 QLocale l(QLocale::req_lang, QLocale::req_script, QLocale::req_country); \
241 QCOMPARE((int)l.language(), (int)exp_lang); \
242 QCOMPARE((int)l.script(), (int)exp_script); \
243 QCOMPARE((int)l.country(), (int)exp_country); \
244 }
245
246 // Exact matches
247 TEST_CTOR(Chinese, SimplifiedHanScript, China,
248 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
249 TEST_CTOR(Chinese, TraditionalHanScript, Taiwan,
250 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan);
251 TEST_CTOR(Chinese, TraditionalHanScript, HongKong,
252 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::HongKong);
253
254 // Best match for AnyCountry
255 TEST_CTOR(Chinese, SimplifiedHanScript, AnyCountry,
256 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
257 TEST_CTOR(Chinese, TraditionalHanScript, AnyCountry,
258 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan);
259
260 // Best match for AnyScript (and change country to supported one, if necessary)
261 TEST_CTOR(Chinese, AnyScript, China,
262 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
263 TEST_CTOR(Chinese, AnyScript, Taiwan,
264 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan);
265 TEST_CTOR(Chinese, AnyScript, HongKong,
266 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::HongKong);
267 TEST_CTOR(Chinese, AnyScript, UnitedStates,
268 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
269
270 // Fully-specified not found; find best alternate country
271 TEST_CTOR(Chinese, SimplifiedHanScript, Taiwan,
272 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
273 TEST_CTOR(Chinese, SimplifiedHanScript, UnitedStates,
274 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
275 TEST_CTOR(Chinese, TraditionalHanScript, China,
276 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan);
277 TEST_CTOR(Chinese, TraditionalHanScript, UnitedStates,
278 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan);
279
280 // Fully-specified not found; find best alternate script
281 TEST_CTOR(Chinese, LatinScript, China,
282 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
283 TEST_CTOR(Chinese, LatinScript, Taiwan,
284 QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan);
285
286 // Fully-specified not found; find best alternate country and script
287 TEST_CTOR(Chinese, LatinScript, UnitedStates,
288 QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China);
289
290#undef TEST_CTOR
291}
292
293void tst_QLocale::defaulted_ctor()
294{
295 QLocale default_locale = QLocale::system();
296 QLocale::Language default_lang = default_locale.language();
297 QLocale::Country default_country = default_locale.country();
298
299 qDebug(msg: "Default: %s/%s", QLocale::languageToString(language: default_lang).toLatin1().constData(),
300 QLocale::countryToString(country: default_country).toLatin1().constData());
301
302 {
303 QLocale l(QLocale::C, QLocale::AnyCountry);
304 QCOMPARE(l.language(), QLocale::C);
305 QCOMPARE(l.country(), QLocale::AnyCountry);
306 }
307
308#define TEST_CTOR(req_lang, req_country, exp_lang, exp_country) \
309 { \
310 QLocale l(QLocale::req_lang, QLocale::req_country); \
311 QCOMPARE((int)l.language(), (int)exp_lang); \
312 QCOMPARE((int)l.country(), (int)exp_country); \
313 }
314
315 TEST_CTOR(AnyLanguage, AnyCountry, default_lang, default_country)
316 TEST_CTOR(C, AnyCountry, QLocale::C, QLocale::AnyCountry)
317 TEST_CTOR(Aymara, AnyCountry, default_lang, default_country)
318 TEST_CTOR(Aymara, France, default_lang, default_country)
319
320 TEST_CTOR(English, AnyCountry, QLocale::English, QLocale::UnitedStates)
321 TEST_CTOR(English, UnitedStates, QLocale::English, QLocale::UnitedStates)
322 TEST_CTOR(English, France, QLocale::English, QLocale::UnitedStates)
323 TEST_CTOR(English, UnitedKingdom, QLocale::English, QLocale::UnitedKingdom)
324
325 TEST_CTOR(French, France, QLocale::French, QLocale::France)
326 TEST_CTOR(C, France, QLocale::C, QLocale::AnyCountry)
327 TEST_CTOR(Spanish, LatinAmerica, QLocale::Spanish,
328 QLocale::LatinAmerica)
329
330 QLocale::setDefault(QLocale(QLocale::English, QLocale::France));
331
332 {
333 QLocale l;
334 QVERIFY(l.language() == QLocale::English);
335 QVERIFY(l.country() == QLocale::UnitedStates);
336 }
337
338 TEST_CTOR(French, France, QLocale::French, QLocale::France)
339 TEST_CTOR(English, UnitedKingdom, QLocale::English, QLocale::UnitedKingdom)
340
341 TEST_CTOR(French, France, QLocale::French, QLocale::France)
342 TEST_CTOR(C, AnyCountry, QLocale::C, QLocale::AnyCountry)
343 TEST_CTOR(C, France, QLocale::C, QLocale::AnyCountry)
344 TEST_CTOR(Aymara, AnyCountry, QLocale::English, QLocale::UnitedStates)
345
346 QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedKingdom));
347
348 {
349 QLocale l;
350 QVERIFY(l.language() == QLocale::English);
351 QVERIFY(l.country() == QLocale::UnitedKingdom);
352 }
353
354 TEST_CTOR(French, France, QLocale::French, QLocale::France)
355 TEST_CTOR(English, UnitedKingdom, QLocale::English, QLocale::UnitedKingdom)
356
357 TEST_CTOR(C, AnyCountry, QLocale::C, QLocale::AnyCountry)
358 TEST_CTOR(C, France, QLocale::C, QLocale::AnyCountry)
359
360 QLocale::setDefault(QLocale(QLocale::Aymara, QLocale::France));
361
362 {
363 QLocale l;
364 QVERIFY(l.language() == QLocale::English);
365 QVERIFY(l.country() == QLocale::UnitedKingdom);
366 }
367
368 TEST_CTOR(Aymara, AnyCountry, QLocale::English, QLocale::UnitedKingdom)
369 TEST_CTOR(Aymara, France, QLocale::English, QLocale::UnitedKingdom)
370
371 TEST_CTOR(English, AnyCountry, QLocale::English, QLocale::UnitedStates)
372 TEST_CTOR(English, UnitedStates, QLocale::English, QLocale::UnitedStates)
373 TEST_CTOR(English, France, QLocale::English, QLocale::UnitedStates)
374 TEST_CTOR(English, UnitedKingdom, QLocale::English, QLocale::UnitedKingdom)
375
376 TEST_CTOR(French, France, QLocale::French, QLocale::France)
377 TEST_CTOR(C, AnyCountry, QLocale::C, QLocale::AnyCountry)
378 TEST_CTOR(C, France, QLocale::C, QLocale::AnyCountry)
379
380 QLocale::setDefault(QLocale(QLocale::Aymara, QLocale::AnyCountry));
381
382 {
383 QLocale l;
384 QVERIFY(l.language() == QLocale::English);
385 QVERIFY(l.country() == QLocale::UnitedKingdom);
386 }
387
388 TEST_CTOR(Aymara, AnyCountry, QLocale::English, QLocale::UnitedKingdom)
389 TEST_CTOR(Aymara, France, QLocale::English, QLocale::UnitedKingdom)
390
391 TEST_CTOR(English, AnyCountry, QLocale::English, QLocale::UnitedStates)
392 TEST_CTOR(English, UnitedStates, QLocale::English, QLocale::UnitedStates)
393 TEST_CTOR(English, France, QLocale::English, QLocale::UnitedStates)
394 TEST_CTOR(English, UnitedKingdom, QLocale::English, QLocale::UnitedKingdom)
395
396 TEST_CTOR(French, France, QLocale::French, QLocale::France)
397 TEST_CTOR(C, AnyCountry, QLocale::C, QLocale::AnyCountry)
398 TEST_CTOR(C, France, QLocale::C, QLocale::AnyCountry)
399
400 TEST_CTOR(Arabic, AnyCountry, QLocale::Arabic, QLocale::Egypt)
401 TEST_CTOR(Dutch, AnyCountry, QLocale::Dutch, QLocale::Netherlands)
402 TEST_CTOR(German, AnyCountry, QLocale::German, QLocale::Germany)
403 TEST_CTOR(Greek, AnyCountry, QLocale::Greek, QLocale::Greece)
404 TEST_CTOR(Malay, AnyCountry, QLocale::Malay, QLocale::Malaysia)
405 TEST_CTOR(Persian, AnyCountry, QLocale::Persian, QLocale::Iran)
406 TEST_CTOR(Portuguese, AnyCountry, QLocale::Portuguese, QLocale::Brazil)
407 TEST_CTOR(Serbian, AnyCountry, QLocale::Serbian, QLocale::Serbia)
408 TEST_CTOR(Somali, AnyCountry, QLocale::Somali, QLocale::Somalia)
409 TEST_CTOR(Spanish, AnyCountry, QLocale::Spanish, QLocale::Spain)
410 TEST_CTOR(Swedish, AnyCountry, QLocale::Swedish, QLocale::Sweden)
411 TEST_CTOR(Uzbek, AnyCountry, QLocale::Uzbek, QLocale::Uzbekistan)
412
413#undef TEST_CTOR
414#define TEST_CTOR(req_lc, exp_lang, exp_country) \
415 { \
416 QLocale l(req_lc); \
417 QVERIFY2(l.language() == QLocale::exp_lang \
418 && l.country() == QLocale::exp_country, \
419 QString("requested: \"" + QString(req_lc) + "\", got: " \
420 + QLocale::languageToString(l.language()) \
421 + QLatin1Char('/') \
422 + QLocale::countryToString(l.country())).toLatin1().constData()); \
423 QCOMPARE(l, QLocale(QLocale::exp_lang, QLocale::exp_country)); \
424 QCOMPARE(qHash(l), qHash(QLocale(QLocale::exp_lang, QLocale::exp_country))); \
425 }
426
427 QLocale::setDefault(QLocale(QLocale::C));
428 const QString empty;
429
430 TEST_CTOR("C", C, AnyCountry)
431 TEST_CTOR("bla", C, AnyCountry)
432 TEST_CTOR("zz", C, AnyCountry)
433 TEST_CTOR("zz_zz", C, AnyCountry)
434 TEST_CTOR("zz...", C, AnyCountry)
435 TEST_CTOR("", C, AnyCountry)
436 TEST_CTOR("en/", C, AnyCountry)
437 TEST_CTOR(empty, C, AnyCountry)
438 TEST_CTOR("en", English, UnitedStates)
439 TEST_CTOR("en", English, UnitedStates)
440 TEST_CTOR("en.", English, UnitedStates)
441 TEST_CTOR("en@", English, UnitedStates)
442 TEST_CTOR("en.@", English, UnitedStates)
443 TEST_CTOR("en_", English, UnitedStates)
444 TEST_CTOR("en_U", English, UnitedStates)
445 TEST_CTOR("en_.", English, UnitedStates)
446 TEST_CTOR("en_.@", English, UnitedStates)
447 TEST_CTOR("en.bla", English, UnitedStates)
448 TEST_CTOR("en@bla", English, UnitedStates)
449 TEST_CTOR("en_blaaa", English, UnitedStates)
450 TEST_CTOR("en_zz", English, UnitedStates)
451 TEST_CTOR("en_GB", English, UnitedKingdom)
452 TEST_CTOR("en_GB.bla", English, UnitedKingdom)
453 TEST_CTOR("en_GB@.bla", English, UnitedKingdom)
454 TEST_CTOR("en_GB@bla", English, UnitedKingdom)
455 TEST_CTOR("en-GB", English, UnitedKingdom)
456 TEST_CTOR("en-GB@bla", English, UnitedKingdom)
457 TEST_CTOR("eo", Esperanto, World)
458 TEST_CTOR("yi", Yiddish, World)
459
460 TEST_CTOR("no", NorwegianBokmal, Norway)
461 TEST_CTOR("nb", NorwegianBokmal, Norway)
462 TEST_CTOR("nn", NorwegianNynorsk, Norway)
463 TEST_CTOR("no_NO", NorwegianBokmal, Norway)
464 TEST_CTOR("nb_NO", NorwegianBokmal, Norway)
465 TEST_CTOR("nn_NO", NorwegianNynorsk, Norway)
466 TEST_CTOR("es_ES", Spanish, Spain)
467 TEST_CTOR("es_419", Spanish, LatinAmerica)
468 TEST_CTOR("es-419", Spanish, LatinAmerica)
469 TEST_CTOR("fr_MA", French, Morocco)
470
471 // test default countries for languages
472 TEST_CTOR("zh", Chinese, China)
473 TEST_CTOR("zh-Hans", Chinese, China)
474 TEST_CTOR("ne", Nepali, Nepal)
475
476#undef TEST_CTOR
477#define TEST_CTOR(req_lc, exp_lang, exp_script, exp_country) \
478 { \
479 QLocale l(req_lc); \
480 QVERIFY2(l.language() == QLocale::exp_lang \
481 && l.script() == QLocale::exp_script \
482 && l.country() == QLocale::exp_country, \
483 QString("requested: \"" + QString(req_lc) + "\", got: " \
484 + QLocale::languageToString(l.language()) \
485 + QLatin1Char('/') + QLocale::scriptToString(l.script()) \
486 + QLatin1Char('/') + QLocale::countryToString(l.country())).toLatin1().constData()); \
487 }
488
489 TEST_CTOR("zh_CN", Chinese, SimplifiedHanScript, China)
490 TEST_CTOR("zh_Hans_CN", Chinese, SimplifiedHanScript, China)
491 TEST_CTOR("zh_Hans", Chinese, SimplifiedHanScript, China)
492 TEST_CTOR("zh_Hant", Chinese, TraditionalHanScript, Taiwan)
493 TEST_CTOR("zh_Hans_MO", Chinese, SimplifiedHanScript, Macau)
494 TEST_CTOR("zh_Hant_MO", Chinese, TraditionalHanScript, Macau)
495 TEST_CTOR("az_Latn_AZ", Azerbaijani, LatinScript, Azerbaijan)
496 TEST_CTOR("ha_NG", Hausa, LatinScript, Nigeria)
497
498 TEST_CTOR("ru", Russian, CyrillicScript, RussianFederation)
499 TEST_CTOR("ru_Cyrl", Russian, CyrillicScript, RussianFederation)
500
501#undef TEST_CTOR
502}
503
504#if QT_CONFIG(process)
505static inline bool runSysApp(const QString &binary,
506 const QStringList &env,
507 QString *output,
508 QString *errorMessage)
509{
510 output->clear();
511 errorMessage->clear();
512 QProcess process;
513 process.setEnvironment(env);
514 process.start(program: binary, arguments: QStringList());
515 process.closeWriteChannel();
516 if (!process.waitForStarted()) {
517 *errorMessage = QLatin1String("Cannot start '") + binary
518 + QLatin1String("': ") + process.errorString();
519 return false;
520 }
521 if (!process.waitForFinished()) {
522 process.kill();
523 *errorMessage = QStringLiteral("Timeout waiting for ") + binary;
524 return false;
525 }
526 *output = QString::fromLocal8Bit(str: process.readAllStandardOutput());
527 return true;
528}
529
530static inline bool runSysAppTest(const QString &binary,
531 QStringList baseEnv,
532 const QString &requestedLocale,
533 const QString &expectedOutput,
534 QString *errorMessage)
535{
536 QString output;
537 baseEnv.append(QStringLiteral("LANG=") + requestedLocale);
538 if (!runSysApp(binary, env: baseEnv, output: &output, errorMessage))
539 return false;
540
541 if (output.isEmpty()) {
542 *errorMessage = QLatin1String("Empty output received for requested '") + requestedLocale
543 + QLatin1String("' (expected '") + expectedOutput + QLatin1String("')");
544 return false;
545 }
546 if (output != expectedOutput) {
547 *errorMessage = QLatin1String("Output mismatch for requested '") + requestedLocale
548 + QLatin1String("': Expected '") + expectedOutput + QLatin1String("', got '")
549 + output + QLatin1String("'");
550 return false;
551 }
552 return true;
553}
554#endif
555
556void tst_QLocale::emptyCtor_data()
557{
558#if !QT_CONFIG(process)
559 QSKIP("No qprocess support", SkipAll);
560#endif
561#ifdef Q_OS_ANDROID
562 QSKIP("This test crashes on Android");
563#endif
564
565 QTest::addColumn<QString>(name: "expected");
566
567#define ADD_CTOR_TEST(give, expect) QTest::newRow(give) << QStringLiteral(expect);
568
569 // For format and meaning, see:
570 // http://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
571 // Note that the accepted values for fields are implementation-dependent;
572 // the template is language[_territory][.codeset][@modifier]
573
574 // Vanilla:
575 ADD_CTOR_TEST("C", "C");
576
577 // Standard forms:
578 ADD_CTOR_TEST("en", "en_US");
579 ADD_CTOR_TEST("en_GB", "en_GB");
580 ADD_CTOR_TEST("de", "de_DE");
581 // Norsk has some quirks:
582 ADD_CTOR_TEST("no", "nb_NO");
583 ADD_CTOR_TEST("nb", "nb_NO");
584 ADD_CTOR_TEST("nn", "nn_NO");
585 ADD_CTOR_TEST("no_NO", "nb_NO");
586 ADD_CTOR_TEST("nb_NO", "nb_NO");
587 ADD_CTOR_TEST("nn_NO", "nn_NO");
588
589 // Not too fussy about case:
590 ADD_CTOR_TEST("DE", "de_DE");
591 ADD_CTOR_TEST("EN", "en_US");
592
593 // Invalid fields
594 ADD_CTOR_TEST("bla", "C");
595 ADD_CTOR_TEST("zz", "C");
596 ADD_CTOR_TEST("zz_zz", "C");
597 ADD_CTOR_TEST("zz...", "C");
598 ADD_CTOR_TEST("en.bla", "en_US");
599 ADD_CTOR_TEST("en@bla", "en_US");
600 ADD_CTOR_TEST("en_blaaa", "en_US");
601 ADD_CTOR_TEST("en_zz", "en_US");
602 ADD_CTOR_TEST("en_GB.bla", "en_GB");
603 ADD_CTOR_TEST("en_GB@.bla", "en_GB");
604 ADD_CTOR_TEST("en_GB@bla", "en_GB");
605
606 // Empty optional fields, but with punctuators supplied
607 ADD_CTOR_TEST("en.", "en_US");
608 ADD_CTOR_TEST("en@", "en_US");
609 ADD_CTOR_TEST("en.@", "en_US");
610 ADD_CTOR_TEST("en_", "en_US");
611 ADD_CTOR_TEST("en_.", "en_US");
612 ADD_CTOR_TEST("en_.@", "en_US");
613#undef ADD_CTOR_TEST
614
615#if QT_CONFIG(process) // for runSysApp
616 // Get default locale.
617 QString defaultLoc;
618 QString errorMessage;
619 if (runSysApp(binary: m_sysapp, env: cleanEnv, output: &defaultLoc, errorMessage: &errorMessage)) {
620#define ADD_CTOR_TEST(give) QTest::newRow(give) << defaultLoc;
621 ADD_CTOR_TEST("en/");
622 ADD_CTOR_TEST("asdfghj");
623 ADD_CTOR_TEST("123456");
624#undef ADD_CTOR_TEST
625 } else {
626 qDebug() << "Skipping tests based on default locale" << qPrintable(errorMessage);
627 }
628#endif // process
629}
630
631void tst_QLocale::emptyCtor()
632{
633#if QT_CONFIG(process) // for runSysAppTest
634 QLatin1String request(QTest::currentDataTag());
635 QFETCH(QString, expected);
636
637 // Test constructor without arguments (see syslocaleapp/syslocaleapp.cpp)
638 // Needs separate process because of caching of the system locale.
639 QString errorMessage;
640 QVERIFY2(runSysAppTest(m_sysapp, cleanEnv, request, expected, &errorMessage),
641 qPrintable(errorMessage));
642
643#else
644 // This won't be called, as _data() skipped out early.
645#endif // process
646}
647
648void tst_QLocale::legacyNames()
649{
650 QLocale::setDefault(QLocale(QLocale::C));
651
652#if QT_DEPRECATED_SINCE(5, 15)
653#define TEST_CTOR(req_lang, req_country, exp_lang, exp_country) \
654 { \
655 QLocale l(QLocale::req_lang, QLocale::req_country); \
656 QCOMPARE((int)l.language(), (int)QLocale::exp_lang); \
657 QCOMPARE((int)l.country(), (int)QLocale::exp_country); \
658 }
659
660 TEST_CTOR(Moldavian, Moldova, Romanian, Moldova)
661 TEST_CTOR(Norwegian, AnyCountry, Norwegian, Norway)
662 TEST_CTOR(SerboCroatian, Montenegro, Serbian, Montenegro)
663 TEST_CTOR(Tagalog, AnyCountry, Filipino, Philippines)
664
665#undef TEST_CTOR
666#endif
667
668#define TEST_CTOR(req_lc, exp_lang, exp_country) \
669 { \
670 QLocale l(req_lc); \
671 QVERIFY2(l.language() == QLocale::exp_lang \
672 && l.country() == QLocale::exp_country, \
673 QString("requested: \"" + QString(req_lc) + "\", got: " \
674 + QLocale::languageToString(l.language()) \
675 + QLatin1Char('/') \
676 + QLocale::countryToString(l.country())).toLatin1().constData()); \
677 }
678
679 TEST_CTOR("mo_MD", Romanian, Moldova)
680 TEST_CTOR("no", NorwegianBokmal, Norway)
681 TEST_CTOR("sh_ME", Serbian, Montenegro)
682 TEST_CTOR("tl", Filipino, Philippines)
683 TEST_CTOR("iw", Hebrew, Israel)
684 TEST_CTOR("in", Indonesian, Indonesia)
685#undef TEST_CTOR
686}
687
688void tst_QLocale::consistentC()
689{
690 const QLocale c(QLocale::C);
691 QCOMPARE(c, QLocale::c());
692 QCOMPARE(c, QLocale(QLocale::C, QLocale::AnyScript, QLocale::AnyCountry));
693 QVERIFY(QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
694 QLocale::AnyCountry).contains(c));
695}
696
697void tst_QLocale::matchingLocales()
698{
699 const QLocale c(QLocale::C);
700 const QLocale ru_RU(QLocale::Russian, QLocale::Russia);
701
702 QList<QLocale> locales = QLocale::matchingLocales(language: QLocale::C, script: QLocale::AnyScript, country: QLocale::AnyCountry);
703 QCOMPARE(locales.size(), 1);
704 QVERIFY(locales.contains(c));
705
706 locales = QLocale::matchingLocales(language: QLocale::Russian, script: QLocale::CyrillicScript, country: QLocale::Russia);
707 QCOMPARE(locales.size(), 1);
708 QVERIFY(locales.contains(ru_RU));
709
710 locales = QLocale::matchingLocales(language: QLocale::Russian, script: QLocale::AnyScript, country: QLocale::AnyCountry);
711 QVERIFY(!locales.isEmpty());
712 QVERIFY(!locales.contains(c));
713 QVERIFY(locales.contains(ru_RU));
714
715 locales = QLocale::matchingLocales(language: QLocale::AnyLanguage, script: QLocale::CyrillicScript, country: QLocale::AnyCountry);
716 QVERIFY(!locales.isEmpty());
717 QVERIFY(!locales.contains(c));
718 QVERIFY(locales.contains(ru_RU));
719
720 locales = QLocale::matchingLocales(language: QLocale::AnyLanguage, script: QLocale::AnyScript, country: QLocale::Russia);
721 QVERIFY(!locales.isEmpty());
722 QVERIFY(!locales.contains(c));
723 QVERIFY(locales.contains(ru_RU));
724}
725
726void tst_QLocale::unixLocaleName_data()
727{
728 QTest::addColumn<QLocale::Language>(name: "lang");
729 QTest::addColumn<QLocale::Country>(name: "land");
730 QTest::addColumn<QString>(name: "expect");
731
732#define ADDROW(nom, lang, land, name) \
733 QTest::newRow(nom) << QLocale::lang << QLocale::land << QStringLiteral(name)
734
735 ADDROW("C_any", C, AnyCountry, "C");
736 ADDROW("en_any", English, AnyCountry, "en_US");
737 ADDROW("en_GB", English, UnitedKingdom, "en_GB");
738 ADDROW("ay_GB", Aymara, UnitedKingdom, "C");
739#undef ADDROW
740}
741
742void tst_QLocale::unixLocaleName()
743{
744 QFETCH(QLocale::Language, lang);
745 QFETCH(QLocale::Country, land);
746 QFETCH(QString, expect);
747
748 QLocale::setDefault(QLocale(QLocale::C));
749
750 QLocale locale(lang, land);
751 QCOMPARE(locale.name(), expect);
752}
753
754void tst_QLocale::toReal_data()
755{
756 QTest::addColumn<QString>(name: "locale_name");
757 QTest::addColumn<QString>(name: "num_str");
758 QTest::addColumn<bool>(name: "good");
759 QTest::addColumn<double>(name: "num");
760
761 QTest::newRow(dataTag: "C 1") << QString("C") << QString("1") << true << 1.0;
762 QTest::newRow(dataTag: "C 1.0") << QString("C") << QString("1.0") << true << 1.0;
763 QTest::newRow(dataTag: "C 1.234") << QString("C") << QString("1.234") << true << 1.234;
764 QTest::newRow(dataTag: "C 1.234e-10") << QString("C") << QString("1.234e-10") << true << 1.234e-10;
765 QTest::newRow(dataTag: "C 1.234E10") << QString("C") << QString("1.234E10") << true << 1.234e10;
766 QTest::newRow(dataTag: "C 1e10") << QString("C") << QString("1e10") << true << 1.0e10;
767 QTest::newRow(dataTag: "C 1e310") << QString("C") << QString("1e310") << false << std::numeric_limits<double>::infinity();
768 QTest::newRow(dataTag: "C 1E310") << QString("C") << QString("1E310") << false << std::numeric_limits<double>::infinity();
769 QTest::newRow(dataTag: "C 1") << QString("C") << QString(" 1") << true << 1.0;
770 QTest::newRow(dataTag: "C 1") << QString("C") << QString(" 1") << true << 1.0;
771 QTest::newRow(dataTag: "C 1 ") << QString("C") << QString("1 ") << true << 1.0;
772 QTest::newRow(dataTag: "C 1 ") << QString("C") << QString("1 ") << true << 1.0;
773
774 QTest::newRow(dataTag: "C 1,") << QString("C") << QString("1,") << false << 0.0;
775 QTest::newRow(dataTag: "C 1,2") << QString("C") << QString("1,2") << false << 0.0;
776 QTest::newRow(dataTag: "C 1,23") << QString("C") << QString("1,23") << false << 0.0;
777 QTest::newRow(dataTag: "C 1,234") << QString("C") << QString("1,234") << true << 1234.0;
778 QTest::newRow(dataTag: "C 1,234,") << QString("C") << QString("1,234,") << false << 0.0;
779 QTest::newRow(dataTag: "C 1,234,5") << QString("C") << QString("1,234,5") << false << 0.0;
780 QTest::newRow(dataTag: "C 1,234,56") << QString("C") << QString("1,234,56") << false << 0.0;
781 QTest::newRow(dataTag: "C 1,234,567") << QString("C") << QString("1,234,567") << true << 1234567.0;
782 QTest::newRow(dataTag: "C 1,234,567.") << QString("C") << QString("1,234,567.") << true << 1234567.0;
783 QTest::newRow(dataTag: "C 1,234,567.8") << QString("C") << QString("1,234,567.8") << true << 1234567.8;
784 QTest::newRow(dataTag: "C 1,234567.8") << QString("C") << QString("1,234567.8") << false << 0.0;
785 QTest::newRow(dataTag: "C 12,34567.8") << QString("C") << QString("12,34567.8") << false << 0.0;
786 QTest::newRow(dataTag: "C 1234,567.8") << QString("C") << QString("1234,567.8") << false << 0.0;
787 QTest::newRow(dataTag: "C 1234567.8") << QString("C") << QString("1234567.8") << true << 1234567.8;
788 QTest::newRow(dataTag: "C ,") << QString("C") << QString(",") << false << 0.0;
789 QTest::newRow(dataTag: "C ,123") << QString("C") << QString(",123") << false << 0.0;
790 QTest::newRow(dataTag: "C ,3") << QString("C") << QString(",3") << false << 0.0;
791 QTest::newRow(dataTag: "C , 3") << QString("C") << QString(", 3") << false << 0.0;
792 QTest::newRow(dataTag: "C , 3") << QString("C") << QString(", 3") << false << 0.0;
793 QTest::newRow(dataTag: "C , 3.2") << QString("C") << QString(", 3.2") << false << 0.0;
794 QTest::newRow(dataTag: "C , 3.2e2") << QString("C") << QString(", 3.2e2") << false << 0.0;
795 QTest::newRow(dataTag: "C , e2") << QString("C") << QString(", e2") << false << 0.0;
796 QTest::newRow(dataTag: "C 1,,234") << QString("C") << QString("1,,234") << false << 0.0;
797
798 QTest::newRow(dataTag: "C empty") << QString("C") << QString("") << false << 0.0;
799 QTest::newRow(dataTag: "C null") << QString("C") << QString() << false << 0.0;
800 QTest::newRow(dataTag: "C .") << QString("C") << QString(".") << false << 0.0;
801 QTest::newRow(dataTag: "C 1e") << QString("C") << QString("1e") << false << 0.0;
802 QTest::newRow(dataTag: "C 1,0") << QString("C") << QString("1,0") << false << 0.0;
803 QTest::newRow(dataTag: "C 1,000") << QString("C") << QString("1,000") << true << 1000.0;
804 QTest::newRow(dataTag: "C 1,000e-6") << QString("C") << QString("1,000e-6") << true << 1000.0e-6;
805 QTest::newRow(dataTag: "C 1e1.0") << QString("C") << QString("1e1.0") << false << 0.0;
806 QTest::newRow(dataTag: "C 1e+") << QString("C") << QString("1e+") << false << 0.0;
807 QTest::newRow(dataTag: "C 1e-") << QString("C") << QString("1e-") << false << 0.0;
808
809 QTest::newRow(dataTag: "C .1") << QString("C") << QString(".1") << true << 0.1;
810 QTest::newRow(dataTag: "C -.1") << QString("C") << QString("-.1") << true << -0.1;
811 QTest::newRow(dataTag: "C 1.") << QString("C") << QString("1.") << true << 1.0;
812 QTest::newRow(dataTag: "C 1.E10") << QString("C") << QString("1.E10") << true << 1.0e10;
813 QTest::newRow(dataTag: "C 1e+10") << QString("C") << QString("1e+10") << true << 1.0e+10;
814
815 QTest::newRow(dataTag: "de_DE 1.") << QString("de_DE") << QString("1.") << false << 0.0;
816 QTest::newRow(dataTag: "de_DE 1.2") << QString("de_DE") << QString("1.2") << false << 0.0;
817 QTest::newRow(dataTag: "de_DE 1.23") << QString("de_DE") << QString("1.23") << false << 0.0;
818 QTest::newRow(dataTag: "de_DE 1.234") << QString("de_DE") << QString("1.234") << true << 1234.0;
819 QTest::newRow(dataTag: "de_DE 1.234,") << QString("de_DE") << QString("1.234.") << false << 0.0;
820 QTest::newRow(dataTag: "de_DE 1.234.5") << QString("de_DE") << QString("1.234.5") << false << 0.0;
821 QTest::newRow(dataTag: "de_DE 1.234.56") << QString("de_DE") << QString("1.234.56") << false << 0.0;
822 QTest::newRow(dataTag: "de_DE 1.234.567") << QString("de_DE") << QString("1.234.567") << true << 1234567.0;
823 QTest::newRow(dataTag: "de_DE 1.234.567,") << QString("de_DE") << QString("1.234.567,") << true << 1234567.0;
824 QTest::newRow(dataTag: "de_DE 1.234.567,8") << QString("de_DE") << QString("1.234.567,8") << true << 1234567.8;
825 QTest::newRow(dataTag: "de_DE 1.234567,8") << QString("de_DE") << QString("1.234567,8") << false << 0.0;
826 QTest::newRow(dataTag: "de_DE 12.34567,8") << QString("de_DE") << QString("12.34567,8") << false << 0.0;
827 QTest::newRow(dataTag: "de_DE 1234.567,8") << QString("de_DE") << QString("1234.567,8") << false << 0.0;
828 QTest::newRow(dataTag: "de_DE 1234567,8") << QString("de_DE") << QString("1234567,8") << true << 1234567.8;
829 QTest::newRow(dataTag: "de_DE .123") << QString("de_DE") << QString(".123") << false << 0.0;
830 QTest::newRow(dataTag: "de_DE .3") << QString("de_DE") << QString(".3") << false << 0.0;
831 QTest::newRow(dataTag: "de_DE . 3") << QString("de_DE") << QString(". 3") << false << 0.0;
832 QTest::newRow(dataTag: "de_DE . 3") << QString("de_DE") << QString(". 3") << false << 0.0;
833 QTest::newRow(dataTag: "de_DE . 3,2") << QString("de_DE") << QString(". 3,2") << false << 0.0;
834 QTest::newRow(dataTag: "de_DE . 3,2e2") << QString("de_DE") << QString(". 3,2e2") << false << 0.0;
835 QTest::newRow(dataTag: "de_DE . e2") << QString("de_DE") << QString(". e2") << false << 0.0;
836 QTest::newRow(dataTag: "de_DE 1..234") << QString("de_DE") << QString("1..234") << false << 0.0;
837
838 QTest::newRow(dataTag: "de_DE 1") << QString("de_DE") << QString("1") << true << 1.0;
839 QTest::newRow(dataTag: "de_DE 1.0") << QString("de_DE") << QString("1.0") << false << 0.0;
840 QTest::newRow(dataTag: "de_DE 1.234e-10") << QString("de_DE") << QString("1.234e-10") << true << 1234.0e-10;
841 QTest::newRow(dataTag: "de_DE 1.234E10") << QString("de_DE") << QString("1.234E10") << true << 1234.0e10;
842 QTest::newRow(dataTag: "de_DE 1e10") << QString("de_DE") << QString("1e10") << true << 1.0e10;
843 QTest::newRow(dataTag: "de_DE .1") << QString("de_DE") << QString(".1") << false << 0.0;
844 QTest::newRow(dataTag: "de_DE -.1") << QString("de_DE") << QString("-.1") << false << 0.0;
845 QTest::newRow(dataTag: "de_DE 1.E10") << QString("de_DE") << QString("1.E10") << false << 0.0;
846 QTest::newRow(dataTag: "de_DE 1e+10") << QString("de_DE") << QString("1e+10") << true << 1.0e+10;
847
848 QTest::newRow(dataTag: "de_DE 1,0") << QString("de_DE") << QString("1,0") << true << 1.0;
849 QTest::newRow(dataTag: "de_DE 1,234") << QString("de_DE") << QString("1,234") << true << 1.234;
850 QTest::newRow(dataTag: "de_DE 1,234e-10") << QString("de_DE") << QString("1,234e-10") << true << 1.234e-10;
851 QTest::newRow(dataTag: "de_DE 1,234E10") << QString("de_DE") << QString("1,234E10") << true << 1.234e10;
852 QTest::newRow(dataTag: "de_DE ,1") << QString("de_DE") << QString(",1") << true << 0.1;
853 QTest::newRow(dataTag: "de_DE -,1") << QString("de_DE") << QString("-,1") << true << -0.1;
854 QTest::newRow(dataTag: "de_DE 1,") << QString("de_DE") << QString("1,") << true << 1.0;
855 QTest::newRow(dataTag: "de_DE 1,E10") << QString("de_DE") << QString("1,E10") << true << 1.0e10;
856
857 QTest::newRow(dataTag: "de_DE empty") << QString("de_DE") << QString("") << false << 0.0;
858 QTest::newRow(dataTag: "de_DE null") << QString("de_DE") << QString() << false << 0.0;
859 QTest::newRow(dataTag: "de_DE .") << QString("de_DE") << QString(".") << false << 0.0;
860 QTest::newRow(dataTag: "de_DE 1e") << QString("de_DE") << QString("1e") << false << 0.0;
861 QTest::newRow(dataTag: "de_DE 1e1.0") << QString("de_DE") << QString("1e1.0") << false << 0.0;
862 QTest::newRow(dataTag: "de_DE 1e+") << QString("de_DE") << QString("1e+") << false << 0.0;
863 QTest::newRow(dataTag: "de_DE 1e-") << QString("de_DE") << QString("1e-") << false << 0.0;
864
865 QTest::newRow(dataTag: "C 9,876543") << QString("C") << QString("9,876543") << false << 0.0;
866 QTest::newRow(dataTag: "C 9,876543.2") << QString("C") << QString("9,876543.2") << false << 0.0;
867 QTest::newRow(dataTag: "C 9,876543e-2") << QString("C") << QString("9,876543e-2") << false << 0.0;
868 QTest::newRow(dataTag: "C 9,876543.0e-2") << QString("C") << QString("9,876543.0e-2") << false << 0.0;
869
870 QTest::newRow(dataTag: "de_DE 9.876543") << QString("de_DE") << QString("9876.543") << false << 0.0;
871 QTest::newRow(dataTag: "de_DE 9.876543,2") << QString("de_DE") << QString("9.876543,2") << false << 0.0;
872 QTest::newRow(dataTag: "de_DE 9.876543e-2") << QString("de_DE") << QString("9.876543e-2") << false << 0.0;
873 QTest::newRow(dataTag: "de_DE 9.876543,0e-2") << QString("de_DE") << QString("9.876543,0e-2") << false << 0.0;
874 QTest::newRow(dataTag: "de_DE 9.876543e--2") << QString("de_DE") << QString("9.876543e")+QChar(8722)+QString("2") << false << 0.0;
875 QTest::newRow(dataTag: "de_DE 9.876543,0e--2") << QString("de_DE") << QString("9.876543,0e")+QChar(8722)+QString("2") << false << 0.0;
876}
877
878void tst_QLocale::stringToDouble_data()
879{
880 toReal_data();
881 if (std::numeric_limits<double>::has_infinity) {
882 double huge = std::numeric_limits<double>::infinity();
883 QTest::newRow(dataTag: "C inf") << QString("C") << QString("inf") << true << huge;
884 QTest::newRow(dataTag: "C +inf") << QString("C") << QString("+inf") << true << +huge;
885 QTest::newRow(dataTag: "C -inf") << QString("C") << QString("-inf") << true << -huge;
886 // Overflow:
887 QTest::newRow(dataTag: "C huge") << QString("C") << QString("2e308") << false << huge;
888 QTest::newRow(dataTag: "C -huge") << QString("C") << QString("-2e308") << false << -huge;
889 }
890 if (std::numeric_limits<double>::has_quiet_NaN)
891 QTest::newRow(dataTag: "C qnan") << QString("C") << QString("NaN") << true << std::numeric_limits<double>::quiet_NaN();
892
893 // In range (but outside float's range):
894 QTest::newRow(dataTag: "C big") << QString("C") << QString("3.5e38") << true << 3.5e38;
895 QTest::newRow(dataTag: "C -big") << QString("C") << QString("-3.5e38") << true << -3.5e38;
896 QTest::newRow(dataTag: "C small") << QString("C") << QString("1e-45") << true << 1e-45;
897 QTest::newRow(dataTag: "C -small") << QString("C") << QString("-1e-45") << true << -1e-45;
898
899 // Underflow:
900 QTest::newRow(dataTag: "C tiny") << QString("C") << QString("2e-324") << false << 0.;
901 QTest::newRow(dataTag: "C -tiny") << QString("C") << QString("-2e-324") << false << 0.;
902}
903
904void tst_QLocale::stringToDouble()
905{
906#define MY_DOUBLE_EPSILON (2.22045e-16) // 1/2^{52}; double has a 53-bit mantissa
907
908 QFETCH(QString, locale_name);
909 QFETCH(QString, num_str);
910 QFETCH(bool, good);
911 QFETCH(double, num);
912 QStringRef num_strRef = num_str.leftRef(n: -1);
913
914 QLocale locale(locale_name);
915 QCOMPARE(locale.name(), locale_name);
916
917 bool ok;
918 double d = locale.toDouble(s: num_str, ok: &ok);
919 QCOMPARE(ok, good);
920
921 {
922 // Make sure result is independent of locale:
923 TransientLocale ignoreme(LC_ALL, "ar_SA");
924 QCOMPARE(locale.toDouble(num_str, &ok), d);
925 QCOMPARE(ok, good);
926 }
927
928 if (ok || std::isinf(x: num)) {
929 // First use fuzzy-compare, then a more precise check:
930 QCOMPARE(d, num);
931 if (std::isfinite(x: num)) {
932 double diff = d > num ? d - num : num - d;
933 QVERIFY(diff <= MY_DOUBLE_EPSILON);
934 }
935 }
936
937 d = locale.toDouble(s: num_strRef, ok: &ok);
938 QCOMPARE(ok, good);
939
940 if (ok || std::isinf(x: num)) {
941 QCOMPARE(d, num);
942 if (std::isfinite(x: num)) {
943 double diff = d > num ? d - num : num - d;
944 QVERIFY(diff <= MY_DOUBLE_EPSILON);
945 }
946 }
947#undef MY_DOUBLE_EPSILON
948}
949
950void tst_QLocale::stringToFloat_data()
951{
952 using Bounds = std::numeric_limits<float>;
953 toReal_data();
954 const QString C(QStringLiteral("C"));
955 if (Bounds::has_infinity) {
956 double huge = Bounds::infinity();
957 QTest::newRow(dataTag: "C inf") << C << QString("inf") << true << huge;
958 QTest::newRow(dataTag: "C +inf") << C << QString("+inf") << true << +huge;
959 QTest::newRow(dataTag: "C -inf") << C << QString("-inf") << true << -huge;
960 // Overflow float, but not double:
961 QTest::newRow(dataTag: "C big") << C << QString("3.5e38") << false << huge;
962 QTest::newRow(dataTag: "C -big") << C << QString("-3.5e38") << false << -huge;
963 // Overflow double, too:
964 QTest::newRow(dataTag: "C huge") << C << QString("2e308") << false << huge;
965 QTest::newRow(dataTag: "C -huge") << C << QString("-2e308") << false << -huge;
966 }
967 if (Bounds::has_quiet_NaN)
968 QTest::newRow(dataTag: "C qnan") << C << QString("NaN") << true << double(Bounds::quiet_NaN());
969
970 // Minimal float: shouldn't underflow
971 QTest::newRow(dataTag: "C float min")
972 << C << QLocale::c().toString(i: Bounds::denorm_min()) << true << double(Bounds::denorm_min());
973 QTest::newRow(dataTag: "C float -min")
974 << C << QLocale::c().toString(i: -Bounds::denorm_min()) << true << -double(Bounds::denorm_min());
975
976 // Underflow float, but not double:
977 QTest::newRow(dataTag: "C small") << C << QString("7e-46") << false << 0.;
978 QTest::newRow(dataTag: "C -small") << C << QString("-7e-46") << false << 0.;
979 using Double = std::numeric_limits<double>;
980 QTest::newRow(dataTag: "C double min")
981 << C << QLocale::c().toString(i: Double::denorm_min()) << false << 0.0;
982 QTest::newRow(dataTag: "C double -min")
983 << C << QLocale::c().toString(i: -Double::denorm_min()) << false << 0.0;
984
985 // Underflow double, too:
986 QTest::newRow(dataTag: "C tiny") << C << QString("2e-324") << false << 0.;
987 QTest::newRow(dataTag: "C -tiny") << C << QString("-2e-324") << false << 0.;
988}
989
990void tst_QLocale::stringToFloat()
991{
992#define MY_FLOAT_EPSILON (2.384e-7) // 1/2^{22}; float has a 23-bit mantissa
993
994 QFETCH(QString, locale_name);
995 QFETCH(QString, num_str);
996 QFETCH(bool, good);
997 QFETCH(double, num);
998 QStringRef num_strRef = num_str.leftRef(n: -1);
999 float fnum = num;
1000
1001 QLocale locale(locale_name);
1002 QCOMPARE(locale.name(), locale_name);
1003
1004 bool ok;
1005 float f = locale.toFloat(s: num_str, ok: &ok);
1006 QCOMPARE(ok, good);
1007
1008 {
1009 // Make sure result is independent of locale:
1010 TransientLocale ignoreme(LC_ALL, "ar_SA");
1011 QCOMPARE(locale.toFloat(num_str, &ok), f);
1012 QCOMPARE(ok, good);
1013 }
1014
1015 if (ok || std::isinf(x: fnum)) {
1016 // First use fuzzy-compare, then a more precise check:
1017 QCOMPARE(f, fnum);
1018 if (std::isfinite(x: fnum)) {
1019 float diff = f > fnum ? f - fnum : fnum - f;
1020 QVERIFY(diff <= MY_FLOAT_EPSILON);
1021 }
1022 }
1023
1024 f = locale.toFloat(s: num_strRef, ok: &ok);
1025 QCOMPARE(ok, good);
1026
1027 if (ok || std::isinf(x: fnum)) {
1028 QCOMPARE(f, fnum);
1029 if (std::isfinite(x: fnum)) {
1030 float diff = f > fnum ? f - fnum : fnum - f;
1031 QVERIFY(diff <= MY_FLOAT_EPSILON);
1032 }
1033 }
1034#undef MY_FLOAT_EPSILON
1035}
1036
1037void tst_QLocale::doubleToString_data()
1038{
1039 QTest::addColumn<QString>(name: "locale_name");
1040 QTest::addColumn<QString>(name: "num_str");
1041 QTest::addColumn<double>(name: "num");
1042 QTest::addColumn<char>(name: "mode");
1043 QTest::addColumn<int>(name: "precision");
1044
1045 int shortest = QLocale::FloatingPointShortest;
1046
1047 QTest::newRow(dataTag: "C 3.4 f 5") << QString("C") << QString("3.40000") << 3.4 << 'f' << 5;
1048 QTest::newRow(dataTag: "C 3.4 f 0") << QString("C") << QString("3") << 3.4 << 'f' << 0;
1049 QTest::newRow(dataTag: "C 3.4 e 5") << QString("C") << QString("3.40000e+00") << 3.4 << 'e' << 5;
1050 QTest::newRow(dataTag: "C 3.4 e 0") << QString("C") << QString("3e+00") << 3.4 << 'e' << 0;
1051 QTest::newRow(dataTag: "C 3.4 g 5") << QString("C") << QString("3.4") << 3.4 << 'g' << 5;
1052 QTest::newRow(dataTag: "C 3.4 g 1") << QString("C") << QString("3") << 3.4 << 'g' << 1;
1053
1054 QTest::newRow(dataTag: "C 3.4 f 1") << QString("C") << QString("3.4") << 3.4 << 'f' << 1;
1055 QTest::newRow(dataTag: "C 3.4 f -") << QString("C") << QString("3.4") << 3.4 << 'f' << shortest;
1056 QTest::newRow(dataTag: "C 3.4 e 1") << QString("C") << QString("3.4e+00") << 3.4 << 'e' << 1;
1057 QTest::newRow(dataTag: "C 3.4 e -") << QString("C") << QString("3.4e+00") << 3.4 << 'e' << shortest;
1058 QTest::newRow(dataTag: "C 3.4 g 2") << QString("C") << QString("3.4") << 3.4 << 'g' << 2;
1059 QTest::newRow(dataTag: "C 3.4 g -") << QString("C") << QString("3.4") << 3.4 << 'g' << shortest;
1060
1061 QTest::newRow(dataTag: "de_DE 3,4 f 1") << QString("de_DE") << QString("3,4") << 3.4 << 'f' << 1;
1062 QTest::newRow(dataTag: "de_DE 3,4 f -") << QString("de_DE") << QString("3,4") << 3.4 << 'f' << shortest;
1063 QTest::newRow(dataTag: "de_DE 3,4 e 1") << QString("de_DE") << QString("3,4e+00") << 3.4 << 'e' << 1;
1064 QTest::newRow(dataTag: "de_DE 3,4 e -") << QString("de_DE") << QString("3,4e+00") << 3.4 << 'e' << shortest;
1065 QTest::newRow(dataTag: "de_DE 3,4 g 2") << QString("de_DE") << QString("3,4") << 3.4 << 'g' << 2;
1066 QTest::newRow(dataTag: "de_DE 3,4 g -") << QString("de_DE") << QString("3,4") << 3.4 << 'g' << shortest;
1067
1068 QTest::newRow(dataTag: "C 0.035003945 f 12") << QString("C") << QString("0.035003945000") << 0.035003945 << 'f' << 12;
1069 QTest::newRow(dataTag: "C 0.035003945 f 6") << QString("C") << QString("0.035004") << 0.035003945 << 'f' << 6;
1070 QTest::newRow(dataTag: "C 0.035003945 e 10") << QString("C") << QString("3.5003945000e-02") << 0.035003945 << 'e' << 10;
1071 QTest::newRow(dataTag: "C 0.035003945 e 4") << QString("C") << QString("3.5004e-02") << 0.035003945 << 'e' << 4;
1072 QTest::newRow(dataTag: "C 0.035003945 g 11") << QString("C") << QString("0.035003945") << 0.035003945 << 'g' << 11;
1073 QTest::newRow(dataTag: "C 0.035003945 g 5") << QString("C") << QString("0.035004") << 0.035003945 << 'g' << 5;
1074
1075 QTest::newRow(dataTag: "C 0.035003945 f 9") << QString("C") << QString("0.035003945") << 0.035003945 << 'f' << 9;
1076 QTest::newRow(dataTag: "C 0.035003945 f -") << QString("C") << QString("0.035003945") << 0.035003945 << 'f' << shortest;
1077 QTest::newRow(dataTag: "C 0.035003945 e 7") << QString("C") << QString("3.5003945e-02") << 0.035003945 << 'e' << 7;
1078 QTest::newRow(dataTag: "C 0.035003945 e -") << QString("C") << QString("3.5003945e-02") << 0.035003945 << 'e' << shortest;
1079 QTest::newRow(dataTag: "C 0.035003945 g 8") << QString("C") << QString("0.035003945") << 0.035003945 << 'g' << 8;
1080 QTest::newRow(dataTag: "C 0.035003945 g -") << QString("C") << QString("0.035003945") << 0.035003945 << 'g' << shortest;
1081
1082 QTest::newRow(dataTag: "de_DE 0,035003945 f 9") << QString("de_DE") << QString("0,035003945") << 0.035003945 << 'f' << 9;
1083 QTest::newRow(dataTag: "de_DE 0,035003945 f -") << QString("de_DE") << QString("0,035003945") << 0.035003945 << 'f' << shortest;
1084 QTest::newRow(dataTag: "de_DE 0,035003945 e 7") << QString("de_DE") << QString("3,5003945e-02") << 0.035003945 << 'e' << 7;
1085 QTest::newRow(dataTag: "de_DE 0,035003945 e -") << QString("de_DE") << QString("3,5003945e-02") << 0.035003945 << 'e' << shortest;
1086 QTest::newRow(dataTag: "de_DE 0,035003945 g 8") << QString("de_DE") << QString("0,035003945") << 0.035003945 << 'g' << 8;
1087 QTest::newRow(dataTag: "de_DE 0,035003945 g -") << QString("de_DE") << QString("0,035003945") << 0.035003945 << 'g' << shortest;
1088
1089 QTest::newRow(dataTag: "C 0.000003945 f 12") << QString("C") << QString("0.000003945000") << 0.000003945 << 'f' << 12;
1090 QTest::newRow(dataTag: "C 0.000003945 f 6") << QString("C") << QString("0.000004") << 0.000003945 << 'f' << 6;
1091 QTest::newRow(dataTag: "C 0.000003945 e 6") << QString("C") << QString("3.945000e-06") << 0.000003945 << 'e' << 6;
1092 QTest::newRow(dataTag: "C 0.000003945 e 0") << QString("C") << QString("4e-06") << 0.000003945 << 'e' << 0;
1093 QTest::newRow(dataTag: "C 0.000003945 g 7") << QString("C") << QString("3.945e-06") << 0.000003945 << 'g' << 7;
1094 QTest::newRow(dataTag: "C 0.000003945 g 1") << QString("C") << QString("4e-06") << 0.000003945 << 'g' << 1;
1095
1096 QTest::newRow(dataTag: "C 0.000003945 f 9") << QString("C") << QString("0.000003945") << 0.000003945 << 'f' << 9;
1097 QTest::newRow(dataTag: "C 0.000003945 f -") << QString("C") << QString("0.000003945") << 0.000003945 << 'f' << shortest;
1098 QTest::newRow(dataTag: "C 0.000003945 e 3") << QString("C") << QString("3.945e-06") << 0.000003945 << 'e' << 3;
1099 QTest::newRow(dataTag: "C 0.000003945 e -") << QString("C") << QString("3.945e-06") << 0.000003945 << 'e' << shortest;
1100 QTest::newRow(dataTag: "C 0.000003945 g 4") << QString("C") << QString("3.945e-06") << 0.000003945 << 'g' << 4;
1101 QTest::newRow(dataTag: "C 0.000003945 g -") << QString("C") << QString("3.945e-06") << 0.000003945 << 'g' << shortest;
1102
1103 QTest::newRow(dataTag: "de_DE 0,000003945 f 9") << QString("de_DE") << QString("0,000003945") << 0.000003945 << 'f' << 9;
1104 QTest::newRow(dataTag: "de_DE 0,000003945 f -") << QString("de_DE") << QString("0,000003945") << 0.000003945 << 'f' << shortest;
1105 QTest::newRow(dataTag: "de_DE 0,000003945 e 3") << QString("de_DE") << QString("3,945e-06") << 0.000003945 << 'e' << 3;
1106 QTest::newRow(dataTag: "de_DE 0,000003945 e -") << QString("de_DE") << QString("3,945e-06") << 0.000003945 << 'e' << shortest;
1107 QTest::newRow(dataTag: "de_DE 0,000003945 g 4") << QString("de_DE") << QString("3,945e-06") << 0.000003945 << 'g' << 4;
1108 QTest::newRow(dataTag: "de_DE 0,000003945 g -") << QString("de_DE") << QString("3,945e-06") << 0.000003945 << 'g' << shortest;
1109
1110 QTest::newRow(dataTag: "C 12456789012 f 3") << QString("C") << QString("12456789012.000") << 12456789012.0 << 'f' << 3;
1111 QTest::newRow(dataTag: "C 12456789012 e 13") << QString("C") << QString("1.2456789012000e+10") << 12456789012.0 << 'e' << 13;
1112 QTest::newRow(dataTag: "C 12456789012 e 7") << QString("C") << QString("1.2456789e+10") << 12456789012.0 << 'e' << 7;
1113 QTest::newRow(dataTag: "C 12456789012 g 14") << QString("C") << QString("12456789012") << 12456789012.0 << 'g' << 14;
1114 QTest::newRow(dataTag: "C 12456789012 g 8") << QString("C") << QString("1.2456789e+10") << 12456789012.0 << 'g' << 8;
1115
1116 QTest::newRow(dataTag: "C 12456789012 f 0") << QString("C") << QString("12456789012") << 12456789012.0 << 'f' << 0;
1117 QTest::newRow(dataTag: "C 12456789012 f -") << QString("C") << QString("12456789012") << 12456789012.0 << 'f' << shortest;
1118 QTest::newRow(dataTag: "C 12456789012 e 10") << QString("C") << QString("1.2456789012e+10") << 12456789012.0 << 'e' << 10;
1119 QTest::newRow(dataTag: "C 12456789012 e -") << QString("C") << QString("1.2456789012e+10") << 12456789012.0 << 'e' << shortest;
1120 QTest::newRow(dataTag: "C 12456789012 g 11") << QString("C") << QString("12456789012") << 12456789012.0 << 'g' << 11;
1121 QTest::newRow(dataTag: "C 12456789012 g -") << QString("C") << QString("12456789012") << 12456789012.0 << 'g' << shortest;
1122
1123 QTest::newRow(dataTag: "de_DE 12456789012 f 0") << QString("de_DE") << QString("12.456.789.012") << 12456789012.0 << 'f' << 0;
1124 QTest::newRow(dataTag: "de_DE 12456789012 f -") << QString("de_DE") << QString("12.456.789.012") << 12456789012.0 << 'f' << shortest;
1125 QTest::newRow(dataTag: "de_DE 12456789012 e 10") << QString("de_DE") << QString("1,2456789012e+10") << 12456789012.0 << 'e' << 10;
1126 QTest::newRow(dataTag: "de_DE 12456789012 e -") << QString("de_DE") << QString("1,2456789012e+10") << 12456789012.0 << 'e' << shortest;
1127 QTest::newRow(dataTag: "de_DE 12456789012 g 11") << QString("de_DE") << QString("12.456.789.012") << 12456789012.0 << 'g' << 11;
1128 QTest::newRow(dataTag: "de_DE 12456789012 g -") << QString("de_DE") << QString("12.456.789.012") << 12456789012.0 << 'g' << shortest;
1129}
1130
1131void tst_QLocale::doubleToString()
1132{
1133 QFETCH(QString, locale_name);
1134 QFETCH(QString, num_str);
1135 QFETCH(double, num);
1136 QFETCH(char, mode);
1137 QFETCH(int, precision);
1138
1139#ifdef QT_NO_DOUBLECONVERSION
1140 if (precision == QLocale::FloatingPointShortest)
1141 QSKIP("'Shortest' double conversion is not that short without libdouble-conversion");
1142#endif
1143
1144 const QLocale locale(locale_name);
1145 QCOMPARE(locale.toString(num, mode, precision), num_str);
1146
1147 TransientLocale ignoreme(LC_ALL, "de_DE");
1148 QCOMPARE(locale.toString(num, mode, precision), num_str);
1149}
1150
1151void tst_QLocale::strtod_data()
1152{
1153 QTest::addColumn<QString>(name: "num_str");
1154 QTest::addColumn<double>(name: "num");
1155 QTest::addColumn<int>(name: "processed");
1156 QTest::addColumn<bool>(name: "ok");
1157
1158 // plain numbers, success
1159 QTest::newRow(dataTag: "0") << QString("0") << 0.0 << 1 << true;
1160 QTest::newRow(dataTag: "0.") << QString("0.") << 0.0 << 2 << true;
1161 QTest::newRow(dataTag: "0.0") << QString("0.0") << 0.0 << 3 << true;
1162 QTest::newRow(dataTag: "0e+0") << QString("0e+0") << 0.0 << 4 << true;
1163 QTest::newRow(dataTag: "0e-0") << QString("0e-0") << 0.0 << 4 << true;
1164 QTest::newRow(dataTag: "0e+1") << QString("0e+1") << 0.0 << 4 << true;
1165 QTest::newRow(dataTag: "0e-1") << QString("0e-1") << 0.0 << 4 << true;
1166 QTest::newRow(dataTag: "0E+0") << QString("0E+0") << 0.0 << 4 << true;
1167 QTest::newRow(dataTag: "0E-0") << QString("0E-0") << 0.0 << 4 << true;
1168 QTest::newRow(dataTag: "0E+1") << QString("0E+1") << 0.0 << 4 << true;
1169 QTest::newRow(dataTag: "0E-1") << QString("0E-1") << 0.0 << 4 << true;
1170 QTest::newRow(dataTag: "3.4") << QString("3.4") << 3.4 << 3 << true;
1171 QTest::newRow(dataTag: "0.035003945") << QString("0.035003945") << 0.035003945 << 11 << true;
1172 QTest::newRow(dataTag: "3.5003945e-2") << QString("3.5003945e-2") << 0.035003945 << 12 << true;
1173 QTest::newRow(dataTag: "0.000003945") << QString("0.000003945") << 0.000003945 << 11 << true;
1174 QTest::newRow(dataTag: "3.945e-6") << QString("3.945e-6") << 0.000003945 << 8 << true;
1175 QTest::newRow(dataTag: "12456789012") << QString("12456789012") << 12456789012.0 << 11 << true;
1176 QTest::newRow(dataTag: "1.2456789012e10") << QString("1.2456789012e10") << 12456789012.0 << 15 << true;
1177
1178 // starts with junk, fails
1179 QTest::newRow(dataTag: "a0") << QString("a0") << 0.0 << 0 << false;
1180 QTest::newRow(dataTag: "a0.") << QString("a0.") << 0.0 << 0 << false;
1181 QTest::newRow(dataTag: "a0.0") << QString("a0.0") << 0.0 << 0 << false;
1182 QTest::newRow(dataTag: "a3.4") << QString("a3.4") << 0.0 << 0 << false;
1183 QTest::newRow(dataTag: "b0.035003945") << QString("b0.035003945") << 0.0 << 0 << false;
1184 QTest::newRow(dataTag: "c3.5003945e-2") << QString("c3.5003945e-2") << 0.0 << 0 << false;
1185 QTest::newRow(dataTag: "d0.000003945") << QString("d0.000003945") << 0.0 << 0 << false;
1186 QTest::newRow(dataTag: "e3.945e-6") << QString("e3.945e-6") << 0.0 << 0 << false;
1187 QTest::newRow(dataTag: "f12456789012") << QString("f12456789012") << 0.0 << 0 << false;
1188 QTest::newRow(dataTag: "g1.2456789012e10") << QString("g1.2456789012e10") << 0.0 << 0 << false;
1189
1190 // ends with junk, success
1191 QTest::newRow(dataTag: "0a") << QString("0a") << 0.0 << 1 << true;
1192 QTest::newRow(dataTag: "0.a") << QString("0.a") << 0.0 << 2 << true;
1193 QTest::newRow(dataTag: "0.0a") << QString("0.0a") << 0.0 << 3 << true;
1194 QTest::newRow(dataTag: "0e+0a") << QString("0e+0a") << 0.0 << 4 << true;
1195 QTest::newRow(dataTag: "0e-0a") << QString("0e-0a") << 0.0 << 4 << true;
1196 QTest::newRow(dataTag: "0e+1a") << QString("0e+1a") << 0.0 << 4 << true;
1197 QTest::newRow(dataTag: "0e-1a") << QString("0e-1a") << 0.0 << 4 << true;
1198 QTest::newRow(dataTag: "0E+0a") << QString("0E+0a") << 0.0 << 4 << true;
1199 QTest::newRow(dataTag: "0E-0a") << QString("0E-0a") << 0.0 << 4 << true;
1200 QTest::newRow(dataTag: "0E+1a") << QString("0E+1a") << 0.0 << 4 << true;
1201 QTest::newRow(dataTag: "0E-1a") << QString("0E-1a") << 0.0 << 4 << true;
1202 QTest::newRow(dataTag: "0.035003945b") << QString("0.035003945b") << 0.035003945 << 11 << true;
1203 QTest::newRow(dataTag: "3.5003945e-2c") << QString("3.5003945e-2c") << 0.035003945 << 12 << true;
1204 QTest::newRow(dataTag: "0.000003945d") << QString("0.000003945d") << 0.000003945 << 11 << true;
1205 QTest::newRow(dataTag: "3.945e-6e") << QString("3.945e-6e") << 0.000003945 << 8 << true;
1206 QTest::newRow(dataTag: "12456789012f") << QString("12456789012f") << 12456789012.0 << 11 << true;
1207 QTest::newRow(dataTag: "1.2456789012e10g") << QString("1.2456789012e10g") << 12456789012.0 << 15 << true;
1208
1209 // "0x" prefix, success but only for the "0" before "x"
1210 QTest::newRow(dataTag: "0x0") << QString("0x0") << 0.0 << 1 << true;
1211 QTest::newRow(dataTag: "0x0.") << QString("0x0.") << 0.0 << 1 << true;
1212 QTest::newRow(dataTag: "0x0.0") << QString("0x0.0") << 0.0 << 1 << true;
1213 QTest::newRow(dataTag: "0x3.4") << QString("0x3.4") << 0.0 << 1 << true;
1214 QTest::newRow(dataTag: "0x0.035003945") << QString("0x0.035003945") << 0.0 << 1 << true;
1215 QTest::newRow(dataTag: "0x3.5003945e-2") << QString("0x3.5003945e-2") << 0.0 << 1 << true;
1216 QTest::newRow(dataTag: "0x0.000003945") << QString("0x0.000003945") << 0.0 << 1 << true;
1217 QTest::newRow(dataTag: "0x3.945e-6") << QString("0x3.945e-6") << 0.0 << 1 << true;
1218 QTest::newRow(dataTag: "0x12456789012") << QString("0x12456789012") << 0.0 << 1 << true;
1219 QTest::newRow(dataTag: "0x1.2456789012e10") << QString("0x1.2456789012e10") << 0.0 << 1 << true;
1220
1221 // hexfloat is not supported (yet)
1222 QTest::newRow(dataTag: "0x1.921fb5p+1") << QString("0x1.921fb5p+1") << 0.0 << 1 << true;
1223}
1224
1225void tst_QLocale::strtod()
1226{
1227 QFETCH(QString, num_str);
1228 QFETCH(double, num);
1229 QFETCH(int, processed);
1230 QFETCH(bool, ok);
1231
1232 QByteArray numData = num_str.toLatin1();
1233 const char *end = 0;
1234 bool actualOk = false;
1235 double result = qstrtod(s00: numData.constData(), se: &end, ok: &actualOk);
1236
1237 QCOMPARE(result, num);
1238 QCOMPARE(actualOk, ok);
1239 QCOMPARE(static_cast<int>(end - numData.constData()), processed);
1240
1241 // make sure neither QByteArray, QString or QLocale also work
1242 // (but they don't support incomplete parsing)
1243 if (processed == num_str.size() || processed == 0) {
1244 actualOk = false;
1245 QCOMPARE(num_str.toDouble(&actualOk), num);
1246 QCOMPARE(actualOk, ok);
1247
1248 actualOk = false;
1249 QCOMPARE(numData.toDouble(&actualOk), num);
1250 QCOMPARE(actualOk, ok);
1251
1252 actualOk = false;
1253 QCOMPARE(QLocale::c().toDouble(num_str, &actualOk), num);
1254 QCOMPARE(actualOk, ok);
1255 }
1256
1257 // and QStringRef, but we can limit the length without allocating memory
1258 QStringRef num_strref(&num_str, 0, processed);
1259 actualOk = false;
1260 QCOMPARE(QLocale::c().toDouble(num_strref, &actualOk), num);
1261 QCOMPARE(actualOk, ok);
1262}
1263
1264void tst_QLocale::long_long_conversion_data()
1265{
1266 QTest::addColumn<QString>(name: "locale_name");
1267 QTest::addColumn<QString>(name: "num_str");
1268 QTest::addColumn<bool>(name: "good");
1269 QTest::addColumn<qlonglong>(name: "num");
1270
1271 QTest::newRow(dataTag: "C null") << QString("C") << QString() << false << (qlonglong) 0;
1272 QTest::newRow(dataTag: "C empty") << QString("C") << QString("") << false << (qlonglong) 0;
1273 QTest::newRow(dataTag: "C 1") << QString("C") << "1" << true << (qlonglong) 1;
1274 QTest::newRow(dataTag: "C 1,") << QString("C") << "1," << false << (qlonglong) 0;
1275 QTest::newRow(dataTag: "C 1,2") << QString("C") << "1,2" << false << (qlonglong) 0;
1276 QTest::newRow(dataTag: "C 1,23") << QString("C") << "1,23" << false << (qlonglong) 0;
1277 QTest::newRow(dataTag: "C 1,234") << QString("C") << "1,234" << true << (qlonglong) 1234;
1278 QTest::newRow(dataTag: "C 1234567") << QString("C") << "1234567" << true << (qlonglong) 1234567;
1279 QTest::newRow(dataTag: "C 1,234567") << QString("C") << "1,234567" << false << (qlonglong) 0;
1280 QTest::newRow(dataTag: "C 12,34567") << QString("C") << "12,34567" << false << (qlonglong) 0;
1281 QTest::newRow(dataTag: "C 123,4567") << QString("C") << "123,4567" << false << (qlonglong) 0;
1282 QTest::newRow(dataTag: "C 1234,567") << QString("C") << "1234,567" << false << (qlonglong) 0;
1283 QTest::newRow(dataTag: "C 12345,67") << QString("C") << "12345,67" << false << (qlonglong) 0;
1284 QTest::newRow(dataTag: "C 123456,7") << QString("C") << "123456,7" << false << (qlonglong) 0;
1285 QTest::newRow(dataTag: "C 1,234,567") << QString("C") << "1,234,567" << true << (qlonglong) 1234567;
1286
1287 QTest::newRow(dataTag: "de_DE 1") << QString("de_DE") << "1" << true << (qlonglong) 1;
1288 QTest::newRow(dataTag: "de_DE 1.") << QString("de_DE") << "1." << false << (qlonglong) 0;
1289 QTest::newRow(dataTag: "de_DE 1.2") << QString("de_DE") << "1.2" << false << (qlonglong) 0;
1290 QTest::newRow(dataTag: "de_DE 1.23") << QString("de_DE") << "1.23" << false << (qlonglong) 0;
1291 QTest::newRow(dataTag: "de_DE 1.234") << QString("de_DE") << "1.234" << true << (qlonglong) 1234;
1292 QTest::newRow(dataTag: "de_DE 1234567") << QString("de_DE") << "1234567" << true << (qlonglong) 1234567;
1293 QTest::newRow(dataTag: "de_DE 1.234567") << QString("de_DE") << "1.234567" << false << (qlonglong) 0;
1294 QTest::newRow(dataTag: "de_DE 12.34567") << QString("de_DE") << "12.34567" << false << (qlonglong) 0;
1295 QTest::newRow(dataTag: "de_DE 123.4567") << QString("de_DE") << "123.4567" << false << (qlonglong) 0;
1296 QTest::newRow(dataTag: "de_DE 1234.567") << QString("de_DE") << "1234.567" << false << (qlonglong) 0;
1297 QTest::newRow(dataTag: "de_DE 12345.67") << QString("de_DE") << "12345.67" << false << (qlonglong) 0;
1298 QTest::newRow(dataTag: "de_DE 123456.7") << QString("de_DE") << "123456.7" << false << (qlonglong) 0;
1299 QTest::newRow(dataTag: "de_DE 1.234.567") << QString("de_DE") << "1.234.567" << true << (qlonglong) 1234567;
1300 QTest::newRow(dataTag: "de_DE 1.234.567 ldspcs") << QString("de_DE") << " 1.234.567" << true << (qlonglong) 1234567;
1301 QTest::newRow(dataTag: "de_DE 1.234.567 trspcs") << QString("de_DE") << "1.234.567 " << true << (qlonglong) 1234567;
1302 QTest::newRow(dataTag: "de_DE 1.234.567 ldtrspcs") << QString("de_DE") << " 1.234.567 " << true << (qlonglong) 1234567;
1303
1304 // test that space is also accepted whenever QLocale::groupSeparator() == 0xa0 (which looks like space).
1305 QTest::newRow(dataTag: "nb_NO 123 groupsep") << QString("nb_NO") << QString("1")+QChar(0xa0)+QString("234") << true << (qlonglong) 1234;
1306 QTest::newRow(dataTag: "nb_NO 123 groupsep_space") << QString("nb_NO") << QString("1")+QChar(0x20)+QString("234") << true << (qlonglong) 1234;
1307
1308 QTest::newRow(dataTag: "nb_NO 123 ldspcs") << QString("nb_NO") << " 123" << true << (qlonglong) 123;
1309 QTest::newRow(dataTag: "nb_NO 123 trspcs") << QString("nb_NO") << "123 " << true << (qlonglong) 123;
1310 QTest::newRow(dataTag: "nb_NO 123 ldtrspcs") << QString("nb_NO") << " 123 " << true << (qlonglong) 123;
1311
1312 QTest::newRow(dataTag: "C 1234") << QString("C") << " 1234" << true << (qlonglong) 1234;
1313 QTest::newRow(dataTag: "C 1234 ") << QString("C") << "1234 " << true << (qlonglong) 1234;
1314 QTest::newRow(dataTag: "C 1234 ") << QString("C") << " 1234 " << true << (qlonglong) 1234;
1315}
1316
1317void tst_QLocale::long_long_conversion()
1318{
1319 QFETCH(QString, locale_name);
1320 QFETCH(QString, num_str);
1321 QFETCH(bool, good);
1322 QFETCH(qlonglong, num);
1323 QStringRef num_strRef = num_str.leftRef(n: -1);
1324
1325 QLocale locale(locale_name);
1326 QCOMPARE(locale.name(), locale_name);
1327
1328 bool ok;
1329 qlonglong l = locale.toLongLong(s: num_str, ok: &ok);
1330 QCOMPARE(ok, good);
1331
1332 if (ok)
1333 QCOMPARE(l, num);
1334
1335 l = locale.toLongLong(s: num_strRef, ok: &ok);
1336 QCOMPARE(ok, good);
1337
1338 if (ok)
1339 QCOMPARE(l, num);
1340}
1341
1342void tst_QLocale::long_long_conversion_extra()
1343{
1344 QLocale l(QLocale::C);
1345 l.setNumberOptions({ });
1346 QCOMPARE(l.toString((qlonglong)1), QString("1"));
1347 QCOMPARE(l.toString((qlonglong)12), QString("12"));
1348 QCOMPARE(l.toString((qlonglong)123), QString("123"));
1349 QCOMPARE(l.toString((qlonglong)1234), QString("1,234"));
1350 QCOMPARE(l.toString((qlonglong)12345), QString("12,345"));
1351 QCOMPARE(l.toString((qlonglong)-1), QString("-1"));
1352 QCOMPARE(l.toString((qlonglong)-12), QString("-12"));
1353 QCOMPARE(l.toString((qlonglong)-123), QString("-123"));
1354 QCOMPARE(l.toString((qlonglong)-1234), QString("-1,234"));
1355 QCOMPARE(l.toString((qlonglong)-12345), QString("-12,345"));
1356 QCOMPARE(l.toString((qulonglong)1), QString("1"));
1357 QCOMPARE(l.toString((qulonglong)12), QString("12"));
1358 QCOMPARE(l.toString((qulonglong)123), QString("123"));
1359 QCOMPARE(l.toString((qulonglong)1234), QString("1,234"));
1360 QCOMPARE(l.toString((qulonglong)12345), QString("12,345"));
1361}
1362
1363void tst_QLocale::testInfAndNan()
1364{
1365 double neginf = log(x: 0.0);
1366 double nan = sqrt(x: -1.0);
1367
1368#ifdef Q_OS_WIN
1369 // these cause INVALID floating point exception so we want to clear the status.
1370 _clear87();
1371#endif
1372
1373 QVERIFY(qIsInf(-neginf));
1374 QVERIFY(!qIsNaN(-neginf));
1375 QVERIFY(!qIsFinite(-neginf));
1376
1377 QVERIFY(!qIsInf(nan));
1378 QVERIFY(qIsNaN(nan));
1379 QVERIFY(!qIsFinite(nan));
1380
1381 QVERIFY(!qIsInf(1.234));
1382 QVERIFY(!qIsNaN(1.234));
1383 QVERIFY(qIsFinite(1.234));
1384}
1385
1386void tst_QLocale::fpExceptions()
1387{
1388#ifndef _MCW_EM
1389#define _MCW_EM 0x0008001F
1390#endif
1391#ifndef _EM_INEXACT
1392#define _EM_INEXACT 0x00000001
1393#endif
1394
1395 // check that double-to-string conversion doesn't throw floating point exceptions when they are
1396 // enabled
1397#ifdef Q_OS_WIN
1398 _clear87();
1399 unsigned int oldbits = _control87(0, 0);
1400 _control87( 0 | _EM_INEXACT, _MCW_EM );
1401#endif
1402
1403#ifdef QT_USE_FENV
1404 fenv_t envp;
1405 fegetenv(envp: &envp);
1406 feclearexcept(FE_ALL_EXCEPT);
1407 feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INVALID);
1408#endif
1409
1410 QString::number(1000.1245);
1411 QString::number(1.1);
1412 QString::number(0.0);
1413
1414 QVERIFY(true);
1415
1416#ifdef Q_OS_WIN
1417 _clear87();
1418 _control87(oldbits, 0xFFFFF);
1419#endif
1420
1421#ifdef QT_USE_FENV
1422 fesetenv(envp: &envp);
1423#endif
1424}
1425
1426void tst_QLocale::negativeZero()
1427{
1428 double negativeZero( 0.0 ); // Initialise to zero.
1429 uchar *ptr = (uchar *)&negativeZero;
1430 ptr[QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 7] = 0x80;
1431 QString s = QString::number(negativeZero);
1432 QCOMPARE(s, QString("0"));
1433}
1434
1435void tst_QLocale::dayOfWeek_data()
1436{
1437 QTest::addColumn<QDate>(name: "date");
1438 QTest::addColumn<QString>(name: "shortName");
1439 QTest::addColumn<QString>(name: "longName");
1440
1441 QTest::newRow(dataTag: "Sun") << QDate(2006, 1, 1) << "Sun" << "Sunday";
1442 QTest::newRow(dataTag: "Mon") << QDate(2006, 1, 2) << "Mon" << "Monday";
1443 QTest::newRow(dataTag: "Tue") << QDate(2006, 1, 3) << "Tue" << "Tuesday";
1444 QTest::newRow(dataTag: "Wed") << QDate(2006, 1, 4) << "Wed" << "Wednesday";
1445 QTest::newRow(dataTag: "Thu") << QDate(2006, 1, 5) << "Thu" << "Thursday";
1446 QTest::newRow(dataTag: "Fri") << QDate(2006, 1, 6) << "Fri" << "Friday";
1447 QTest::newRow(dataTag: "Sat") << QDate(2006, 1, 7) << "Sat" << "Saturday";
1448}
1449
1450void tst_QLocale::dayOfWeek()
1451{
1452 QFETCH(QDate, date);
1453 QFETCH(QString, shortName);
1454 QFETCH(QString, longName);
1455
1456 QCOMPARE(QLocale::c().toString(date, "ddd"), shortName);
1457 QCOMPARE(QLocale::c().toString(date, "dddd"), longName);
1458
1459 QCOMPARE(QLocale::c().toString(date, u"ddd"), shortName);
1460 QCOMPARE(QLocale::c().toString(date, u"dddd"), longName);
1461}
1462
1463void tst_QLocale::formatDate_data()
1464{
1465 QTest::addColumn<QDate>(name: "date");
1466 QTest::addColumn<QString>(name: "format");
1467 QTest::addColumn<QString>(name: "result");
1468
1469 QTest::newRow(dataTag: "1") << QDate(1974, 12, 1) << "d/M/yyyy" << "1/12/1974";
1470 QTest::newRow(dataTag: "2") << QDate(1974, 12, 1) << "d/M/yyyyy" << "1/12/1974y";
1471 QTest::newRow(dataTag: "4") << QDate(1974, 1, 1) << "d/M/yyyy" << "1/1/1974";
1472 QTest::newRow(dataTag: "5") << QDate(1974, 1, 1) << "dd/MM/yyy" << "01/01/74y";
1473 QTest::newRow(dataTag: "6") << QDate(1974, 12, 1) << "ddd/MMM/yy" << "Sun/Dec/74";
1474 QTest::newRow(dataTag: "7") << QDate(1974, 12, 1) << "dddd/MMMM/y" << "Sunday/December/y";
1475 QTest::newRow(dataTag: "8") << QDate(1974, 12, 1) << "ddddd/MMMMM/yy" << "Sunday1/December12/74";
1476 QTest::newRow(dataTag: "9") << QDate(1974, 12, 1) << "'dddd'/MMMM/yy" << "dddd/December/74";
1477 QTest::newRow(dataTag: "10") << QDate(1974, 12, 1) << "d'dd'd/MMMM/yyy" << "1dd1/December/74y";
1478 QTest::newRow(dataTag: "11") << QDate(1974, 12, 1) << "d'dd'd/MMM'M'/yy" << "1dd1/DecM/74";
1479 QTest::newRow(dataTag: "12") << QDate(1974, 12, 1) << "d'd'dd/M/yy" << "1d01/12/74";
1480
1481 QTest::newRow(dataTag: "20") << QDate(1974, 12, 1) << "foo" << "foo";
1482 QTest::newRow(dataTag: "21") << QDate(1974, 12, 1) << "'" << "";
1483 QTest::newRow(dataTag: "22") << QDate(1974, 12, 1) << "''" << "'";
1484 QTest::newRow(dataTag: "23") << QDate(1974, 12, 1) << "'''" << "'";
1485 QTest::newRow(dataTag: "24") << QDate(1974, 12, 1) << "\"" << "\"";
1486 QTest::newRow(dataTag: "25") << QDate(1974, 12, 1) << "\"\"" << "\"\"";
1487 QTest::newRow(dataTag: "26") << QDate(1974, 12, 1) << "\"yy\"" << "\"74\"";
1488 QTest::newRow(dataTag: "27") << QDate(1974, 12, 1) << "'\"yy\"'" << "\"yy\"";
1489 QTest::newRow(dataTag: "28") << QDate() << "'\"yy\"'" << "";
1490 QTest::newRow(dataTag: "29")
1491 << QDate(1974, 12, 1) << "hh:mm:ss.zzz ap d'd'dd/M/yy" << "hh:mm:ss.zzz ap 1d01/12/74";
1492
1493 QTest::newRow(dataTag: "dd MMMM yyyy") << QDate(1, 1, 1) << "dd MMMM yyyy" << "01 January 0001";
1494}
1495
1496void tst_QLocale::formatDate()
1497{
1498 QFETCH(QDate, date);
1499 QFETCH(QString, format);
1500 QFETCH(QString, result);
1501
1502 QLocale l(QLocale::C);
1503 QCOMPARE(l.toString(date, format), result);
1504 QCOMPARE(l.toString(date, QStringView(format)), result);
1505}
1506
1507void tst_QLocale::formatTime_data()
1508{
1509 QTest::addColumn<QTime>(name: "time");
1510 QTest::addColumn<QString>(name: "format");
1511 QTest::addColumn<QString>(name: "result");
1512
1513 QTest::newRow(dataTag: "1") << QTime(1, 2, 3) << "h:m:s" << "1:2:3";
1514 QTest::newRow(dataTag: "3") << QTime(1, 2, 3) << "H:m:s" << "1:2:3";
1515 QTest::newRow(dataTag: "4") << QTime(1, 2, 3) << "hh:mm:ss" << "01:02:03";
1516 QTest::newRow(dataTag: "5") << QTime(1, 2, 3) << "HH:mm:ss" << "01:02:03";
1517 QTest::newRow(dataTag: "6") << QTime(1, 2, 3) << "hhh:mmm:sss" << "011:022:033";
1518
1519 QTest::newRow(dataTag: "8") << QTime(14, 2, 3) << "h:m:s" << "14:2:3";
1520 QTest::newRow(dataTag: "9") << QTime(14, 2, 3) << "H:m:s" << "14:2:3";
1521 QTest::newRow(dataTag: "10") << QTime(14, 2, 3) << "hh:mm:ss" << "14:02:03";
1522 QTest::newRow(dataTag: "11") << QTime(14, 2, 3) << "HH:mm:ss" << "14:02:03";
1523 QTest::newRow(dataTag: "12") << QTime(14, 2, 3) << "hhh:mmm:sss" << "1414:022:033";
1524
1525 QTest::newRow(dataTag: "14") << QTime(14, 2, 3) << "h:m:s ap" << "2:2:3 pm";
1526 QTest::newRow(dataTag: "15") << QTime(14, 2, 3) << "H:m:s AP" << "14:2:3 PM";
1527 QTest::newRow(dataTag: "16") << QTime(14, 2, 3) << "hh:mm:ss aap" << "02:02:03 pmpm";
1528 QTest::newRow(dataTag: "17") << QTime(14, 2, 3) << "HH:mm:ss AP aa" << "14:02:03 PM pmpm";
1529
1530 QTest::newRow(dataTag: "18") << QTime(1, 2, 3) << "h:m:s ap" << "1:2:3 am";
1531 QTest::newRow(dataTag: "19") << QTime(1, 2, 3) << "H:m:s AP" << "1:2:3 AM";
1532
1533 QTest::newRow(dataTag: "20") << QTime(1, 2, 3) << "foo" << "foo";
1534 QTest::newRow(dataTag: "21") << QTime(1, 2, 3) << "'" << "";
1535 QTest::newRow(dataTag: "22") << QTime(1, 2, 3) << "''" << "'";
1536 QTest::newRow(dataTag: "23") << QTime(1, 2, 3) << "'''" << "'";
1537 QTest::newRow(dataTag: "24") << QTime(1, 2, 3) << "\"" << "\"";
1538 QTest::newRow(dataTag: "25") << QTime(1, 2, 3) << "\"\"" << "\"\"";
1539 QTest::newRow(dataTag: "26") << QTime(1, 2, 3) << "\"H\"" << "\"1\"";
1540 QTest::newRow(dataTag: "27") << QTime(1, 2, 3) << "'\"H\"'" << "\"H\"";
1541
1542 QTest::newRow(dataTag: "28") << QTime(1, 2, 3, 456) << "H:m:s.z" << "1:2:3.456";
1543 QTest::newRow(dataTag: "29") << QTime(1, 2, 3, 456) << "H:m:s.zz" << "1:2:3.456456";
1544 QTest::newRow(dataTag: "30") << QTime(1, 2, 3, 456) << "H:m:s.zzz" << "1:2:3.456";
1545 QTest::newRow(dataTag: "31") << QTime(1, 2, 3, 400) << "H:m:s.z" << "1:2:3.4";
1546 QTest::newRow(dataTag: "32") << QTime(1, 2, 3, 4) << "H:m:s.z" << "1:2:3.004";
1547 QTest::newRow(dataTag: "33") << QTime(1, 2, 3, 4) << "H:m:s.zzz" << "1:2:3.004";
1548 QTest::newRow(dataTag: "34") << QTime() << "H:m:s.zzz" << "";
1549 QTest::newRow(dataTag: "35") << QTime(1, 2, 3, 4) << "dd MM yyyy H:m:s.zzz" << "dd MM yyyy 1:2:3.004";
1550}
1551
1552void tst_QLocale::formatTime()
1553{
1554 QFETCH(QTime, time);
1555 QFETCH(QString, format);
1556 QFETCH(QString, result);
1557
1558 QLocale l(QLocale::C);
1559 QCOMPARE(l.toString(time, format), result);
1560 QCOMPARE(l.toString(time, QStringView(format)), result);
1561}
1562
1563
1564void tst_QLocale::formatDateTime_data()
1565{
1566 QTest::addColumn<QString>(name: "localeName");
1567 QTest::addColumn<QDateTime>(name: "dateTime");
1568 QTest::addColumn<QString>(name: "format");
1569 QTest::addColumn<QString>(name: "result");
1570
1571 QTest::newRow(dataTag: "1C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(5, 14, 13))
1572 << "d/M/yyyy hh:h:mm" << "1/12/1974 05:5:14";
1573 QTest::newRow(dataTag: "2C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1574 << "d/M/yyyyy h" << "1/12/1974y 15";
1575 QTest::newRow(dataTag: "4C") << "C" << QDateTime(QDate(1974, 1, 1), QTime(15, 14, 13))
1576 << "d/M/yyyy zzz" << "1/1/1974 000";
1577 QTest::newRow(dataTag: "5C") << "C" << QDateTime(QDate(1974, 1, 1), QTime(15, 14, 13))
1578 << "dd/MM/yyy z" << "01/01/74y 0";
1579 QTest::newRow(dataTag: "6C") << "C" << QDateTime(QDate(1974, 12, 2), QTime(15, 14, 13))
1580 << "ddd/MMM/yy AP" << "Mon/Dec/74 PM";
1581 QTest::newRow(dataTag: "7C") << "C" << QDateTime(QDate(1974, 12, 2), QTime(15, 14, 13))
1582 << "dddd/MMMM/y apa" << "Monday/December/y pmpm";
1583 QTest::newRow(dataTag: "8C") << "C" << QDateTime(QDate(1974, 12, 2), QTime(15, 14, 13))
1584 << "ddddd/MMMMM/yy ss" << "Monday2/December12/74 13";
1585 QTest::newRow(dataTag: "9C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1586 << "'dddd'/MMMM/yy s" << "dddd/December/74 13";
1587 QTest::newRow(dataTag: "10C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 4, 13))
1588 << "d'dd'd/MMMM/yyy m'm'mm" << "1dd1/December/74y 4m04";
1589 QTest::newRow(dataTag: "11C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 3))
1590 << "d'dd'd/MMM'M'/yysss" << "1dd1/DecM/74033";
1591 QTest::newRow(dataTag: "12C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1592 << "d'd'dd/M/yyh" << "1d01/12/7415";
1593
1594 QTest::newRow(dataTag: "dd MMMM yyyy, hh:mm:ss") << "C" << QDateTime(QDate(1, 1, 1), QTime(12, 00, 00))
1595 << "dd MMMM yyyy, hh:mm:ss" << "01 January 0001, 12:00:00";
1596
1597 QTest::newRow(dataTag: "20C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1598 << "foo" << "foo";
1599 QTest::newRow(dataTag: "21C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1600 << "'" << "";
1601 QTest::newRow(dataTag: "22C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1602 << "''" << "'";
1603 QTest::newRow(dataTag: "23C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1604 << "'''" << "'";
1605 QTest::newRow(dataTag: "24C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1606 << "\"" << "\"";
1607 QTest::newRow(dataTag: "25C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1608 << "\"\"" << "\"\"";
1609 QTest::newRow(dataTag: "26C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1610 << "\"yymm\"" << "\"7414\"";
1611 QTest::newRow(dataTag: "27C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1612 << "'\"yymm\"'" << "\"yymm\"";
1613 QTest::newRow(dataTag: "28C") << "C" << QDateTime()
1614 << "'\"yymm\"'" << "";
1615
1616 QTest::newRow(dataTag: "1no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(5, 14, 13))
1617 << "d/M/yyyy hh:h:mm" << "1/12/1974 05:5:14";
1618 QTest::newRow(dataTag: "2no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1619 << "d/M/yyyyy h" << "1/12/1974y 15";
1620 QTest::newRow(dataTag: "4no_NO") << "no_NO" << QDateTime(QDate(1974, 1, 1), QTime(15, 14, 13))
1621 << "d/M/yyyy zzz" << "1/1/1974 000";
1622 QTest::newRow(dataTag: "5no_NO") << "no_NO" << QDateTime(QDate(1974, 1, 1), QTime(15, 14, 13))
1623 << "dd/MM/yyy z" << "01/01/74y 0";
1624 QTest::newRow(dataTag: "6no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 2), QTime(15, 14, 13))
1625 << "ddd/MMM/yy AP" << "man./des./74 P.M.";
1626 QTest::newRow(dataTag: "7no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 2), QTime(15, 14, 13))
1627 << "dddd/MMMM/y apa" << "mandag/desember/y p.m.p.m.";
1628 QTest::newRow(dataTag: "8no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 2), QTime(15, 14, 13))
1629 << "ddddd/MMMMM/yy ss" << "mandag2/desember12/74 13";
1630 QTest::newRow(dataTag: "9no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1631 << "'dddd'/MMMM/yy s" << "dddd/desember/74 13";
1632 QTest::newRow(dataTag: "10no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 4, 13))
1633 << "d'dd'd/MMMM/yyy m'm'mm" << "1dd1/desember/74y 4m04";
1634 QTest::newRow(dataTag: "11no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 3))
1635 << "d'dd'd/MMM'M'/yysss" << "1dd1/des.M/74033";
1636 QTest::newRow(dataTag: "12no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1637 << "d'd'dd/M/yyh" << "1d01/12/7415";
1638
1639 QTest::newRow(dataTag: "20no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1640 << "foo" << "foo";
1641 QTest::newRow(dataTag: "21no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1642 << "'" << "";
1643 QTest::newRow(dataTag: "22no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1644 << "''" << "'";
1645 QTest::newRow(dataTag: "23no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1646 << "'''" << "'";
1647 QTest::newRow(dataTag: "24no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1648 << "\"" << "\"";
1649 QTest::newRow(dataTag: "25no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1650 << "\"\"" << "\"\"";
1651 QTest::newRow(dataTag: "26no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1652 << "\"yymm\"" << "\"7414\"";
1653 QTest::newRow(dataTag: "27no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 14, 13))
1654 << "'\"yymm\"'" << "\"yymm\"";
1655 QTest::newRow(dataTag: "28no_NO") << "no_NO" << QDateTime()
1656 << "'\"yymm\"'" << "";
1657
1658 QDateTime testLongHour(QDate(1999, 12, 31), QTime(23, 59, 59, 999));
1659 QDateTime testShortHour(QDate(1999, 12, 31), QTime(3, 59, 59, 999));
1660 QDateTime testZeroHour(QDate(1999, 12, 31), QTime(0, 59, 59, 999));
1661
1662 QTest::newRow(dataTag: "datetime0") << "en_US" << QDateTime()
1663 << QString("dd-MM-yyyy hh:mm:ss") << QString();
1664 QTest::newRow(dataTag: "datetime1") << "en_US" << testLongHour
1665 << QString("dd-'mmddyy'MM-yyyy hh:mm:ss.zzz")
1666 << QString("31-mmddyy12-1999 23:59:59.999");
1667 QTest::newRow(dataTag: "datetime2") << "en_US" << testLongHour
1668 << QString("dd-'apAP'MM-yyyy hh:mm:ss.zzz")
1669 << QString("31-apAP12-1999 23:59:59.999");
1670 QTest::newRow(dataTag: "datetime3") << "en_US" << testLongHour
1671 << QString("Apdd-MM-yyyy hh:mm:ss.zzz")
1672 << QString("PMp31-12-1999 11:59:59.999");
1673 QTest::newRow(dataTag: "datetime4") << "en_US" << testLongHour
1674 << QString("'ap'apdd-MM-yyyy 'AP'hh:mm:ss.zzz")
1675 << QString("appm31-12-1999 AP11:59:59.999");
1676 QTest::newRow(dataTag: "datetime5") << "en_US" << testLongHour
1677 << QString("'''") << QString("'");
1678 QTest::newRow(dataTag: "datetime6") << "en_US" << testLongHour
1679 << QString("'ap") << QString("ap");
1680 QTest::newRow(dataTag: "datetime7") << "en_US" << testLongHour
1681 << QString("' ' 'hh' hh") << QString(" hh 23");
1682 QTest::newRow(dataTag: "datetime8") << "en_US" << testLongHour
1683 << QString("d'foobar'") << QString("31foobar");
1684 QTest::newRow(dataTag: "datetime9") << "en_US" << testShortHour
1685 << QString("hhhhh") << QString("03033");
1686 QTest::newRow(dataTag: "datetime11") << "en_US" << testLongHour
1687 << QString("HHHhhhAaAPap") << QString("23231111PMpmPMpm");
1688 QTest::newRow(dataTag: "datetime12") << "en_US" << testShortHour
1689 << QString("HHHhhhAaAPap") << QString("033033AMamAMam");
1690 QTest::newRow(dataTag: "datetime13") << "en_US" << QDateTime(QDate(1974, 12, 1), QTime(14, 14, 20))
1691 << QString("hh''mm''ss dd''MM''yyyy")
1692 << QString("14'14'20 01'12'1974");
1693 QTest::newRow(dataTag: "AM no p") << "en_US" << testZeroHour
1694 << QString("hhAX") << QString("12AMX");
1695 QTest::newRow(dataTag: "AM no p, x 2") << "en_US" << testShortHour
1696 << QString("hhhhhaA") << QString("03033amAM");
1697 QTest::newRow(dataTag: "am 0 hour") << "en_US" << testZeroHour
1698 << QString("hAP") << QString("12AM");
1699 QTest::newRow(dataTag: "AM zero hour") << "en_US" << testZeroHour
1700 << QString("hhAP") << QString("12AM");
1701 QTest::newRow(dataTag: "dddd") << "en_US" << testZeroHour
1702 << QString("dddd") << QString("Friday");
1703 QTest::newRow(dataTag: "ddd") << "en_US" << testZeroHour
1704 << QString("ddd") << QString("Fri");
1705 QTest::newRow(dataTag: "MMMM") << "en_US" << testZeroHour
1706 << QString("MMMM") << QString("December");
1707 QTest::newRow(dataTag: "MMM") << "en_US" << testZeroHour
1708 << QString("MMM") << QString("Dec");
1709 QTest::newRow(dataTag: "empty") << "en_US" << testZeroHour
1710 << QString("") << QString("");
1711}
1712
1713void tst_QLocale::formatDateTime()
1714{
1715 QFETCH(QString, localeName);
1716 QFETCH(QDateTime, dateTime);
1717 QFETCH(QString, format);
1718 QFETCH(QString, result);
1719
1720 QLocale l(localeName);
1721 QCOMPARE(l.toString(dateTime, format), result);
1722 QCOMPARE(l.toString(dateTime, QStringView(format)), result);
1723}
1724
1725void tst_QLocale::formatTimeZone()
1726{
1727 QLocale enUS("en_US");
1728
1729 QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60);
1730 QCOMPARE(enUS.toString(dt1, "t"), QLatin1String("UTC+01:00"));
1731
1732 QDateTime dt2(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, -60 * 60);
1733 QCOMPARE(enUS.toString(dt2, "t"), QLatin1String("UTC-01:00"));
1734
1735 QDateTime dt3(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC);
1736 QCOMPARE(enUS.toString(dt3, "t"), QLatin1String("UTC"));
1737
1738 // LocalTime should vary
1739 if (europeanTimeZone) {
1740 // Time definitely in Standard Time
1741 QDateTime dt4(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
1742#ifdef Q_OS_WIN
1743 QEXPECT_FAIL("", "Windows only returns long name (QTBUG-32759)", Continue);
1744#endif // Q_OS_WIN
1745 QCOMPARE(enUS.toString(dt4, "t"), QLatin1String("CET"));
1746
1747 // Time definitely in Daylight Time
1748 QDateTime dt5(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::LocalTime);
1749#ifdef Q_OS_WIN
1750 QEXPECT_FAIL("", "Windows only returns long name (QTBUG-32759)", Continue);
1751#endif // Q_OS_WIN
1752 QCOMPARE(enUS.toString(dt5, "t"), QLatin1String("CEST"));
1753 } else {
1754 qDebug(msg: "(Skipped some CET-only tests)");
1755 }
1756
1757#if QT_CONFIG(timezone)
1758 const QTimeZone berlin("Europe/Berlin");
1759 const QDateTime jan(QDate(2010, 1, 1).startOfDay(zone: berlin));
1760 const QDateTime jul(QDate(2010, 7, 1).startOfDay(zone: berlin));
1761
1762 QCOMPARE(enUS.toString(jan, "t"), berlin.abbreviation(jan));
1763 QCOMPARE(enUS.toString(jul, "t"), berlin.abbreviation(jul));
1764#endif
1765
1766 // Current datetime should return current abbreviation
1767 QCOMPARE(enUS.toString(QDateTime::currentDateTime(), "t"),
1768 QDateTime::currentDateTime().timeZoneAbbreviation());
1769
1770 // Time on its own will always be current local time zone
1771 QCOMPARE(enUS.toString(QTime(1, 2, 3), "t"),
1772 QDateTime::currentDateTime().timeZoneAbbreviation());
1773}
1774
1775void tst_QLocale::toDateTime_data()
1776{
1777 QTest::addColumn<QString>(name: "localeName");
1778 QTest::addColumn<QDateTime>(name: "result");
1779 QTest::addColumn<QString>(name: "format");
1780 QTest::addColumn<QString>(name: "string");
1781 QTest::addColumn<bool>(name: "clean"); // No non-format letters in format string
1782
1783 QTest::newRow(dataTag: "1C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(5, 14, 0))
1784 << "d/M/yyyy hh:h:mm" << "1/12/1974 05:5:14" << true;
1785 QTest::newRow(dataTag: "2C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 0, 0))
1786 << "d/M/yyyyy h" << "1/12/1974y 15" << false;
1787 QTest::newRow(dataTag: "4C") << "C" << QDateTime(QDate(1974, 1, 1), QTime(0, 0, 0, 1))
1788 << "d/M/yyyy zzz" << "1/1/1974 001" << true;
1789 QTest::newRow(dataTag: "5C") << "C" << QDateTime(QDate(1974, 1, 1), QTime(0, 0, 0, 1))
1790 << "dd/MM/yyy z" << "01/01/74y 001" << false;
1791 QTest::newRow(dataTag: "6C") << "C" << QDateTime(QDate(1974, 1, 1), QTime(0, 0, 0, 100))
1792 << "dd/MM/yyy z" << "01/01/74y 1" << false;
1793 QTest::newRow(dataTag: "8C") << "C" << QDateTime(QDate(1974, 12, 2), QTime(0, 0, 13))
1794 << "ddddd/MMMMM/yy ss" << "Monday2/December12/74 13" << true;
1795 QTest::newRow(dataTag: "9C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(0, 0, 13))
1796 << "'dddd'/MMMM/yy s" << "dddd/December/74 13" << false;
1797 QTest::newRow(dataTag: "10C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(0, 4, 0))
1798 << "d'dd'd/MMMM/yyy m'm'mm" << "1dd1/December/74y 4m04" << false;
1799 QTest::newRow(dataTag: "11C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(0, 0, 3))
1800 << "d'dd'd/MMM'M'/yysss" << "1dd1/DecM/74033" << false;
1801 QTest::newRow(dataTag: "12C") << "C" << QDateTime(QDate(1974, 12, 1), QTime(15, 0, 0))
1802 << "d'd'dd/M/yyh" << "1d01/12/7415" << false;
1803 // Unpadded value for fixed-width field is wrong:
1804 QTest::newRow(dataTag: "bad-day-C") << "C" << QDateTime() << "dd-MMM-yy" << "4-Jun-11" << true;
1805 QTest::newRow(dataTag: "bad-month-C") << "C" << QDateTime() << "d-MM-yy" << "4-6-11" << true;
1806 QTest::newRow(dataTag: "bad-year-C") << "C" << QDateTime() << "d-MMM-yyyy" << "4-Jun-11" << true;
1807 QTest::newRow(dataTag: "bad-hour-C") << "C" << QDateTime() << "d-MMM-yy hh:m" << "4-Jun-11 1:2" << true;
1808 QTest::newRow(dataTag: "bad-min-C") << "C" << QDateTime() << "d-MMM-yy h:mm" << "4-Jun-11 1:2" << true;
1809 QTest::newRow(dataTag: "bad-sec-C")
1810 << "C" << QDateTime() << "d-MMM-yy h:m:ss" << "4-Jun-11 1:2:3" << true;
1811 QTest::newRow(dataTag: "bad-milli-C")
1812 << "C" << QDateTime() << "d-MMM-yy h:m:s.zzz" << "4-Jun-11 1:2:3.4" << true;
1813 QTest::newRow(dataTag: "ok-C") << "C" << QDateTime(QDate(1911, 6, 4), QTime(1, 2, 3, 400))
1814 << "d-MMM-yy h:m:s.z" << "4-Jun-11 1:2:3.4" << true;
1815
1816 QTest::newRow(dataTag: "1no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(5, 14, 0))
1817 << "d/M/yyyy hh:h:mm" << "1/12/1974 05:5:14" << true;
1818 QTest::newRow(dataTag: "2no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 0, 0))
1819 << "d/M/yyyyy h" << "1/12/1974y 15" << false;
1820 QTest::newRow(dataTag: "4no_NO") << "no_NO" << QDateTime(QDate(1974, 1, 1), QTime(0, 0, 0))
1821 << "d/M/yyyy zzz" << "1/1/1974 000" << true;
1822 QTest::newRow(dataTag: "5no_NO") << "no_NO" << QDateTime(QDate(1974, 1, 1), QTime(0, 0, 0))
1823 << "dd/MM/yyy z" << "01/01/74y 0" << false;
1824 QTest::newRow(dataTag: "8no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 2), QTime(0, 0, 13))
1825 << "ddddd/MMMMM/yy ss" << "mandag2/desember12/74 13" << true;
1826 QTest::newRow(dataTag: "9no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(0, 0, 13))
1827 << "'dddd'/MMMM/yy s" << "dddd/desember/74 13" << false;
1828 QTest::newRow(dataTag: "10no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(0, 4, 0))
1829 << "d'dd'd/MMMM/yyy m'm'mm" << "1dd1/desember/74y 4m04" << false;
1830 QTest::newRow(dataTag: "11no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(0, 0, 3))
1831 << "d'dd'd/MMM'M'/yysss" << "1dd1/des.M/74033" << false;
1832 QTest::newRow(dataTag: "12no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 0, 0))
1833 << "d'd'dd/M/yyh" << "1d01/12/7415" << false;
1834
1835 QTest::newRow(dataTag: "RFC-1123")
1836 << "C" << QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30))
1837 << "ddd, dd MMM yyyy hh:mm:ss 'GMT'" << "Thu, 01 Nov 2007 18:08:30 GMT" << false;
1838
1839 QTest::newRow(dataTag: "longFormat")
1840 << "en_US" << QDateTime(QDate(2009, 1, 5), QTime(11, 48, 32))
1841 << "dddd, MMMM d, yyyy h:mm:ss AP " << "Monday, January 5, 2009 11:48:32 AM " << true;
1842
1843 const QDateTime dt(QDate(2017, 02, 25), QTime(17, 21, 25));
1844 // These formats correspond to the locale formats, with the timezone removed.
1845 // We hardcode them in case an update to the locale DB changes them.
1846
1847 QTest::newRow(dataTag: "C:long") << "C" << dt << "dddd, d MMMM yyyy HH:mm:ss"
1848 << "Saturday, 25 February 2017 17:21:25" << true;
1849 QTest::newRow(dataTag: "C:short")
1850 << "C" << dt << "d MMM yyyy HH:mm:ss" << "25 Feb 2017 17:21:25" << true;
1851 QTest::newRow(dataTag: "C:narrow")
1852 << "C" << dt << "d MMM yyyy HH:mm:ss" << "25 Feb 2017 17:21:25" << true;
1853
1854 QTest::newRow(dataTag: "fr:long") << "fr" << dt << "dddd d MMMM yyyy HH:mm:ss"
1855 << "Samedi 25 février 2017 17:21:25" << true;
1856 QTest::newRow(dataTag: "fr:short")
1857 << "fr" << dt.addSecs(secs: -25) << "dd/MM/yyyy HH:mm" << "25/02/2017 17:21" << true;
1858
1859 // In Turkish, the word for Friday ("Cuma") is a prefix for the word for
1860 // Saturday ("Cumartesi")
1861 QTest::newRow(dataTag: "tr:long")
1862 << "tr" << dt << "d MMMM yyyy dddd HH:mm:ss" << "25 Şubat 2017 Cumartesi 17:21:25" << true;
1863 QTest::newRow(dataTag: "tr:long2") << "tr" << dt.addDays(days: -1) << "d MMMM yyyy dddd HH:mm:ss"
1864 << "24 Şubat 2017 Cuma 17:21:25" << true;
1865 QTest::newRow(dataTag: "tr:mashed")
1866 << "tr" << dt << "d MMMMyyyy ddddHH:mm:ss" << "25 Şubat2017 Cumartesi17:21:25" << true;
1867 QTest::newRow(dataTag: "tr:mashed2") << "tr" << dt.addDays(days: -1) << "d MMMMyyyy ddddHH:mm:ss"
1868 << "24 Şubat2017 Cuma17:21:25" << true;
1869 QTest::newRow(dataTag: "tr:short")
1870 << "tr" << dt.addSecs(secs: -25) << "d.MM.yyyy HH:mm" << "25.02.2017 17:21" << true;
1871}
1872
1873void tst_QLocale::toDateTime()
1874{
1875 QFETCH(QString, localeName);
1876 QFETCH(QDateTime, result);
1877 QFETCH(QString, format);
1878 QFETCH(QString, string);
1879 QFETCH(bool, clean);
1880
1881 QLocale l(localeName);
1882 QCOMPARE(l.toDateTime(string, format), result);
1883 if (clean) {
1884 QCOMPARE(l.toDateTime(string.toLower(), format), result);
1885 QCOMPARE(l.toDateTime(string.toUpper(), format), result);
1886 }
1887
1888 if (l.dateTimeFormat(format: QLocale::LongFormat) == format)
1889 QCOMPARE(l.toDateTime(string, QLocale::LongFormat), result);
1890 if (l.dateTimeFormat(format: QLocale::ShortFormat) == format)
1891 QCOMPARE(l.toDateTime(string, QLocale::ShortFormat), result);
1892}
1893
1894#ifdef Q_OS_MAC
1895
1896// Format number string according to system locale settings.
1897// Expected in format is US "1,234.56".
1898QString systemLocaleFormatNumber(QString &&numberString)
1899{
1900 QLocale locale = QLocale::system();
1901 QString numberStringMunged =
1902 numberString.replace(QChar(','), QChar('G')).replace(QChar('.'), QChar('D'));
1903 if (locale.numberOptions() & QLocale::OmitGroupSeparator)
1904 numberStringMunged.remove(QLatin1Char('G'));
1905 else
1906 numberStringMunged.replace(QChar('G'), locale.groupSeparator());
1907 return numberStringMunged.replace(QChar('D'), locale.decimalPoint());
1908}
1909
1910void tst_QLocale::macDefaultLocale()
1911{
1912 QLocale locale = QLocale::system();
1913
1914 if (locale.name() != QLatin1String("en_US"))
1915 QSKIP("This test only tests for en_US");
1916
1917 QTime invalidTime;
1918 QDate invalidDate;
1919 QCOMPARE(locale.toString(invalidTime, QLocale::ShortFormat), QString());
1920 QCOMPARE(locale.toString(invalidDate, QLocale::ShortFormat), QString());
1921 QCOMPARE(locale.toString(invalidTime, QLocale::NarrowFormat), QString());
1922 QCOMPARE(locale.toString(invalidDate, QLocale::NarrowFormat), QString());
1923 QCOMPARE(locale.toString(invalidTime, QLocale::LongFormat), QString());
1924 QCOMPARE(locale.toString(invalidDate, QLocale::LongFormat), QString());
1925
1926 // On OS X the decimal point and group separator are configurable
1927 // independently of the locale. Verify that they have one of the
1928 // allowed values and are not the same.
1929 QVERIFY(locale.decimalPoint() == QChar('.') || locale.decimalPoint() == QChar(','));
1930 if (!(locale.numberOptions() & QLocale::OmitGroupSeparator)) {
1931 QVERIFY(locale.groupSeparator() == QChar(',')
1932 || locale.groupSeparator() == QChar('.')
1933 || locale.groupSeparator() == QChar('\xA0') // no-breaking space
1934 || locale.groupSeparator() == QChar('\'')
1935 || locale.groupSeparator() == QChar());
1936 QVERIFY(locale.decimalPoint() != locale.groupSeparator());
1937 }
1938
1939 // make sure we are using the system to parse them
1940 QCOMPARE(locale.toString(1234.56), systemLocaleFormatNumber(QString("1,234.56")));
1941
1942 QTime testTime = QTime(1, 2, 3);
1943 QTime utcTime = QDateTime(QDate::currentDate(), testTime).toUTC().time();
1944 int diff = testTime.hour() - utcTime.hour();
1945
1946 // Check if local time and utc time are on opposite sides of the 24-hour wrap-around.
1947 if (diff < -12)
1948 diff += 24;
1949 if (diff > 12)
1950 diff -= 24;
1951
1952 const QString timeString = locale.toString(testTime, QLocale::LongFormat);
1953 QVERIFY(timeString.contains(QString("1:02:03")));
1954
1955 // To run this test make sure "Curreny" is US Dollar in System Preferences->Language & Region->Advanced.
1956 if (locale.currencySymbol() == QString("$")) {
1957 QCOMPARE(locale.toCurrencyString(qulonglong(1234)),
1958 systemLocaleFormatNumber(QString("$1,234.00")));
1959 QCOMPARE(locale.toCurrencyString(double(1234.56)),
1960 systemLocaleFormatNumber(QString("$1,234.56")));
1961 }
1962
1963 // Depending on the configured time zone, the time string might not
1964 // contain a GMT specifier. (Sometimes it just names the zone, like "CEST")
1965 QLatin1String gmt("GMT");
1966 if (timeString.contains(gmt) && diff) {
1967 QLatin1Char sign(diff < 0 ? '-' : '+');
1968 QString number(QString::number(qAbs(diff)));
1969 const QString expect = gmt + sign + number;
1970
1971 if (diff < 10) {
1972 const QString zeroed = gmt + sign + QLatin1Char('0') + number;
1973
1974 QVERIFY2(timeString.contains(expect) || timeString.contains(zeroed),
1975 qPrintable(QString("timeString `%1', expected GMT specifier `%2' or `%3'")
1976 .arg(timeString).arg(expect).arg(zeroed)));
1977 } else {
1978 QVERIFY2(timeString.contains(expect),
1979 qPrintable(QString("timeString `%1', expected GMT specifier `%2'")
1980 .arg(timeString).arg(expect)));
1981 }
1982 }
1983 QCOMPARE(locale.dayName(1), QString("Monday"));
1984 QCOMPARE(locale.dayName(7), QString("Sunday"));
1985 QCOMPARE(locale.monthName(1), QString("January"));
1986 QCOMPARE(locale.monthName(12), QString("December"));
1987 QCOMPARE(locale.quoteString("string"),
1988 QString::fromUtf8("\xe2\x80\x9c" "string" "\xe2\x80\x9d"));
1989 QCOMPARE(locale.quoteString("string", QLocale::AlternateQuotation),
1990 QString::fromUtf8("\xe2\x80\x98" "string" "\xe2\x80\x99"));
1991
1992 QList<Qt::DayOfWeek> days;
1993 days << Qt::Monday << Qt::Tuesday << Qt::Wednesday << Qt::Thursday << Qt::Friday;
1994 QCOMPARE(locale.weekdays(), days);
1995
1996}
1997#endif // Q_OS_MAC
1998
1999#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
2000#include <qt_windows.h>
2001
2002static QString getWinLocaleInfo(LCTYPE type)
2003{
2004 LCID id = GetThreadLocale();
2005 int cnt = GetLocaleInfo(id, type, 0, 0) * 2;
2006
2007 if (cnt == 0) {
2008 qWarning().nospace() << "QLocale: empty windows locale info (" << type << ')';
2009 return QString();
2010 }
2011 cnt /= sizeof(wchar_t);
2012 QScopedArrayPointer<wchar_t> buf(new wchar_t[cnt]);
2013 cnt = GetLocaleInfo(id, type, buf.data(), cnt);
2014
2015 if (cnt == 0) {
2016 qWarning().nospace() << "QLocale: empty windows locale info (" << type << ')';
2017 return QString();
2018 }
2019 return QString::fromWCharArray(buf.data());
2020}
2021
2022static void setWinLocaleInfo(LCTYPE type, const QString &value)
2023{
2024 LCID id = GetThreadLocale();
2025 SetLocaleInfo(id, type, reinterpret_cast<const wchar_t*>(value.utf16()));
2026}
2027
2028#ifndef LOCALE_SSHORTTIME
2029# define LOCALE_SSHORTTIME 0x00000079
2030#endif
2031
2032class RestoreLocaleHelper
2033{
2034public:
2035 RestoreLocaleHelper()
2036 {
2037 m_decimal = getWinLocaleInfo(LOCALE_SDECIMAL);
2038 m_thousand = getWinLocaleInfo(LOCALE_STHOUSAND);
2039 m_sdate = getWinLocaleInfo(LOCALE_SSHORTDATE);
2040 m_ldate = getWinLocaleInfo(LOCALE_SLONGDATE);
2041 m_time = getWinLocaleInfo(LOCALE_SSHORTTIME);
2042 }
2043
2044 ~RestoreLocaleHelper()
2045 {
2046 // restore these, or the user will get a surprise
2047 setWinLocaleInfo(LOCALE_SDECIMAL, m_decimal);
2048 setWinLocaleInfo(LOCALE_STHOUSAND, m_thousand);
2049 setWinLocaleInfo(LOCALE_SSHORTDATE, m_sdate);
2050 setWinLocaleInfo(LOCALE_SLONGDATE, m_ldate);
2051 setWinLocaleInfo(LOCALE_SSHORTTIME, m_time);
2052
2053 QSystemLocale dummy; // to provoke a refresh of the system locale
2054 }
2055
2056 QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
2057};
2058
2059void tst_QLocale::windowsDefaultLocale()
2060{
2061 RestoreLocaleHelper systemLocale;
2062 // set weird system defaults and make sure we're using them
2063 setWinLocaleInfo(LOCALE_SDECIMAL, QLatin1String("@"));
2064 setWinLocaleInfo(LOCALE_STHOUSAND, QLatin1String("?"));
2065 const QString shortDateFormat = QStringLiteral("d*M*yyyy");
2066 setWinLocaleInfo(LOCALE_SSHORTDATE, shortDateFormat);
2067 const QString longDateFormat = QStringLiteral("d@M@yyyy");
2068 setWinLocaleInfo(LOCALE_SLONGDATE, longDateFormat);
2069 const QString shortTimeFormat = QStringLiteral("h^m^s");
2070 setWinLocaleInfo(LOCALE_SSHORTTIME, shortTimeFormat);
2071 const QString longTimeFormat = QStringLiteral("HH%mm%ss");
2072 setWinLocaleInfo(LOCALE_STIMEFORMAT, longTimeFormat);
2073
2074 QSystemLocale dummy; // to provoke a refresh of the system locale
2075 QLocale locale = QLocale::system();
2076
2077 // make sure we are seeing the system's format strings
2078 QCOMPARE(locale.decimalPoint(), QChar('@'));
2079 QCOMPARE(locale.groupSeparator(), QChar('?'));
2080 QCOMPARE(locale.dateFormat(QLocale::ShortFormat), shortDateFormat);
2081 QCOMPARE(locale.dateFormat(QLocale::LongFormat), longDateFormat);
2082 QCOMPARE(locale.timeFormat(QLocale::ShortFormat), shortTimeFormat);
2083 QCOMPARE(locale.dateTimeFormat(QLocale::ShortFormat),
2084 shortDateFormat + QLatin1Char(' ') + shortTimeFormat);
2085 const QString expectedLongDateTimeFormat
2086 = longDateFormat + QLatin1Char(' ') + longTimeFormat;
2087 QCOMPARE(locale.dateTimeFormat(QLocale::LongFormat), expectedLongDateTimeFormat);
2088
2089 // make sure we are using the system to parse them
2090 QCOMPARE(locale.toString(1234.56), QString("1?234@56"));
2091 QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::ShortFormat), QString("1*12*1974"));
2092 QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::NarrowFormat),
2093 locale.toString(QDate(1974, 12, 1), QLocale::ShortFormat));
2094 QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::LongFormat), QString("1@12@1974"));
2095 const QString expectedFormattedShortTimeSeconds = QStringLiteral("1^2^3");
2096 const QString expectedFormattedShortTime = QStringLiteral("1^2");
2097 QCOMPARE(locale.toString(QTime(1,2,3), QLocale::ShortFormat), expectedFormattedShortTime);
2098 QCOMPARE(locale.toString(QTime(1,2,3), QLocale::NarrowFormat),
2099 locale.toString(QTime(1,2,3), QLocale::ShortFormat));
2100 const QString expectedFormattedLongTime = QStringLiteral("01%02%03");
2101 QCOMPARE(locale.toString(QTime(1,2,3), QLocale::LongFormat), expectedFormattedLongTime);
2102 QCOMPARE(locale.toString(QDateTime(QDate(1974, 12, 1), QTime(1,2,3)), QLocale::ShortFormat),
2103 QStringLiteral("1*12*1974 ") + expectedFormattedShortTime);
2104 QCOMPARE(locale.toString(QDateTime(QDate(1974, 12, 1), QTime(1,2,3)), QLocale::NarrowFormat),
2105 locale.toString(QDateTime(QDate(1974, 12, 1), QTime(1,2,3)), QLocale::ShortFormat));
2106 QCOMPARE(locale.toString(QDateTime(QDate(1974, 12, 1), QTime(1,2,3)), QLocale::LongFormat),
2107 QStringLiteral("1@12@1974 ") + expectedFormattedLongTime);
2108}
2109#endif // Q_OS_WIN but !Q_OS_WINRT
2110
2111void tst_QLocale::numberOptions()
2112{
2113 bool ok;
2114
2115 QLocale locale(QLocale::C);
2116 QCOMPARE(locale.numberOptions(), QLocale::OmitGroupSeparator);
2117 QCOMPARE(locale.toInt(QString("12345"), &ok), 12345);
2118 QVERIFY(ok);
2119 QCOMPARE(locale.toInt(QString("12345"), &ok), 12345);
2120 QVERIFY(ok);
2121 QCOMPARE(locale.toString(12345), QString("12345"));
2122
2123 locale.setNumberOptions({ });
2124 QCOMPARE(locale.numberOptions(), 0);
2125 QCOMPARE(locale.toInt(QString("12,345"), &ok), 12345);
2126 QVERIFY(ok);
2127 QCOMPARE(locale.toInt(QString("12345"), &ok), 12345);
2128 QVERIFY(ok);
2129 QCOMPARE(locale.toString(12345), QString("12,345"));
2130
2131 locale.setNumberOptions(QLocale::RejectGroupSeparator);
2132 QCOMPARE(locale.numberOptions(), QLocale::RejectGroupSeparator);
2133 locale.toInt(s: QString("12,345"), ok: &ok);
2134 QVERIFY(!ok);
2135 QCOMPARE(locale.toInt(QString("12345"), &ok), 12345);
2136 QVERIFY(ok);
2137 QCOMPARE(locale.toString(12345), QString("12,345"));
2138
2139 QLocale locale2 = locale;
2140 QCOMPARE(locale2.numberOptions(), QLocale::RejectGroupSeparator);
2141
2142 QCOMPARE(locale.toString(12.4, 'e', 2), QString("1.24e+01"));
2143 locale.setNumberOptions(QLocale::OmitLeadingZeroInExponent);
2144 QCOMPARE(locale.numberOptions(), QLocale::OmitLeadingZeroInExponent);
2145 QCOMPARE(locale.toString(12.4, 'e', 2), QString("1.24e+1"));
2146
2147 locale.toDouble(s: QString("1.24e+01"), ok: &ok);
2148 QVERIFY(ok);
2149 locale.setNumberOptions(QLocale::RejectLeadingZeroInExponent);
2150 QCOMPARE(locale.numberOptions(), QLocale::RejectLeadingZeroInExponent);
2151 locale.toDouble(s: QString("1.24e+1"), ok: &ok);
2152 QVERIFY(ok);
2153 locale.toDouble(s: QString("1.24e+01"), ok: &ok);
2154 QVERIFY(!ok);
2155
2156 QCOMPARE(locale.toString(12.4, 'g', 5), QString("12.4"));
2157 locale.setNumberOptions(QLocale::IncludeTrailingZeroesAfterDot);
2158 QCOMPARE(locale.numberOptions(), QLocale::IncludeTrailingZeroesAfterDot);
2159 QCOMPARE(locale.toString(12.4, 'g', 5), QString("12.400"));
2160
2161 locale.toDouble(s: QString("1.24e+01"), ok: &ok);
2162 QVERIFY(ok);
2163 locale.toDouble(s: QString("1.2400e+01"), ok: &ok);
2164 QVERIFY(ok);
2165 locale.toDouble(s: QString("12.4"), ok: &ok);
2166 QVERIFY(ok);
2167 locale.toDouble(s: QString("12.400"), ok: &ok);
2168 QVERIFY(ok);
2169 locale.setNumberOptions(QLocale::RejectTrailingZeroesAfterDot);
2170 QCOMPARE(locale.numberOptions(), QLocale::RejectTrailingZeroesAfterDot);
2171 locale.toDouble(s: QString("1.24e+01"), ok: &ok);
2172 QVERIFY(ok);
2173 locale.toDouble(s: QString("1.2400e+01"), ok: &ok);
2174 QVERIFY(!ok);
2175 locale.toDouble(s: QString("12.4"), ok: &ok);
2176 QVERIFY(ok);
2177 locale.toDouble(s: QString("12.400"), ok: &ok);
2178 QVERIFY(!ok);
2179}
2180
2181void tst_QLocale::negativeNumbers()
2182{
2183 QLocale locale(QLocale::C);
2184
2185 bool ok;
2186 int i;
2187
2188 i = locale.toInt(s: QLatin1String("-100"), ok: &ok);
2189 QVERIFY(ok);
2190 QCOMPARE(i, -100);
2191
2192 i = locale.toInt(s: QLatin1String("-1,000"), ok: &ok);
2193 QVERIFY(ok);
2194 QCOMPARE(i, -1000);
2195
2196 i = locale.toInt(s: QLatin1String("-1000"), ok: &ok);
2197 QVERIFY(ok);
2198 QCOMPARE(i, -1000);
2199
2200 i = locale.toInt(s: QLatin1String("-10,000"), ok: &ok);
2201 QVERIFY(ok);
2202 QCOMPARE(i, -10000);
2203
2204 i = locale.toInt(s: QLatin1String("-10000"), ok: &ok);
2205 QVERIFY(ok);
2206 QCOMPARE(i, -10000);
2207
2208 i = locale.toInt(s: QLatin1String("-100,000"), ok: &ok);
2209 QVERIFY(ok);
2210 QCOMPARE(i, -100000);
2211
2212 i = locale.toInt(s: QLatin1String("-100000"), ok: &ok);
2213 QVERIFY(ok);
2214 QCOMPARE(i, -100000);
2215
2216 i = locale.toInt(s: QLatin1String("-1,000,000"), ok: &ok);
2217 QVERIFY(ok);
2218 QCOMPARE(i, -1000000);
2219
2220 i = locale.toInt(s: QLatin1String("-1000000"), ok: &ok);
2221 QVERIFY(ok);
2222 QCOMPARE(i, -1000000);
2223}
2224
2225#include <private/qlocale_p.h>
2226#include <private/qlocale_data_p.h>
2227
2228static const int locale_data_count = sizeof(locale_data)/sizeof(locale_data[0]);
2229
2230void tst_QLocale::testNames_data()
2231{
2232 QTest::addColumn<int>(name: "language");
2233 QTest::addColumn<int>(name: "country");
2234
2235 QLocale::setDefault(QLocale(QLocale::C)); // Ensures predictable fall-backs
2236
2237 for (int i = 0; i < locale_data_count; ++i) {
2238 const QLocaleData &item = locale_data[i];
2239
2240 const QString testName = QLatin1String("data_") + QString::number(i) + QLatin1String(" (")
2241 + QLocale::languageToString(language: (QLocale::Language)item.m_language_id)
2242 + QLatin1Char('/') + QLocale::countryToString(country: (QLocale::Country)item.m_country_id)
2243 + QLatin1Char(')');
2244 QTest::newRow(dataTag: testName.toLatin1().constData()) << (int)item.m_language_id << (int)item.m_country_id;
2245 }
2246}
2247
2248void tst_QLocale::testNames()
2249{
2250 QFETCH(int, language);
2251 QFETCH(int, country);
2252
2253 QLocale l1((QLocale::Language)language, (QLocale::Country)country);
2254 if (language == QLocale::AnyLanguage && country == QLocale::AnyCountry)
2255 language = QLocale::C;
2256 QCOMPARE((int)l1.language(), language);
2257 QCOMPARE((int)l1.country(), country);
2258
2259 QString name = l1.name();
2260
2261 QLocale l2(name);
2262 QCOMPARE((int)l2.language(), language);
2263 QCOMPARE((int)l2.country(), country);
2264 QCOMPARE(l2.name(), name);
2265
2266 QLocale l3(name + QLatin1String("@foo"));
2267 QCOMPARE((int)l3.language(), language);
2268 QCOMPARE((int)l3.country(), country);
2269 QCOMPARE(l3.name(), name);
2270
2271 QLocale l4(name + QLatin1String(".foo"));
2272 QCOMPARE((int)l4.language(), language);
2273 QCOMPARE((int)l4.country(), country);
2274 QCOMPARE(l4.name(), name);
2275
2276 if (language != QLocale::C) {
2277 int idx = name.indexOf(c: QLatin1Char('_'));
2278 QVERIFY(idx != -1);
2279 QString lang = name.left(n: idx);
2280
2281 QCOMPARE((int)QLocale(lang).language(), language);
2282 QCOMPARE((int)QLocale(lang + QLatin1String("@foo")).language(), language);
2283 QCOMPARE((int)QLocale(lang + QLatin1String(".foo")).language(), language);
2284 }
2285}
2286
2287void tst_QLocale::dayName_data()
2288{
2289 QTest::addColumn<QString>(name: "locale_name");
2290 QTest::addColumn<QString>(name: "dayName");
2291 QTest::addColumn<int>(name: "day");
2292 QTest::addColumn<QLocale::FormatType>(name: "format");
2293
2294 QTest::newRow(dataTag: "no_NO") << QString("no_NO") << QString("tirsdag") << 2 << QLocale::LongFormat;
2295 QTest::newRow(dataTag: "nb_NO") << QString("nb_NO") << QString("tirsdag") << 2 << QLocale::LongFormat;
2296 QTest::newRow(dataTag: "nn_NO") << QString("nn_NO") << QString("tysdag") << 2 << QLocale::LongFormat;
2297
2298 QTest::newRow(dataTag: "C long") << QString("C") << QString("Sunday") << 7 << QLocale::LongFormat;
2299 QTest::newRow(dataTag: "C short") << QString("C") << QString("Sun") << 7 << QLocale::ShortFormat;
2300 QTest::newRow(dataTag: "C narrow") << QString("C") << QString("7") << 7 << QLocale::NarrowFormat;
2301
2302 QTest::newRow(dataTag: "ru_RU long")
2303 << QString("ru_RU")
2304 << QString::fromUtf8(str: "\320\262\320\276\321\201\320\272\321\200\320"
2305 "\265\321\201\320\265\320\275\321\214\320\265")
2306 << 7 << QLocale::LongFormat;
2307 QTest::newRow(dataTag: "ru_RU short")
2308 << QString("ru_RU") << QString::fromUtf8(str: "\320\262\321\201") << 7 << QLocale::ShortFormat;
2309 QTest::newRow(dataTag: "ru_RU narrow")
2310 << QString("ru_RU") << QString::fromUtf8(str: "\320\262\321\201") << 7 << QLocale::NarrowFormat;
2311}
2312
2313void tst_QLocale::dayName()
2314{
2315 QFETCH(QString, locale_name);
2316 QFETCH(QString, dayName);
2317 QFETCH(int, day);
2318 QFETCH(QLocale::FormatType, format);
2319
2320 QLocale l(locale_name);
2321 QCOMPARE(l.dayName(day, format), dayName);
2322
2323 QLocale ir("ga_IE");
2324 QCOMPARE(ir.dayName(1, QLocale::ShortFormat), QLatin1String("Luan"));
2325 QCOMPARE(ir.dayName(7, QLocale::ShortFormat), QLatin1String("Domh"));
2326
2327 QLocale gr("el_GR");
2328 QCOMPARE(gr.dayName(2, QLocale::ShortFormat), QString::fromUtf8("\316\244\317\201\316\257"));
2329 QCOMPARE(gr.dayName(4, QLocale::ShortFormat), QString::fromUtf8("\316\240\316\255\316\274"));
2330 QCOMPARE(gr.dayName(6, QLocale::ShortFormat), QString::fromUtf8("\316\243\316\254\316\262"));
2331}
2332
2333void tst_QLocale::standaloneDayName_data()
2334{
2335 QTest::addColumn<QString>(name: "locale_name");
2336 QTest::addColumn<QString>(name: "dayName");
2337 QTest::addColumn<int>(name: "day");
2338 QTest::addColumn<QLocale::FormatType>(name: "format");
2339
2340 QTest::newRow(dataTag: "no_NO") << QString("no_NO") << QString("tirsdag") << 2 << QLocale::LongFormat;
2341 QTest::newRow(dataTag: "nb_NO") << QString("nb_NO") << QString("tirsdag") << 2 << QLocale::LongFormat;
2342 QTest::newRow(dataTag: "nn_NO") << QString("nn_NO") << QString("tysdag") << 2 << QLocale::LongFormat;
2343
2344 QTest::newRow(dataTag: "C invalid: 0 long") << QString("C") << QString() << 0 << QLocale::LongFormat;
2345 QTest::newRow(dataTag: "C invalid: 0 short") << QString("C") << QString() << 0 << QLocale::ShortFormat;
2346 QTest::newRow(dataTag: "C invalid: 0 narrow") << QString("C") << QString() << 0 << QLocale::NarrowFormat;
2347 QTest::newRow(dataTag: "C invalid: 8 long") << QString("C") << QString() << 8 << QLocale::LongFormat;
2348 QTest::newRow(dataTag: "C invalid: 8 short") << QString("C") << QString() << 8 << QLocale::ShortFormat;
2349 QTest::newRow(dataTag: "C invalid: 8 narrow") << QString("C") << QString() << 8 << QLocale::NarrowFormat;
2350
2351 QTest::newRow(dataTag: "C long") << QString("C") << QString("Sunday") << 7 << QLocale::LongFormat;
2352 QTest::newRow(dataTag: "C short") << QString("C") << QString("Sun") << 7 << QLocale::ShortFormat;
2353 QTest::newRow(dataTag: "C narrow") << QString("C") << QString("S") << 7 << QLocale::NarrowFormat;
2354
2355 QTest::newRow(dataTag: "ru_RU long")
2356 << QString("ru_RU")
2357 << QString::fromUtf8(str: "\320\262\320\276\321\201\320\272\321\200\320"
2358 "\265\321\201\320\265\320\275\321\214\320\265")
2359 << 7 << QLocale::LongFormat;
2360 QTest::newRow(dataTag: "ru_RU short")
2361 << QString("ru_RU") << QString::fromUtf8(str: "\320\262\321\201") << 7 << QLocale::ShortFormat;
2362 QTest::newRow(dataTag: "ru_RU narrow")
2363 << QString("ru_RU") << QString::fromUtf8(str: "\320\222") << 7 << QLocale::NarrowFormat;
2364}
2365
2366void tst_QLocale::standaloneDayName()
2367{
2368 QFETCH(QString, locale_name);
2369 QFETCH(QString, dayName);
2370 QFETCH(int, day);
2371 QFETCH(QLocale::FormatType, format);
2372
2373 QLocale l(locale_name);
2374 QCOMPARE(l.standaloneDayName(day, format), dayName);
2375}
2376
2377void tst_QLocale::underflowOverflow()
2378{
2379 QString a(QLatin1String("0.") + QString(546, QLatin1Char('0')) + QLatin1String("1e10"));
2380 bool ok = false;
2381 double d = a.toDouble(ok: &ok);
2382 QVERIFY(!ok);
2383 QCOMPARE(d, 0.0);
2384
2385 a = QLatin1String("1e600");
2386 ok = false;
2387 d = a.toDouble(ok: &ok);
2388 QVERIFY(!ok); // detectable overflow
2389 QVERIFY(qIsInf(d));
2390
2391 a = QLatin1String("-1e600");
2392 ok = false;
2393 d = a.toDouble(ok: &ok);
2394 QVERIFY(!ok); // detectable underflow
2395 QVERIFY(qIsInf(-d));
2396
2397 a = QLatin1String("1e-600");
2398 ok = false;
2399 d = a.toDouble(ok: &ok);
2400 QVERIFY(!ok);
2401 QCOMPARE(d, 0.0);
2402
2403 a = QLatin1String("-9223372036854775809");
2404 a.toLongLong(ok: &ok);
2405 QVERIFY(!ok);
2406}
2407
2408void tst_QLocale::defaultNumberingSystem_data()
2409{
2410 QTest::addColumn<QString>(name: "expect");
2411
2412 QTest::newRow(dataTag: "sk_SK") << QStringLiteral("123");
2413 QTest::newRow(dataTag: "ta_IN") << QStringLiteral("123");
2414 QTest::newRow(dataTag: "te_IN") << QStringLiteral("123");
2415 QTest::newRow(dataTag: "hi_IN") << QStringLiteral("123");
2416 QTest::newRow(dataTag: "gu_IN") << QStringLiteral("123");
2417 QTest::newRow(dataTag: "kn_IN") << QStringLiteral("123");
2418 QTest::newRow(dataTag: "pa_IN") << QStringLiteral("123");
2419 QTest::newRow(dataTag: "ne_IN") << QString::fromUtf8(str: "१२३");
2420 QTest::newRow(dataTag: "mr_IN") << QString::fromUtf8(str: "१२३");
2421 QTest::newRow(dataTag: "ml_IN") << QStringLiteral("123");
2422 QTest::newRow(dataTag: "kok_IN") << QStringLiteral("123");
2423}
2424
2425void tst_QLocale::defaultNumberingSystem()
2426{
2427 QFETCH(QString, expect);
2428 QLatin1String name(QTest::currentDataTag());
2429 QLocale locale(name);
2430 QCOMPARE(locale.toString(123), expect);
2431}
2432
2433void tst_QLocale::ampm_data()
2434{
2435 QTest::addColumn<QString>(name: "morn");
2436 QTest::addColumn<QString>(name: "even");
2437
2438 QTest::newRow(dataTag: "C") << QStringLiteral("AM") << QStringLiteral("PM");
2439 QTest::newRow(dataTag: "de_DE") << QStringLiteral("AM") << QStringLiteral("PM");
2440 QTest::newRow(dataTag: "sv_SE") << QStringLiteral("fm") << QStringLiteral("em");
2441 QTest::newRow(dataTag: "nl_NL") << QStringLiteral("a.m.") << QStringLiteral("p.m.");
2442 QTest::newRow(dataTag: "uk_UA") << QString::fromUtf8(str: "\320\264\320\277")
2443 << QString::fromUtf8(str: "\320\277\320\277");
2444 QTest::newRow(dataTag: "tr_TR") << QString::fromUtf8(str: "\303\226\303\226")
2445 << QString::fromUtf8(str: "\303\226\123");
2446 QTest::newRow(dataTag: "id_ID") << QStringLiteral("AM") << QStringLiteral("PM");
2447 QTest::newRow(dataTag: "ta_LK") << QString::fromUtf8(str: "முற்பகல்") << QString::fromUtf8(str: "பிற்பகல்");
2448}
2449
2450void tst_QLocale::ampm()
2451{
2452 QFETCH(QString, morn);
2453 QFETCH(QString, even);
2454 QLatin1String name(QTest::currentDataTag());
2455 QLocale locale(name == QLatin1String("C") ? QLocale(QLocale::C) : QLocale(name));
2456 QCOMPARE(locale.amText(), morn);
2457 QCOMPARE(locale.pmText(), even);
2458}
2459
2460void tst_QLocale::dateFormat()
2461{
2462 const QLocale c(QLocale::C);
2463 // check that the NarrowFormat is the same as ShortFormat.
2464 QCOMPARE(c.dateFormat(QLocale::NarrowFormat), c.dateFormat(QLocale::ShortFormat));
2465
2466 const QLocale no("no_NO");
2467 QCOMPARE(no.dateFormat(QLocale::NarrowFormat), QLatin1String("dd.MM.yyyy"));
2468 QCOMPARE(no.dateFormat(QLocale::ShortFormat), QLatin1String("dd.MM.yyyy"));
2469 QCOMPARE(no.dateFormat(QLocale::LongFormat), QLatin1String("dddd d. MMMM yyyy"));
2470
2471 const QLocale ca("en_CA");
2472 QCOMPARE(ca.dateFormat(QLocale::ShortFormat), QLatin1String("yyyy-MM-dd"));
2473 QCOMPARE(ca.dateFormat(QLocale::LongFormat), QLatin1String("dddd, MMMM d, yyyy"));
2474
2475 const QLocale ja("ja_JP");
2476 QCOMPARE(ja.dateFormat(QLocale::ShortFormat), QLatin1String("yyyy/MM/dd"));
2477
2478 const QLocale ir("ga_IE");
2479 QCOMPARE(ir.dateFormat(QLocale::ShortFormat), QLatin1String("dd/MM/yyyy"));
2480
2481 const auto sys = QLocale::system(); // QTBUG-92018, ru_RU on MS
2482 const QDate date(2021, 3, 17);
2483 QCOMPARE(sys.toString(date, sys.dateFormat(QLocale::LongFormat)), sys.toString(date));
2484}
2485
2486void tst_QLocale::timeFormat()
2487{
2488 const QLocale c(QLocale::C);
2489 // check that the NarrowFormat is the same as ShortFormat.
2490 QCOMPARE(c.timeFormat(QLocale::NarrowFormat), c.timeFormat(QLocale::ShortFormat));
2491
2492 const QLocale no("no_NO");
2493 QCOMPARE(no.timeFormat(QLocale::NarrowFormat), QLatin1String("HH:mm"));
2494 QCOMPARE(no.timeFormat(QLocale::ShortFormat), QLatin1String("HH:mm"));
2495 QCOMPARE(no.timeFormat(QLocale::LongFormat), QLatin1String("HH:mm:ss t"));
2496
2497 const QLocale id("id_ID");
2498 QCOMPARE(id.timeFormat(QLocale::ShortFormat), QLatin1String("HH.mm"));
2499 QCOMPARE(id.timeFormat(QLocale::LongFormat), QLatin1String("HH.mm.ss t"));
2500
2501 const QLocale cat("ca_ES");
2502 QCOMPARE(cat.timeFormat(QLocale::ShortFormat), QLatin1String("H:mm"));
2503 QCOMPARE(cat.timeFormat(QLocale::LongFormat), QLatin1String("H:mm:ss (t)"));
2504
2505 const QLocale bra("pt_BR");
2506 QCOMPARE(bra.timeFormat(QLocale::ShortFormat), QLatin1String("HH:mm"));
2507 QCOMPARE(bra.timeFormat(QLocale::LongFormat), QLatin1String("HH:mm:ss t"));
2508}
2509
2510void tst_QLocale::dateTimeFormat()
2511{
2512 const QLocale c(QLocale::C);
2513 // check that the NarrowFormat is the same as ShortFormat.
2514 QCOMPARE(c.dateTimeFormat(QLocale::NarrowFormat), c.dateTimeFormat(QLocale::ShortFormat));
2515
2516 const QLocale no("no_NO");
2517 QCOMPARE(no.dateTimeFormat(QLocale::NarrowFormat), QLatin1String("dd.MM.yyyy HH:mm"));
2518 QCOMPARE(no.dateTimeFormat(QLocale::ShortFormat), QLatin1String("dd.MM.yyyy HH:mm"));
2519 QCOMPARE(no.dateTimeFormat(QLocale::LongFormat), QLatin1String("dddd d. MMMM yyyy HH:mm:ss t"));
2520}
2521
2522void tst_QLocale::monthName()
2523{
2524 const QLocale c(QLocale::C);
2525 QCOMPARE(c.monthName(0, QLocale::ShortFormat), QString());
2526 QCOMPARE(c.monthName(0, QLocale::LongFormat), QString());
2527 QCOMPARE(c.monthName(0, QLocale::NarrowFormat), QString());
2528 QCOMPARE(c.monthName(13, QLocale::ShortFormat), QString());
2529 QCOMPARE(c.monthName(13, QLocale::LongFormat), QString());
2530 QCOMPARE(c.monthName(13, QLocale::NarrowFormat), QString());
2531
2532 QCOMPARE(c.monthName(1, QLocale::LongFormat), QLatin1String("January"));
2533 QCOMPARE(c.monthName(1, QLocale::ShortFormat), QLatin1String("Jan"));
2534 QCOMPARE(c.monthName(1, QLocale::NarrowFormat), QLatin1String("1"));
2535
2536 const QLocale de("de_DE");
2537 QCOMPARE(de.monthName(12, QLocale::LongFormat), QLatin1String("Dezember"));
2538 QCOMPARE(de.monthName(12, QLocale::ShortFormat), QLatin1String("Dez."));
2539 // 'de' locale doesn't have narrow month name
2540 QCOMPARE(de.monthName(12, QLocale::NarrowFormat), QLatin1String("D"));
2541
2542 const QLocale ru("ru_RU");
2543 QCOMPARE(ru.monthName(1, QLocale::LongFormat),
2544 QString::fromUtf8("\321\217\320\275\320\262\320\260\321\200\321\217"));
2545 QCOMPARE(ru.monthName(1, QLocale::ShortFormat),
2546 QString::fromUtf8("\321\217\320\275\320\262\56"));
2547 QCOMPARE(ru.monthName(1, QLocale::NarrowFormat), QString::fromUtf8("\320\257"));
2548 const auto sys = QLocale::system();
2549 if (sys.language() == QLocale::Russian) // QTBUG-92018
2550 QVERIFY(sys.monthName(3) != sys.standaloneMonthName(3));
2551
2552 const QLocale ir("ga_IE");
2553 QCOMPARE(ir.monthName(1, QLocale::ShortFormat), QLatin1String("Ean"));
2554 QCOMPARE(ir.monthName(12, QLocale::ShortFormat), QLatin1String("Noll"));
2555
2556 const QLocale cz("cs_CZ");
2557 QCOMPARE(cz.monthName(1, QLocale::ShortFormat), QLatin1String("led"));
2558 QCOMPARE(cz.monthName(12, QLocale::ShortFormat), QLatin1String("pro"));
2559}
2560
2561void tst_QLocale::standaloneMonthName()
2562{
2563 const QLocale c(QLocale::C);
2564 QCOMPARE(c.monthName(0, QLocale::ShortFormat), QString());
2565 QCOMPARE(c.monthName(0, QLocale::LongFormat), QString());
2566 QCOMPARE(c.monthName(0, QLocale::NarrowFormat), QString());
2567 QCOMPARE(c.monthName(13, QLocale::ShortFormat), QString());
2568 QCOMPARE(c.monthName(13, QLocale::LongFormat), QString());
2569 QCOMPARE(c.monthName(13, QLocale::NarrowFormat), QString());
2570
2571 QCOMPARE(c.standaloneMonthName(1, QLocale::LongFormat), QLatin1String("January"));
2572 QCOMPARE(c.standaloneMonthName(1, QLocale::ShortFormat), QLatin1String("Jan"));
2573
2574 const QLocale de("de_DE");
2575 // For de_DE locale Unicode CLDR database doesn't contain standalone long months
2576 // so just checking if the return value is the same as in monthName().
2577 QCOMPARE(de.standaloneMonthName(12, QLocale::LongFormat), QLatin1String("Dezember"));
2578 QCOMPARE(de.standaloneMonthName(12, QLocale::LongFormat),
2579 de.monthName(12, QLocale::LongFormat));
2580 QCOMPARE(de.standaloneMonthName(12, QLocale::ShortFormat), QLatin1String("Dez"));
2581 QCOMPARE(de.standaloneMonthName(12, QLocale::NarrowFormat), QLatin1String("D"));
2582
2583 QLocale ru("ru_RU");
2584 QCOMPARE(ru.standaloneMonthName(1, QLocale::LongFormat),
2585 QString::fromUtf8("\xd1\x8f\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\xd1\x8c"));
2586 QCOMPARE(ru.standaloneMonthName(1, QLocale::ShortFormat),
2587 QString::fromUtf8("\xd1\x8f\xd0\xbd\xd0\xb2."));
2588 QCOMPARE(ru.standaloneMonthName(1, QLocale::NarrowFormat), QString::fromUtf8("\xd0\xaf"));
2589}
2590
2591void tst_QLocale::currency()
2592{
2593 const QLocale c(QLocale::C);
2594 QCOMPARE(c.toCurrencyString(qulonglong(1234)), QString("1234"));
2595 QCOMPARE(c.toCurrencyString(qlonglong(-1234)), QString("-1234"));
2596 QCOMPARE(c.toCurrencyString(double(1234.56)), QString("1234.56"));
2597 QCOMPARE(c.toCurrencyString(double(-1234.56)), QString("-1234.56"));
2598 QCOMPARE(c.toCurrencyString(double(-1234.5678)), QString("-1234.57"));
2599 QCOMPARE(c.toCurrencyString(double(-1234.5678), NULL, 4), QString("-1234.5678"));
2600 QCOMPARE(c.toCurrencyString(double(-1234.56), NULL, 4), QString("-1234.5600"));
2601
2602 const QLocale en_US("en_US");
2603 QCOMPARE(en_US.toCurrencyString(qulonglong(1234)), QString("$1,234"));
2604 QCOMPARE(en_US.toCurrencyString(qlonglong(-1234)), QString("($1,234)"));
2605 QCOMPARE(en_US.toCurrencyString(double(1234.56)), QString("$1,234.56"));
2606 QCOMPARE(en_US.toCurrencyString(double(-1234.56)), QString("($1,234.56)"));
2607 QCOMPARE(en_US.toCurrencyString(double(-1234.5678)), QString("($1,234.57)"));
2608 QCOMPARE(en_US.toCurrencyString(double(-1234.5678), NULL, 4), QString("($1,234.5678)"));
2609 QCOMPARE(en_US.toCurrencyString(double(-1234.56), NULL, 4), QString("($1,234.5600)"));
2610
2611 const QLocale ru_RU("ru_RU");
2612 QCOMPARE(ru_RU.toCurrencyString(qulonglong(1234)),
2613 QString::fromUtf8("1" "\xc2\xa0" "234\xc2\xa0\xe2\x82\xbd"));
2614 QCOMPARE(ru_RU.toCurrencyString(qlonglong(-1234)),
2615 QString::fromUtf8("-1" "\xc2\xa0" "234\xc2\xa0\xe2\x82\xbd"));
2616 QCOMPARE(ru_RU.toCurrencyString(double(1234.56)),
2617 QString::fromUtf8("1" "\xc2\xa0" "234,56\xc2\xa0\xe2\x82\xbd"));
2618 QCOMPARE(ru_RU.toCurrencyString(double(-1234.56)),
2619 QString::fromUtf8("-1" "\xc2\xa0" "234,56\xc2\xa0\xe2\x82\xbd"));
2620
2621 const QLocale de_DE("de_DE");
2622 QCOMPARE(de_DE.toCurrencyString(qulonglong(1234)),
2623 QString::fromUtf8("1.234\xc2\xa0\xe2\x82\xac"));
2624 QCOMPARE(de_DE.toCurrencyString(qulonglong(1234), QLatin1String("BAZ")),
2625 QString::fromUtf8("1.234\xc2\xa0" "BAZ"));
2626 QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234)),
2627 QString::fromUtf8("-1.234\xc2\xa0\xe2\x82\xac"));
2628 QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234), QLatin1String("BAZ")),
2629 QString::fromUtf8("-1.234\xc2\xa0" "BAZ"));
2630 QCOMPARE(de_DE.toCurrencyString(double(1234.56)),
2631 QString::fromUtf8("1.234,56\xc2\xa0\xe2\x82\xac"));
2632 QCOMPARE(de_DE.toCurrencyString(double(-1234.56)),
2633 QString::fromUtf8("-1.234,56\xc2\xa0\xe2\x82\xac"));
2634 QCOMPARE(de_DE.toCurrencyString(double(-1234.56), QLatin1String("BAZ")),
2635 QString::fromUtf8("-1.234,56\xc2\xa0" "BAZ"));
2636
2637 const QLocale es_CR(QLocale::Spanish, QLocale::CostaRica);
2638 QCOMPARE(es_CR.toCurrencyString(double(1565.25)),
2639 QString::fromUtf8("\xE2\x82\xA1" "1\xC2\xA0" "565,25"));
2640 QCOMPARE(es_CR.toCurrencyString(double(12565.25)),
2641 QString::fromUtf8("\xE2\x82\xA1" "12\xC2\xA0" "565,25"));
2642
2643 const QLocale system = QLocale::system();
2644 QVERIFY(system.toCurrencyString(1, QLatin1String("FOO")).contains(QLatin1String("FOO")));
2645}
2646
2647void tst_QLocale::quoteString()
2648{
2649 const QString someText("text");
2650 const QLocale c(QLocale::C);
2651 QCOMPARE(c.quoteString(someText), QString::fromUtf8("\x22" "text" "\x22"));
2652 QCOMPARE(c.quoteString(someText, QLocale::AlternateQuotation),
2653 QString::fromUtf8("\x27" "text" "\x27"));
2654
2655 const QLocale de_CH("de_CH");
2656 QCOMPARE(de_CH.quoteString(someText), QString::fromUtf8("\xe2\x80\x9e" "text" "\xe2\x80\x9c"));
2657 QCOMPARE(de_CH.quoteString(someText, QLocale::AlternateQuotation),
2658 QString::fromUtf8("\xe2\x80\x9a" "text" "\xe2\x80\x98"));
2659
2660}
2661
2662void tst_QLocale::uiLanguages()
2663{
2664 const QLocale c(QLocale::C);
2665 QCOMPARE(c.uiLanguages().size(), 1);
2666 QCOMPARE(c.uiLanguages().at(0), QLatin1String("C"));
2667
2668 const QLocale en_US("en_US");
2669 QCOMPARE(en_US.uiLanguages().size(), 3);
2670 QCOMPARE(en_US.uiLanguages().at(0), QLatin1String("en"));
2671 QCOMPARE(en_US.uiLanguages().at(1), QLatin1String("en-US"));
2672 QCOMPARE(en_US.uiLanguages().at(2), QLatin1String("en-Latn-US"));
2673
2674 const QLocale en_Latn_US("en_Latn_US");
2675 QCOMPARE(en_Latn_US.uiLanguages().size(), 3);
2676 QCOMPARE(en_Latn_US.uiLanguages().at(0), QLatin1String("en"));
2677 QCOMPARE(en_Latn_US.uiLanguages().at(1), QLatin1String("en-US"));
2678 QCOMPARE(en_Latn_US.uiLanguages().at(2), QLatin1String("en-Latn-US"));
2679
2680 const QLocale en_GB("en_GB");
2681 QCOMPARE(en_GB.uiLanguages().size(), 2);
2682 QCOMPARE(en_GB.uiLanguages().at(0), QLatin1String("en-GB"));
2683 QCOMPARE(en_GB.uiLanguages().at(1), QLatin1String("en-Latn-GB"));
2684
2685 const QLocale en_Dsrt_US("en_Dsrt_US");
2686 QCOMPARE(en_Dsrt_US.uiLanguages().size(), 2);
2687 QCOMPARE(en_Dsrt_US.uiLanguages().at(0), QLatin1String("en-Dsrt"));
2688 QCOMPARE(en_Dsrt_US.uiLanguages().at(1), QLatin1String("en-Dsrt-US"));
2689
2690 const QLocale ru_RU("ru_RU");
2691 QCOMPARE(ru_RU.uiLanguages().size(), 3);
2692 QCOMPARE(ru_RU.uiLanguages().at(0), QLatin1String("ru"));
2693 QCOMPARE(ru_RU.uiLanguages().at(1), QLatin1String("ru-RU"));
2694 QCOMPARE(ru_RU.uiLanguages().at(2), QLatin1String("ru-Cyrl-RU"));
2695
2696 const QLocale zh_Hant("zh_Hant");
2697 QCOMPARE(zh_Hant.uiLanguages().size(), 2);
2698 QCOMPARE(zh_Hant.uiLanguages().at(0), QLatin1String("zh-TW"));
2699 QCOMPARE(zh_Hant.uiLanguages().at(1), QLatin1String("zh-Hant-TW"));
2700}
2701
2702void tst_QLocale::weekendDays()
2703{
2704 const QLocale c(QLocale::C);
2705 QList<Qt::DayOfWeek> days;
2706 days << Qt::Monday << Qt::Tuesday << Qt::Wednesday << Qt::Thursday << Qt::Friday;
2707 QCOMPARE(c.weekdays(), days);
2708}
2709
2710void tst_QLocale::listPatterns()
2711{
2712 QStringList sl1;
2713 QStringList sl2;
2714 sl2 << "aaa";
2715 QStringList sl3;
2716 sl3 << "aaa" << "bbb";
2717 QStringList sl4;
2718 sl4 << "aaa" << "bbb" << "ccc";
2719 QStringList sl5;
2720 sl5 << "aaa" << "bbb" << "ccc" << "ddd";
2721
2722 const QLocale c(QLocale::C);
2723 QCOMPARE(c.createSeparatedList(sl1), QString(""));
2724 QCOMPARE(c.createSeparatedList(sl2), QString("aaa"));
2725 QCOMPARE(c.createSeparatedList(sl3), QString("aaa, bbb"));
2726 QCOMPARE(c.createSeparatedList(sl4), QString("aaa, bbb, ccc"));
2727 QCOMPARE(c.createSeparatedList(sl5), QString("aaa, bbb, ccc, ddd"));
2728
2729 const QLocale en_US("en_US");
2730 QCOMPARE(en_US.createSeparatedList(sl1), QString(""));
2731 QCOMPARE(en_US.createSeparatedList(sl2), QString("aaa"));
2732 QCOMPARE(en_US.createSeparatedList(sl3), QString("aaa and bbb"));
2733 QCOMPARE(en_US.createSeparatedList(sl4), QString("aaa, bbb, and ccc"));
2734 QCOMPARE(en_US.createSeparatedList(sl5), QString("aaa, bbb, ccc, and ddd"));
2735
2736 const QLocale zh_CN("zh_CN");
2737 QCOMPARE(zh_CN.createSeparatedList(sl1), QString(""));
2738 QCOMPARE(zh_CN.createSeparatedList(sl2), QString("aaa"));
2739 QCOMPARE(zh_CN.createSeparatedList(sl3), QString::fromUtf8("aaa" "\xe5\x92\x8c" "bbb"));
2740 QCOMPARE(zh_CN.createSeparatedList(sl4),
2741 QString::fromUtf8("aaa" "\xe3\x80\x81" "bbb" "\xe5\x92\x8c" "ccc"));
2742 QCOMPARE(zh_CN.createSeparatedList(sl5),
2743 QString::fromUtf8("aaa" "\xe3\x80\x81" "bbb" "\xe3\x80\x81"
2744 "ccc" "\xe5\x92\x8c" "ddd"));
2745}
2746
2747void tst_QLocale::measurementSystems_data()
2748{
2749 QTest::addColumn<QLocale>(name: "locale");
2750 QTest::addColumn<QLocale::MeasurementSystem>(name: "system");
2751 QTest::newRow(dataTag: "en_US") << QLocale(QLocale::English, QLocale::UnitedStates) << QLocale::ImperialUSSystem;
2752 QTest::newRow(dataTag: "en_GB") << QLocale(QLocale::English, QLocale::UnitedKingdom) << QLocale::ImperialUKSystem;
2753 QTest::newRow(dataTag: "en_AU") << QLocale(QLocale::English, QLocale::Australia) << QLocale::MetricSystem;
2754 QTest::newRow(dataTag: "de") << QLocale(QLocale::German) << QLocale::MetricSystem;
2755}
2756
2757void tst_QLocale::measurementSystems()
2758{
2759 QFETCH(QLocale, locale);
2760 QFETCH(QLocale::MeasurementSystem, system);
2761 QCOMPARE(locale.measurementSystem(), system);
2762}
2763
2764void tst_QLocale::QTBUG_26035_positivesign()
2765{
2766 QLocale locale(QLocale::C);
2767 bool ok (false);
2768 QCOMPARE(locale.toInt(QString("+100,000"), &ok), 100000);
2769 QVERIFY(ok);
2770 ok = false;
2771 QCOMPARE(locale.toInt(QString("+100,000,000"), &ok), 100000000);
2772 QVERIFY(ok);
2773 ok = false;
2774 QCOMPARE(locale.toLongLong(QString("+100,000"), &ok), (qlonglong)100000);
2775 QVERIFY(ok);
2776 ok = false;
2777 QCOMPARE(locale.toLongLong(QString("+100,000,000"), &ok), (qlonglong)100000000);
2778 QVERIFY(ok);
2779}
2780
2781void tst_QLocale::textDirection_data()
2782{
2783 QTest::addColumn<int>(name: "language");
2784 QTest::addColumn<int>(name: "script");
2785 QTest::addColumn<bool>(name: "rightToLeft");
2786
2787 for (int language = QLocale::C; language <= QLocale::LastLanguage; ++language) {
2788 bool rightToLeft = false;
2789 switch (language) {
2790 // based on likelySubtags for RTL scripts
2791#if QT_DEPRECATED_SINCE(5, 15)
2792 case QLocale::AncientNorthArabian:
2793 case QLocale::ClassicalMandaic:
2794 case QLocale::Lydian:
2795 case QLocale::ManichaeanMiddlePersian:
2796 case QLocale::Meroitic:
2797 case QLocale::OldTurkish:
2798 case QLocale::Parthian:
2799 case QLocale::PrakritLanguage:
2800 case QLocale::Sabaean:
2801 case QLocale::Samaritan:
2802#endif
2803 case QLocale::AncientGreek:
2804 case QLocale::Arabic:
2805 case QLocale::Aramaic:
2806 case QLocale::Avestan:
2807 case QLocale::CentralKurdish:
2808 case QLocale::Divehi:
2809// case QLocale::Fulah:
2810// case QLocale::Hausa:
2811 case QLocale::Hebrew:
2812// case QLocale::Hungarian:
2813 case QLocale::Kashmiri:
2814// case QLocale::Kurdish:
2815 case QLocale::Mandingo:
2816 case QLocale::Mazanderani:
2817 case QLocale::Mende:
2818 case QLocale::Nko:
2819 case QLocale::NorthernLuri:
2820 case QLocale::Pahlavi:
2821 case QLocale::Pashto:
2822 case QLocale::Persian:
2823 case QLocale::Phoenician:
2824 case QLocale::Sindhi:
2825 case QLocale::SouthernKurdish:
2826 case QLocale::Syriac:
2827 case QLocale::Uighur:
2828 case QLocale::Urdu:
2829 case QLocale::WesternBalochi:
2830 case QLocale::Yiddish:
2831 // false if there is no locale data for language:
2832 rightToLeft = (QLocale(QLocale::Language(language)).language()
2833 == QLocale::Language(language));
2834 break;
2835 default:
2836 break;
2837 }
2838 const QLatin1String testName = QLocalePrivate::languageToCode(language: QLocale::Language(language));
2839 QTest::newRow(qPrintable(testName)) << language << int(QLocale::AnyScript) << rightToLeft;
2840 }
2841 QTest::newRow(dataTag: "pa_Arab") << int(QLocale::Punjabi) << int(QLocale::ArabicScript) << true;
2842 QTest::newRow(dataTag: "uz_Arab") << int(QLocale::Uzbek) << int(QLocale::ArabicScript) << true;
2843}
2844
2845void tst_QLocale::textDirection()
2846{
2847 QFETCH(int, language);
2848 QFETCH(int, script);
2849 QFETCH(bool, rightToLeft);
2850
2851 QLocale locale(QLocale::Language(language), QLocale::Script(script), QLocale::AnyCountry);
2852 QCOMPARE(locale.textDirection() == Qt::RightToLeft, rightToLeft);
2853}
2854
2855void tst_QLocale::formattedDataSize_data()
2856{
2857 QTest::addColumn<QLocale::Language>(name: "language");
2858 QTest::addColumn<int>(name: "decimalPlaces");
2859 QTest::addColumn<QLocale::DataSizeFormats>(name: "units");
2860 QTest::addColumn<int>(name: "bytes");
2861 QTest::addColumn<QString>(name: "output");
2862
2863 struct {
2864 const char *name;
2865 QLocale::Language lang;
2866 const char *bytes;
2867 const char abbrev;
2868 const char sep; // decimal separator
2869 } data[] = {
2870 { .name: "English", .lang: QLocale::English, .bytes: "bytes", .abbrev: 'B', .sep: '.' },
2871 { .name: "French", .lang: QLocale::French, .bytes: "octets", .abbrev: 'o', .sep: ',' },
2872 { .name: "C", .lang: QLocale::C, .bytes: "bytes", .abbrev: 'B', .sep: '.' }
2873 };
2874
2875 for (const auto row : data) {
2876#define ROWB(id, deci, num, text) \
2877 QTest::addRow("%s-%s", row.name, id) \
2878 << row.lang << deci << format \
2879 << num << (QString(text) + QChar(' ') + QString(row.bytes))
2880#define ROWQ(id, deci, num, head, tail) \
2881 QTest::addRow("%s-%s", row.name, id) \
2882 << row.lang << deci << format \
2883 << num << (QString(head) + QChar(row.sep) + QString(tail) + QChar(row.abbrev))
2884
2885 // Metatype system fails to handle raw enum members as format; needs variable
2886 {
2887 const QLocale::DataSizeFormats format = QLocale::DataSizeIecFormat;
2888 ROWB("IEC-0", 2, 0, "0");
2889 ROWB("IEC-10", 2, 10, "10");
2890 ROWQ("IEC-12Ki", 2, 12345, "12", "06 Ki");
2891 ROWQ("IEC-16Ki", 2, 16384, "16", "00 Ki");
2892 ROWQ("IEC-1235k", 2, 1234567, "1", "18 Mi");
2893 ROWQ("IEC-1374k", 2, 1374744, "1", "31 Mi");
2894 ROWQ("IEC-1234M", 2, 1234567890, "1", "15 Gi");
2895 }
2896 {
2897 const QLocale::DataSizeFormats format = QLocale::DataSizeTraditionalFormat;
2898 ROWB("Trad-0", 2, 0, "0");
2899 ROWB("Trad-10", 2, 10, "10");
2900 ROWQ("Trad-12Ki", 2, 12345, "12", "06 k");
2901 ROWQ("Trad-16Ki", 2, 16384, "16", "00 k");
2902 ROWQ("Trad-1235k", 2, 1234567, "1", "18 M");
2903 ROWQ("Trad-1374k", 2, 1374744, "1", "31 M");
2904 ROWQ("Trad-1234M", 2, 1234567890, "1", "15 G");
2905 }
2906 {
2907 const QLocale::DataSizeFormats format = QLocale::DataSizeSIFormat;
2908 ROWB("Decimal-0", 2, 0, "0");
2909 ROWB("Decimal-10", 2, 10, "10");
2910 ROWQ("Decimal-16Ki", 2, 16384, "16", "38 k");
2911 ROWQ("Decimal-1234k", 2, 1234567, "1", "23 M");
2912 ROWQ("Decimal-1374k", 2, 1374744, "1", "37 M");
2913 ROWQ("Decimal-1234M", 2, 1234567890, "1", "23 G");
2914 }
2915#undef ROWQ
2916#undef ROWB
2917 }
2918
2919 // Languages which don't use a Latin alphabet
2920
2921 const QLocale::DataSizeFormats iecFormat = QLocale::DataSizeIecFormat;
2922 const QLocale::DataSizeFormats traditionalFormat = QLocale::DataSizeTraditionalFormat;
2923 const QLocale::DataSizeFormats siFormat = QLocale::DataSizeSIFormat;
2924 const QLocale::Language lang = QLocale::Russian;
2925
2926 QTest::newRow(dataTag: "Russian-IEC-0") << lang << 2 << iecFormat << 0 << QString("0 \u0431\u0430\u0439\u0442\u044B");
2927 QTest::newRow(dataTag: "Russian-IEC-10") << lang << 2 << iecFormat << 10 << QString("10 \u0431\u0430\u0439\u0442\u044B");
2928 // CLDR doesn't provide IEC prefixes (yet?) so they aren't getting translated
2929 QTest::newRow(dataTag: "Russian-IEC-12Ki") << lang << 2 << iecFormat << 12345 << QString("12,06 KiB");
2930 QTest::newRow(dataTag: "Russian-IEC-16Ki") << lang << 2 << iecFormat << 16384 << QString("16,00 KiB");
2931 QTest::newRow(dataTag: "Russian-IEC-1235k") << lang << 2 << iecFormat << 1234567 << QString("1,18 MiB");
2932 QTest::newRow(dataTag: "Russian-IEC-1374k") << lang << 2 << iecFormat << 1374744 << QString("1,31 MiB");
2933 QTest::newRow(dataTag: "Russian-IEC-1234M") << lang << 2 << iecFormat << 1234567890 << QString("1,15 GiB");
2934
2935 QTest::newRow(dataTag: "Russian-Trad-0") << lang << 2 << traditionalFormat << 0 << QString("0 \u0431\u0430\u0439\u0442\u044B");
2936 QTest::newRow(dataTag: "Russian-Trad-10") << lang << 2 << traditionalFormat << 10 << QString("10 \u0431\u0430\u0439\u0442\u044B");
2937 QTest::newRow(dataTag: "Russian-Trad-12Ki") << lang << 2 << traditionalFormat << 12345 << QString("12,06 \u043A\u0411");
2938 QTest::newRow(dataTag: "Russian-Trad-16Ki") << lang << 2 << traditionalFormat << 16384 << QString("16,00 \u043A\u0411");
2939 QTest::newRow(dataTag: "Russian-Trad-1235k") << lang << 2 << traditionalFormat << 1234567 << QString("1,18 \u041C\u0411");
2940 QTest::newRow(dataTag: "Russian-Trad-1374k") << lang << 2 << traditionalFormat << 1374744 << QString("1,31 \u041C\u0411");
2941 QTest::newRow(dataTag: "Russian-Trad-1234M") << lang << 2 << traditionalFormat << 1234567890 << QString("1,15 \u0413\u0411");
2942
2943 QTest::newRow(dataTag: "Russian-Decimal-0") << lang << 2 << siFormat << 0 << QString("0 \u0431\u0430\u0439\u0442\u044B");
2944 QTest::newRow(dataTag: "Russian-Decimal-10") << lang << 2 << siFormat << 10 << QString("10 \u0431\u0430\u0439\u0442\u044B");
2945 QTest::newRow(dataTag: "Russian-Decimal-16Ki") << lang << 2 << siFormat << 16384 << QString("16,38 \u043A\u0411");
2946 QTest::newRow(dataTag: "Russian-Decimal-1234k") << lang << 2 << siFormat << 1234567 << QString("1,23 \u041C\u0411");
2947 QTest::newRow(dataTag: "Russian-Decimal-1374k") << lang << 2 << siFormat << 1374744 << QString("1,37 \u041C\u0411");
2948 QTest::newRow(dataTag: "Russian-Decimal-1234M") << lang << 2 << siFormat << 1234567890 << QString("1,23 \u0413\u0411");
2949}
2950
2951void tst_QLocale::formattedDataSize()
2952{
2953 QFETCH(QLocale::Language, language);
2954 QFETCH(int, decimalPlaces);
2955 QFETCH(QLocale::DataSizeFormats, units);
2956 QFETCH(int, bytes);
2957 QFETCH(QString, output);
2958 QCOMPARE(QLocale(language).formattedDataSize(bytes, decimalPlaces, units), output);
2959}
2960
2961void tst_QLocale::bcp47Name_data()
2962{
2963 QTest::addColumn<QString>(name: "expect");
2964
2965 QTest::newRow(dataTag: "C") << QStringLiteral("en");
2966 QTest::newRow(dataTag: "en") << QStringLiteral("en");
2967 QTest::newRow(dataTag: "en_US") << QStringLiteral("en");
2968 QTest::newRow(dataTag: "en_GB") << QStringLiteral("en-GB");
2969 QTest::newRow(dataTag: "en_DE") << QStringLiteral("en-DE");
2970 QTest::newRow(dataTag: "de_DE") << QStringLiteral("de");
2971 QTest::newRow(dataTag: "sr_RS") << QStringLiteral("sr");
2972 QTest::newRow(dataTag: "sr_Cyrl_RS") << QStringLiteral("sr");
2973 QTest::newRow(dataTag: "sr_Latn_RS") << QStringLiteral("sr-Latn");
2974 QTest::newRow(dataTag: "sr_ME") << QStringLiteral("sr-ME");
2975 QTest::newRow(dataTag: "sr_Cyrl_ME") << QStringLiteral("sr-Cyrl-ME");
2976 QTest::newRow(dataTag: "sr_Latn_ME") << QStringLiteral("sr-ME");
2977
2978 // Fall back to defaults when country isn't in CLDR for this language:
2979 QTest::newRow(dataTag: "sr_HR") << QStringLiteral("sr");
2980 QTest::newRow(dataTag: "sr_Cyrl_HR") << QStringLiteral("sr");
2981 QTest::newRow(dataTag: "sr_Latn_HR") << QStringLiteral("sr-Latn");
2982}
2983
2984void tst_QLocale::bcp47Name()
2985{
2986 QFETCH(QString, expect);
2987 QCOMPARE(QLocale(QLatin1String(QTest::currentDataTag())).bcp47Name(), expect);
2988}
2989
2990class MySystemLocale : public QSystemLocale
2991{
2992public:
2993 MySystemLocale(const QString &locale) : m_name(locale), m_locale(locale)
2994 {
2995 }
2996
2997 QVariant query(QueryType type, QVariant /*in*/) const override
2998 {
2999 return type == UILanguages ? QVariant(QStringList{m_name}) : QVariant();
3000 }
3001
3002 QLocale fallbackUiLocale() const override
3003 {
3004 return m_locale;
3005 }
3006
3007private:
3008 const QString m_name;
3009 const QLocale m_locale;
3010};
3011
3012void tst_QLocale::systemLocale_data()
3013{
3014 // Test uses MySystemLocale, so is platform-independent.
3015 QTest::addColumn<QString>(name: "name");
3016 QTest::addColumn<QLocale::Language>(name: "language");
3017 QTest::addColumn<QStringList>(name: "uiLanguages");
3018
3019 QTest::addRow(format: "catalan")
3020 << QString("ca") << QLocale::Catalan
3021 << QStringList{QStringLiteral("ca"), QStringLiteral("ca-ES"), QStringLiteral("ca-Latn-ES")};
3022 QTest::addRow(format: "ukrainian")
3023 << QString("uk") << QLocale::Ukrainian
3024 << QStringList{QStringLiteral("uk"), QStringLiteral("uk-UA"), QStringLiteral("uk-Cyrl-UA")};
3025 QTest::addRow(format: "german")
3026 << QString("de") << QLocale::German
3027 << QStringList{QStringLiteral("de"), QStringLiteral("de-DE"), QStringLiteral("de-Latn-DE")};
3028 QTest::addRow(format: "chinese-min")
3029 << QString("zh") << QLocale::Chinese
3030 << QStringList{QStringLiteral("zh"), QStringLiteral("zh-CN"), QStringLiteral("zh-Hans-CN")};
3031 QTest::addRow(format: "chinese-full")
3032 << QString("zh-Hans-CN") << QLocale::Chinese
3033 << QStringList{QStringLiteral("zh-Hans-CN"), QStringLiteral("zh"), QStringLiteral("zh-CN")};
3034}
3035
3036void tst_QLocale::systemLocale()
3037{
3038 QLocale originalLocale;
3039 QLocale originalSystemLocale = QLocale::system();
3040
3041 QFETCH(QString, name);
3042 QFETCH(QLocale::Language, language);
3043 QFETCH(QStringList, uiLanguages);
3044
3045 {
3046 MySystemLocale sLocale(name);
3047 QCOMPARE(QLocale().language(), language);
3048 QCOMPARE(QLocale::system().language(), language);
3049 QCOMPARE(QLocale::system().uiLanguages(), uiLanguages);
3050 }
3051
3052 QCOMPARE(QLocale(), originalLocale);
3053 QCOMPARE(QLocale::system(), originalSystemLocale);
3054}
3055
3056void tst_QLocale::IndianNumberGrouping()
3057{
3058 QLocale indian(QLocale::Hindi, QLocale::India);
3059
3060 qint8 int8 = 100;
3061 QString strResult8("100");
3062 QCOMPARE(indian.toString(int8), strResult8);
3063 QCOMPARE(indian.toShort(strResult8), short(int8));
3064
3065 quint8 uint8 = 100;
3066 QCOMPARE(indian.toString(uint8), strResult8);
3067 QCOMPARE(indian.toShort(strResult8), short(uint8));
3068
3069 // Boundary case 1000 for short and ushort
3070 short shortInt = 1000;
3071 QString strResult16("1,000");
3072 QCOMPARE(indian.toString(shortInt), strResult16);
3073 QCOMPARE(indian.toShort(strResult16), shortInt);
3074
3075 ushort uShortInt = 1000;
3076 QCOMPARE(indian.toString(uShortInt), strResult16);
3077 QCOMPARE(indian.toUShort(strResult16), uShortInt);
3078
3079 shortInt = 10000;
3080 strResult16 = "10,000";
3081 QCOMPARE(indian.toString(shortInt), strResult16);
3082 QCOMPARE(indian.toShort(strResult16), shortInt);
3083
3084 uShortInt = 10000;
3085 QCOMPARE(indian.toString(uShortInt), strResult16);
3086 QCOMPARE(indian.toUShort(strResult16), uShortInt);
3087
3088 int intInt = 1000000000;
3089 QString strResult32("1,00,00,00,000");
3090 QCOMPARE(indian.toString(intInt), strResult32);
3091 QCOMPARE(indian.toInt(strResult32), intInt);
3092
3093 uint uIntInt = 1000000000;
3094 QCOMPARE(indian.toString(uIntInt), strResult32);
3095 QCOMPARE(indian.toUInt(strResult32), uIntInt);
3096
3097 QString strResult64("10,00,00,00,00,00,00,00,000");
3098 qint64 int64 = Q_INT64_C(1000000000000000000);
3099 QCOMPARE(indian.toString(int64), strResult64);
3100 QCOMPARE(indian.toLongLong(strResult64), int64);
3101
3102 quint64 uint64 = Q_UINT64_C(1000000000000000000);
3103 QCOMPARE(indian.toString(uint64), strResult64);
3104 QCOMPARE(indian.toULongLong(strResult64), uint64);
3105}
3106
3107QTEST_MAIN(tst_QLocale)
3108#include "tst_qlocale.moc"
3109

source code of qtbase/tests/auto/corelib/text/qlocale/tst_qlocale.cpp