mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-10 20:13:24 -08:00
Merge upstream/master into scryfall-tagger-tags
Conflict resolution: keep both the tag database and the upstream database writer in the oracle test targets and both includes in the importer test; restore the tags.svg resource entry that the rerere replay dropped. Upstream's icon rules also landed mid-branch: use loadThemedIcon() for the tag quick-filter button and re-author tags.svg as a 24x24 currentColor icon so IconAssets.AssetsFollowTheRules passes.
This commit is contained in:
commit
f154335f5c
553 changed files with 21045 additions and 11980 deletions
|
|
@ -42,6 +42,7 @@ cockatrice_add_test(
|
|||
../../oracle/src/parsehelpers.cpp
|
||||
../../oracle/src/raw_json_scanner.cpp
|
||||
../../oracle/src/tag_database.cpp
|
||||
../../oracle/src/database_writer.cpp
|
||||
oracle_importer_test.cpp
|
||||
${_ORACLE_COMPRESSION_SOURCES}
|
||||
LIBS
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
#include "../../oracle/src/database_writer.h"
|
||||
#include "../../oracle/src/oracleimporter.h"
|
||||
#include "../../oracle/src/raw_json_scanner.h"
|
||||
#include "../../oracle/src/tag_database.h"
|
||||
#include "../support/scoped_file_size_limit.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <QByteArray>
|
||||
#include <QChar>
|
||||
#include <QDate>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
|
@ -21,6 +24,7 @@
|
|||
#include <QRegularExpressionMatch>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QUuid>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
|
|
@ -1293,6 +1297,208 @@ TEST_F(OracleImporterTest, StartImportAppliesLocalizedData)
|
|||
ASSERT_EQ(result->getLocalizedText("pt"), "Raio causa 3 de dano a qualquer alvo.");
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
QByteArray refreshSource(const QString &name = "New Card")
|
||||
{
|
||||
QJsonObject card{{"name", name}, {"text", "Rules text."}, {"layout", "normal"},
|
||||
{"type", "Artifact"}, {"number", "1"}, {"rarity", "common"}};
|
||||
QJsonObject set{{"code", "TST"}, {"name", "Test Set"}, {"type", "expansion"}, {"cards", QJsonArray{card}}};
|
||||
return QJsonDocument(QJsonObject{{"data", QJsonObject{{"TST", set}}}}).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(OracleImporterTest, FailedLazyParseKeepsInstalledDatabase)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = dir.filePath("cards.xml");
|
||||
QFile original(path);
|
||||
ASSERT_TRUE(original.open(QIODevice::WriteOnly));
|
||||
ASSERT_EQ(original.write("existing database"), 17);
|
||||
original.close();
|
||||
|
||||
// The metadata scanner skips numeric values without converting them. Qt's
|
||||
// per-set parser rejects an overflowing number after selection succeeds.
|
||||
auto badSet = QJsonDocument::fromJson(refreshSource()).object().value("data").toObject().value("TST").toObject();
|
||||
auto goodSet = badSet;
|
||||
goodSet["code"] = "AAA";
|
||||
goodSet["name"] = "A successful first set";
|
||||
auto badCard = badSet.value("cards").toArray().first().toObject();
|
||||
badCard["number"] = "overflow";
|
||||
badSet["cards"] = QJsonArray{badCard};
|
||||
QByteArray data = QJsonDocument(QJsonObject{{"data", QJsonObject{{"AAA", goodSet}, {"TST", badSet}}}})
|
||||
.toJson(QJsonDocument::Compact);
|
||||
data.replace("\"number\":\"overflow\"", "\"number\":1e9999");
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(data));
|
||||
EXPECT_EQ(importer->startImport(), 0);
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
EXPECT_TRUE(importer->getImportError().contains("TST"));
|
||||
EXPECT_TRUE(importer->getCardList().isEmpty());
|
||||
EXPECT_FALSE(importer->saveToFile(path, {}, {}));
|
||||
ASSERT_TRUE(original.open(QIODevice::ReadOnly));
|
||||
EXPECT_EQ(original.readAll(), "existing database");
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, EmptyImportCannotReplaceInstalledCards)
|
||||
{
|
||||
const QByteArray source = R"({"data":{"TST":{"code":"TST","name":"Test","type":"expansion","cards":[]}}})";
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(source));
|
||||
EXPECT_EQ(importer->startImport(), 0);
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
EXPECT_FALSE(importer->getImportError().isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, MissingSourceBytesCannotProduceSuccessfulImport)
|
||||
{
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource()));
|
||||
const auto selected = importer->getSets();
|
||||
importer->releaseSetData();
|
||||
importer->getSets() = selected;
|
||||
EXPECT_EQ(importer->startImport(), 0);
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
EXPECT_TRUE(importer->getImportError().contains("TST"));
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, CancellationDuringFinalSetCannotSavePartialDatabase)
|
||||
{
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource()));
|
||||
QObject::connect(
|
||||
importer, &OracleImporter::setIndexChanged, importer,
|
||||
[this](int, int, const QString &name) {
|
||||
if (!name.isEmpty()) {
|
||||
importer->cancelImport();
|
||||
}
|
||||
},
|
||||
Qt::DirectConnection);
|
||||
EXPECT_EQ(importer->startImport(), 0);
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
EXPECT_TRUE(importer->getCardList().isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, CancellationBeforeWorkerStartsIsNotLost)
|
||||
{
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource()));
|
||||
importer->prepareImport();
|
||||
importer->cancelImport();
|
||||
EXPECT_EQ(importer->startImport(), 0);
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
importer->prepareImport();
|
||||
EXPECT_EQ(importer->startImport(), 1);
|
||||
EXPECT_TRUE(importer->hasSuccessfulImport());
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, NewImportReplacesPreviousInMemoryCards)
|
||||
{
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource("Old Card")));
|
||||
ASSERT_EQ(importer->startImport(), 1);
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource("New Card")));
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
ASSERT_EQ(importer->startImport(), 1);
|
||||
EXPECT_EQ(importer->getCardList().size(), 1);
|
||||
EXPECT_FALSE(importer->getCardList().contains("Old Card"));
|
||||
EXPECT_TRUE(importer->getCardList().contains("New Card"));
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, FailedSourceLoadCannotReusePreviousSuccessfulImport)
|
||||
{
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource()));
|
||||
ASSERT_EQ(importer->startImport(), 1);
|
||||
ASSERT_FALSE(importer->readSetsFromByteArray("invalid JSON"));
|
||||
EXPECT_FALSE(importer->hasSuccessfulImport());
|
||||
EXPECT_EQ(importer->startImport(), 0);
|
||||
}
|
||||
|
||||
TEST_F(OracleImporterTest, SuccessfulRefreshCommitsCompleteXml)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = dir.filePath("cards.xml");
|
||||
ASSERT_TRUE(importer->readSetsFromByteArray(refreshSource()));
|
||||
ASSERT_EQ(importer->startImport(), 1);
|
||||
importer->releaseSetData();
|
||||
ASSERT_TRUE(importer->saveToFile(path, "test-source", "v1"));
|
||||
QFile file(path);
|
||||
ASSERT_TRUE(file.open(QIODevice::ReadOnly));
|
||||
const QByteArray xml = file.readAll();
|
||||
EXPECT_TRUE(xml.contains("New Card"));
|
||||
EXPECT_TRUE(xml.contains("test-source"));
|
||||
EXPECT_TRUE(xml.contains("</cockatrice_carddatabase>"));
|
||||
}
|
||||
|
||||
TEST(OracleDatabaseWriter, AcceptsCompleteSupportedVersions)
|
||||
{
|
||||
EXPECT_TRUE(OracleDatabase::validateXml(
|
||||
"<?xml version=\"1.0\"?><cockatrice_carddatabase version=\"3\"><cards/></cockatrice_carddatabase>")
|
||||
.isEmpty());
|
||||
EXPECT_TRUE(OracleDatabase::validateXml("<cockatrice_carddatabase version=\"4\"><cards/></cockatrice_carddatabase>")
|
||||
.isEmpty());
|
||||
EXPECT_FALSE(
|
||||
OracleDatabase::validateXml("<cockatrice_carddatabase version=\"5\"><cards/></cockatrice_carddatabase>")
|
||||
.isEmpty());
|
||||
}
|
||||
|
||||
TEST(OracleDatabaseWriter, InvalidDownloadPreservesInstalledBytes)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = dir.filePath("tokens.xml");
|
||||
QFile original(path);
|
||||
ASSERT_TRUE(original.open(QIODevice::WriteOnly));
|
||||
ASSERT_EQ(original.write("installed tokens"), 16);
|
||||
original.close();
|
||||
for (const QByteArray &payload : {QByteArray("<html>server error</html>"),
|
||||
QByteArray("<cockatrice_carddatabase version=\"4\"><cards>"), QByteArray()}) {
|
||||
QString error;
|
||||
EXPECT_FALSE(OracleDatabase::saveXml(path, payload, &error));
|
||||
EXPECT_FALSE(error.isEmpty());
|
||||
ASSERT_TRUE(original.open(QIODevice::ReadOnly));
|
||||
EXPECT_EQ(original.readAll(), "installed tokens");
|
||||
original.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(OracleDatabaseWriter, CompleteDownloadReplacesInstalledBytes)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = dir.filePath("cards.xml");
|
||||
const QByteArray data("<cockatrice_carddatabase version=\"4\"><cards/></cockatrice_carddatabase>");
|
||||
QString error = "previous failure";
|
||||
ASSERT_TRUE(OracleDatabase::saveXml(path, data, &error));
|
||||
EXPECT_TRUE(error.isEmpty());
|
||||
QFile file(path);
|
||||
ASSERT_TRUE(file.open(QIODevice::ReadOnly));
|
||||
EXPECT_EQ(file.readAll(), data);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
TEST(OracleDatabaseWriter, ShortWriteKeepsPreviouslyInstalledDatabase)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = dir.filePath("cards.xml");
|
||||
QFile original(path);
|
||||
ASSERT_TRUE(original.open(QIODevice::WriteOnly));
|
||||
ASSERT_EQ(original.write("installed database"), 18);
|
||||
original.close();
|
||||
const QByteArray data = "<cockatrice_carddatabase version=\"4\"><!--" + QByteArray(16384, 'x') +
|
||||
"--><cards/></cockatrice_carddatabase>";
|
||||
// A real filesystem write failure: the soft file-size cap makes the writer's own
|
||||
// flush fail without filling a disk. The cap is scoped rather than forked, because a
|
||||
// death-test child under RLIMIT_FSIZE is killed by SIGSEGV on macOS.
|
||||
test_support::ScopedFileSizeLimit fileSizeCap(128);
|
||||
ASSERT_TRUE(fileSizeCap.isValid());
|
||||
|
||||
QString error;
|
||||
EXPECT_FALSE(OracleDatabase::saveXml(path, data, &error));
|
||||
EXPECT_FALSE(error.isEmpty());
|
||||
|
||||
ASSERT_TRUE(original.open(QIODevice::ReadOnly));
|
||||
EXPECT_EQ(original.readAll(), "installed database");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue