kate-deft/src/plumb/test_plumb_live.cpp

100 lines
3.1 KiB
C++

/*
* SPDX-License-Identifier: LGPL-2.0-or-later
*
* Live round-trip against a running plan9port plumber. This is the real proof
* that our native C++ 9P2000 client speaks the protocol correctly: it sends a
* plumb message through the "send" port and receives the routed message back on
* the "edit" port via the Plumber reader thread.
*
* If no plumb socket is reachable (no plumber, no $NAMESPACE), the test skips
* itself and PASSES, so it is safe to run in headless/CI environments.
*/
#include "ninep.h"
#include "plumber.h"
#include <QCoreApplication>
#include <QFileInfo>
#include <QObject>
#include <QSignalSpy>
#include <QTemporaryFile>
#include <QTest>
using namespace deft;
class TestPlumbLive : public QObject
{
Q_OBJECT
private Q_SLOTS:
void connectAttach();
void sendThenReceiveOnEditPort();
};
static bool plumberReachable()
{
const QString path = Plumber::socketPath();
if (path.isEmpty() || !QFileInfo::exists(path)) {
return false;
}
NineP n;
const bool ok = n.connectAndAttach(path, QStringLiteral("tester"));
return ok;
}
void TestPlumbLive::connectAttach()
{
if (!plumberReachable()) {
QSKIP("no reachable plumber (socketPath empty/absent) — skipping live test");
}
NineP n;
QVERIFY2(n.connectAndAttach(Plumber::socketPath(), QStringLiteral("tester")),
qPrintable(n.errorString()));
QVERIFY(n.isConnected());
// We can walk to the well-known ports.
QVERIFY2(n.walk(2, QStringLiteral("send")), qPrintable(n.errorString()));
QVERIFY2(n.walk(3, QStringLiteral("edit")), qPrintable(n.errorString()));
}
void TestPlumbLive::sendThenReceiveOnEditPort()
{
if (!plumberReachable()) {
QSKIP("no reachable plumber — skipping live round-trip");
}
// A real, existing file so the plumber's "existing file -> edit" rule fires.
QTemporaryFile tmp;
tmp.setAutoRemove(true);
QVERIFY(tmp.open());
tmp.write("line1\nline2\nline3\n");
tmp.flush();
const QString file = tmp.fileName();
const QFileInfo fi(file);
const QString wdir = fi.absolutePath();
Plumber plumber;
QSignalSpy editSpy(&plumber, &Plumber::edit);
QSignalSpy errSpy(&plumber, &Plumber::readerError);
plumber.startReader();
// Give the reader thread a moment to attach + open the edit port before we
// send, so the routed message isn't delivered before anyone is listening.
QTest::qWait(300);
if (errSpy.count() > 0) {
QSKIP(qPrintable(QStringLiteral("reader could not open edit port: %1")
.arg(errSpy.first().first().toString())));
}
// Send "<file>:2" — the plumber resolves it and routes it to the edit port.
QVERIFY2(plumber.send(file + QStringLiteral(":2"), wdir),
qPrintable(plumber.errorString()));
// The reader should surface the routed message as an edit() signal.
QVERIFY(editSpy.wait(3000));
QCOMPARE(editSpy.count(), 1);
const QList<QVariant> args = editSpy.first();
QCOMPARE(args.at(0).toString(), file);
QCOMPARE(args.at(1).toString(), QStringLiteral("2"));
}
QTEST_MAIN(TestPlumbLive)
#include "test_plumb_live.moc"