GUI Milestone 8c

This commit is contained in:
Joey Yakimowich-Payne 2026-01-30 08:57:33 -07:00
commit fa67dd3708
9 changed files with 458 additions and 22 deletions

View file

@ -1,11 +1,15 @@
#include <warppipe/warppipe.hpp>
#include "../../gui/GraphEditorWidget.h"
#include "../../gui/PresetManager.h"
#include "../../gui/WarpGraphModel.h"
#include <QAction>
#include <QApplication>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStandardPaths>
#include <catch2/catch_test_macros.hpp>
@ -849,3 +853,113 @@ TEST_CASE("clearSavedPositions resets model positions") {
REQUIRE(posAfter != QPointF(0, 0));
}
TEST_CASE("preset save/load round-trip preserves virtual devices and layout") {
auto tc = TestClient::Create();
if (!tc.available()) { SUCCEED("PipeWire unavailable"); return; }
ensureApp();
REQUIRE(tc.client->Test_InsertNode(
MakeNode(100500, "preset-vsink", "Audio/Sink", {}, {}, true)).ok());
REQUIRE(tc.client->Test_InsertPort(
MakePort(100501, 100500, "FL", true)).ok());
REQUIRE(tc.client->Test_InsertPort(
MakePort(100502, 100500, "FR", true)).ok());
REQUIRE(tc.client->Test_InsertNode(
MakeNode(100503, "preset-src", "Audio/Source")).ok());
REQUIRE(tc.client->Test_InsertPort(
MakePort(100504, 100503, "out_FL", false)).ok());
REQUIRE(tc.client->Test_InsertLink(
MakeLink(100505, 100504, 100501)).ok());
WarpGraphModel model(tc.client.get());
model.refreshFromClient();
model.setNodeData(model.qtNodeIdForPw(100500),
QtNodes::NodeRole::Position, QPointF(300, 400));
QString path = QStandardPaths::writableLocation(
QStandardPaths::TempLocation) +
"/warppipe_test_preset.json";
REQUIRE(PresetManager::savePreset(path, tc.client.get(), &model));
QFile file(path);
REQUIRE(file.open(QIODevice::ReadOnly));
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
file.close();
REQUIRE(doc.isObject());
QJsonObject root = doc.object();
REQUIRE(root["version"].toInt() == 1);
REQUIRE(root["virtual_devices"].toArray().size() >= 1);
REQUIRE(root["routing"].toArray().size() >= 1);
REQUIRE(root["layout"].toArray().size() >= 2);
bool foundVsink = false;
for (const auto &val : root["virtual_devices"].toArray()) {
if (val.toObject()["name"].toString() == "preset-vsink") {
foundVsink = true;
REQUIRE(val.toObject()["channels"].toInt() == 2);
}
}
REQUIRE(foundVsink);
bool foundRoute = false;
for (const auto &val : root["routing"].toArray()) {
QJsonObject route = val.toObject();
if (route["out_node"].toString() == "preset-src" &&
route["in_node"].toString() == "preset-vsink") {
foundRoute = true;
}
}
REQUIRE(foundRoute);
bool foundLayout = false;
for (const auto &val : root["layout"].toArray()) {
QJsonObject obj = val.toObject();
if (obj["name"].toString() == "preset-vsink") {
foundLayout = true;
REQUIRE(obj["x"].toDouble() == Catch::Approx(300.0));
REQUIRE(obj["y"].toDouble() == Catch::Approx(400.0));
}
}
REQUIRE(foundLayout);
QFile::remove(path);
}
TEST_CASE("splitter sizes persist in layout JSON") {
auto tc = TestClient::Create();
if (!tc.available()) { SUCCEED("PipeWire unavailable"); return; }
ensureApp();
REQUIRE(tc.client->Test_InsertNode(
MakeNode(100510, "splitter-node", "Audio/Sink")).ok());
WarpGraphModel model(tc.client.get());
model.refreshFromClient();
WarpGraphModel::ViewState vs;
vs.scale = 1.0;
vs.centerX = 0.0;
vs.centerY = 0.0;
vs.splitterGraph = 900;
vs.splitterSidebar = 400;
vs.valid = true;
QString path = QStandardPaths::writableLocation(
QStandardPaths::TempLocation) +
"/warppipe_test_splitter.json";
model.saveLayout(path, vs);
WarpGraphModel model2(tc.client.get());
model2.loadLayout(path);
auto restored = model2.savedViewState();
REQUIRE(restored.valid);
REQUIRE(restored.splitterGraph == 900);
REQUIRE(restored.splitterSidebar == 400);
QFile::remove(path);
}