Browse Source

added settings generator tests

pull/2/head
Skycoder42 7 years ago
parent
commit
a5d8d641f1
No known key found for this signature in database GPG Key ID: 8E01AD9EF0578D2B
  1. 12
      README.md
  2. 19
      src/mvvmcore/settingsentry.h
  3. 7
      tests/auto/mvvmcore/settingsgenerator/generatortest.xml
  4. 31
      tests/auto/mvvmcore/settingsgenerator/import_config.xml
  5. 7
      tests/auto/mvvmcore/settingsgenerator/import_normal.xml
  6. 11
      tests/auto/mvvmcore/settingsgenerator/importtest.xml
  7. 13
      tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro
  8. 8
      tests/auto/mvvmcore/settingsgenerator/testbackend.cpp
  9. 3
      tests/auto/mvvmcore/settingsgenerator/testbackend.h
  10. 80
      tests/auto/mvvmcore/settingsgenerator/tst_settingsgenerator.cpp
  11. 1
      tools/settingsgenerator/qsettingsgenerator.xsd
  12. 74
      tools/settingsgenerator/settingsgenerator.cpp
  13. 8
      tools/settingsgenerator/settingsgenerator.h

12
README.md

@ -57,17 +57,13 @@ Good links to get started:
- Package: `qtmvvm`
- **IMPORTANT:** Due to limitations of homebrew, you must run `source /usr/local/opt/qtmvvm/bashrc.sh` before you can use the module. Some goes for the `qtdatasync` dependency.
2. Simply add my repository to your Qt MaintenanceTool (Image-based How-To here: [Add custom repository](https://github.com/Skycoder42/QtModules/blob/master/README.md#add-my-repositories-to-qt-maintenancetool)):
1. Open the MaintenanceTool, located in your Qt install directory (e.g. `~/Qt/MaintenanceTool`)
2. Select `Add or remove components` and click on the `Settings` button
3. Go to `Repositories`, scroll to the bottom, select `User defined repositories` and press `Add`
4. In the right column (selected by default), type:
1. Start the MaintenanceTool from the commandline using `/path/to/MaintenanceTool --addTempRepository <url>` with one of the following urls (GUI-Method is currently broken, see [QTIFW-1156](https://bugreports.qt.io/browse/QTIFW-1156)) - This must be done *every time* you start the tool:
- On Linux: https://install.skycoder42.de/qtmodules/linux_x64
- On Windows: https://install.skycoder42.de/qtmodules/windows_x86
- On Mac: https://install.skycoder42.de/qtmodules/mac_x64
5. Press `Ok`, make shure `Add or remove components` is still selected, and continue the install (`Next >`)
6. A new entry appears under all supported Qt Versions (e.g. `Qt > Qt 5.10 > Skycoder42 Qt modules`)
7. You can install either all of my modules, or select the one you need: `Qt Mvvm`
8. Continue the setup and thats it! you can now use the module for all of your installed Kits for that Qt Version
2. A new entry appears under all supported Qt Versions (e.g. `Qt > Qt 5.11 > Skycoder42 Qt modules`)
3. You can install either all of my modules, or select the one you need: `Qt Mvvm`
4. Continue the setup and thats it! you can now use the module for all of your installed Kits for that Qt Version
3. Download the compiled modules from the release page. **Note:** You will have to add the correct ones yourself and may need to adjust some paths to fit your installation! In addition to that, you will have to download the modules this one depends on as well. See Section "Requirements" below.
4. Build it yourself! **Note:** This requires all build an runtime dependencies to be available (See Section "Requirements" below). If you don't have/need cmake, you can ignore the related warnings. To automatically build and install to your Qt installation, run:
- `qmake`

19
src/mvvmcore/settingsentry.h

@ -14,6 +14,7 @@ public:
SettingsEntry() = default;
bool isSet() const;
QString key() const;
T get() const;
void set(const T &value);
@ -26,7 +27,7 @@ public:
void addChangeCallback(QObject *scope, const std::function<void(T)> &callback);
// internal
void setup(const QString &key, ISettingsAccessor *accessor, const QVariant &defaultValue = {});
void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {});
private:
QString _key;
@ -40,10 +41,10 @@ class SettingsEntry<void>
Q_DISABLE_COPY(SettingsEntry)
public:
SettingsEntry() = default;
inline SettingsEntry() = default;
// internal
void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {}
inline void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {}
};
@ -55,6 +56,12 @@ bool SettingsEntry<T>::isSet() const
return _accessor->contains(_key);
}
template<typename T>
QString SettingsEntry<T>::key() const
{
return _key;
}
template<typename T>
T SettingsEntry<T>::get() const
{
@ -110,12 +117,12 @@ SettingsEntry<T>::operator const T() const
}
template<typename T>
void SettingsEntry<T>::setup(const QString &key, ISettingsAccessor *accessor, const QVariant &defaultValue)
void SettingsEntry<T>::setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue)
{
Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!");
_key = key;
_key = std::move(key);
_accessor = accessor;
_default = defaultValue;
_default = std::move(defaultValue);
}
}

