From 7b6ff8dd371c22ad842cb0f54063f8a71668e853 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 3 Aug 2020 15:13:51 +0200 Subject: [PATCH 1/5] [libcalamares] Minor tests for parts of RAII --- src/libcalamares/utils/Tests.cpp | 36 ++++++++++++++++++++++++++------ src/libcalamares/utils/Tests.h | 5 ++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/libcalamares/utils/Tests.cpp b/src/libcalamares/utils/Tests.cpp index a1bfcd0ed..120a9238b 100644 --- a/src/libcalamares/utils/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -1,5 +1,5 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2018 Adriaan de Groot * * @@ -26,6 +26,7 @@ #include "CalamaresUtilsSystem.h" #include "Entropy.h" #include "Logger.h" +#include "RAII.h" #include "UMask.h" #include "Yaml.h" @@ -114,15 +115,16 @@ findConf( const QDir& d ) return mine; } -void LibCalamaresTests::recursiveCompareMap(const QVariantMap& a, const QVariantMap& b, int depth ) +void +LibCalamaresTests::recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth ) { cDebug() << "Comparing depth" << depth << a.count() << b.count(); QCOMPARE( a.keys(), b.keys() ); for ( const auto& k : a.keys() ) { cDebug() << Logger::SubEntry << k; - const auto& av = a[k]; - const auto& bv = b[k]; + const auto& av = a[ k ]; + const auto& bv = b[ k ]; if ( av.typeName() != bv.typeName() ) { @@ -130,9 +132,9 @@ void LibCalamaresTests::recursiveCompareMap(const QVariantMap& a, const QVariant cDebug() << Logger::SubEntry << "b type" << bv.typeName() << bv; } QCOMPARE( av.typeName(), bv.typeName() ); - if ( av.canConvert() ) + if ( av.canConvert< QVariantMap >() ) { - recursiveCompareMap( av.toMap(), bv.toMap(), depth+1 ); + recursiveCompareMap( av.toMap(), bv.toMap(), depth + 1 ); } else { @@ -289,3 +291,25 @@ LibCalamaresTests::testOddSizedPrintable() } } } + +void +LibCalamaresTests::testBoolSetter() +{ + bool b = false; + + QVERIFY( !b ); + { + QVERIFY( !b ); + cBoolSetter< true > x( b ); + QVERIFY( b ); + } + QVERIFY( !b ); + + QVERIFY( !b ); + { + QVERIFY( !b ); + cBoolSetter< false > x( b ); + QVERIFY( !b ); // Still! + } + QVERIFY( b ); +} diff --git a/src/libcalamares/utils/Tests.h b/src/libcalamares/utils/Tests.h index 2e1cba016..5a1f3528a 100644 --- a/src/libcalamares/utils/Tests.h +++ b/src/libcalamares/utils/Tests.h @@ -1,5 +1,5 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2018 Adriaan de Groot * * @@ -50,6 +50,9 @@ private Q_SLOTS: void testPrintableEntropy(); void testOddSizedPrintable(); + /** @brief Tests the RAII bits. */ + void testBoolSetter(); + private: void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth ); }; From b5c0158ec2d6bedcbb426cdd544f6cadcca0c932 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 3 Aug 2020 19:07:47 +0200 Subject: [PATCH 2/5] [libcalamares] Some if-method-exists trickery This builds some machinery so that we can create a detector for member-functions (methods) named . Use the macro to build the machinery: DECLARE_HAS_METHOD(myFunction) then after that, has_myFunction is either std::true_type or std::false_type depending on whether T has a method myFunction. --- src/libcalamares/utils/Tests.cpp | 53 ++++++++++++++++++++++ src/libcalamares/utils/Tests.h | 3 ++ src/libcalamares/utils/Traits.h | 77 ++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 src/libcalamares/utils/Traits.h diff --git a/src/libcalamares/utils/Tests.cpp b/src/libcalamares/utils/Tests.cpp index 120a9238b..300b9b531 100644 --- a/src/libcalamares/utils/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -27,6 +27,7 @@ #include "Entropy.h" #include "Logger.h" #include "RAII.h" +#include "Traits.h" #include "UMask.h" #include "Yaml.h" @@ -313,3 +314,55 @@ LibCalamaresTests::testBoolSetter() } QVERIFY( b ); } + +/* Demonstration of Traits support for has-a-method or not. + * + * We have two classes, c1 and c2; one has a method do_the_thing() and the + * other does not. A third class, Thinginator, has a method thingify(), + * which should call do_the_thing() of its argument if it exists. + */ + +struct c1 { int do_the_thing() { return 2; } }; +struct c2 { }; + +DECLARE_HAS_METHOD(do_the_thing) + +struct Thinginator +{ +public: + /// When class T has function do_the_thing() + template< class T > int thingify( T& t, const std::true_type& ) + { + return t.do_the_thing(); + } + + template< class T > int thingify( T&, const std::false_type& ) + { + return -1; + } + + template< class T > int thingify( T& t ) + { + return thingify(t, has_do_the_thing{}); + } +} ; + + +void +LibCalamaresTests::testTraits() +{ + has_do_the_thing x{}; + has_do_the_thing y{}; + + QVERIFY(x); + QVERIFY(!y); + + c1 c1{}; + c2 c2{}; + + QCOMPARE(c1.do_the_thing(), 2); + + Thinginator t; + QCOMPARE(t.thingify(c1), 2); // Calls c1::do_the_thing() + QCOMPARE(t.thingify(c2), -1); +} diff --git a/src/libcalamares/utils/Tests.h b/src/libcalamares/utils/Tests.h index 5a1f3528a..2d630392a 100644 --- a/src/libcalamares/utils/Tests.h +++ b/src/libcalamares/utils/Tests.h @@ -53,6 +53,9 @@ private Q_SLOTS: /** @brief Tests the RAII bits. */ void testBoolSetter(); + /** @brief Tests the Traits bits. */ + void testTraits(); + private: void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth ); }; diff --git a/src/libcalamares/utils/Traits.h b/src/libcalamares/utils/Traits.h new file mode 100644 index 000000000..5d7061b7b --- /dev/null +++ b/src/libcalamares/utils/Traits.h @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSE + * + */ + +#ifndef UTILS_TRAITS_H +#define UTILS_TRAITS_H + +#include + + +namespace CalamaresUtils +{ + +/** @brief Traits machinery lives in this namespace + * + * The primary purpose of this namespace is to hold machinery that + * is created by the DECLARE_HAS_METHOD macro. + * + * The DECLARE_HAS_METHOD macro builds machinery to check whether + * a class has a particular named method. This can be used to + * specialize templates elsewhere for use with classes with, or without, + * the named method. + * + * To use the machinery (which is not that sophisticated): + * + * - Put `DECLARE_HAS_METHOD(myFunction)` somewhere in file scope. + * This puts together the machinery for detecting if `myFunction` + * is a method of some class. + * - At global scope, `has_myFunction` is now either std::true_type, + * if the type `T` has a method `T::myFunction`, or std::false_type, + * if it does not. + * + * To specialize template methods based on the presence of the named + * method, write **three** overloads: + * + * - `template myMethod(args ..., const std::true_type& )` + * This is the implementation where class T has `myFunction`. + * - `template myMethod(args ..., const std::false_type& )` + * This is the implementation without. + * - `template myMethod(args ...)` is the general implementation, + * which can call the specialized implementations with + * `return myMethod(args ..., has_myFunction{})` + */ +namespace Traits +{ +template< class > struct sfinae_true : std::true_type{}; +} +} + +#define DECLARE_HAS_METHOD(m) \ + namespace CalamaresUtils { namespace Traits { \ + struct has_ ## m { \ + template< class T > static auto f(int) -> sfinae_true; \ + template< class T > static auto f(long) -> std::false_type; \ + template< class T > using t = decltype( f (0) ); \ + }; } } \ + template< class T > using has_ ## m = CalamaresUtils::Traits:: has_ ## m ::t; + +#endif From 29cfcb01dabcd33d12654375892697c39071aaa5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 12:37:46 +0200 Subject: [PATCH 3/5] i18n: suppress Interlingue - like Esperanto before Qt 5.12, Interlingue does not seem to be supported by QLocale, so it gets turned into "C" locale, which then messes up the default language selection in the welcome page. Move it to _incomplete until QLocale does support it. FIXES #1475 --- CMakeLists.txt | 53 ++++++++++++++++---------------------------------- ci/txstats.py | 1 + 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cabd72700..8fcebae6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,20 +140,18 @@ set( CALAMARES_DESCRIPTION_SUMMARY # TODO: drop the es_ES translation from Transifex # # NOTE: move eo (Esperanto) to _ok once Qt can actually create a -# locale for it. (Qt 5.12.2 can, see check later on). -# NOTE: update these lines by running txstats.py, or copy these four lines -# and prefix each variable name with "p", so that the automatic -# checks for new languages and misspelled ones are done (that is, -# copy these four lines to four backup lines, add "p", and then update -# the original four lines with the current translations). +# locale for it. (Qt 5.12.2 can, see Translation Status section). +# NOTE: move ie (Interlingue) to _ok once Qt supports it. +# NOTE: update these lines by running `txstats.py`, or for full automation +# `txstats.py -e`. See also # # Total 68 languages set( _tx_complete ca fi_FI he hr nl pt_BR sq tg tr_TR uk zh_TW ) set( _tx_good as ast az az_AZ be cs_CZ da de es fr hi hu it_IT ja ko lt ml pt_PT ru sk sv zh_CN ) -set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id ie is mr nb +set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id is mr nb pl ro sl sr sr@latin th ) -set( _tx_incomplete bn ca@valencia eo fr_CH gu kk kn lo lv mk ne_NP +set( _tx_incomplete bn ca@valencia eo fr_CH gu ie kk kn lo lv mk ne_NP ur uz ) ### Required versions @@ -286,14 +284,6 @@ find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Concurrent Core Gui LinguistTool if( WITH_QML ) find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Quick QuickWidgets ) endif() -if( Qt5_VERSION VERSION_GREATER 5.12.1 ) - # At least Qt 5.12.2 seems to support Esperanto in QLocale - if( "eo" IN_LIST _tx_incomplete ) - message(STATUS "Esperanto support since Qt 5.12.2, enabling Esperanto locale") - list( REMOVE_ITEM _tx_incomplete "eo" ) - list( APPEND _tx_ok "eo" ) - endif() -endif() # Optional Qt parts find_package( Qt5DBus CONFIG ) @@ -429,30 +419,22 @@ set(Calamares_WITH_QML ${WITH_QML}) ### Transifex Translation status # -# Construct language lists for use. If there are p_tx* variables, -# then run an extra cmake-time check for consistency of the old -# (p_tx*) and new (_tx*) lists. +# Construct language lists for use. # -set( prev_tx ${p_tx_complete} ${p_tx_good} ${p_tx_ok} ${p_tx_incomplete} ) +if( Qt5_VERSION VERSION_GREATER 5.12.1 ) + # At least Qt 5.12.2 seems to support Esperanto in QLocale + if( "eo" IN_LIST _tx_incomplete ) + message(STATUS "Esperanto support since Qt 5.12.2, enabling Esperanto locale") + list( REMOVE_ITEM _tx_incomplete "eo" ) + list( APPEND _tx_ok "eo" ) + endif() +endif() + set( curr_tx ${_tx_complete} ${_tx_good} ${_tx_ok} ${_tx_incomplete} ) set( tx_errors OFF ) -if ( prev_tx ) - # Gone in new list - foreach( l ${prev_tx} ) - list( FIND curr_tx ${l} p_l ) - if( p_l EQUAL -1 ) - message(WARNING "Language ${l} was present in previous translations and is now absent.") - set( tx_errors ON ) - endif() - endforeach() - +if ( curr_tx ) # New in list foreach( l ${curr_tx} ) - list( FIND prev_tx ${l} p_l ) - if( p_l EQUAL -1 ) - message(WARNING "Language ${l} is new.") - set( tx_errors ON ) - endif() set( p_l "lang/calamares_${l}.ts" ) if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${p_l} ) message(WARNING "Language ${l} has no .ts file yet.") @@ -463,7 +445,6 @@ if ( prev_tx ) unset( p_l ) unset( l ) endif() -unset( prev_tx ) unset( curr_tx ) if( tx_errors ) message( FATAL_ERROR "Translation warnings, see above." ) diff --git a/ci/txstats.py b/ci/txstats.py index a9f412743..40fe3f43b 100755 --- a/ci/txstats.py +++ b/ci/txstats.py @@ -178,6 +178,7 @@ def get_tx_stats(languages, outputter, verbose): # and it's at-the-least ok. incomplete_languages = ( "eo", # Not supported by QLocale < 5.12.1 + "ie", # Not supported by Qt at least through 5.15.0 ) all_langs = [] From b27bc11975339915b60e973b8081bdbf7cc7feac Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 12:59:55 +0200 Subject: [PATCH 4/5] [libcalamares] Merge locale tests files - No need for a separate .h in most test cases --- src/libcalamares/locale/Tests.cpp | 35 +++++++++++++++++----- src/libcalamares/locale/Tests.h | 49 ------------------------------- 2 files changed, 28 insertions(+), 56 deletions(-) delete mode 100644 src/libcalamares/locale/Tests.h diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 6c5a508d7..3ee3dfdd3 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,13 +16,8 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ -#include "Tests.h" - #include "locale/LabelModel.h" #include "locale/TimeZone.h" #include "locale/TranslatableConfiguration.h" @@ -31,7 +27,26 @@ #include -QTEST_GUILESS_MAIN( LocaleTests ) +class LocaleTests : public QObject +{ + Q_OBJECT +public: + LocaleTests(); + ~LocaleTests() override; + +private Q_SLOTS: + void initTestCase(); + + void testLanguageModelCount(); + void testEsperanto(); + void testTranslatableLanguages(); + void testTranslatableConfig1(); + void testTranslatableConfig2(); + + // TimeZone testing + void testSimpleZones(); + void testComplexZones(); +}; LocaleTests::LocaleTests() {} @@ -257,3 +272,9 @@ LocaleTests::testComplexZones() QCOMPARE( r.tr(), QStringLiteral( "zxc,;* vm" ) ); // Only _ is special } } + +QTEST_GUILESS_MAIN( LocaleTests ) + +#include "utils/moc-warnings.h" + +#include "Tests.moc" diff --git a/src/libcalamares/locale/Tests.h b/src/libcalamares/locale/Tests.h deleted file mode 100644 index dfe85a865..000000000 --- a/src/libcalamares/locale/Tests.h +++ /dev/null @@ -1,49 +0,0 @@ -/* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2019 Adriaan de Groot - * - * Calamares is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Calamares is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Calamares. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * - */ - -#ifndef LIBCALAMARES_LOCALE_TESTS_H -#define LIBCALAMARES_LOCALE_TESTS_H - -#include - -class LocaleTests : public QObject -{ - Q_OBJECT -public: - LocaleTests(); - ~LocaleTests() override; - -private Q_SLOTS: - void initTestCase(); - - void testLanguageModelCount(); - void testEsperanto(); - void testTranslatableLanguages(); - void testTranslatableConfig1(); - void testTranslatableConfig2(); - - // TimeZone testing - void testSimpleZones(); - void testComplexZones(); -}; - -#endif From 3f1b31e352cb6fba908f0d1e5c1b413217951972 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 13:21:08 +0200 Subject: [PATCH 5/5] [libcalamares] Explicit tests for Interlingue - The language code "ie" is not recognized, - "ia" is, and it seems to be the post-war variant of Interlingue, so we may want to rename / relabel. The testEsperanto test -- now split into scripts and esperanto -- would have picked "ie" out of the list because it does map to C locale. --- src/libcalamares/locale/Tests.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 3ee3dfdd3..10b4ad056 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -38,10 +38,13 @@ private Q_SLOTS: void initTestCase(); void testLanguageModelCount(); - void testEsperanto(); void testTranslatableLanguages(); void testTranslatableConfig1(); void testTranslatableConfig2(); + void testLanguageScripts(); + + void testEsperanto(); + void testInterlingue(); // TimeZone testing void testSimpleZones(); @@ -55,6 +58,8 @@ LocaleTests::~LocaleTests() {} void LocaleTests::initTestCase() { + Logger::setupLogLevel( Logger::LOGDEBUG ); + // Otherwise plain get() is dubious in the TranslatableConfiguration tests QLocale::setDefault( QLocale( QStringLiteral( "en_US" ) ) ); QVERIFY( ( QLocale().name() == "C" ) || ( QLocale().name() == "en_US" ) ); @@ -80,10 +85,8 @@ LocaleTests::testLanguageModelCount() } void -LocaleTests::testEsperanto() +LocaleTests::testLanguageScripts() { - Logger::setupLogLevel( Logger::LOGDEBUG ); - const auto* m = CalamaresUtils::Locale::availableTranslations(); QVERIFY( m ); @@ -104,13 +107,33 @@ LocaleTests::testEsperanto() QVERIFY( locale.language() == QLocale::Lithuanian ? locale.country() == QLocale::Lithuania : true ); QVERIFY( locale.language() != QLocale::C ); } +} + +void +LocaleTests::testEsperanto() +{ #if QT_VERSION < QT_VERSION_CHECK( 5, 12, 2 ) QCOMPARE( QLocale( "eo" ).language(), QLocale::C ); #else QCOMPARE( QLocale( "eo" ).language(), QLocale::Esperanto ); #endif + QCOMPARE( QLocale( QLocale::Esperanto ).language(), QLocale::Esperanto ); // Probably fails on 5.12, too } +void +LocaleTests::testInterlingue() +{ + // ie / Interlingue is borked (is "ie" even the right name?) + QCOMPARE( QLocale( "ie" ).language(), QLocale::C ); + QCOMPARE( QLocale( QLocale::Interlingue ).language(), QLocale::English ); + + // "ia" exists (post-war variant of Interlingue) + QCOMPARE( QLocale( "ia" ).language(), QLocale::Interlingua ); + // "bork" does not exist + QCOMPARE( QLocale( "bork" ).language(), QLocale::C ); +} + + static const QStringList& someLanguages() {