summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt106
-rw-r--r--tests/TestUtil.h48
-rw-r--r--tests/data/.gitattributes2
-rw-r--r--tests/data/1.json43
-rw-r--r--tests/data/2.json31
-rw-r--r--tests/data/channels.json23
-rw-r--r--tests/data/errorChannels.json23
-rw-r--r--tests/data/fileOneA1
-rw-r--r--tests/data/fileOneB3
-rw-r--r--tests/data/fileThree1
-rw-r--r--tests/data/fileTwo1
-rw-r--r--tests/data/garbageChannels.json22
-rw-r--r--tests/data/index.json9
-rw-r--r--tests/data/noChannels.json5
-rw-r--r--tests/data/oneChannel.json11
-rw-r--r--tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml17
-rwxr-xr-xtests/data/tst_userutils-test_createShortcut-unix6
-rw-r--r--tests/test.manifest27
-rw-r--r--tests/test.rc28
-rw-r--r--tests/test_config.h.in3
-rw-r--r--tests/tst_DownloadUpdateTask.cpp273
-rw-r--r--tests/tst_UpdateChecker.cpp183
-rw-r--r--tests/tst_pathutils.cpp74
-rw-r--r--tests/tst_userutils.cpp71
24 files changed, 1011 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 00000000..7aeae59f
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,106 @@
+# run the unit tests with `make test`
+find_package(Qt5 COMPONENTS Test Core Network Widgets)
+
+include_directories(${MMC_SRC})
+
+unset(MultiMC_TESTS)
+macro(add_unit_test name)
+ unset(srcs)
+ foreach(arg ${testname} ${ARGN})
+ list(APPEND srcs ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
+ if (WIN32)
+ list(APPEND srcs ${CMAKE_CURRENT_SOURCE_DIR}/test.rc)
+ endif()
+ endforeach()
+ add_executable(tst_${name} ${srcs})
+ qt5_use_modules(tst_${name} Test Core Network Widgets)
+ target_link_libraries(tst_${name} MultiMC_common)
+ list(APPEND MultiMC_TESTS tst_${name})
+ add_test(NAME ${name} COMMAND tst_${name})
+endmacro()
+
+# Tests START #
+
+add_unit_test(pathutils tst_pathutils.cpp)
+add_unit_test(userutils tst_userutils.cpp)
+add_unit_test(UpdateChecker tst_UpdateChecker.cpp)
+add_unit_test(DownloadUpdateTask tst_DownloadUpdateTask.cpp)
+
+# Tests END #
+
+set(COVERAGE_SOURCE_DIRS
+ ${MMC_SRC}/logic/*
+ ${MMC_SRC}/logic/auth/*
+ ${MMC_SRC}/logic/auth/flows/*
+ ${MMC_SRC}/logic/lists/*
+ ${MMC_SRC}/logic/net/*
+ ${MMC_SRC}/logic/tasks/*
+ ${MMC_SRC}/gui/*
+ ${MMC_SRC}/gui/dialogs/*
+ ${MMC_SRC}/gui/widgets/*
+ ${MMC_SRC}/depends/settings/include/*
+ ${MMC_SRC}/depends/settings/src/*
+ ${MMC_SRC}/depends/util/include/*
+ ${MMC_SRC}/depends/util/src/*
+)
+
+if(MultiMC_CODE_COVERAGE)
+ unset(MultiMC_RUN_TESTS)
+ unset(MultiMC_TEST_COVERAGE_FILES)
+
+ foreach(test ${MultiMC_TESTS})
+ add_custom_target(MultiMC_RUN_TEST_${test}
+ COMMAND lcov -d ${CMAKE_CURRENT_BINARY_DIR} -z -q # clean test
+ && lcov -d ${MMC_BIN} -z -q # clean common
+ && lcov -d ${MMC_BIN}/depends/settings/CMakeFiles/libSettings.dir -z -q # clean settings
+ && lcov -d ${MMC_BIN}/depends/utils/CMakeFiles/libUtil.dir -z -q # clean utils
+ && ${MMC_BIN}/${test} -o coverage_${test}.out,xml # run test
+ && lcov -q --checksum -b ${MMC_SRC} -d ${CMAKE_CURRENT_BINARY_DIR} -c -o coverage_${test}.info # generate for test
+ && lcov -q --checksum -b ${MMC_SRC} -d ${MMC_BIN} -c -o coverage_common.info # generate for common
+ && lcov -q --checksum -b ${MMC_SRC} -d ${MMC_BIN}/depends/settings/CMakeFiles/libSettings.dir -c -o coverage_settings.info # generate for settings
+ && lcov -q --checksum -b ${MMC_SRC} -d ${MMC_BIN}/depends/util/CMakeFiles/libUtil.dir -c -o coverage_utils.info # generate for utils
+ && lcov -q --checksum -b ${MMC_SRC} -d .
+ -a coverage_${test}.info -a coverage_common.info -a coverage_settings.info -a coverage_utils.info
+ -o coverage_${test}-combined.info # combine test and common
+ && lcov -q --checksum -b ${MMC_SRC} --list-full-path --extract coverage_${test}-combined.info ${COVERAGE_SOURCE_DIRS} -o coverage_${test}-stripped.info # strip
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ VERBATIM
+ DEPENDS ${test}
+ COMMENT "Running ${test}..."
+ )
+ list(APPEND MultiMC_TEST_COVERAGE_FILES coverage_${test}-stripped.info)
+ list(APPEND MultiMC_RUN_TESTS MultiMC_RUN_TEST_${test})
+ endforeach(test)
+
+ add_custom_target(MultiMC_GENERATE_COVERAGE
+ DEPENDS ${MultiMC_RUN_TESTS}
+ COMMENT "Generating coverage files..."
+ )
+ add_custom_target(MultiMC_GENERATE_COVERAGE_HTML
+ COMMAND genhtml -t "MultiMC 5 Test Coverage" --num-spaces 4 --demangle-cpp --legend -o ${MMC_SRC}/html/coverage ${MultiMC_TEST_COVERAGE_FILES}
+ DEPENDS MultiMC_GENERATE_COVERAGE
+ COMMENT "Generating test coverage html..."
+ )
+ add_custom_target(MultiMC_RUN_TESTS DEPENDS MultiMC_GENERATE_COVERAGE_HTML)
+endif(MultiMC_CODE_COVERAGE)
+
+set(MultiMC_TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
+if(UNIX)
+ # on unix we get the third / from the filename
+ set(MultiMC_TEST_DATA_PATH "file://${MultiMC_TEST_DATA_PATH}")
+else()
+ # we don't on windows, so we have to add it ourselves
+ set(MultiMC_TEST_DATA_PATH "file:///${MultiMC_TEST_DATA_PATH}")
+endif()
+file(GLOB data_files "data/*")
+foreach(data_file ${data_files})
+ get_filename_component(filename ${data_file} NAME)
+ configure_file(
+ ${data_file}
+ ${CMAKE_CURRENT_BINARY_DIR}/data/${filename}
+ @ONLY
+ NEWLINE_STYLE LF
+ )
+endforeach()
+
+configure_file(test_config.h.in test_config.h @ONLY)
diff --git a/tests/TestUtil.h b/tests/TestUtil.h
new file mode 100644
index 00000000..e9099b15
--- /dev/null
+++ b/tests/TestUtil.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <QFile>
+#include <QCoreApplication>
+#include <QTest>
+#include <QDir>
+
+#include "MultiMC.h"
+
+#include "test_config.h"
+
+struct TestsInternal
+{
+ static QByteArray readFile(const QString &fileName)
+ {
+ QFile f(fileName);
+ f.open(QFile::ReadOnly);
+ return f.readAll();
+ }
+ static QString readFileUtf8(const QString &fileName)
+ {
+ return QString::fromUtf8(readFile(fileName));
+ }
+};
+
+#define MULTIMC_GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA( file ))
+#define MULTIMC_GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA( file ))
+
+#ifdef Q_OS_LINUX
+# define _MMC_EXTRA_ARGV , "-platform", "offscreen"
+# define _MMC_EXTRA_ARGC 2
+#else
+# define _MMC_EXTRA_ARGV
+# define _MMC_EXTRA_ARGC 0
+#endif
+
+
+
+#define QTEST_GUILESS_MAIN_MULTIMC(TestObject) \
+int main(int argc, char *argv[]) \
+{ \
+ char *argv_[] = { argv[0] _MMC_EXTRA_ARGV }; \
+ int argc_ = 1 + _MMC_EXTRA_ARGC; \
+ MultiMC app(argc_, argv_, true); \
+ app.setAttribute(Qt::AA_Use96Dpi, true); \
+ TestObject tc; \
+ return QTest::qExec(&tc, argc, argv); \
+}
diff --git a/tests/data/.gitattributes b/tests/data/.gitattributes
new file mode 100644
index 00000000..9ac803f0
--- /dev/null
+++ b/tests/data/.gitattributes
@@ -0,0 +1,2 @@
+* -text -diff
+
diff --git a/tests/data/1.json b/tests/data/1.json
new file mode 100644
index 00000000..0f2d6ced
--- /dev/null
+++ b/tests/data/1.json
@@ -0,0 +1,43 @@
+{
+ "ApiVersion": 0,
+ "Id": 1,
+ "Name": "1.0.1",
+ "Files": [
+ {
+ "Path": "fileOne",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileOneA"
+ }
+ ],
+ "Executable": true,
+ "Perms": 493,
+ "MD5": "9eb84090956c484e32cb6c08455a667b"
+ },
+ {
+ "Path": "fileTwo",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileTwo"
+ }
+ ],
+ "Executable": false,
+ "Perms": 644,
+ "MD5": "38f94f54fa3eb72b0ea836538c10b043"
+ },
+ {
+ "Path": "fileThree",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileThree"
+ }
+ ],
+ "Executable": false,
+ "Perms": "750",
+ "MD5": "f12df554b21e320be6471d7154130e70"
+ }
+ ]
+}
diff --git a/tests/data/2.json b/tests/data/2.json
new file mode 100644
index 00000000..6e2b0d3c
--- /dev/null
+++ b/tests/data/2.json
@@ -0,0 +1,31 @@
+{
+ "ApiVersion": 0,
+ "Id": 1,
+ "Name": "1.0.1",
+ "Files": [
+ {
+ "Path": "fileOne",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileOneB"
+ }
+ ],
+ "Executable": true,
+ "Perms": 493,
+ "MD5": "42915a71277c9016668cce7b82c6b577"
+ },
+ {
+ "Path": "fileTwo",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileTwo"
+ }
+ ],
+ "Executable": false,
+ "Perms": 644,
+ "MD5": "38f94f54fa3eb72b0ea836538c10b043"
+ }
+ ]
+}
diff --git a/tests/data/channels.json b/tests/data/channels.json
new file mode 100644
index 00000000..3ad504b0
--- /dev/null
+++ b/tests/data/channels.json
@@ -0,0 +1,23 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "@MultiMC_TEST_DATA_PATH@"
+ },
+ {
+ "id": "stable",
+ "name": "Stable",
+ "description": "It's stable at least",
+ "url": "@MultiMC_TEST_DATA_PATH@"
+ },
+ {
+ "id": "42",
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": "https://dent.me/tea"
+ }
+ ]
+}
diff --git a/tests/data/errorChannels.json b/tests/data/errorChannels.json
new file mode 100644
index 00000000..333cd445
--- /dev/null
+++ b/tests/data/errorChannels.json
@@ -0,0 +1,23 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "http://example.org/stuff"
+ },
+ {
+ "id": "stable",
+ "name": "",
+ "description": "It's stable at least",
+ "url": "ftp://username@host/path/to/stuff"
+ },
+ {
+ "id": "42",
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": ""
+ }
+ ]
+}
diff --git a/tests/data/fileOneA b/tests/data/fileOneA
new file mode 100644
index 00000000..f2e41136
--- /dev/null
+++ b/tests/data/fileOneA
@@ -0,0 +1 @@
+stuff
diff --git a/tests/data/fileOneB b/tests/data/fileOneB
new file mode 100644
index 00000000..f9aba922
--- /dev/null
+++ b/tests/data/fileOneB
@@ -0,0 +1,3 @@
+stuff
+
+more stuff that came in the new version
diff --git a/tests/data/fileThree b/tests/data/fileThree
new file mode 100644
index 00000000..6353ff16
--- /dev/null
+++ b/tests/data/fileThree
@@ -0,0 +1 @@
+this is yet another file
diff --git a/tests/data/fileTwo b/tests/data/fileTwo
new file mode 100644
index 00000000..aad9a93a
--- /dev/null
+++ b/tests/data/fileTwo
@@ -0,0 +1 @@
+some other stuff
diff --git a/tests/data/garbageChannels.json b/tests/data/garbageChannels.json
new file mode 100644
index 00000000..1450fb9c
--- /dev/null
+++ b/tests/data/garbageChannels.json
@@ -0,0 +1,22 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+aa "url": "http://example.org/stuff"
+ },
+a "id": "stable",
+ "name": "Stable",
+ "description": "It's stable at least",
+ "url": "ftp://username@host/path/to/stuff"
+ },
+ {
+ "id": "42"f
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": "https://dent.me/tea"
+ }
+ ]
+}
diff --git a/tests/data/index.json b/tests/data/index.json
new file mode 100644
index 00000000..20ceb9f4
--- /dev/null
+++ b/tests/data/index.json
@@ -0,0 +1,9 @@
+{
+ "ApiVersion": 0,
+ "Versions": [
+ { "Id": 0, "Name": "1.0.0" },
+ { "Id": 1, "Name": "1.0.1" },
+ { "Id": 2, "Name": "1.0.2" },
+ { "Id": 3, "Name": "1.0.3" }
+ ]
+}
diff --git a/tests/data/noChannels.json b/tests/data/noChannels.json
new file mode 100644
index 00000000..bbb2cb70
--- /dev/null
+++ b/tests/data/noChannels.json
@@ -0,0 +1,5 @@
+{
+ "format_version": 0,
+ "channels": [
+ ]
+}
diff --git a/tests/data/oneChannel.json b/tests/data/oneChannel.json
new file mode 100644
index 00000000..84727ac7
--- /dev/null
+++ b/tests/data/oneChannel.json
@@ -0,0 +1,11 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "http://example.org/stuff"
+ }
+ ]
+}
diff --git a/tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml b/tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml
new file mode 100644
index 00000000..09c162ca
--- /dev/null
+++ b/tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml
@@ -0,0 +1,17 @@
+<update version="3">
+ <install>
+ <file>
+ <source>sourceOne</source>
+ <dest>destOne</dest>
+ <mode>0777</mode>
+ </file>
+ <file>
+ <source>MultiMC.exe</source>
+ <dest>M/u/l/t/i/M/C/e/x/e</dest>
+ <mode>0644</mode>
+ </file>
+ </install>
+ <uninstall>
+ <file>toDelete.abc</file>
+ </uninstall>
+</update>
diff --git a/tests/data/tst_userutils-test_createShortcut-unix b/tests/data/tst_userutils-test_createShortcut-unix
new file mode 100755
index 00000000..1ce3a2bd
--- /dev/null
+++ b/tests/data/tst_userutils-test_createShortcut-unix
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+TryExec=asdfDest
+Exec=asdfDest 'arg1' 'arg2'
+Name=asdf
+Icon=
diff --git a/tests/test.manifest b/tests/test.manifest
new file mode 100644
index 00000000..8b4dbb98
--- /dev/null
+++ b/tests/test.manifest
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity name="MultiMC.Test.0" type="win32" version="5.0.0.0" />
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
+ </dependentAssembly>
+ </dependency>
+ <description>Custom Minecraft launcher for managing multiple installs.</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!--The ID below indicates app support for Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!--The ID below indicates app support for Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly> \ No newline at end of file
diff --git a/tests/test.rc b/tests/test.rc
new file mode 100644
index 00000000..a288dba6
--- /dev/null
+++ b/tests/test.rc
@@ -0,0 +1,28 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+1 RT_MANIFEST "test.manifest"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 1,0,0,0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "FileDescription", "Testcase"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "ProductName", "MultiMC Testcase"
+ VALUE "ProductVersion", "5"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04b0 // Unicode
+ END
+END
diff --git a/tests/test_config.h.in b/tests/test_config.h.in
new file mode 100644
index 00000000..69dd38e7
--- /dev/null
+++ b/tests/test_config.h.in
@@ -0,0 +1,3 @@
+#pragma once
+
+#define MultiMC_TEST_DATA_PATH "@MultiMC_TEST_DATA_PATH@"
diff --git a/tests/tst_DownloadUpdateTask.cpp b/tests/tst_DownloadUpdateTask.cpp
new file mode 100644
index 00000000..e6784402
--- /dev/null
+++ b/tests/tst_DownloadUpdateTask.cpp
@@ -0,0 +1,273 @@
+#include <QTest>
+#include <QSignalSpy>
+
+#include "TestUtil.h"
+
+#include "logic/updater/DownloadUpdateTask.h"
+#include "logic/updater/UpdateChecker.h"
+#include "depends/util/include/pathutils.h"
+
+DownloadUpdateTask::FileSourceList encodeBaseFile(const char *suffix)
+{
+ auto base = qApp->applicationDirPath();
+ QUrl localFile = QUrl::fromLocalFile(base + suffix);
+ QString localUrlString = localFile.toString(QUrl::FullyEncoded);
+ auto item = DownloadUpdateTask::FileSource("http", localUrlString);
+ return DownloadUpdateTask::FileSourceList({item});
+}
+
+Q_DECLARE_METATYPE(DownloadUpdateTask::VersionFileList)
+Q_DECLARE_METATYPE(DownloadUpdateTask::UpdateOperation)
+
+bool operator==(const DownloadUpdateTask::FileSource &f1,
+ const DownloadUpdateTask::FileSource &f2)
+{
+ return f1.type == f2.type && f1.url == f2.url && f1.compressionType == f2.compressionType;
+}
+bool operator==(const DownloadUpdateTask::VersionFileEntry &v1,
+ const DownloadUpdateTask::VersionFileEntry &v2)
+{
+ return v1.path == v2.path && v1.mode == v2.mode && v1.sources == v2.sources &&
+ v1.md5 == v2.md5;
+}
+bool operator==(const DownloadUpdateTask::UpdateOperation &u1,
+ const DownloadUpdateTask::UpdateOperation &u2)
+{
+ return u1.type == u2.type && u1.file == u2.file && u1.dest == u2.dest && u1.mode == u2.mode;
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::FileSource &f)
+{
+ dbg.nospace() << "FileSource(type=" << f.type << " url=" << f.url
+ << " comp=" << f.compressionType << ")";
+ return dbg.maybeSpace();
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::VersionFileEntry &v)
+{
+ dbg.nospace() << "VersionFileEntry(path=" << v.path << " mode=" << v.mode
+ << " md5=" << v.md5 << " sources=" << v.sources << ")";
+ return dbg.maybeSpace();
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::UpdateOperation::Type &t)
+{
+ switch (t)
+ {
+ case DownloadUpdateTask::UpdateOperation::OP_COPY:
+ dbg << "OP_COPY";
+ break;
+ case DownloadUpdateTask::UpdateOperation::OP_DELETE:
+ dbg << "OP_DELETE";
+ break;
+ case DownloadUpdateTask::UpdateOperation::OP_MOVE:
+ dbg << "OP_MOVE";
+ break;
+ case DownloadUpdateTask::UpdateOperation::OP_CHMOD:
+ dbg << "OP_CHMOD";
+ break;
+ }
+ return dbg.maybeSpace();
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::UpdateOperation &u)
+{
+ dbg.nospace() << "UpdateOperation(type=" << u.type << " file=" << u.file
+ << " dest=" << u.dest << " mode=" << u.mode << ")";
+ return dbg.maybeSpace();
+}
+
+class DownloadUpdateTaskTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+ }
+ void cleanupTestCase()
+ {
+ }
+
+ void test_writeInstallScript()
+ {
+ DownloadUpdateTask task(
+ QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/")).toString(), 0);
+
+ DownloadUpdateTask::UpdateOperationList ops;
+
+ ops << DownloadUpdateTask::UpdateOperation::CopyOp("sourceOne", "destOne", 0777)
+ << DownloadUpdateTask::UpdateOperation::CopyOp("MultiMC.exe", "M/u/l/t/i/M/C/e/x/e")
+ << DownloadUpdateTask::UpdateOperation::DeleteOp("toDelete.abc");
+ auto testFile = "tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml";
+ const QString script = QDir::temp().absoluteFilePath("MultiMCUpdateScript.xml");
+ QVERIFY(task.writeInstallScript(ops, script));
+ QCOMPARE(TestsInternal::readFileUtf8(script).replace(QRegExp("[\r\n]+"), "\n"),
+ MULTIMC_GET_TEST_FILE_UTF8(testFile).replace(QRegExp("[\r\n]+"), "\n"));
+ }
+
+// DISABLED: fails.
+/*
+ void test_parseVersionInfo_data()
+ {
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<DownloadUpdateTask::VersionFileList>("list");
+ QTest::addColumn<QString>("error");
+ QTest::addColumn<bool>("ret");
+
+ QTest::newRow("one")
+ << MULTIMC_GET_TEST_FILE("tests/data/1.json")
+ << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{"fileOne",
+ 493,
+ encodeBaseFile("/tests/data/fileOneA"),
+ "9eb84090956c484e32cb6c08455a667b"}
+ << DownloadUpdateTask::VersionFileEntry{"fileTwo",
+ 644,
+ encodeBaseFile("/tests/data/fileTwo"),
+ "38f94f54fa3eb72b0ea836538c10b043"}
+ << DownloadUpdateTask::VersionFileEntry{"fileThree",
+ 750,
+ encodeBaseFile("/tests/data/fileThree"),
+ "f12df554b21e320be6471d7154130e70"})
+ << QString() << true;
+ QTest::newRow("two")
+ << MULTIMC_GET_TEST_FILE("tests/data/2.json")
+ << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{"fileOne",
+ 493,
+ encodeBaseFile("/tests/data/fileOneB"),
+ "42915a71277c9016668cce7b82c6b577"}
+ << DownloadUpdateTask::VersionFileEntry{"fileTwo",
+ 644,
+ encodeBaseFile("/tests/data/fileTwo"),
+ "38f94f54fa3eb72b0ea836538c10b043"})
+ << QString() << true;
+ }
+ void test_parseVersionInfo()
+ {
+ QFETCH(QByteArray, data);
+ QFETCH(DownloadUpdateTask::VersionFileList, list);
+ QFETCH(QString, error);
+ QFETCH(bool, ret);
+
+ DownloadUpdateTask::VersionFileList outList;
+ QString outError;
+ bool outRet = DownloadUpdateTask("", 0).parseVersionInfo(data, &outList, &outError);
+ QCOMPARE(outRet, ret);
+ QCOMPARE(outList, list);
+ QCOMPARE(outError, error);
+ }
+*/
+ void test_processFileLists_data()
+ {
+ QTest::addColumn<DownloadUpdateTask *>("downloader");
+ QTest::addColumn<DownloadUpdateTask::VersionFileList>("currentVersion");
+ QTest::addColumn<DownloadUpdateTask::VersionFileList>("newVersion");
+ QTest::addColumn<DownloadUpdateTask::UpdateOperationList>("expectedOperations");
+
+ DownloadUpdateTask *downloader = new DownloadUpdateTask(QString(), -1);
+
+ // update fileOne, keep fileTwo, remove fileThree
+ QTest::newRow("test 1")
+ << downloader << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileOne", 493,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource(
+ "http", "http://host/path/fileOne-1"),
+ "9eb84090956c484e32cb6c08455a667b"}
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileTwo", 644,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource(
+ "http", "http://host/path/fileTwo-1"),
+ "38f94f54fa3eb72b0ea836538c10b043"}
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileThree", 420,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource(
+ "http", "http://host/path/fileThree-1"),
+ "f12df554b21e320be6471d7154130e70"})
+ << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileOne", 493,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource("http",
+ "http://host/path/fileOne-2"),
+ "42915a71277c9016668cce7b82c6b577"}
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileTwo", 644,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource("http",
+ "http://host/path/fileTwo-2"),
+ "38f94f54fa3eb72b0ea836538c10b043"})
+ << (DownloadUpdateTask::UpdateOperationList()
+ << DownloadUpdateTask::UpdateOperation::DeleteOp("tests/data/fileThree")
+ << DownloadUpdateTask::UpdateOperation::CopyOp(
+ PathCombine(downloader->updateFilesDir(),
+ QString("tests/data/fileOne").replace("/", "_")),
+ "tests/data/fileOne", 493));
+ }
+ void test_processFileLists()
+ {
+ QFETCH(DownloadUpdateTask *, downloader);
+ QFETCH(DownloadUpdateTask::VersionFileList, currentVersion);
+ QFETCH(DownloadUpdateTask::VersionFileList, newVersion);
+ QFETCH(DownloadUpdateTask::UpdateOperationList, expectedOperations);
+
+ DownloadUpdateTask::UpdateOperationList operations;
+
+ downloader->processFileLists(new NetJob("Dummy"), currentVersion, newVersion,
+ operations);
+ qDebug() << (operations == expectedOperations);
+ qDebug() << operations;
+ qDebug() << expectedOperations;
+ QCOMPARE(operations, expectedOperations);
+ }
+/*
+ void test_masterTest()
+ {
+ QLOG_INFO() << "#####################";
+ MMC->m_version.build = 1;
+ MMC->m_version.channel = "develop";
+ auto channels =
+ QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/channels.json"));
+ auto root = QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/"));
+ QLOG_DEBUG() << "channels: " << channels;
+ QLOG_DEBUG() << "root: " << root;
+ MMC->updateChecker()->setChannelListUrl(channels.toString());
+ MMC->updateChecker()->setCurrentChannel("develop");
+
+ DownloadUpdateTask task(root.toString(), 2);
+
+ QSignalSpy succeededSpy(&task, SIGNAL(succeeded()));
+
+ task.start();
+
+ QVERIFY(succeededSpy.wait());
+ }
+*/
+ void test_OSXPathFixup()
+ {
+ QString path, pathOrig;
+ bool result;
+ // Proper OSX path
+ pathOrig = path = "MultiMC.app/Foo/Bar/Baz";
+ qDebug() << "Proper OSX path: " << path;
+ result = DownloadUpdateTask::fixPathForOSX(path);
+ QCOMPARE(path, QString("Foo/Bar/Baz"));
+ QCOMPARE(result, true);
+
+ // Bad OSX path
+ pathOrig = path = "translations/klingon.lol";
+ qDebug() << "Bad OSX path: " << path;
+ result = DownloadUpdateTask::fixPathForOSX(path);
+ QCOMPARE(path, pathOrig);
+ QCOMPARE(result, false);
+ }
+};
+
+QTEST_GUILESS_MAIN_MULTIMC(DownloadUpdateTaskTest)
+
+#include "tst_DownloadUpdateTask.moc"
diff --git a/tests/tst_UpdateChecker.cpp b/tests/tst_UpdateChecker.cpp
new file mode 100644
index 00000000..0dcb242f
--- /dev/null
+++ b/tests/tst_UpdateChecker.cpp
@@ -0,0 +1,183 @@
+#include <QTest>
+#include <QSignalSpy>
+
+#include "depends/settings/settingsobject.h"
+#include "depends/settings/setting.h"
+
+#include "TestUtil.h"
+#include "logic/updater/UpdateChecker.h"
+
+Q_DECLARE_METATYPE(UpdateChecker::ChannelListEntry)
+
+bool operator==(const UpdateChecker::ChannelListEntry &e1, const UpdateChecker::ChannelListEntry &e2)
+{
+ return e1.id == e2.id &&
+ e1.name == e2.name &&
+ e1.description == e2.description &&
+ e1.url == e2.url;
+}
+
+QDebug operator<<(QDebug dbg, const UpdateChecker::ChannelListEntry &c)
+{
+ dbg.nospace() << "ChannelListEntry(id=" << c.id << " name=" << c.name << " description=" << c.description << " url=" << c.url << ")";
+ return dbg.maybeSpace();
+}
+
+class ResetSetting
+{
+public:
+ ResetSetting(std::shared_ptr<Setting> setting) : setting(setting), oldValue(setting->get()) {}
+ ~ResetSetting()
+ {
+ setting->set(oldValue);
+ }
+
+ std::shared_ptr<Setting> setting;
+ QVariant oldValue;
+};
+
+class UpdateCheckerTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ static QString findTestDataUrl(const char *file)
+ {
+ return QUrl::fromLocalFile(QFINDTESTDATA(file)).toString();
+ }
+ void tst_ChannelListParsing_data()
+ {
+ QTest::addColumn<QString>("channel");
+ QTest::addColumn<QString>("channelUrl");
+ QTest::addColumn<bool>("hasChannels");
+ QTest::addColumn<bool>("valid");
+ QTest::addColumn<QList<UpdateChecker::ChannelListEntry> >("result");
+
+ QTest::newRow("garbage")
+ << QString()
+ << findTestDataUrl("tests/data/garbageChannels.json")
+ << false
+ << false
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("errors")
+ << QString()
+ << findTestDataUrl("tests/data/errorChannels.json")
+ << false
+ << true
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("no channels")
+ << QString()
+ << findTestDataUrl("tests/data/noChannels.json")
+ << false
+ << true
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("one channel")
+ << QString("develop")
+ << findTestDataUrl("tests/data/oneChannel.json")
+ << true
+ << true
+ << (QList<UpdateChecker::ChannelListEntry>() << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", "http://example.org/stuff"});
+ QTest::newRow("several channels")
+ << QString("develop")
+ << findTestDataUrl("tests/data/channels.json")
+ << true
+ << true
+ << (QList<UpdateChecker::ChannelListEntry>()
+ << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", MultiMC_TEST_DATA_PATH}
+ << UpdateChecker::ChannelListEntry{"stable", "Stable", "It's stable at least", MultiMC_TEST_DATA_PATH}
+ << UpdateChecker::ChannelListEntry{"42", "The Channel", "This is the channel that is going to answer all of your questions", "https://dent.me/tea"});
+ }
+ void tst_ChannelListParsing()
+ {
+ ResetSetting resetUpdateChannel(MMC->settings()->getSetting("UpdateChannel"));
+
+ QFETCH(QString, channel);
+ QFETCH(QString, channelUrl);
+ QFETCH(bool, hasChannels);
+ QFETCH(bool, valid);
+ QFETCH(QList<UpdateChecker::ChannelListEntry>, result);
+
+ MMC->settings()->set("UpdateChannel", channel);
+
+ UpdateChecker checker;
+
+ QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
+ QVERIFY(channelListLoadedSpy.isValid());
+
+ checker.setChannelListUrl(channelUrl);
+
+ checker.updateChanList();
+
+ if (valid)
+ {
+ QVERIFY(channelListLoadedSpy.wait());
+ QCOMPARE(channelListLoadedSpy.size(), 1);
+ }
+ else
+ {
+ channelListLoadedSpy.wait();
+ QCOMPARE(channelListLoadedSpy.size(), 0);
+ }
+
+ QCOMPARE(checker.hasChannels(), hasChannels);
+ QCOMPARE(checker.getChannelList(), result);
+ }
+
+ void tst_UpdateChecking_data()
+ {
+ QTest::addColumn<QString>("channel");
+ QTest::addColumn<QString>("channelUrl");
+ QTest::addColumn<int>("currentBuild");
+ QTest::addColumn<QList<QVariant> >("result");
+
+ QTest::newRow("valid channel")
+ << "develop" << findTestDataUrl("tests/data/channels.json")
+ << 2
+ << (QList<QVariant>() << QString() << "1.0.3" << 3);
+ }
+ void tst_UpdateChecking()
+ {
+ ResetSetting resetUpdateChannel(MMC->settings()->getSetting("UpdateChannel"));
+
+ QFETCH(QString, channel);
+ QFETCH(QString, channelUrl);
+ QFETCH(int, currentBuild);
+ QFETCH(QList<QVariant>, result);
+
+ MMC->settings()->set("UpdateChannel", channel);
+ MMC->m_version.build = currentBuild;
+
+ UpdateChecker checker;
+ checker.setChannelListUrl(channelUrl);
+
+ QSignalSpy updateAvailableSpy(&checker, SIGNAL(updateAvailable(QString,QString,int)));
+ QVERIFY(updateAvailableSpy.isValid());
+ QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
+ QVERIFY(channelListLoadedSpy.isValid());
+
+ checker.updateChanList();
+ QVERIFY(channelListLoadedSpy.wait());
+
+ checker.m_channels[0].url = QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/")).toString();
+
+ checker.checkForUpdate(false);
+
+ QVERIFY(updateAvailableSpy.wait());
+ QList<QVariant> res = result;
+ res[0] = checker.m_channels[0].url;
+ QCOMPARE(updateAvailableSpy.first(), res);
+ }
+};
+
+QTEST_GUILESS_MAIN_MULTIMC(UpdateCheckerTest)
+
+#include "tst_UpdateChecker.moc"
diff --git a/tests/tst_pathutils.cpp b/tests/tst_pathutils.cpp
new file mode 100644
index 00000000..a1310d00
--- /dev/null
+++ b/tests/tst_pathutils.cpp
@@ -0,0 +1,74 @@
+#include <QTest>
+#include "TestUtil.h"
+
+#include "depends/util/include/pathutils.h"
+
+class PathUtilsTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void test_PathCombine1_data()
+ {
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("path1");
+ QTest::addColumn<QString>("path2");
+
+ QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
+ QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
+#if defined(Q_OS_WIN)
+ QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
+ QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
+ QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
+#endif
+ }
+ void test_PathCombine1()
+ {
+ QFETCH(QString, result);
+ QFETCH(QString, path1);
+ QFETCH(QString, path2);
+
+ QCOMPARE(PathCombine(path1, path2), result);
+ }
+
+ void test_PathCombine2_data()
+ {
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("path1");
+ QTest::addColumn<QString>("path2");
+ QTest::addColumn<QString>("path3");
+
+ QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
+ QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
+ QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
+ QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
+#if defined(Q_OS_WIN)
+ QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
+ QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
+ QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
+ QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
+#endif
+ }
+ void test_PathCombine2()
+ {
+ QFETCH(QString, result);
+ QFETCH(QString, path1);
+ QFETCH(QString, path2);
+ QFETCH(QString, path3);
+
+ QCOMPARE(PathCombine(path1, path2, path3), result);
+ }
+};
+
+QTEST_GUILESS_MAIN_MULTIMC(PathUtilsTest)
+
+#include "tst_pathutils.moc"
diff --git a/tests/tst_userutils.cpp b/tests/tst_userutils.cpp
new file mode 100644
index 00000000..3bc980c0
--- /dev/null
+++ b/tests/tst_userutils.cpp
@@ -0,0 +1,71 @@
+#include <QTest>
+#include <QStandardPaths>
+#include "TestUtil.h"
+
+#include "depends/util/include/userutils.h"
+
+class UserUtilsTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void test_getDesktop()
+ {
+ QCOMPARE(Util::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
+ }
+
+// this is only valid on linux
+// FIXME: implement on windows, OSX, then test.
+#if defined(Q_OS_LINUX)
+ void test_createShortcut_data()
+ {
+ QTest::addColumn<QString>("location");
+ QTest::addColumn<QString>("dest");
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("iconLocation");
+ QTest::addColumn<QByteArray>("result");
+
+ QTest::newRow("unix") << QDir::currentPath()
+ << "asdfDest"
+ << (QStringList() << "arg1" << "arg2")
+ << "asdf"
+ << QString()
+ #if defined(Q_OS_LINUX)
+ << MULTIMC_GET_TEST_FILE("data/tst_userutils-test_createShortcut-unix")
+ #elif defined(Q_OS_WIN)
+ << QByteArray()
+ #endif
+ ;
+ }
+
+ void test_createShortcut()
+ {
+ QFETCH(QString, location);
+ QFETCH(QString, dest);
+ QFETCH(QStringList, args);
+ QFETCH(QString, name);
+ QFETCH(QString, iconLocation);
+ QFETCH(QByteArray, result);
+
+ QVERIFY(Util::createShortCut(location, dest, args, name, iconLocation));
+ QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
+
+ //QDir().remove(location);
+ }
+#endif
+};
+
+
+QTEST_GUILESS_MAIN_MULTIMC(UserUtilsTest)
+
+#include "tst_userutils.moc"