221 lines
8.5 KiB
C++
221 lines
8.5 KiB
C++
#include <QtTest>
|
|
|
|
#include "moonlight_session.h"
|
|
|
|
using namespace vdi;
|
|
|
|
class MoonlightSessionTest : public QObject {
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void parsesSplitReadyMarkers();
|
|
void requiresBothReadyMarkers();
|
|
void acceptsSeparateHardwareTestDecoder();
|
|
void rejectsSecondStart();
|
|
void reportsKioskLaunchFailure();
|
|
void buildsConsistentStreamArguments();
|
|
void selectsProfileWithoutLosingStats();
|
|
void rejectsInvalidDecoderOverride();
|
|
void validatesConfiguredBitrate();
|
|
};
|
|
|
|
void MoonlightSessionTest::validatesConfiguredBitrate() {
|
|
ConnectionTicket ticket;
|
|
ticket.width = 2560;
|
|
ticket.height = 1440;
|
|
QProcessEnvironment environment;
|
|
for (const QString& value : {QString("25000"), QString("0"), QString("150001"), QString("invalid")}) {
|
|
environment.insert("VDI_MOONLIGHT_BITRATE_KBPS", value);
|
|
auto options = MoonlightSession::optionsFromEnvironment(environment);
|
|
const auto arguments = MoonlightSession::buildStreamArguments(ticket, options);
|
|
QCOMPARE(arguments.at(arguments.indexOf("--bitrate") + 1), value == "25000" ? QString("25000") : QString("30000"));
|
|
}
|
|
}
|
|
|
|
void MoonlightSessionTest::parsesSplitReadyMarkers() {
|
|
MoonlightOutputParser parser;
|
|
parser.append("Test decode succ");
|
|
QVERIFY(!parser.isReady());
|
|
parser.append("essful\nRenderer 'SDL' with 'DRM' backend ");
|
|
QVERIFY(parser.hasTestDecode());
|
|
QVERIFY(!parser.hasRenderer());
|
|
parser.append("chosen\n");
|
|
QVERIFY(parser.isReady());
|
|
}
|
|
|
|
void MoonlightSessionTest::requiresBothReadyMarkers() {
|
|
MoonlightOutputParser parser;
|
|
parser.append("Test decode successful\n");
|
|
QVERIFY(!parser.isReady());
|
|
|
|
parser.reset();
|
|
parser.append("Renderer 'SDL' chosen\n");
|
|
QVERIFY(!parser.isReady());
|
|
|
|
parser.append("Connection terminated: -1\n");
|
|
QVERIFY(parser.hasTerminalError());
|
|
QVERIFY(!parser.isReady());
|
|
}
|
|
|
|
void MoonlightSessionTest::acceptsSeparateHardwareTestDecoder() {
|
|
MoonlightOutputParser parser;
|
|
parser.append("Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp\n");
|
|
QVERIFY(!parser.isReady());
|
|
parser.append("Not reusing test decoder for h264_rkmpp\n");
|
|
QVERIFY(parser.hasTestDecode());
|
|
QVERIFY(!parser.isReady());
|
|
parser.append("Renderer 'EGL' with 'DRM' backend chosen\n");
|
|
QVERIFY(parser.isReady());
|
|
parser.append("Connection terminated: -1\n");
|
|
QVERIFY(!parser.isReady());
|
|
}
|
|
|
|
void MoonlightSessionTest::rejectsSecondStart() {
|
|
QTemporaryDir tempDir;
|
|
QVERIFY(tempDir.isValid());
|
|
|
|
const QString executable = tempDir.filePath(QStringLiteral("moonlight-qt"));
|
|
QFile script(executable);
|
|
QVERIFY(script.open(QIODevice::WriteOnly | QIODevice::Text));
|
|
script.write("#!/bin/sh\n"
|
|
"printf 'Test decode successful\\n'\n"
|
|
"printf \"Renderer 'SDL' with 'DRM' backend chosen\\n\"\n"
|
|
"sleep 5\n");
|
|
script.close();
|
|
QVERIFY(script.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner |
|
|
QFileDevice::ExeOwner | QFileDevice::ReadGroup |
|
|
QFileDevice::ExeGroup | QFileDevice::ReadOther |
|
|
QFileDevice::ExeOther));
|
|
|
|
const QByteArray oldPath = qgetenv("PATH");
|
|
qputenv("PATH", tempDir.path().toUtf8() + ':' + oldPath);
|
|
|
|
ConnectionTicket ticket;
|
|
ticket.endpoint = QStringLiteral("127.0.0.1");
|
|
ticket.app = QStringLiteral("Desktop");
|
|
|
|
MoonlightSession session;
|
|
int started = 0;
|
|
int ready = 0;
|
|
int terminal = 0;
|
|
const auto callback = [&](const MoonlightSession::Event& event) {
|
|
if (event.type == MoonlightSession::EventType::kStarted) {
|
|
++started;
|
|
} else if (event.type == MoonlightSession::EventType::kStreamReady) {
|
|
++ready;
|
|
} else if (event.type == MoonlightSession::EventType::kFailed ||
|
|
event.type == MoonlightSession::EventType::kFinished) {
|
|
++terminal;
|
|
}
|
|
};
|
|
|
|
QVERIFY(session.start(ticket, callback));
|
|
QTRY_COMPARE_WITH_TIMEOUT(started, 1, 2000);
|
|
QVERIFY(!session.start(ticket, callback));
|
|
QTRY_COMPARE_WITH_TIMEOUT(ready, 1, 3000);
|
|
|
|
session.stop();
|
|
QTRY_COMPARE_WITH_TIMEOUT(terminal, 1, 4000);
|
|
QCOMPARE(ready, 1);
|
|
QCOMPARE(terminal, 1);
|
|
|
|
qputenv("PATH", oldPath);
|
|
}
|
|
|
|
void MoonlightSessionTest::reportsKioskLaunchFailure() {
|
|
QTemporaryDir tempDir;
|
|
QVERIFY(tempDir.isValid());
|
|
const QString executable = tempDir.filePath(QStringLiteral("failed-client"));
|
|
QFile script(executable);
|
|
QVERIFY(script.open(QIODevice::WriteOnly | QIODevice::Text));
|
|
script.write("#!/bin/sh\n"
|
|
"test \"$VDI_KIOSK_STREAM\" = 1 || exit 2\n"
|
|
"exit 1\n");
|
|
script.close();
|
|
QVERIFY(script.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner |
|
|
QFileDevice::ExeOwner));
|
|
MoonlightSession session;
|
|
MoonlightSession::Options options;
|
|
options.program = executable;
|
|
session.setOptions(options);
|
|
ConnectionTicket ticket;
|
|
ticket.endpoint = QStringLiteral("127.0.0.1");
|
|
ticket.app = QStringLiteral("MissingApplication");
|
|
int failed = 0;
|
|
int finished = 0;
|
|
QString message;
|
|
QVERIFY(session.start(ticket, [&](const MoonlightSession::Event& event) {
|
|
if (event.type == MoonlightSession::EventType::kFailed) {
|
|
++failed;
|
|
message = event.message;
|
|
} else if (event.type == MoonlightSession::EventType::kFinished) {
|
|
++finished;
|
|
}
|
|
}));
|
|
QTRY_COMPARE_WITH_TIMEOUT(failed, 1, 2000);
|
|
QCOMPARE(finished, 0);
|
|
QVERIFY(message.contains(QStringLiteral("code=1")));
|
|
}
|
|
|
|
void MoonlightSessionTest::buildsConsistentStreamArguments() {
|
|
ConnectionTicket ticket;
|
|
ticket.endpoint = QStringLiteral("192.168.1.70");
|
|
ticket.app = QStringLiteral("Desktop");
|
|
ticket.width = 2560;
|
|
ticket.height = 1440;
|
|
ticket.fps = 30;
|
|
|
|
MoonlightSession::Options options;
|
|
options.profile = QStringLiteral("low-latency");
|
|
options.decoder = QStringLiteral("software");
|
|
options.videoCodec = QStringLiteral("H.264");
|
|
options.vsync = false;
|
|
options.framePacing = false;
|
|
options.performanceOverlay = true;
|
|
|
|
const QStringList args = MoonlightSession::buildStreamArguments(ticket, options);
|
|
QVERIFY(args.contains(QStringLiteral("--resolution")));
|
|
QVERIFY(args.contains(QStringLiteral("2560x1440")));
|
|
QVERIFY(args.contains(QStringLiteral("--fps")));
|
|
QVERIFY(args.contains(QStringLiteral("30")));
|
|
QVERIFY(args.contains(QStringLiteral("--bitrate")));
|
|
QVERIFY(args.contains(QStringLiteral("30000")));
|
|
QVERIFY(args.contains(QStringLiteral("--no-vsync")));
|
|
QVERIFY(args.contains(QStringLiteral("--no-frame-pacing")));
|
|
QVERIFY(args.contains(QStringLiteral("--performance-overlay")));
|
|
QVERIFY(args.contains(QStringLiteral("--absolute-mouse")));
|
|
const int captureOption = args.indexOf(QStringLiteral("--capture-system-keys"));
|
|
QVERIFY(captureOption >= 0);
|
|
QCOMPARE(args.value(captureOption + 1), QStringLiteral("always"));
|
|
}
|
|
|
|
void MoonlightSessionTest::selectsProfileWithoutLosingStats() {
|
|
QProcessEnvironment environment;
|
|
environment.insert(QStringLiteral("VDI_STREAM_PROFILE"), QStringLiteral("low-latency"));
|
|
environment.insert(QStringLiteral("VDI_STREAM_STATS"), QStringLiteral("1"));
|
|
auto options = MoonlightSession::optionsFromEnvironment(environment);
|
|
QVERIFY(!options.vsync);
|
|
QVERIFY(!options.framePacing);
|
|
QVERIFY(options.performanceOverlay);
|
|
environment.insert(QStringLiteral("VDI_STREAM_PROFILE"), QStringLiteral("smooth"));
|
|
options = MoonlightSession::optionsFromEnvironment(environment);
|
|
QVERIFY(options.vsync);
|
|
QVERIFY(options.framePacing);
|
|
QVERIFY(options.performanceOverlay);
|
|
}
|
|
|
|
void MoonlightSessionTest::rejectsInvalidDecoderOverride() {
|
|
QProcessEnvironment environment;
|
|
environment.insert(QStringLiteral("VDI_MOONLIGHT_DECODER"), QStringLiteral("h264_rkmpp"));
|
|
environment.insert(QStringLiteral("VDI_STREAM_PROFILE"), QStringLiteral("invalid"));
|
|
auto options = MoonlightSession::optionsFromEnvironment(environment);
|
|
QCOMPARE(options.decoder, QStringLiteral("software"));
|
|
QCOMPARE(options.profile, QStringLiteral("smooth"));
|
|
environment.insert(QStringLiteral("VDI_MOONLIGHT_DECODER"), QStringLiteral("hardware"));
|
|
options = MoonlightSession::optionsFromEnvironment(environment);
|
|
QCOMPARE(options.decoder, QStringLiteral("hardware"));
|
|
}
|
|
|
|
QTEST_GUILESS_MAIN(MoonlightSessionTest)
|
|
#include "test_moonlight_session.moc"
|