mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
[Theme] Carry Default theme directory customizations over to System (#7385)
* [Theme] Carry Default theme directory customizations over to System The Default -> System rename rewrote the stored theme name but left the user profile's themes/Default directory behind, so anyone who had customized zone graphics, stylesheets or palettes there silently lost them. Migrate the directory alongside the name. Rename it when System does not exist yet, otherwise merge file by file without overwriting what System already has, then drop the legacy directory. Users who already ran the rename get their customizations back on the next start, and a hand-made Default folder of a user on another theme is left untouched. * [Tests] Give theme_migration_test an explicit main and gate it on WITH_CLIENT The build machines that cannot find GTest fall back to the downloaded googletest, which links the bare gtest library without gtest_main. Every other test defines its own main() for that reason; this one did not, so Debian 13, Fedora 44 and Ubuntu 26.04 failed to link it. The test compiles client sources, so -DTEST=1 -DWITH_CLIENT=0 has no way to build it. Gate the subdirectory on WITH_CLIENT like the other client-source test. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
add65caae5
commit
014983b5bc
6 changed files with 299 additions and 1 deletions
|
|
@ -178,6 +178,11 @@ add_subdirectory(movecard_tests)
|
|||
add_subdirectory(oracle)
|
||||
add_subdirectory(settings)
|
||||
|
||||
# theme_migration_test compiles client sources, whose headers are only reachable from a client build.
|
||||
if(WITH_CLIENT)
|
||||
add_subdirectory(theme_migration)
|
||||
endif()
|
||||
|
||||
# picture_loader_benchmark links libcockatrice_settings, which only exists when
|
||||
# a client/UI-capable target is being built.
|
||||
if(WITH_ORACLE OR WITH_CLIENT)
|
||||
|
|
|
|||
|
|
@ -542,6 +542,34 @@ TEST_F(SettingsMigrationTest, PartialMigrationOnlyMigratesPresentKeys)
|
|||
ASSERT_FALSE(fileExists("chat.ini"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, ThemeNameDefaultIsRewrittenToSystem)
|
||||
{
|
||||
{
|
||||
QSettings g(settingsPath + "global.ini", QSettings::IniFormat);
|
||||
g.setValue("theme/name", "Default");
|
||||
g.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateSettingsFromGlobalIni(settingsPath));
|
||||
|
||||
ASSERT_TRUE(fileExists("appearance.ini"));
|
||||
ASSERT_EQ(readFromIni("appearance.ini", "appearance/themeName"), QVariant("System"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, OtherThemeNamesAreLeftAlone)
|
||||
{
|
||||
{
|
||||
QSettings g(settingsPath + "global.ini", QSettings::IniFormat);
|
||||
g.setValue("theme/name", "VelvetMarble");
|
||||
g.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateSettingsFromGlobalIni(settingsPath));
|
||||
|
||||
ASSERT_TRUE(fileExists("appearance.ini"));
|
||||
ASSERT_EQ(readFromIni("appearance.ini", "appearance/themeName"), QVariant("VelvetMarble"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, KeyMapTranslationIsCorrect)
|
||||
{
|
||||
{
|
||||
|
|
|
|||
30
tests/theme_migration/CMakeLists.txt
Normal file
30
tests/theme_migration/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
add_executable(
|
||||
theme_migration_test
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/interface/theme_manager.cpp
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/interface/theme_config.cpp
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/interface/pixel_map_generator.cpp
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/client/settings/cache_settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/client/settings/card_counter_settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/client/settings/shortcuts_settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/cockatrice/src/client/network/update/client/release_channel.cpp
|
||||
${VERSION_STRING_CPP}
|
||||
theme_migration_test.cpp
|
||||
)
|
||||
target_include_directories(theme_migration_test PRIVATE ${CMAKE_SOURCE_DIR}/cockatrice/src)
|
||||
|
||||
target_link_libraries(
|
||||
theme_migration_test
|
||||
libcockatrice_settings
|
||||
libcockatrice_network
|
||||
libcockatrice_network_server_remote
|
||||
libcockatrice_protocol
|
||||
Threads::Threads
|
||||
${GTEST_BOTH_LIBRARIES}
|
||||
${TEST_QT_MODULES}
|
||||
)
|
||||
|
||||
add_test(NAME theme_migration_test COMMAND theme_migration_test)
|
||||
|
||||
if(NOT GTEST_FOUND)
|
||||
add_dependencies(theme_migration_test gtest)
|
||||
endif()
|
||||
154
tests/theme_migration/theme_migration_test.cpp
Normal file
154
tests/theme_migration/theme_migration_test.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#include "interface/theme_manager.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Owned by main.cpp in the real application; pixel_map_generator.cpp references
|
||||
// it, so the test binary needs a definition even though it never renders.
|
||||
ThemeManager *themeManager = nullptr;
|
||||
|
||||
namespace
|
||||
{
|
||||
const QString LEGACY = QStringLiteral("Default");
|
||||
const QString CURRENT = QStringLiteral("System");
|
||||
|
||||
// Writes content at <themesPath>/<theme>/<relativePath>, creating parents.
|
||||
void writeThemeFile(const QString &themesPath,
|
||||
const QString &theme,
|
||||
const QString &relativePath,
|
||||
const QByteArray &content)
|
||||
{
|
||||
const QString filePath = QDir(QDir(themesPath).absoluteFilePath(theme)).absoluteFilePath(relativePath);
|
||||
ASSERT_TRUE(QDir().mkpath(QFileInfo(filePath).absolutePath()));
|
||||
QFile file(filePath);
|
||||
ASSERT_TRUE(file.open(QIODevice::WriteOnly));
|
||||
file.write(content);
|
||||
file.close();
|
||||
}
|
||||
|
||||
QByteArray readThemeFile(const QString &themesPath, const QString &theme, const QString &relativePath)
|
||||
{
|
||||
QFile file(QDir(QDir(themesPath).absoluteFilePath(theme)).absoluteFilePath(relativePath));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return QByteArray();
|
||||
}
|
||||
return file.readAll();
|
||||
}
|
||||
|
||||
class ThemeMigrationTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
QTemporaryDir tmp;
|
||||
QString themesPath;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
themesPath = QDir(tmp.path()).absoluteFilePath("themes");
|
||||
ASSERT_TRUE(QDir().mkpath(themesPath));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(ThemeMigrationTest, NoLegacyDirectoryIsANoOp)
|
||||
{
|
||||
writeThemeFile(themesPath, CURRENT, "theme.cfg", "[Appearance]\n");
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, CURRENT);
|
||||
|
||||
EXPECT_TRUE(QDir(QDir(themesPath).absoluteFilePath(CURRENT)).exists());
|
||||
EXPECT_TRUE(QDir(QDir(themesPath).absoluteFilePath(LEGACY)).exists() == false);
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "theme.cfg").toStdString(), std::string("[Appearance]\n"));
|
||||
}
|
||||
|
||||
TEST_F(ThemeMigrationTest, RenamesDirectoryWhenTargetIsFree)
|
||||
{
|
||||
// A customised "Default": stylesheet, zone graphics in a subdirectory and a
|
||||
// palette, i.e. everything a user is liable to have personalised.
|
||||
writeThemeFile(themesPath, LEGACY, "style.css", "QPushButton { color: red; }");
|
||||
writeThemeFile(themesPath, LEGACY, "zones/handzone.png", "handzone-bytes");
|
||||
writeThemeFile(themesPath, LEGACY, "zones/tablezone-dark.svg", "tablezone-bytes");
|
||||
writeThemeFile(themesPath, LEGACY, "palette-dark.toml", "palette");
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, LEGACY);
|
||||
|
||||
EXPECT_FALSE(QDir(QDir(themesPath).absoluteFilePath(LEGACY)).exists());
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "style.css").toStdString(),
|
||||
std::string("QPushButton { color: red; }"));
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "zones/handzone.png").toStdString(), std::string("handzone-bytes"));
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "zones/tablezone-dark.svg").toStdString(),
|
||||
std::string("tablezone-bytes"));
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "palette-dark.toml").toStdString(), std::string("palette"));
|
||||
}
|
||||
|
||||
TEST_F(ThemeMigrationTest, RenamesForAnAlreadyMigratedUserWhoseDirectoryWasLeftBehind)
|
||||
{
|
||||
// The settings value was rewritten to "System" on an earlier run, but the
|
||||
// customised directory is still called "Default". The customisations are
|
||||
// still reachable and must still be carried over.
|
||||
writeThemeFile(themesPath, LEGACY, "style.css", "legacy-css");
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, CURRENT);
|
||||
|
||||
EXPECT_FALSE(QDir(QDir(themesPath).absoluteFilePath(LEGACY)).exists());
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "style.css").toStdString(), std::string("legacy-css"));
|
||||
}
|
||||
|
||||
TEST_F(ThemeMigrationTest, MergesIntoAnExistingTargetWithoutOverwriting)
|
||||
{
|
||||
// Post-rename re-customisation: "System" already exists and holds newer
|
||||
// files. Those win; the legacy directory only fills the gaps.
|
||||
writeThemeFile(themesPath, CURRENT, "style.css", "newer-css");
|
||||
writeThemeFile(themesPath, CURRENT, "zones/handzone.png", "newer-handzone");
|
||||
writeThemeFile(themesPath, LEGACY, "style.css", "legacy-css");
|
||||
writeThemeFile(themesPath, LEGACY, "zones/handzone.png", "legacy-handzone");
|
||||
writeThemeFile(themesPath, LEGACY, "zones/stackzone.png", "legacy-stackzone");
|
||||
writeThemeFile(themesPath, LEGACY, "palette-light.toml", "legacy-palette");
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, CURRENT);
|
||||
|
||||
EXPECT_FALSE(QDir(QDir(themesPath).absoluteFilePath(LEGACY)).exists());
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "style.css").toStdString(), std::string("newer-css"));
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "zones/handzone.png").toStdString(), std::string("newer-handzone"));
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "zones/stackzone.png").toStdString(), std::string("legacy-stackzone"));
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "palette-light.toml").toStdString(), std::string("legacy-palette"));
|
||||
}
|
||||
|
||||
TEST_F(ThemeMigrationTest, LeavesAnotherThemesDefaultDirectoryAlone)
|
||||
{
|
||||
// A user on an unrelated theme may legitimately have made a "Default"
|
||||
// folder of their own; it must not be folded into "System".
|
||||
writeThemeFile(themesPath, LEGACY, "style.css", "unrelated");
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, QStringLiteral("Leather"));
|
||||
|
||||
EXPECT_TRUE(QDir(QDir(themesPath).absoluteFilePath(LEGACY)).exists());
|
||||
EXPECT_EQ(readThemeFile(themesPath, LEGACY, "style.css").toStdString(), std::string("unrelated"));
|
||||
EXPECT_FALSE(QDir(QDir(themesPath).absoluteFilePath(CURRENT)).exists());
|
||||
}
|
||||
|
||||
TEST_F(ThemeMigrationTest, IsIdempotent)
|
||||
{
|
||||
writeThemeFile(themesPath, LEGACY, "style.css", "css");
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, LEGACY);
|
||||
ASSERT_EQ(readThemeFile(themesPath, CURRENT, "style.css").toStdString(), std::string("css"));
|
||||
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, CURRENT);
|
||||
ThemeManager::migrateLegacyThemeDir(themesPath, LEGACY);
|
||||
|
||||
EXPECT_EQ(readThemeFile(themesPath, CURRENT, "style.css").toStdString(), std::string("css"));
|
||||
}
|
||||
|
||||
// Every test brings its own main(): GTest is not guaranteed to be found on the build machines, and
|
||||
// the fallback then links the bare gtest library without gtest_main.
|
||||
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