Restore configured volume after playback stops

This commit is contained in:
Storm Dragon
2026-06-14 01:48:13 -04:00
parent e2df8972e7
commit 14163bdbc1
7 changed files with 229 additions and 97 deletions
+206
View File
@@ -0,0 +1,206 @@
import configparser
import logging
import os
import tempfile
import types
import unittest
from unittest import mock
import bragi
import constants
from database import DatabaseMigration, MusicDatabase, SettingsDatabase
import command
import util
import variables as var
class FakeBot:
def __init__(self):
self.registered = []
def register_command(self, cmd, handle, no_partial_match=False, access_outside_channel=False, admin=False):
self.registered.append(cmd)
class FakePlaylist:
def __init__(self, item=None):
self.item = item
def __len__(self):
return 1 if self.item is not None else 0
def current_item(self):
return self.item
class ExhaustedPlaylist:
current_index = -1
def next(self):
return False
class FakeSoundOutput:
def get_buffer_size(self):
return 0
class FakeMumble:
def __init__(self):
self.sound_output = FakeSoundOutput()
self.alive_checks = 0
def is_alive(self):
self.alive_checks += 1
return self.alive_checks < 2
class VolumeRestoreTests(unittest.TestCase):
def setUp(self):
settings_file = tempfile.NamedTemporaryFile(delete=False)
settings_file.close()
music_file = tempfile.NamedTemporaryFile(delete=False)
music_file.close()
self.settings_path = settings_file.name
self.music_path = music_file.name
self.settings_db = SettingsDatabase(self.settings_path)
self.music_db = MusicDatabase(self.music_path)
DatabaseMigration(self.settings_db, self.music_db).migrate()
self.config = configparser.ConfigParser(interpolation=None, allow_no_value=True)
self.config.read("configuration.default.ini", encoding="utf-8")
var.config = self.config
var.db = self.settings_db
var.music_db = self.music_db
var.cache = None
var.playlist = []
def tearDown(self):
os.unlink(self.settings_path)
os.unlink(self.music_path)
def make_bot(self):
bot = bragi.MumbleBot.__new__(bragi.MumbleBot)
bot.volume_helper = util.VolumeHelper()
bot.volume_helper.set_volume(1.0)
bot.thread = None
bot.on_interrupting = False
bot.song_start_at = -1
bot.read_pcm_size = 0
bot.is_pause = False
bot.wait_for_ready = False
bot.log = logging.getLogger("test")
return bot
def test_restore_volume_on_stop_updates_runtime_and_db_from_config(self):
self.config.set("bot", "restore_volume_on_stop", "True")
self.config.set("bot", "volume", "0.7")
self.settings_db.set("bot", "volume", "1.0")
bot = self.make_bot()
bot.restore_default_volume()
self.assertAlmostEqual(bot.volume_helper.plain_volume_set, 0.7)
self.assertAlmostEqual(self.settings_db.getfloat("bot", "volume"), 0.7)
def test_restore_volume_on_stop_respects_max_volume_limit(self):
self.config.set("bot", "restore_volume_on_stop", "True")
self.config.set("bot", "volume", "0.9")
self.settings_db.set("bot", "max_volume", "0.5")
bot = self.make_bot()
bot.restore_default_volume()
self.assertAlmostEqual(bot.volume_helper.plain_volume_set, 0.5)
self.assertAlmostEqual(self.settings_db.getfloat("bot", "volume"), 0.5)
def test_restore_volume_on_stop_is_disabled_by_default(self):
self.config.set("bot", "restore_volume_on_stop", "False")
self.config.set("bot", "volume", "0.7")
self.settings_db.set("bot", "volume", "1.0")
bot = self.make_bot()
bot.restore_default_volume()
self.assertAlmostEqual(bot.volume_helper.plain_volume_set, 1.0)
self.assertAlmostEqual(self.settings_db.getfloat("bot", "volume"), 1.0)
def test_stop_restores_default_volume(self):
self.config.set("bot", "restore_volume_on_stop", "True")
var.playlist = [object()]
bot = self.make_bot()
bot.restore_default_volume = mock.Mock()
bot._cleanup_ffmpeg_process = mock.Mock()
bot.stop()
bot.restore_default_volume.assert_called_once_with()
def test_pause_does_not_restore_default_volume(self):
var.playlist = FakePlaylist(types.SimpleNamespace(id="track-id"))
bot = self.make_bot()
bot.playhead = 12.5
bot.restore_default_volume = mock.Mock()
bot.pause()
bot.restore_default_volume.assert_not_called()
def test_loop_restores_volume_without_entering_pause_when_queue_runs_empty(self):
var.playlist = ExhaustedPlaylist()
bot = self.make_bot()
bot.mumble = FakeMumble()
bot.restore_default_volume = mock.Mock()
bot._cleanup_ffmpeg_process = mock.Mock()
bot.exit = False
bot.last_ffmpeg_err = ""
bot.pcm_buffer_size = 1
bot.read_pcm_size = 1
bot._loop_status = ""
bragi.MumbleBot.loop(bot)
bot.restore_default_volume.assert_called_once_with()
self.assertFalse(bot.is_pause)
class HelpTextTests(unittest.TestCase):
def test_help_text_does_not_reference_web_interface(self):
constants.load_lang("en_US")
help_text = constants.tr_cli("help")
admin_help = constants.tr_cli("admin_help")
self.assertNotIn("!<u>w</u>eb", help_text)
self.assertNotIn("web interface", help_text.lower())
self.assertNotIn("web interface", admin_help.lower())
class CommandRegistrationTests(unittest.TestCase):
def setUp(self):
self.config = configparser.ConfigParser(interpolation=None, allow_no_value=True)
self.config.read("configuration.default.ini", encoding="utf-8")
var.config = self.config
def test_register_all_commands_excludes_web_commands(self):
bot = FakeBot()
command.register_all_commands(bot)
registered = ",".join(bot.registered)
self.assertIn(self.config.get("commands", "volume"), registered)
self.assertNotIn("web", registered)
self.assertNotIn("webuseradd", registered)
self.assertNotIn("webuserlist", registered)
self.assertNotIn("webuserdel", registered)
if __name__ == "__main__":
unittest.main()