1 /*
2     SPDX-FileCopyrightText: 2007 Bertjan Broeksema <b.broeksema@kdemail.net>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "plasmoidpackagetest.h"
8 #include "../src/kpackage/config-package.h"
9 #include "../src/kpackage/private/versionparser.cpp"
10 
11 #include <KJob>
12 #include <QDir>
13 #include <QFile>
14 #include <QSignalSpy>
15 #include <QStandardPaths>
16 #include <kzip.h>
17 
18 #include <QDebug>
19 
20 #include "packageloader.h"
21 #include "packagestructures/plasmoidstructure.h"
22 
initTestCase()23 void PlasmoidPackageTest::initTestCase()
24 {
25     QStandardPaths::setTestModeEnabled(true);
26 }
27 
init()28 void PlasmoidPackageTest::init()
29 {
30     qDebug() << "PlasmoidPackage::init()";
31     m_package = QStringLiteral("Package");
32     m_packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/packageRoot";
33     m_defaultPackage = KPackage::Package(new KPackage::PlasmoidPackage);
34     cleanup(); // to prevent previous runs from interfering with this one
35 }
36 
cleanup()37 void PlasmoidPackageTest::cleanup()
38 {
39     qDebug() << "cleaning up";
40     // Clean things up.
41     QDir(m_packageRoot).removeRecursively();
42 }
43 
createTestPackage(const QString & packageName,const QString & version)44 void PlasmoidPackageTest::createTestPackage(const QString &packageName, const QString &version)
45 {
46     qDebug() << "Create test package" << m_packageRoot;
47     QDir pRoot(m_packageRoot);
48     // Create the root and package dir.
49     if (!pRoot.exists()) {
50         QVERIFY(QDir().mkpath(m_packageRoot));
51     }
52 
53     // Create the package dir
54     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName));
55     qDebug() << "Created" << (m_packageRoot + "/" + packageName);
56 
57     // Create the metadata.desktop file
58     QFile file(m_packageRoot + "/" + packageName + "/metadata.desktop");
59 
60     QVERIFY(file.open(QIODevice::WriteOnly));
61 
62     QTextStream out(&file);
63     out << "[Desktop Entry]\n";
64     out << "Name=" << packageName << "\n";
65     out << "X-KDE-PluginInfo-Name=" << packageName << "\n";
66     out << "X-KDE-PluginInfo-Version=" << version << "\n";
67     file.flush();
68     file.close();
69 
70     qDebug() << "OUT: " << packageName;
71 
72     // Create the ui dir.
73     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName + "/contents/ui"));
74 
75     // Create the main file.
76     file.setFileName(m_packageRoot + "/" + packageName + "/contents/ui/main.qml");
77     QVERIFY(file.open(QIODevice::WriteOnly));
78 
79     out << "THIS IS A PLASMOID SCRIPT.....";
80     file.flush();
81     file.close();
82 
83     qDebug() << "THIS IS A PLASMOID SCRIPT THING";
84     // Now we have a minimal plasmoid package which is valid. Let's add some
85     // files to it for test purposes.
86 
87     // Create the images dir.
88     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName + "/contents/images"));
89     file.setFileName(m_packageRoot + "/" + packageName + "/contents/images/image-1.svg");
90 
91     QVERIFY(file.open(QIODevice::WriteOnly));
92 
93     out << "<svg>This is a test image</svg>";
94     file.flush();
95     file.close();
96 
97     file.setFileName(m_packageRoot + "/" + packageName + "/contents/images/image-2.svg");
98 
99     QVERIFY(file.open(QIODevice::WriteOnly));
100 
101     out.setDevice(&file);
102     out << "<svg>This is another test image</svg>";
103     file.flush();
104     file.close();
105 
106     // Create the scripts dir.
107     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName + "/contents/code"));
108 
109     // Create 2 js files
110     file.setFileName(m_packageRoot + "/" + packageName + "/contents/code/script.js");
111     QVERIFY(file.open(QIODevice::WriteOnly));
112 
113     out << "THIS IS A SCRIPT.....";
114     file.flush();
115     file.close();
116 }
117 
isValid()118 void PlasmoidPackageTest::isValid()
119 {
120     KPackage::Package *p = new KPackage::Package(m_defaultPackage);
121     p->setPath(m_packageRoot + '/' + m_package);
122 #ifndef NDEBUG
123     qDebug() << "package path is" << p->path();
124 #endif
125 
126     // A PlasmoidPackage is valid when:
127     // - The package root exists.
128     // - The package root consists an file named "ui/main.qml"
129     QVERIFY(!p->isValid());
130 
131     // Create the root and package dir.
132     QVERIFY(QDir().mkpath(m_packageRoot));
133     QVERIFY(QDir().mkpath(m_packageRoot + "/" + m_package));
134 
135     // Should still be invalid.
136     delete p;
137     p = new KPackage::Package(m_defaultPackage);
138     p->setPath(m_packageRoot + '/' + m_package);
139     QVERIFY(!p->isValid());
140 
141     // Create the metadata.desktop file.
142     QFile file(m_packageRoot + "/" + m_package + "/metadata.desktop");
143     QVERIFY(file.open(QIODevice::WriteOnly));
144 
145     QTextStream out(&file);
146     out << "[Desktop Entry]\n";
147     out << "Name=test\n";
148     out << "Description=Just a test desktop file";
149     file.flush();
150     file.close();
151 
152     // Create the ui dir.
153     QVERIFY(QDir().mkpath(m_packageRoot + "/" + m_package + "/contents/ui"));
154 
155     // No main file yet so should still be invalid.
156     delete p;
157     p = new KPackage::Package(m_defaultPackage);
158     p->setPath(m_packageRoot + '/' + m_package);
159     QVERIFY(!p->isValid());
160 
161     // Create the main file.
162     file.setFileName(m_packageRoot + "/" + m_package + "/contents/ui/main.qml");
163     QVERIFY(file.open(QIODevice::WriteOnly));
164 
165     out.setDevice(&file);
166     out << "THIS IS A PLASMOID SCRIPT.....\n";
167     file.flush();
168     file.close();
169 
170     file.setPermissions(QFile::ReadUser | QFile::WriteUser);
171     // Main file exists so should be valid now.
172     delete p;
173     p = new KPackage::Package(m_defaultPackage);
174     p->setPath(m_packageRoot + '/' + m_package);
175     QVERIFY(p->isValid());
176 #if KPACKAGE_ENABLE_DEPRECATED_SINCE(5, 21)
177     QCOMPARE(p->contentsHash(), QStringLiteral("a41160c6a763ea505c95bee12a7fc87952a61cf1"));
178 #endif
179     QCOMPARE(p->cryptographicHash(QCryptographicHash::Sha1), QByteArrayLiteral("a41160c6a763ea505c95bee12a7fc87952a61cf1"));
180     delete p;
181 }
182 
filePath()183 void PlasmoidPackageTest::filePath()
184 {
185     return;
186     // Package::filePath() returns
187     // - {package_root}/{package_name}/path/to/file if the file exists
188     // - QString() otherwise.
189     KPackage::Package *p = new KPackage::Package(m_defaultPackage);
190     p->setPath(m_packageRoot + '/' + m_package);
191 
192     QCOMPARE(p->filePath("scripts", QStringLiteral("main")), QString());
193 
194     QVERIFY(QDir().mkpath(m_packageRoot + "/" + m_package + "/contents/ui/main.qml"));
195     QFile file(m_packageRoot + "/" + m_package + "/contents/ui/main.qml");
196     QVERIFY(file.open(QIODevice::WriteOnly));
197 
198     QTextStream out(&file);
199     out << "THIS IS A PLASMOID SCRIPT.....";
200     file.flush();
201     file.close();
202 
203     // The package is valid by now so a path for code/main should get returned.
204     delete p;
205     p = new KPackage::Package(m_defaultPackage);
206     p->setPath(m_packageRoot + '/' + m_package);
207 
208     const QString path = QFileInfo(m_packageRoot + "/" + m_package + "/contents/ui/main.qml").canonicalFilePath();
209 
210     // Two ways to get the same info.
211     // 1. Give the file type which refers to a class of files (a directory) in
212     //    the package structure and the file name.
213     // 2. Give the file type which refers to a file in the package structure.
214     //
215     // NOTE: scripts, main and mainscript are defined in packages.cpp and are
216     //       specific for a PlasmoidPackage.
217     QCOMPARE(p->filePath("scripts", QStringLiteral("main")), path);
218     QCOMPARE(p->filePath("mainscript"), path);
219     delete p;
220 }
221 
entryList()222 void PlasmoidPackageTest::entryList()
223 {
224     // Create a package named @p packageName which is valid and has some images.
225     createTestPackage(m_package, QStringLiteral("1.1"));
226 
227     // Create a package object and verify that it is valid.
228     KPackage::Package *p = new KPackage::Package(m_defaultPackage);
229     p->setPath(m_packageRoot + '/' + m_package);
230     QVERIFY(p->isValid());
231 
232     // Now we have a valid package that should contain the following files in
233     // given filetypes:
234     // fileTye - Files
235     // scripts - {"script.js"}
236     // images - {"image-1.svg", "image-2.svg"}
237     QStringList files = p->entryList("scripts");
238     QCOMPARE(files.size(), 1);
239     QVERIFY(files.contains(QStringLiteral("script.js")));
240 
241     files = p->entryList("images");
242     QCOMPARE(files.size(), 2);
243     QVERIFY(files.contains(QStringLiteral("image-1.svg")));
244     QVERIFY(files.contains(QStringLiteral("image-2.svg")));
245     delete p;
246 }
247 
createAndInstallPackage()248 void PlasmoidPackageTest::createAndInstallPackage()
249 {
250     qDebug() << "                   ";
251     qDebug() << "   CreateAndInstall ";
252     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.1"));
253     const QString packagePath = m_packageRoot + '/' + "testpackage.plasmoid";
254 
255     KZip creator(packagePath);
256     QVERIFY(creator.open(QIODevice::WriteOnly));
257     creator.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
258     creator.close();
259     QDir rootDir(m_packageRoot + "/plasmoid_to_package");
260     rootDir.removeRecursively();
261 
262     QVERIFY2(QFile::exists(packagePath), qPrintable(packagePath));
263 
264     KZip package(packagePath);
265     QVERIFY(package.open(QIODevice::ReadOnly));
266     const KArchiveDirectory *dir = package.directory();
267     QVERIFY(dir); //
268     QVERIFY(dir->entry(QStringLiteral("metadata.desktop")));
269     const KArchiveEntry *contentsEntry = dir->entry(QStringLiteral("contents"));
270     QVERIFY(contentsEntry);
271     QVERIFY(contentsEntry->isDirectory());
272     const KArchiveDirectory *contents = static_cast<const KArchiveDirectory *>(contentsEntry);
273     QVERIFY(contents->entry(QStringLiteral("ui")));
274     QVERIFY(contents->entry(QStringLiteral("images")));
275 
276     m_defaultPackageStructure = new KPackage::PackageStructure(this);
277     KPackage::Package *p = new KPackage::Package(m_defaultPackageStructure);
278     qDebug() << "Installing " << packagePath;
279     // const QString packageRoot = "plasma/plasmoids/";
280     // const QString servicePrefix = "plasma-applet-";
281     KJob *job = p->install(packagePath, m_packageRoot);
282     // clang-format off
283     connect(job, SIGNAL(finished(KJob*)), SLOT(packageInstalled(KJob*)));
284     QSignalSpy spy(job, SIGNAL(finished(KJob*)));
285     // clang-format on
286     QVERIFY(spy.wait(1000));
287 
288     // is the package instance usable (ie proper path) after the install job has been completed?
289     QCOMPARE(p->path(), QString(QDir(m_packageRoot % "/plasmoid_to_package").canonicalPath() + QLatin1Char('/')));
290     cleanupPackage(QStringLiteral("plasmoid_to_package"));
291 
292     // QVERIFY(p->isValid());
293     delete p;
294 }
295 
noCrashOnAsyncInstall()296 void PlasmoidPackageTest::noCrashOnAsyncInstall()
297 {
298     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.1"));
299     const QString packagePath = m_packageRoot + '/' + "testpackage.plasmoid";
300 
301     KZip creator(packagePath);
302     QVERIFY(creator.open(QIODevice::WriteOnly));
303     creator.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
304     creator.close();
305     QDir rootDir(m_packageRoot + "/plasmoid_to_package");
306     rootDir.removeRecursively();
307 
308     KJob *job;
309     // scope the package so it will get deleted before the install operation finishes
310     // package is explicitlyshared internally and designed to be used on the stack
311     // see #370718
312     {
313         KPackage::Package package(new KPackage::PackageStructure(this));
314         job = package.install(packagePath, m_packageRoot);
315     }
316     // clang-format off
317     connect(job, SIGNAL(finished(KJob*)), SLOT(packageInstalled(KJob*)));
318     QSignalSpy spy(job, SIGNAL(finished(KJob*)));
319     // clang-format on
320     QVERIFY(spy.wait(1000));
321 
322     cleanupPackage(QStringLiteral("plasmoid_to_package"));
323 }
324 
createAndUpdatePackage()325 void PlasmoidPackageTest::createAndUpdatePackage()
326 {
327     // does the version number parsing work?
328     QVERIFY(KPackage::isVersionNewer(QStringLiteral("1.1"), QStringLiteral("1.1.1")));
329     QVERIFY(!KPackage::isVersionNewer(QStringLiteral("1.1.1"), QStringLiteral("1.1")));
330     QVERIFY(KPackage::isVersionNewer(QStringLiteral("1.1.1"), QStringLiteral("1.1.2")));
331     QVERIFY(KPackage::isVersionNewer(QStringLiteral("1.1.2"), QStringLiteral("2.1")));
332     QVERIFY(KPackage::isVersionNewer(QStringLiteral("0.1.2"), QStringLiteral("2")));
333     QVERIFY(!KPackage::isVersionNewer(QStringLiteral("1"), QStringLiteral("0.1.2")));
334 
335     qDebug() << "                   ";
336     qDebug() << "   CreateAndUpdate ";
337     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.1"));
338     const QString packagePath = m_packageRoot + '/' + "testpackage.plasmoid";
339 
340     KZip creator(packagePath);
341     QVERIFY(creator.open(QIODevice::WriteOnly));
342     creator.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
343     creator.close();
344     QDir rootDir(m_packageRoot + "/plasmoid_to_package");
345     rootDir.removeRecursively();
346 
347     QVERIFY(QFile::exists(packagePath));
348 
349     KZip package(packagePath);
350     QVERIFY(package.open(QIODevice::ReadOnly));
351     const KArchiveDirectory *dir = package.directory();
352     QVERIFY(dir); //
353     QVERIFY(dir->entry(QStringLiteral("metadata.desktop")));
354     const KArchiveEntry *contentsEntry = dir->entry(QStringLiteral("contents"));
355     QVERIFY(contentsEntry);
356     QVERIFY(contentsEntry->isDirectory());
357     const KArchiveDirectory *contents = static_cast<const KArchiveDirectory *>(contentsEntry);
358     QVERIFY(contents->entry(QStringLiteral("ui")));
359     QVERIFY(contents->entry(QStringLiteral("images")));
360 
361     m_defaultPackageStructure = new KPackage::PackageStructure(this);
362     KPackage::Package *p = new KPackage::Package(m_defaultPackageStructure);
363     qDebug() << "Installing " << packagePath;
364     // const QString packageRoot = "plasma/plasmoids/";
365     // const QString servicePrefix = "plasma-applet-";
366 
367     // clang-format off
368     KJob *job = p->update(packagePath, m_packageRoot);
369     connect(job, SIGNAL(finished(KJob*)), SLOT(packageInstalled(KJob*)));
370     QSignalSpy spy(job, SIGNAL(finished(KJob*)));
371     QVERIFY(spy.wait(1000));
372 
373     // same version, should fail
374     job = p->update(packagePath, m_packageRoot);
375     QSignalSpy spyFail(job, SIGNAL(finished(KJob*)));
376     QVERIFY(spyFail.wait(1000));
377     QVERIFY(job->error() == KPackage::Package::JobError::NewerVersionAlreadyInstalledError);
378     qDebug()<<job->errorText();
379 
380 
381     // create a new package with higher version
382     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.2"));
383 
384     KZip creator2(packagePath);
385     QVERIFY(creator2.open(QIODevice::WriteOnly));
386     creator2.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
387     creator2.close();
388     QDir rootDir2(m_packageRoot + "/plasmoid_to_package");
389     rootDir2.removeRecursively();
390 
391 
392     KJob *job2 = p->update(packagePath, m_packageRoot);
393     connect(job2, SIGNAL(finished(KJob*)), SLOT(packageInstalled(KJob*)));
394     QSignalSpy spy2(job2, SIGNAL(finished(KJob*)));
395     QVERIFY(spy2.wait(1000));
396     // clang-format on
397 
398     cleanupPackage(QStringLiteral("plasmoid_to_package"));
399 
400     // QVERIFY(p->isValid());
401     delete p;
402 }
403 
uncompressPackageWithSubFolder()404 void PlasmoidPackageTest::uncompressPackageWithSubFolder()
405 {
406     KPackage::PackageStructure *structure = new KPackage::PackageStructure;
407     KPackage::Package package(structure);
408     package.setPath(QFINDTESTDATA("data/customcontent.tar.gz"));
409 
410     // if metadata is correctly found, servicetypes should be ("SimpleContent", "CustomContent")
411     QCOMPARE(package.metadata().serviceTypes(), QStringList({"SimpleContent", "CustomContent"}));
412 }
413 
cleanupPackage(const QString & packageName)414 void PlasmoidPackageTest::cleanupPackage(const QString &packageName)
415 {
416     KPackage::Package *p = new KPackage::Package(m_defaultPackageStructure);
417     KJob *jj = p->uninstall(packageName, m_packageRoot);
418     connect(jj, SIGNAL(finished(KJob *)), SLOT(packageUninstalled(KJob *)));
419 
420     QSignalSpy spy(jj, &KJob::finished);
421     QVERIFY(spy.wait(1000));
422 }
423 
packageInstalled(KJob * j)424 void PlasmoidPackageTest::packageInstalled(KJob *j)
425 {
426     QVERIFY2(j->error() == KJob::NoError, qPrintable(j->errorText()));
427 }
428 
packageUninstalled(KJob * j)429 void PlasmoidPackageTest::packageUninstalled(KJob *j)
430 {
431     QVERIFY2(j->error() == KJob::NoError, qPrintable(j->errorText()));
432 }
433 
434 QTEST_MAIN(PlasmoidPackageTest)
435