/* * 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 #include #include #include #include #include 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 ":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 args = editSpy.first(); QCOMPARE(args.at(0).toString(), file); QCOMPARE(args.at(1).toString(), QStringLiteral("2")); } QTEST_MAIN(TestPlumbLive) #include "test_plumb_live.moc"