mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
[Installer][Client] Back up user data before old uninstallers can delete it
This commit is contained in:
parent
a712553c81
commit
d33542db59
7 changed files with 367 additions and 12 deletions
|
|
@ -15,6 +15,7 @@ Var ReinstallMode
|
|||
Var AllowForceClose
|
||||
Var GuardDir
|
||||
Var OldUninstallString
|
||||
Var DataBackupDir
|
||||
Var StaleFindHandle
|
||||
Var StaleFindName
|
||||
Var IsUpgrade
|
||||
|
|
@ -317,7 +318,9 @@ ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatr
|
|||
!insertmacro CockLog "uninstall (32-bit): '$R0 _?=$INSTDIR'"
|
||||
StrCmp $R0 "" done32
|
||||
DetailPrint "Removing previous version (32-bit)..."
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait '$R0 _?=$INSTDIR'
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done32:
|
||||
|
||||
|
|
@ -329,13 +332,67 @@ ${If} ${RunningX64}
|
|||
!insertmacro CockLog "uninstall (64-bit): '$R0 _?=$INSTDIR'"
|
||||
StrCmp $R0 "" done64
|
||||
DetailPrint "Removing previous version (64-bit)..."
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait '$R0 _?=$INSTDIR'
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done64:
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
; BackupUserDataBeforeUninstall / RestoreUserDataAfterUninstall
|
||||
;
|
||||
; The uninstallers shipped in 3.1.0/3.1.1 run a blanket "RMDir /r $INSTDIR".
|
||||
; For a portable install that deletes $INSTDIR\data (decks, settings, pictures)
|
||||
; along with the payload, and for a normal install it deletes anything the user
|
||||
; pointed into $INSTDIR. The fixed uninstaller preserves foreign files, but the
|
||||
; destructive one is already on disk on affected machines, so stash a copy
|
||||
; outside $INSTDIR before invoking it and put it back afterwards. Best effort
|
||||
; only: a failed backup must never abort the install.
|
||||
Function BackupUserDataBeforeUninstall
|
||||
StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup"
|
||||
${If} ${FileExists} "$INSTDIR\portable.dat"
|
||||
${AndIf} ${FileExists} "$INSTDIR\data\*.*"
|
||||
!insertmacro CockLog "pre-uninstall backup: copying $INSTDIR\data"
|
||||
DetailPrint "Backing up portable user data before removing previous version..."
|
||||
CreateDirectory "$DataBackupDir\data"
|
||||
CopyFiles /SILENT "$INSTDIR\data\*.*" "$DataBackupDir\data\"
|
||||
DetailPrint "Portable user data backup complete."
|
||||
${EndIf}
|
||||
${If} ${FileExists} "$INSTDIR\*.cod"
|
||||
!insertmacro CockLog "pre-uninstall backup: copying loose deck files"
|
||||
DetailPrint "Backing up deck files stored in the install directory..."
|
||||
CreateDirectory "$DataBackupDir\loose_decks"
|
||||
CopyFiles /SILENT "$INSTDIR\*.cod" "$DataBackupDir\loose_decks\"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function RestoreUserDataAfterUninstall
|
||||
StrCmp $DataBackupDir "" done
|
||||
${If} ${FileExists} "$DataBackupDir\data\*.*"
|
||||
${If} ${FileExists} "$INSTDIR\data\*.*"
|
||||
!insertmacro CockLog "post-uninstall restore: user data survived, backup kept"
|
||||
DetailPrint "Previous user data survived the uninstall; backup kept at $DataBackupDir\data."
|
||||
${Else}
|
||||
!insertmacro CockLog "post-uninstall restore: restoring $INSTDIR\data"
|
||||
CreateDirectory "$INSTDIR\data"
|
||||
CopyFiles /SILENT "$DataBackupDir\data\*.*" "$INSTDIR\data\"
|
||||
DetailPrint "Restored portable user data from pre-uninstall backup."
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${If} ${FileExists} "$DataBackupDir\loose_decks\*.cod"
|
||||
!insertmacro CockLog "post-uninstall restore: restoring loose deck files"
|
||||
CreateDirectory "$INSTDIR"
|
||||
CopyFiles /SILENT "$DataBackupDir\loose_decks\*.cod" "$INSTDIR\"
|
||||
DetailPrint "Restored deck files stored in the install directory."
|
||||
${IfNot} ${Silent}
|
||||
MessageBox MB_OK|MB_ICONINFORMATION "Your decks were stored inside the Cockatrice install folder, which the previous version's uninstaller deletes.$\n$\nThey have been restored from a backup. Please move them out of the install folder (Settings -> General -> Paths) so a future uninstall cannot delete them again."
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
done:
|
||||
FunctionEnd
|
||||
|
||||
; --- Running instance handling ---
|
||||
; Cockatrice, Oracle and Servatrice must not be running while files are
|
||||
; replaced or deleted. A still-running process holds locks on its .exe and
|
||||
|
|
@ -1041,7 +1098,9 @@ ${If} $PortableMode = 0
|
|||
!insertmacro CockLog "componentsPagePre: old 32-bit uninstall '$OldUninstallString _?=$INSTDIR'"
|
||||
Call EnsureOldInstallNotRunning
|
||||
ClearErrors
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait "$OldUninstallString _?=$INSTDIR"
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done32:
|
||||
|
||||
|
|
@ -1064,7 +1123,9 @@ ${If} $PortableMode = 0
|
|||
!insertmacro CockLog "componentsPagePre: old 64-bit uninstall '$OldUninstallString _?=$INSTDIR'"
|
||||
Call EnsureOldInstallNotRunning
|
||||
ClearErrors
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait "$OldUninstallString _?=$INSTDIR"
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done64:
|
||||
${EndIf}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDialog>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
|
|
@ -110,6 +111,7 @@
|
|||
#include <libcockatrice/settings/tabs_settings.h>
|
||||
#include <libcockatrice/settings/updates_settings.h>
|
||||
#include <libcockatrice/utility/local_server_name.h>
|
||||
#include <libcockatrice/utility/user_data_backup.h>
|
||||
#include <memory>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -609,23 +611,82 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
void MainWindow::warnAboutDataPathsInProgramDir()
|
||||
{
|
||||
auto &paths = SettingsCache::instance().paths();
|
||||
if (SettingsCache::instance().getIsPortableBuild() || paths.getAppDirWarningAcknowledged()) {
|
||||
if (paths.getAppDirWarningAcknowledged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QStringList offenders = paths.pathsInsideDir(QCoreApplication::applicationDirPath());
|
||||
if (offenders.isEmpty()) {
|
||||
return;
|
||||
const QString appDir = QCoreApplication::applicationDirPath();
|
||||
const bool isPortable = SettingsCache::instance().getIsPortableBuild();
|
||||
|
||||
// Sources to back up outside the install directory before warning. The uninstallers
|
||||
// shipped in 3.1.0/3.1.1 delete the whole install tree, so a safety copy is kept
|
||||
// even though the fixed uninstaller preserves foreign files - the destructive one
|
||||
// is already on disk on affected machines.
|
||||
QStringList backupSources;
|
||||
QStringList offenders;
|
||||
if (isPortable) {
|
||||
// Portable data lives inside the install directory by design, so there is
|
||||
// nothing to reconfigure. Back up the small irreplaceable data (decks, custom
|
||||
// sets, settings) instead. Pictures are deliberately excluded: they are large
|
||||
// and can always be re-downloaded.
|
||||
const QString dataDir = SettingsCache::instance().getDataPath();
|
||||
for (const QString &sub : {QStringLiteral("decks"), QStringLiteral("customsets"), QStringLiteral("settings")}) {
|
||||
const QString candidate = QDir(dataDir).absoluteFilePath(sub);
|
||||
if (QFileInfo::exists(candidate)) {
|
||||
backupSources.append(candidate);
|
||||
}
|
||||
}
|
||||
if (backupSources.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
offenders = paths.pathsInsideDir(appDir);
|
||||
if (offenders.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
backupSources = offenders;
|
||||
}
|
||||
|
||||
QMessageBox box(QMessageBox::Warning, tr("Data paths inside the program directory"),
|
||||
tr("These paths are configured inside the Cockatrice program directory:\n\n%1\n\n"
|
||||
"Cockatrice keeps its own data outside the program directory by default. Files "
|
||||
"stored inside it can be deleted by an update or by uninstalling Cockatrice. "
|
||||
"Change them in the Settings dialog - most are on the General page - to keep "
|
||||
"them safe.")
|
||||
.arg(offenders.join(QLatin1Char('\n'))),
|
||||
QMessageBox::Ok, this);
|
||||
QString backupRoot = UserDataBackup::defaultBackupRoot();
|
||||
if (PathsSettings::isInsideDir(backupRoot, appDir)) {
|
||||
backupRoot = QDir::temp().absoluteFilePath(QStringLiteral("CockatriceBackups"));
|
||||
}
|
||||
QStringList backupLocations;
|
||||
for (const QString &source : backupSources) {
|
||||
QString backupLocation;
|
||||
if (UserDataBackup::backupAtRiskPath(source, backupRoot, &backupLocation)) {
|
||||
qCInfo(MainWindowLog) << "Backed up at-risk user data" << source << "to" << backupLocation;
|
||||
backupLocations.append(backupLocation);
|
||||
} else {
|
||||
qCWarning(MainWindowLog) << "Failed to back up at-risk user data:" << source;
|
||||
}
|
||||
}
|
||||
|
||||
QString message;
|
||||
if (isPortable) {
|
||||
message = tr("This portable install keeps its data inside the program directory, where the "
|
||||
"uninstallers shipped in Cockatrice 3.1.0/3.1.1 delete everything on uninstall or "
|
||||
"update.\n\nA safety backup of your decks, custom sets and settings has been created "
|
||||
"at:\n%1\n\nUninstalling with the old uninstaller still deletes the program "
|
||||
"directory, so keep your own copy of that backup until you have updated once.")
|
||||
.arg(backupLocations.isEmpty() ? backupRoot : backupLocations.join(QLatin1Char('\n')));
|
||||
} else {
|
||||
message = tr("These paths are configured inside the Cockatrice program directory:\n\n%1\n\n"
|
||||
"Cockatrice keeps its own data outside the program directory by default. Files "
|
||||
"stored inside it can be deleted by an update or by uninstalling Cockatrice. "
|
||||
"Change them in the Settings dialog - most are on the General page - to keep "
|
||||
"them safe.")
|
||||
.arg(offenders.join(QLatin1Char('\n')));
|
||||
if (!backupLocations.isEmpty()) {
|
||||
message += tr("\n\nA safety backup has been created at:\n%1").arg(backupLocations.join(QLatin1Char('\n')));
|
||||
} else {
|
||||
message += tr("\n\nThe automatic safety backup failed; please back up these folders manually.");
|
||||
}
|
||||
}
|
||||
QMessageBox box(QMessageBox::Warning,
|
||||
isPortable ? tr("Portable data at risk on uninstall")
|
||||
: tr("Data paths inside the program directory"),
|
||||
message, QMessageBox::Ok, this);
|
||||
box.setTextFormat(Qt::PlainText);
|
||||
auto *dontShowAgain = new QCheckBox(tr("Don't show this again"));
|
||||
box.setCheckBox(dontShowAgain);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ set(UTILITY_SOURCES
|
|||
libcockatrice/utility/report_categories.cpp
|
||||
libcockatrice/utility/server_rate_limiter.cpp
|
||||
libcockatrice/utility/translation_loader.cpp
|
||||
libcockatrice/utility/user_data_backup.cpp
|
||||
libcockatrice/utility/warning_categories.cpp
|
||||
)
|
||||
|
||||
|
|
@ -35,6 +36,7 @@ set(UTILITY_HEADERS
|
|||
libcockatrice/utility/days_years_between.h
|
||||
libcockatrice/utility/server_rate_limiter.h
|
||||
libcockatrice/utility/translation_loader.h
|
||||
libcockatrice/utility/user_data_backup.h
|
||||
libcockatrice/utility/warning_categories.h
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
#include "user_data_backup.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace UserDataBackup
|
||||
{
|
||||
|
||||
bool copyDirectoryRecursively(const QString &sourceDir, const QString &destDir)
|
||||
{
|
||||
QDir source(sourceDir);
|
||||
if (!source.exists()) {
|
||||
return false;
|
||||
}
|
||||
if (!QDir().mkpath(destDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
QDirIterator it(sourceDir, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden,
|
||||
QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
QString entryRel = source.relativeFilePath(it.filePath());
|
||||
QString target = QDir(destDir).absoluteFilePath(entryRel);
|
||||
if (it.fileInfo().isDir()) {
|
||||
if (!QDir().mkpath(target)) {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
if (QFile::exists(target) && !QFile::remove(target)) {
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
if (!QFile::copy(it.filePath(), target)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool backupAtRiskPath(const QString &sourcePath, const QString &backupRoot, QString *backupLocationOut)
|
||||
{
|
||||
QFileInfo source(sourcePath);
|
||||
if (!source.exists()) {
|
||||
return false;
|
||||
}
|
||||
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss");
|
||||
QString backupDir = QDir(backupRoot).absoluteFilePath("CockatriceDataBackup-" + timestamp);
|
||||
if (!QDir().mkpath(backupDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
QString backupLocation;
|
||||
if (source.isDir()) {
|
||||
backupLocation = QDir(backupDir).absoluteFilePath(source.fileName());
|
||||
ok = copyDirectoryRecursively(sourcePath, backupLocation);
|
||||
} else {
|
||||
backupLocation = QDir(backupDir).absoluteFilePath(source.fileName());
|
||||
if (QFile::exists(backupLocation)) {
|
||||
QFile::remove(backupLocation);
|
||||
}
|
||||
ok = QFile::copy(sourcePath, backupLocation);
|
||||
}
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
if (backupLocationOut) {
|
||||
*backupLocationOut = backupLocation;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString defaultBackupRoot()
|
||||
{
|
||||
QString documents = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
if (!documents.isEmpty()) {
|
||||
return QDir(documents).absoluteFilePath("CockatriceBackups");
|
||||
}
|
||||
QString appData = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||
if (!appData.isEmpty()) {
|
||||
return QDir(appData).absoluteFilePath("../CockatriceBackups");
|
||||
}
|
||||
return QDir::temp().absoluteFilePath("CockatriceBackups");
|
||||
}
|
||||
|
||||
} // namespace UserDataBackup
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @file user_data_backup.h
|
||||
* @ingroup Utility
|
||||
* @brief Best-effort safety copies of user data that lives inside the install directory.
|
||||
*
|
||||
* Background: Windows uninstallers shipped in 3.1.0/3.1.1 ran a blanket
|
||||
* "RMDir /r $INSTDIR". For a portable install that deletes $INSTDIR\data
|
||||
* (decks, settings, pictures) along with the payload, and for a normal install
|
||||
* it deletes anything the user pointed into $INSTDIR. The fixed uninstaller
|
||||
* preserves foreign files, but the destructive one is already on disk on
|
||||
* affected machines, so the client keeps a safety copy outside the install
|
||||
* directory before warning the user. Detection of at-risk paths lives in
|
||||
* PathsSettings::pathsInsideDir; this helper only copies.
|
||||
*/
|
||||
|
||||
#ifndef USER_DATA_BACKUP_H
|
||||
#define USER_DATA_BACKUP_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace UserDataBackup
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Recursively copies @p sourceDir into @p destDir, creating it as needed.
|
||||
* @returns true when every file was copied (or there was nothing to copy).
|
||||
*/
|
||||
bool copyDirectoryRecursively(const QString &sourceDir, const QString &destDir);
|
||||
|
||||
/**
|
||||
* @brief Copies an at-risk path (file or directory) to a timestamped folder under @p backupRoot.
|
||||
* @param sourcePath file or directory holding user data (must exist).
|
||||
* @param backupRoot directory OUTSIDE the install tree that will hold timestamped backups.
|
||||
* @param backupLocationOut receives the created backup file or directory on success.
|
||||
* @returns true on success, false when the source is missing or nothing could be copied.
|
||||
*/
|
||||
bool backupAtRiskPath(const QString &sourcePath, const QString &backupRoot, QString *backupLocationOut = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Default backup root guaranteed to live outside the install directory.
|
||||
*/
|
||||
QString defaultBackupRoot();
|
||||
|
||||
} // namespace UserDataBackup
|
||||
|
||||
#endif // USER_DATA_BACKUP_H
|
||||
|
|
@ -174,6 +174,7 @@ endfunction()
|
|||
cockatrice_add_test(NAME expression_test SOURCES expression_test.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME clamped_arithmetic_test SOURCES clamped_arithmetic_test.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME test_age_formatting SOURCES test_age_formatting.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME user_data_backup_test SOURCES user_data_backup_test.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME password_hash_test SOURCES password_hash_test.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME report_categories_test SOURCES report_categories_test.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME warning_categories_test SOURCES warning_categories_test.cpp LIBS libcockatrice_utility)
|
||||
|
|
|
|||
91
tests/user_data_backup_test.cpp
Normal file
91
tests/user_data_backup_test.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTemporaryDir>
|
||||
#include <libcockatrice/utility/user_data_backup.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
TEST(UserDataBackup, CopyDirectoryKeepsNestedStructure)
|
||||
{
|
||||
QTemporaryDir sourceDir;
|
||||
QTemporaryDir destParent;
|
||||
ASSERT_TRUE(sourceDir.isValid());
|
||||
ASSERT_TRUE(destParent.isValid());
|
||||
QDir source(sourceDir.path());
|
||||
ASSERT_TRUE(source.mkpath("decks/sub"));
|
||||
QFile deck(source.absoluteFilePath("decks/sub/mydeck.cod"));
|
||||
ASSERT_TRUE(deck.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
deck.write("<cockatrice_deck version=\"1\"/>\n");
|
||||
deck.close();
|
||||
|
||||
QString dest = QDir(destParent.path()).absoluteFilePath("copy");
|
||||
EXPECT_TRUE(UserDataBackup::copyDirectoryRecursively(sourceDir.path(), dest));
|
||||
EXPECT_TRUE(QFile::exists(dest + "/decks/sub/mydeck.cod"));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, CopyMissingDirectoryFails)
|
||||
{
|
||||
QTemporaryDir destParent;
|
||||
ASSERT_TRUE(destParent.isValid());
|
||||
EXPECT_FALSE(
|
||||
UserDataBackup::copyDirectoryRecursively("/nonexistent-cockatrice-path-xyz", destParent.path() + "/copy"));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, BackupDirectoryCreatesTimestampedCopy)
|
||||
{
|
||||
QTemporaryDir sourceDir;
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(sourceDir.isValid());
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
QFile deck(QDir(sourceDir.path()).absoluteFilePath("mydeck.cod"));
|
||||
ASSERT_TRUE(deck.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
deck.write("<cockatrice_deck version=\"1\"/>\n");
|
||||
deck.close();
|
||||
|
||||
QString backupLocation;
|
||||
EXPECT_TRUE(UserDataBackup::backupAtRiskPath(sourceDir.path(), backupRoot.path(), &backupLocation));
|
||||
EXPECT_TRUE(QFile::exists(backupLocation + "/mydeck.cod"));
|
||||
EXPECT_TRUE(backupLocation.startsWith(QDir(backupRoot.path()).absolutePath()));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, BackupSingleFile)
|
||||
{
|
||||
QTemporaryDir sourceDir;
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(sourceDir.isValid());
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
QString cardDb = QDir(sourceDir.path()).absoluteFilePath("cards.xml");
|
||||
QFile db(cardDb);
|
||||
ASSERT_TRUE(db.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
db.write("<cockatrice_card_database/>\n");
|
||||
db.close();
|
||||
|
||||
QString backupLocation;
|
||||
EXPECT_TRUE(UserDataBackup::backupAtRiskPath(cardDb, backupRoot.path(), &backupLocation));
|
||||
EXPECT_TRUE(QFile::exists(backupLocation));
|
||||
EXPECT_TRUE(backupLocation.endsWith("cards.xml"));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, BackupMissingSourceFails)
|
||||
{
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
EXPECT_FALSE(UserDataBackup::backupAtRiskPath("/nonexistent-cockatrice-path-xyz", backupRoot.path(), nullptr));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, DefaultBackupRootIsUsable)
|
||||
{
|
||||
QString root = UserDataBackup::defaultBackupRoot();
|
||||
EXPECT_FALSE(root.isEmpty());
|
||||
EXPECT_TRUE(QDir(root).isAbsolute());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue