Add capture routing rules (source → app) to complement playback rules
This commit is contained in:
parent
621d67ebab
commit
242d0ec09f
6 changed files with 418 additions and 78 deletions
|
|
@ -1013,3 +1013,163 @@ TEST_CASE("EnsureNodeMeter fails for nonexistent node") {
|
|||
REQUIRE_FALSE(status.ok());
|
||||
REQUIRE(status.code == warppipe::StatusCode::kNotFound);
|
||||
}
|
||||
|
||||
TEST_CASE("capture rule validation rejects empty source_node") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "discord";
|
||||
rule.direction = warppipe::RuleDirection::kCapture;
|
||||
auto r = result.value->AddRouteRule(rule);
|
||||
REQUIRE_FALSE(r.ok());
|
||||
REQUIRE(r.status.code == warppipe::StatusCode::kInvalidArgument);
|
||||
}
|
||||
|
||||
TEST_CASE("capture rule validation accepts valid source_node") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "discord";
|
||||
rule.direction = warppipe::RuleDirection::kCapture;
|
||||
rule.source_node = "alsa_input.usb-mic";
|
||||
auto r = result.value->AddRouteRule(rule);
|
||||
REQUIRE(r.ok());
|
||||
REQUIRE(r.value.value != 0);
|
||||
|
||||
auto list = result.value->ListRouteRules();
|
||||
REQUIRE(list.ok());
|
||||
REQUIRE(list.value.size() == 1);
|
||||
REQUIRE(list.value[0].direction == warppipe::RuleDirection::kCapture);
|
||||
REQUIRE(list.value[0].source_node == "alsa_input.usb-mic");
|
||||
}
|
||||
|
||||
TEST_CASE("capture rule creates pending auto-link for matching app") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "teams";
|
||||
rule.direction = warppipe::RuleDirection::kCapture;
|
||||
rule.source_node = "hw-mic";
|
||||
REQUIRE(result.value->AddRouteRule(rule).ok());
|
||||
|
||||
REQUIRE(result.value->Test_GetPendingAutoLinkCount() == 0);
|
||||
|
||||
warppipe::NodeInfo app_node;
|
||||
app_node.id = warppipe::NodeId{800001};
|
||||
app_node.name = "teams-capture";
|
||||
app_node.media_class = "Stream/Input/Audio";
|
||||
app_node.application_name = "teams";
|
||||
REQUIRE(result.value->Test_InsertNode(app_node).ok());
|
||||
|
||||
REQUIRE(result.value->Test_GetPendingAutoLinkCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("capture rule ignores non-matching app") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "discord";
|
||||
rule.direction = warppipe::RuleDirection::kCapture;
|
||||
rule.source_node = "hw-mic";
|
||||
REQUIRE(result.value->AddRouteRule(rule).ok());
|
||||
|
||||
warppipe::NodeInfo node;
|
||||
node.id = warppipe::NodeId{800002};
|
||||
node.name = "zoom-capture";
|
||||
node.media_class = "Stream/Input/Audio";
|
||||
node.application_name = "zoom";
|
||||
REQUIRE(result.value->Test_InsertNode(node).ok());
|
||||
|
||||
REQUIRE(result.value->Test_GetPendingAutoLinkCount() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("capture rule added after app creates pending link") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::NodeInfo node;
|
||||
node.id = warppipe::NodeId{800003};
|
||||
node.name = "discord-capture";
|
||||
node.media_class = "Stream/Input/Audio";
|
||||
node.application_name = "discord";
|
||||
REQUIRE(result.value->Test_InsertNode(node).ok());
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "discord";
|
||||
rule.direction = warppipe::RuleDirection::kCapture;
|
||||
rule.source_node = "hw-mic";
|
||||
REQUIRE(result.value->AddRouteRule(rule).ok());
|
||||
|
||||
REQUIRE(result.value->Test_GetPendingAutoLinkCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("playback rule still rejects empty target_node") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "firefox";
|
||||
rule.direction = warppipe::RuleDirection::kPlayback;
|
||||
auto r = result.value->AddRouteRule(rule);
|
||||
REQUIRE_FALSE(r.ok());
|
||||
REQUIRE(r.status.code == warppipe::StatusCode::kInvalidArgument);
|
||||
}
|
||||
|
||||
TEST_CASE("save and load capture rule round trip") {
|
||||
auto result = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
warppipe::RouteRule rule;
|
||||
rule.match.application_name = "discord";
|
||||
rule.match.process_binary = "Discord";
|
||||
rule.direction = warppipe::RuleDirection::kCapture;
|
||||
rule.source_node = "alsa_input.usb-mic";
|
||||
REQUIRE(result.value->AddRouteRule(rule).ok());
|
||||
|
||||
const char* path = "/tmp/warppipe_test_capture_config.json";
|
||||
REQUIRE(result.value->SaveConfig(path).ok());
|
||||
|
||||
auto result2 = warppipe::Client::Create(DefaultOptions());
|
||||
if (!result2.ok()) {
|
||||
SUCCEED("PipeWire unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
REQUIRE(result2.value->LoadConfig(path).ok());
|
||||
|
||||
auto rules = result2.value->ListRouteRules();
|
||||
REQUIRE(rules.ok());
|
||||
REQUIRE(rules.value.size() == 1);
|
||||
REQUIRE(rules.value[0].match.application_name == "discord");
|
||||
REQUIRE(rules.value[0].match.process_binary == "Discord");
|
||||
REQUIRE(rules.value[0].direction == warppipe::RuleDirection::kCapture);
|
||||
REQUIRE(rules.value[0].source_node == "alsa_input.usb-mic");
|
||||
REQUIRE(rules.value[0].target_node.empty());
|
||||
|
||||
std::remove(path);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue