diff --git a/src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java b/src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java index 27bf4975..8b926632 100644 --- a/src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java +++ b/src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java @@ -6,6 +6,7 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.platform.AudioSession; import com.getpcpanel.integration.volume.platform.AudioSessionEvent; import com.getpcpanel.integration.volume.platform.EventType; import com.getpcpanel.integration.volume.platform.ISndCtrl; @@ -73,17 +74,23 @@ private boolean triggerCommandVolumeProcessIfAvailable(AudioSessionEvent event, return false; } - /** - * Returns {@code true} if the given command matches the session's executable - */ - private boolean isProcessAndDevice(AudioSessionEvent event, CommandVolumeProcess c) { + /** Returns {@code true} if the given command names the session's executable or title */ + boolean isProcessAndDevice(AudioSessionEvent event, CommandVolumeProcess c) { var session = event.session(); if (session.executable() == null) return false; - if (!c.getProcessName().contains(session.executable().getName())) { + if (c.getProcessName().stream().noneMatch(n -> matchesName(session, n))) { return false; } var deviceId = c.getDevice(); return StringUtils.isBlank(deviceId) || "*".equals(deviceId); } + + private static boolean matchesName(AudioSession session, String query) { + var normalized = StringUtils.removeEndIgnoreCase(StringUtils.trimToEmpty(query), ".exe"); + return StringUtils.isNotBlank(normalized) + && StringUtils.equalsAnyIgnoreCase(normalized, + StringUtils.removeEndIgnoreCase(session.executable().getName(), ".exe"), + StringUtils.removeEndIgnoreCase(session.title(), ".exe")); + } } diff --git a/src/test/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeServiceTest.java b/src/test/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeServiceTest.java new file mode 100644 index 00000000..def66e23 --- /dev/null +++ b/src/test/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeServiceTest.java @@ -0,0 +1,62 @@ +package com.getpcpanel.integration.volume; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.AudioSessionEvent; +import com.getpcpanel.integration.volume.platform.EventType; + +class LinuxNewSessionVolumeServiceTest { + private final LinuxNewSessionVolumeService sut = new LinuxNewSessionVolumeService(); + + private static AudioSessionEvent event(String executable, String title) { + return new AudioSessionEvent(new AudioSession(null, 1234, new File(executable), title, null, 1f, false), EventType.ADDED); + } + + private static CommandVolumeProcess binding(String device, String... processNames) { + return new CommandVolumeProcess(List.of(processNames), device, false, null); + } + + @Test + void matchesExecutableOrTitle() { + var zen = event("zen-bin", "Zen"); + + assertTrue(sut.isProcessAndDevice(zen, binding("", "chromium", "zen", "chrome"))); + assertTrue(sut.isProcessAndDevice(zen, binding("", "zen-bin"))); + assertTrue(sut.isProcessAndDevice(zen, binding("", "ZEN"))); + assertFalse(sut.isProcessAndDevice(zen, binding("", "chromium", "chrome"))); + } + + @Test + void ignoresTrailingExe() { + var game = event("deadlock.exe", "deadlock.exe"); + + assertTrue(sut.isProcessAndDevice(game, binding("", "Deadlock"))); + assertTrue(sut.isProcessAndDevice(game, binding("", "deadlock.exe"))); + } + + @Test + void honoursDeviceScope() { + var mpv = event("mpv", "mpv"); + + assertTrue(sut.isProcessAndDevice(mpv, binding("", "mpv"))); + assertTrue(sut.isProcessAndDevice(mpv, binding("*", "mpv"))); + assertFalse(sut.isProcessAndDevice(mpv, binding("alsa_output.pci-0000_0c_00.4", "mpv"))); + } + + @Test + void blankNamesMatchNothing() { + var sparse = event("/", "Spotify"); + + assertFalse(sut.isProcessAndDevice(sparse, binding(""))); + assertFalse(sut.isProcessAndDevice(sparse, binding("", ".exe"))); + assertTrue(sut.isProcessAndDevice(sparse, binding("", "Spotify"))); + } +}