test_state.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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_xdg(tmp_path, monkeypatch):
  64. """Test that StateStore uses XDG_STATE_HOME for default path."""
  65. monkeypatch.delenv("STT_DAEMON_STATE", raising=False)
  66. monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path))
  67. store = StateStore()
  68. expected_path = tmp_path / "stt-daemon" / "audio.json"
  69. assert store.path == expected_path
  70. def test_state_store_creates_parent_dirs(tmp_path):
  71. """Test that save creates parent directories."""
  72. state_file = tmp_path / "deep" / "nested" / "dirs" / "audio.json"
  73. store = StateStore(state_file)
  74. state = AudioState(
  75. sink_name="VirtualMicSink",
  76. remap_name="VirtualMic",
  77. null_sink_module=10,
  78. remap_module=11,
  79. )
  80. store.save(state)
  81. assert state_file.parent.exists()
  82. assert state_file.exists()
  83. def test_state_store_load_respects_json_format(tmp_path):
  84. """Test that load respects the JSON format with loopbacks."""
  85. state_file = tmp_path / "audio.json"
  86. data = {
  87. "sink_name": "VirtualMicSink",
  88. "remap_name": "VirtualMic",
  89. "null_sink_module": 10,
  90. "remap_module": 11,
  91. "loopbacks": [
  92. {"module_id": 12, "source": "alsa_input.usb-Foo"},
  93. ],
  94. "selected_sources": ["alsa_input.usb-Foo"],
  95. }
  96. state_file.write_text(json.dumps(data))
  97. store = StateStore(state_file)
  98. loaded_state = store.load()
  99. assert loaded_state is not None
  100. assert loaded_state.selected_sources == ["alsa_input.usb-Foo"]