GSoC Status Update - Week 2

This is a status update for my Google Summer of Code 2013 project - implementing advanced statistics importers for Amarok. Please read the first post if you would like to know more about the project.

I didn't think writing tests could be so much fun! Neither did I think they could take me so much time. What I scheduled for the past week was "implementing the initial test suite for importers (exam week)," and it was supposed to be a fast & easy task, allowing me to focus on my exams. And yet after my last test that week I found myself spending every day intensively coding. Most of the time was spent on figuring out how to approach testing and writing the code to make that chosen approach possible; writing tests themselves actually was the fast & easy task I thought it to be.

At the end, I ran git diff --stat for my changes: 26 files changed, 1246 insertions(+), 69 deletions(-). I was surprised.

I managed to test things the way I wanted to. I created two test suites, TestITunesImporter and TestFastForwardImporter, both inheriting from TestImportersCommon, which holds common tests and utility functions. I slightly modified both importers so that I could statically provide them with arbitrary database paths to read from and arbitrary collections to write to - one of my goals for this week was *not to modify existing importers in any major way.

I created two collections: localCollection and fileCollection; the latter fulfilling a role of FileTrackProvider, giving importers information about tracks located at nonexistent paths; the former being the collection tracks were synchronized to. To easily check, clear, and modify their contents I extended capabilities of Collections::CollectionTestImpl class, which stores collection in-memory. The 'init' and 'clean' test procedures take care of filling the fileCollection with necessary data, clearing localCollection, and resetting statistics after each test.

The code has no idea which importers it's dealing with, operating on DatabaseImporter, a superclass of both. The tests don't know even that. A test sets preconditions by modifying collections' contents, calls blockingImport() method of TestImportersCommon, and then checks the resulting state. An elegant, implementation-agnostic way, which fulfills my second goal for the week: write tests for current importers in a way that would make them easily reused for reimplemented importers. Done and done.

So here's something I learned that week: how to run an asynchronous task in Qt and block until it's done. That's a useful thing for testing! Here's the full implementation of blockingImport():

void
TestImportersCommon::blockingImport()
{
    QScopedPointer<DatabaseImporter> importer( newInstance() );
    QEventLoop loop;

    connect( importer.data(), SIGNAL(importSucceeded()), &loop, SLOT(quit()), Qt::QueuedConnection );
    connect( importer.data(), SIGNAL(importFailed()), &loop, SLOT(quit()), Qt::QueuedConnection );
    connect( importer.data(), SIGNAL(importError(QString)), &loop, SLOT(quit()), Qt::QueuedConnection );
    connect( importer.data(), SIGNAL(importFailed()), this, SLOT(importFailed()) );
    connect( importer.data(), SIGNAL(importError(QString)), this, SLOT(importFailed()) );

    importer->startImporting();
    loop.exec();
}

And this is as personal as it gets when it comes to interacting with importers in tests. (As you can see, I'm playing with a new plugin for code snippets. Configuring it fully is yet another thing I have planned for later.)

You can check out my progress on my public Amarok clone. The branch is named gsoc-importers.

Thanks for reading!