mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 17:15:09 -07:00
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
CardPictureLoaderLocal compared the full file name (including extension) of the custom-folder candidate against the extension-stripped base name of each directory entry, so a CUSTOM subfolder image like pics/CUSTOM/poker/1 of Hearts.png never matched and the card fell through to the network. Compare the complete base names instead. Add matcher tests covering CUSTOM subfolder resolution. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
221 lines
No EOL
7.4 KiB
C++
221 lines
No EOL
7.4 KiB
C++
#include "client/settings/cache_settings.h"
|
|
#include "interface/card_picture_loader/card_picture_loader_local.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
#include <QColor>
|
|
#include <QCoreApplication>
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QImage>
|
|
#include <QImageWriter>
|
|
#include <QLoggingCategory>
|
|
#include <QStandardPaths>
|
|
#include <QTemporaryDir>
|
|
#include <libcockatrice/card/lazy_properties_hash.h>
|
|
#include <libcockatrice/card/printing/exact_card.h>
|
|
#include <libcockatrice/card/set/card_set.h>
|
|
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
|
|
#include <libcockatrice/settings/paths_settings.h>
|
|
|
|
namespace
|
|
{
|
|
|
|
/**
|
|
* @brief Builds an ExactCard with the requested identity fields.
|
|
*
|
|
* Mirrors how the client constructs cards: the set short name feeds tryLoad()'s
|
|
* setName, and the "num" printing property feeds the collector number.
|
|
*/
|
|
ExactCard cardFor(const QString &name, const QString &setShortName, const QString &collectorNumber)
|
|
{
|
|
CardSetPtr set;
|
|
if (!setShortName.isEmpty()) {
|
|
set = CardSet::newInstance(new NoopCardSetPriorityController(), setShortName, setShortName);
|
|
}
|
|
|
|
LazyPropertiesHash properties;
|
|
if (!collectorNumber.isEmpty()) {
|
|
properties.insert("num", collectorNumber);
|
|
}
|
|
|
|
return ExactCard(CardInfo::newInstance(name), PrintingInfo(set, properties));
|
|
}
|
|
|
|
class LocalMatcherTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
QTemporaryDir tempDir; ///< Sandboxed "pics" root for every test.
|
|
CardPictureLoaderLocal *loader = nullptr; ///< Constructed per test against the sandboxed paths.
|
|
|
|
QString picsPath() const
|
|
{
|
|
return tempDir.path() + "/pics";
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
// The loader ctor snapshots the global picture paths once, so point them at the
|
|
// sandbox before constructing it.
|
|
SettingsCache::instance().paths().setPicsPath(picsPath());
|
|
SettingsCache::instance().paths().setCustomPicsPath(picsPath() + "/CUSTOM/");
|
|
|
|
loader = new CardPictureLoaderLocal(nullptr);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delete loader;
|
|
loader = nullptr;
|
|
}
|
|
|
|
/**
|
|
* @brief Destroys and rebuilds the loader so the CUSTOM index is re-scanned.
|
|
*
|
|
* The CUSTOM-folder index is snapshotted at construction; rebuild it after
|
|
* writing files so freshly placed images are discoverable.
|
|
*/
|
|
void rebuildLoader()
|
|
{
|
|
delete loader;
|
|
loader = new CardPictureLoaderLocal(nullptr);
|
|
}
|
|
|
|
/**
|
|
* @brief Writes a valid 1x1 PNG under the sandboxed pics path.
|
|
*/
|
|
void writePngUnderPics(const QString &relativePath, const QColor &color = Qt::red)
|
|
{
|
|
const QString fullPath = picsPath() + "/" + relativePath;
|
|
ASSERT_TRUE(QDir().mkpath(QFileInfo(fullPath).absolutePath()));
|
|
|
|
QImage image(1, 1, QImage::Format_RGB32);
|
|
image.fill(color);
|
|
|
|
QImageWriter writer(fullPath, "PNG");
|
|
ASSERT_TRUE(writer.write(image));
|
|
}
|
|
};
|
|
|
|
TEST_F(LocalMatcherTest, ExactMatchBareFileWinsOverSuffixedCompanion)
|
|
{
|
|
writePngUnderPics("downloadedPics/TestCard.png");
|
|
writePngUnderPics("downloadedPics/TestCard (1).png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "", ""));
|
|
|
|
EXPECT_FALSE(image.isNull()) << "The bare TestCard.png must be picked over its suffixed companion";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, SuffixedFileWithoutExactMatchIsNotLoaded)
|
|
{
|
|
// The pre-refactor prefix match would have accepted "TestCard (1).png" for "TestCard".
|
|
writePngUnderPics("downloadedPics/TestCard (1).png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "", ""));
|
|
|
|
EXPECT_TRUE(image.isNull()) << "A suffixed file must not satisfy an exact card-name lookup";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, SetFolderLookupIgnoresSuffixedFiles)
|
|
{
|
|
writePngUnderPics("M10/TestCard (1).png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "M10", ""));
|
|
|
|
EXPECT_TRUE(image.isNull()) << "Set-folder lookups must also require an exact name match";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, SetFolderLookupStillResolvesExactFile)
|
|
{
|
|
writePngUnderPics("M10/TestCard.png");
|
|
writePngUnderPics("M10/TestCard (1).png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "M10", ""));
|
|
|
|
EXPECT_FALSE(image.isNull()) << "The exact file in the set folder must still resolve";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, RootDownloadedPicsFallbackResolvesSchemeFilename)
|
|
{
|
|
// Non-set-folder export schemes (Name_Set_Collector) write straight into downloadedPics/.
|
|
writePngUnderPics("downloadedPics/TestCard_M10_1.png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "M10", "1"));
|
|
|
|
EXPECT_FALSE(image.isNull()) << "downloadedPics/TestCard_M10_1.png must resolve via the root fallback";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, RootFallbackResolvesDashSeparatedVariant)
|
|
{
|
|
writePngUnderPics("downloadedPics/TestCard-M10-1.png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "M10", "1"));
|
|
|
|
EXPECT_FALSE(image.isNull()) << "The dash-separated import variant must resolve via the root fallback";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, DownloadedPicsSetSubfolderStillResolves)
|
|
{
|
|
writePngUnderPics("downloadedPics/M10/TestCard_M10_1.png");
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "M10", "1"));
|
|
|
|
EXPECT_FALSE(image.isNull()) << "The set-subfolder export scheme must keep resolving";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, SetFolderCandidateTakesPrecedenceOverRootFallback)
|
|
{
|
|
writePngUnderPics("M10/TestCard_M10_1.png", Qt::red);
|
|
writePngUnderPics("downloadedPics/TestCard_M10_1.png", Qt::blue);
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "M10", "1"));
|
|
|
|
ASSERT_FALSE(image.isNull());
|
|
EXPECT_EQ(image.pixelColor(0, 0), QColor(Qt::red)) << "The set-folder candidate must be preferred";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, CustomSubfolderResolvesExactFile)
|
|
{
|
|
// Images in the CUSTOM folder are indexed by their full file path (extension included),
|
|
// unlike the extension-less candidate paths built from the naming schemes.
|
|
writePngUnderPics("CUSTOM/poker/TestCard.png");
|
|
rebuildLoader();
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "", ""));
|
|
|
|
EXPECT_FALSE(image.isNull()) << "A CUSTOM-folder image in a subdirectory must resolve";
|
|
}
|
|
|
|
TEST_F(LocalMatcherTest, CustomSubfolderIgnoresSuffixedFiles)
|
|
{
|
|
writePngUnderPics("CUSTOM/poker/TestCard (1).png");
|
|
rebuildLoader();
|
|
|
|
const QImage image = loader->tryLoad(cardFor("TestCard", "", ""));
|
|
|
|
EXPECT_TRUE(image.isNull()) << "A suffixed CUSTOM-folder file must not satisfy an exact name lookup";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Redirect SettingsCache reads/writes (app-data location) away from the real user profile.
|
|
QStandardPaths::setTestModeEnabled(true);
|
|
|
|
// Some CI containers run as a uid without a passwd entry (e.g. GitHub's docker
|
|
// runner), so HOME resolves to "/" and the test-mode qttest data dir cannot be
|
|
// created. SettingsCache's QSettings then silently drops every write, reads come
|
|
// back empty, and the paths the loader searches are "". Give the test a writable
|
|
// HOME for the duration of the run so settings behave like on a normal machine.
|
|
QTemporaryDir home;
|
|
if (home.isValid()) {
|
|
qputenv("HOME", home.path().toLocal8Bit());
|
|
}
|
|
|
|
QCoreApplication app(argc, argv);
|
|
QLoggingCategory::setFilterRules("card_picture_loader.*=false\nsettings_cache.*=false");
|
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
} |