166 lines
5.4 KiB
Lua
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();
|