Fix 'kformattest fails with installed kcoreaddons language package'
AbandonedPublic

Authored by habacker on Aug 13 2018, 8:43 AM.

Details

Reviewers
None
Group Reviewers
Frameworks
Summary

It is required to use the same translation for the reference values.

BUG:397404
FIXED-IN:5.50.0

Diff Detail

Repository
R244 KCoreAddons
Branch
master
Lint
No Linters Available
Unit
No Unit Test Coverage
Build Status
Buildable 1821
Build 1839: arc lint + arc unit
habacker created this revision.Aug 13 2018, 8:43 AM
Restricted Application added a project: Frameworks. · View Herald TranscriptAug 13 2018, 8:43 AM
Restricted Application added a subscriber: kde-frameworks-devel. · View Herald Transcript
habacker requested review of this revision.Aug 13 2018, 8:43 AM
ngraham edited the summary of this revision. (Show Details)Aug 13 2018, 8:53 AM
ngraham added reviewers: Frameworks, cfeck.
habacker updated this revision to Diff 39684.Aug 14 2018, 11:13 AM
habacker edited the summary of this revision. (Show Details)
  • after feedback from ecm maintainers the fix could be refactored to not be depending on ecm patch
aacid added a subscriber: aacid.Aug 14 2018, 11:37 AM

Personally i would include (and use) a fake translation as part of the tests and then it would just evaluate to the actual result, i.e.

QCOMPARE(format.formatSpelloutDuration(3610000), QStringLiteral("1 hour(s)"));

would end up being

QCOMPARE(format.formatSpelloutDuration(3610000), QStringLiteral("1 hour"));

because we're using the "fake" translation.

But i understand that is much more work and this makes the test pass already, so if noone disagrees i think this is good enough :)

You have remembered that in this case the loader created by ecm_create_qm_loader(kcoreaddons_QM_LOADER kcoreaddons5_qt) must be adjusted to find this - not installed - fake translation by default ?

You have remembered that in this case the loader created by ecm_create_qm_loader(kcoreaddons_QM_LOADER kcoreaddons5_qt) must be adjusted to find this - not installed - fake translation by default ?

I think that in the test we just need to call

bool QCoreApplication::installTranslator(QTranslator *translationFile)

with the location of the fake translation and since newest location is tried first it should overwrite the one from the ecm loader.

cfeck resigned from this revision.Aug 15 2018, 3:03 PM
cfeck removed a reviewer: cfeck.

I think that in the test we just need to call

bool QCoreApplication::installTranslator(QTranslator *translationFile)

with the location of the fake translation

and since newest location is tried first it should overwrite the one from the ecm loader.

According to http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qcoreapplication.cpp#n1985 new translators are prepended - not overwritten. Not sure if a missing translation in the latest file added would be search in the previous translators.

To understand you correctly: You really are thinking about generating a qm file from the kcoreaddons lib source with gettext containing real translations added with lokalize or similar to avoid this issue ?

aacid added a comment.Aug 15 2018, 9:14 PM

I think that in the test we just need to call

bool QCoreApplication::installTranslator(QTranslator *translationFile)

with the location of the fake translation

and since newest location is tried first it should overwrite the one from the ecm loader.

According to http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qcoreapplication.cpp#n1985 new translators are prepended - not overwritten.

So exactly what i said, the newly installed translator gets used first

Not sure if a missing translation in the latest file added would be search in the previous translators.

Why would it be missing?

To understand you correctly: You really are thinking about generating a qm file from the kcoreaddons lib source with gettext containing real translations added with lokalize or similar to avoid this issue ?

I am not sure what is "this issue", but what we need to do is just copy the english translation from the tarball to here and make sure it's used when the test is run.

With the drawback that the qm file need to be generated on each build and the po file needs to be updated on any translations change in the test app

BTW: I tried your approach by

  1. downloaded po file from https://websvn.kde.org/trunk/l10n-kf5/en/messages/frameworks/kcoreaddons5_qt.po?revision=1522662&view=markup
  2. did run lrelease on that file and placed it into kcoreaddons source dir as kcoreaddons5_qt.qm
  3. added to top level CMakelists.txt

add_definitions(-DCMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}")

  1. add to autotests/kformattest.cpp
