File indexing completed on 2024-04-14 03:54:15

0001 /*
0002     SPDX-FileCopyrightText: 2007 Bertjan Broeksema <b.broeksema@kdemail.net>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "plasmoidpackagetest.h"
0008 #include "../src/kpackage/config-package.h"
0009 
0010 #include <KJob>
0011 #include <QDir>
0012 #include <QFile>
0013 #include <QSignalSpy>
0014 #include <QStandardPaths>
0015 #include <kzip.h>
0016 
0017 #include <QDebug>
0018 #include <QJsonDocument>
0019 #include <QJsonObject>
0020 #include <qtestcase.h>
0021 
0022 #include "packagejob.h"
0023 #include "packageloader.h"
0024 #include "private/utils.h"
0025 
0026 void PlasmoidPackageTest::initTestCase()
0027 {
0028     QStandardPaths::setTestModeEnabled(true);
0029 }
0030 
0031 void PlasmoidPackageTest::init()
0032 {
0033     qDebug() << "PlasmoidPackage::init()";
0034     qRegisterMetaType<KPackage::Package>(); // Needed for signal spy
0035     m_package = QStringLiteral("Package");
0036     m_packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/packageRoot";
0037     const auto pack = KPackage::PackageLoader::self()->loadPackageStructure("Plasma/TestKPackageInternalPlasmoid");
0038     QVERIFY(pack);
0039     m_defaultPackage = KPackage::Package(pack);
0040     cleanup(); // to prevent previous runs from interfering with this one
0041 }
0042 
0043 void PlasmoidPackageTest::cleanup()
0044 {
0045     qDebug() << "cleaning up";
0046     // Clean things up.
0047     QDir(m_packageRoot).removeRecursively();
0048 }
0049 
0050 void PlasmoidPackageTest::createTestPackage(const QString &packageName, const QString &version)
0051 {
0052     qDebug() << "Create test package" << m_packageRoot;
0053     QDir pRoot(m_packageRoot);
0054     // Create the root and package dir.
0055     if (!pRoot.exists()) {
0056         QVERIFY(QDir().mkpath(m_packageRoot));
0057     }
0058 
0059     // Create the package dir
0060     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName));
0061     qDebug() << "Created" << (m_packageRoot + "/" + packageName);
0062 
0063     // Create the metadata.json file
0064     QFile file(m_packageRoot + "/" + packageName + "/metadata.json");
0065 
0066     QVERIFY(file.open(QIODevice::WriteOnly));
0067 
0068     QJsonObject kplugin;
0069     kplugin.insert(QLatin1String("Id"), packageName);
0070     kplugin.insert(QLatin1String("Name"), packageName);
0071     kplugin.insert(QLatin1String("Version"), version);
0072     QJsonObject root{{QLatin1String("KPlugin"), kplugin}};
0073 
0074     QTextStream out(&file);
0075     file.write(QJsonDocument(root).toJson());
0076     file.flush();
0077     file.close();
0078 
0079     qDebug() << "OUT: " << packageName;
0080 
0081     // Create the ui dir.
0082     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName + "/contents/ui"));
0083 
0084     // Create the main file.
0085     file.setFileName(m_packageRoot + "/" + packageName + "/contents/ui/main.qml");
0086     QVERIFY(file.open(QIODevice::WriteOnly));
0087 
0088     out << "THIS IS A PLASMOID SCRIPT.....";
0089     file.flush();
0090     file.close();
0091 
0092     qDebug() << "THIS IS A PLASMOID SCRIPT THING";
0093     // Now we have a minimal plasmoid package which is valid. Let's add some
0094     // files to it for test purposes.
0095 
0096     // Create the images dir.
0097     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName + "/contents/images"));
0098     file.setFileName(m_packageRoot + "/" + packageName + "/contents/images/image-1.svg");
0099 
0100     QVERIFY(file.open(QIODevice::WriteOnly));
0101 
0102     out << "<svg>This is a test image</svg>";
0103     file.flush();
0104     file.close();
0105 
0106     file.setFileName(m_packageRoot + "/" + packageName + "/contents/images/image-2.svg");
0107 
0108     QVERIFY(file.open(QIODevice::WriteOnly));
0109 
0110     out.setDevice(&file);
0111     out << "<svg>This is another test image</svg>";
0112     file.flush();
0113     file.close();
0114 
0115     // Create the scripts dir.
0116     QVERIFY(QDir().mkpath(m_packageRoot + "/" + packageName + "/contents/code"));
0117 
0118     // Create 2 js files
0119     file.setFileName(m_packageRoot + "/" + packageName + "/contents/code/script.js");
0120     QVERIFY(file.open(QIODevice::WriteOnly));
0121 
0122     out << "THIS IS A SCRIPT.....";
0123     file.flush();
0124     file.close();
0125 }
0126 
0127 void PlasmoidPackageTest::isValid()
0128 {
0129     KPackage::Package p(m_defaultPackage);
0130     p.setPath(m_packageRoot + '/' + m_package);
0131 #ifndef NDEBUG
0132     qDebug() << "package path is" << p.path();
0133 #endif
0134 
0135     // A PlasmoidPackage is valid when:
0136     // - The package root exists.
0137     // - The package root consists an file named "ui/main.qml"
0138     QVERIFY(!p.isValid());
0139 
0140     // Create the root and package dir.
0141     QVERIFY(QDir().mkpath(m_packageRoot));
0142     QVERIFY(QDir().mkpath(m_packageRoot + "/" + m_package));
0143 
0144     // Should still be invalid.
0145     p = KPackage::Package(m_defaultPackage);
0146     p.setPath(m_packageRoot + '/' + m_package);
0147     QVERIFY(!p.isValid());
0148 
0149     // Create the ui dir.
0150     QVERIFY(QDir().mkpath(m_packageRoot + "/" + m_package + "/contents/ui"));
0151 
0152     // No main file yet so should still be invalid.
0153     p = KPackage::Package(m_defaultPackage);
0154     p.setPath(m_packageRoot + '/' + m_package);
0155     QVERIFY(!p.isValid());
0156 
0157     // Create the main file.
0158     QFile file(m_packageRoot + "/" + m_package + "/contents/ui/main.qml");
0159     QVERIFY(file.open(QIODevice::WriteOnly));
0160 
0161     QTextStream out(&file);
0162     out << "THIS IS A PLASMOID SCRIPT.....\n";
0163     file.flush();
0164     file.close();
0165 
0166     file.setPermissions(QFile::ReadUser | QFile::WriteUser);
0167     // Main file exists so should be valid now.
0168     p = KPackage::Package(m_defaultPackage);
0169     p.setPath(m_packageRoot + '/' + m_package);
0170     QVERIFY(p.isValid());
0171     QCOMPARE(p.cryptographicHash(QCryptographicHash::Sha1), QByteArrayLiteral("468c7934dfa635986a85e3364363b1f39d157cd5"));
0172 }
0173 
0174 void PlasmoidPackageTest::filePath()
0175 {
0176     // Package::filePath() returns
0177     // - {package_root}/{package_name}/path/to/file if the file exists
0178     // - QString() otherwise.
0179     KPackage::Package p(m_defaultPackage);
0180     p.setPath(m_packageRoot + '/' + m_package);
0181 
0182     QCOMPARE(p.filePath("scripts", QStringLiteral("main")), QString());
0183 
0184     QVERIFY(QDir().mkpath(m_packageRoot + "/" + m_package + "/contents/ui/"));
0185     QFile file(m_packageRoot + "/" + m_package + "/contents/ui/main.qml");
0186     QVERIFY(file.open(QIODevice::WriteOnly));
0187 
0188     QTextStream out(&file);
0189     out << "THIS IS A PLASMOID SCRIPT.....";
0190     file.flush();
0191     file.close();
0192 
0193     // The package is valid by now so a path for code/main should get returned.
0194     p = KPackage::Package(m_defaultPackage);
0195     p.setPath(m_packageRoot + '/' + m_package);
0196 
0197     const QString path = QFileInfo(m_packageRoot + "/" + m_package + "/contents/ui/main.qml").canonicalFilePath();
0198 
0199     // Two ways to get the same info.
0200     // 1. Give the file type which refers to a class of files (a directory) in
0201     //    the package structure and the file name.
0202     // 2. Give the file type which refers to a file in the package structure.
0203     //
0204     // NOTE: scripts, main and mainscript are defined in packages.cpp and are
0205     //       specific for a PlasmoidPackage.
0206     QCOMPARE(p.filePath("mainscript"), path);
0207     QCOMPARE(p.filePath("ui", QStringLiteral("main.qml")), path);
0208 }
0209 
0210 void PlasmoidPackageTest::entryList()
0211 {
0212     // Create a package named @p packageName which is valid and has some images.
0213     createTestPackage(m_package, QStringLiteral("1.1"));
0214 
0215     // Create a package object and verify that it is valid.
0216     KPackage::Package p(m_defaultPackage);
0217     p.setPath(m_packageRoot + '/' + m_package);
0218     QVERIFY(p.isValid());
0219 
0220     // Now we have a valid package that should contain the following files in
0221     // given filetypes:
0222     // fileTye - Files
0223     // scripts - {"script.js"}
0224     // images - {"image-1.svg", "image-2.svg"}
0225     QStringList files = p.entryList("scripts");
0226     QCOMPARE(files.size(), 1);
0227     QVERIFY(files.contains(QStringLiteral("script.js")));
0228 
0229     files = p.entryList("images");
0230     QCOMPARE(files.size(), 2);
0231     QVERIFY(files.contains(QStringLiteral("image-1.svg")));
0232     QVERIFY(files.contains(QStringLiteral("image-2.svg")));
0233 }
0234 
0235 void PlasmoidPackageTest::createAndInstallPackage()
0236 {
0237     qDebug() << "                   ";
0238     qDebug() << "   CreateAndInstall ";
0239     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.1"));
0240     const QString packagePath = m_packageRoot + '/' + "testpackage.plasmoid";
0241 
0242     KZip creator(packagePath);
0243     QVERIFY(creator.open(QIODevice::WriteOnly));
0244     creator.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
0245     creator.close();
0246     QDir rootDir(m_packageRoot + "/plasmoid_to_package");
0247     rootDir.removeRecursively();
0248 
0249     QVERIFY2(QFile::exists(packagePath), qPrintable(packagePath));
0250 
0251     KZip package(packagePath);
0252     QVERIFY(package.open(QIODevice::ReadOnly));
0253     const KArchiveDirectory *dir = package.directory();
0254     QVERIFY(dir); //
0255     QVERIFY(dir->entry(QStringLiteral("metadata.json")));
0256     const KArchiveEntry *contentsEntry = dir->entry(QStringLiteral("contents"));
0257     QVERIFY(contentsEntry);
0258     QVERIFY(contentsEntry->isDirectory());
0259     const KArchiveDirectory *contents = static_cast<const KArchiveDirectory *>(contentsEntry);
0260     QVERIFY(contents->entry(QStringLiteral("ui")));
0261     QVERIFY(contents->entry(QStringLiteral("images")));
0262 
0263     KPackage::PackageLoader::self()->addKnownPackageStructure(m_defaultPackageStructure, new KPackage::PackageStructure(this));
0264     KPackage::Package p;
0265     qDebug() << "Installing " << packagePath;
0266     auto job = KPackage::PackageJob::install(m_defaultPackageStructure, packagePath, m_packageRoot);
0267     connect(job, &KJob::finished, this, [&p, job]() { // clazy:exclude=lambda-in-connect
0268         p = job->package();
0269     });
0270     QSignalSpy spy(job, &KJob::finished);
0271     QVERIFY(spy.wait(1000));
0272 
0273     // is the package instance usable (ie proper path) after the install job has been completed?
0274     QCOMPARE(p.path(), QString(QDir(m_packageRoot % "/plasmoid_to_package").canonicalPath() + QLatin1Char('/')));
0275     cleanupPackage(QStringLiteral("plasmoid_to_package"));
0276 }
0277 
0278 void PlasmoidPackageTest::createAndUpdatePackage()
0279 {
0280     // does the version number parsing work?
0281     QVERIFY(isVersionNewer(QStringLiteral("1.1"), QStringLiteral("1.1.1")));
0282     QVERIFY(!isVersionNewer(QStringLiteral("1.1.1"), QStringLiteral("1.1")));
0283     QVERIFY(isVersionNewer(QStringLiteral("1.1.1"), QStringLiteral("1.1.2")));
0284     QVERIFY(isVersionNewer(QStringLiteral("1.1.2"), QStringLiteral("2.1")));
0285     QVERIFY(isVersionNewer(QStringLiteral("0.1.2"), QStringLiteral("2")));
0286     QVERIFY(!isVersionNewer(QStringLiteral("1"), QStringLiteral("0.1.2")));
0287 
0288     qDebug() << "                   ";
0289     qDebug() << "   CreateAndUpdate ";
0290     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.1"));
0291     const QString packagePath = m_packageRoot + '/' + "testpackage.plasmoid";
0292 
0293     KZip creator(packagePath);
0294     QVERIFY(creator.open(QIODevice::WriteOnly));
0295     creator.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
0296     creator.close();
0297     QDir rootDir(m_packageRoot + "/plasmoid_to_package");
0298     rootDir.removeRecursively();
0299 
0300     QVERIFY(QFile::exists(packagePath));
0301 
0302     KZip package(packagePath);
0303     QVERIFY(package.open(QIODevice::ReadOnly));
0304     const KArchiveDirectory *dir = package.directory();
0305     QVERIFY(dir); //
0306     QVERIFY(dir->entry(QStringLiteral("metadata.json")));
0307     const KArchiveEntry *contentsEntry = dir->entry(QStringLiteral("contents"));
0308     QVERIFY(contentsEntry);
0309     QVERIFY(contentsEntry->isDirectory());
0310     const KArchiveDirectory *contents = static_cast<const KArchiveDirectory *>(contentsEntry);
0311     QVERIFY(contents->entry(QStringLiteral("ui")));
0312     QVERIFY(contents->entry(QStringLiteral("images")));
0313 
0314     qDebug() << "Installing " << packagePath;
0315 
0316     KJob *job = KPackage::PackageJob::update(m_defaultPackageStructure, packagePath, m_packageRoot);
0317     connect(job, &KJob::finished, [this, job]() {
0318         packageInstalled(job);
0319     });
0320     QSignalSpy spy(job, &KJob::finished);
0321     QVERIFY(spy.wait(1000));
0322 
0323     // same version, should fail
0324     job = KPackage::PackageJob::update(m_defaultPackageStructure, packagePath, m_packageRoot);
0325     QSignalSpy spyFail(job, &KJob::finished);
0326     QVERIFY(spyFail.wait(1000));
0327     QVERIFY(job->error() == KPackage::PackageJob::JobError::NewerVersionAlreadyInstalledError);
0328     qDebug() << job->errorText();
0329 
0330     // create a new package with higher version
0331     createTestPackage(QStringLiteral("plasmoid_to_package"), QStringLiteral("1.2"));
0332 
0333     KZip creator2(packagePath);
0334     QVERIFY(creator2.open(QIODevice::WriteOnly));
0335     creator2.addLocalDirectory(m_packageRoot + '/' + "plasmoid_to_package", QStringLiteral("."));
0336     creator2.close();
0337     QDir rootDir2(m_packageRoot + "/plasmoid_to_package");
0338     rootDir2.removeRecursively();
0339 
0340     KJob *job2 = KPackage::PackageJob::update(m_defaultPackageStructure, packagePath, m_packageRoot);
0341     connect(job2, &KJob::finished, [this, job2]() {
0342         packageInstalled(job2);
0343     });
0344     QSignalSpy spy2(job2, &KJob::finished);
0345     QVERIFY(spy2.wait(1000));
0346 
0347     cleanupPackage(QStringLiteral("plasmoid_to_package"));
0348 }
0349 
0350 void PlasmoidPackageTest::uncompressPackageWithSubFolder()
0351 {
0352     KPackage::PackageStructure *structure = new KPackage::PackageStructure;
0353     KPackage::Package package(structure);
0354     package.setPath(QFINDTESTDATA("data/customcontent.tar.gz"));
0355 
0356     QCOMPARE(readKPackageType(package.metadata()), "KPackage/CustomContent");
0357 }
0358 
0359 void PlasmoidPackageTest::cleanupPackage(const QString &packageName)
0360 {
0361     KJob *j = KPackage::PackageJob::uninstall(m_defaultPackageStructure, packageName, m_packageRoot);
0362     connect(j, &KJob::finished, [this, j]() {
0363         packageUninstalled(j);
0364     });
0365 
0366     QSignalSpy spy(j, &KJob::finished);
0367     QVERIFY(spy.wait(1000));
0368 }
0369 
0370 void PlasmoidPackageTest::packageInstalled(KJob *j)
0371 {
0372     QVERIFY2(j->error() == KJob::NoError, qPrintable(j->errorText()));
0373 }
0374 
0375 void PlasmoidPackageTest::packageUninstalled(KJob *j)
0376 {
0377     QVERIFY2(j->error() == KJob::NoError, qPrintable(j->errorText()));
0378 }
0379 void PlasmoidPackageTest::testInstallNonExistentPackageStructure()
0380 {
0381     const QString packageName = "testpackage";
0382     createTestPackage(packageName, "1.0");
0383     auto job = KPackage::PackageJob::install("KPackage/DoesNotExist", packageName, m_packageRoot);
0384     connect(job, &KJob::result, this, [job]() {
0385         QVERIFY(!job->package().isValid());
0386         QCOMPARE(job->error(), KPackage::PackageJob::JobError::InvalidPackageStructure);
0387         QCOMPARE(job->errorText(), "Could not load package structure KPackage/DoesNotExist");
0388     });
0389     QSignalSpy spy(job, &KJob::result);
0390     QVERIFY(spy.wait(1000));
0391 }
0392 
0393 QTEST_MAIN(PlasmoidPackageTest)
0394 
0395 #include "moc_plasmoidpackagetest.cpp"