test_state.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. """Tests for audio state persistence."""
  2. import json
  3. from pathlib import Path
  4. from stt_daemon.state import AudioState, Loopback, StateStore
  5. def test_state_store_save_and_load(tmp_path):
  6. """Test round-trip save and load of state."""
  7. state_file = tmp_path / "audio.json"
  8. store = StateStore(state_file)
  9. original_state = AudioState(
  10. sink_name="VirtualMicSink",
  11. remap_name="VirtualMic",
  12. null_sink_module=10,
  13. remap_module=11,
  14. loopbacks=[
  15. Loopback(module_id=12, source="alsa_input.usb-Foo"),
  16. Loopback(module_id=13, source="alsa_output.pci-0000.monitor"),
  17. ],
  18. selected_sources=["alsa_input.usb-Foo", "alsa_output.pci-0000.monitor"],
  19. )
  20. store.save(original_state)
  21. loaded_state = store.load()
  22. assert loaded_state is not None
  23. assert loaded_state.sink_name == original_state.sink_name
  24. assert loaded_state.remap_name == original_state.remap_name
  25. assert loaded_state.null_sink_module == original_state.null_sink_module
  26. assert loaded_state.remap_module == original_state.remap_module
  27. assert len(loaded_state.loopbacks) == 2
  28. assert loaded_state.loopbacks[0].module_id == 12
  29. assert loaded_state.loopbacks[0].source == "alsa_input.usb-Foo"
  30. assert loaded_state.selected_sources == original_state.selected_sources
  31. def test_state_store_load_missing():
  32. """Test that load returns None for missing file."""
  33. store = StateStore(Path("/nonexistent/path/audio.json"))
  34. loaded_state = store.load()
  35. assert loaded_state is None
  36. def test_state_store_load_corrupt_json(tmp_path):
  37. """Test that load returns None for corrupt JSON."""
  38. state_file = tmp_path / "audio.json"
  39. state_file.write_text("{ invalid json }")
  40. store = StateStore(state_file)
  41. loaded_state = store.load()
  42. assert loaded_state is None
  43. def test_state_store_clear(tmp_path):
  44. """Test that clear removes the state file."""
  45. state_file = tmp_path / "audio.json"
  46. store = StateStore(state_file)
  47. state = AudioState(
  48. sink_name="VirtualMicSink",
  49. remap_name="VirtualMic",
  50. null_sink_module=10,
  51. remap_module=11,
  52. )
  53. store.save(state)
  54. assert state_file.exists()
  55. store.clear()
  56. assert not state_file.exists()
  57. def test_state_store_default_path_env_var(tmp_path, monkeypatch):
  58. """Test that StateStore uses STT_DAEMON_STATE env var."""
  59. state_file = tmp_path / "custom_audio.json"
  60. monkeypatch.setenv("STT_DAEMON_STATE", str(state_file))
  61. store = StateStore()
  62. assert store.path == state_file
  63. def test_state_store_default_path_is_project_local(tmp_path, monkeypatch):
  64. """Without overrides the state lives in ./audio.json, inside the project."""
  65. monkeypatch.delenv("STT_DAEMON_STATE", raising=False)
  66. store = StateStore()
  67. assert store.path == Path("audio.json")
  68. def test_state_store_creates_parent_dirs(tmp_path):
  69. """Test that save creates parent directories."""
  70. state_file = tmp_path / "deep" / "nested" / "dirs" / "audio.json"
  71. store = StateStore(state_file)
  72. state = AudioState(
  73. sink_name="VirtualMicSink",
  74. remap_name="VirtualMic",
  75. null_sink_module=10,
  76. remap_module=11,
  77. )
  78. store.save(state)
  79. assert state_file.parent.exists()
  80. assert state_file.exists()
  81. def test_state_store_load_respects_json_format(tmp_path):
  82. """Test that load respects the JSON format with loopbacks."""
  83. state_file = tmp_path / "audio.json"
  84. data = {
  85. "sink_name": "VirtualMicSink",
  86. "remap_name": "VirtualMic",
  87. "null_sink_module": 10,
  88. "remap_module": 11,
  89. "loopbacks": [
  90. {"module_id": 12, "source": "alsa_input.usb-Foo"},
  91. ],
  92. "selected_sources": ["alsa_input.usb-Foo"],
  93. }
  94. state_file.write_text(json.dumps(data))
  95. store = StateStore(state_file)
  96. loaded_state = store.load()
  97. assert loaded_state is not None
  98. assert loaded_state.selected_sources == ["alsa_input.usb-Foo"]