initial commit

This commit is contained in:
2026-09-21 13:41:40 +09:00
commit 855c7328df
411 changed files with 85352 additions and 0 deletions
+258
View File
@@ -0,0 +1,258 @@
#include "common/font_contract.h"
#include "common/ipc.h"
#include "common/worker_font_client.h"
#include "common/worker_runtime.h"
#include <QElapsedTimer>
#include <QLocalServer>
#include <QLocalSocket>
#include <QSignalSpy>
#include <QTimer>
#include <QtTest>
#include <limits>
using namespace docview;
namespace {
const QString fontId(32, 'a');
QCborMap mapPayload(qint64 id = 1) {
return {{"fontRequestId", id}, {"faceLocal", QByteArray("Helvetica")},
{"weight", 400}, {"italic", false},
{"charset", 0}, {"pitchFamily", 0}};
}
QCborMap selected(qint64 id = 1) {
return {{"fontRequestId", id},
{"found", true},
{"fontId", fontId},
{"actualFaceLocal", QByteArray("Actual Face")},
{"charset", 0},
{"size", 4},
{"sha256", QByteArray(32, 'x')},
{"faceIndex", 0},
{"substituted", true}};
}
QCborMap parentRequest() {
return {{"protocolVersion", 1}, {"sessionId", "font-session"}, {"generation", 3},
{"requestId", 7}, {"operation", "render"}, {"payload", QCborMap{{"page", 1}}}};
}
QCborMap resultReply(QCborMap request, QCborMap result) {
request.remove(QStringLiteral("payload"));
request.insert(QStringLiteral("result"), result);
return request;
}
struct ChannelPair {
QLocalServer server;
WorkerRuntime runtime;
QLocalSocket *peer = nullptr;
bool open() {
server.setSocketOptions(QLocalServer::UserAccessOption);
if (!server.listen("font-contract-" + QUuid::createUuid().toString(QUuid::Id128)))
return false;
auto socket = std::make_unique<QLocalSocket>();
socket->connectToServer(server.fullServerName());
if (!socket->waitForConnected(1000) || !server.waitForNewConnection(1000))
return false;
peer = server.nextPendingConnection();
runtime.transport = std::move(socket);
return peer;
}
};
} // namespace
class FontContractTest : public QObject {
Q_OBJECT
private slots:
void strictMapSchema() {
const auto request = mapPayload();
QVERIFY(validateFontPayload("font.map", request));
auto emptyName = request;
emptyName.insert(QStringLiteral("faceLocal"), QByteArray{});
QVERIFY(validateFontPayload("font.map", emptyName));
const QList<QPair<QString, QCborValue>> bad{{"faceLocal", "string coercion"},
{"faceLocal", QByteArray(257, 'a')},
{"faceLocal", QByteArray("a\0b", 3)},
{"weight", -1},
{"weight", 1001},
{"weight", 400.0},
{"italic", 1},
{"charset", 130},
{"charset", "0"},
{"pitchFamily", 3},
{"pitchFamily", 0x60},
{"fontRequestId", 0},
{"fontRequestId", 1.0},
{"path", "/secret/font.ttf"}};
for (const auto &[field, value] : bad) {
auto invalid = request;
invalid.insert(field, value);
QVERIFY2(!validateFontPayload("font.map", invalid), qPrintable(field));
}
auto missing = request;
missing.remove(QStringLiteral("charset"));
QVERIFY(!validateFontPayload("font.map", missing));
QVERIFY(!validateFontPayload("font.list", request));
}
void boundedReadSchema() {
QCborMap request{
{"fontRequestId", 2}, {"fontId", fontId}, {"offset", 0}, {"length", MaxFontReadBytes}};
QVERIFY(validateFontPayload("font.read", request));
for (const auto badLength :
{qint64(-1), qint64(0), qint64(MaxFontReadBytes + 1), std::numeric_limits<qint64>::max()}) {
auto invalid = request;
invalid.insert(QStringLiteral("length"), badLength);
QVERIFY(!validateFontPayload("font.read", invalid));
}
for (const auto badOffset :
{qint64(-1), MaxFontSnapshotBytes - 1, std::numeric_limits<qint64>::max()}) {
auto invalid = request;
invalid.insert(QStringLiteral("offset"), badOffset);
QVERIFY(!validateFontPayload("font.read", invalid));
}
request.insert(QStringLiteral("offset"), MaxFontSnapshotBytes - MaxFontReadBytes);
QVERIFY(validateFontPayload("font.read", request));
request.insert(QStringLiteral("fontId"), fontId.toUpper());
QVERIFY(!validateFontPayload("font.read", request));
QVERIFY(validateFontPayload("font.close", {{"fontRequestId", 3}, {"fontId", fontId}}));
}
void selectedMetadataAndReadRepliesAreExact() {
const auto request = mapPayload();
QVERIFY(validateFontResult("font.map", request, selected()));
auto offset = selected();
offset.remove(QStringLiteral("faceIndex"));
offset.insert(QStringLiteral("faceOffset"), 3);
QVERIFY(validateFontResult("font.map", request, offset));
offset.insert(QStringLiteral("faceOffset"), 4);
QVERIFY(!validateFontResult("font.map", request, offset));
const QList<QPair<QString, QCborValue>> bad{{"fontRequestId", 2},
{"fontId", QByteArray(32, 'a')},
{"fontId", QString(31, 'a')},
{"actualFaceLocal", QByteArray{}},
{"charset", 255},
{"size", 0},
{"size", MaxFontSnapshotBytes + 1},
{"sha256", QByteArray(31, 'x')},
{"sha256", QString(64, 'a')},
{"faceIndex", -1},
{"faceIndex", 65536},
{"faceOffset", 0},
{"substituted", 1},
{"more", false}};
for (const auto &[field, value] : bad) {
auto invalid = selected();
invalid.insert(field, value);
QVERIFY2(!validateFontResult("font.map", request, invalid), qPrintable(field));
}
QVERIFY(validateFontResult("font.map", request, {{"fontRequestId", 1}, {"found", false}}));
QVERIFY(!validateFontResult("font.map", request,
{{"fontRequestId", 1}, {"found", false}, {"fontId", fontId}}));
const QCborMap read{{"fontRequestId", 2}, {"fontId", fontId}, {"offset", 1}, {"length", 3}};
const QCborMap good{
{"fontRequestId", 2}, {"fontId", fontId}, {"offset", 1}, {"data", QByteArray("abc")}};
QVERIFY(validateFontResult("font.read", read, good));
for (const auto &[field, value] : QList<QPair<QString, QCborValue>>{{"fontId", QString(32, 'b')},
{"offset", 0},
{"offset", 1.0},
{"data", QByteArray("ab")},
{"data", "abc"},
{"more", true}}) {
auto invalid = good;
invalid.insert(field, value);
QVERIFY(!validateFontResult("font.read", read, invalid));
}
}
void errorAndEnvelopeRoleValidation() {
QCborMap error{{"fontRequestId", 1}, {"code", "E_FONT_LIMIT"}, {"message", "Font quota reached"}};
QVERIFY(validateFontError(error));
error.insert(QStringLiteral("code"), "E_UNREGISTERED");
QVERIFY(!validateFontError(error));
auto envelope = parentRequest();
envelope.insert(QStringLiteral("operation"), "font.map");
envelope.insert(QStringLiteral("payload"), mapPayload());
QVERIFY(validateEnvelope(envelope));
envelope.insert(QStringLiteral("unexpected"), true);
QVERIFY(!validateEnvelope(envelope));
}
void synchronousClientRoutesMultipleQueuedResponses() {
ChannelPair pair;
QVERIFY(pair.open());
IpcChannel incoming(pair.runtime.transport.get()), broker(pair.peer);
WorkerFontClient client(&incoming, &pair.runtime);
QSignalSpy errors(&client, &WorkerFontClient::protocolError);
connect(&incoming, &IpcChannel::messageReceived, &client,
[&](const QCborMap &reply) { QVERIFY(client.routeReply(reply)); });
QList<qint64> ids;
connect(&broker, &IpcChannel::messageReceived, &broker, [&](const QCborMap &request) {
QCOMPARE(request.value("requestId").toInteger(), 7);
QCOMPARE(request.value("generation").toInteger(), 3);
const auto id = request.value("payload").toMap().value("fontRequestId").toInteger();
ids << id;
QTimer::singleShot(0, &broker, [&, request, id] {
QVERIFY(broker.send(resultReply(request, {{"fontRequestId", id}, {"found", false}})));
});
});
QVERIFY(client.setParentRequest(parentRequest()));
auto payload = mapPayload();
payload.remove(QStringLiteral("fontRequestId"));
QCOMPARE(client.call("font.map", payload), QCborMap({{"found", false}}));
QCOMPARE(client.call("font.map", payload), QCborMap({{"found", false}}));
QCOMPARE(ids, QList<qint64>({1, 2}));
QVERIFY(!client.failed());
QCOMPARE(errors.size(), 0);
client.clearParentRequest();
}
void clientRejectsMismatchedReply() {
ChannelPair pair;
QVERIFY(pair.open());
IpcChannel incoming(pair.runtime.transport.get()), broker(pair.peer);
WorkerFontClient client(&incoming, &pair.runtime);
QSignalSpy errors(&client, &WorkerFontClient::protocolError);
connect(&incoming, &IpcChannel::messageReceived, &client,
[&](const QCborMap &reply) { client.routeReply(reply); });
connect(&broker, &IpcChannel::messageReceived, &broker, [&](const QCborMap &request) {
QVERIFY(broker.send(resultReply(request, {{"fontRequestId", 9}, {"found", false}})));
});
QVERIFY(client.setParentRequest(parentRequest()));
auto payload = mapPayload();
payload.remove(QStringLiteral("fontRequestId"));
QCOMPARE(client.call("font.map", payload).value("error").toMap().value("code").toString(),
"E_WORKER_CRASH");
QCOMPARE(errors.size(), 1);
QVERIFY(client.failed());
client.clearParentRequest();
}
void clientTimeoutIsStickyAndDoesNotSendAnotherRequest() {
ChannelPair pair;
QVERIFY(pair.open());
IpcChannel incoming(pair.runtime.transport.get()), broker(pair.peer);
WorkerFontClient client(&incoming, &pair.runtime);
QSignalSpy requests(&broker, &IpcChannel::messageReceived);
QVERIFY(client.setParentRequest(parentRequest()));
auto payload = mapPayload();
payload.remove(QStringLiteral("fontRequestId"));
QElapsedTimer elapsed;
elapsed.start();
const auto failure = client.call("font.map", payload, 20);
QCOMPARE(failure.value("error").toMap().value("code").toString(), "E_WORKER_TIMEOUT");
QVERIFY(elapsed.elapsed() < 1000);
QCOMPARE(client.call("font.map", payload), failure);
QCOMPARE(requests.size(), 1);
client.clearParentRequest();
}
void clientTransportDisconnectEndsWaitImmediately() {
ChannelPair pair;
QVERIFY(pair.open());
IpcChannel incoming(pair.runtime.transport.get()), broker(pair.peer);
WorkerFontClient client(&incoming, &pair.runtime);
QVERIFY(client.setParentRequest(parentRequest()));
auto payload = mapPayload();
payload.remove(QStringLiteral("fontRequestId"));
QTimer::singleShot(10, pair.peer, &QLocalSocket::close);
QElapsedTimer elapsed;
elapsed.start();
QCOMPARE(client.call("font.map", payload).value("error").toMap().value("code").toString(),
"E_WORKER_CRASH");
QVERIFY(elapsed.elapsed() < 1000);
client.clearParentRequest();
}
};
QTEST_GUILESS_MAIN(FontContractTest)
#include "test_font_contract.moc"