[PictureLoader] Improve image negotiation, cache recovery and redirects (#7703)

* Fix picture-loader cache formats, redirects and offline queue handling

* Format picture-loader changes using project style

* Address review: name decoded image fields, test cancelled requests

Use designated initializers for DecodedImage so a field reorder cannot
silently swap the image and its error string.

Extract the cancelled-request sweep shared by the dispatcher and the
single-request path into takeCancelledRequests(), and cover it: one status
retirement per vanished work item, in queue order, with live entries kept.

---------

Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
This commit is contained in:
Bryce! 2026-11-08 13:02:07 -05:00 • committed by GitHub
parent 078d0858a3
commit 1ed7e45357
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 509 additions and 122 deletions

View file

@ -0,0 +1,85 @@
#include "../cockatrice/src/interface/card_picture_loader/card_picture_loader_request_queue.h"
#include <QObject>
#include <QPointer>
#include <QQueue>
#include <QUrl>
#include <gtest/gtest.h>
namespace
{
using TestRequest = QPair<QUrl, QPointer<QObject>>;
QUrl url(int index)
{
return QUrl(QStringLiteral("https://cards.example/%1").arg(index));
}
TestRequest live(int index, QObject &owner)
{
return qMakePair(url(index), QPointer<QObject>(&owner));
}
TestRequest cancelled(int index)
{
return qMakePair(url(index), QPointer<QObject>());
}
QList<QUrl> queuedUrls(const QQueue<TestRequest> &queue)
{
QList<QUrl> urls;
for (const auto &entry : queue) {
urls.append(entry.first);
}
return urls;
}
} // namespace
TEST(PictureLoaderRequestQueue, EmptyQueueHasNothingToRetire)
{
QQueue<TestRequest> queue;
EXPECT_TRUE(takeCancelledRequests(queue).isEmpty());
EXPECT_TRUE(queue.isEmpty());
}
TEST(PictureLoaderRequestQueue, LiveRequestsStayQueuedInOrder)
{
QObject first;
QObject second;
QQueue<TestRequest> queue;
queue.enqueue(live(1, first));
queue.enqueue(live(2, second));
EXPECT_TRUE(takeCancelledRequests(queue).isEmpty());
EXPECT_EQ(queuedUrls(queue), (QList<QUrl>{url(1), url(2)}));
}
TEST(PictureLoaderRequestQueue, CancelledRequestsAreRetiredInQueueOrder)
{
QObject owner;
QQueue<TestRequest> queue;
queue.enqueue(live(1, owner));
queue.enqueue(cancelled(2));
queue.enqueue(live(3, owner));
queue.enqueue(cancelled(4));
queue.enqueue(cancelled(5));
// Exactly one status entry is retired per vanished work item, in queue order.
EXPECT_EQ(takeCancelledRequests(queue), (QList<QUrl>{url(2), url(4), url(5)}));
// The surviving requests keep their relative order.
EXPECT_EQ(queuedUrls(queue), (QList<QUrl>{url(1), url(3)}));
// Nothing is retired twice on a later sweep.
EXPECT_TRUE(takeCancelledRequests(queue).isEmpty());
EXPECT_EQ(queuedUrls(queue), (QList<QUrl>{url(1), url(3)}));
}
TEST(PictureLoaderRequestQueue, AllCancelledEmptiesTheQueue)
{
QQueue<TestRequest> queue;
queue.enqueue(cancelled(1));
queue.enqueue(cancelled(2));
EXPECT_EQ(takeCancelledRequests(queue), (QList<QUrl>{url(1), url(2)}));
EXPECT_TRUE(queue.isEmpty());
}