7
tests/auto/mvvmcore/settingsgenerator/settings.xml → tests/auto/mvvmcore/settingsgenerator/generatortest.xml

@ -6,7 +6,7 @@
<Include local="false">QtCore/QUrl</Include>
<Include local="true">testbackend.h</Include>
<Backend class="TestBackend">
<Backend class="TestBackend" scope="DestroyOnAppQuit">
<Param type="QString" asStr="true">Test Backend</Param>
<Param type="int">42</Param>
</Backend>
@ -35,13 +35,14 @@
default="42"/>
</Node>
<Entry key="parentEntry"
type="bool">
type="bool"
default="true">
<Node key="subNode"/>
<Entry key="nodeWithCodeEntry"
type="int">
<Node key="someNode"/>
<Code>
qRound(42.5)
qRound(42.8)
</Code>
</Entry>
<Entry key="leafEntry"

31
tests/auto/mvvmcore/settingsgenerator/import_config.xml

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<SettingsConfig allowSearch="true" allowRestore="true">
<Category>
<Entry key="node1/entry1"
type="bool"
title="dummy"
default="true">
</Entry>
</Category>
<Category>
<Section>
<Entry key="node1/entry2"
type="int"
title="dummy"
default="42">
</Entry>
</Section>
<Section>
<Group>
<Entry key="node2/entry1"
type="string"
title="dummy">
</Entry>
<Entry key="node1"
type="url"
title="dummy">
</Entry>
</Group>
</Section>
</Category>
</SettingsConfig>

7
tests/auto/mvvmcore/settingsgenerator/import_normal.xml

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Settings>
<Node key="baum42">
<Entry key="importedEntry" type="bool"/>
</Node>
<Entry key="unimportedEntry" type="bool"/>
</Settings>

11
tests/auto/mvvmcore/settingsgenerator/importtest.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Settings name="TestImportSettings">
<Include>QtCore/QUrl</Include>
<TypeMapping key="string" type="QString"/>
<TypeMapping key="url" type="QUrl"/>
<Node key="impRoot">
<Import required="false" rootNode="baum42">import_normal.xml</Import>
</Node>
<Import required="true">import_config.xml</Import>
</Settings>

13
tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro

@ -8,11 +8,16 @@ CONFIG -= app_bundle
TARGET = tst_settingsgenerator
SOURCES += \
tst_settingsgenerator.cpp \
testbackend.cpp
tst_settingsgenerator.cpp \
testbackend.cpp
SETTINGS_DEFINITIONS += \
settings.xml
generatortest.xml \
importtest.xml
HEADERS += \
testbackend.h
testbackend.h
DISTFILES += \
import_normal.xml \
import_config.xml

8
tests/auto/mvvmcore/settingsgenerator/testbackend.cpp

