From 4a285b160fc0c106ccf9c42a7ebfd8cd5ed54744 Mon Sep 17 00:00:00 2001 From: Julien Olivain Date: Sat, 15 Jun 2024 11:16:11 +0200 Subject: [PATCH] support/testing: test_audio_codec_base.py: new helper class This is a helper class providing a template for testing audio codec programs such as lame mp3 encoder, flac tools, ogg vorbis-tools, ... Signed-off-by: Julien Olivain Signed-off-by: Thomas Petazzoni --- DEVELOPERS | 1 + .../tests/package/test_audio_codec_base.py | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 support/testing/tests/package/test_audio_codec_base.py diff --git a/DEVELOPERS b/DEVELOPERS index 90c34e51f5..44462dfdfd 100644 --- a/DEVELOPERS +++ b/DEVELOPERS @@ -1842,6 +1842,7 @@ F: support/testing/tests/package/test_acpica.py F: support/testing/tests/package/test_acpica/ F: support/testing/tests/package/test_apache.py F: support/testing/tests/package/test_attr.py +F: support/testing/tests/package/test_audio_codec_base.py F: support/testing/tests/package/test_bc.py F: support/testing/tests/package/test_bcc.py F: support/testing/tests/package/test_bcc/ diff --git a/support/testing/tests/package/test_audio_codec_base.py b/support/testing/tests/package/test_audio_codec_base.py new file mode 100644 index 0000000000..59c7efcad3 --- /dev/null +++ b/support/testing/tests/package/test_audio_codec_base.py @@ -0,0 +1,86 @@ +import math +import os + +import infra.basetest + + +class TestAudioCodecBase(infra.basetest.BRTest): + """Common class to test an audio codec package. + + This base class builds a Buildroot system image containing the + package enabled in its config, start the emulator, login to it. It + prepares an input test WAV file containing a tone. This WAV file + is encoded into a new output file in the native encoder format. It + is then decoded to a new output WAV file. This final output WAV + file is checked to contain the expected tone generated in the + initial input WAV file. There is no specific check about file + size, nor audio quality. The acceptance criteria is the + recognition of the tone. Note: the tone generation is made with + the Sox package, and the tone recognition is made with the Aubio + package. + + Each test case that inherits from this class must have: + __test__ = True - to let nose2 know that it is a test case + config - defconfig fragment with the packages to run the test + encode_test() - the function that runs the encoder and produces an + encoded file. + decode_test() - the function that runs the decoder and produces a + decode file. + """ + __test__ = False + config = infra.basetest.BASIC_TOOLCHAIN_CONFIG + \ + """ + BR2_PACKAGE_AUBIO=y + BR2_PACKAGE_SOX=y + BR2_TARGET_ROOTFS_CPIO=y + # BR2_TARGET_ROOTFS_TAR is not set + """ + input_file = "reference.wav" + decoded_file = "decoded.wav" + tone_freq = 440 # General Midi note A3 + + def login(self): + cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio") + self.emulator.boot(arch="armv5", + kernel="builtin", + options=["-initrd", cpio_file]) + self.emulator.login() + + def test_run(self): + self.login() + self.prepare_data() + self.encode_test(self.input_file) + self.decode_test(self.decoded_file) + self.check_test() + + def prepare_data(self): + # Generate a sinusoidal tone. + cmd = "sox -V -r 48000 -n -b 16 -c 1 " + cmd += self.input_file + cmd += f" synth 3 sin {self.tone_freq} vol -10dB" + self.assertRunOk(cmd) + + def encode_test(self, input_filename): + msg = "method must be implemented in derived class" + raise NotImplementedError(msg) + + def decode_test(self, output_filename): + msg = "method must be implemented in derived class" + raise NotImplementedError(msg) + + def note_from_freq(self, freq): + """Return a note number from the input frequency in Hertz.""" + return round((12 * math.log(freq / 440) / math.log(2)) + 69) + + def check_test(self): + expected_note = self.note_from_freq(self.tone_freq) + out, ret = self.emulator.run(f"aubionotes {self.decoded_file}", timeout=20) + self.assertEqual(ret, 0) + note_found = False + for line in out: + values = line.split() + if len(values) == 3: + note = round(float(values[0])) + if note == expected_note: + note_found = True + self.assertTrue(note_found, "The expected note was not found")