Merge branch 'issue-1343'
- Also add documentation to CHANGES FIXES #1343 FIXES #1319
This commit is contained in:
commit
33a4b08ac0
10
CHANGES
10
CHANGES
@ -34,9 +34,15 @@ This release contains contributions from (alphabetically by first name):
|
||||
|
||||
## Modules ##
|
||||
- *packages* now reports more details in the installation progress-bar.
|
||||
- *netinstall* module supports and `expanded` key, which will pre-expand
|
||||
- *netinstall* module supports an `expanded` key, which will pre-expand
|
||||
a group (as if the user had pressed the arrow-button in the tree-view).
|
||||
This only affects the UI.
|
||||
This only affects the UI, and only the **outermost** level of groups.
|
||||
- *netinstall* module now supports a special value for *groupsUrl*.
|
||||
Setting this to *local* will read the groups directly from the
|
||||
configuration file.
|
||||
- *netinstall* groups now support a new key `immutable` which prevents
|
||||
their check-state from being changed (they are shown, or hidden,
|
||||
as usual and can be expanded).
|
||||
- Modules that use QML need a new import line. The QML file for the
|
||||
module is configured through new keys *qmlSearch* and *qmlFilename*
|
||||
(previously those were without the `qml` prefix, which invites name
|
||||
|
@ -50,7 +50,7 @@ function( calamares_add_test )
|
||||
Qt5::Test
|
||||
)
|
||||
calamares_automoc( ${TEST_NAME} )
|
||||
target_compile_definitions( ${TEST_NAME} PRIVATE -DBUILD_AS_TEST ${TEST_DEFINITIONS} )
|
||||
target_compile_definitions( ${TEST_NAME} PRIVATE -DBUILD_AS_TEST="${CMAKE_CURRENT_SOURCE_DIR}" ${TEST_DEFINITIONS} )
|
||||
if( TEST_GUI )
|
||||
target_link_libraries( ${TEST_NAME} calamaresui Qt5::Gui )
|
||||
endif()
|
||||
|
@ -2,6 +2,7 @@ calamares_add_plugin( netinstall
|
||||
TYPE viewmodule
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
Config.cpp
|
||||
NetInstallViewStep.cpp
|
||||
NetInstallPage.cpp
|
||||
PackageTreeItem.cpp
|
||||
@ -16,3 +17,14 @@ calamares_add_plugin( netinstall
|
||||
yamlcpp
|
||||
SHARED_LIB
|
||||
)
|
||||
|
||||
calamares_add_test(
|
||||
netinstalltest
|
||||
SOURCES
|
||||
Tests.cpp
|
||||
PackageTreeItem.cpp
|
||||
PackageModel.cpp
|
||||
LIBRARIES
|
||||
Qt5::Gui
|
||||
)
|
||||
|
||||
|
156
src/modules/netinstall/Config.cpp
Normal file
156
src/modules/netinstall/Config.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2016, Luca Giambonini <almack@chakraos.org>
|
||||
* Copyright 2016, Lisa Vitolo <shainer@chakraos.org>
|
||||
* Copyright 2017, Kyle Robbertze <krobbertze@gmail.com>
|
||||
* Copyright 2017-2018, 2020, Adriaan de Groot <groot@kde.org>
|
||||
* Copyright 2017, Gabriel Craciunescu <crazy@frugalware.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "network/Manager.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_model( new PackageModel( this ) )
|
||||
{
|
||||
}
|
||||
|
||||
Config::~Config() {}
|
||||
|
||||
QString
|
||||
Config::status() const
|
||||
{
|
||||
switch ( m_status )
|
||||
{
|
||||
case Status::Ok:
|
||||
return QString();
|
||||
case Status::FailedBadConfiguration:
|
||||
return tr( "Network Installation. (Disabled: Incorrect configuration)" );
|
||||
case Status::FailedBadData:
|
||||
return tr( "Network Installation. (Disabled: Received invalid groups data)" );
|
||||
case Status::FailedInternalError:
|
||||
return tr( "Network Installation. (Disabled: internal error)" );
|
||||
case Status::FailedNetworkError:
|
||||
return tr( "Network Installation. (Disabled: Unable to fetch package lists, check your network connection)" );
|
||||
}
|
||||
NOTREACHED return QString();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Config::setStatus( Status s )
|
||||
{
|
||||
m_status = s;
|
||||
emit statusChanged( status() );
|
||||
}
|
||||
|
||||
void
|
||||
Config::loadGroupList( const QVariantList& groupData )
|
||||
{
|
||||
m_model->setupModelData( groupData );
|
||||
emit statusReady();
|
||||
}
|
||||
|
||||
void
|
||||
Config::loadGroupList( const QUrl& url )
|
||||
{
|
||||
if ( !url.isValid() )
|
||||
{
|
||||
setStatus( Status::FailedBadConfiguration );
|
||||
}
|
||||
|
||||
using namespace CalamaresUtils::Network;
|
||||
|
||||
cDebug() << "NetInstall loading groups from" << url;
|
||||
QNetworkReply* reply = Manager::instance().asynchronousGet(
|
||||
url,
|
||||
RequestOptions( RequestOptions::FakeUserAgent | RequestOptions::FollowRedirect, std::chrono::seconds( 30 ) ) );
|
||||
|
||||
if ( !reply )
|
||||
{
|
||||
cDebug() << Logger::Continuation << "request failed immediately.";
|
||||
setStatus( Status::FailedBadConfiguration );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reply = reply;
|
||||
connect( reply, &QNetworkReply::finished, this, &Config::receivedGroupData );
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Convenience to zero out and deleteLater on the reply, used in dataIsHere
|
||||
struct ReplyDeleter
|
||||
{
|
||||
QNetworkReply*& p;
|
||||
|
||||
~ReplyDeleter()
|
||||
{
|
||||
if ( p )
|
||||
{
|
||||
p->deleteLater();
|
||||
}
|
||||
p = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
Config::receivedGroupData()
|
||||
{
|
||||
if ( !m_reply || !m_reply->isFinished() )
|
||||
{
|
||||
cWarning() << "NetInstall data called too early.";
|
||||
setStatus( Status::FailedInternalError );
|
||||
return;
|
||||
}
|
||||
|
||||
cDebug() << "NetInstall group data received" << m_reply->size() << "bytes from" << m_reply->url();
|
||||
|
||||
ReplyDeleter d { m_reply };
|
||||
|
||||
// If m_required is *false* then we still say we're ready
|
||||
// even if the reply is corrupt or missing.
|
||||
if ( m_reply->error() != QNetworkReply::NoError )
|
||||
{
|
||||
cWarning() << "unable to fetch netinstall package lists.";
|
||||
cDebug() << Logger::SubEntry << "Netinstall reply error: " << m_reply->error();
|
||||
cDebug() << Logger::SubEntry << "Request for url: " << m_reply->url().toString()
|
||||
<< " failed with: " << m_reply->errorString();
|
||||
setStatus( Status::FailedNetworkError );
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray yamlData = m_reply->readAll();
|
||||
try
|
||||
{
|
||||
YAML::Node groups = YAML::Load( yamlData.constData() );
|
||||
|
||||
if ( !groups.IsSequence() )
|
||||
{
|
||||
cWarning() << "NetInstall groups data does not form a sequence.";
|
||||
}
|
||||
loadGroupList( CalamaresUtils::yamlSequenceToVariant( groups ) );
|
||||
}
|
||||
catch ( YAML::Exception& e )
|
||||
{
|
||||
CalamaresUtils::explainYamlException( e, yamlData, "netinstall groups data" );
|
||||
setStatus( Status::FailedBadData );
|
||||
}
|
||||
}
|
87
src/modules/netinstall/Config.h
Normal file
87
src/modules/netinstall/Config.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016, Luca Giambonini <almack@chakraos.org>
|
||||
* Copyright 2016, Lisa Vitolo <shainer@chakraos.org>
|
||||
* Copyright 2017, Kyle Robbertze <krobbertze@gmail.com>
|
||||
* Copyright 2017-2018, 2020, Adriaan de Groot <groot@kde.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NETINSTALL_CONFIG_H
|
||||
#define NETINSTALL_CONFIG_H
|
||||
|
||||
#include "PackageModel.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( PackageModel* packageModel MEMBER m_model FINAL )
|
||||
Q_PROPERTY( QString status READ status NOTIFY statusChanged FINAL )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
virtual ~Config();
|
||||
|
||||
enum class Status
|
||||
{
|
||||
Ok,
|
||||
FailedBadConfiguration,
|
||||
FailedInternalError,
|
||||
FailedNetworkError,
|
||||
FailedBadData
|
||||
};
|
||||
|
||||
QString status() const;
|
||||
void setStatus( Status s );
|
||||
|
||||
bool required() const { return m_required; }
|
||||
void setRequired( bool r ) { m_required = r; }
|
||||
|
||||
/** @brief Retrieves the groups, with name, description and packages
|
||||
*
|
||||
* Loads data from the given URL. Once done, the data is parsed
|
||||
* and passed on to the other loadGroupList() method.
|
||||
*/
|
||||
void loadGroupList( const QUrl& url );
|
||||
|
||||
/** @brief Fill model from parsed data.
|
||||
*
|
||||
* Fills the model with a list of groups -- which can contain
|
||||
* subgroups and packages -- from @p groupData.
|
||||
*/
|
||||
void loadGroupList( const QVariantList& groupData );
|
||||
|
||||
PackageModel* model() const { return m_model; }
|
||||
|
||||
signals:
|
||||
void statusChanged( QString status ); ///< Something changed
|
||||
void statusReady(); ///< Loading groups is complete
|
||||
|
||||
private slots:
|
||||
void receivedGroupData(); ///< From async-loading group data
|
||||
|
||||
private:
|
||||
PackageModel* m_model = nullptr;
|
||||
QNetworkReply* m_reply = nullptr; // For fetching data
|
||||
Status m_status = Status::Ok;
|
||||
bool m_required = false;
|
||||
};
|
||||
|
||||
#endif
|
@ -34,22 +34,21 @@
|
||||
#include <QHeaderView>
|
||||
#include <QNetworkReply>
|
||||
|
||||
NetInstallPage::NetInstallPage( QWidget* parent )
|
||||
NetInstallPage::NetInstallPage( Config* c, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, m_config( c )
|
||||
, ui( new Ui::Page_NetInst )
|
||||
, m_reply( nullptr )
|
||||
, m_groups( nullptr )
|
||||
{
|
||||
ui->setupUi( this );
|
||||
ui->groupswidget->setModel( c->model() );
|
||||
connect( c, &Config::statusChanged, this, &NetInstallPage::setStatus );
|
||||
connect( c, &Config::statusReady, this, &NetInstallPage::expandGroups );
|
||||
|
||||
setPageTitle( nullptr );
|
||||
CALAMARES_RETRANSLATE_SLOT( &NetInstallPage::retranslate );
|
||||
}
|
||||
|
||||
NetInstallPage::~NetInstallPage()
|
||||
{
|
||||
delete m_groups;
|
||||
delete m_reply;
|
||||
}
|
||||
NetInstallPage::~NetInstallPage() {}
|
||||
|
||||
void
|
||||
NetInstallPage::setPageTitle( CalamaresUtils::Locale::TranslatedString* t )
|
||||
@ -69,146 +68,33 @@ NetInstallPage::setPageTitle( CalamaresUtils::Locale::TranslatedString* t )
|
||||
void
|
||||
NetInstallPage::retranslate()
|
||||
{
|
||||
if ( ui && m_title )
|
||||
if ( m_title )
|
||||
{
|
||||
ui->label->setText( m_title->get() ); // That's get() on the TranslatedString
|
||||
}
|
||||
ui->netinst_status->setText( m_config->status() );
|
||||
}
|
||||
|
||||
bool
|
||||
NetInstallPage::readGroups( const QByteArray& yamlData )
|
||||
{
|
||||
try
|
||||
{
|
||||
YAML::Node groups = YAML::Load( yamlData.constData() );
|
||||
|
||||
if ( !groups.IsSequence() )
|
||||
{
|
||||
cWarning() << "netinstall groups data does not form a sequence.";
|
||||
}
|
||||
Q_ASSERT( groups.IsSequence() );
|
||||
m_groups = new PackageModel( groups );
|
||||
return true;
|
||||
}
|
||||
catch ( YAML::Exception& e )
|
||||
{
|
||||
CalamaresUtils::explainYamlException( e, yamlData, "netinstall groups data" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Convenience to zero out and deleteLater on the reply, used in dataIsHere
|
||||
struct ReplyDeleter
|
||||
{
|
||||
QNetworkReply*& p;
|
||||
|
||||
~ReplyDeleter()
|
||||
{
|
||||
if ( p )
|
||||
{
|
||||
p->deleteLater();
|
||||
}
|
||||
p = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
NetInstallPage::dataIsHere()
|
||||
NetInstallPage::expandGroups()
|
||||
{
|
||||
if ( !m_reply || !m_reply->isFinished() )
|
||||
{
|
||||
cWarning() << "NetInstall data called too early.";
|
||||
return;
|
||||
}
|
||||
|
||||
cDebug() << "NetInstall group data received" << m_reply->url();
|
||||
|
||||
ReplyDeleter d { m_reply };
|
||||
|
||||
// If m_required is *false* then we still say we're ready
|
||||
// even if the reply is corrupt or missing.
|
||||
if ( m_reply->error() != QNetworkReply::NoError )
|
||||
{
|
||||
cWarning() << "unable to fetch netinstall package lists.";
|
||||
cDebug() << Logger::SubEntry << "Netinstall reply error: " << m_reply->error();
|
||||
cDebug() << Logger::SubEntry << "Request for url: " << m_reply->url().toString()
|
||||
<< " failed with: " << m_reply->errorString();
|
||||
ui->netinst_status->setText(
|
||||
tr( "Network Installation. (Disabled: Unable to fetch package lists, check your network connection)" ) );
|
||||
emit checkReady( !m_required );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !readGroups( m_reply->readAll() ) )
|
||||
{
|
||||
cWarning() << "netinstall groups data was received, but invalid.";
|
||||
cDebug() << Logger::SubEntry << "Url: " << m_reply->url().toString();
|
||||
cDebug() << Logger::SubEntry << "Headers: " << m_reply->rawHeaderList();
|
||||
ui->netinst_status->setText( tr( "Network Installation. (Disabled: Received invalid groups data)" ) );
|
||||
emit checkReady( !m_required );
|
||||
return;
|
||||
}
|
||||
|
||||
retranslate(); // For changed model
|
||||
ui->groupswidget->setModel( m_groups );
|
||||
ui->groupswidget->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
|
||||
ui->groupswidget->header()->setSectionResizeMode( 1, QHeaderView::Stretch );
|
||||
|
||||
auto* model = m_config->model();
|
||||
// Go backwards because expanding a group may cause rows to appear below it
|
||||
for ( int i = m_groups->rowCount() - 1; i >= 0; --i )
|
||||
for ( int i = model->rowCount() - 1; i >= 0; --i )
|
||||
{
|
||||
auto index = m_groups->index(i,0);
|
||||
if ( m_groups->data(index, PackageModel::MetaExpandRole).toBool() )
|
||||
auto index = model->index( i, 0 );
|
||||
if ( model->data( index, PackageModel::MetaExpandRole ).toBool() )
|
||||
{
|
||||
ui->groupswidget->setExpanded( index, true );
|
||||
}
|
||||
}
|
||||
|
||||
emit checkReady( true );
|
||||
}
|
||||
|
||||
PackageModel::PackageItemDataList
|
||||
NetInstallPage::selectedPackages() const
|
||||
{
|
||||
if ( m_groups )
|
||||
{
|
||||
return m_groups->getPackages();
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "no netinstall groups are available.";
|
||||
return PackageModel::PackageItemDataList();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetInstallPage::loadGroupList( const QString& confUrl )
|
||||
NetInstallPage::setStatus( QString s )
|
||||
{
|
||||
using namespace CalamaresUtils::Network;
|
||||
|
||||
cDebug() << "NetInstall loading groups from" << confUrl;
|
||||
QNetworkReply* reply = Manager::instance().asynchronousGet(
|
||||
QUrl( confUrl ),
|
||||
RequestOptions( RequestOptions::FakeUserAgent | RequestOptions::FollowRedirect, std::chrono::seconds( 30 ) ) );
|
||||
|
||||
if ( !reply )
|
||||
{
|
||||
cDebug() << Logger::Continuation << "request failed immediately.";
|
||||
ui->netinst_status->setText( tr( "Network Installation. (Disabled: Incorrect configuration)" ) );
|
||||
ui->netinst_status->setText( s );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reply = reply;
|
||||
connect( reply, &QNetworkReply::finished, this, &NetInstallPage::dataIsHere );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetInstallPage::setRequired( bool b )
|
||||
{
|
||||
m_required = b;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
NetInstallPage::onActivate()
|
||||
|
@ -21,6 +21,7 @@
|
||||
#ifndef NETINSTALLPAGE_H
|
||||
#define NETINSTALLPAGE_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "PackageModel.h"
|
||||
#include "PackageTreeItem.h"
|
||||
|
||||
@ -42,7 +43,7 @@ class NetInstallPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NetInstallPage( QWidget* parent = nullptr );
|
||||
NetInstallPage( Config* config, QWidget* parent = nullptr );
|
||||
virtual ~NetInstallPage();
|
||||
|
||||
/** @brief Sets the page title
|
||||
@ -58,45 +59,22 @@ public:
|
||||
|
||||
void onActivate();
|
||||
|
||||
/** @brief Retrieves the groups, with name, description and packages
|
||||
*
|
||||
* Loads data from the given URL. This should be called before
|
||||
* displaying the page.
|
||||
*/
|
||||
void loadGroupList( const QString& url );
|
||||
|
||||
// Sets the "required" state of netinstall data. Influences whether
|
||||
// corrupt or unavailable data causes checkReady() to be emitted
|
||||
// true (not-required) or false.
|
||||
void setRequired( bool );
|
||||
bool getRequired() const { return m_required; }
|
||||
|
||||
// Returns the list of packages belonging to groups that are
|
||||
// selected in the view in this given moment. No data is cached here, so
|
||||
// this function does not have constant time.
|
||||
PackageModel::PackageItemDataList selectedPackages() const;
|
||||
|
||||
public slots:
|
||||
void dataIsHere();
|
||||
|
||||
void retranslate();
|
||||
void setStatus( QString s );
|
||||
|
||||
signals:
|
||||
void checkReady( bool );
|
||||
/** @brief Expand entries that should be pre-expanded.
|
||||
*
|
||||
* Follows the *expanded* key / the startExpanded field in the
|
||||
* group entries of the model. Call this after filling up the model.
|
||||
*/
|
||||
void expandGroups();
|
||||
|
||||
private:
|
||||
// Takes the YAML data representing the groups and reads them into the
|
||||
// m_groups and m_groupOrder internal structures. See the README.md
|
||||
// of this module to know the format expected of the YAML files.
|
||||
bool readGroups( const QByteArray& yamlData );
|
||||
|
||||
Config* m_config;
|
||||
Ui::Page_NetInst* ui;
|
||||
|
||||
std::unique_ptr< CalamaresUtils::Locale::TranslatedString > m_title; // Above the treeview
|
||||
|
||||
QNetworkReply* m_reply;
|
||||
PackageModel* m_groups;
|
||||
bool m_required;
|
||||
};
|
||||
|
||||
#endif // NETINSTALLPAGE_H
|
||||
|
@ -32,12 +32,11 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( NetInstallViewStepFactory, registerPlugin<
|
||||
|
||||
NetInstallViewStep::NetInstallViewStep( QObject* parent )
|
||||
: Calamares::ViewStep( parent )
|
||||
, m_widget( new NetInstallPage() )
|
||||
, m_nextEnabled( false )
|
||||
, m_widget( new NetInstallPage( &m_config ) )
|
||||
, m_sidebarLabel( nullptr )
|
||||
, m_nextEnabled( false )
|
||||
{
|
||||
emit nextStatusChanged( true );
|
||||
connect( m_widget, &NetInstallPage::checkReady, this, &NetInstallViewStep::nextIsReady );
|
||||
connect( &m_config, &Config::statusReady, this, &NetInstallViewStep::nextIsReady );
|
||||
}
|
||||
|
||||
|
||||
@ -86,7 +85,7 @@ NetInstallViewStep::widget()
|
||||
bool
|
||||
NetInstallViewStep::isNextEnabled() const
|
||||
{
|
||||
return m_nextEnabled;
|
||||
return !m_config.required() || m_nextEnabled;
|
||||
}
|
||||
|
||||
|
||||
@ -111,10 +110,10 @@ NetInstallViewStep::isAtEnd() const
|
||||
}
|
||||
|
||||
|
||||
QList< Calamares::job_ptr >
|
||||
Calamares::JobList
|
||||
NetInstallViewStep::jobs() const
|
||||
{
|
||||
return m_jobs;
|
||||
return Calamares::JobList();
|
||||
}
|
||||
|
||||
|
||||
@ -127,7 +126,7 @@ NetInstallViewStep::onActivate()
|
||||
void
|
||||
NetInstallViewStep::onLeave()
|
||||
{
|
||||
PackageModel::PackageItemDataList packages = m_widget->selectedPackages();
|
||||
auto packages = m_config.model()->getPackages();
|
||||
cDebug() << "Netinstall: Processing" << packages.length() << "packages.";
|
||||
|
||||
static const char PACKAGEOP[] = "packageOperations";
|
||||
@ -158,13 +157,13 @@ NetInstallViewStep::onLeave()
|
||||
|
||||
for ( const auto& package : packages )
|
||||
{
|
||||
if ( package.isCritical )
|
||||
if ( package->isCritical() )
|
||||
{
|
||||
installPackages.append( package.toOperation() );
|
||||
installPackages.append( package->toOperation() );
|
||||
}
|
||||
else
|
||||
{
|
||||
tryInstallPackages.append( package.toOperation() );
|
||||
tryInstallPackages.append( package->toOperation() );
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,16 +191,16 @@ NetInstallViewStep::onLeave()
|
||||
}
|
||||
|
||||
void
|
||||
NetInstallViewStep::nextIsReady( bool b )
|
||||
NetInstallViewStep::nextIsReady()
|
||||
{
|
||||
m_nextEnabled = b;
|
||||
emit nextStatusChanged( b );
|
||||
m_nextEnabled = true;
|
||||
emit nextStatusChanged( true );
|
||||
}
|
||||
|
||||
void
|
||||
NetInstallViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
m_widget->setRequired( CalamaresUtils::getBool( configurationMap, "required", false ) );
|
||||
m_config.setRequired( CalamaresUtils::getBool( configurationMap, "required", false ) );
|
||||
|
||||
QString groupsUrl = CalamaresUtils::getString( configurationMap, "groupsUrl" );
|
||||
if ( !groupsUrl.isEmpty() )
|
||||
@ -209,7 +208,15 @@ NetInstallViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
// Keep putting groupsUrl into the global storage,
|
||||
// even though it's no longer used for in-module data-passing.
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "groupsUrl", groupsUrl );
|
||||
m_widget->loadGroupList( groupsUrl );
|
||||
if ( groupsUrl == QStringLiteral( "local" ) )
|
||||
{
|
||||
QVariantList l = configurationMap.value( "groups" ).toList();
|
||||
m_config.loadGroupList( l );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_config.loadGroupList( groupsUrl );
|
||||
}
|
||||
}
|
||||
|
||||
bool bogus = false;
|
||||
|
@ -20,6 +20,8 @@
|
||||
#ifndef NETINSTALLVIEWSTEP_H
|
||||
#define NETINSTALLVIEWSTEP_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
#include "locale/TranslatableConfiguration.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
@ -47,7 +49,7 @@ public:
|
||||
bool isAtBeginning() const override;
|
||||
bool isAtEnd() const override;
|
||||
|
||||
QList< Calamares::job_ptr > jobs() const override;
|
||||
Calamares::JobList jobs() const override;
|
||||
|
||||
void onActivate() override;
|
||||
|
||||
@ -57,13 +59,14 @@ public:
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
public slots:
|
||||
void nextIsReady( bool );
|
||||
void nextIsReady();
|
||||
|
||||
private:
|
||||
Config m_config;
|
||||
|
||||
NetInstallPage* m_widget;
|
||||
bool m_nextEnabled;
|
||||
CalamaresUtils::Locale::TranslatedString* m_sidebarLabel; // As it appears in the sidebar
|
||||
QList< Calamares::job_ptr > m_jobs;
|
||||
bool m_nextEnabled = false;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( NetInstallViewStepFactory )
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright (c) 2017, Kyle Robbertze <kyle@aims.ac.za>
|
||||
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
|
||||
* Copyright 2017-2018, 2020, Adriaan de Groot <groot@kde.org>
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,13 +19,12 @@
|
||||
|
||||
#include "PackageModel.h"
|
||||
|
||||
#include "utils/Variant.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
PackageModel::PackageModel( const YAML::Node& data, QObject* parent )
|
||||
PackageModel::PackageModel( QObject* parent )
|
||||
: QAbstractItemModel( parent )
|
||||
{
|
||||
m_rootItem = new PackageTreeItem();
|
||||
setupModelData( data, m_rootItem );
|
||||
}
|
||||
|
||||
PackageModel::~PackageModel()
|
||||
@ -36,7 +35,7 @@ PackageModel::~PackageModel()
|
||||
QModelIndex
|
||||
PackageModel::index( int row, int column, const QModelIndex& parent ) const
|
||||
{
|
||||
if ( !hasIndex( row, column, parent ) )
|
||||
if ( !m_rootItem || !hasIndex( row, column, parent ) )
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
@ -66,7 +65,7 @@ PackageModel::index( int row, int column, const QModelIndex& parent ) const
|
||||
QModelIndex
|
||||
PackageModel::parent( const QModelIndex& index ) const
|
||||
{
|
||||
if ( !index.isValid() )
|
||||
if ( !m_rootItem || !index.isValid() )
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
@ -84,7 +83,7 @@ PackageModel::parent( const QModelIndex& index ) const
|
||||
int
|
||||
PackageModel::rowCount( const QModelIndex& parent ) const
|
||||
{
|
||||
if ( parent.column() > 0 )
|
||||
if ( !m_rootItem || ( parent.column() > 0 ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -111,7 +110,7 @@ PackageModel::columnCount( const QModelIndex& ) const
|
||||
QVariant
|
||||
PackageModel::data( const QModelIndex& index, int role ) const
|
||||
{
|
||||
if ( !index.isValid() )
|
||||
if ( !m_rootItem || !index.isValid() )
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
@ -120,7 +119,7 @@ PackageModel::data( const QModelIndex& index, int role ) const
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::CheckStateRole:
|
||||
return index.column() == NameColumn ? item->isSelected() : QVariant();
|
||||
return index.column() == NameColumn ? ( item->isImmutable() ? QVariant() : item->isSelected() ) : QVariant();
|
||||
case Qt::DisplayRole:
|
||||
return item->isHidden() ? QVariant() : item->data( index.column() );
|
||||
case MetaExpandRole:
|
||||
@ -133,6 +132,11 @@ PackageModel::data( const QModelIndex& index, int role ) const
|
||||
bool
|
||||
PackageModel::setData( const QModelIndex& index, const QVariant& value, int role )
|
||||
{
|
||||
if ( !m_rootItem )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( role == Qt::CheckStateRole && index.isValid() )
|
||||
{
|
||||
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
|
||||
@ -148,12 +152,17 @@ PackageModel::setData( const QModelIndex& index, const QVariant& value, int role
|
||||
Qt::ItemFlags
|
||||
PackageModel::flags( const QModelIndex& index ) const
|
||||
{
|
||||
if ( !index.isValid() )
|
||||
if ( !m_rootItem || !index.isValid() )
|
||||
{
|
||||
return Qt::ItemFlags();
|
||||
}
|
||||
if ( index.column() == NameColumn )
|
||||
{
|
||||
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
|
||||
if ( item->isImmutable() )
|
||||
{
|
||||
return QAbstractItemModel::flags( index ); //Qt::NoItemFlags;
|
||||
}
|
||||
return Qt::ItemIsUserCheckable | QAbstractItemModel::flags( index );
|
||||
}
|
||||
return QAbstractItemModel::flags( index );
|
||||
@ -169,109 +178,84 @@ PackageModel::headerData( int section, Qt::Orientation orientation, int role ) c
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QList< PackageTreeItem::ItemData >
|
||||
PackageTreeItem::List
|
||||
PackageModel::getPackages() const
|
||||
{
|
||||
QList< PackageTreeItem* > items = getItemPackages( m_rootItem );
|
||||
if ( !m_rootItem )
|
||||
{
|
||||
return PackageTreeItem::List();
|
||||
}
|
||||
|
||||
auto items = getItemPackages( m_rootItem );
|
||||
for ( auto package : m_hiddenItems )
|
||||
{
|
||||
if ( package->hiddenSelected() )
|
||||
{
|
||||
items.append( getItemPackages( package ) );
|
||||
}
|
||||
QList< PackageTreeItem::ItemData > packages;
|
||||
for ( auto item : items )
|
||||
{
|
||||
PackageTreeItem::ItemData itemData;
|
||||
itemData.preScript = item->parentItem()->preScript(); // Only groups have hooks
|
||||
itemData.packageName = item->packageName(); // this seg faults
|
||||
itemData.postScript = item->parentItem()->postScript(); // Only groups have hooks
|
||||
itemData.isCritical = item->parentItem()->isCritical(); // Only groups are critical
|
||||
packages.append( itemData );
|
||||
}
|
||||
return packages;
|
||||
return items;
|
||||
}
|
||||
|
||||
QList< PackageTreeItem* >
|
||||
PackageTreeItem::List
|
||||
PackageModel::getItemPackages( PackageTreeItem* item ) const
|
||||
{
|
||||
QList< PackageTreeItem* > selectedPackages;
|
||||
PackageTreeItem::List selectedPackages;
|
||||
for ( int i = 0; i < item->childCount(); i++ )
|
||||
{
|
||||
if ( item->child( i )->isSelected() == Qt::Unchecked )
|
||||
auto* child = item->child( i );
|
||||
if ( child->isSelected() == Qt::Unchecked )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !item->child( i )->childCount() ) // package
|
||||
if ( child->isPackage() ) // package
|
||||
{
|
||||
selectedPackages.append( item->child( i ) );
|
||||
selectedPackages.append( child );
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedPackages.append( getItemPackages( item->child( i ) ) );
|
||||
selectedPackages.append( getItemPackages( child ) );
|
||||
}
|
||||
}
|
||||
return selectedPackages;
|
||||
}
|
||||
|
||||
static QString
|
||||
getString( const YAML::Node& itemDefinition, const char* key )
|
||||
{
|
||||
return itemDefinition[ key ] ? CalamaresUtils::yamlToVariant( itemDefinition[ key ] ).toString() : QString();
|
||||
}
|
||||
|
||||
static bool
|
||||
getBool( const YAML::Node& itemDefinition, const char* key )
|
||||
{
|
||||
return itemDefinition[ key ] ? CalamaresUtils::yamlToVariant( itemDefinition[ key ] ).toBool() : false;
|
||||
}
|
||||
|
||||
void
|
||||
PackageModel::setupModelData( const YAML::Node& data, PackageTreeItem* parent )
|
||||
PackageModel::setupModelData( const QVariantList& groupList, PackageTreeItem* parent )
|
||||
{
|
||||
for ( YAML::const_iterator it = data.begin(); it != data.end(); ++it )
|
||||
for ( const auto& group : groupList )
|
||||
{
|
||||
const YAML::Node itemDefinition = *it;
|
||||
|
||||
QString name( tr( CalamaresUtils::yamlToVariant( itemDefinition[ "name" ] ).toByteArray() ) );
|
||||
QString description( tr( CalamaresUtils::yamlToVariant( itemDefinition[ "description" ] ).toByteArray() ) );
|
||||
|
||||
PackageTreeItem::ItemData itemData;
|
||||
itemData.name = name;
|
||||
itemData.description = description;
|
||||
|
||||
itemData.preScript = getString( itemDefinition, "pre-install" );
|
||||
itemData.postScript = getString( itemDefinition, "post-install" );
|
||||
itemData.isCritical = getBool( itemDefinition, "critical" );
|
||||
itemData.isHidden = getBool( itemDefinition, "hidden" );
|
||||
itemData.startExpanded = getBool( itemDefinition, "expanded" );
|
||||
|
||||
PackageTreeItem* item = new PackageTreeItem( itemData, parent );
|
||||
|
||||
if ( itemDefinition[ "selected" ] )
|
||||
QVariantMap groupMap = group.toMap();
|
||||
if ( groupMap.isEmpty() )
|
||||
{
|
||||
item->setSelected( getBool( itemDefinition, "selected" ) ? Qt::Checked : Qt::Unchecked );
|
||||
}
|
||||
else
|
||||
{
|
||||
item->setSelected( parent->isSelected() ); // Inherit from it's parent
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( itemDefinition[ "packages" ] )
|
||||
PackageTreeItem* item = new PackageTreeItem( groupMap, parent );
|
||||
if ( groupMap.contains( "selected" ) )
|
||||
{
|
||||
for ( YAML::const_iterator packageIt = itemDefinition[ "packages" ].begin();
|
||||
packageIt != itemDefinition[ "packages" ].end();
|
||||
++packageIt )
|
||||
item->setSelected( CalamaresUtils::getBool( groupMap, "selected", false ) ? Qt::Checked : Qt::Unchecked );
|
||||
}
|
||||
if ( groupMap.contains( "packages" ) )
|
||||
{
|
||||
item->appendChild(
|
||||
new PackageTreeItem( CalamaresUtils::yamlToVariant( *packageIt ).toString(), item ) );
|
||||
for ( const auto& packageName : groupMap.value( "packages" ).toStringList() )
|
||||
{
|
||||
item->appendChild( new PackageTreeItem( packageName, item ) );
|
||||
}
|
||||
}
|
||||
if ( itemDefinition[ "subgroups" ] )
|
||||
if ( groupMap.contains( "subgroups" ) )
|
||||
{
|
||||
setupModelData( itemDefinition[ "subgroups" ], item );
|
||||
QVariantList subgroups = groupMap.value( "subgroups" ).toList();
|
||||
if ( !subgroups.isEmpty() )
|
||||
{
|
||||
setupModelData( subgroups, item );
|
||||
// The children might be checked while the parent isn't (yet).
|
||||
// Children are added to their parent (below) without affecting
|
||||
// the checked-state -- do it manually.
|
||||
item->updateSelected();
|
||||
}
|
||||
}
|
||||
|
||||
if ( item->isHidden() )
|
||||
{
|
||||
m_hiddenItems.append( item );
|
||||
@ -283,3 +267,13 @@ PackageModel::setupModelData( const YAML::Node& data, PackageTreeItem* parent )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PackageModel::setupModelData( const QVariantList& l )
|
||||
{
|
||||
emit beginResetModel();
|
||||
delete m_rootItem;
|
||||
m_rootItem = new PackageTreeItem();
|
||||
setupModelData( l, m_rootItem );
|
||||
emit endResetModel();
|
||||
}
|
||||
|
@ -37,8 +37,6 @@ class PackageModel : public QAbstractItemModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using PackageItemDataList = QList< PackageTreeItem::ItemData >;
|
||||
|
||||
// Names for columns (unused in the code)
|
||||
static constexpr const int NameColumn = 0;
|
||||
static constexpr const int DescriptionColumn = 1;
|
||||
@ -49,9 +47,11 @@ public:
|
||||
*/
|
||||
static constexpr const int MetaExpandRole = Qt::UserRole + 1;
|
||||
|
||||
explicit PackageModel( const YAML::Node& data, QObject* parent = nullptr );
|
||||
explicit PackageModel( QObject* parent = nullptr );
|
||||
~PackageModel() override;
|
||||
|
||||
void setupModelData( const QVariantList& l );
|
||||
|
||||
QVariant data( const QModelIndex& index, int role ) const override;
|
||||
bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) override;
|
||||
Qt::ItemFlags flags( const QModelIndex& index ) const override;
|
||||
@ -63,14 +63,16 @@ public:
|
||||
int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
|
||||
int columnCount( const QModelIndex& parent = QModelIndex() ) const override;
|
||||
|
||||
PackageItemDataList getPackages() const;
|
||||
QList< PackageTreeItem* > getItemPackages( PackageTreeItem* item ) const;
|
||||
PackageTreeItem::List getPackages() const;
|
||||
PackageTreeItem::List getItemPackages( PackageTreeItem* item ) const;
|
||||
|
||||
private:
|
||||
void setupModelData( const YAML::Node& data, PackageTreeItem* parent );
|
||||
friend class ItemTests;
|
||||
|
||||
PackageTreeItem* m_rootItem;
|
||||
QList< PackageTreeItem* > m_hiddenItems;
|
||||
void setupModelData( const QVariantList& l, PackageTreeItem* parent );
|
||||
|
||||
PackageTreeItem* m_rootItem = nullptr;
|
||||
PackageTreeItem::List m_hiddenItems;
|
||||
};
|
||||
|
||||
#endif // PACKAGEMODEL_H
|
||||
|
@ -20,56 +20,68 @@
|
||||
#include "PackageTreeItem.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
QVariant
|
||||
PackageTreeItem::ItemData::toOperation() const
|
||||
/** @brief Should a package be selected, given its parent's state? */
|
||||
static Qt::CheckState
|
||||
parentCheckState( PackageTreeItem* parent )
|
||||
{
|
||||
// If it's a package with a pre- or post-script, replace
|
||||
// with the more complicated datastructure.
|
||||
if ( !preScript.isEmpty() || !postScript.isEmpty() )
|
||||
if ( parent )
|
||||
{
|
||||
QMap< QString, QVariant > sdetails;
|
||||
sdetails.insert( "pre-script", preScript );
|
||||
sdetails.insert( "package", packageName );
|
||||
sdetails.insert( "post-script", postScript );
|
||||
return sdetails;
|
||||
// Avoid partially-checked .. a package can't be partial
|
||||
return parent->isSelected() == Qt::Unchecked ? Qt::Unchecked : Qt::Checked;
|
||||
}
|
||||
else
|
||||
{
|
||||
return packageName;
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( const ItemData& data, PackageTreeItem* parent )
|
||||
/** @brief Should a subgroup be marked critical?
|
||||
*
|
||||
* If set explicitly, then use that, otherwise use the parent's critical-ness.
|
||||
*/
|
||||
static bool
|
||||
parentCriticality( const QVariantMap& groupData, PackageTreeItem* parent )
|
||||
{
|
||||
if ( groupData.contains( "critical" ) )
|
||||
{
|
||||
return CalamaresUtils::getBool( groupData, "critical", false );
|
||||
}
|
||||
return parent ? parent->isCritical() : false;
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( const QString& packageName, PackageTreeItem* parent )
|
||||
: m_parentItem( parent )
|
||||
, m_data( data )
|
||||
, m_packageName( packageName )
|
||||
, m_selected( parentCheckState( parent ) )
|
||||
, m_isGroup( false )
|
||||
, m_isCritical( parent ? parent->isCritical() : false )
|
||||
, m_showReadOnly( parent ? parent->isImmutable() : false )
|
||||
{
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( const QString packageName, PackageTreeItem* parent )
|
||||
: m_parentItem( parent )
|
||||
{
|
||||
m_data.packageName = packageName;
|
||||
if ( parent != nullptr )
|
||||
{
|
||||
m_data.selected = parent->isSelected();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data.selected = Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( PackageTreeItem* parent )
|
||||
PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, PackageTreeItem* parent )
|
||||
: m_parentItem( parent )
|
||||
, m_name( CalamaresUtils::getString( groupData, "name" ) )
|
||||
, m_selected( parentCheckState( parent ) )
|
||||
, m_description( CalamaresUtils::getString( groupData, "description" ) )
|
||||
, m_preScript( CalamaresUtils::getString( groupData, "pre-install" ) )
|
||||
, m_postScript( CalamaresUtils::getString( groupData, "post-install" ) )
|
||||
, m_isGroup( true )
|
||||
, m_isCritical( parentCriticality( groupData, parent ) )
|
||||
, m_isHidden( CalamaresUtils::getBool( groupData, "hidden", false ) )
|
||||
, m_showReadOnly( CalamaresUtils::getBool( groupData, "immutable", false ) )
|
||||
, m_startExpanded( CalamaresUtils::getBool( groupData, "expanded", false ) )
|
||||
{
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem::PackageTreeItem()
|
||||
: PackageTreeItem( QString(), nullptr )
|
||||
: m_parentItem( nullptr )
|
||||
, m_name( QStringLiteral( "<root>" ) )
|
||||
, m_selected( Qt::Checked )
|
||||
, m_isGroup( true )
|
||||
{
|
||||
m_data.selected = Qt::Checked;
|
||||
m_data.name = QLatin1String( "<root>" );
|
||||
}
|
||||
|
||||
PackageTreeItem::~PackageTreeItem()
|
||||
@ -108,7 +120,7 @@ PackageTreeItem::row() const
|
||||
QVariant
|
||||
PackageTreeItem::data( int column ) const
|
||||
{
|
||||
if ( !packageName().isEmpty() ) // packages have a packagename, groups don't
|
||||
if ( isPackage() ) // packages have a packagename, groups don't
|
||||
{
|
||||
switch ( column )
|
||||
{
|
||||
@ -123,7 +135,7 @@ PackageTreeItem::data( int column ) const
|
||||
switch ( column ) // group
|
||||
{
|
||||
case 0:
|
||||
return QVariant( prettyName() );
|
||||
return QVariant( name() );
|
||||
case 1:
|
||||
return QVariant( description() );
|
||||
default:
|
||||
@ -145,47 +157,15 @@ PackageTreeItem::parentItem() const
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
PackageTreeItem::prettyName() const
|
||||
{
|
||||
return m_data.name;
|
||||
}
|
||||
|
||||
QString
|
||||
PackageTreeItem::description() const
|
||||
{
|
||||
return m_data.description;
|
||||
}
|
||||
|
||||
QString
|
||||
PackageTreeItem::preScript() const
|
||||
{
|
||||
return m_data.preScript;
|
||||
}
|
||||
|
||||
QString
|
||||
PackageTreeItem::packageName() const
|
||||
{
|
||||
return m_data.packageName;
|
||||
}
|
||||
|
||||
QString
|
||||
PackageTreeItem::postScript() const
|
||||
{
|
||||
return m_data.postScript;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageTreeItem::isHidden() const
|
||||
{
|
||||
return m_data.isHidden;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageTreeItem::hiddenSelected() const
|
||||
{
|
||||
Q_ASSERT( m_data.isHidden );
|
||||
if ( !m_data.selected )
|
||||
if ( !m_isHidden )
|
||||
{
|
||||
return m_selected != Qt::Unchecked;
|
||||
}
|
||||
|
||||
if ( m_selected == Qt::Unchecked )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -200,33 +180,21 @@ PackageTreeItem::hiddenSelected() const
|
||||
currentItem = currentItem->parentItem();
|
||||
}
|
||||
|
||||
/* Has no non-hiddent parents */
|
||||
return m_data.selected;
|
||||
/* Has no non-hidden parents */
|
||||
return m_selected != Qt::Unchecked;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PackageTreeItem::isCritical() const
|
||||
{
|
||||
return m_data.isCritical;
|
||||
}
|
||||
|
||||
Qt::CheckState
|
||||
PackageTreeItem::isSelected() const
|
||||
{
|
||||
return m_data.selected;
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::setSelected( Qt::CheckState isSelected )
|
||||
{
|
||||
if ( parentItem() == nullptr )
|
||||
// This is the root, it is always checked so don't change state
|
||||
{
|
||||
// This is the root, it is always checked so don't change state
|
||||
return;
|
||||
}
|
||||
|
||||
m_data.selected = isSelected;
|
||||
m_selected = isSelected;
|
||||
setChildrenSelected( isSelected );
|
||||
|
||||
// Look for suitable parent item which may change checked-state
|
||||
@ -237,39 +205,46 @@ PackageTreeItem::setSelected( Qt::CheckState isSelected )
|
||||
currentItem = currentItem->parentItem();
|
||||
}
|
||||
if ( currentItem == nullptr )
|
||||
// Reached the root .. don't bother
|
||||
{
|
||||
// Reached the root .. don't bother
|
||||
return;
|
||||
}
|
||||
|
||||
currentItem->updateSelected();
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::updateSelected()
|
||||
{
|
||||
// Figure out checked-state based on the children
|
||||
int childrenSelected = 0;
|
||||
int childrenPartiallySelected = 0;
|
||||
for ( int i = 0; i < currentItem->childCount(); i++ )
|
||||
for ( int i = 0; i < childCount(); i++ )
|
||||
{
|
||||
if ( currentItem->child( i )->isSelected() == Qt::Checked )
|
||||
if ( child( i )->isSelected() == Qt::Checked )
|
||||
{
|
||||
childrenSelected++;
|
||||
}
|
||||
if ( currentItem->child( i )->isSelected() == Qt::PartiallyChecked )
|
||||
if ( child( i )->isSelected() == Qt::PartiallyChecked )
|
||||
{
|
||||
childrenPartiallySelected++;
|
||||
}
|
||||
}
|
||||
if ( !childrenSelected && !childrenPartiallySelected )
|
||||
{
|
||||
currentItem->setSelected( Qt::Unchecked );
|
||||
setSelected( Qt::Unchecked );
|
||||
}
|
||||
else if ( childrenSelected == currentItem->childCount() )
|
||||
else if ( childrenSelected == childCount() )
|
||||
{
|
||||
currentItem->setSelected( Qt::Checked );
|
||||
setSelected( Qt::Checked );
|
||||
}
|
||||
else
|
||||
{
|
||||
currentItem->setSelected( Qt::PartiallyChecked );
|
||||
setSelected( Qt::PartiallyChecked );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PackageTreeItem::setChildrenSelected( Qt::CheckState isSelected )
|
||||
{
|
||||
@ -277,7 +252,7 @@ PackageTreeItem::setChildrenSelected( Qt::CheckState isSelected )
|
||||
// Children are never root; don't need to use setSelected on them.
|
||||
for ( auto child : m_childItems )
|
||||
{
|
||||
child->m_data.selected = isSelected;
|
||||
child->m_selected = isSelected;
|
||||
child->setChildrenSelected( isSelected );
|
||||
}
|
||||
}
|
||||
@ -287,3 +262,43 @@ PackageTreeItem::type() const
|
||||
{
|
||||
return QStandardItem::UserType;
|
||||
}
|
||||
|
||||
QVariant
|
||||
PackageTreeItem::toOperation() const
|
||||
{
|
||||
// If it's a package with a pre- or post-script, replace
|
||||
// with the more complicated datastructure.
|
||||
if ( !m_preScript.isEmpty() || !m_postScript.isEmpty() )
|
||||
{
|
||||
QMap< QString, QVariant > sdetails;
|
||||
sdetails.insert( "pre-script", m_preScript );
|
||||
sdetails.insert( "package", m_packageName );
|
||||
sdetails.insert( "post-script", m_postScript );
|
||||
return sdetails;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_packageName;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PackageTreeItem::operator==( const PackageTreeItem& rhs ) const
|
||||
{
|
||||
if ( isGroup() != rhs.isGroup() )
|
||||
{
|
||||
// Different kinds
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isGroup() )
|
||||
{
|
||||
return name() == rhs.name() && description() == rhs.description() && preScript() == rhs.preScript()
|
||||
&& postScript() == rhs.postScript() && isCritical() == rhs.isCritical() && isHidden() == rhs.isHidden()
|
||||
&& m_showReadOnly == rhs.m_showReadOnly && expandOnStart() == rhs.expandOnStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
return packageName() == rhs.packageName();
|
||||
}
|
||||
}
|
||||
|
@ -27,29 +27,14 @@
|
||||
class PackageTreeItem : public QStandardItem
|
||||
{
|
||||
public:
|
||||
struct ItemData
|
||||
{
|
||||
QString name;
|
||||
QString description;
|
||||
QString preScript;
|
||||
QString packageName;
|
||||
QString postScript;
|
||||
bool isCritical = false;
|
||||
bool isHidden = false;
|
||||
bool startExpanded = false; // Only for groups
|
||||
Qt::CheckState selected = Qt::Unchecked;
|
||||
using List = QList< PackageTreeItem* >;
|
||||
|
||||
/** @brief Turns this item into a variant for PackageOperations use
|
||||
*
|
||||
* For "plain" items, this is just the package name; items with
|
||||
* scripts return a map. See the package module for how it's interpreted.
|
||||
*/
|
||||
QVariant toOperation() const;
|
||||
};
|
||||
explicit PackageTreeItem( const ItemData& data, PackageTreeItem* parent = nullptr );
|
||||
explicit PackageTreeItem( const QString packageName, PackageTreeItem* parent = nullptr );
|
||||
explicit PackageTreeItem( PackageTreeItem* parent );
|
||||
explicit PackageTreeItem(); // The root of the tree; always selected, named <root>
|
||||
///@brief A package (individual package)
|
||||
explicit PackageTreeItem( const QString& packageName, PackageTreeItem* parent = nullptr );
|
||||
///@brief A group (sub-items and sub-groups are ignored)
|
||||
explicit PackageTreeItem( const QVariantMap& groupData, PackageTreeItem* parent = nullptr );
|
||||
///@brief A root item, always selected, named "<root>"
|
||||
explicit PackageTreeItem();
|
||||
~PackageTreeItem() override;
|
||||
|
||||
void appendChild( PackageTreeItem* child );
|
||||
@ -61,18 +46,33 @@ public:
|
||||
PackageTreeItem* parentItem();
|
||||
const PackageTreeItem* parentItem() const;
|
||||
|
||||
QString prettyName() const;
|
||||
QString description() const;
|
||||
QString preScript() const;
|
||||
QString packageName() const;
|
||||
QString postScript() const;
|
||||
QString name() const { return m_name; }
|
||||
QString packageName() const { return m_packageName; }
|
||||
|
||||
QString description() const { return m_description; }
|
||||
QString preScript() const { return m_preScript; }
|
||||
QString postScript() const { return m_postScript; }
|
||||
|
||||
/** @brief Is this item a group-item?
|
||||
*
|
||||
* Groups have a (possibly empty) list of packages, and a
|
||||
* (possibly empty) list of sub-groups, and can be marked
|
||||
* critical, hidden, etc. Packages, on the other hand, only
|
||||
* have a meaningful packageName() and selection status.
|
||||
*
|
||||
* Root is a group.
|
||||
*/
|
||||
bool isGroup() const { return m_isGroup; }
|
||||
|
||||
/// @brief Is this item a single package?
|
||||
bool isPackage() const { return !isGroup(); }
|
||||
|
||||
/** @brief Is this item hidden?
|
||||
*
|
||||
* Hidden items (generally only groups) are maintained separately,
|
||||
* not shown to the user, but do enter into the package-installation process.
|
||||
*/
|
||||
bool isHidden() const;
|
||||
bool isHidden() const { return m_isHidden; }
|
||||
|
||||
/** @brief Is this hidden item, considered "selected"?
|
||||
*
|
||||
@ -87,7 +87,7 @@ public:
|
||||
* A critical group must be successfully installed, for the Calamares
|
||||
* installation to continue.
|
||||
*/
|
||||
bool isCritical() const;
|
||||
bool isCritical() const { return m_isCritical; }
|
||||
|
||||
/** @brief Is this group expanded on start?
|
||||
*
|
||||
@ -95,17 +95,69 @@ public:
|
||||
* that expands on start is shown expanded (not collapsed)
|
||||
* in the treeview when the page is loaded.
|
||||
*/
|
||||
bool expandOnStart() const { return m_data.startExpanded; }
|
||||
bool expandOnStart() const { return m_startExpanded; }
|
||||
|
||||
/** @brief Is this an immutable item?
|
||||
*
|
||||
* Groups can be immutable: then you can't toggle the selected
|
||||
* state of any of its items.
|
||||
*/
|
||||
bool isImmutable() const { return m_showReadOnly; }
|
||||
|
||||
/** @brief is this item selected?
|
||||
*
|
||||
* Groups may be partially selected; packages are only on or off.
|
||||
*/
|
||||
Qt::CheckState isSelected() const { return m_selected; }
|
||||
|
||||
/** @brief Turns this item into a variant for PackageOperations use
|
||||
*
|
||||
* For "plain" items, this is just the package name; items with
|
||||
* scripts return a map. See the package module for how it's interpreted.
|
||||
*/
|
||||
QVariant toOperation() const;
|
||||
|
||||
Qt::CheckState isSelected() const;
|
||||
void setSelected( Qt::CheckState isSelected );
|
||||
void setChildrenSelected( Qt::CheckState isSelected );
|
||||
|
||||
/** @brief Update selectedness based on the children's states
|
||||
*
|
||||
* This only makes sense for groups, which might have packages
|
||||
* or subgroups; it checks only direct children.
|
||||
*/
|
||||
void updateSelected();
|
||||
|
||||
// QStandardItem methods
|
||||
int type() const override;
|
||||
|
||||
/** @brief Are two items equal
|
||||
*
|
||||
* This **disregards** parent-item and the child-items, and compares
|
||||
* only the fields for the items-proper (name, .. expanded). Note
|
||||
* also that *isSelected()* is a run-time state, and is **not**
|
||||
* compared either.
|
||||
*/
|
||||
bool operator==( const PackageTreeItem& rhs ) const;
|
||||
bool operator!=( const PackageTreeItem& rhs ) const { return !( *this == rhs ); }
|
||||
|
||||
private:
|
||||
PackageTreeItem* m_parentItem;
|
||||
QList< PackageTreeItem* > m_childItems;
|
||||
ItemData m_data;
|
||||
List m_childItems;
|
||||
|
||||
// An entry can be a package, or a group.
|
||||
QString m_name;
|
||||
QString m_packageName;
|
||||
Qt::CheckState m_selected = Qt::Unchecked;
|
||||
|
||||
// These are only useful for groups
|
||||
QString m_description;
|
||||
QString m_preScript;
|
||||
QString m_postScript;
|
||||
bool m_isGroup = false;
|
||||
bool m_isCritical = false;
|
||||
bool m_isHidden = false;
|
||||
bool m_showReadOnly = false;
|
||||
bool m_startExpanded = false;
|
||||
};
|
||||
|
||||
#endif // PACKAGETREEITEM_H
|
||||
|
@ -48,8 +48,12 @@ More keys (per group) are supported:
|
||||
- *critical*: if true, make the installation process fail if installing
|
||||
any of the packages in the group fails. Otherwise, just log a warning.
|
||||
Defaults to false.
|
||||
- *immutable*: if true, the state of the group (and all its subgroups)
|
||||
cannot be changed; it really only makes sense in combination
|
||||
with *selected* set to true. This only affects the user-interface.
|
||||
- *expanded*: if true, the group is shown in an expanded form (that is,
|
||||
not-collapsed) in the treeview on start.
|
||||
not-collapsed) in the treeview on start. This only affects the user-
|
||||
interface.
|
||||
- *subgroups*: if present this follows the same structure as the top level
|
||||
of the YAML file, allowing there to be sub-groups of packages to an
|
||||
arbitary depth
|
||||
|
310
src/modules/netinstall/Tests.cpp
Normal file
310
src/modules/netinstall/Tests.cpp
Normal file
@ -0,0 +1,310 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2020, Adriaan de Groot <groot@kde.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PackageModel.h"
|
||||
#include "PackageTreeItem.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class ItemTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ItemTests();
|
||||
virtual ~ItemTests() {}
|
||||
|
||||
private:
|
||||
void checkAllSelected( PackageTreeItem* p );
|
||||
void recursiveCompare( PackageTreeItem*, PackageTreeItem* );
|
||||
void recursiveCompare( PackageModel&, PackageModel& );
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testRoot();
|
||||
void testPackage();
|
||||
void testGroup();
|
||||
void testCompare();
|
||||
void testModel();
|
||||
void testExampleFiles();
|
||||
};
|
||||
|
||||
ItemTests::ItemTests() {}
|
||||
|
||||
void
|
||||
ItemTests::initTestCase()
|
||||
{
|
||||
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::testRoot()
|
||||
{
|
||||
PackageTreeItem r;
|
||||
|
||||
QCOMPARE( r.isSelected(), Qt::Checked );
|
||||
QCOMPARE( r.name(), QStringLiteral( "<root>" ) );
|
||||
QCOMPARE( r.parentItem(), nullptr );
|
||||
QVERIFY( r.isGroup() );
|
||||
|
||||
QVERIFY( r == r );
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::testPackage()
|
||||
{
|
||||
PackageTreeItem p( "bash", nullptr );
|
||||
|
||||
QCOMPARE( p.isSelected(), Qt::Unchecked );
|
||||
QCOMPARE( p.packageName(), QStringLiteral( "bash" ) );
|
||||
QVERIFY( p.name().isEmpty() ); // not a group
|
||||
QCOMPARE( p.parentItem(), nullptr );
|
||||
QCOMPARE( p.childCount(), 0 );
|
||||
QVERIFY( !p.isHidden() );
|
||||
QVERIFY( !p.isCritical() );
|
||||
QVERIFY( !p.isGroup() );
|
||||
QVERIFY( p.isPackage() );
|
||||
QVERIFY( p == p );
|
||||
|
||||
// This doesn't happen in normal constructions,
|
||||
// because a package can't have children.
|
||||
PackageTreeItem c( "zsh", &p );
|
||||
QCOMPARE( c.isSelected(), Qt::Unchecked );
|
||||
QCOMPARE( c.packageName(), QStringLiteral( "zsh" ) );
|
||||
QVERIFY( c.name().isEmpty() ); // not a group
|
||||
QCOMPARE( c.parentItem(), &p );
|
||||
QVERIFY( !c.isGroup() );
|
||||
QVERIFY( c.isPackage() );
|
||||
QVERIFY( c == c );
|
||||
QVERIFY( c != p );
|
||||
|
||||
QCOMPARE( p.childCount(), 0 ); // not noticed it has a child
|
||||
}
|
||||
|
||||
// *INDENT-OFF*
|
||||
// clang-format off
|
||||
static const char doc[] =
|
||||
"- name: \"CCR\"\n"
|
||||
" description: \"Tools for the Chakra Community Repository\"\n"
|
||||
" packages:\n"
|
||||
" - ccr\n"
|
||||
" - base-devel\n"
|
||||
" - bash\n";
|
||||
|
||||
static const char doc_no_packages[] =
|
||||
"- name: \"CCR\"\n"
|
||||
" description: \"Tools for the Chakra Community Repository\"\n"
|
||||
" packages: []\n";
|
||||
|
||||
static const char doc_with_expanded[] =
|
||||
"- name: \"CCR\"\n"
|
||||
" description: \"Tools for the Chakra Community Repository\"\n"
|
||||
" expanded: true\n"
|
||||
" packages:\n"
|
||||
" - ccr\n"
|
||||
" - base-devel\n"
|
||||
" - bash\n";
|
||||
// *INDENT-ON*
|
||||
// clang-format on
|
||||
|
||||
void
|
||||
ItemTests::testGroup()
|
||||
{
|
||||
YAML::Node yamldoc = YAML::Load( doc );
|
||||
QVariantList yamlContents = CalamaresUtils::yamlSequenceToVariant( yamldoc );
|
||||
|
||||
QCOMPARE( yamlContents.length(), 1 );
|
||||
|
||||
PackageTreeItem p( yamlContents[ 0 ].toMap(), nullptr );
|
||||
QCOMPARE( p.name(), QStringLiteral( "CCR" ) );
|
||||
QVERIFY( p.packageName().isEmpty() );
|
||||
QVERIFY( p.description().startsWith( QStringLiteral( "Tools " ) ) );
|
||||
QCOMPARE( p.parentItem(), nullptr );
|
||||
QVERIFY( !p.isHidden() );
|
||||
QVERIFY( !p.isCritical() );
|
||||
// The item-constructor doesn't consider the packages: list
|
||||
QCOMPARE( p.childCount(), 0 );
|
||||
QVERIFY( p.isGroup() );
|
||||
QVERIFY( !p.isPackage() );
|
||||
QVERIFY( p == p );
|
||||
|
||||
PackageTreeItem c( "zsh", nullptr );
|
||||
QVERIFY( p != c );
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::testCompare()
|
||||
{
|
||||
PackageTreeItem p0( "bash", nullptr );
|
||||
PackageTreeItem p1( "bash", &p0 );
|
||||
PackageTreeItem p2( "bash", nullptr );
|
||||
|
||||
QVERIFY( p0 == p1 ); // Parent doesn't matter
|
||||
QVERIFY( p0 == p2 );
|
||||
|
||||
p2.setSelected( Qt::Checked );
|
||||
p1.setSelected( Qt::Unchecked );
|
||||
QVERIFY( p0 == p1 ); // Neither does selected state
|
||||
QVERIFY( p0 == p2 );
|
||||
|
||||
PackageTreeItem r0( nullptr );
|
||||
QVERIFY( p0 != r0 );
|
||||
QVERIFY( p1 != r0 );
|
||||
QVERIFY( r0 == r0 );
|
||||
PackageTreeItem r1( nullptr );
|
||||
QVERIFY( r0 == r1 ); // Different roots are still equal
|
||||
|
||||
PackageTreeItem r2( "<root>", nullptr ); // Fake root
|
||||
QVERIFY( r0 != r2 );
|
||||
QVERIFY( r1 != r2 );
|
||||
QVERIFY( p0 != r2 );
|
||||
PackageTreeItem r3( "<root>", nullptr );
|
||||
QVERIFY( r3 == r2 );
|
||||
|
||||
YAML::Node yamldoc = YAML::Load( doc ); // See testGroup()
|
||||
QVariantList yamlContents = CalamaresUtils::yamlSequenceToVariant( yamldoc );
|
||||
QCOMPARE( yamlContents.length(), 1 );
|
||||
|
||||
PackageTreeItem p3( yamlContents[ 0 ].toMap(), nullptr );
|
||||
QVERIFY( p3 == p3 );
|
||||
QVERIFY( p3 != p1 );
|
||||
QVERIFY( p1 != p3 );
|
||||
QCOMPARE( p3.childCount(), 0 ); // Doesn't load the packages: list
|
||||
|
||||
PackageTreeItem p4( CalamaresUtils::yamlSequenceToVariant( YAML::Load( doc ) )[ 0 ].toMap(), nullptr );
|
||||
QVERIFY( p3 == p4 );
|
||||
PackageTreeItem p5( CalamaresUtils::yamlSequenceToVariant( YAML::Load( doc_no_packages ) )[ 0 ].toMap(), nullptr );
|
||||
QVERIFY( p3 == p5 );
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::checkAllSelected( PackageTreeItem* p )
|
||||
{
|
||||
QVERIFY( p->isSelected() );
|
||||
for ( int i = 0; i < p->childCount(); ++i )
|
||||
{
|
||||
checkAllSelected( p->child( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::recursiveCompare( PackageTreeItem* l, PackageTreeItem* r )
|
||||
{
|
||||
QVERIFY( l && r );
|
||||
QVERIFY( *l == *r );
|
||||
QCOMPARE( l->childCount(), r->childCount() );
|
||||
|
||||
for ( int i = 0; i < l->childCount(); ++i )
|
||||
{
|
||||
QCOMPARE( l->childCount(), r->childCount() );
|
||||
recursiveCompare( l->child( i ), r->child( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::recursiveCompare( PackageModel& l, PackageModel& r )
|
||||
{
|
||||
return recursiveCompare( l.m_rootItem, r.m_rootItem );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ItemTests::testModel()
|
||||
{
|
||||
YAML::Node yamldoc = YAML::Load( doc ); // See testGroup()
|
||||
QVariantList yamlContents = CalamaresUtils::yamlSequenceToVariant( yamldoc );
|
||||
QCOMPARE( yamlContents.length(), 1 );
|
||||
|
||||
PackageModel m0( nullptr );
|
||||
m0.setupModelData( yamlContents );
|
||||
|
||||
QCOMPARE( m0.m_hiddenItems.count(), 0 ); // Nothing hidden
|
||||
QCOMPARE( m0.rowCount(), 1 ); // Group, the packages are invisible
|
||||
QCOMPARE( m0.rowCount( m0.index( 0, 0 ) ), 3 ); // The packages
|
||||
|
||||
checkAllSelected( m0.m_rootItem );
|
||||
|
||||
PackageModel m2( nullptr );
|
||||
m2.setupModelData( CalamaresUtils::yamlSequenceToVariant( YAML::Load( doc_with_expanded ) ) );
|
||||
QCOMPARE( m2.m_hiddenItems.count(), 0 );
|
||||
QCOMPARE( m2.rowCount(), 1 ); // Group, now the packages expanded but not counted
|
||||
QCOMPARE( m2.rowCount( m2.index( 0, 0 ) ), 3 ); // The packages
|
||||
checkAllSelected( m2.m_rootItem );
|
||||
|
||||
PackageTreeItem r;
|
||||
QVERIFY( r == *m0.m_rootItem );
|
||||
|
||||
QCOMPARE( m0.m_rootItem->childCount(), 1 );
|
||||
|
||||
PackageTreeItem* group = m0.m_rootItem->child( 0 );
|
||||
QVERIFY( group->isGroup() );
|
||||
QCOMPARE( group->name(), QStringLiteral( "CCR" ) );
|
||||
QCOMPARE( group->childCount(), 3 );
|
||||
|
||||
PackageTreeItem bash( "bash", nullptr );
|
||||
// Check that the sub-packages loaded correctly
|
||||
bool found_one_bash = false;
|
||||
for ( int i = 0; i < group->childCount(); ++i )
|
||||
{
|
||||
QVERIFY( group->child( i )->isPackage() );
|
||||
if ( bash == *( group->child( i ) ) )
|
||||
{
|
||||
found_one_bash = true;
|
||||
}
|
||||
}
|
||||
QVERIFY( found_one_bash );
|
||||
|
||||
// But m2 has "expanded" set which the others do no
|
||||
QVERIFY( *( m2.m_rootItem->child( 0 ) ) != *group );
|
||||
}
|
||||
|
||||
void
|
||||
ItemTests::testExampleFiles()
|
||||
{
|
||||
QVERIFY( QStringLiteral( BUILD_AS_TEST ).endsWith( "/netinstall" ) );
|
||||
|
||||
QDir d( BUILD_AS_TEST );
|
||||
|
||||
for ( const QString& filename : QStringList { "netinstall.yaml" } )
|
||||
{
|
||||
QFile f( d.filePath( filename ) );
|
||||
QVERIFY( f.exists() );
|
||||
QVERIFY( f.open( QIODevice::ReadOnly ) );
|
||||
QByteArray contents = f.readAll();
|
||||
QVERIFY( !contents.isEmpty() );
|
||||
|
||||
YAML::Node yamldoc = YAML::Load( contents.constData() );
|
||||
QVariantList yamlContents = CalamaresUtils::yamlSequenceToVariant( yamldoc );
|
||||
|
||||
PackageModel m1( nullptr );
|
||||
m1.setupModelData( yamlContents );
|
||||
|
||||
// TODO: should test *something* about this file :/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_GUILESS_MAIN( ItemTests )
|
||||
|
||||
#include "utils/moc-warnings.h"
|
||||
|
||||
#include "Tests.moc"
|
@ -11,7 +11,11 @@
|
||||
#
|
||||
# The format of the groups file is documented in `README.md`.
|
||||
#
|
||||
# groupsUrl: file:///usr/share/calamares/netinstall.yaml
|
||||
# As a special case, setting *groupsUrl* to the literal string
|
||||
# `local` means that the data is obtained from **this** config
|
||||
# file, under the key *groups*.
|
||||
#
|
||||
groupsUrl: local
|
||||
|
||||
# If the installation can proceed without netinstall (e.g. the Live CD
|
||||
# can create a working installed system, but netinstall is preferred
|
||||
@ -46,3 +50,46 @@ label:
|
||||
# sidebar[nl]: "Pakketkeuze"
|
||||
# title: "Office Package"
|
||||
# title[nl]: "Kantoorsoftware"
|
||||
|
||||
# If, and only if, *groupsUrl* is set to the literal string `local`,
|
||||
# groups data is read from this file. The value of *groups* must be
|
||||
# a list, with the same format as the regular `netinstall.yaml` file.
|
||||
#
|
||||
# This is recommended only for small static package lists.
|
||||
groups:
|
||||
- name: "Default"
|
||||
description: "Default group"
|
||||
hidden: true
|
||||
selected: true
|
||||
critical: false
|
||||
packages:
|
||||
- base
|
||||
- chakra-live-skel
|
||||
- name: "Shells"
|
||||
description: "Shells"
|
||||
hidden: false
|
||||
selected: false
|
||||
critical: true
|
||||
subgroups:
|
||||
- name: "Bash"
|
||||
description: "Bourne Again Shell"
|
||||
selected: true
|
||||
packages:
|
||||
- bash
|
||||
- bash-completion
|
||||
- name: "Zsh"
|
||||
description: "Zee shell, boss"
|
||||
packages:
|
||||
- zsh
|
||||
- zsh-completion
|
||||
- zsh-extensions
|
||||
- name: "Kernel"
|
||||
description: "Kernel bits"
|
||||
hidden: false
|
||||
selected: true
|
||||
critical: true
|
||||
immutable: true
|
||||
packages:
|
||||
- kernel
|
||||
- kernel-debugsym
|
||||
- kernel-nvidia
|
||||
|
Loading…
Reference in New Issue
Block a user