From 09c3240364bccfb7df454b0b4e36bf459367c7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20G=C3=A2teau?= Date: Tue, 5 Aug 2014 14:57:00 +0200 Subject: [PATCH] Scaffolding and tests for ResizePartitionJob --- src/modules/partition/CMakeLists.txt | 1 + .../partition/EditExistingPartitionDialog.cpp | 4 +- src/modules/partition/PartitionCoreModule.cpp | 15 ++ src/modules/partition/PartitionCoreModule.h | 2 + src/modules/partition/ResizePartitionJob.cpp | 230 ++++++++++++++++++ src/modules/partition/ResizePartitionJob.h | 55 +++++ src/modules/partition/tests/CMakeLists.txt | 1 + src/modules/partition/tests/JobTests.cpp | 79 +++++- src/modules/partition/tests/JobTests.h | 3 + 9 files changed, 385 insertions(+), 5 deletions(-) create mode 100644 src/modules/partition/ResizePartitionJob.cpp create mode 100644 src/modules/partition/ResizePartitionJob.h diff --git a/src/modules/partition/CMakeLists.txt b/src/modules/partition/CMakeLists.txt index 4e96e039e..0323af79c 100644 --- a/src/modules/partition/CMakeLists.txt +++ b/src/modules/partition/CMakeLists.txt @@ -41,6 +41,7 @@ calamares_add_plugin( partition PartitionSizeWidget.cpp PartitionViewStep.cpp PMUtils.cpp + ResizePartitionJob.cpp UI CreatePartitionDialog.ui CreatePartitionTableDialog.ui diff --git a/src/modules/partition/EditExistingPartitionDialog.cpp b/src/modules/partition/EditExistingPartitionDialog.cpp index 18a25800c..e946fec91 100644 --- a/src/modules/partition/EditExistingPartitionDialog.cpp +++ b/src/modules/partition/EditExistingPartitionDialog.cpp @@ -69,9 +69,7 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core ) core->createPartition( m_device, newPartition ); } else - { - //core->resizePartition( m_device, m_partition ); - } + core->resizePartition( m_device, m_partition, range.first, range.second ); } else { diff --git a/src/modules/partition/PartitionCoreModule.cpp b/src/modules/partition/PartitionCoreModule.cpp index 9f6bb829a..2ad439950 100644 --- a/src/modules/partition/PartitionCoreModule.cpp +++ b/src/modules/partition/PartitionCoreModule.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -257,6 +258,20 @@ PartitionCoreModule::formatPartition( Device* device, Partition* partition ) refresh(); } +void +PartitionCoreModule::resizePartition( Device* device, Partition* partition, qint64 first, qint64 last ) +{ + auto deviceInfo = infoForDevice( device ); + Q_ASSERT( deviceInfo ); + PartitionModel::ResetHelper helper( partitionModelForDevice( device ) ); + + ResizePartitionJob* job = new ResizePartitionJob( device, partition, first, last ); + job->updatePreview(); + deviceInfo->jobs << Calamares::job_ptr( job ); + + refresh(); +} + QList< Calamares::job_ptr > PartitionCoreModule::jobs() const { diff --git a/src/modules/partition/PartitionCoreModule.h b/src/modules/partition/PartitionCoreModule.h index 4a29f0994..7d3fb5f3b 100644 --- a/src/modules/partition/PartitionCoreModule.h +++ b/src/modules/partition/PartitionCoreModule.h @@ -69,6 +69,8 @@ public: void formatPartition( Device* device, Partition* partition ); + void resizePartition( Device* device, Partition* partition, qint64 first, qint64 last ); + void setBootLoaderInstallPath( const QString& path ); QList< Calamares::job_ptr > jobs() const; diff --git a/src/modules/partition/ResizePartitionJob.cpp b/src/modules/partition/ResizePartitionJob.cpp new file mode 100644 index 000000000..0ad5bd7a3 --- /dev/null +++ b/src/modules/partition/ResizePartitionJob.cpp @@ -0,0 +1,230 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Aurélien Gâteau + * + * 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 . + */ + +// This class is heavily based on the ResizeOperation class from KDE Partition +// Manager. Original copyright follow: + +/*************************************************************************** + * Copyright (C) 2008,2012 by Volker Lanz * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +//#include + +// CalaPM +#include +#include +#include +#include +#include +#include +#include +/* +#include +#include +*/ +#include + +// Qt +#include + +//- Context -------------------------------------------------------------------- +struct Context +{ + Context( ResizePartitionJob* job_ ) + : job( job_ ) + {} + + ResizePartitionJob* job; +}; + +//- ResizeFileSystemJob -------------------------------------------------------- +class ResizeFileSystemJob : public Calamares::Job +{ +public: + ResizeFileSystemJob( Context* context, qint64 length ) + {} + + QString prettyName() const override + { + return QString(); + } + + Calamares::JobResult exec() override + { + return Calamares::JobResult::ok(); + } +}; + +//- SetPartGeometryJob --------------------------------------------------------- +class SetPartGeometryJob : public Calamares::Job +{ +public: + SetPartGeometryJob( Context* context, qint64 firstSector, qint64 length ) + {} + + QString prettyName() const override + { + return QString(); + } + + Calamares::JobResult exec() override + { + return Calamares::JobResult::ok(); + } +}; + +//- MoveFileSystemJob ---------------------------------------------------------- +class MoveFileSystemJob : public Calamares::Job +{ +public: + MoveFileSystemJob( Context* context, qint64 firstSector ) + {} + + QString prettyName() const override + { + return QString(); + } + + Calamares::JobResult exec() override + { + return Calamares::JobResult::ok(); + } +}; + +//- ResizePartitionJob --------------------------------------------------------- +ResizePartitionJob::ResizePartitionJob( Device* device, Partition* partition, qint64 firstSector, qint64 lastSector ) + : PartitionJob( partition ) + , m_device( device ) + , m_newFirstSector( firstSector ) + , m_newLastSector( lastSector ) +{ +} + +QString +ResizePartitionJob::prettyName() const +{ + /* + return tr( "Format partition %1 (file system: %2, size: %3 MB) on %4." ) + .arg( m_partition->partitionPath() ) + .arg( m_partition->fileSystem().name() ) + .arg( m_partition->capacity() / 1024 / 1024 ) + .arg( m_device->name() ); + */ + return QString(); +} + +Calamares::JobResult +ResizePartitionJob::exec() +{ + /* + Report report( 0 ); + QString partitionPath = m_partition->partitionPath(); + QString message = tr( "The installer failed to format partition %1 on disk '%2'." ).arg( partitionPath, m_device->name() ); + + CoreBackend* backend = CoreBackendManager::self()->backend(); + QScopedPointer backendDevice( backend->openDevice( m_device->deviceNode() ) ); + if ( !backendDevice.data() ) + { + return Calamares::JobResult::error( + message, + tr( "Could not open device '%1'." ).arg( m_device->deviceNode() ) + ); + } + + QScopedPointer backendPartitionTable( backendDevice->openPartitionTable() ); + backendPartitionTable->commit(); + return Calamares::JobResult::ok(); + */ + qint64 oldLength = m_partition->lastSector() - m_partition->firstSector() + 1; + qint64 newLength = m_newLastSector - m_newFirstSector + 1; + + Context context( this ); + QList< Calamares::job_ptr > jobs; + if ( m_partition->roles().has( PartitionRole::Extended ) ) + jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_newFirstSector, newLength ) ); + else + { + bool shrink = newLength < oldLength; + bool grow = newLength > oldLength; + bool moveRight = m_newFirstSector > m_partition->firstSector(); + bool moveLeft = m_newFirstSector < m_partition->firstSector(); + if ( shrink ) + { + jobs << Calamares::job_ptr( new ResizeFileSystemJob( &context, newLength ) ); + jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_partition->firstSector(), newLength ) ); + } + if ( moveRight || moveLeft ) + { + // At this point, we need to set the partition's length to either the resized length, if it has already been + // shrunk, or to the original length (it may or may not then later be grown, we don't care here) + const qint64 length = shrink ? newLength : oldLength; + jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_newFirstSector, length ) ); + jobs << Calamares::job_ptr( new MoveFileSystemJob( &context, m_newFirstSector ) ); + } + if ( grow ) + { + jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_newFirstSector, newLength ) ); + jobs << Calamares::job_ptr( new ResizeFileSystemJob( &context, newLength ) ); + } + } + return execJobList( jobs ); +} + +void +ResizePartitionJob::updatePreview() +{ + m_device->partitionTable()->removeUnallocated(); + m_partition->parent()->remove( m_partition ); + m_partition->setFirstSector( m_newFirstSector ); + m_partition->setLastSector( m_newLastSector ); + m_partition->parent()->insert( m_partition ); + m_device->partitionTable()->updateUnallocated( *m_device ); +} + +Calamares::JobResult +ResizePartitionJob::execJobList( const QList< Calamares::job_ptr >& jobs ) +{ + int nbJobs = jobs.size(); + int count = 0; + for ( Calamares::job_ptr job : jobs ) + { + Calamares::JobResult result = job->exec(); + if ( !result ) + return result; + ++count; + progress( qreal( count ) / nbJobs ); + } + return Calamares::JobResult::ok(); +} diff --git a/src/modules/partition/ResizePartitionJob.h b/src/modules/partition/ResizePartitionJob.h new file mode 100644 index 000000000..15761b1c5 --- /dev/null +++ b/src/modules/partition/ResizePartitionJob.h @@ -0,0 +1,55 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Aurélien Gâteau + * + * 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 RESIZEPARTITIONJOB_H +#define RESIZEPARTITIONJOB_H + +#include + +class Device; +class Partition; +class FileSystem; + +struct Context; + +class ResizePartitionJob : public PartitionJob +{ + Q_OBJECT +public: + ResizePartitionJob( Device* device, Partition* partition, qint64 firstSector, qint64 lastSector ); + QString prettyName() const override; + Calamares::JobResult exec() override; + + void updatePreview(); + + Device* device() const + { + return m_device; + } + +private: + Device* m_device; + qint64 m_newFirstSector; + qint64 m_newLastSector; + + Calamares::JobResult execJobList( const QList< Calamares::job_ptr >& jobs ); + + friend struct Context; +}; + +#endif /* RESIZEPARTITIONJOB_H */ diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index 9b0c54767..aa1663f43 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -9,6 +9,7 @@ set( jobtests_SRCS ${PartitionModule_SOURCE_DIR}/PartitionIterator.cpp ${PartitionModule_SOURCE_DIR}/PartitionJob.cpp ${PartitionModule_SOURCE_DIR}/PMUtils.cpp + ${PartitionModule_SOURCE_DIR}/ResizePartitionJob.cpp JobTests.cpp ) diff --git a/src/modules/partition/tests/JobTests.cpp b/src/modules/partition/tests/JobTests.cpp index 169683c5d..6228d6bed 100644 --- a/src/modules/partition/tests/JobTests.cpp +++ b/src/modules/partition/tests/JobTests.cpp @@ -2,6 +2,7 @@ #include #include +#include #include // CalaPM @@ -77,12 +78,18 @@ JobTests::initTestCase() } QVERIFY( CalaPM::init() ); + FileSystemFactory::init(); + refreshDevice(); +} + +void +JobTests::refreshDevice() +{ + QString devicePath = qgetenv( "CALAMARES_TEST_DISK" ); CoreBackend* backend = CoreBackendManager::self()->backend(); m_device.reset( backend->scanDevice( devicePath ) ); QVERIFY( !m_device.isNull() ); - - FileSystemFactory::init(); } void @@ -216,3 +223,71 @@ JobTests::testCreatePartitionExtended() QCOMPARE( extendedPartition->partitionPath(), devicePath + "2" ); QCOMPARE( partition2->partitionPath(), devicePath + "5" ); } + +void +JobTests::testResizePartition_data() +{ + QTest::addColumn< int >( "oldStartMB" ); + QTest::addColumn< int >( "oldSizeMB" ); + QTest::addColumn< int >( "newStartMB" ); + QTest::addColumn< int >( "newSizeMB" ); + + QTest::newRow("grow") << 10 << 5 << 10 << 7; + QTest::newRow("shrink") << 10 << 6 << 10 << 3; + QTest::newRow("moveLeft") << 10 << 5 << 8 << 5; + QTest::newRow("moveRight") << 10 << 5 << 12 << 5; +} + +void +JobTests::testResizePartition() +{ + QFETCH( int, oldStartMB ); + QFETCH( int, oldSizeMB ); + QFETCH( int, newStartMB ); + QFETCH( int, newSizeMB ); + + const qint64 sectorSize = m_device->logicalSectorSize(); + const qint64 sectorForMB = MB / sectorSize; + + qint64 oldFirst = sectorForMB * oldStartMB; + qint64 oldLast = oldFirst + sectorForMB * oldSizeMB - 1; + qint64 newFirst = sectorForMB * newStartMB; + qint64 newLast = newFirst + sectorForMB * newSizeMB - 1; + + // Setup: create the test partition + { + queuePartitionTableCreation( PartitionTable::msdos ); + + Partition* freePartition = firstFreePartition( m_device->partitionTable() ); + QVERIFY( freePartition ); + Partition* partition = PMUtils::createNewPartition( freePartition->parent(), *m_device, PartitionRole( PartitionRole::Primary ), FileSystem::Ext4, oldFirst, oldLast ); + CreatePartitionJob* job = new CreatePartitionJob( m_device.data(), partition ); + job->updatePreview(); + m_queue.enqueue( job_ptr( job ) ); + + QVERIFY( m_runner.run() ); + } + + // Resize + { + refreshDevice(); + QVERIFY( m_device->partitionTable() ); + Partition* partition = m_device->partitionTable()->findPartitionBySector( oldFirst, PartitionRole( PartitionRole::Primary ) ); + QVERIFY( partition ); + + ResizePartitionJob* job = new ResizePartitionJob( m_device.data(), partition, newFirst, newLast ); + QVERIFY( m_runner.run() ); + } + + // Test + { + refreshDevice(); + QVERIFY( m_device->partitionTable() ); + Partition* partition = m_device->partitionTable()->findPartitionBySector( newFirst, PartitionRole( PartitionRole::Primary ) ); + QVERIFY( partition ); + QCOMPARE( partition->firstSector(), newFirst ); + QCOMPARE( partition->lastSector(), newLast ); + QCOMPARE( partition->fileSystem().firstSector(), newFirst ); + QCOMPARE( partition->fileSystem().lastSector(), newLast ); + } +} diff --git a/src/modules/partition/tests/JobTests.h b/src/modules/partition/tests/JobTests.h index e4a35cb3f..281450cc5 100644 --- a/src/modules/partition/tests/JobTests.h +++ b/src/modules/partition/tests/JobTests.h @@ -43,6 +43,8 @@ private Q_SLOTS: void testPartitionTable(); void testCreatePartition(); void testCreatePartitionExtended(); + void testResizePartition_data(); + void testResizePartition(); private: QScopedPointer< Device > m_device; @@ -51,6 +53,7 @@ private: void queuePartitionTableCreation( PartitionTable::TableType type ); CreatePartitionJob* newCreatePartitionJob( Partition* freeSpacePartition, PartitionRole, FileSystem::Type type, qint64 size ); + void refreshDevice(); }; #endif /* JOBTESTS_H */