#include "audio/audio_manager.h" #include using namespace radiantwave::audio; TEST(AudioTest, FriendlyDeviceNameMapping) { EXPECT_EQ(mapToFriendlyDeviceName("alsa_output.pci-0000_00_1f.3.analog-stereo"), "Built-In Speakers"); EXPECT_EQ(mapToFriendlyDeviceName("AMD Radeon HD Audio Controller HDMI"), "TV / HDMI Monitor"); EXPECT_EQ(mapToFriendlyDeviceName("USB Audio Headset Dongle"), "USB Audio Device"); EXPECT_EQ(mapToFriendlyDeviceName("bluez_sink.00_11_22_33_44_55"), "Bluetooth Audio"); EXPECT_EQ(mapToFriendlyDeviceName("Virtual Speaker"), "Built-In Speakers"); EXPECT_EQ(mapToFriendlyDeviceName(""), "Default Audio Output"); } TEST(AudioTest, ResolvePlaybackDeviceNameHandlesSentinels) { // The sentinel and 0 mean "system default" and never correspond to a real // enumerated playback device, so they resolve to the empty (default) name. EXPECT_EQ(AudioManager::resolvePlaybackDeviceName(0), ""); EXPECT_EQ(AudioManager::resolvePlaybackDeviceName(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK), ""); } TEST(AudioTest, GetPlaybackDevicesFallsBackWithIdZeroWhenNone) { AudioManager am; auto devices = am.getPlaybackDevices(); // Headless hosts (CI) expose no playback devices, so expect the synthetic // fallback entry that carries id 0 (the "system default" marker). if (devices.empty()) { // No devices and no fallback would be a contract violation. FAIL() << "getPlaybackDevices() returned an empty list without fallback"; } for (const auto& d : devices) { if (d.raw_name.empty()) { EXPECT_EQ(d.id, 0u); EXPECT_EQ(d.friendly_name, "System Default"); } } } TEST(AudioTest, AudioManagerVolumeClamping) { radiantwave::config::ConfigManager config; std::string temp_path = (std::filesystem::temp_directory_path() / "test_rw_audio_cfg.yaml").string(); if (std::filesystem::exists(temp_path)) { std::filesystem::remove(temp_path); } ASSERT_TRUE(config.initialize(temp_path)); AudioManager am; // Test initialization without audio device (graceful fallback) am.initialize(config); am.setVolume(150); EXPECT_EQ(am.getVolume(), 100); am.setVolume(-20); EXPECT_EQ(am.getVolume(), 0); am.setVolume(75); EXPECT_EQ(am.getVolume(), 75); am.shutdown(); std::filesystem::remove(temp_path); } TEST(AudioTest, PlayOptionsLoopInfinite) { // Regression: MIX_PlayTrack() must not default to a single pass. Tracks // have to loop forever so playback never runs out mid-scroll; a track that // stops (rather than attenuates) also can't be revived by raising app // volume. Assert the options request infinite looping (-1). SDL_PropertiesID props = AudioManager::createInfiniteLoopOptions(); ASSERT_NE(props, 0u); EXPECT_EQ(SDL_GetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, 0), -1); SDL_DestroyProperties(props); }