1 /*
2     SPDX-FileCopyrightText: 2019 Andreas Cord-Landwehr <cordlandwehr@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6 
7 #include "test_skeletonresource.h"
8 #include "../mocks/languagestub.h"
9 #include "core/language.h"
10 #include "core/phrase.h"
11 #include "core/resources/skeletonresource.h"
12 #include "core/unit.h"
13 #include "resourcerepositorystub.h"
14 
15 #include <QDebug>
16 #include <QFile>
17 #include <QIODevice>
18 #include <QSignalSpy>
19 #include <QTemporaryFile>
20 #include <QTest>
21 #include <QXmlSchema>
22 #include <QXmlSchemaValidator>
23 
TestSkeletonResource()24 TestSkeletonResource::TestSkeletonResource()
25 {
26 }
27 
init()28 void TestSkeletonResource::init()
29 {
30 }
31 
cleanup()32 void TestSkeletonResource::cleanup()
33 {
34 }
35 
loadSkeletonResource()36 void TestSkeletonResource::loadSkeletonResource()
37 {
38     std::shared_ptr<ILanguage> language(new LanguageStub("de"));
39     ResourceRepositoryStub repository({language});
40     const QString courseDirectory = "data/contributorrepository/skeletons/";
41     const QString courseFile = courseDirectory + "skeleton.xml";
42 
43     auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository);
44     QCOMPARE(skeleton->file().toLocalFile(), courseFile);
45     QCOMPARE(skeleton->id(), "skeleton-testdata");
46     QCOMPARE(skeleton->foreignId(), "skeleton-testdata"); // always same as ID
47     QCOMPARE(skeleton->title(), "Artikulate Test Course Title");
48     QCOMPARE(skeleton->description(), "Artikulate Test Course Description");
49     QVERIFY(skeleton->language() == nullptr); // a skeleton must not have a language
50 
51     QCOMPARE(skeleton->units().count(), 2);
52 
53     const auto unit = skeleton->units().first();
54     QVERIFY(unit != nullptr);
55     QCOMPARE(unit->id(), "{11111111-b885-4833-97ff-27cb1ca2f543}");
56     QCOMPARE(unit->title(), QStringLiteral("Numbers"));
57     QCOMPARE(unit->phrases().count(), 2);
58     QVERIFY(unit->course() != nullptr);
59     QCOMPARE(unit->course().get(), skeleton.get());
60 
61     // note: this test takes the silent assumption that phrases are added to the list in same
62     //   order as they are defined in the file. This assumption should be made explicit or dropped
63     const auto firstPhrase = unit->phrases().first();
64     QVERIFY(firstPhrase != nullptr);
65     QCOMPARE(firstPhrase->id(), "{22222222-9234-4da5-a6fe-dbd5104f57d5}");
66     QCOMPARE(firstPhrase->text(), "0");
67     QCOMPARE(firstPhrase->type(), Phrase::Type::Word);
68     const auto secondPhrase = unit->phrases().at(1);
69     QVERIFY(secondPhrase != nullptr);
70     QCOMPARE(secondPhrase->id(), "{333333333-b4a9-4264-9a26-75a55aa5d302}");
71     QCOMPARE(secondPhrase->text(), "1");
72     QCOMPARE(secondPhrase->type(), Phrase::Type::Word);
73 }
74 
unitAddAndRemoveHandling()75 void TestSkeletonResource::unitAddAndRemoveHandling()
76 {
77     // boilerplate
78     std::shared_ptr<ILanguage> language(new LanguageStub("de"));
79     ResourceRepositoryStub repository({language});
80     const QString courseDirectory = "data/contributorrepository/skeletons/";
81     const QString courseFile = courseDirectory + "skeleton.xml";
82     auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository);
83 
84     // begin of test
85     auto unit = Unit::create();
86     unit->setId("testunit");
87     const int initialUnitNumber = skeleton->units().count();
88     QCOMPARE(initialUnitNumber, 2);
89     QSignalSpy spyAboutToBeAdded(skeleton.get(), SIGNAL(unitAboutToBeAdded(std::shared_ptr<Unit>, int)));
90     QSignalSpy spyAdded(skeleton.get(), SIGNAL(unitAdded()));
91     QCOMPARE(spyAboutToBeAdded.count(), 0);
92     QCOMPARE(spyAdded.count(), 0);
93     auto sharedUnit = skeleton->addUnit(std::move(unit));
94     QCOMPARE(skeleton->units().count(), initialUnitNumber + 1);
95     QCOMPARE(spyAboutToBeAdded.count(), 1);
96     QCOMPARE(spyAdded.count(), 1);
97     QCOMPARE(sharedUnit->course(), skeleton);
98 }
99 
coursePropertyChanges()100 void TestSkeletonResource::coursePropertyChanges()
101 {
102     // boilerplate
103     std::shared_ptr<ILanguage> language(new LanguageStub("de"));
104     ResourceRepositoryStub repository({language});
105     const QString courseDirectory = "data/contributorrepository/skeletons/";
106     const QString courseFile = courseDirectory + "skeleton.xml";
107     auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository);
108 
109     // id
110     {
111         const QString value = "newId";
112         QSignalSpy spy(skeleton.get(), SIGNAL(idChanged()));
113         QCOMPARE(spy.count(), 0);
114         skeleton->setId(value);
115         QCOMPARE(skeleton->id(), value);
116         QCOMPARE(spy.count(), 1);
117     }
118 
119     // title
120     {
121         const QString value = "newTitle";
122         QSignalSpy spy(skeleton.get(), SIGNAL(titleChanged()));
123         QCOMPARE(spy.count(), 0);
124         skeleton->setTitle(value);
125         QCOMPARE(skeleton->title(), value);
126         QCOMPARE(spy.count(), 1);
127     }
128 
129     // description
130     {
131         const QString value = "newDescription";
132         QSignalSpy spy(skeleton.get(), SIGNAL(descriptionChanged()));
133         QCOMPARE(spy.count(), 0);
134         skeleton->setDescription(value);
135         QCOMPARE(skeleton->description(), value);
136         QCOMPARE(spy.count(), 1);
137     }
138 }
139 
fileLoadSaveCompleteness()140 void TestSkeletonResource::fileLoadSaveCompleteness()
141 {
142     // boilerplate
143     std::shared_ptr<ILanguage> language(new LanguageStub("de"));
144     ResourceRepositoryStub repository({language});
145     const QString courseDirectory = "data/contributorrepository/skeletons/";
146     const QString courseFile = courseDirectory + "skeleton.xml";
147     auto skeleton = SkeletonResource::create(QUrl::fromLocalFile(courseFile), &repository);
148 
149     QTemporaryFile outputFile;
150     outputFile.open();
151     skeleton->exportToFile(QUrl::fromLocalFile(outputFile.fileName()));
152 
153     // note: this only works, since the resource manager not checks uniqueness of course ids!
154     auto loadedSkeleton = SkeletonResource::create(QUrl::fromLocalFile(outputFile.fileName()), &repository);
155 
156     // test that we actually call the different files
157     QVERIFY(skeleton->file().toLocalFile() != loadedSkeleton->file().toLocalFile());
158     QCOMPARE(loadedSkeleton->id(), skeleton->id());
159     QCOMPARE(loadedSkeleton->foreignId(), skeleton->foreignId());
160     QCOMPARE(loadedSkeleton->title(), skeleton->title());
161     QCOMPARE(loadedSkeleton->description(), skeleton->description());
162     QCOMPARE(loadedSkeleton->language(), skeleton->language());
163     QCOMPARE(loadedSkeleton->units().count(), skeleton->units().count());
164 
165     auto testUnit = skeleton->units().constFirst();
166     auto compareUnit = loadedSkeleton->units().constFirst();
167     QCOMPARE(testUnit->id(), compareUnit->id());
168     QCOMPARE(testUnit->foreignId(), compareUnit->foreignId());
169     QCOMPARE(testUnit->title(), compareUnit->title());
170     QCOMPARE(testUnit->phrases().count(), compareUnit->phrases().count());
171 
172     std::shared_ptr<IPhrase> testPhrase = testUnit->phrases().constFirst();
173     std::shared_ptr<IPhrase> comparePhrase = Phrase::create();
174     // note that this actually means that we DO NOT respect phrase orders by list order
175     for (const auto &phrase : compareUnit->phrases()) {
176         if (testPhrase->id() == phrase->id()) {
177             comparePhrase = phrase;
178             break;
179         }
180     }
181     QVERIFY(testPhrase->id() == comparePhrase->id());
182     QVERIFY(testPhrase->foreignId() == comparePhrase->foreignId());
183     QVERIFY(testPhrase->text() == comparePhrase->text());
184     QVERIFY(testPhrase->type() == comparePhrase->type());
185     QVERIFY(testPhrase->sound().fileName() == comparePhrase->sound().fileName());
186     QVERIFY(testPhrase->phonemes().count() == comparePhrase->phonemes().count());
187 }
188 
189 QTEST_GUILESS_MAIN(TestSkeletonResource)
190