This commit is contained in:
Joey Yakimowich-Payne 2026-01-30 10:40:52 -07:00
commit ecec82c70e
10 changed files with 809 additions and 21 deletions

View file

@ -1,5 +1,6 @@
#include <warppipe/warppipe.hpp>
#include "../../gui/AudioLevelMeter.h"
#include "../../gui/GraphEditorWidget.h"
#include "../../gui/PresetManager.h"
#include "../../gui/VolumeWidgets.h"
@ -12,6 +13,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QStandardPaths>
#include <QTabWidget>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
@ -1151,6 +1153,81 @@ TEST_CASE("preset saves and loads volume state") {
QFile::remove(path);
}
TEST_CASE("AudioLevelMeter setLevel clamps to 0-1") {
ensureApp();
AudioLevelMeter meter;
meter.setLevel(0.5f);
REQUIRE(meter.level() == Catch::Approx(0.5f));
meter.setLevel(-0.5f);
REQUIRE(meter.level() == Catch::Approx(0.0f));
meter.setLevel(1.5f);
REQUIRE(meter.level() == Catch::Approx(1.0f));
}
TEST_CASE("AudioLevelMeter peak hold tracks maximum") {
ensureApp();
AudioLevelMeter meter;
meter.setLevel(0.8f);
REQUIRE(meter.peakHold() == Catch::Approx(0.8f));
meter.setLevel(0.3f);
REQUIRE(meter.peakHold() == Catch::Approx(0.8f));
meter.setLevel(0.9f);
REQUIRE(meter.peakHold() == Catch::Approx(0.9f));
}
TEST_CASE("AudioLevelMeter peak decays after hold period") {
ensureApp();
AudioLevelMeter meter;
meter.setLevel(0.5f);
REQUIRE(meter.peakHold() == Catch::Approx(0.5f));
for (int i = 0; i < 7; ++i)
meter.setLevel(0.0f);
REQUIRE(meter.peakHold() < 0.5f);
REQUIRE(meter.peakHold() > 0.0f);
}
TEST_CASE("AudioLevelMeter resetPeakHold clears peak") {
ensureApp();
AudioLevelMeter meter;
meter.setLevel(0.7f);
REQUIRE(meter.peakHold() == Catch::Approx(0.7f));
meter.resetPeakHold();
REQUIRE(meter.peakHold() == Catch::Approx(0.0f));
}
TEST_CASE("GraphEditorWidget has METERS tab") {
auto tc = TestClient::Create();
if (!tc.available()) { SUCCEED("PipeWire unavailable"); return; }
ensureApp();
GraphEditorWidget widget(tc.client.get());
auto *sidebar = widget.findChild<QTabWidget *>();
REQUIRE(sidebar != nullptr);
bool found = false;
for (int i = 0; i < sidebar->count(); ++i) {
if (sidebar->tabText(i) == "METERS") {
found = true;
break;
}
}
REQUIRE(found);
}
TEST_CASE("node meter rows created for injected nodes") {
auto tc = TestClient::Create();
if (!tc.available()) { SUCCEED("PipeWire unavailable"); return; }
ensureApp();
REQUIRE(tc.client->Test_InsertNode(
MakeNode(100700, "meter-node", "Audio/Sink")).ok());
REQUIRE(tc.client->Test_InsertPort(
MakePort(100701, 100700, "FL", true)).ok());
GraphEditorWidget widget(tc.client.get());
auto meters = widget.findChildren<AudioLevelMeter *>();
REQUIRE(meters.size() >= 3);
}
TEST_CASE("volume state cleaned up on node deletion") {
auto tc = TestClient::Create();
if (!tc.available()) { SUCCEED("PipeWire unavailable"); return; }

View file

@ -853,3 +853,109 @@ TEST_CASE("Test_SetNodeVolume fails for nonexistent node") {
REQUIRE_FALSE(status.ok());
REQUIRE(status.code == warppipe::StatusCode::kNotFound);
}
TEST_CASE("EnsureNodeMeter and NodeMeterPeak round-trip") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
auto &client = result.value;
warppipe::NodeInfo node;
node.id = warppipe::NodeId{950};
node.name = "meter-test";
node.media_class = "Audio/Sink";
REQUIRE(client->Test_InsertNode(node).ok());
REQUIRE(client->EnsureNodeMeter(warppipe::NodeId{950}).ok());
auto peak = client->NodeMeterPeak(warppipe::NodeId{950});
REQUIRE(peak.ok());
REQUIRE(peak.value.peak_left == Catch::Approx(0.0f));
REQUIRE(peak.value.peak_right == Catch::Approx(0.0f));
}
TEST_CASE("Test_SetNodeMeterPeak updates peaks") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
auto &client = result.value;
warppipe::NodeInfo node;
node.id = warppipe::NodeId{951};
node.name = "meter-set";
node.media_class = "Audio/Sink";
REQUIRE(client->Test_InsertNode(node).ok());
REQUIRE(client->Test_SetNodeMeterPeak(warppipe::NodeId{951}, 0.6f, 0.8f).ok());
auto peak = client->NodeMeterPeak(warppipe::NodeId{951});
REQUIRE(peak.ok());
REQUIRE(peak.value.peak_left == Catch::Approx(0.6f));
REQUIRE(peak.value.peak_right == Catch::Approx(0.8f));
}
TEST_CASE("DisableNodeMeter removes metering") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
auto &client = result.value;
warppipe::NodeInfo node;
node.id = warppipe::NodeId{952};
node.name = "meter-disable";
node.media_class = "Audio/Sink";
REQUIRE(client->Test_InsertNode(node).ok());
REQUIRE(client->EnsureNodeMeter(warppipe::NodeId{952}).ok());
REQUIRE(client->DisableNodeMeter(warppipe::NodeId{952}).ok());
auto peak = client->NodeMeterPeak(warppipe::NodeId{952});
REQUIRE_FALSE(peak.ok());
REQUIRE(peak.status.code == warppipe::StatusCode::kNotFound);
}
TEST_CASE("MasterMeterPeak defaults to zero") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
auto peak = result.value->MeterPeak();
REQUIRE(peak.ok());
REQUIRE(peak.value.peak_left == Catch::Approx(0.0f));
REQUIRE(peak.value.peak_right == Catch::Approx(0.0f));
}
TEST_CASE("Test_SetMasterMeterPeak updates master peaks") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
REQUIRE(result.value->Test_SetMasterMeterPeak(0.9f, 0.7f).ok());
auto peak = result.value->MeterPeak();
REQUIRE(peak.ok());
REQUIRE(peak.value.peak_left == Catch::Approx(0.9f));
REQUIRE(peak.value.peak_right == Catch::Approx(0.7f));
}
TEST_CASE("Test_SetNodeMeterPeak clamps values") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
auto &client = result.value;
warppipe::NodeInfo node;
node.id = warppipe::NodeId{953};
node.name = "meter-clamp";
node.media_class = "Audio/Sink";
REQUIRE(client->Test_InsertNode(node).ok());
REQUIRE(client->Test_SetNodeMeterPeak(warppipe::NodeId{953}, 1.5f, -0.5f).ok());
auto peak = client->NodeMeterPeak(warppipe::NodeId{953});
REQUIRE(peak.ok());
REQUIRE(peak.value.peak_left == Catch::Approx(1.0f));
REQUIRE(peak.value.peak_right == Catch::Approx(0.0f));
}
TEST_CASE("EnsureNodeMeter fails for nonexistent node") {
auto result = warppipe::Client::Create(DefaultOptions());
REQUIRE(result.ok());
auto status = result.value->EnsureNodeMeter(warppipe::NodeId{999});
REQUIRE_FALSE(status.ok());
REQUIRE(status.code == warppipe::StatusCode::kNotFound);
}