cc-libs/packages/trapos-music/tests/libmusic.lua
2026-06-20 13:45:28 +02:00

166 lines
5.4 KiB
Lua

local createLibTest = require('/apis/libtest');
local createMusic = require('/apis/libmusic');
local testlib = createLibTest({ ... });
local function fakeFs(dir, entries)
return {
isDir = function(path) return path == dir; end,
list = function(path)
if path ~= dir then error('unexpected list: ' .. tostring(path)); end
return entries;
end,
};
end
testlib.test('listTracks filters dfpwm, strips extension and sorts', function()
local music = createMusic({
dir = '/data/musics',
fs = fakeFs('/data/musics', { 'Zebra.dfpwm', 'notes.txt', 'Apple.dfpwm' }),
});
local tracks = music.listTracks();
testlib.assertEquals(#tracks, 2);
testlib.assertEquals(tracks[1].name, 'Apple');
testlib.assertEquals(tracks[1].path, '/data/musics/Apple.dfpwm');
testlib.assertEquals(tracks[2].name, 'Zebra');
testlib.assertEquals(tracks[2].path, '/data/musics/Zebra.dfpwm');
end);
testlib.test('listTracks returns empty when directory is missing', function()
local music = createMusic({
dir = '/data/musics',
fs = { isDir = function() return false; end },
});
testlib.assertEquals(#music.listTracks(), 0);
end);
-- A speaker whose queue accepts `capacity` buffers before refusing. Tests
-- simulate a drained queue by resetting `queued` before firing the captured
-- `speaker_audio_empty` handler.
local function fakeSpeaker(capacity)
local s = { played = {}, stops = 0, queued = 0, capacity = capacity or 1 };
s.playAudio = function(buffer)
if s.queued >= s.capacity then
return false;
end
s.queued = s.queued + 1;
s.played[#s.played + 1] = buffer;
return true;
end;
s.stop = function()
s.stops = s.stops + 1;
end;
return s;
end
local function fakeHandle(chunks)
local index = 0;
return {
closed = false,
read = function(_, _)
index = index + 1;
return chunks[index];
end,
close = function(self)
self.closed = true;
end,
};
end
-- identity decoder: the played buffers equal the raw chunks for easy asserts
local fakeDfpwm = { make_decoder = function() return function(chunk) return chunk; end; end };
local function makePlayer(opts)
local captured = {};
local eventloop = {
register = function(event, handler)
captured[event] = handler;
return function() end;
end,
};
local music = createMusic({
peripheral = { find = function(name) return name == 'speaker' and opts.speaker or nil; end },
io = { open = function(path) return opts.handles[path]; end },
dfpwm = fakeDfpwm,
});
local player = music.createPlayer(eventloop);
return player, captured, eventloop;
end
testlib.test('play opens the file, primes the speaker and reports playing', function()
local speaker = fakeSpeaker(1);
local handle = fakeHandle({ 'a', 'b' });
local player = makePlayer({ speaker = speaker, handles = { ['/song.dfpwm'] = handle } });
player.play('/song.dfpwm');
testlib.assertEquals(player.getStatus(), 'playing');
testlib.assertEquals(player.getCurrentPath(), '/song.dfpwm');
testlib.assertEquals(#speaker.played, 1);
testlib.assertEquals(speaker.played[1], 'a');
end);
testlib.test('back-pressure resumes feeding on speaker_audio_empty', function()
local speaker = fakeSpeaker(1);
local handle = fakeHandle({ 'a', 'b' });
local player, captured = makePlayer({ speaker = speaker, handles = { ['/song.dfpwm'] = handle } });
player.play('/song.dfpwm'); -- 'a' queued, 'b' refused (pending)
testlib.assertEquals(#speaker.played, 1);
speaker.queued = 0; -- simulate the queue draining
captured.speaker_audio_empty();
testlib.assertEquals(speaker.played[2], 'b'); -- pending buffer replayed
testlib.assertEquals(player.getStatus(), 'stopped'); -- then EOF
end);
testlib.test('pause stops feeding and resume loses no buffered chunk', function()
local speaker = fakeSpeaker(1);
local handle = fakeHandle({ 'a', 'b', 'c' });
local player, captured = makePlayer({ speaker = speaker, handles = { ['/song.dfpwm'] = handle } });
player.play('/song.dfpwm'); -- 'a' queued, 'b' pending
player.pause();
testlib.assertEquals(player.getStatus(), 'paused');
speaker.queued = 0;
captured.speaker_audio_empty(); -- no-op while paused
testlib.assertEquals(#speaker.played, 1);
player.resume();
testlib.assertEquals(player.getStatus(), 'playing');
testlib.assertEquals(speaker.played[2], 'b'); -- pending 'b' fed, nothing dropped
end);
testlib.test('stop clears the speaker, closes the handle and resets state', function()
local speaker = fakeSpeaker(1);
local handle = fakeHandle({ 'a', 'b' });
local player = makePlayer({ speaker = speaker, handles = { ['/song.dfpwm'] = handle } });
player.play('/song.dfpwm');
player.stop();
testlib.assertEquals(speaker.stops, 1);
testlib.assertTrue(handle.closed, 'handle should be closed');
testlib.assertEquals(player.getStatus(), 'stopped');
testlib.assertEquals(player.getCurrentPath(), nil);
end);
testlib.test('reaching the end fires onFinish once and stays stopped', function()
local speaker = fakeSpeaker(10); -- large queue: whole track flows in one pump
local handle = fakeHandle({ 'a', 'b' });
local player = makePlayer({ speaker = speaker, handles = { ['/song.dfpwm'] = handle } });
local finishes = 0;
player.onFinish(function() finishes = finishes + 1; end);
player.play('/song.dfpwm');
testlib.assertEquals(finishes, 1);
testlib.assertEquals(player.getStatus(), 'stopped');
testlib.assertEquals(#speaker.played, 2);
end);
testlib.run();