Close the test gaps mutation testing found in audio and draw
A mutation smoke run scored src/audio.c at 50% and src/draw.c at 70% and named three real holes: - Nothing asserted that an unconfigured voice is audible, which is the entire reason the voice table defaults to a square wave at full level instead of a zeroed struct. Deleting the defaults survived. - No envelope test used a non-zero attack and decay together, so the decay measuring from the start of the note rather than from the end of the attack survived. - The circle was only checked at its four axis points, which a mis-signed octant reflection survives. It now checks that every plotted pixel has a mirror in the other three quadrants. Also adds a backend that exists but was never given an SDL_Renderer, which is the state a host is in between allocating one and initializing it; every draw entry point has to report it rather than dereference it. audio 50% -> 75%, draw 70% -> 90%. The survivors are recorded in TODO.md and are honestly untestable from here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,8 +23,13 @@
|
||||
|
||||
#include "testutil.h"
|
||||
|
||||
/** @brief Frames of output most tests generate at a time. */
|
||||
#define TEST_MIX_FRAMES 512
|
||||
/**
|
||||
* @brief Frames of output most tests generate at a time.
|
||||
*
|
||||
* Enough to hold a 10 ms attack and a 10 ms decay back to back at 44100 frames
|
||||
* per second, which is the longest single stretch any test here inspects.
|
||||
*/
|
||||
#define TEST_MIX_FRAMES 1024
|
||||
|
||||
/** @brief 441 Hz at 44100 frames per second is exactly 100 frames per cycle. */
|
||||
#define TEST_TONE_HZ 441.0f
|
||||
@@ -67,6 +72,30 @@ static float32_t peak_of(int frames)
|
||||
return peak;
|
||||
}
|
||||
|
||||
akerr_ErrorContext *test_audio_defaults(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
|
||||
ATTEMPT {
|
||||
// Deliberately the first thing this file does, and deliberately without
|
||||
// a call to reset_audio(): a voice nobody has configured has to make a
|
||||
// sound. A zeroed voice would have a sustain of 0.0 and be silent, with
|
||||
// no error to say why, which is exactly the trap this defends against.
|
||||
CATCH(errctx, akgl_audio_tone(0, TEST_TONE_HZ, 1000));
|
||||
CATCH(errctx, akgl_audio_mix(samples, TEST_MIX_FRAMES));
|
||||
TEST_ASSERT_FEQ(errctx, samples[0], 1.0f,
|
||||
"an unconfigured voice mixed to %f, expected a full-level square wave",
|
||||
samples[0]);
|
||||
TEST_ASSERT_FEQ(errctx, samples[50], -1.0f,
|
||||
"an unconfigured voice is not a square wave (%f half a cycle in)",
|
||||
samples[50]);
|
||||
} CLEANUP {
|
||||
IGNORE(reset_audio());
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *test_audio_silence(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
@@ -254,6 +283,21 @@ akerr_ErrorContext *test_audio_envelope(void)
|
||||
TEST_ASSERT_FEQ(errctx, samples[450], -0.5f,
|
||||
"after the decay the level is %f, expected the 0.5 sustain", samples[450]);
|
||||
|
||||
// Attack and decay together, which is the case where the decay has to
|
||||
// measure from the end of the attack rather than from the start of the
|
||||
// note. 10 ms of each is 441 frames of each at 44100.
|
||||
CATCH(errctx, reset_audio());
|
||||
CATCH(errctx, akgl_audio_envelope(0, 10, 10, 0.5f, 0));
|
||||
CATCH(errctx, akgl_audio_tone(0, TEST_TONE_HZ, 1000));
|
||||
CATCH(errctx, akgl_audio_mix(samples, TEST_MIX_FRAMES));
|
||||
TEST_ASSERT_FEQ(errctx, samples[441], 1.0f,
|
||||
"at the end of the attack the level is %f, expected full", samples[441]);
|
||||
TEST_ASSERT_FEQ(errctx, samples[661], -(1.0f - (0.5f * (220.0f / 441.0f))),
|
||||
"halfway through the decay the level is %f, expected the halfway ramp",
|
||||
samples[661]);
|
||||
TEST_ASSERT_FEQ(errctx, samples[900], 0.5f,
|
||||
"after the decay the level is %f, expected the 0.5 sustain", samples[900]);
|
||||
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_OUTOFBOUNDS, akgl_audio_envelope(0, 1, 1, -0.1f, 1),
|
||||
"setting a negative sustain level");
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_OUTOFBOUNDS, akgl_audio_envelope(0, 1, 1, 1.5f, 1),
|
||||
@@ -432,6 +476,7 @@ int main(void)
|
||||
"Couldn't initialize SDL: %s",
|
||||
SDL_GetError());
|
||||
|
||||
CATCH(errctx, test_audio_defaults());
|
||||
CATCH(errctx, test_audio_silence());
|
||||
CATCH(errctx, test_audio_square_tone());
|
||||
CATCH(errctx, test_audio_waveforms());
|
||||
|
||||
Reference in New Issue
Block a user