mohan-bee/mini-mp3-player

Portable, battery-powered MP3 player hardware with USB charging, microSD storage, an MCU, VS10xx audio decoder, headphone output, status controls, and regulated power rails.

Version
1.2.0
License
unset
Stars
0

firmware/tests/run.py

#!/usr/bin/env python3
"""Host integration tests. Images are synthetic filesystem fixtures, not audio."""
import pathlib, struct, subprocess, tempfile
root = pathlib.Path(__file__).resolve().parents[1]
def run(*args):
    subprocess.run(args, cwd=root, check=True)
with tempfile.TemporaryDirectory() as tmp:
    run('cc', '-std=c11', '-Wall', '-Wextra', '-Werror', '-Iinclude',
        'src/buttons.c', 'src/decoder.c', 'tests/test_controls.c', '-o', tmp+'/controls')
    run(tmp+'/controls')
    run('cc', '-std=c11', '-Wall', '-Wextra', '-Werror', '-Iinclude', '-Ivendor/fatfs',
        'src/diskio.c', 'tests/test_sd.c', '-o', tmp+'/sd')
    run(tmp+'/sd')
    run('cc', '-std=c11', '-Wall', '-Wextra', '-Ivendor/fatfs',
        'vendor/fatfs/ff.c', 'tests/test_fatfs.c', '-o', tmp+'/fatfs')
    for fat32 in (False, True):
        sectors, reserved, fat_sectors = (70000,32,550) if fat32 else (8192,1,32)
        root_sectors = 0 if fat32 else 32
        data_start = reserved+fat_sectors+root_sectors
        image = bytearray(sectors*512)
        def u16(at,v): struct.pack_into('<H',image,at,v)
        def u32(at,v): struct.pack_into('<I',image,at,v)
        image[:3]=b'\xeb\x58\x90'; image[3:11]=b'MP3TEST '
        u16(11,512); image[13]=1; u16(14,reserved); image[16]=1
        u16(17,0 if fat32 else 512); image[21]=0xf8
        if fat32:
            u32(32,sectors); u32(36,fat_sectors); u32(44,2)
            u16(48,1); image[66]=0x29; image[82:90]=b'FAT32   '
        else:
            u16(19,sectors); u16(22,fat_sectors); image[38]=0x29
            image[54:62]=b'FAT16   '
        image[510:512]=b'\x55\xaa'
        eof=0x0fffffff if fat32 else 0xffff
        def fat(cluster,value):
            (u32 if fat32 else u16)(reserved*512+cluster*(4 if fat32 else 2),value)
        fat(0,eof-7); fat(1,eof)
        if fat32: fat(2,eof)
        # Deliberately fragmented so cluster-chain traversal is exercised.
        chain=list(range(4,44,2))
        payload=bytes((i*17+3)&255 for i in range(10003))
        for i,cluster in enumerate(chain):
            fat(cluster,chain[i+1] if i+1<len(chain) else eof)
            offset=(data_start+cluster-2)*512
            chunk=payload[i*512:(i+1)*512]
            image[offset:offset+len(chunk)]=chunk
        directory=(data_start if fat32 else reserved+fat_sectors)*512
        image[directory:directory+11]=b'0001    MP3'; image[directory+11]=0x20
        u16(directory+26,chain[0]); u32(directory+28,len(payload))
        path=pathlib.Path(tmp)/('fat32.img' if fat32 else 'fat16.img')
        path.write_bytes(image); run(tmp+'/fatfs',str(path))