Commit 87eb2daa authored by Andreas Cord-Landwehr's avatar Andreas Cord-Landwehr
Browse files

Implement sync from skeleton and add tests

parent 50da0459
......@@ -115,6 +115,7 @@ ecm_mark_as_test(test_skeletonresource)
set(TestEditableCourseResource_SRCS
editablecourseresource/test_editablecourseresource.cpp
../mocks/resourcerepositorystub.cpp
../mocks/coursestub.cpp
../mocks/languagestub.cpp
)
qt5_add_resources(TestEditableCourseResource_SRCS ../../data/languages.qrc)
......
......@@ -26,6 +26,7 @@
#include "core/resources/courseparser.h"
#include "core/resources/editablecourseresource.h"
#include "../mocks/languagestub.h"
#include "../mocks/coursestub.h"
#include <memory>
#include <QTest>
......@@ -314,4 +315,49 @@ void TestEditableCourseResource::modifiedStatus()
}
}
void TestEditableCourseResource::skeletonUpdate()
{
std::shared_ptr<ILanguage> language(new LanguageStub("de"));
ResourceRepositoryStub repository({language});
auto course = EditableCourseResource::create(QUrl::fromLocalFile(":/courses/de.xml"), &repository);
QCOMPARE(course->units().count(), 1);
// create skeleton stub
auto importPhrase = new Phrase;
importPhrase->setId("importPhraseId");
importPhrase->setText("phraseText");
importPhrase->setType(Phrase::Sentence);
auto importUnit = std::shared_ptr<Unit>(new Unit);
importUnit->setId("importId");
importUnit->addPhrase(importPhrase);
auto skeleton = CourseStub::create(language, {importUnit});
// test import
course->updateFrom(skeleton);
QCOMPARE(course->units().count(), 2);
{
std::shared_ptr<Unit> importedUnit;
for (auto unit : course->units()) {
if (unit->foreignId() == importUnit->id()) {
importedUnit = unit;
break;
}
}
QVERIFY(importedUnit != nullptr);
QCOMPARE(importedUnit->foreignId(), importUnit->id());
QCOMPARE(importedUnit->id(), importUnit->id());
QCOMPARE(importedUnit->title(), importUnit->title());
QCOMPARE(importedUnit->phraseList().count(), 1);
auto importedPhrase = importedUnit->phraseList().first();
QCOMPARE(importedPhrase->id(), importPhrase->id());
QCOMPARE(importedPhrase->foreignId(), importPhrase->id());
QCOMPARE(importedPhrase->text(), importPhrase->text());
QCOMPARE(importedPhrase->type(), importPhrase->type());
}
// test that re-import does not change course
course->updateFrom(skeleton);
QCOMPARE(course->units().count(), 2);
}
QTEST_GUILESS_MAIN(TestEditableCourseResource)
......@@ -66,6 +66,11 @@ private Q_SLOTS:
* Test if the modified status is correctly set.
*/
void modifiedStatus();
/**
* Test correct update of course from a skeleton
*/
void skeletonUpdate();
};
#endif
......@@ -121,6 +121,10 @@ public:
{
return false;
}
void updateFrom(std::shared_ptr<ICourse>) override
{
// not implemented
}
bool isModified() const override
{
return false;
......
......@@ -51,6 +51,13 @@ public:
* @return true if no errors occured
*/
virtual bool sync() = 0;
/**
* @brief Update course from skeleton
* This method imports all units and phrases from the specified skeleton
*
* @param skeleton
*/
virtual void updateFrom(std::shared_ptr<ICourse> skeleton) = 0;
virtual bool isModified() const = 0;
protected:
......
......@@ -19,21 +19,21 @@
*/
#include "editablecourseresource.h"
#include "courseparser.h"
#include "artikulate_debug.h"
#include "core/unit.h"
#include "core/phrase.h"
#include "core/phoneme.h"
#include "core/phrase.h"
#include "core/unit.h"
#include "courseparser.h"
#include <QObject>
#include <QQmlEngine>
#include <QDomDocument>
#include <KLocalizedString>
#include <KTar>
#include <QDir>
#include <QDomDocument>
#include <QFile>
#include <QFileInfo>
#include <QObject>
#include <QQmlEngine>
#include <QUuid>
#include <KTar>
#include <KLocalizedString>
EditableCourseResource::EditableCourseResource(const QUrl &path, IResourceRepository *repository)
: IEditableCourse()
......@@ -48,29 +48,27 @@ EditableCourseResource::EditableCourseResource(const QUrl &path, IResourceReposi
connect(m_course.get(), &ICourse::unitAboutToBeAdded, this, &ICourse::unitAboutToBeAdded);
connect(m_course.get(), &ICourse::unitAdded, this, &ICourse::unitAdded);
connect(m_course.get(), &CourseResource::idChanged, this, &EditableCourseResource::idChanged);
connect(m_course.get(), &CourseResource::foreignIdChanged, this, &EditableCourseResource::foreignIdChanged);
connect(m_course.get(), &CourseResource::titleChanged, this, &EditableCourseResource::titleChanged);
connect(m_course.get(), &CourseResource::descriptionChanged, this, &EditableCourseResource::descriptionChanged);
connect(m_course.get(), &CourseResource::languageChanged, this, &EditableCourseResource::languageChanged);
connect(m_course.get(), &CourseResource::foreignIdChanged, this,
&EditableCourseResource::foreignIdChanged);
connect(
m_course.get(), &CourseResource::titleChanged, this, &EditableCourseResource::titleChanged);
connect(m_course.get(), &CourseResource::descriptionChanged, this,
&EditableCourseResource::descriptionChanged);
connect(m_course.get(), &CourseResource::languageChanged, this,
&EditableCourseResource::languageChanged);
}
std::shared_ptr<EditableCourseResource> EditableCourseResource::create(const QUrl &path, IResourceRepository *repository)
std::shared_ptr<EditableCourseResource> EditableCourseResource::create(
const QUrl &path, IResourceRepository *repository)
{
std::shared_ptr<EditableCourseResource> course(new EditableCourseResource(path, repository));
course->setSelf(course);
return course;
}
void EditableCourseResource::setSelf(std::shared_ptr<ICourse> self) { m_course->setSelf(self); }
void EditableCourseResource::setSelf(std::shared_ptr<ICourse> self)
{
m_course->setSelf(self);
}
QString EditableCourseResource::id() const
{
return m_course->id();
}
QString EditableCourseResource::id() const { return m_course->id(); }
void EditableCourseResource::setId(QString id)
{
......@@ -80,20 +78,14 @@ void EditableCourseResource::setId(QString id)
}
}
QString EditableCourseResource::foreignId() const
{
return m_course->foreignId();
}
QString EditableCourseResource::foreignId() const { return m_course->foreignId(); }
void EditableCourseResource::setForeignId(QString foreignId)
{
m_course->setForeignId(std::move(foreignId));
}
QString EditableCourseResource::title() const
{
return m_course->title();
}
QString EditableCourseResource::title() const { return m_course->title(); }
void EditableCourseResource::setTitle(QString title)
{
......@@ -103,10 +95,7 @@ void EditableCourseResource::setTitle(QString title)
}
}
QString EditableCourseResource::i18nTitle() const
{
return m_course->i18nTitle();
}
QString EditableCourseResource::i18nTitle() const { return m_course->i18nTitle(); }
void EditableCourseResource::setI18nTitle(QString i18nTitle)
{
......@@ -116,10 +105,7 @@ void EditableCourseResource::setI18nTitle(QString i18nTitle)
}
}
QString EditableCourseResource::description() const
{
return m_course->description();
}
QString EditableCourseResource::description() const { return m_course->description(); }
void EditableCourseResource::setDescription(QString description)
{
......@@ -129,10 +115,7 @@ void EditableCourseResource::setDescription(QString description)
}
}
std::shared_ptr<ILanguage> EditableCourseResource::language() const
{
return m_course->language();
}
std::shared_ptr<ILanguage> EditableCourseResource::language() const { return m_course->language(); }
void EditableCourseResource::setLanguage(std::shared_ptr<ILanguage> language)
{
......@@ -142,10 +125,7 @@ void EditableCourseResource::setLanguage(std::shared_ptr<ILanguage> language)
}
}
QUrl EditableCourseResource::file() const
{
return m_course->file();
}
QUrl EditableCourseResource::file() const { return m_course->file(); }
bool EditableCourseResource::sync()
{
......@@ -169,17 +149,18 @@ bool EditableCourseResource::exportToFile(const QUrl &filePath) const
{
// write back to file
// create directories if necessary
QFileInfo info(filePath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path());
QFileInfo info(filePath.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path());
if (!info.exists()) {
qCDebug(ARTIKULATE_LOG()) << "create xml output file directory, not existing";
QDir dir;
dir.mkpath(filePath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path());
dir.mkpath(filePath.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path());
}
//TODO port to KSaveFile
// TODO port to KSaveFile
QFile file(filePath.toLocalFile());
if (!file.open(QIODevice::WriteOnly)) {
qCWarning(ARTIKULATE_LOG()) << "Unable to open file " << file.fileName() << " in write mode, aborting.";
qCWarning(ARTIKULATE_LOG())
<< "Unable to open file " << file.fileName() << " in write mode, aborting.";
return false;
}
......@@ -195,17 +176,55 @@ std::shared_ptr<Unit> EditableCourseResource::addUnit(std::unique_ptr<Unit> unit
return sharedUnit;
}
QVector<std::shared_ptr<Unit>> EditableCourseResource::units()
{
return m_course->units();
}
QVector<std::shared_ptr<Unit>> EditableCourseResource::units() { return m_course->units(); }
bool EditableCourseResource::isModified() const
void EditableCourseResource::updateFrom(std::shared_ptr<ICourse> skeleton)
{
return m_modified;
for (auto skeletonUnit : skeleton->units()) {
// find matching unit or create one
std::shared_ptr<Unit> matchingUnit;
auto it = std::find_if(m_course->units().cbegin(), m_course->units().cend(),
[skeletonUnit](std::shared_ptr<Unit> compareUnit) {
return compareUnit->foreignId() == skeletonUnit->id();
});
if (it == m_course->units().cend()) {
// import complete unit
auto importUnit = std::unique_ptr<Unit>(new Unit);
importUnit->setId(skeletonUnit->id());
importUnit->setForeignId(skeletonUnit->id());
importUnit->setTitle(skeletonUnit->title());
matchingUnit = m_course->addUnit(std::move(importUnit));
} else {
matchingUnit = *it;
}
// import phrases
for (auto skeletonPhrase : skeletonUnit->phraseList()) {
auto it = std::find_if(matchingUnit->phraseList().cbegin(),
matchingUnit->phraseList().cend(), [skeletonPhrase](Phrase *comparePhrase) {
return comparePhrase->foreignId() == skeletonPhrase->id();
});
if (it == matchingUnit->phraseList().cend()) {
// import complete Phrase
Phrase *importPhrase = new Phrase(matchingUnit.get());
importPhrase->setId(skeletonPhrase->id());
importPhrase->setForeignId(skeletonPhrase->id());
importPhrase->setText(skeletonPhrase->text());
importPhrase->seti18nText(skeletonPhrase->i18nText());
importPhrase->setType(skeletonPhrase->type());
importPhrase->setUnit(matchingUnit.get());
matchingUnit->addPhrase(importPhrase);
}
}
}
qCInfo(ARTIKULATE_LOG()) << "Update performed!";
}
Unit * EditableCourseResource::createUnit()
bool EditableCourseResource::isModified() const { return m_modified; }
Unit *EditableCourseResource::createUnit()
{
// find first unused id
QStringList unitIds;
......@@ -228,7 +247,7 @@ Unit * EditableCourseResource::createUnit()
return sharedUnit.get();
}
Phrase * EditableCourseResource::createPhrase(Unit *unit)
Phrase *EditableCourseResource::createPhrase(Unit *unit)
{
// find globally unique phrase id inside course
QStringList phraseIds;
......
......@@ -96,6 +96,7 @@ public:
std::shared_ptr<Unit> addUnit(std::unique_ptr<Unit> unit) override;
QVector<std::shared_ptr<Unit>> units() override;
void updateFrom(std::shared_ptr<ICourse> course) override;
bool isModified() const override;
QUrl file() const override;
......
......@@ -327,6 +327,11 @@ bool SkeletonResource::sync()
return ok;
}
void SkeletonResource::updateFrom(std::shared_ptr<ICourse>)
{
// not supported
}
bool SkeletonResource::isModified() const
{
return d->m_modified;
......
......@@ -58,6 +58,7 @@ public:
bool exportToFile(const QUrl &filePath) const override;
std::shared_ptr<Unit> addUnit(std::unique_ptr<Unit> unit) override;
bool sync() override;
void updateFrom(std::shared_ptr<ICourse>);
bool isModified() const override;
private:
......
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