Files
Nonemm/tests/Nonemm.Recorder.Tests/RecorderConfigStoreTests.cs
erikbrockoandClaude Sonnet 5.5 c01032a2cc Add a sound recorder that writes several devices to one Opus file
nonemm-recorder is a separate program (src/Nonemm.Recorder.App) over a
library (src/Nonemm.Recorder). Each row in its list is one channel of one
device, and each row becomes one channel in an Ogg Opus file: 60 ms frames,
complexity 9, 32 kbps per channel by default.

- A device that is not connected keeps its row, in red, and writes silence.
  It is recorded from the moment it returns, also during a recording.
- Output devices are recorded as loopback: WASAPI on Windows, the
  "Monitor of" sources on PulseAudio and PipeWire.
- A headphone button plays one channel, in mono, on the default device.
- The rows, bitrate and folder are saved when the window closes.

SoundFlow 1.4.2 numbers its backend enum one higher than the miniaudio it
ships, so asking for PulseAudio gave ALSA and Wasapi gave DirectSound.
SoundFlowBackend lowers the value by one and checks which backend started.

Output devices and live capture are not tested on real hardware. The
tests use a fake sound system and decode the written file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 23:47:45 +00:00

52 lines
1.5 KiB
C#

namespace Nonemm.Recorder.Tests;
public class RecorderConfigStoreTests : IDisposable
{
private readonly string folder = Path.Combine(Path.GetTempPath(), $"recorder-{Guid.NewGuid():N}");
public void Dispose()
{
if (Directory.Exists(folder))
{
Directory.Delete(folder, true);
}
}
[Fact]
public void A_missing_file_gives_32_kbps_and_no_channels()
{
RecorderConfig config = new RecorderConfigStore(Path.Combine(folder, "none.json")).Load();
Assert.Equal(32, config.BitrateKbps);
Assert.Empty(config.Channels);
}
[Fact]
public void Channels_that_were_saved_come_back_in_order()
{
RecorderConfigStore store = new(Path.Combine(folder, "config.json"));
store.Save(new RecorderConfig
{
BitrateKbps = 48,
Channels = [new("Line In", false, 1, 2), new("Speakers", true, 0, 2)],
});
RecorderConfig loaded = store.Load();
Assert.Equal(48, loaded.BitrateKbps);
Assert.Equal(["Line In R", "Speakers (output) L"], loaded.Channels.Select(channel => channel.Label));
}
[Fact]
public void A_file_that_is_not_json_throws_with_its_path()
{
Directory.CreateDirectory(folder);
string path = Path.Combine(folder, "config.json");
File.WriteAllText(path, "not json");
InvalidDataException error = Assert.Throws<InvalidDataException>(() => new RecorderConfigStore(path).Load());
Assert.Contains(path, error.Message);
}
}