void KFormatTest::initTestCase()
{
    QLocale::setDefault(QLocale::C);
    QTranslator *l = new QTranslator();
    l->load(QStringLiteral(CMAKE_SOURCE_DIR "/kcoreaddons5_qt.qm"));
    QCoreApplication::installTranslator(l);
}

without kcoreaddons-lang package installed the test case returns on a system with installed de_DE

strace -e trace=file bin/kformattest  2>&1 | grep \.qm
...
access("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", R_OK) = 0
stat("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", {st_mode=S_IFREG|0644, st_size=729, ...}) = 0
open("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5
~/src/kf5/kcoreaddons-build> bin/kformattest 
********* Start testing of KFormatTest *********
Config: Using QtTest library 5.11.1, Qt 5.11.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.8.5)
PASS   : KFormatTest::initTestCase()
PASS   : KFormatTest::formatByteSize()
PASS   : KFormatTest::formatDuration()
FAIL!  : KFormatTest::formatDecimalDuration() Compared values are not the same
   Actual   (format.formatDecimalDuration(10))   : "10 milliseconds"
   Expected (QStringLiteral("10 millisecond(s)")): "10 millisecond(s)"
   Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(302)]
FAIL!  : KFormatTest::formatSpelloutDuration() Compared values are not the same
   Actual   (format.formatSpelloutDuration(1000)): "1 second"
   Expected (QStringLiteral("1 second(s)"))      : "1 second(s)"
   Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(318)]
PASS   : KFormatTest::formatRelativeDate()
PASS   : KFormatTest::formatValue()
PASS   : KFormatTest::cleanupTestCase()
Totals: 6 passed, 2 failed, 0 skipped, 0 blacklisted, 1ms
********* Finished testing of KFormatTest *********

with installed kcoreaddons-lang