@ -8,20 +8,24 @@ TestBackend::TestBackend(QString name, int code, QObject *parent) :
bool TestBackend::contains(const QString &key) const
{
return false;
return _data.contains(key);
}
QVariant TestBackend::load(const QString &key, const QVariant &defaultValue) const
{
return {};
return _data.value(key, defaultValue);
}
void TestBackend::save(const QString &key, const QVariant &value)
{
_data.insert(key, value);
emit entryChanged(key, value);
}
void TestBackend::remove(const QString &key)
{
_data.remove(key);
emit entryRemoved(key);
}
void TestBackend::sync()

3
tests/auto/mvvmcore/settingsgenerator/testbackend.h

@ -1,6 +1,7 @@
#ifndef TESTBACKEND_H
#define TESTBACKEND_H
#include <QtCore/QVariantHash>
#include <QtMvvmCore/ISettingsAccessor>
class TestBackend : public QtMvvm::ISettingsAccessor
@ -23,6 +24,8 @@ public slots:
public:
QString _name;
int _code;
QVariantHash _data;
};
#define SOME_EXPORT

80
tests/auto/mvvmcore/settingsgenerator/tst_settingsgenerator.cpp

@ -1,33 +1,85 @@
#include <QString>
#include <QtTest>
#include <QCoreApplication>
#include <QtMvvmCore/QSettingsAccessor>
#include "generatortest.h"
#include "importtest.h"
class SettingsGeneratorTest : public QObject
{
Q_OBJECT
public:
SettingsGeneratorTest();
private Q_SLOTS:
void testSettingsGeneration_data();
void testSettingsGeneration();
void testSettingsGenerator();
void testImportedSettings();
};
SettingsGeneratorTest::SettingsGeneratorTest()
{
}
void SettingsGeneratorTest::testSettingsGeneration_data()
void SettingsGeneratorTest::testSettingsGenerator()
{
QTest::addColumn<QString>("data");
QTest::newRow("0") << QString();
auto settings = TestSettings::instance();
QVERIFY(settings);
// verify backend
auto tBackend = qobject_cast<TestBackend*>(settings->accessor());
QVERIFY(tBackend);
QCOMPARE(tBackend->parent(), settings);
QCOMPARE(tBackend->_name, QStringLiteral("Test Backend"));
QCOMPARE(tBackend->_code, 42);
//verify keys
QCOMPARE(settings->emptyEntry.key(), QStringLiteral("tests/emptyEntry"));
QCOMPARE(settings->advancedEntry.key(), QStringLiteral("tests/advancedEntry"));
QCOMPARE(settings->codeEntry.key(), QStringLiteral("tests/codeEntry"));
QCOMPARE(settings->parentNode.fullChildNode.replaceEntry.key(), QStringLiteral("tests/parentNode/fullChildNode/replaceEntry"));
QCOMPARE(settings->parentNode.parentEntry.key(), QStringLiteral("tests/parentNode/parentEntry"));
QCOMPARE(settings->parentNode.parentEntry.nodeWithCodeEntry.key(), QStringLiteral("tests/parentNode/parentEntry/nodeWithCodeEntry"));
QCOMPARE(settings->parentNode.parentEntry.leafEntry.key(), QStringLiteral("tests/parentNode/parentEntry/leafEntry"));
//verify defaults
QCOMPARE(settings->emptyEntry.get(), false);
QCOMPARE(settings->advancedEntry.get(), QStringLiteral("Hello World"));
QCOMPARE(settings->codeEntry.get(), QUrl::fromLocalFile(QStringLiteral("/path/to/something")));
QCOMPARE(settings->parentNode.fullChildNode.replaceEntry.get(), 42);
QCOMPARE(settings->parentNode.parentEntry.get(), true);
QCOMPARE(settings->parentNode.parentEntry.nodeWithCodeEntry.get(), 43);
QCOMPARE(settings->parentNode.parentEntry.leafEntry.get(), QStringLiteral("translate me"));
//verify read/write (just on a single entry, they work all the same)
Q_ASSERT(tBackend->_data.isEmpty());
QString cValue;
settings->advancedEntry.addChangeCallback([&](QString value) {
cValue = std::move(value);
});
QString tValue = QStringLiteral("test42");
auto tKey = QStringLiteral("tests/advancedEntry");
QCOMPARE(settings->advancedEntry.key(), tKey);
settings->advancedEntry = tValue;
QCOMPARE(tBackend->_data.value(tKey).toString(), tValue);
QCOMPARE(cValue, tValue);
QVERIFY(settings->advancedEntry.isSet());
QCOMPARE(static_cast<QString>(settings->advancedEntry), tValue);
}
void SettingsGeneratorTest::testSettingsGeneration()
void SettingsGeneratorTest::testImportedSettings()
{
QFETCH(QString, data);
QVERIFY2(true, "Failure");
auto settings = TestImportSettings::instance();
QVERIFY(settings);
QVERIFY(qobject_cast<QtMvvm::QSettingsAccessor*>(settings->accessor()));
//verify keys
QCOMPARE(settings->impRoot.importedEntry.key(), QStringLiteral("impRoot/importedEntry"));
QCOMPARE(settings->node1.key(), QStringLiteral("node1"));
QCOMPARE(settings->node1.entry1.key(), QStringLiteral("node1/entry1"));
QCOMPARE(settings->node1.entry2.key(), QStringLiteral("node1/entry2"));
QCOMPARE(settings->node2.entry1.key(), QStringLiteral("node2/entry1"));
//verify defaults
QCOMPARE(settings->impRoot.importedEntry.get(), false);
QCOMPARE(settings->node1.get(), QUrl{});
QCOMPARE(settings->node1.entry1.get(), true);
QCOMPARE(settings->node1.entry2.get(), 42);
QCOMPARE(settings->node2.entry1.get(), QString{});
}
QTEST_MAIN(SettingsGeneratorTest)

