Commit 51b49bf7 authored by Andreas Cord-Landwehr's avatar Andreas Cord-Landwehr
Browse files

Implement unit tests for editor session

parent ca0885f7
......@@ -27,6 +27,7 @@
#include <QVector>
class Language;
class SkeletonResource;
/**
* @brief The EditableRepositoryStub is simple sub class only for testing
......@@ -35,8 +36,13 @@ class EditableRepositoryStub : public IEditableRepository
{
Q_OBJECT
public:
EditableRepositoryStub(QVector<IEditableCourse *> courses)
: m_courses{ courses }
EditableRepositoryStub(
QVector<Language *> languages,
QVector<IEditableCourse *> skeletons,
QVector<IEditableCourse *> courses)
: m_languages{ languages }
, m_skeletons{ skeletons }
, m_courses{ courses }
{
}
~EditableRepositoryStub() override;
......@@ -44,6 +50,10 @@ public:
{
return QString();
}
QVector<IEditableCourse *> skeletons() const override
{
return m_skeletons;
}
QVector<IEditableCourse *> editableCourses() const override
{
return m_courses;
......@@ -73,7 +83,7 @@ public:
}
QVector<Language *> languages() const override
{
return QVector<Language *>();
return m_languages;
}
Q_SIGNALS:
void courseAboutToBeAdded(ICourse*,int) override;
......@@ -81,6 +91,8 @@ Q_SIGNALS:
void courseAboutToBeRemoved(int) override;
void courseRemoved() override;
private:
QVector<Language *> m_languages;
QVector<IEditableCourse *> m_skeletons;
QVector<IEditableCourse *> m_courses;
};
......
......@@ -25,6 +25,7 @@
#include "src/core/ieditablecourse.h"
#include "src/core/ieditablerepository.h"
#include "src/core/language.h"
#include "src/core/resources/skeletonresource.h"
#include "src/core/unit.h"
#include <QTest>
#include <QSignalSpy>
......@@ -126,13 +127,106 @@ void TestEditorSession::cleanup()
void TestEditorSession::createEditorSession()
{
Language language;
EditableCourseStub course(&language, QVector<Unit *>());
EditableRepositoryStub repository{ {&course} };
Language languageGerman;
languageGerman.setId("de");
Language languageEnglish;
languageEnglish.setId("en");
EditableCourseStub course(&languageGerman, QVector<Unit *>());
course.setLanguage(&languageGerman);
SkeletonResource skeleton(QUrl(), nullptr);
EditableRepositoryStub repository{
{&languageGerman, &languageEnglish}, // languages
{&skeleton},
{&course} // courses
};
EditorSession session;
session.setRepository(&repository);
QVERIFY(session.course() == nullptr);
QVERIFY(session.language() == nullptr);
QVERIFY(session.skeleton() == nullptr);
}
void TestEditorSession::nonSkeletonSwitchingBehavior()
{
Language languageGerman;
languageGerman.setId("de");
Language languageEnglish;
languageEnglish.setId("en");
EditableCourseStub courseGerman(&languageGerman, QVector<Unit *>());
courseGerman.setId("course-german");
EditableCourseStub courseEnglish(&languageEnglish, QVector<Unit *>());
courseEnglish.setId("course-english");
EditableRepositoryStub repository{
{&languageGerman, &languageEnglish}, // languages
{}, // skeletons
{&courseGerman, &courseEnglish} // courses
};
EditorSession session;
session.setRepository(&repository);
QVERIFY(session.course() == nullptr);
session.setCourse(&courseGerman);
QCOMPARE(session.course()->id(), courseGerman.id());
QVERIFY(session.language() != nullptr);
QCOMPARE(session.language()->id(), languageGerman.id());
QVERIFY(session.language() != nullptr);
QCOMPARE(session.language()->id(), languageGerman.id());
session.setCourse(&courseEnglish);
QVERIFY(session.course() != nullptr);
QCOMPARE(session.course()->id(), courseEnglish.id());
QVERIFY(session.language() != nullptr);
QCOMPARE(session.language()->id(), languageEnglish.id());
}
void TestEditorSession::skeletonSwitchingBehavior()
{
Language languageGerman;
languageGerman.setId("de");
Language languageEnglish;
languageEnglish.setId("en");
EditableCourseStub courseGermanA(&languageGerman, QVector<Unit *>());
courseGermanA.setId("course-german");
courseGermanA.setForeignId("testskeletonA");
EditableCourseStub courseGermanB(&languageGerman, QVector<Unit *>());
courseGermanB.setId("course-german");
courseGermanB.setForeignId("testskeletonB");
EditableCourseStub courseEnglishA(&languageEnglish, QVector<Unit *>());
courseEnglishA.setId("course-english");
courseEnglishA.setForeignId("testskeletonA");
SkeletonResource skeletonA(QUrl(), nullptr);
skeletonA.setId("testskeletonA");
SkeletonResource skeletonB(QUrl(), nullptr);
skeletonB.setId("testskeletonB");
EditableRepositoryStub repository{
{&languageGerman, &languageEnglish}, // languages
{&skeletonA, &skeletonB}, // skeletons
{&courseGermanA, &courseEnglishA, &courseGermanB} // courses
};
EditorSession session;
session.setRepository(&repository);
session.setSkeleton(&skeletonA);
Q_ASSERT(session.skeleton() != nullptr);
QCOMPARE(session.skeleton()->id(), skeletonA.id());
Q_ASSERT(session.course() != nullptr);
QCOMPARE(session.course()->id(), courseGermanA.id());
session.setCourse(&courseEnglishA);
Q_ASSERT(session.course() != nullptr);
QCOMPARE(session.course()->id(), courseEnglishA.id());
session.setCourse(&courseGermanB);
QVERIFY(session.skeleton() != nullptr);
QCOMPARE(session.skeleton()->id(), skeletonB.id());
QVERIFY(session.course() != nullptr);
QCOMPARE(session.course()->id(), course.id());
QCOMPARE(session.course()->id(), courseGermanB.id());
QVERIFY(session.language() != nullptr);
QCOMPARE(session.language()->id(), languageGerman.id());
}
QTEST_GUILESS_MAIN(TestEditorSession)
......@@ -42,9 +42,19 @@ private Q_SLOTS:
void cleanup();
/**
* @brief Construct and destruct editor session
* @brief Construct and destruct editor session and test initial values
*/
void createEditorSession();
/**
* @brief Test switching behavior for courses without skeleton.
*/
void nonSkeletonSwitchingBehavior();
/**
* @brief Test handling of skeletons and respective course switching
*/
void skeletonSwitchingBehavior();
};
#endif
......@@ -107,7 +107,9 @@ void Application::registerQmlTypes()
// interfaces
qmlRegisterInterface<IResourceRepository>("IResourceRepository");
qmlRegisterInterface<IResourceRepository>("IEditableRepository");
qmlRegisterInterface<ICourse>("ICourse");
qmlRegisterInterface<IEditableCourse>("IEditableCourse");
// concrete instantiable types
qmlRegisterType<LearnerProfile::Learner>("artikulate", 1, 0, "Learner");
......
......@@ -471,7 +471,11 @@ void ContributorRepository::removeSkeleton(SkeletonResource *skeleton)
}
}
QList< SkeletonResource* > ContributorRepository::skeletonResources()
QVector<IEditableCourse *> ContributorRepository::skeletons() const
{
return m_skeletonResources;
QVector<IEditableCourse *> skeletonList;
for (const auto &skeleton : m_skeletonResources) {
skeletonList.append(skeleton);
}
return skeletonList;
}
......@@ -26,6 +26,7 @@
#include <QObject>
#include <QMap>
#include <QHash>
#include <QVector>
#include <QStringList>
#include "liblearnerprofile/src/learninggoal.h"
......@@ -180,10 +181,7 @@ public:
*/
void removeSkeleton(SkeletonResource *skeleton);
/**
* \return list of all loaded skeletons resources
*/
QList<SkeletonResource *> skeletonResources();
QVector<IEditableCourse *> skeletons() const override;
Q_SIGNALS:
void languageResourceAdded();
......@@ -210,7 +208,7 @@ private:
QString m_storageLocation;
QList<LanguageResource *> m_languageResources;
QMap<QString, QList<EditableCourseResource *> > m_courses; //!> (language-id, course-resource)
QList<SkeletonResource *> m_skeletonResources;
QVector<SkeletonResource *> m_skeletonResources;
QStringList m_loadedResources;
};
......
......@@ -30,15 +30,6 @@
EditorSession::EditorSession(QObject *parent)
: QObject(parent)
, m_repository(nullptr)
, m_skeletonMode(true)
, m_editSkeleton(false)
, m_skeleton(nullptr)
, m_language(nullptr)
, m_course(nullptr)
, m_tmpCourseWhileSkeletonEditing(nullptr)
, m_unit(nullptr)
, m_phrase(nullptr)
{
}
......@@ -46,18 +37,6 @@ EditorSession::EditorSession(QObject *parent)
void EditorSession::setRepository(IEditableRepository *repository)
{
m_repository = repository;
if (!repository->editableCourses().isEmpty()) {
setCourse(repository->editableCourses().first());
}
}
void EditorSession::setSkeletonMode(bool enabled)
{
if (m_skeletonMode == enabled) {
return;
}
m_skeletonMode = enabled;
emit skeletonModeChanged();
}
bool EditorSession::skeletonMode() const
......@@ -86,38 +65,32 @@ bool EditorSession::isEditSkeleton() const
return m_editSkeleton;
}
SkeletonResource * EditorSession::skeleton() const
IEditableCourse * EditorSession::skeleton() const
{
return m_skeleton;
}
void EditorSession::setSkeleton(SkeletonResource *skeleton)
void EditorSession::setSkeleton(IEditableCourse *skeleton)
{
if (m_skeleton == skeleton) {
return;
}
m_skeleton = skeleton;
Language *language = m_language;
if (!m_language) {
language = m_repository->languages().constFirst();
if (m_skeletonMode != true) {
m_skeletonMode = true;
emit skeletonModeChanged();
}
if (m_skeleton) {
bool found = false;
int resources = m_repository->courses(language).count();
for (int i=0; i < resources; ++i) {
auto course = m_repository->editableCourse(language, i);
IEditableCourse *newCourse{ nullptr };
if (m_skeleton && m_repository) {
for (const auto &course : m_repository->editableCourses()) {
if (course->foreignId() == m_skeleton->id()) {
setCourse(course);
found = true;
newCourse = course;
break;
}
}
if (!found) {
setCourse(nullptr);
}
}
setCourse(newCourse);
emit skeletonChanged();
}
......@@ -127,37 +100,6 @@ Language * EditorSession::language() const
return m_language;
}
void EditorSession::setLanguage(Language *language)
{
if (m_language == language) {
return;
}
m_language = language;
if (m_skeletonMode) {
bool found = false;
if (m_skeleton) {
int resources = m_repository->courses(m_language).count();
for (int i=0; i < resources; ++i) {
IEditableCourse *course = m_repository->editableCourse(m_language, i);
if (course->foreignId() == m_skeleton->id()) {
setCourse(course);
found = true;
break;
}
}
}
if (!found) {
setCourse(nullptr);
}
}
else { // not skeleton mode
if (m_repository->courses(m_language).count() > 0) {
setCourse(m_repository->editableCourse(m_language, 0));
}
}
emit languageChanged();
}
IEditableCourse * EditorSession::course() const
{
return m_course;
......@@ -169,6 +111,23 @@ void EditorSession::setCourse(IEditableCourse *course)
return;
}
m_course = course;
if (m_skeleton == nullptr || m_skeleton->id() != course->foreignId()) {
for (const auto &skeleton : m_repository->skeletons()) {
if (skeleton->id() == course->foreignId()) {
m_skeleton = skeleton;
emit skeletonChanged();
break;
}
}
}
if (m_course != nullptr) {
m_language = m_course->language();
} else {
m_language = nullptr;
}
emit languageChanged();
if (m_course && !m_course->unitList().isEmpty()) {
setUnit(m_course->unitList().constFirst());
} else {
......
......@@ -55,10 +55,10 @@ class IEditableRepository;
class ARTIKULATECORE_EXPORT EditorSession : public QObject
{
Q_OBJECT
Q_PROPERTY(bool skeletonMode READ skeletonMode WRITE setSkeletonMode NOTIFY skeletonModeChanged)
Q_PROPERTY(bool skeletonMode READ skeletonMode NOTIFY skeletonModeChanged)
Q_PROPERTY(bool editSkeleton READ isEditSkeleton WRITE setEditSkeleton NOTIFY editSkeletonChanged)
Q_PROPERTY(SkeletonResource *skeleton READ skeleton WRITE setSkeleton NOTIFY skeletonChanged)
Q_PROPERTY(Language *language READ language WRITE setLanguage NOTIFY languageChanged)
Q_PROPERTY(IEditableCourse *skeleton READ skeleton WRITE setSkeleton NOTIFY skeletonChanged)
Q_PROPERTY(Language *language READ language NOTIFY languageChanged)
Q_PROPERTY(IEditableCourse *course READ course WRITE setCourse NOTIFY courseChanged)
Q_PROPERTY(Unit *unit READ unit WRITE setUnit NOTIFY unitChanged)
Q_PROPERTY(Phrase *phrase READ phrase WRITE setPhrase NOTIFY phraseChanged)
......@@ -69,14 +69,12 @@ public:
explicit EditorSession(QObject *parent = nullptr);
void setRepository(IEditableRepository *repository);
void setSkeletonMode(bool enabled=true);
bool skeletonMode() const;
void setEditSkeleton(bool enabled=true);
bool isEditSkeleton() const;
SkeletonResource * skeleton() const;
void setSkeleton(SkeletonResource *skeleton);
IEditableCourse * skeleton() const;
void setSkeleton(IEditableCourse *skeleton);
Language * language() const;
void setLanguage(Language *language);
IEditableCourse * course() const;
void setCourse(IEditableCourse *course);
Unit * unit() const;
......@@ -106,15 +104,15 @@ Q_SIGNALS:
private:
Q_DISABLE_COPY(EditorSession)
IEditableRepository * m_repository;
bool m_skeletonMode;
bool m_editSkeleton;
SkeletonResource *m_skeleton;
Language *m_language;
IEditableCourse *m_course;
IEditableCourse *m_tmpCourseWhileSkeletonEditing;
Unit *m_unit;
Phrase *m_phrase;
IEditableRepository * m_repository{ nullptr };
bool m_skeletonMode{ false };
bool m_editSkeleton{ false };
IEditableCourse *m_skeleton{ nullptr };
Language *m_language{ nullptr };
IEditableCourse *m_course{ nullptr };
IEditableCourse *m_tmpCourseWhileSkeletonEditing{ nullptr };
Unit *m_unit{ nullptr };
Phrase *m_phrase{ nullptr };
};
#endif
......@@ -39,6 +39,7 @@ public:
virtual ~IEditableRepository() = default;
virtual QVector<IEditableCourse *> editableCourses() const = 0;
virtual IEditableCourse * editableCourse(Language *language, int index) const = 0;
virtual QVector<IEditableCourse *> skeletons() const = 0;
};
Q_DECLARE_INTERFACE(IEditableRepository, "IEditableRepository")
......
......@@ -180,7 +180,7 @@ QDomDocument SkeletonResourcePrivate::serializedSkeleton()
}
SkeletonResource::SkeletonResource(const QUrl &path, IResourceRepository *repository)
: ICourse()
: IEditableCourse()
, d(new SkeletonResourcePrivate(path))
{
Q_UNUSED(repository);
......@@ -193,7 +193,7 @@ QString SkeletonResource::id() const
return d->m_identifier;
}
void SkeletonResource::setId(const QString &id)
void SkeletonResource::setId(QString id)
{
if (d->m_identifier == id) {
return;
......@@ -207,12 +207,18 @@ QString SkeletonResource::foreignId() const
return id();
}
void SkeletonResource::setForeignId(QString id)
{
Q_UNUSED(id);
Q_UNREACHABLE();
}
QString SkeletonResource::title() const
{
return d->m_title;
}
void SkeletonResource::setTitle(const QString &title)
void SkeletonResource::setTitle(QString title)
{
if (d->m_title == title) {
return;
......@@ -227,12 +233,18 @@ QString SkeletonResource::i18nTitle() const
return title();
}
void SkeletonResource::setI18nTitle(QString title)
{
Q_UNUSED(title);
Q_UNREACHABLE();
}
QString SkeletonResource::description() const
{
return d->m_description;
}
void SkeletonResource::setDescription(const QString &description)
void SkeletonResource::setDescription(QString description)
{
if (d->m_description == description) {
return;
......@@ -276,6 +288,12 @@ Language * SkeletonResource::language() const
return nullptr;
}
void SkeletonResource::setLanguage(Language *language)
{
Q_UNUSED(language);
Q_UNREACHABLE();
}
QList<Unit *> SkeletonResource::unitList()
{
return d->units().toList();
......
......@@ -22,7 +22,7 @@
#define SKELETONRESOURCE_H
#include "artikulatecore_export.h"
#include "core/icourse.h"
#include "core/ieditablecourse.h"
#include <QObject>
......@@ -32,7 +32,7 @@ class IResourceRepository;
/**
* @brief The SkeletonResource class is a decorator for EditableCourseResource
*/
class ARTIKULATECORE_EXPORT SkeletonResource : public ICourse
class ARTIKULATECORE_EXPORT SkeletonResource : public IEditableCourse
{
Q_OBJECT
Q_INTERFACES(ICourse)
......@@ -47,19 +47,18 @@ public:
QString id() const override;
void setId(const QString &id);
void setId(QString id) override;
QString foreignId() const override;
void setForeignId(QString id) override;
QString title() const override;
void setTitle(const QString &title);
void setTitle(QString title) override;
QString i18nTitle() const override;
void setI18nTitle(QString title) override;
QString description() const override;
void setDescription(const QString &description);
void setDescription(QString description) override;
Language * language() const override;
void setLanguage(Language *language) override;
QList<Unit *> unitList() override;
QUrl file() const override;
......
......@@ -85,11 +85,11 @@ QVariant SkeletonModel::data(const QModelIndex& index, int role) const
return QVariant();
}
if (index.row() >= m_repository->skeletonResources().count()) {
if (index.row() >= m_repository->skeletons().count()) {
return QVariant();
}
ICourse * const skeleton = m_repository->skeletonResources().at(index.row());
ICourse * const skeleton = m_repository->skeletons().at(index.row());
switch(role)
{
......@@ -121,7 +121,7 @@ int SkeletonModel::rowCount(const QModelIndex &parent) const
return 0;
}
return m_repository->skeletonResources().count();
return m_repository->skeletons().count();
}
void SkeletonModel::onSkeletonAboutToBeAdded(ICourse *skeleton, int index)
......@@ -168,14 +168,14 @@ QVariant SkeletonModel::headerData(int section, Qt::Orientation orientation, int
int SkeletonModel::count() const
{
return m_repository->skeletonResources().count();
return m_repository->skeletons().count();
}
void SkeletonModel::updateMappings()
{
int skeletons = m_repository->skeletonResources().count();
int skeletons = m_repository->skeletons().count();
for (int i = 0; i < skeletons; ++i) {
m_signalMapper->setMapping(m_repository->skeletonResources().at(i), i);