mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-06 10:03:26 -08:00
[Installer][Client] Fix review findings: subdir backup, portable warning, backup API
NSIS: - Back up unknown top-level install subdirs (e.g. <installdir>\decks), not just loose deck files, so the 3.1.1 -> new upgrade no longer loses data folders the old RMDir /r deletes before the client ever runs. Payload dirs (plugins/translations/sounds/themes) stay in place to avoid restoring stale DLLs; restore + relocate handle the new backup set and the one-time notice covers both decks and folders. - Restart the %TEMP% backup when the 32/64-bit passes point at different dirs instead of merging (which restored one dir's files into the other); document that only the last pass relocates and the %TEMP% copy is not a durable second copy. Client: - Drop the portable at-risk warning: portable installs never shipped an uninstaller and portable updates preserve data/, so every portable user got a spurious dialog plus daily host-machine copies. - AtRiskDataWarning is now a plain class with Q_DECLARE_TR_FUNCTIONS instead of QObject+Q_OBJECT; logging category renamed to at_risk_data_warning; MainWindow wrapper inlined into startupConfigCheck. - Lower the synchronous startup backup cap 500 -> 200 MB. Utility/tests: - Remove test-only backupAtRiskPath()/findRecentBackupSession() and the no-marker/no-manifest legacy paths; isBackupSessionComplete() now requires the complete marker; tests updated to the plural API.
This commit is contained in:
parent
202c0d6561
commit
a23e02b5a4
8 changed files with 229 additions and 345 deletions
|
|
@ -98,10 +98,12 @@ TEST(UserDataBackup, BackupDirectoryCreatesTimestampedCopy)
|
|||
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()));
|
||||
QStringList locations;
|
||||
QString sessionDir;
|
||||
EXPECT_TRUE(UserDataBackup::backupAtRiskPaths({sourceDir.path()}, backupRoot.path(), &locations, &sessionDir));
|
||||
ASSERT_EQ(locations.size(), 1);
|
||||
EXPECT_TRUE(QFile::exists(locations.first() + "/mydeck.cod"));
|
||||
EXPECT_TRUE(locations.first().startsWith(QDir(backupRoot.path()).absolutePath()));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, BackupSingleFile)
|
||||
|
|
@ -116,17 +118,23 @@ TEST(UserDataBackup, BackupSingleFile)
|
|||
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"));
|
||||
QStringList locations;
|
||||
EXPECT_TRUE(UserDataBackup::backupAtRiskPaths({cardDb}, backupRoot.path(), &locations, nullptr));
|
||||
ASSERT_EQ(locations.size(), 1);
|
||||
EXPECT_TRUE(QFile::exists(locations.first()));
|
||||
EXPECT_TRUE(locations.first().endsWith("cards.xml"));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, BackupMissingSourceFails)
|
||||
{
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
EXPECT_FALSE(UserDataBackup::backupAtRiskPath("/nonexistent-cockatrice-path-xyz", backupRoot.path(), nullptr));
|
||||
QStringList locations;
|
||||
QString sessionDir;
|
||||
EXPECT_FALSE(UserDataBackup::backupAtRiskPaths({QDir(backupRoot.path()).absoluteFilePath("does-not-exist.cod")},
|
||||
backupRoot.path(), &locations, &sessionDir));
|
||||
EXPECT_TRUE(locations.isEmpty());
|
||||
EXPECT_TRUE(sessionDir.isEmpty());
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, BackupPathsShareSingleSessionDir)
|
||||
|
|
@ -241,7 +249,7 @@ TEST(UserDataBackup, ListSessionsFindsRecentAndPrunesOld)
|
|||
// Newest first
|
||||
EXPECT_EQ(sessions.first(), secondSession);
|
||||
|
||||
const QString recent = UserDataBackup::findRecentBackupSession(backupRoot.path(), 24);
|
||||
const QString recent = UserDataBackup::findRecentUsableBackupSession(backupRoot.path(), {sourceDir.path()}, 24);
|
||||
EXPECT_EQ(recent, secondSession);
|
||||
|
||||
const QStringList reusedLocations = UserDataBackup::listSessionBackupLocations(recent);
|
||||
|
|
@ -262,7 +270,10 @@ TEST(UserDataBackup, FindRecentReturnsEmptyWhenNone)
|
|||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
EXPECT_TRUE(UserDataBackup::listBackupSessions(backupRoot.path()).isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentBackupSession(backupRoot.path(), 24).isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentUsableBackupSession(backupRoot.path(), {backupRoot.path() + "/missing"}, 24)
|
||||
.isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentCoveringBackupSession(backupRoot.path(), {backupRoot.path() + "/missing"}, 24)
|
||||
.isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::listSessionBackupLocations(backupRoot.path() + "/missing").isEmpty());
|
||||
EXPECT_FALSE(UserDataBackup::isBackupSessionComplete(backupRoot.path() + "/missing"));
|
||||
// Pruning an empty root must not fail
|
||||
|
|
@ -292,7 +303,10 @@ TEST(UserDataBackup, EmptySessionsAreSkippedAndPruned)
|
|||
// An empty session from a failed attempt must never block a retry
|
||||
EXPECT_TRUE(UserDataBackup::listSessionBackupLocations(emptySession).isEmpty());
|
||||
EXPECT_FALSE(UserDataBackup::isBackupSessionComplete(emptySession));
|
||||
EXPECT_TRUE(UserDataBackup::findRecentBackupSession(backupRoot.path(), 24).isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentUsableBackupSession(backupRoot.path(), {backupRoot.path() + "/missing"}, 24)
|
||||
.isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentCoveringBackupSession(backupRoot.path(), {backupRoot.path() + "/missing"}, 24)
|
||||
.isEmpty());
|
||||
UserDataBackup::pruneOldBackupSessions(backupRoot.path(), 5);
|
||||
EXPECT_FALSE(QDir(emptySession).exists());
|
||||
}
|
||||
|
|
@ -321,7 +335,7 @@ TEST(UserDataBackup, SuccessfulBackupIsMarkedComplete)
|
|||
}
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, IncompleteMarkerReportsIncompleteButRemainsReusable)
|
||||
TEST(UserDataBackup, IncompleteMarkerIsNotUsableButRemainsCovering)
|
||||
{
|
||||
QTemporaryDir sourceDir;
|
||||
QTemporaryDir backupRoot;
|
||||
|
|
@ -342,11 +356,13 @@ TEST(UserDataBackup, IncompleteMarkerReportsIncompleteButRemainsReusable)
|
|||
marker.close();
|
||||
|
||||
EXPECT_FALSE(UserDataBackup::isBackupSessionComplete(sessionDir));
|
||||
// Still non-empty, so throttling reuses it and the caller warns instead of copying again
|
||||
EXPECT_EQ(UserDataBackup::findRecentBackupSession(backupRoot.path(), 24), sessionDir);
|
||||
// Incomplete sessions are skipped by the usable search but still found by the
|
||||
// covering search, so a failed copy falls back to the salvaged content.
|
||||
EXPECT_TRUE(UserDataBackup::findRecentUsableBackupSession(backupRoot.path(), {sourceDir.path()}, 24).isEmpty());
|
||||
EXPECT_EQ(UserDataBackup::findRecentCoveringBackupSession(backupRoot.path(), {sourceDir.path()}, 24), sessionDir);
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, LegacySessionWithoutMarkerCountsAsComplete)
|
||||
TEST(UserDataBackup, SessionWithoutMarkerCountsAsIncomplete)
|
||||
{
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
|
|
@ -357,8 +373,12 @@ TEST(UserDataBackup, LegacySessionWithoutMarkerCountsAsComplete)
|
|||
ASSERT_TRUE(deck.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
deck.write("<cockatrice_deck version=\"1\"/>\n");
|
||||
deck.close();
|
||||
EXPECT_TRUE(UserDataBackup::isBackupSessionComplete(sessionDir));
|
||||
EXPECT_EQ(UserDataBackup::findRecentBackupSession(backupRoot.path(), 24), sessionDir);
|
||||
EXPECT_FALSE(UserDataBackup::isBackupSessionComplete(sessionDir));
|
||||
// No source manifest either, so neither search reuses it.
|
||||
EXPECT_TRUE(UserDataBackup::findRecentUsableBackupSession(backupRoot.path(), {backupRoot.path() + "/missing"}, 24)
|
||||
.isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentCoveringBackupSession(backupRoot.path(), {backupRoot.path() + "/missing"}, 24)
|
||||
.isEmpty());
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, FindRecentIgnoresExpiredSessions)
|
||||
|
|
@ -375,7 +395,8 @@ TEST(UserDataBackup, FindRecentIgnoresExpiredSessions)
|
|||
QStringList locations;
|
||||
QString sessionDir;
|
||||
ASSERT_TRUE(UserDataBackup::backupAtRiskPaths({sourceDir.path()}, backupRoot.path(), &locations, &sessionDir));
|
||||
EXPECT_TRUE(UserDataBackup::findRecentBackupSession(backupRoot.path(), 0).isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentUsableBackupSession(backupRoot.path(), {sourceDir.path()}, 0).isEmpty());
|
||||
EXPECT_TRUE(UserDataBackup::findRecentCoveringBackupSession(backupRoot.path(), {sourceDir.path()}, 0).isEmpty());
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, EstimateBackupSize)
|
||||
|
|
@ -414,7 +435,7 @@ TEST(UserDataBackup, SessionCoversSourcesRequiresManifestMatch)
|
|||
EXPECT_FALSE(UserDataBackup::sessionCoversSources(sessionDir, {sourceB.path()}));
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, LegacySessionWithoutManifestIsNotCovering)
|
||||
TEST(UserDataBackup, SessionWithoutManifestIsNotCovering)
|
||||
{
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
|
|
@ -539,14 +560,16 @@ TEST(UserDataBackup, CopyDirectoryDoesNotSilentlyEmptyDirSymlink)
|
|||
}
|
||||
}
|
||||
|
||||
TEST(UserDataBackup, SingularBackupClearsOutParamOnTotalFailure)
|
||||
TEST(UserDataBackup, PluralBackupClearsOutParamsOnTotalFailure)
|
||||
{
|
||||
QTemporaryDir backupRoot;
|
||||
ASSERT_TRUE(backupRoot.isValid());
|
||||
QString location = QStringLiteral("stale");
|
||||
QStringList locations = {QStringLiteral("stale")};
|
||||
QString sessionDir = QStringLiteral("stale");
|
||||
const QString missing = QDir(backupRoot.path()).absoluteFilePath("does-not-exist.cod");
|
||||
EXPECT_FALSE(UserDataBackup::backupAtRiskPath(missing, backupRoot.path(), &location));
|
||||
EXPECT_TRUE(location.isEmpty());
|
||||
EXPECT_FALSE(UserDataBackup::backupAtRiskPaths({missing}, backupRoot.path(), &locations, &sessionDir));
|
||||
EXPECT_TRUE(locations.isEmpty());
|
||||
EXPECT_TRUE(sessionDir.isEmpty());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue