Commit 380cbdb5 authored by Martin Kampas's avatar Martin Kampas

ut_settings: add upgrade() test

parent d134a5ef
......@@ -17,4 +17,5 @@ UI_SOURCES_DIR = $$BUILD
RCC_DIR = $$BUILD
LIBS += -L$$PWD/build/libssu
LD_LIBRARY_PATH = $$PWD/build/libssu
INCLUDEPATH += $$PWD/libssu
......@@ -97,6 +97,12 @@ void SsuSettings::merge(QSettings *masterSettings, const QStringList &settingsFi
}
}
/*
* If you change anything here, run `make update-upgrade-test-recipe` inside
* tests/ut_settings/ and check the impact of your changes with
* `git diff testdata/upgrade/recipe`. See ut_settings/upgradetesthelper.cpp for
* more details.
*/
void SsuSettings::upgrade(){
int configVersion=0;
int defaultConfigVersion=0;
......
......@@ -5,11 +5,20 @@
* @date 2012
*/
#include <QtCore/QCoreApplication>
#include <QtTest/QtTest>
#include "settingstest.h"
#include "upgradetesthelper.h"
int main(int argc, char **argv){
QCoreApplication app(argc, argv);
if (app.arguments().contains("-generate-upgrade-test-recipe")){
QTextStream out(stdout);
return UpgradeTestHelper::generateSnapshotRecipe(&out) ? 0 : 1;
}
SettingsTest settingsTest;
if (QTest::qExec(&settingsTest, argc, argv))
......
......@@ -11,6 +11,8 @@
#include <ssusettings.h>
#include "upgradetesthelper.h"
void SettingsTest::initTestCase(){
}
......@@ -61,3 +63,75 @@ void SettingsTest::testMerge(){
QCOMPARE(keyIsMerged, keyShouldBeMerged);
QCOMPARE(actualValue, expectedValue);
}
void SettingsTest::testUpgrade_data(){
// Read recipe
QFile recipe(":/testdata/upgrade/recipe");
QVERIFY(recipe.open(QIODevice::ReadOnly));
QList<UpgradeTestHelper::TestCase> testCases = UpgradeTestHelper::readRecipe(&recipe);
// Generate settings file according to recipe
QTemporaryFile settingsFile;
QVERIFY(settingsFile.open() == true);
QSettings settings(settingsFile.fileName(), QSettings::IniFormat);
UpgradeTestHelper::fillSettings(&settings, testCases);
// Generate defaults file according to recipe
QTemporaryFile defaultSettingsFile;
QVERIFY(defaultSettingsFile.open() == true);
QSettings defaultSettings(defaultSettingsFile.fileName(), QSettings::IniFormat);
UpgradeTestHelper::fillDefaultSettings(&defaultSettings, testCases);
// Parse settings -- do upgrade
#if 0
settingsFile.seek(0);
defaultSettingsFile.seek(0);
qDebug() << "SETTINGS {{{\n" << settingsFile.readAll() << "\n}}}";
qDebug() << "DEFAULT SETTINGS {{{\n" << defaultSettingsFile.readAll() << "\n}}}";
#endif
SsuSettings ssuSettings(settingsFile.fileName(), QSettings::IniFormat,
defaultSettingsFile.fileName());
#if 0
settingsFile.seek(0);
qDebug() << "SETTINGS UPGRADED {{{\n" << settingsFile.readAll() << "\n}}}";
#endif
// Record data for verification phase
QTest::addColumn<bool>("keyIsSet");
QTest::addColumn<bool>("keyShouldBeSet");
QTest::addColumn<QString>("actualValue");
QTest::addColumn<QString>("expectedValue");
foreach (const UpgradeTestHelper::TestCase &testCase, testCases){
foreach (const QString &group, UpgradeTestHelper::groups()){
const QString key = group.isEmpty() ? testCase.key() : group + '/' + testCase.key();
QTest::newRow(qPrintable(QString("%1%2:%3:%4")
.arg(group.isEmpty() ? "" : group + "/")
.arg(testCase.history)
.arg(testCase.current)
.arg(testCase.expected)))
<< ssuSettings.contains(key)
<< testCase.keyShouldBeSet()
<< ssuSettings.value(key).toString()
<< testCase.expected;
}
}
}
void SettingsTest::testUpgrade(){
QFETCH(bool, keyIsSet);
QFETCH(bool, keyShouldBeSet);
QFETCH(QString, actualValue);
QFETCH(QString, expectedValue);
QCOMPARE(keyIsSet, keyShouldBeSet);
if (keyIsSet){
QCOMPARE(actualValue, expectedValue);
}
}
......@@ -18,6 +18,8 @@ class SettingsTest: public QObject {
void cleanupTestCase();
void testMerge_data();
void testMerge();
void testUpgrade_data();
void testUpgrade();
private:
};
......
......@@ -5,5 +5,6 @@
<file>testdata/merge/settings.d/foo.ini</file>
<file>testdata/merge/merged.ini</file>
<file>testdata/merge/settings.ini</file>
<file>testdata/upgrade/recipe</file>
</qresource>
</RCC>
This diff is collapsed.
/**
* @file upgradetesthelper.cpp
* @copyright 2013 Jolla Ltd.
* @author Martin Kampas <martin.kampas@tieto.com>
* @date 2013
*/
#include "upgradetesthelper.h"
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include <QtCore/qmath.h>
#include <QtCore/QTemporaryFile>
#include <QtCore/QTextStream>
#include <ssusettings.h>
/**
* @class UpgradeTestHelper
* @brief Utilities to generate upgrade-test data
*
* It is driven by recipe in following format.
*
* Every line consists of three values delimited by colon:
*
* @verbatim
* <history>:<current>:<expected>
* @endverbatim
*
* - history of changes of the key. A word of length HistoryLength made up
* of letters [SKRN] -- (S)et value, (K)eep value, (R)emove key, (N)oop
* - current value (settings at revision CurrentVersion)
* - value expected after upgrade. The special value "@NOTSET@" means the key
* is expected to not be set after upgrade.
*
* Action (S)et results in assigning "v<revision>-default" to the key, e.g.,
* "v5-default" when 'S' appears as 5th letter in the history word.
*
* The key is built as "<history>__<current>".
*
* Example:
*
* @verbatim
* SKNRN:v1-default:@NOTSET@
* SSNSN:v2-default:v4-default
* @endverbatim
*
* Generated settings:
*
* @verbatim
* [Global]
* configVersion = 2
* SKNRN__v1-default = v1-default
* SSNSN__v2-default = v2-default
*
* [groupA]
* SKNRN__v1-default = v1-default
* SSNSN__v2-default = v2-default
* @endverbatim
*
* Generated default settings:
*
* @verbatim
* [Global]
* configVersion = 5
*
* [1]
* SKNRN__v1-default = v1-default
* groupA\SKNRN__v1-default = v1-default
* SSNSN__v2-default = v1-default
* groupA\SSNSN__v2-default = v1-default
*
* [2]
* SKNRN__v1-default = v1-default
* groupA\SKNRN__v1-default = v1-default
* SSNSN__v2-default = v2-default
* groupA\SSNSN__v2-default = v2-default
*
* [3]
*
* [4]
* cmd-remove = SKNRN__v1-default, groupA/SKNRN__v1-default
* SSNSN__v2-default = v4-default
* groupA\SSNSN__v2-default = v4-default
*
* [5]
* @endverbatim
*/
QList<UpgradeTestHelper::TestCase> UpgradeTestHelper::readRecipe(QIODevice *recipe){
QList<TestCase> testCases;
while (!recipe->atEnd()){
const QString line = recipe->readLine().trimmed();
if (line.startsWith('#') || line.isEmpty())
continue;
const QStringList splitted = line.split(':');
Q_ASSERT_X(splitted.count() == 3, Q_FUNC_INFO,
qPrintable(QString("Inalid recipe line '%1'").arg(line)));
testCases.append(TestCase(splitted.at(0), splitted.at(1), splitted.at(2)));
}
return testCases;
}
void UpgradeTestHelper::fillSettings(QSettings *settings, const QList<TestCase> &testCases){
settings->setValue("configVersion", CurrentVersion);
foreach (const QString &group, groups()){
settings->beginGroup(group);
foreach (const TestCase &testCase, testCases){
if (!testCase.current.isEmpty()){
settings->setValue(testCase.key(), testCase.current);
}
}
settings->endGroup();
}
settings->sync();
}
void UpgradeTestHelper::fillDefaultSettings(QSettings *defaultSettings, const QList<TestCase>
&testCases){
defaultSettings->setValue("configVersion", HistoryLength);
QHash<QString, QString> lastSetValue; // for the (K)eep action; no need to qualify with group
for (int revision = 1; revision <= HistoryLength; ++revision){
defaultSettings->beginGroup(QString::number(revision));
QStringList keysToRemove;
foreach (const QString &group, groups()){
defaultSettings->beginGroup(group);
foreach (const TestCase &testCase, testCases){
switch (testCase.history.at(revision - 1).toAscii()){
case 'S': // (S)et value
lastSetValue[testCase.key()] = QString("v%1-default").arg(revision);
defaultSettings->setValue(testCase.key(), lastSetValue[testCase.key()]);
break;
case 'K': // (K)eep value
Q_ASSERT_X(!lastSetValue[testCase.key()].isEmpty(), Q_FUNC_INFO,
qPrintable(QString("Inalid TestCase::history: '%1'").arg(testCase.history)));
defaultSettings->setValue(testCase.key(), lastSetValue[testCase.key()]);
break;
case 'R': // (R)emove key
keysToRemove.append((group.isEmpty() ? group : group + "/") + testCase.key());
lastSetValue.remove(testCase.key());
break;
case 'N': // (N)oop
break;
default:
Q_ASSERT_X(false, Q_FUNC_INFO, qPrintable(QString(
"Inalid TestCase::history: '%1': invalid command-code '%2'")
.arg(testCase.history)
.arg(testCase.history.at(revision - 1))));
}
}
defaultSettings->endGroup();
}
if (!keysToRemove.isEmpty()){
defaultSettings->setValue("cmd-remove", keysToRemove);
}
defaultSettings->endGroup();
}
defaultSettings->sync();
}
bool UpgradeTestHelper::generateSnapshotRecipe(QTextStream *out){
const QString actions = "SKRN";
QBuffer buf;
buf.open(QIODevice::ReadWrite);
QTextStream stream(&buf);
// for all "valid" variations of the letters "SKRN" of length HistoryLength
for (int i = 0; i < qPow(actions.count(), HistoryLength); ++i){
QString history = QString::number(i, actions.count());
// Left pad to HistoryLength
history.prepend(QString(HistoryLength - history.length(), '0'));
for (int revision = 0; revision < history.length(); ++revision){
history.replace(revision, 1, actions.at(history.at(revision).digitValue()));
}
static const QRegExp invalidSequence("(^[^S]*K|R[^S]*K|^R)");
if (history.contains(invalidSequence)){
continue;
}
for (int revision = 0; revision < HistoryLength; ++revision){
stream << history << QString(":v%1-default:\n").arg(revision);
}
stream << history << ":custom:\n";
}
stream.flush();
// Read recipe
buf.seek(0);
QList<TestCase> testCases = readRecipe(&buf);
// Generate settings file according to recipe
QTemporaryFile settingsFile;
if (!settingsFile.open()){
return false;
}
QSettings settings(settingsFile.fileName(), QSettings::IniFormat);
fillSettings(&settings, testCases);
// Generate defaults file according to recipe
QTemporaryFile defaultSettingsFile;
if (!defaultSettingsFile.open()){
return false;
}
QSettings defaultSettings(defaultSettingsFile.fileName(), QSettings::IniFormat);
fillDefaultSettings(&defaultSettings, testCases);
// Parse settings -- do upgrade
SsuSettings ssuSettings(settingsFile.fileName(), QSettings::IniFormat,
defaultSettingsFile.fileName());
// Output recipe
foreach (const UpgradeTestHelper::TestCase &testCase, testCases){
const QString expected = ssuSettings.contains(testCase.key())
? ssuSettings.value(testCase.key()).toString()
: "@NOTSET@";
*out << QString("%1:%2:%3\n")
.arg(testCase.history)
.arg(testCase.current)
.arg(expected);
}
return true;
}
QStringList UpgradeTestHelper::groups(){
static const QStringList groups = QStringList() << "" /* General */ << "groupA";
return groups;
}
/**
* @file upgradetesthelper.h
* @copyright 2013 Jolla Ltd.
* @author Martin Kampas <martin.kampas@tieto.com>
* @date 2013
*/
#ifndef _UPGRADETESTHELPER_H
#define _UPGRADETESTHELPER_H
#include <QtCore/QString>
#include <QtCore/QStringList>
class QIODevice;
class QSettings;
class QTextStream;
class UpgradeTestHelper {
public:
enum { HistoryLength = 5, CurrentVersion = 3 };
struct TestCase;
static QList<TestCase> readRecipe(QIODevice *recipe);
static void fillSettings(QSettings *settings, const QList<TestCase> &testCases);
static void fillDefaultSettings(QSettings *defaultSettings, const QList<TestCase> &testCases);
static bool generateSnapshotRecipe(QTextStream *out);
static QStringList groups();
};
struct UpgradeTestHelper::TestCase {
TestCase(const QString &history, const QString &current, const QString &expected) :
history(history), current(current), expected(expected){
}
QString key() const{
return QString("%1__%2").arg(history).arg(current);
}
bool keyShouldBeSet() const{
return expected != "@NOTSET@";
}
const QString history; // Sequence of (S)et, (K)eep, (R)emove, (N)oop
const QString current;
const QString expected;
};
#endif
HEADERS = \
settingstest.h \
upgradetesthelper.h \
SOURCES = \
main.cpp \
settingstest.cpp \
upgradetesthelper.cpp \
RESOURCES = testdata.qrc
TEMPLATE = app
......@@ -20,3 +22,12 @@ unix:target.path = $${PREFIX}/$$TESTS_PATH
INSTALLS += target
!include( ../../buildpath.pri ) { error("Unable to find build path specification") }
update_upgrade_test_recipe.target = update-upgrade-test-recipe
update_upgrade_test_recipe.depends = first
update_upgrade_test_recipe.commands = \
LD_LIBRARY_PATH="$${LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}" \
$${DESTDIR}/$${TARGET} -generate-upgrade-test-recipe \
2>/dev/null > $${PWD}/testdata/upgrade/recipe
update_upgrade_test_recipe.CONFIG += phony
QMAKE_EXTRA_TARGETS += update_upgrade_test_recipe
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment