[PATCH] kselftest: alsa: Add test case for writing invalid values
Shuah Khan
skhan at linuxfoundation.org
Mon Jan 24 22:35:53 CET 2022
On 1/24/22 8:14 AM, Mark Brown wrote:
> Attempt to write various invalid values for control types we know about and
> check that something sensible happens. The ABI isn't quite as clearly
> defined as one might like, rather than generating an error when an invalid
> value is written many devices will silently rewrite the value into one that
> is valid for the control. The exact value chosen is not predictable so in
> the case the write succeeds we just check that the value we read back is
> one that is valid for the control.
>
> Signed-off-by: Mark Brown <broonie at kernel.org>
> ---
> tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++-
> 1 file changed, 221 insertions(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
> index 17f158d7a767..15e05b241468 100644
> --- a/tools/testing/selftests/alsa/mixer-test.c
> +++ b/tools/testing/selftests/alsa/mixer-test.c
> @@ -13,6 +13,7 @@
> #include <stdio.h>
> #include <stdlib.h>
> #include <stdbool.h>
> +#include <limits.h>
> #include <string.h>
> #include <getopt.h>
> #include <stdarg.h>
> @@ -26,7 +27,7 @@
>
> #include "../kselftest.h"
>
> -#define TESTS_PER_CONTROL 3
> +#define TESTS_PER_CONTROL 4
>
> struct card_data {
> snd_ctl_t *handle;
> @@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl)
> ctl->card->card, ctl->elem);
> }
>
> +bool test_ctl_write_invalid_value(struct ctl_data *ctl,
> + snd_ctl_elem_value_t *val)
> +{
> + int err;
> + long val_read;
> +
> + /* Ideally this will fail... */
> + err = snd_ctl_elem_write(ctl->card->handle, val);
> + if (err < 0)
> + return false;
> +
> + /* ...but some devices will clamp to an in range value */
> + err = snd_ctl_elem_read(ctl->card->handle, val);
> + if (err < 0) {
> + ksft_print_msg("%s failed to read: %s\n",
> + ctl->name, snd_strerror(err));
> + return true;
> + }
> +
> + return !ctl_value_valid(ctl, val);
> +}
> +
> +bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
> +{
> + int err, i;
> + long val_read;
> + bool fail = false;
> + snd_ctl_elem_value_t *val;
> + snd_ctl_elem_value_alloca(&val);
> +
> + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_boolean(val, i, 2);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> + }
> +
> + return !fail;
> +}
> +
> +bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
> +{
> + int i;
> + bool fail = false;
> + snd_ctl_elem_value_t *val;
> + snd_ctl_elem_value_alloca(&val);
> +
> + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> + if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
> + /* Just under range */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer(val, i,
> + snd_ctl_elem_info_get_min(ctl->info) - 1);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> +
> + /* Minimum representable value */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> + }
> +
> + if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
> + /* Just over range */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer(val, i,
> + snd_ctl_elem_info_get_max(ctl->info) + 1);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> +
> + /* Maximum representable value */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> + }
> + }
> +
> + return !fail;
> +}
> +
> +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
> +{
> + int i;
> + bool fail = false;
> + snd_ctl_elem_value_t *val;
> + snd_ctl_elem_value_alloca(&val);
> +
> + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> + if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
> + /* Just under range */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer64(val, i,
> + snd_ctl_elem_info_get_min64(ctl->info) - 1);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> +
> + /* Minimum representable value */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> + }
> +
> + if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
> + /* Just over range */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer64(val, i,
> + snd_ctl_elem_info_get_max64(ctl->info) + 1);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> +
> + /* Maximum representable value */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> + }
> + }
> +
> + return !fail;
> +}
> +
> +bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
> +{
> + int err, i;
> + unsigned int val_read;
> + bool fail = false;
> + snd_ctl_elem_value_t *val;
> + snd_ctl_elem_value_alloca(&val);
> +
> + snd_ctl_elem_value_set_id(val, ctl->id);
> +
> + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> + /* One beyond maximum */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_enumerated(val, i,
> + snd_ctl_elem_info_get_items(ctl->info));
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> +
> + /* Maximum representable value */
> + snd_ctl_elem_value_copy(val, ctl->def_val);
> + snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
> +
> + if (test_ctl_write_invalid_value(ctl, val))
> + fail = true;
> +
> + }
> +
> + return !fail;
> +}
> +
> +
> +void test_ctl_write_invalid(struct ctl_data *ctl)
> +{
> + bool pass;
> + int err;
> +
> + /* If the control is turned off let's be polite */
> + if (snd_ctl_elem_info_is_inactive(ctl->info)) {
> + ksft_print_msg("%s is inactive\n", ctl->name);
> + ksft_test_result_skip("write_invalid.%d.%d\n",
> + ctl->card->card, ctl->elem);
> + return;
> + }
> +
> + if (!snd_ctl_elem_info_is_writable(ctl->info)) {
> + ksft_print_msg("%s is not writeable\n", ctl->name);
> + ksft_test_result_skip("write_invalid.%d.%d\n",
> + ctl->card->card, ctl->elem);
> + return;
> + }
> +
> + switch (snd_ctl_elem_info_get_type(ctl->info)) {
> + case SND_CTL_ELEM_TYPE_BOOLEAN:
> + pass = test_ctl_write_invalid_boolean(ctl);
> + break;
> +
> + case SND_CTL_ELEM_TYPE_INTEGER:
> + pass = test_ctl_write_invalid_integer(ctl);
> + break;
> +
> + case SND_CTL_ELEM_TYPE_INTEGER64:
> + pass = test_ctl_write_invalid_integer64(ctl);
> + break;
> +
> + case SND_CTL_ELEM_TYPE_ENUMERATED:
> + pass = test_ctl_write_invalid_enumerated(ctl);
> + break;
> +
> + default:
> + /* No tests for this yet */
> + ksft_test_result_skip("write_invalid.%d.%d\n",
> + ctl->card->card, ctl->elem);
> + return;
> + }
> +
> + /* Restore the default value to minimise disruption */
> + err = write_and_verify(ctl, ctl->def_val, NULL);
Why not read the value and then restore the value, in case the value
before the write test is different from the ctl->def_val?
> + if (err < 0)
> + pass = false;
> +
> + ksft_test_result(pass, "write_invalid.%d.%d\n",
> + ctl->card->card, ctl->elem);
> +}
> +
> int main(void)
> {
> struct ctl_data *ctl;
> @@ -697,6 +916,7 @@ int main(void)
> test_ctl_get_value(ctl);
> test_ctl_write_default(ctl);
> test_ctl_write_valid(ctl);
> + test_ctl_write_invalid(ctl);
> }
>
> ksft_exit_pass();
>
thanks,
-- Shuah
More information about the Alsa-devel
mailing list