source: calamares/trunk/fuentes/src/modules/partition/tests/PartitionJobTests.cpp @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 13 months ago

sync with github

File size: 13.2 KB
Line 
1/* === This file is part of Calamares - <https://github.com/calamares> ===
2 *
3 *   Copyright 2014, Aurélien Gâteau <agateau@kde.org>
4 *   Copyright 2017, Adriaan de Groot <groot@kde.org>
5 *
6 *   Calamares is free software: you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation, either version 3 of the License, or
9 *   (at your option) any later version.
10 *
11 *   Calamares is distributed in the hope that it will be useful,
12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 *   GNU General Public License for more details.
15 *
16 *   You should have received a copy of the GNU General Public License
17 *   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <PartitionJobTests.h>
21
22#include "utils/Units.h"
23
24#include <jobs/CreatePartitionJob.h>
25#include <jobs/CreatePartitionTableJob.h>
26#include <jobs/ResizePartitionJob.h>
27#include <core/KPMHelpers.h>
28
29// CalaPM
30#include <backend/corebackend.h>
31#include <backend/corebackendmanager.h>
32#include <fs/filesystemfactory.h>
33
34// Qt
35#include <QEventLoop>
36#include <QProcess>
37#include <QtTest/QtTest>
38
39QTEST_GUILESS_MAIN( PartitionJobTests )
40
41using namespace Calamares;
42using CalamaresUtils::operator""_MiB;
43
44class PartitionMounter
45{
46public:
47    PartitionMounter( const QString& devicePath )
48        : m_mountPointDir( "calamares-partitiontests-mountpoint" )
49    {
50        QStringList args = QStringList() << devicePath << m_mountPointDir.path();
51        int ret = QProcess::execute( "mount", args );
52        m_mounted = ret == 0;
53        QCOMPARE( ret, 0 );
54    }
55
56    ~PartitionMounter()
57    {
58        if ( !m_mounted )
59            return;
60        int ret = QProcess::execute( "umount", QStringList() << m_mountPointDir.path() );
61        QCOMPARE( ret, 0 );
62    }
63
64    QString mountPoint() const
65    {
66        return m_mounted ? m_mountPointDir.path() : QString();
67    }
68
69private:
70    QString m_devicePath;
71    QTemporaryDir m_mountPointDir;
72    bool m_mounted;
73};
74
75static QByteArray
76generateTestData( qint64 size )
77{
78    QByteArray ba;
79    ba.resize( size );
80    // Fill the array explicitly to keep Valgrind happy
81    for ( auto it = ba.data() ; it < ba.data() + size ; ++it )
82    {
83        *it = char( rand() & 0xff );
84    }
85    return ba;
86}
87
88static void
89writeFile( const QString& path, const QByteArray data )
90{
91    QFile file( path );
92    QVERIFY( file.open( QIODevice::WriteOnly ) );
93
94    const char* ptr = data.constData();
95    const char* end = data.constData() + data.size();
96    const qint64 chunkSize = 16384;
97
98    while ( ptr < end )
99    {
100        qint64 count = file.write( ptr, chunkSize );
101        if ( count < 0 )
102        {
103            QString msg = QString( "Writing file failed. Only %1 bytes written out of %2. Error: '%3'." )
104                .arg( ptr - data.constData() )
105                .arg( data.size() )
106                .arg( file.errorString() );
107            QFAIL( qPrintable( msg ) );
108        }
109        ptr += count;
110    }
111}
112
113static Partition*
114firstFreePartition( PartitionNode* parent )
115{
116    for( auto child : parent->children() )
117        if ( KPMHelpers::isPartitionFreeSpace( child ) )
118            return child;
119    return nullptr;
120}
121
122//- QueueRunner ---------------------------------------------------------------
123QueueRunner::QueueRunner( JobQueue* queue )
124    : m_queue( queue )
125    , m_finished( false )  // Same initalizations as in ::run()
126    , m_success( true )
127{
128    connect( m_queue, &JobQueue::finished, this, &QueueRunner::onFinished );
129    connect( m_queue, &JobQueue::failed, this, &QueueRunner::onFailed );
130}
131
132bool
133QueueRunner::run()
134{
135    m_finished = false;
136    m_success = true;
137    m_queue->start();
138    QEventLoop loop;
139    while ( !m_finished )
140        loop.processEvents();
141    return m_success;
142}
143
144void
145QueueRunner::onFinished()
146{
147    m_finished = true;
148}
149
150void
151QueueRunner::onFailed( const QString& message, const QString& details )
152{
153    m_success = false;
154    QString msg = message + "\ndetails: " + details;
155    QFAIL( qPrintable( msg ) );
156}
157
158//- PartitionJobTests ------------------------------------------------------------------
159PartitionJobTests::PartitionJobTests()
160    : m_runner( &m_queue )
161{}
162
163void
164PartitionJobTests::initTestCase()
165{
166    QString devicePath = qgetenv( "CALAMARES_TEST_DISK" );
167    if ( devicePath.isEmpty() )
168    {
169        QSKIP( "Skipping test, CALAMARES_TEST_DISK is not set. It should point to a disk which can be safely formatted" );
170    }
171
172    QVERIFY( KPMHelpers::initKPMcore() );
173    FileSystemFactory::init();
174
175    refreshDevice();
176}
177
178void
179PartitionJobTests::refreshDevice()
180{
181    QString devicePath = qgetenv( "CALAMARES_TEST_DISK" );
182    CoreBackend* backend = CoreBackendManager::self()->backend();
183    m_device.reset( backend->scanDevice( devicePath ) );
184    QVERIFY( !m_device.isNull() );
185}
186
187void
188PartitionJobTests::testPartitionTable()
189{
190    queuePartitionTableCreation( PartitionTable::msdos );
191    QVERIFY( m_runner.run() );
192    QVERIFY( m_device->partitionTable() );
193    QVERIFY( firstFreePartition( m_device->partitionTable() ) );
194
195    queuePartitionTableCreation( PartitionTable::gpt );
196    QVERIFY( m_runner.run() );
197    QVERIFY( m_device->partitionTable() );
198    QVERIFY( firstFreePartition( m_device->partitionTable() ) );
199}
200
201void
202PartitionJobTests::queuePartitionTableCreation( PartitionTable::TableType type)
203{
204    auto job = new CreatePartitionTableJob( m_device.data(), type );
205    job->updatePreview();
206    m_queue.enqueue( job_ptr( job ) );
207}
208
209CreatePartitionJob*
210PartitionJobTests::newCreatePartitionJob( Partition* freeSpacePartition, PartitionRole role, FileSystem::Type type, qint64 size )
211{
212    Q_ASSERT( freeSpacePartition );
213
214    qint64 firstSector = freeSpacePartition->firstSector();
215    qint64 lastSector;
216
217    if ( size > 0 )
218        lastSector = firstSector + size / m_device->logicalSize();
219    else
220        lastSector = freeSpacePartition->lastSector();
221    FileSystem* fs = FileSystemFactory::create( type, firstSector, lastSector
222        ,m_device->logicalSize()
223    );
224
225    Partition* partition = new Partition(
226        freeSpacePartition->parent(),
227        *m_device,
228        role,
229        fs, firstSector, lastSector,
230        QString() /* path */,
231        PartitionTable::FlagNone /* availableFlags */,
232        QString() /* mountPoint */,
233        false /* mounted */,
234        PartitionTable::FlagNone /* activeFlags */,
235        Partition::StateNew
236    );
237    return new CreatePartitionJob( m_device.data(), partition );
238}
239
240void
241PartitionJobTests::testCreatePartition()
242{
243    queuePartitionTableCreation( PartitionTable::gpt );
244    CreatePartitionJob* job;
245    Partition* freePartition;
246
247    freePartition = firstFreePartition( m_device->partitionTable() );
248    QVERIFY( freePartition );
249    job = newCreatePartitionJob( freePartition, PartitionRole( PartitionRole::Primary ), FileSystem::Ext4, 1_MiB);
250    Partition* partition1 = job->partition();
251    QVERIFY( partition1 );
252    job->updatePreview();
253    m_queue.enqueue( job_ptr( job ) );
254
255    freePartition = firstFreePartition( m_device->partitionTable() );
256    QVERIFY( freePartition );
257    job = newCreatePartitionJob( freePartition, PartitionRole( PartitionRole::Primary ), FileSystem::LinuxSwap, 1_MiB);
258    Partition* partition2 = job->partition();
259    QVERIFY( partition2 );
260    job->updatePreview();
261    m_queue.enqueue( job_ptr( job ) );
262
263    freePartition = firstFreePartition( m_device->partitionTable() );
264    QVERIFY( freePartition );
265    job = newCreatePartitionJob( freePartition, PartitionRole( PartitionRole::Primary ), FileSystem::Fat32, 1_MiB);
266    Partition* partition3 = job->partition();
267    QVERIFY( partition3 );
268    job->updatePreview();
269    m_queue.enqueue( job_ptr( job ) );
270
271    QVERIFY( m_runner.run() );
272
273    // Check partitionPath has been set. It is not known until the job has
274    // executed.
275    QString devicePath = m_device->deviceNode();
276    QCOMPARE( partition1->partitionPath(), devicePath + "1" );
277    QCOMPARE( partition2->partitionPath(), devicePath + "2" );
278    QCOMPARE( partition3->partitionPath(), devicePath + "3" );
279}
280
281void
282PartitionJobTests::testCreatePartitionExtended()
283{
284    queuePartitionTableCreation( PartitionTable::msdos );
285    CreatePartitionJob* job;
286    Partition* freePartition;
287
288    freePartition = firstFreePartition( m_device->partitionTable() );
289    QVERIFY( freePartition );
290    job = newCreatePartitionJob( freePartition, PartitionRole( PartitionRole::Primary ), FileSystem::Ext4, 10_MiB);
291    Partition* partition1 = job->partition();
292    QVERIFY( partition1 );
293    job->updatePreview();
294    m_queue.enqueue( job_ptr( job ) );
295
296    freePartition = firstFreePartition( m_device->partitionTable() );
297    QVERIFY( freePartition );
298    job = newCreatePartitionJob( freePartition, PartitionRole( PartitionRole::Extended ), FileSystem::Extended, 10_MiB);
299    job->updatePreview();
300    m_queue.enqueue( job_ptr( job ) );
301    Partition* extendedPartition = job->partition();
302
303    freePartition = firstFreePartition( extendedPartition );
304    QVERIFY( freePartition );
305    job = newCreatePartitionJob( freePartition, PartitionRole( PartitionRole::Logical ), FileSystem::Ext4, 0);
306    Partition* partition2 = job->partition();
307    QVERIFY( partition2 );
308    job->updatePreview();
309    m_queue.enqueue( job_ptr( job ) );
310
311    QVERIFY( m_runner.run() );
312
313    // Check partitionPath has been set. It is not known until the job has
314    // executed.
315    QString devicePath = m_device->deviceNode();
316    QCOMPARE( partition1->partitionPath(), devicePath + "1" );
317    QCOMPARE( extendedPartition->partitionPath(), devicePath + "2" );
318    QCOMPARE( partition2->partitionPath(), devicePath + "5" );
319}
320
321void
322PartitionJobTests::testResizePartition_data()
323{
324    QTest::addColumn< int >( "oldStartMB" );
325    QTest::addColumn< int >( "oldSizeMB" );
326    QTest::addColumn< int >( "newStartMB" );
327    QTest::addColumn< int >( "newSizeMB" );
328
329    QTest::newRow("grow")      << 10 << 50 << 10 << 70;
330    QTest::newRow("shrink")    << 10 << 70 << 10 << 50;
331    QTest::newRow("moveLeft")  << 10 << 50 << 8 << 50;
332    QTest::newRow("moveRight") << 10 << 50 << 12 << 50;
333}
334
335void
336PartitionJobTests::testResizePartition()
337{
338    QFETCH( int, oldStartMB );
339    QFETCH( int, oldSizeMB );
340    QFETCH( int, newStartMB );
341    QFETCH( int, newSizeMB );
342
343    const qint64 sectorForMB = 1_MiB / m_device->logicalSize();
344
345    qint64 oldFirst = sectorForMB * oldStartMB;
346    qint64 oldLast  = oldFirst + sectorForMB * oldSizeMB - 1;
347    qint64 newFirst = sectorForMB * newStartMB;
348    qint64 newLast  = newFirst + sectorForMB * newSizeMB - 1;
349
350    // Make the test data file smaller than the full size of the partition to
351    // accomodate for the file system overhead
352    const QByteArray testData = generateTestData( CalamaresUtils::MiBtoBytes( qMin( oldSizeMB, newSizeMB ) ) * 3 / 4 );
353    const QString testName = "test.data";
354
355    // Setup: create the test partition
356    {
357        queuePartitionTableCreation( PartitionTable::msdos );
358
359        Partition* freePartition = firstFreePartition( m_device->partitionTable() );
360        QVERIFY( freePartition );
361        Partition* partition = KPMHelpers::createNewPartition( freePartition->parent(), *m_device, PartitionRole( PartitionRole::Primary ), FileSystem::Ext4, oldFirst, oldLast );
362        CreatePartitionJob* job = new CreatePartitionJob( m_device.data(), partition );
363        job->updatePreview();
364        m_queue.enqueue( job_ptr( job ) );
365
366        QVERIFY( m_runner.run() );
367    }
368
369    {
370        // Write a test file in the partition
371        refreshDevice();
372        QVERIFY( m_device->partitionTable() );
373        Partition* partition = m_device->partitionTable()->findPartitionBySector( oldFirst, PartitionRole( PartitionRole::Primary ) );
374        QVERIFY( partition );
375        QCOMPARE( partition->firstSector(), oldFirst );
376        QCOMPARE( partition->lastSector(), oldLast );
377        {
378            PartitionMounter mounter( partition->partitionPath() );
379            QString mountPoint = mounter.mountPoint();
380            QVERIFY( !mountPoint.isEmpty() );
381            writeFile( mountPoint + '/' + testName, testData );
382        }
383
384        // Resize
385        ResizePartitionJob* job = new ResizePartitionJob( m_device.data(), partition, newFirst, newLast );
386        job->updatePreview();
387        m_queue.enqueue( job_ptr( job ) );
388        QVERIFY( m_runner.run() );
389
390        QCOMPARE( partition->firstSector(), newFirst );
391        QCOMPARE( partition->lastSector(), newLast );
392    }
393
394    // Test
395    {
396        refreshDevice();
397        QVERIFY( m_device->partitionTable() );
398        Partition* partition = m_device->partitionTable()->findPartitionBySector( newFirst, PartitionRole( PartitionRole::Primary ) );
399        QVERIFY( partition );
400        QCOMPARE( partition->firstSector(), newFirst );
401        QCOMPARE( partition->lastSector(), newLast );
402        QCOMPARE( partition->fileSystem().firstSector(), newFirst );
403        QCOMPARE( partition->fileSystem().lastSector(), newLast );
404
405
406        PartitionMounter mounter( partition->partitionPath() );
407        QString mountPoint = mounter.mountPoint();
408        QVERIFY( !mountPoint.isEmpty() );
409        {
410            QFile file( mountPoint + '/' + testName );
411            QVERIFY( file.open( QIODevice::ReadOnly ) );
412            QByteArray outData = file.readAll();
413            QCOMPARE( outData.size(), testData.size() );
414            QCOMPARE( outData, testData );
415        }
416    }
417}
Note: See TracBrowser for help on using the repository browser.