diff --git a/src/modules/contextualprocess/CMakeLists.txt b/src/modules/contextualprocess/CMakeLists.txt index df27dc938..2cf8d3879 100644 --- a/src/modules/contextualprocess/CMakeLists.txt +++ b/src/modules/contextualprocess/CMakeLists.txt @@ -7,3 +7,22 @@ calamares_add_plugin( contextualprocess calamares SHARED_LIB ) + +if( ECM_FOUND ) + find_package( Qt5 COMPONENTS Test REQUIRED ) + include( ECMAddTests ) + + ecm_add_test( + Tests.cpp + ContextualProcessJob.cpp # Builds it a second time + TEST_NAME + contextualprocesstest + LINK_LIBRARIES + ${CALAMARES_LIBRARIES} + calamaresui + ${YAMLCPP_LIBRARY} + Qt5::Core + Qt5::Test + ) + set_target_properties( contextualprocesstest PROPERTIES AUTOMOC TRUE ) +endif() diff --git a/src/modules/contextualprocess/ContextualProcessJob.cpp b/src/modules/contextualprocess/ContextualProcessJob.cpp index f03d53466..a61a8bb3f 100644 --- a/src/modules/contextualprocess/ContextualProcessJob.cpp +++ b/src/modules/contextualprocess/ContextualProcessJob.cpp @@ -30,31 +30,72 @@ #include "utils/CommandList.h" #include "utils/Logger.h" +struct ValueCheck : public QPair +{ + ValueCheck( const QString& value, CalamaresUtils::CommandList* commands ) + : QPair(value, commands) + { + } + + ~ValueCheck() + { + // We don't own the commandlist, the binding holding this valuecheck + // does, so don't delete. This is closely tied to (temporaries created + // by) pass-by-value in QList::append(). + } + + QString value() const { return first; } + CalamaresUtils::CommandList* commands() const { return second; } +} ; + struct ContextualProcessBinding { - ContextualProcessBinding( const QString& _n, const QString& _v, CalamaresUtils::CommandList* _c ) - : variable( _n ) - , value( _v ) - , commands( _c ) + ContextualProcessBinding( const QString& varname ) + : variable( varname ) { } ~ContextualProcessBinding(); - int count() const + /** + * @brief add commands to be executed when @p value is matched. + * + * Ownership of the CommandList passes to this binding. + */ + void append( const QString& value, CalamaresUtils::CommandList* commands ) { - return commands ? commands->count() : 0; + checks.append( ValueCheck( value, commands ) ); + if ( value == '*' ) + wildcard = commands; + } + + Calamares::JobResult run( const QString& value ) const + { + for ( const auto& c : checks ) + { + if ( value == c.value() ) + return c.commands()->run(); + } + + if ( wildcard ) + return wildcard->run(); + + return Calamares::JobResult::ok(); } QString variable; - QString value; - CalamaresUtils::CommandList* commands; + QList checks; + CalamaresUtils::CommandList* wildcard{ nullptr }; } ; ContextualProcessBinding::~ContextualProcessBinding() { - delete commands; + wildcard = nullptr; + for ( const auto& c : checks ) + { + delete c.commands(); + } } ContextualProcessJob::ContextualProcessJob( QObject* parent ) @@ -83,12 +124,14 @@ ContextualProcessJob::exec() for ( const ContextualProcessBinding* binding : m_commands ) { - if ( gs->contains( binding->variable ) && ( gs->value( binding->variable ).toString() == binding->value ) ) + if ( gs->contains( binding->variable ) ) { - Calamares::JobResult r = binding->commands->run(); + Calamares::JobResult r = binding->run( gs->value( binding->variable ).toString() ); if ( !r ) return r; } + else + cWarning() << "ContextualProcess checks for unknown variable" << binding->variable; } return Calamares::JobResult::ok(); } @@ -114,6 +157,8 @@ ContextualProcessJob::setConfigurationMap( const QVariantMap& configurationMap ) continue; } + auto binding = new ContextualProcessBinding( variableName ); + m_commands.append( binding ); QVariantMap values = iter.value().toMap(); for ( QVariantMap::const_iterator valueiter = values.cbegin(); valueiter != values.cend(); ++valueiter ) { @@ -126,15 +171,24 @@ ContextualProcessJob::setConfigurationMap( const QVariantMap& configurationMap ) CalamaresUtils::CommandList* commands = new CalamaresUtils::CommandList( valueiter.value(), !dontChroot, timeout ); - if ( commands->count() > 0 ) - { - m_commands.append( new ContextualProcessBinding( variableName, valueString, commands ) ); - cDebug() << variableName << '=' << valueString << "will execute" << commands->count() << "commands"; - } - else - delete commands; + binding->append( valueString, commands ); } } } +int +ContextualProcessJob::count() +{ + return m_commands.count(); +} + +int +ContextualProcessJob::count(const QString& variableName) +{ + for ( const ContextualProcessBinding* binding : m_commands ) + if ( binding->variable == variableName ) + return binding->checks.count(); + return -1; +} + CALAMARES_PLUGIN_FACTORY_DEFINITION( ContextualProcessJobFactory, registerPlugin(); ) diff --git a/src/modules/contextualprocess/ContextualProcessJob.h b/src/modules/contextualprocess/ContextualProcessJob.h index e8a39c3f4..fbc102058 100644 --- a/src/modules/contextualprocess/ContextualProcessJob.h +++ b/src/modules/contextualprocess/ContextualProcessJob.h @@ -43,6 +43,11 @@ public: void setConfigurationMap( const QVariantMap& configurationMap ) override; + /// The number of bindings + int count(); + /// The number of value-checks for the named binding (-1 if binding doesn't exist) + int count( const QString& variableName ); + private: QList m_commands; }; diff --git a/src/modules/contextualprocess/Tests.cpp b/src/modules/contextualprocess/Tests.cpp new file mode 100644 index 000000000..ed6d4f278 --- /dev/null +++ b/src/modules/contextualprocess/Tests.cpp @@ -0,0 +1,71 @@ +/* === This file is part of Calamares - === + * + * Copyright 2017, 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 . + */ + +#include "Tests.h" +#include "ContextualProcessJob.h" + +#include "utils/CommandList.h" +#include "utils/YamlUtils.h" + +#include + +#include + +#include +#include + +QTEST_GUILESS_MAIN( ContextualProcessTests ) + +using CommandList = CalamaresUtils::CommandList; + +ContextualProcessTests::ContextualProcessTests() +{ +} + +ContextualProcessTests::~ContextualProcessTests() +{ +} + +void +ContextualProcessTests::initTestCase() +{ +} + +void +ContextualProcessTests::testProcessListSampleConfig() +{ + YAML::Node doc; + + QStringList dirs { "src/modules/contextualprocess", "." }; + for ( const auto& dir : dirs ) + { + QString filename = dir + "/contextualprocess.conf"; + if ( QFileInfo::exists( filename ) ) + { + doc = YAML::LoadFile( filename.toStdString() ); + break; + } + } + + ContextualProcessJob job; + job.setConfigurationMap( CalamaresUtils::yamlMapToVariant( doc ).toMap() ); + + QCOMPARE(job.count(), 1); // Only "firmwareType" + QCOMPARE(job.count("firmwareType"), 4); +} + diff --git a/src/modules/contextualprocess/Tests.h b/src/modules/contextualprocess/Tests.h new file mode 100644 index 000000000..1708e53f0 --- /dev/null +++ b/src/modules/contextualprocess/Tests.h @@ -0,0 +1,37 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, 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 . + */ + +#ifndef TESTS_H +#define TESTS_H + +#include + +class ContextualProcessTests : public QObject +{ + Q_OBJECT +public: + ContextualProcessTests(); + ~ContextualProcessTests() override; + +private Q_SLOTS: + void initTestCase(); + // Check the sample config file is processed correctly + void testProcessListSampleConfig(); +}; + +#endif diff --git a/src/modules/contextualprocess/contextualprocess.conf b/src/modules/contextualprocess/contextualprocess.conf index 20668e1ce..1f148328c 100644 --- a/src/modules/contextualprocess/contextualprocess.conf +++ b/src/modules/contextualprocess/contextualprocess.conf @@ -15,12 +15,18 @@ # # You can check for an empty value with "". # +# As a special case, the value-check "*" matches any value, but **only** +# if no other value-check matches. Use it as an *else* form for value- +# checks. Take care to put the asterisk in quotes. +# # Global configuration variables are not checked in a deterministic # order, so do not rely on commands from one variable-check to # always happen before (or after) checks on another # variable. Similarly, the value-equality checks are not # done in a deterministic order, but all of the value-checks -# for a given variable happen together. +# for a given variable happen together. As a special case, the +# value-check for "*" (the *else* case) happens after all of the +# other value-checks, and only matches if none of the others do. # # The values after a value sub-keys are the same kinds of values # as can be given to the *script* key in the shellprocess module. @@ -34,3 +40,4 @@ firmwareType: timeout: 120 # This is slow bios: "-pkg remove bios-firmware" "": "/bin/false no-firmware-type-set" + "*": "/bin/false some-other-firmware-value"