~/src/kf5/kcoreaddons-build> strace -e trace=file bin/kformattest  2>&1 | grep \.qm
access("/usr/share/locale/en/LC_MESSAGES/kcoreaddons5_qt.qm", R_OK) = 0
open("/usr/share/locale/en/LC_MESSAGES/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5
access("/usr/share/locale/de/LC_MESSAGES/kcoreaddons5_qt.qm", R_OK) = 0
open("/usr/share/locale/de/LC_MESSAGES/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5
access("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", R_OK) = 0
stat("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", {st_mode=S_IFREG|0644, st_size=729, ...}) = 0
open("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5

```~/src/kf5/kcoreaddons-build> bin/kformattest 
********* Start testing of KFormatTest *********
Config: Using QtTest library 5.11.1, Qt 5.11.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.8.5)
PASS   : KFormatTest::initTestCase()
PASS   : KFormatTest::formatByteSize()
FAIL!  : KFormatTest::formatDuration() Compared values are not the same
   Actual   (format.formatDuration(singleSecond, options)): "0 h 00 m 04 s"
   Expected (QStringLiteral("0h00m04s"))                  : "0h00m04s"
   Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(226)]
FAIL!  : KFormatTest::formatDecimalDuration() Compared values are not the same
   Actual   (format.formatDecimalDuration(10))   : "10 milliseconds"
   Expected (QStringLiteral("10 millisecond(s)")): "10 millisecond(s)"
   Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(302)]
FAIL!  : KFormatTest::formatSpelloutDuration() Compared values are not the same
   Actual   (format.formatSpelloutDuration(1000)): "1 second"
   Expected (QStringLiteral("1 second(s)"))      : "1 second(s)"
   Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(318)]
FAIL!  : KFormatTest::formatRelativeDate() Compared values are not the same
   Actual   (format.formatRelativeDate(testDate, QLocale::LongFormat)): "Heute"
   Expected (QStringLiteral("Today"))                                 : "Today"
   Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(341)]
PASS   : KFormatTest::formatValue()
PASS   : KFormatTest::cleanupTestCase()
Totals: 4 passed, 4 failed, 0 skipped, 0 blacklisted, 1ms
********* Finished testing of KFormatTest *********

Seems not to work without further changes 

What is the drawback to not load any translation in the ECM qm file loader with using QLocale::C as default locale ?
aacid added a comment.Aug 16 2018, 1:34 PM

With the drawback that the qm file need to be generated on each build and the po file needs to be updated on any translations change in the test app

BTW: I tried your approach by

  1. downloaded po file from https://websvn.kde.org/trunk/l10n-kf5/en/messages/frameworks/kcoreaddons5_qt.po?revision=1522662&view=markup
  2. did run lrelease on that file and placed it into kcoreaddons source dir as kcoreaddons5_qt.qm
  3. added to top level CMakelists.txt

    add_definitions(-DCMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}")
  4. add to autotests/kformattest.cpp

    `void KFormatTest::initTestCase() { QLocale::setDefault(QLocale::C); QTranslator *l = new QTranslator(); l->load(QStringLiteral(CMAKE_SOURCE_DIR "/kcoreaddons5_qt.qm")); QCoreApplication::installTranslator(l); } ` without kcoreaddons-lang package installed the test case returns on a system with installed de_DE

    `strace -e trace=file bin/kformattest 2>&1 | grep \.qm ... access("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", R_OK) = 0 stat("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", {st_mode=S_IFREG|0644, st_size=729, ...}) = 0 open("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5 `

    `~/src/kf5/kcoreaddons-build> bin/kformattest
    • Start testing of KFormatTest ***** Config: Using QtTest library 5.11.1, Qt 5.11.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.8.5) PASS : KFormatTest::initTestCase() PASS : KFormatTest::formatByteSize() PASS : KFormatTest::formatDuration() FAIL! : KFormatTest::formatDecimalDuration() Compared values are not the same Actual (format.formatDecimalDuration(10)) : "10 milliseconds" Expected (QStringLiteral("10 millisecond(s)")): "10 millisecond(s)" Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(302)] FAIL! : KFormatTest::formatSpelloutDuration() Compared values are not the same Actual (format.formatSpelloutDuration(1000)): "1 second" Expected (QStringLiteral("1 second(s)")) : "1 second(s)" Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(318)] PASS : KFormatTest::formatRelativeDate() PASS : KFormatTest::formatValue() PASS : KFormatTest::cleanupTestCase() Totals: 6 passed, 2 failed, 0 skipped, 0 blacklisted, 1ms
    • Finished testing of KFormatTest ***** ` with installed kcoreaddons-lang

      `~/src/kf5/kcoreaddons-build> strace -e trace=file bin/kformattest 2>&1 | grep \.qm access("/usr/share/locale/en/LC_MESSAGES/kcoreaddons5_qt.qm", R_OK) = 0 open("/usr/share/locale/en/LC_MESSAGES/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5 access("/usr/share/locale/de/LC_MESSAGES/kcoreaddons5_qt.qm", R_OK) = 0 open("/usr/share/locale/de/LC_MESSAGES/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5 access("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", R_OK) = 0 stat("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", {st_mode=S_IFREG|0644, st_size=729, ...}) = 0 open("/home/xxx/src/kf5/kcoreaddons/kcoreaddons5_qt.qm", O_RDONLY|O_CLOEXEC) = 5

      `~/src/kf5/kcoreaddons-build> bin/kformattest
    • Start testing of KFormatTest ***** Config: Using QtTest library 5.11.1, Qt 5.11.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.8.5) PASS : KFormatTest::initTestCase() PASS : KFormatTest::formatByteSize() FAIL! : KFormatTest::formatDuration() Compared values are not the same Actual (format.formatDuration(singleSecond, options)): "0 h 00 m 04 s" Expected (QStringLiteral("0h00m04s")) : "0h00m04s" Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(226)] FAIL! : KFormatTest::formatDecimalDuration() Compared values are not the same Actual (format.formatDecimalDuration(10)) : "10 milliseconds" Expected (QStringLiteral("10 millisecond(s)")): "10 millisecond(s)" Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(302)] FAIL! : KFormatTest::formatSpelloutDuration() Compared values are not the same Actual (format.formatSpelloutDuration(1000)): "1 second" Expected (QStringLiteral("1 second(s)")) : "1 second(s)" Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(318)] FAIL! : KFormatTest::formatRelativeDate() Compared values are not the same Actual (format.formatRelativeDate(testDate, QLocale::LongFormat)): "Heute" Expected (QStringLiteral("Today")) : "Today" Loc: [/home/xxx/src/kf5/kcoreaddons/autotests/kformattest.cpp(341)] PASS : KFormatTest::formatValue() PASS : KFormatTest::cleanupTestCase() Totals: 4 passed, 4 failed, 0 skipped, 0 blacklisted, 1ms
    • Finished testing of KFormatTest *****

      Seems not to work without further changes