1
tools/settingsgenerator/qsettingsgenerator.xsd

@ -53,6 +53,7 @@
<xs:element maxOccurs="unbounded" minOccurs="0" name="Param" type="ParamType"/>
</xs:sequence>
<xs:attribute name="class" type="xs:string" use="required" qxg:member="className"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:complexType>
<xs:group name="NodeContentGroup">

74
tools/settingsgenerator/settingsgenerator.cpp

@ -257,7 +257,7 @@ void SettingsGenerator::writeHeader(const SettingsType &settings)
<< "\tstatic " << settings.name.value() << " *instance();\n\n"
<< "\tQtMvvm::ISettingsAccessor *accessor() const;\n\n";
writeNodeElements(settings, settings.typeMappings);
writeNodeElementDeclarations(settings, settings.typeMappings);
_hdr << "\nprivate:\n"
<< "\tQtMvvm::ISettingsAccessor *_accessor;\n"
@ -265,32 +265,34 @@ void SettingsGenerator::writeHeader(const SettingsType &settings)
<< "#endif //" << incGuard << '\n';
}
void SettingsGenerator::writeNodeElements(const NodeContentGroup &node, const QHash<QString, QString> &typeMappings, int intendent)
void SettingsGenerator::writeNodeElementDeclarations(const NodeContentGroup &node, const QHash<QString, QString> &typeMappings, int intendent)
{
for(const auto &cNode : node.contentNodes) {
if(nonstd::holds_alternative<NodeType>(cNode))
writeNode(nonstd::get<NodeType>(cNode), typeMappings, intendent);
writeNodeDeclaration(nonstd::get<NodeType>(cNode), typeMappings, intendent);
else if(nonstd::holds_alternative<EntryType>(cNode))
writeEntry(nonstd::get<EntryType>(cNode), typeMappings, intendent);
writeEntryDeclaration(nonstd::get<EntryType>(cNode), typeMappings, intendent);
else if(nonstd::holds_alternative<NodeContentGroup>(cNode))
writeNodeElements(nonstd::get<NodeContentGroup>(cNode), typeMappings, intendent);
writeNodeElementDeclarations(nonstd::get<NodeContentGroup>(cNode), typeMappings, intendent);
}
}
void SettingsGenerator::writeNode(const NodeType &node, const QHash<QString, QString> &typeMappings, int intendent)
void SettingsGenerator::writeNodeDeclaration(const NodeType &node, const QHash<QString, QString> &typeMappings, int intendent)
{
_hdr << TABS << "struct { //" << node.key << "\n";
writeNodeElements(node, typeMappings, intendent + 1);
writeNodeElementDeclarations(node, typeMappings, intendent + 1);
_hdr << TABS << "} " << node.key << ";\n";
}
void SettingsGenerator::writeEntry(const EntryType &entry, const QHash<QString, QString> &typeMappings, int intendent)
void SettingsGenerator::writeEntryDeclaration(const EntryType &entry, const QHash<QString, QString> &typeMappings, int intendent)
{
if(entry.contentNodes.isEmpty())
_hdr << TABS << "QtMvvm::SettingsEntry<" << typeMappings.value(entry.type, entry.type) << "> " << entry.key << ";\n";
else {
_hdr << TABS << "struct : QtMvvm::SettingsEntry<" << typeMappings.value(entry.type, entry.type) << "> { //" << entry.key << "\n";
writeNodeElements(entry, typeMappings, intendent + 1);
const auto &mType = typeMappings.value(entry.type, entry.type);
_hdr << TABS << "struct : QtMvvm::SettingsEntry<" << mType << "> { //" << entry.key << "\n";
writeNodeElementDeclarations(entry, typeMappings, intendent + 1);
_hdr << TABS << "\tinline auto &operator=(const " << mType << " &__value) { SettingsEntry<" << mType << ">::operator=(__value); return *this; }\n";
_hdr << TABS << "} " << entry.key << ";\n";
}
}
@ -298,11 +300,25 @@ void SettingsGenerator::writeEntry(const EntryType &entry, const QHash<QString,
void SettingsGenerator::writeSource(const SettingsType &settings)
{
_src << "#include \"" << _hdrFile.fileName() << "\"\n";
_src << "#include <QtCore/QCoreApplication>\n";
_src << "#include <QtMvvmCore/ServiceRegistry>\n";
if(!settings.backend)
_src << "#include <QtMvvmCore/QSettingsAccessor>\n";
_src << "\n";
auto backend = settings.backend.value_or(BackendType{QStringLiteral("QtMvvm::QSettingsAccessor"), {}});
_src << "namespace {\n\n"
<< "void __generated_settings_setup()\n"
<< "{\n"
<< "\tQtMvvm::ServiceRegistry::instance()->registerObject<" << settings.name.value() << ">(";
if(backend.scope)
_src << "QtMvvm::ServiceRegistry::" << backend.scope.value();
_src << ");\n"
<< "}\n\n"
<< "}\n"
<< "Q_COREAPP_STARTUP_FUNCTION(__generated_settings_setup)\n\n";
_src << settings.name.value() << "::" << settings.name.value() << "(QObject *parent) : \n"
<< "\t" << settings.name.value() << "{new " << backend.className << "{";
if(!backend.param.isEmpty()) {
@ -330,11 +346,13 @@ void SettingsGenerator::writeSource(const SettingsType &settings)
_src << settings.name.value() << "::" << settings.name.value() << "(QtMvvm::ISettingsAccessor *accessor, QObject *parent) : \n"
<< "\tQObject{parent},\n"
<< "\t_accessor{accessor}\n"
<< "{}\n\n";
<< "{\n";
writeNodeElementDefinitions(settings, settings.typeMappings, settings.baseKey);
_src << "}\n\n";
_src << settings.name.value() << " *" << settings.name.value() << "::instance()\n"
<< "{\n"
<< "\treturn nullptr;\n"
<< "\treturn QtMvvm::ServiceRegistry::instance()->service<" << settings.name.value() << ">();\n"
<< "}\n\n";
_src << "QtMvvm::ISettingsAccessor *" << settings.name.value() << "::accessor() const\n"
@ -342,3 +360,35 @@ void SettingsGenerator::writeSource(const SettingsType &settings)
<< "\treturn _accessor;\n"
<< "}\n\n";
}
void SettingsGenerator::writeNodeElementDefinitions(const SettingsGeneratorBase::NodeContentGroup &node, const QHash<QString, QString> &typeMappings, const optional<QString> &baseKey, const QStringList &keyChain)
{
for(const auto &cNode : node.contentNodes) {
if(nonstd::holds_alternative<NodeType>(cNode)) {
const auto &xNode = nonstd::get<NodeType>(cNode);
writeNodeElementDefinitions(xNode, typeMappings, baseKey, QStringList{keyChain} << xNode.key);
} else if(nonstd::holds_alternative<EntryType>(cNode))
writeEntryDefinition(nonstd::get<EntryType>(cNode), typeMappings, baseKey, keyChain);
else if(nonstd::holds_alternative<NodeContentGroup>(cNode))
writeNodeElementDefinitions(nonstd::get<NodeContentGroup>(cNode), typeMappings, baseKey, keyChain);
}
}
void SettingsGenerator::writeEntryDefinition(const SettingsGeneratorBase::EntryType &entry, const QHash<QString, QString> &typeMappings, const optional<QString> &baseKey, QStringList keyChain)
{
keyChain.append(entry.key);
_src << "\t" << keyChain.join(QLatin1Char('.'))
<< ".setup(QStringLiteral(\"" << (baseKey ? QStringList{baseKey.value()} + keyChain : keyChain).join(QLatin1Char('/')) << "\"), _accessor";
if(entry.code)
_src << ", QVariant::fromValue<" << typeMappings.value(entry.type, entry.type) << ">(" << entry.code.value().trimmed() << ")";
else if(entry.defaultValue) {
_src << ", QVariant{";
if(entry.tr)
_src << "QCoreApplication::translate(\"" << entry.trContext.value_or(QStringLiteral("qtmvvm_settings_xml")) << "\", \"" << entry.defaultValue.value() << "\")";
else
_src << "QStringLiteral(\"" << entry.defaultValue.value() << "\")";
_src << "}.value<" << typeMappings.value(entry.type, entry.type) << ">()";
}
_src << ");\n";
writeNodeElementDefinitions(entry, typeMappings, baseKey, std::move(keyChain));
}

8
tools/settingsgenerator/settingsgenerator.h

@ -36,11 +36,13 @@ private:
NodeContentGroup *replaceNodeByEntry(NodeContentGroup *cGrp, NodeContentGroup *node, EntryType &&entry);
void writeHeader(const SettingsType &settings);
void writeNodeElements(const NodeContentGroup &node, const QHash<QString, QString> &typeMappings, int intendent = 1);
void writeNode(const NodeType &node, const QHash<QString, QString> &typeMappings, int intendent = 1);
void writeEntry(const EntryType &entry, const QHash<QString, QString> &typeMappings, int intendent = 1);
void writeNodeElementDeclarations(const NodeContentGroup &node, const QHash<QString, QString> &typeMappings, int intendent = 1);
void writeNodeDeclaration(const NodeType &node, const QHash<QString, QString> &typeMappings, int intendent = 1);
void writeEntryDeclaration(const EntryType &entry, const QHash<QString, QString> &typeMappings, int intendent = 1);
void writeSource(const SettingsType &settings);
void writeNodeElementDefinitions(const NodeContentGroup &node, const QHash<QString, QString> &typeMappings, const optional<QString> &baseKey, const QStringList &keyChain = {});
void writeEntryDefinition(const EntryType &entry, const QHash<QString, QString> &typeMappings, const optional<QString> &baseKey, QStringList keyChain);
};
#endif // SETTINGSGENERATOR_H

Loading…
Cancel
Save