diff --git a/autotests/resourcetests/languageresources/testlanguagefiles.h b/autotests/mocks/languagestub.cpp similarity index 56% copy from autotests/resourcetests/languageresources/testlanguagefiles.h copy to autotests/mocks/languagestub.cpp index f3ff5a2..106b30f 100644 --- a/autotests/resourcetests/languageresources/testlanguagefiles.h +++ b/autotests/mocks/languagestub.cpp @@ -1,59 +1,24 @@ /* - * Copyright 2013 Oindrila Gupta + * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef TESTLANGUAGEFILES_H -#define TESTLANGUAGEFILES_H +#include "languagestub.h" -#include - -class QXmlSchema; -class QDomDocument; -class QUrl; - -class TestLanguageFiles : public QObject -{ - Q_OBJECT - -public: - TestLanguageFiles(); - -private slots: - /** - * Called before every test case. - */ - void init(); - - /** - * Called after every test case. - */ - void cleanup(); - - /** - * Test if language XSD specification is valid. - */ - void languageSchemeValidationTest(); - - /** - * Test if id of each phoneme is unique in every language file. - */ - void checkIdUniqueness(); -}; - -#endif +// define one virtual method out of line to pin LanguageStub to this translation unit +LanguageStub::~LanguageStub() = default; diff --git a/autotests/mocks/languagestub.h b/autotests/mocks/languagestub.h new file mode 100644 index 0000000..7e2a6d5 --- /dev/null +++ b/autotests/mocks/languagestub.h @@ -0,0 +1,72 @@ +/* + * Copyright 2019 Andreas Cord-Landwehr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LANGUAGESTUB_H +#define LANGUAGESTUB_H + +#include "src/core/ilanguage.h" +#include +#include + +class Phoneme; +class PhonemeGroup; + +class LanguageStub : public ILanguage +{ +public: + LanguageStub(QString id) + : m_id(id) + { + } + ~LanguageStub() override; + + QString id() const override + { + return m_id; + } + QString title() const override + { + return m_title; + } + void setTitle(QString title) + { + m_title = title; + emit titleChanged(); + } + QString i18nTitle() const override + { + return "i18n title"; + } + QVector> phonemes() const override + { + return QVector>(); + } + QVector> phonemeGroups() const override + { + return QVector>(); + } + +private: + QString m_id{ "UNKNOWN_ID" }; + QString m_title{ "title" }; +}; + + +#endif diff --git a/autotests/resourcetests/languageresources/testlanguagefiles.cpp b/autotests/resourcetests/languageresources/testlanguagefiles.cpp index 9f71770..f9bde8b 100644 --- a/autotests/resourcetests/languageresources/testlanguagefiles.cpp +++ b/autotests/resourcetests/languageresources/testlanguagefiles.cpp @@ -1,101 +1,63 @@ /* * Copyright 2013 Oindrila Gupta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testlanguagefiles.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" +#include "core/phoneme.h" #include "core/resources/courseparser.h" #include -#include -#include #include +#include #include -#include -#include -#include -#include -#include -#include - TestLanguageFiles::TestLanguageFiles() { } void TestLanguageFiles::init() { // TODO initialization of test case } void TestLanguageFiles::cleanup() { // TODO cleanup after test run } -void TestLanguageFiles::languageSchemeValidationTest() -{ - QUrl languageFile = QUrl::fromLocalFile(":/artikulate/schemes/language.xsd"); - QXmlSchema languageSchema; - QVERIFY(languageSchema.load(languageFile)); - QVERIFY(languageSchema.isValid()); -} - void TestLanguageFiles::checkIdUniqueness() { QDirIterator iter(QDir(":/artikulate/languages/")); while (iter.hasNext()) { const QString &file = iter.next(); qDebug() << "File being parsed: " << file; QStringList idList; - const QUrl &languageFile = QUrl::fromLocalFile(file); - QVERIFY(languageFile.isLocalFile()); - - QXmlSchema schema = CourseParser::loadXmlSchema(QStringLiteral("language")); - QVERIFY(schema.isValid()); - - QDomDocument document = CourseParser::loadDomDocument(languageFile, schema); - QVERIFY(!document.isNull()); - QDomElement root(document.documentElement()); - std::unique_ptr language(new Language); - language->setFile(languageFile); - language->setId(root.firstChildElement(QStringLiteral("id")).text()); - language->setTitle(root.firstChildElement(QStringLiteral("title")).text()); - // create phoneme groups - for (QDomElement groupNode = root.firstChildElement(QStringLiteral("phonemeGroups")).firstChildElement(); - !groupNode.isNull(); - groupNode = groupNode.nextSiblingElement()) - { - for (QDomElement phonemeNode = groupNode.firstChildElement(QStringLiteral("phonemes")).firstChildElement(); - !phonemeNode.isNull(); - phonemeNode = phonemeNode.nextSiblingElement()) - { - QString id = phonemeNode.firstChildElement(QStringLiteral("id")).text(); - qDebug() << "ID: " << id; - QVERIFY2(!idList.contains(id),"Phoneme ID used more than once in the tested file"); - idList.append(id); - } + auto language = Language::create(QUrl::fromLocalFile(file)); + for (auto phoneme : language->phonemes()) { + QVERIFY2(!idList.contains(phoneme->id()), "Phoneme ID used more than once in the tested file"); + idList.append(phoneme->id()); } } } QTEST_GUILESS_MAIN(TestLanguageFiles) diff --git a/autotests/resourcetests/languageresources/testlanguagefiles.h b/autotests/resourcetests/languageresources/testlanguagefiles.h index f3ff5a2..e41a55f 100644 --- a/autotests/resourcetests/languageresources/testlanguagefiles.h +++ b/autotests/resourcetests/languageresources/testlanguagefiles.h @@ -1,59 +1,51 @@ /* * Copyright 2013 Oindrila Gupta + * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTLANGUAGEFILES_H #define TESTLANGUAGEFILES_H #include -class QXmlSchema; -class QDomDocument; -class QUrl; - class TestLanguageFiles : public QObject { Q_OBJECT public: TestLanguageFiles(); private slots: /** * Called before every test case. */ void init(); /** * Called after every test case. */ void cleanup(); - /** - * Test if language XSD specification is valid. - */ - void languageSchemeValidationTest(); - /** * Test if id of each phoneme is unique in every language file. */ void checkIdUniqueness(); }; #endif diff --git a/autotests/unittests/CMakeLists.txt b/autotests/unittests/CMakeLists.txt index bed651e..14f4ad3 100644 --- a/autotests/unittests/CMakeLists.txt +++ b/autotests/unittests/CMakeLists.txt @@ -1,140 +1,146 @@ ### # Copyright 2013-2019 Andreas Cord-Landwehr # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### include_directories( ../../src/ ../../ ../mocks/ ${CMAKE_CURRENT_BINARY_DIR} ) # copy test data file(COPY ../testdata/courses/de.xml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/courses/de/) # copy test files file(COPY ../testdata/courses/fr.xml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/courses/fr/) # copy test files file(COPY ../testdata/contributorrepository/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/contributorrepository/) # copy test files set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) # repository tests set(TestResourceRepository_SRCS resourcerepository/test_resourcerepository.cpp) qt5_add_resources(TestResourceRepository_SRCS ../../data/languages.qrc) add_executable(test_resourcerepository ${TestResourceRepository_SRCS}) target_link_libraries(test_resourcerepository artikulatecore Qt5::Test ) add_test(NAME test_resourcerepository COMMAND test_resourcerepository) ecm_mark_as_test(test_resourcerepository) # training session tests set(TestTrainingSession_SRCS trainingsession/test_trainingsession.cpp ../mocks/coursestub.cpp + ../mocks/languagestub.cpp ) add_executable(test_trainingsession ${TestTrainingSession_SRCS}) target_link_libraries(test_trainingsession artikulatecore Qt5::Test ) add_test(NAME test_trainingsession COMMAND test_trainingsession) ecm_mark_as_test(test_trainingsession) # editor session tests set(TestEditorSession_SRCS editorsession/test_editorsession.cpp ../mocks/editablerepositorystub.cpp + ../mocks/languagestub.cpp ) add_executable(test_editorsession ${TestEditorSession_SRCS}) target_link_libraries(test_editorsession artikulatecore Qt5::Test ) add_test(NAME test_editorsession COMMAND test_editorsession) ecm_mark_as_test(test_editorsession) # test course resource class set(TestCourseResource_SRCS courseresource/test_courseresource.cpp ../mocks/resourcerepositorystub.cpp + ../mocks/languagestub.cpp ) qt5_add_resources(TestCourseResource_SRCS ../../data/languages.qrc) add_executable(test_courseresource ${TestCourseResource_SRCS} ) target_link_libraries(test_courseresource artikulatecore Qt5::Test ) add_test(NAME test_courseresource COMMAND test_courseresource) ecm_mark_as_test(test_courseresource) # test skeleton resource class set(TestSkeletonResource_SRCS skeletonresource/test_skeletonresource.cpp ../mocks/resourcerepositorystub.cpp + ../mocks/languagestub.cpp ) qt5_add_resources(TestSkeletonResource_SRCS ../../data/languages.qrc) add_executable(test_skeletonresource ${TestSkeletonResource_SRCS} ) target_link_libraries(test_skeletonresource artikulatecore Qt5::Test ) add_test(NAME test_skeletonresource COMMAND test_skeletonresource) ecm_mark_as_test(test_skeletonresource) # test editable course resource class set(TestEditableCourseResource_SRCS editablecourseresource/test_editablecourseresource.cpp ../mocks/resourcerepositorystub.cpp + ../mocks/languagestub.cpp ) qt5_add_resources(TestEditableCourseResource_SRCS ../../data/languages.qrc) qt5_add_resources(TestEditableCourseResource_SRCS ../testdata/testdata.qrc) add_executable(test_editablecourseresource ${TestEditableCourseResource_SRCS} ) target_link_libraries(test_editablecourseresource artikulatecore Qt5::Test ) add_test(NAME test_editablecourseresource COMMAND test_editablecourseresource) ecm_mark_as_test(test_editablecourseresource) # test course model class set(TestCourseModel_SRCS coursemodel/test_coursemodel.cpp ../mocks/resourcerepositorystub.cpp ../mocks/coursestub.cpp + ../mocks/languagestub.cpp ) qt5_add_resources(TestCourseModel_SRCS ../../data/languages.qrc) qt5_add_resources(TestCourseModel_SRCS ../testdata/testdata.qrc) add_executable(test_coursemodel ${TestCourseModel_SRCS} ) target_link_libraries(test_coursemodel artikulatecore Qt5::Test ) add_test(NAME test_coursemodel COMMAND test_coursemodel) ecm_mark_as_test(test_coursemodel) diff --git a/autotests/unittests/coursemodel/test_coursemodel.cpp b/autotests/unittests/coursemodel/test_coursemodel.cpp index e268a23..b9756d6 100644 --- a/autotests/unittests/coursemodel/test_coursemodel.cpp +++ b/autotests/unittests/coursemodel/test_coursemodel.cpp @@ -1,130 +1,128 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_coursemodel.h" #include "src/core/icourse.h" #include "src/core/language.h" #include "src/models/coursemodel.h" #include "../mocks/resourcerepositorystub.h" #include "../mocks/coursestub.h" +#include "../mocks/languagestub.h" #include #include void TestCourseModel::init() { // TODO initialization of test case } void TestCourseModel::cleanup() { // TODO cleanup after test run } void TestCourseModel::testInit() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); std::vector> languages; languages.push_back(language); auto course = CourseStub::create(language, QVector>({})); ResourceRepositoryStub repository(languages, {course}); // test initialization CourseModel model(&repository); QVERIFY(model.resourceRepository() == &repository); QCOMPARE(model.rowCount(), 1); QCOMPARE(model.course(0).value(), course.get()); } void TestCourseModel::testAddRemoveOperations() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); std::vector> languages; languages.push_back(language); ResourceRepositoryStub repository(languages, {}); // test initialization CourseModel model(&repository); QVERIFY(model.resourceRepository() == &repository); QCOMPARE(model.rowCount(), 0); auto course = CourseStub::create(language, QVector>({})); { // add course QSignalSpy spyAboutToBeAdded(&repository, SIGNAL(courseAboutToBeAdded(std::shared_ptr,int))); QSignalSpy spyAdded(&repository, SIGNAL(courseAdded())); QCOMPARE(spyAboutToBeAdded.count(), 0); QCOMPARE(spyAdded.count(), 0); repository.appendCourse(course); QCOMPARE(model.rowCount(), 1); QCOMPARE(model.course(0).value(), course.get()); QCOMPARE(spyAboutToBeAdded.count(), 1); QCOMPARE(spyAdded.count(), 1); } { // remove course QSignalSpy spyAboutToBeRemoved(&repository, SIGNAL(courseAboutToBeRemoved(int))); QSignalSpy spyRemoved(&repository, SIGNAL(courseRemoved())); QCOMPARE(spyAboutToBeRemoved.count(), 0); QCOMPARE(spyRemoved.count(), 0); repository.removeCourse(course); QCOMPARE(model.rowCount(), 0); QCOMPARE(spyAboutToBeRemoved.count(), 1); QCOMPARE(spyRemoved.count(), 1); } } void TestCourseModel::testDataChangedSignals() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); std::vector> languages; languages.push_back(language); auto course = CourseStub::create(language, QVector>({})); ResourceRepositoryStub repository(languages, {course}); // test initialization CourseModel model(&repository); QVERIFY(model.resourceRepository() == &repository); QCOMPARE(model.rowCount(), 1); QCOMPARE(model.course(0).value(), course.get()); { // test adding of connections QSignalSpy spyUpdate(&model, SIGNAL(dataChanged(const QModelIndex &,const QModelIndex &, const QVector))); QCOMPARE(spyUpdate.count(), 0); std::static_pointer_cast(course)->setTitle("TitleSwitched"); QCOMPARE(spyUpdate.count(), 1); } { // test removal of connections QSignalSpy spyUpdate(&model, SIGNAL(dataChanged(const QModelIndex &,const QModelIndex &, const QVector))); QCOMPARE(spyUpdate.count(), 0); repository.removeCourse(course); std::static_pointer_cast(course)->setTitle("TitleSwitchedAgain"); -// QCOMPARE(spyUpdate.count(), 0); + QCOMPARE(spyUpdate.count(), 0); } } QTEST_GUILESS_MAIN(TestCourseModel) diff --git a/autotests/unittests/courseresource/test_courseresource.cpp b/autotests/unittests/courseresource/test_courseresource.cpp index 3c8cad9..d8bd0e9 100644 --- a/autotests/unittests/courseresource/test_courseresource.cpp +++ b/autotests/unittests/courseresource/test_courseresource.cpp @@ -1,194 +1,191 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_courseresource.h" #include "resourcerepositorystub.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" #include "core/phonemegroup.h" #include "core/resources/courseresource.h" -#include +#include "../mocks/languagestub.h" #include #include #include #include #include #include #include #include #include TestCourseResource::TestCourseResource() { } void TestCourseResource::init() { } void TestCourseResource::cleanup() { } void TestCourseResource::loadCourseResource() { - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); auto group = std::static_pointer_cast(language)->addPhonemeGroup("id", "title"); group->addPhoneme("g", "G"); group->addPhoneme("u", "U"); std::vector> languages; languages.push_back(language); ResourceRepositoryStub repository(languages); const QString courseDirectory = "data/courses/de/"; const QString courseFile = courseDirectory + "de.xml"; auto course = CourseResource::create(QUrl::fromLocalFile(courseFile), &repository); QCOMPARE(course->file().toLocalFile(), courseFile); QCOMPARE(course->id(), "de"); QCOMPARE(course->foreignId(), "artikulate-basic"); QCOMPARE(course->title(), "Artikulate Deutsch"); QCOMPARE(course->description(), "Ein Kurs in (hoch-)deutscher Aussprache."); QVERIFY(course->language() != nullptr); QCOMPARE(course->language()->id(), "de"); QCOMPARE(course->units().count(), 1); QCOMPARE(course->units().first()->course(), course.get()); const auto unit = course->units().first(); QVERIFY(unit != nullptr); QCOMPARE(unit->id(), "1"); QCOMPARE(unit->title(), QStringLiteral("Auf der Straße")); QCOMPARE(unit->foreignId(), "{dd60f04a-eb37-44b7-9787-67aaf7d3578d}"); QCOMPARE(unit->phraseList().count(), 3); // note: this test takes the silent assumption that phrases are added to the list in same // order as they are defined in the file. This assumption should be made explicit or dropped const auto firstPhrase = unit->phraseList().first(); QVERIFY(firstPhrase != nullptr); QCOMPARE(firstPhrase->id(), "1"); QCOMPARE(firstPhrase->foreignId(), "{3a4c1926-60d7-44c6-80d1-03165a641c75}"); QCOMPARE(firstPhrase->text(), "Guten Tag."); QCOMPARE(firstPhrase->soundFileUrl(), courseDirectory + "de_01.ogg"); QCOMPARE(firstPhrase->type(), Phrase::Type::Sentence); QCOMPARE(firstPhrase->phonemes().count(), 2); } void TestCourseResource::unitAddAndRemoveHandling() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); const QString courseDirectory = "data/courses/de/"; const QString courseFile = courseDirectory + "de.xml"; auto course = CourseResource::create(QUrl::fromLocalFile(courseFile), &repository); // begin of test std::unique_ptr unit(new Unit); unit->setId("testunit"); const int initialUnitNumber = course->units().count(); QCOMPARE(initialUnitNumber, 1); QSignalSpy spyAboutToBeAdded(course.get(), SIGNAL(unitAboutToBeAdded(std::shared_ptr, int))); QSignalSpy spyAdded(course.get(), SIGNAL(unitAdded())); QCOMPARE(spyAboutToBeAdded.count(), 0); QCOMPARE(spyAdded.count(), 0); auto sharedUnit = course->addUnit(std::move(unit)); QCOMPARE(course->units().count(), initialUnitNumber + 1); QCOMPARE(spyAboutToBeAdded.count(), 1); QCOMPARE(spyAdded.count(), 1); QCOMPARE(sharedUnit->course(), course.get()); } void TestCourseResource::coursePropertyChanges() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); const QString courseDirectory = "data/courses/de/"; const QString courseFile = courseDirectory + "de.xml"; auto course = CourseResource::create(QUrl::fromLocalFile(courseFile), &repository); // id { const QString value = "newId"; QSignalSpy spy(course.get(), SIGNAL(idChanged())); QCOMPARE(spy.count(), 0); course->setId(value); QCOMPARE(course->id(), value); QCOMPARE(spy.count(), 1); } // foreign id { const QString value = "newForeignId"; QSignalSpy spy(course.get(), SIGNAL(foreignIdChanged())); QCOMPARE(spy.count(), 0); course->setForeignId(value); QCOMPARE(course->foreignId(), value); QCOMPARE(spy.count(), 1); } // title { const QString value = "newTitle"; QSignalSpy spy(course.get(), SIGNAL(titleChanged())); QCOMPARE(spy.count(), 0); course->setTitle(value); QCOMPARE(course->title(), value); QCOMPARE(spy.count(), 1); } // title { const QString value = "newI18nTitle"; QSignalSpy spy(course.get(), SIGNAL(i18nTitleChanged())); QCOMPARE(spy.count(), 0); course->setI18nTitle(value); QCOMPARE(course->i18nTitle(), value); QCOMPARE(spy.count(), 1); } // description { const QString value = "newDescription"; QSignalSpy spy(course.get(), SIGNAL(descriptionChanged())); QCOMPARE(spy.count(), 0); course->setDescription(value); QCOMPARE(course->description(), value); QCOMPARE(spy.count(), 1); } // language { std::shared_ptr testLanguage; QSignalSpy spy(course.get(), SIGNAL(languageChanged())); QCOMPARE(spy.count(), 0); course->setLanguage(testLanguage); QCOMPARE(course->language(), testLanguage); QCOMPARE(spy.count(), 1); } } QTEST_GUILESS_MAIN(TestCourseResource) diff --git a/autotests/unittests/editablecourseresource/test_editablecourseresource.cpp b/autotests/unittests/editablecourseresource/test_editablecourseresource.cpp index 68e5b71..64e8049 100644 --- a/autotests/unittests/editablecourseresource/test_editablecourseresource.cpp +++ b/autotests/unittests/editablecourseresource/test_editablecourseresource.cpp @@ -1,229 +1,226 @@ /* * Copyright 2013-2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_editablecourseresource.h" #include "resourcerepositorystub.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" #include "core/resources/courseparser.h" #include "core/resources/editablecourseresource.h" +#include "../mocks/languagestub.h" #include #include #include #include #include #include #include #include #include #include TestEditableCourseResource::TestEditableCourseResource() { } void TestEditableCourseResource::init() { } void TestEditableCourseResource::cleanup() { } void TestEditableCourseResource::loadCourseResource() { - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); auto course = EditableCourseResource::create(QUrl::fromLocalFile(":/courses/de.xml"), &repository); QCOMPARE(course->file().toLocalFile(), ":/courses/de.xml"); QCOMPARE(course->id(), "de"); QCOMPARE(course->foreignId(), "artikulate-basic"); QCOMPARE(course->title(), "Artikulate Deutsch"); QCOMPARE(course->description(), "Ein Kurs in (hoch-)deutscher Aussprache."); QVERIFY(course->language() != nullptr); QCOMPARE(course->language()->id(), "de"); QCOMPARE(course->units().count(), 1); QCOMPARE(course->units().first()->course(), course.get()); const auto unit = course->units().first(); QVERIFY(unit != nullptr); QCOMPARE(unit->id(), "1"); QCOMPARE(unit->title(), QStringLiteral("Auf der Straße")); QCOMPARE(unit->foreignId(), "{dd60f04a-eb37-44b7-9787-67aaf7d3578d}"); QCOMPARE(unit->phraseList().count(), 3); // note: this test takes the silent assumption that phrases are added to the list in same // order as they are defined in the file. This assumption should be made explicit or dropped const auto firstPhrase = unit->phraseList().first(); QVERIFY(firstPhrase != nullptr); QCOMPARE(firstPhrase->id(), "1"); QCOMPARE(firstPhrase->foreignId(), "{3a4c1926-60d7-44c6-80d1-03165a641c75}"); QCOMPARE(firstPhrase->text(), "Guten Tag."); QCOMPARE(firstPhrase->soundFileUrl(), ":/courses/de_01.ogg"); QCOMPARE(firstPhrase->type(), Phrase::Type::Sentence); QVERIFY(firstPhrase->phonemes().isEmpty()); } void TestEditableCourseResource::unitAddAndRemoveHandling() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); auto course = EditableCourseResource::create(QUrl::fromLocalFile(":/courses/de.xml"), &repository); // begin of test std::unique_ptr unit(new Unit); unit->setId("testunit"); const int initialUnitNumber = course->units().count(); QCOMPARE(initialUnitNumber, 1); QSignalSpy spyAboutToBeAdded(course.get(), SIGNAL(unitAboutToBeAdded(std::shared_ptr, int))); QSignalSpy spyAdded(course.get(), SIGNAL(unitAdded())); QCOMPARE(spyAboutToBeAdded.count(), 0); QCOMPARE(spyAdded.count(), 0); auto sharedUnit = course->addUnit(std::move(unit)); QCOMPARE(course->units().count(), initialUnitNumber + 1); QCOMPARE(spyAboutToBeAdded.count(), 1); QCOMPARE(spyAdded.count(), 1); QCOMPARE(sharedUnit->course(), course.get()); } void TestEditableCourseResource::coursePropertyChanges() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); auto course = CourseResource::create(QUrl::fromLocalFile(":/courses/de.xml"), &repository); // id { const QString value = "newId"; QSignalSpy spy(course.get(), SIGNAL(idChanged())); QCOMPARE(spy.count(), 0); course->setId(value); QCOMPARE(course->id(), value); QCOMPARE(spy.count(), 1); } // foreign id { const QString value = "newForeignId"; QSignalSpy spy(course.get(), SIGNAL(foreignIdChanged())); QCOMPARE(spy.count(), 0); course->setForeignId(value); QCOMPARE(course->foreignId(), value); QCOMPARE(spy.count(), 1); } // title { const QString value = "newTitle"; QSignalSpy spy(course.get(), SIGNAL(titleChanged())); QCOMPARE(spy.count(), 0); course->setTitle(value); QCOMPARE(course->title(), value); QCOMPARE(spy.count(), 1); } // title { const QString value = "newI18nTitle"; QSignalSpy spy(course.get(), SIGNAL(i18nTitleChanged())); QCOMPARE(spy.count(), 0); course->setI18nTitle(value); QCOMPARE(course->i18nTitle(), value); QCOMPARE(spy.count(), 1); } // description { const QString value = "newDescription"; QSignalSpy spy(course.get(), SIGNAL(descriptionChanged())); QCOMPARE(spy.count(), 0); course->setDescription(value); QCOMPARE(course->description(), value); QCOMPARE(spy.count(), 1); } // language { std::shared_ptr testLanguage; QSignalSpy spy(course.get(), SIGNAL(languageChanged())); QCOMPARE(spy.count(), 0); course->setLanguage(testLanguage); QCOMPARE(course->language(), testLanguage); QCOMPARE(spy.count(), 1); } } void TestEditableCourseResource::fileLoadSaveCompleteness() { // boilerplate - std::shared_ptr language(new Language); - std::static_pointer_cast(language)->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); auto course = EditableCourseResource::create(QUrl::fromLocalFile(":/courses/de.xml"), &repository); QTemporaryFile outputFile; outputFile.open(); course->exportToFile(QUrl::fromLocalFile(outputFile.fileName())); // note: this only works, since the resource manager not checks uniqueness of course ids! auto loadedCourse = EditableCourseResource::create(QUrl::fromLocalFile(outputFile.fileName()), &repository); // test that we actually call the different files QVERIFY(course->file().toLocalFile() != loadedCourse->file().toLocalFile()); QVERIFY(course->id() == loadedCourse->id()); QVERIFY(course->foreignId() == loadedCourse->foreignId()); QVERIFY(course->title() == loadedCourse->title()); QVERIFY(course->description() == loadedCourse->description()); QVERIFY(course->language()->id() == loadedCourse->language()->id()); QVERIFY(course->units().count() == loadedCourse->units().count()); auto testUnit = course->units().constFirst(); auto compareUnit = loadedCourse->units().constFirst(); QVERIFY(testUnit->id() == compareUnit->id()); QVERIFY(testUnit->foreignId() == compareUnit->foreignId()); QVERIFY(testUnit->title() == compareUnit->title()); QVERIFY(testUnit->phraseList().count() == compareUnit->phraseList().count()); Phrase *testPhrase = testUnit->phraseList().constFirst(); Phrase *comparePhrase = new Phrase(this); // note that this actually means that we DO NOT respect phrase orders by list order for (Phrase *phrase : compareUnit->phraseList()) { if (testPhrase->id() == phrase->id()) { comparePhrase = phrase; break; } } QVERIFY(testPhrase->id() == comparePhrase->id()); QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId()); QVERIFY(testPhrase->text() == comparePhrase->text()); QVERIFY(testPhrase->type() == comparePhrase->type()); QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName()); QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count()); } QTEST_GUILESS_MAIN(TestEditableCourseResource) diff --git a/autotests/unittests/editorsession/test_editorsession.cpp b/autotests/unittests/editorsession/test_editorsession.cpp index 3f70473..c9f5c99 100644 --- a/autotests/unittests/editorsession/test_editorsession.cpp +++ b/autotests/unittests/editorsession/test_editorsession.cpp @@ -1,347 +1,341 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_editorsession.h" #include "editablerepositorystub.h" #include "src/core/editorsession.h" #include "src/core/icourse.h" #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 "../mocks/languagestub.h" #include #include class EditableCourseStub : public IEditableCourse { public: - EditableCourseStub(std::shared_ptr language, QVector> units) + EditableCourseStub(std::shared_ptr language, QVector> units) : IEditableCourse() , m_language(language) , m_units(units) { for (auto unit : m_units) { unit->setCourse(this); } } ~EditableCourseStub() override; void setSelf(std::shared_ptr self) override { m_self = self; } QString id() const override { return m_id; } void setId(QString id) override { m_id = id; emit idChanged(); } QString foreignId() const override { return m_foreignId; } void setForeignId(QString id) override { m_foreignId = id; } QString title() const override { return m_title; } void setTitle(QString title) override { m_title = title; emit titleChanged(); } QString i18nTitle() const override { return m_i18nTitle; } void setI18nTitle(QString title) override { m_i18nTitle = title; } QString description() const override { return m_description; } void setDescription(QString description) override { m_description = description; emit descriptionChanged(); } std::shared_ptr language() const override { return m_language; } void setLanguage(std::shared_ptr language) override { m_language = language; emit languageChanged(); } QVector> units() override { return m_units; } std::shared_ptr addUnit(std::unique_ptr unit) override { m_units.append(std::move(unit)); auto unitPtr = m_units.last(); unitPtr->setCourse(this); return unitPtr; } QUrl file() const override { return QUrl(); } bool isModified() const override { return false; } bool exportToFile(const QUrl &) const override { // do nothing return false; } private: std::weak_ptr m_self; QString m_id{ "courseid" }; QString m_foreignId{ "foreigncourseid" }; QString m_title{ "title" }; QString m_i18nTitle{ "i18n title" }; QString m_description{ "description of the course" }; std::shared_ptr m_language; QVector> m_units; }; // define one virtual method out of line to pin CourseStub to this translation unit EditableCourseStub::~EditableCourseStub() = default; void TestEditorSession::init() { // TODO initialization of test case } void TestEditorSession::cleanup() { // TODO cleanup after test run } void TestEditorSession::createEditorSession() { - auto languageGerman = std::make_shared(); - languageGerman->setId("de"); - auto languageEnglish = std::make_shared(); - languageEnglish->setId("en"); + auto languageGerman = std::make_shared("de"); + auto languageEnglish = std::make_shared("en"); std::shared_ptr course(new EditableCourseStub(languageGerman, QVector>())); course->setLanguage(languageGerman); auto skeleton = SkeletonResource::create(QUrl(), nullptr); EditableRepositoryStub repository{ {languageGerman, languageEnglish}, // languages {skeleton}, // skeletons {course} // courses }; EditorSession session; session.setRepository(&repository); QVERIFY(session.course() == nullptr); QVERIFY(session.language() == nullptr); QVERIFY(session.skeleton() == nullptr); } void TestEditorSession::nonSkeletonSwitchingBehavior() { - auto languageGerman = std::make_shared(); - languageGerman->setId("de"); - auto languageEnglish = std::make_shared(); - languageEnglish->setId("en"); + auto languageGerman = std::make_shared("de"); + auto languageEnglish = std::make_shared("en"); std::shared_ptr courseGerman(new EditableCourseStub(languageGerman, QVector>())); courseGerman->setId("course-german"); std::shared_ptr courseEnglish(new EditableCourseStub(languageEnglish, QVector>())); 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.get()); 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.get()); QVERIFY(session.course() != nullptr); QCOMPARE(session.course()->id(), courseEnglish->id()); QVERIFY(session.language() != nullptr); QCOMPARE(session.language()->id(), languageEnglish->id()); } void TestEditorSession::skeletonSwitchingBehavior() { - auto languageGerman = std::make_shared(); - languageGerman->setId("de"); - auto languageEnglish = std::make_shared(); - languageEnglish->setId("en"); + auto languageGerman = std::make_shared("de"); + auto languageEnglish = std::make_shared("en"); std::shared_ptr courseGermanA(new EditableCourseStub(languageGerman, QVector>())); courseGermanA->setId("course-german"); courseGermanA->setForeignId("testskeletonA"); std::shared_ptr courseGermanB(new EditableCourseStub(languageGerman, QVector>())); courseGermanB->setId("course-german"); courseGermanB->setForeignId("testskeletonB"); std::shared_ptr courseEnglishA(new EditableCourseStub(languageEnglish, QVector>())); courseEnglishA->setId("course-english"); courseEnglishA->setForeignId("testskeletonA"); auto skeletonA = SkeletonResource::create(QUrl(), nullptr); skeletonA->setId("testskeletonA"); auto skeletonB = SkeletonResource::create(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.get()); 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.get()); Q_ASSERT(session.course() != nullptr); QCOMPARE(session.course()->id(), courseEnglishA->id()); session.setCourse(courseGermanB.get()); QVERIFY(session.skeleton() != nullptr); QCOMPARE(session.skeleton()->id(), skeletonB->id()); QVERIFY(session.course() != nullptr); QCOMPARE(session.course()->id(), courseGermanB->id()); QVERIFY(session.language() != nullptr); QCOMPARE(session.language()->id(), languageGerman->id()); } void TestEditorSession::iterateCourse() { // language - auto language = std::make_shared(); - language->setId("de"); + auto language = std::make_shared("de"); // course std::shared_ptr unitA(new Unit); std::shared_ptr unitB(new Unit); Phrase *phraseA1 = new Phrase; Phrase *phraseA2 = new Phrase; Phrase *phraseB1 = new Phrase; Phrase *phraseB2 = new Phrase; // note: phrases without soundfiles are skipped in session generation phraseA1->setId("A1"); phraseA2->setId("A2"); phraseB1->setId("B1"); phraseB2->setId("B2"); phraseA1->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); phraseA2->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); phraseB1->setSound(QUrl::fromLocalFile("/tmp/b1.ogg")); phraseB2->setSound(QUrl::fromLocalFile("/tmp/b2.ogg")); unitA->addPhrase(phraseA1); unitA->addPhrase(phraseA2); unitB->addPhrase(phraseB1); unitB->addPhrase(phraseB2); auto course = std::make_shared(language, QVector>({unitA, unitB})); EditableRepositoryStub repository{ {language}, // languages {}, // skeletons {course} // courses }; EditorSession session; session.setRepository(&repository); session.setCourse(course.get()); // session assumed to initialize with first units's first phrase QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase(), phraseA1); QVERIFY(course.get() == session.course()); // test direct unit setters session.setUnit(unitA.get()); QCOMPARE(session.activeUnit(), unitA.get()); session.setUnit(unitB.get()); QCOMPARE(session.activeUnit(), unitB.get()); // test direct phrase setters session.setPhrase(phraseA1); QCOMPARE(session.activePhrase(), phraseA1); QCOMPARE(session.activeUnit(), unitA.get()); session.setPhrase(phraseB1); QCOMPARE(session.activePhrase(), phraseB1); QCOMPARE(session.activeUnit(), unitB.get()); // test phrase forward iterators session.setPhrase(phraseA1); QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase()->id(), phraseA1->id()); QVERIFY(session.hasNextPhrase()); session.switchToNextPhrase(); QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase()->id(), phraseA2->id()); session.switchToNextPhrase(); QCOMPARE(session.activePhrase(), phraseB1); session.switchToNextPhrase(); QCOMPARE(session.activePhrase(), phraseB2); QVERIFY(!session.hasNextPhrase()); // at the end, do not iterate further session.switchToNextPhrase(); QCOMPARE(session.activePhrase(), phraseB2); // test phrase backward iterators QVERIFY(session.hasPreviousPhrase()); session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase(), phraseB1); session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->id(), phraseA2->id()); session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->id(), phraseA1->id()); QVERIFY(!session.hasPreviousPhrase()); // at the end, do not iterate further session.switchToPreviousPhrase(); QCOMPARE(session.activePhrase()->id(), phraseA1->id()); } QTEST_GUILESS_MAIN(TestEditorSession) diff --git a/autotests/unittests/skeletonresource/test_skeletonresource.cpp b/autotests/unittests/skeletonresource/test_skeletonresource.cpp index 16f5a29..a23ebf3 100644 --- a/autotests/unittests/skeletonresource/test_skeletonresource.cpp +++ b/autotests/unittests/skeletonresource/test_skeletonresource.cpp @@ -1,203 +1,200 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_skeletonresource.h" #include "resourcerepositorystub.h" #include "core/language.h" #include "core/unit.h" #include "core/phrase.h" #include "core/resources/skeletonresource.h" +#include "../mocks/languagestub.h" #include #include #include #include #include #include #include #include #include TestSkeletonResource::TestSkeletonResource() { } void TestSkeletonResource::init() { } void TestSkeletonResource::cleanup() { } void TestSkeletonResource::loadSkeletonResource() { - std::shared_ptr language(new Language); - language->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); const QString courseDirectory = "data/contributorrepository/skeletons/"; const QString courseFile = courseDirectory + "skeleton.xml"; auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository); QCOMPARE(skeleton->file().toLocalFile(), courseFile); QCOMPARE(skeleton->id(), "skeleton-testdata"); QCOMPARE(skeleton->foreignId(), "skeleton-testdata"); // always same as ID QCOMPARE(skeleton->title(), "Artikulate Test Course Title"); QCOMPARE(skeleton->description(), "Artikulate Test Course Description"); QVERIFY(skeleton->language() == nullptr); // a skeleton must not have a language QCOMPARE(skeleton->units().count(), 2); const auto unit = skeleton->units().first(); QVERIFY(unit != nullptr); QCOMPARE(unit->id(), "{11111111-b885-4833-97ff-27cb1ca2f543}"); QCOMPARE(unit->title(), QStringLiteral("Numbers")); QCOMPARE(unit->phraseList().count(), 2); // note: this test takes the silent assumption that phrases are added to the list in same // order as they are defined in the file. This assumption should be made explicit or dropped const auto firstPhrase = unit->phraseList().first(); QVERIFY(firstPhrase != nullptr); QCOMPARE(firstPhrase->id(), "{22222222-9234-4da5-a6fe-dbd5104f57d5}"); QCOMPARE(firstPhrase->text(), "0"); QCOMPARE(firstPhrase->type(), Phrase::Type::Word); const auto secondPhrase = unit->phraseList().at(1); QVERIFY(secondPhrase != nullptr); QCOMPARE(secondPhrase->id(), "{333333333-b4a9-4264-9a26-75a55aa5d302}"); QCOMPARE(secondPhrase->text(), "1"); QCOMPARE(secondPhrase->type(), Phrase::Type::Word); } void TestSkeletonResource::unitAddAndRemoveHandling() { // boilerplate - std::shared_ptr language(new Language); - language->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); const QString courseDirectory = "data/contributorrepository/skeletons/"; const QString courseFile = courseDirectory + "skeleton.xml"; auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository); // begin of test std::unique_ptr unit(new Unit); unit->setId("testunit"); const int initialUnitNumber = skeleton->units().count(); QCOMPARE(initialUnitNumber, 2); QSignalSpy spyAboutToBeAdded(skeleton.get(), SIGNAL(unitAboutToBeAdded(std::shared_ptr, int))); QSignalSpy spyAdded(skeleton.get(), SIGNAL(unitAdded())); QCOMPARE(spyAboutToBeAdded.count(), 0); QCOMPARE(spyAdded.count(), 0); skeleton->addUnit(std::move(unit)); QCOMPARE(skeleton->units().count(), initialUnitNumber + 1); QCOMPARE(spyAboutToBeAdded.count(), 1); QCOMPARE(spyAdded.count(), 1); } void TestSkeletonResource::coursePropertyChanges() { // boilerplate - std::shared_ptr language(new Language); - language->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); const QString courseDirectory = "data/contributorrepository/skeletons/"; const QString courseFile = courseDirectory + "skeleton.xml"; auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository); // id { const QString value = "newId"; QSignalSpy spy(skeleton.get(), SIGNAL(idChanged())); QCOMPARE(spy.count(), 0); skeleton->setId(value); QCOMPARE(skeleton->id(), value); QCOMPARE(spy.count(), 1); } // title { const QString value = "newTitle"; QSignalSpy spy(skeleton.get(), SIGNAL(titleChanged())); QCOMPARE(spy.count(), 0); skeleton->setTitle(value); QCOMPARE(skeleton->title(), value); QCOMPARE(spy.count(), 1); } // description { const QString value = "newDescription"; QSignalSpy spy(skeleton.get(), SIGNAL(descriptionChanged())); QCOMPARE(spy.count(), 0); skeleton->setDescription(value); QCOMPARE(skeleton->description(), value); QCOMPARE(spy.count(), 1); } } void TestSkeletonResource::fileLoadSaveCompleteness() { // boilerplate - std::shared_ptr language(new Language); - language->setId("de"); + std::shared_ptr language(new LanguageStub("de")); ResourceRepositoryStub repository({language}); const QString courseDirectory = "data/contributorrepository/skeletons/"; const QString courseFile = courseDirectory + "skeleton.xml"; auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository); QTemporaryFile outputFile; outputFile.open(); skeleton->exportToFile(QUrl::fromLocalFile(outputFile.fileName())); // note: this only works, since the resource manager not checks uniqueness of course ids! auto loadedSkeleton = SkeletonResource::create(QUrl::fromLocalFile(outputFile.fileName()), &repository); // test that we actually call the different files QVERIFY(skeleton->file().toLocalFile() != loadedSkeleton->file().toLocalFile()); QCOMPARE(loadedSkeleton->id(), skeleton->id()); QCOMPARE(loadedSkeleton->foreignId(), skeleton->foreignId()); QCOMPARE(loadedSkeleton->title(), skeleton->title()); QCOMPARE(loadedSkeleton->description(), skeleton->description()); QCOMPARE(loadedSkeleton->language(), skeleton->language()); QCOMPARE(loadedSkeleton->units().count(), skeleton->units().count()); auto testUnit = skeleton->units().constFirst(); auto compareUnit = loadedSkeleton->units().constFirst(); QCOMPARE(testUnit->id(), compareUnit->id()); QCOMPARE(testUnit->foreignId(), compareUnit->foreignId()); QCOMPARE(testUnit->title(), compareUnit->title()); QCOMPARE(testUnit->phraseList().count(), compareUnit->phraseList().count()); Phrase *testPhrase = testUnit->phraseList().constFirst(); Phrase *comparePhrase = new Phrase(this); // note that this actually means that we DO NOT respect phrase orders by list order for (Phrase *phrase : compareUnit->phraseList()) { if (testPhrase->id() == phrase->id()) { comparePhrase = phrase; break; } } QVERIFY(testPhrase->id() == comparePhrase->id()); QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId()); QVERIFY(testPhrase->text() == comparePhrase->text()); QVERIFY(testPhrase->type() == comparePhrase->type()); QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName()); QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count()); } QTEST_GUILESS_MAIN(TestSkeletonResource) diff --git a/autotests/unittests/trainingsession/test_trainingsession.cpp b/autotests/unittests/trainingsession/test_trainingsession.cpp index e02828e..7d65c80 100644 --- a/autotests/unittests/trainingsession/test_trainingsession.cpp +++ b/autotests/unittests/trainingsession/test_trainingsession.cpp @@ -1,206 +1,207 @@ /* * Copyright 2019 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_trainingsession.h" #include "src/core/trainingsession.h" #include "src/core/icourse.h" #include "src/core/language.h" #include "src/core/unit.h" #include "src/core/trainingaction.h" #include "../mocks/coursestub.h" +#include "../mocks/languagestub.h" #include "liblearnerprofile/src/profilemanager.h" #include #include // assumption: during a training session the units and phrases of a course do not change // any change of such a course shall result in a reload of a training session void TestTrainingSession::init() { // TODO initialization of test case } void TestTrainingSession::cleanup() { // TODO cleanup after test run } void TestTrainingSession::createTrainingSessionWithoutUnits() { - auto language = std::make_shared(); + auto language = std::make_shared("de"); CourseStub course(language, QVector>()); LearnerProfile::ProfileManager manager; TrainingSession session(&manager); session.setCourse(&course); QVERIFY(&course == session.course()); } void TestTrainingSession::createTrainingSessionWithEmptySounds() { - auto language = std::make_shared(); + auto language = std::make_shared("de"); std::shared_ptr unitA(new Unit); std::shared_ptr unitB(new Unit); Phrase *phraseA1 = new Phrase; Phrase *phraseA2 = new Phrase; Phrase *phraseB1 = new Phrase; Phrase *phraseB2 = new Phrase; // note: phrases without soundfiles are skipped in session generation phraseA1->setId("A1"); phraseA2->setId("A2"); phraseB1->setId("B1"); phraseB2->setId("B2"); phraseA1->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); unitA->addPhrase(phraseA1); unitA->addPhrase(phraseA2); unitB->addPhrase(phraseB1); unitB->addPhrase(phraseB2); CourseStub course(language, QVector>({unitA, unitB})); LearnerProfile::ProfileManager manager; TrainingSession session(&manager); session.setCourse(&course); // test number of actions auto actions = session.trainingActions(); QCOMPARE(actions.count(), 1); QCOMPARE(actions.at(0)->actions().count(), 1); } void TestTrainingSession::createTrainingSessionWithEmptyUnits() { - auto language = std::make_shared(); + auto language = std::make_shared("de"); std::shared_ptr unitA(new Unit); std::shared_ptr unitB(new Unit); CourseStub course(language, QVector>({unitA, unitB})); LearnerProfile::ProfileManager manager; TrainingSession session(&manager); session.setCourse(&course); QVERIFY(&course == session.course()); } void TestTrainingSession::createTrainingSessionWithUnitsAndPhrases() { - auto language = std::make_shared(); + auto language = std::make_shared("de"); std::shared_ptr unit(new Unit); Phrase *firstPhrase = new Phrase(); Phrase *secondPhrase = new Phrase(); unit->addPhrase(firstPhrase); unit->addPhrase(secondPhrase); CourseStub course(language, QVector>({unit})); LearnerProfile::ProfileManager manager; TrainingSession session(&manager); session.setCourse(&course); QVERIFY(&course == session.course()); } void TestTrainingSession::iterateCourse() { - auto language = std::make_shared(); + auto language = std::make_shared("de"); std::shared_ptr unitA(new Unit); std::shared_ptr unitB(new Unit); Phrase *phraseA1 = new Phrase; Phrase *phraseA2 = new Phrase; Phrase *phraseB1 = new Phrase; Phrase *phraseB2 = new Phrase; // note: phrases without soundfiles are skipped in session generation phraseA1->setId("A1"); phraseA2->setId("A2"); phraseB1->setId("B1"); phraseB2->setId("B2"); phraseA1->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); phraseA2->setSound(QUrl::fromLocalFile("/tmp/a1.ogg")); phraseB1->setSound(QUrl::fromLocalFile("/tmp/b1.ogg")); phraseB2->setSound(QUrl::fromLocalFile("/tmp/b2.ogg")); unitA->addPhrase(phraseA1); unitA->addPhrase(phraseA2); unitB->addPhrase(phraseB1); unitB->addPhrase(phraseB2); CourseStub course(language, QVector>({unitA, unitB})); LearnerProfile::ProfileManager manager; TrainingSession session(&manager); session.setCourse(&course); // session assumed to initialize with first units's first phrase QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase(), phraseA1); QVERIFY(&course == session.course()); // test direct unit setters session.setUnit(unitA.get()); QCOMPARE(session.activeUnit(), unitA.get()); session.setUnit(unitB.get()); QCOMPARE(session.activeUnit(), unitB.get()); // test direct phrase setters session.setPhrase(phraseA1); QCOMPARE(session.activePhrase(), phraseA1); QCOMPARE(session.activeUnit(), unitA.get()); session.setPhrase(phraseB1); QCOMPARE(session.activePhrase(), phraseB1); QCOMPARE(session.activeUnit(), unitB.get()); // test number of actions auto actions = session.trainingActions(); QCOMPARE(actions.count(), 2); QCOMPARE(actions.at(0)->actions().count(), 2); QCOMPARE(actions.at(1)->actions().count(), 2); // test phrase iterators: accept iterator session.setPhrase(phraseA1); QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase(), phraseA1); QVERIFY(session.hasNext()); session.accept(); QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase(), phraseA2); session.accept(); QCOMPARE(session.activePhrase(), phraseB1); session.accept(); QCOMPARE(session.activePhrase(), phraseB2); QVERIFY(!session.hasNext()); // test phrase iterators: skip iterator session.setPhrase(phraseA1); QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase(), phraseA1); QVERIFY(!session.hasPrevious()); QVERIFY(session.hasNext()); session.skip(); QCOMPARE(session.activeUnit(), unitA.get()); QCOMPARE(session.activePhrase(), phraseA2); session.skip(); QCOMPARE(session.activePhrase(), phraseB1); session.skip(); QCOMPARE(session.activePhrase(), phraseB2); QVERIFY(session.hasPrevious()); QVERIFY(!session.hasNext()); // test completed signal QSignalSpy spy(&session, SIGNAL(completed())); session.setPhrase(phraseB1); session.accept(); QCOMPARE(spy.count(), 0); session.accept(); QCOMPARE(spy.count(), 1); } QTEST_GUILESS_MAIN(TestTrainingSession) diff --git a/src/core/language.h b/src/core/language.h index f1c585c..433cd46 100644 --- a/src/core/language.h +++ b/src/core/language.h @@ -1,78 +1,78 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LANGUAGE_H #define LANGUAGE_H #include "artikulatecore_export.h" #include "ilanguage.h" #include #include #include #include class QString; class Phoneme; class PhonemeGroup; class ARTIKULATECORE_EXPORT Language : public ILanguage { Q_OBJECT Q_INTERFACES(ILanguage) Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString i18nTitle READ i18nTitle WRITE seti18nTitle NOTIFY i18nTitleChanged) public: static std::shared_ptr create(QUrl file); ~Language() override; - QString id() const; + QString id() const override; void setId(const QString &id); - QString title() const; + QString title() const override; void seti18nTitle(const QString &title); - QString i18nTitle() const; + QString i18nTitle() const override; void setTitle(const QString &title); QUrl file() const; void setFile(const QUrl &file); - QVector> phonemes() const; - QVector> phonemeGroups() const; + QVector> phonemes() const override; + QVector> phonemeGroups() const override; std::shared_ptr addPhonemeGroup(const QString &identifier, const QString &title); Q_SIGNALS: void idChanged(); void titleChanged(); void i18nTitleChanged(); void phonemesChanged(); void phonemeGroupsChanged(); -public: // TODO +protected: explicit Language(); private: QString m_id; QString m_title; QString m_i18nTitle; QUrl m_file; QVector> m_phonemeGroups; }; #endif // LANGUAGE_H