The failures were expected, which is very weird is that you're getting different failures, i.e you're still getting Heute instead of Today even if you set the locale to C.

Did you have that problem when you made ecm not create a translator?

What is the drawback to not load any translation in the ECM qm file loader with using QLocale::C as default locale ?

I've seen some systems that default to C locale, and I would prefer them to still have the english translation working.

Cheers,

Albert

Did you have that problem when you made ecm not create a translator?

no, therefore I created D14778

What is the drawback to not load any translation in the ECM qm file loader with using QLocale::C as default locale ?

I've seen some systems that default to C locale,

Can you give a pointer to that system ?

and I would prefer them to still have the english translation working.

but then there need to be a different way to disable translation for this test because this

QCOMPARE(format.formatSpelloutDuration(3610000), QStringLiteral("1 hour(s)"));

and similar lines are not designed to use any translation.

aacid added a comment.EditedAug 19 2018, 5:13 PM

Can you please replace your setLocale call in initTestCase for

void initLocale()
{
  setenv("LC_ALL", "en_US.utf-8", 1);
}
Q_CONSTRUCTOR_FUNCTION(initLocale)

This should make it work (or at least it does here)

Can you please replace your setLocale call in initTestCase for

void initLocale()
{
  setenv("LC_ALL", "en_US.utf-8", 1);

Just to inform you: this will not work with msvc https://stackoverflow.com/questions/17258029/c-setenv-undefined-identifier-in-visual-studio and adds an additional 'kdewin' dependency to windows builds

}
Q_CONSTRUCTOR_FUNCTION(initLocale)

This should make it work (or at least it does here)

Just recognized general issues in kcoreaddons translation, which need to be fixed before - please accept D14940.

Then I will rebase this patch and check your suggestion.

dfaure added a subscriber: dfaure.Aug 20 2018, 8:03 AM

Use qputenv instead of setenv.

Example:
kio/autotests/kdirmodeltest.cpp: qputenv("LC_ALL", "en_US.UTF-8");

Thanks for this pointer - with this setting the german translation is not fetched anymore.

But there is still the question open how to deal with plurals with and without installed kcoreaddons language package.

I guess running tests without kcoreaddons-lang installed will be performed at KDE CI and running tests with kcoreaddons language package mainly happens on developers desktop systems, where KF5 is installed and a developer is working on kcoreaddons.

From what I read on Qt doc ui string with plurals always need to use a translation string with "%n millisecond(s)" (cleaned with D14940), but in the test code there are two cases

QCOMPARE(format.formatDecimalDuration(1 * MSecsInDay + 10 * MSecsInHour, 3), QStringLiteral("1.417 days"));

This one requires to have a translation installed, which translates "day(s)" to "days"

and this one

QCOMPARE(format.formatDecimalDuration(10), QStringLiteral("10 millisecond(s)"));

This fails with a translation installed because an installed english translation translates this to "10 milliseconds"

How to proceed ?

Thanks for this pointer - with this setting the german translation is not fetched anymore.

A further look into the windows implementation of the qm loader part (https://cgit.kde.org/extra-cmake-modules.git/tree/modules/ECMQmLoader.cpp.in#n65) shows that the language is fetched from QLocale::system()

QLocale::system() is initiated on unix os from LC_ALL and friends (http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qlocale_unix.cpp#n75), which could be changed with the qputenv call.

But on Windows the system locale is fetched from the current user locale (http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qlocale_win.cpp#n172), which could not be changed temporary by the test app.

To have a cross platform solution I think it is really required to disable loading translations in this test. If QLocale::C could not be used for that (Albert mentioned that) an auxiliary function provided by the qmloader is required for that.
I'm going to file a related review request.

habacker abandoned this revision.Aug 21 2018, 8:12 AM

superseeded by D14967 (tried to updated this review with arc diff, but forgot to add the annoying --verbatim argument to let phabricator know that I have a changed commit message :-/)