ASoC: topology: KUnit: Add KUnit tests passing various arguments to snd_soc_tplg_comp...
authorAmadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Wed, 20 Jan 2021 15:28:44 +0000 (16:28 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 21 Jan 2021 12:36:24 +0000 (12:36 +0000)
In order to ensure correct behaviour of topology API, add unit tests
exercising topology functionality.

Start with adding cases for passing various arguments to
snd_soc_tplg_component_load as it is part of exposed topology API.

First test case adds test passing NULL component as argument.
Following one adds test case for passing NULL ops as argument.
Finally add test case passing NULL fw as argument.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Tested-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20210120152846.1703655-4-amadeuszx.slawinski@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/soc-topology-test.c [new file with mode: 0644]

index ba79d5f4e29954ce2ad718dcfefe8016f2c3ae2b..640494f76cbd1d5b839337864ded6de23d16ed1d 100644 (file)
@@ -37,6 +37,23 @@ config SND_SOC_COMPRESS
 config SND_SOC_TOPOLOGY
        bool
 
+config SND_SOC_TOPOLOGY_KUNIT_TESTS
+       tristate "KUnit tests for SoC topology"
+       depends on KUNIT
+       depends on SND_SOC_TOPOLOGY
+       default KUNIT_ALL_TESTS
+       help
+         If you want to perform tests on ALSA SoC topology support say Y here.
+
+         This builds a module which can be later manually loaded to run KUNIT
+         test cases against soc-topology.c API. This should be primarily used
+         by developers to test their changes to ASoC.
+
+         Do note that it creates fake playback devices which do not interact
+         well with userspace. When running tests one may want to disable
+         userspace applications such as pulseaudio, to prevent unnecessary
+         problems.
+
 config SND_SOC_ACPI
        tristate
 
index eee370492eeeb47cdadf6ce1474c9f7eddb5997e..f56ad996eae88bc5fda09250e130d4e84d2c3568 100644 (file)
@@ -7,6 +7,11 @@ ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
 snd-soc-core-objs += soc-topology.o
 endif
 
+ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TESTS),)
+# snd-soc-test-objs := soc-topology-test.o
+obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TESTS) := soc-topology-test.o
+endif
+
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
 endif
diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c
new file mode 100644 (file)
index 0000000..7276ed9
--- /dev/null
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-topology-test.c  --  ALSA SoC Topology Kernel Unit Tests
+ *
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/firmware.h>
+#include <linux/random.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-topology.h>
+#include <kunit/test.h>
+
+/* ===== HELPER FUNCTIONS =================================================== */
+
+/*
+ * snd_soc_component needs device to operate on (primarily for prints), create
+ * fake one, as we don't register with PCI or anything else
+ * device_driver name is used in some of the prints (fmt_single_name) so
+ * we also mock up minimal one
+ */
+static struct device *test_dev;
+
+static struct device_driver test_drv = {
+       .name = "sound-soc-topology-test-driver",
+};
+
+static int snd_soc_tplg_test_init(struct kunit *test)
+{
+       test_dev = root_device_register("sound-soc-topology-test");
+       test_dev = get_device(test_dev);
+       if (!test_dev)
+               return -ENODEV;
+
+       test_dev->driver = &test_drv;
+
+       return 0;
+}
+
+static void snd_soc_tplg_test_exit(struct kunit *test)
+{
+       put_device(test_dev);
+       root_device_unregister(test_dev);
+}
+
+/*
+ * helper struct we use when registering component, as we load topology during
+ * component probe, we need to pass struct kunit somehow to probe function, so
+ * we can report test result
+ */
+struct kunit_soc_component {
+       struct kunit *kunit;
+       int expect; /* what result we expect when loading topology */
+       struct snd_soc_component comp;
+       struct snd_soc_card card;
+       struct firmware fw;
+};
+
+static int d_probe(struct snd_soc_component *component)
+{
+       struct kunit_soc_component *kunit_comp =
+                       container_of(component, struct kunit_soc_component, comp);
+       int ret;
+
+       ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw);
+       KUNIT_EXPECT_EQ_MSG(kunit_comp->kunit, kunit_comp->expect, ret,
+                           "Failed topology load");
+
+       return 0;
+}
+
+static void d_remove(struct snd_soc_component *component)
+{
+       struct kunit_soc_component *kunit_comp =
+                       container_of(component, struct kunit_soc_component, comp);
+       int ret;
+
+       ret = snd_soc_tplg_component_remove(component);
+       KUNIT_EXPECT_EQ(kunit_comp->kunit, 0, ret);
+}
+
+/*
+ * ASoC minimal boiler plate
+ */
+SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("sound-soc-topology-test")));
+
+static struct snd_soc_dai_link kunit_dai_links[] = {
+       {
+               .name = "KUNIT Audio Port",
+               .id = 0,
+               .stream_name = "Audio Playback/Capture",
+               .nonatomic = 1,
+               .dynamic = 1,
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               SND_SOC_DAILINK_REG(dummy, dummy, platform),
+       },
+};
+
+static const struct snd_soc_component_driver test_component = {
+       .name = "sound-soc-topology-test",
+       .probe = d_probe,
+       .remove = d_remove,
+       .non_legacy_dai_naming = 1,
+};
+
+/* ===== TEST CASES ========================================================= */
+
+// TEST CASE
+// Test passing NULL component as parameter to snd_soc_tplg_component_load
+
+/*
+ * need to override generic probe function with one using NULL when calling
+ * topology load during component initialization, we don't need .remove
+ * handler as load should fail
+ */
+static int d_probe_null_comp(struct snd_soc_component *component)
+{
+       struct kunit_soc_component *kunit_comp =
+                       container_of(component, struct kunit_soc_component, comp);
+       int ret;
+
+       /* instead of passing component pointer as first argument, pass NULL here */
+       ret = snd_soc_tplg_component_load(NULL, NULL, &kunit_comp->fw);
+       KUNIT_EXPECT_EQ_MSG(kunit_comp->kunit, kunit_comp->expect, ret,
+                           "Failed topology load");
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver test_component_null_comp = {
+       .name = "sound-soc-topology-test",
+       .probe = d_probe_null_comp,
+       .non_legacy_dai_naming = 1,
+};
+
+static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test)
+{
+       struct kunit_soc_component *kunit_comp;
+       int ret;
+
+       /* prepare */
+       kunit_comp = kunit_kzalloc(test, sizeof(*kunit_comp), GFP_KERNEL);
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, kunit_comp);
+       kunit_comp->kunit = test;
+       kunit_comp->expect = -EINVAL; /* expect failure */
+
+       kunit_comp->card.dev = test_dev,
+       kunit_comp->card.name = "kunit-card",
+       kunit_comp->card.owner = THIS_MODULE,
+       kunit_comp->card.dai_link = kunit_dai_links,
+       kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links),
+       kunit_comp->card.fully_routed = true,
+
+       /* run test */
+       ret = snd_soc_register_card(&kunit_comp->card);
+       if (ret != 0 && ret != -EPROBE_DEFER)
+               KUNIT_FAIL(test, "Failed to register card");
+
+       ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       /* cleanup */
+       ret = snd_soc_unregister_card(&kunit_comp->card);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       snd_soc_unregister_component(test_dev);
+}
+
+// TEST CASE
+// Test passing NULL ops as parameter to snd_soc_tplg_component_load
+
+/*
+ * NULL ops is default case, we pass empty topology (fw), so we don't have
+ * anything to parse and just do nothing, which results in return 0; from
+ * calling soc_tplg_dapm_complete in soc_tplg_process_headers
+ */
+static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test)
+{
+       struct kunit_soc_component *kunit_comp;
+       int ret;
+
+       /* prepare */
+       kunit_comp = kunit_kzalloc(test, sizeof(*kunit_comp), GFP_KERNEL);
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, kunit_comp);
+       kunit_comp->kunit = test;
+       kunit_comp->expect = 0; /* expect success */
+
+       kunit_comp->card.dev = test_dev,
+       kunit_comp->card.name = "kunit-card",
+       kunit_comp->card.owner = THIS_MODULE,
+       kunit_comp->card.dai_link = kunit_dai_links,
+       kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links),
+       kunit_comp->card.fully_routed = true,
+
+       /* run test */
+       ret = snd_soc_register_card(&kunit_comp->card);
+       if (ret != 0 && ret != -EPROBE_DEFER)
+               KUNIT_FAIL(test, "Failed to register card");
+
+       ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       /* cleanup */
+       ret = snd_soc_unregister_card(&kunit_comp->card);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       snd_soc_unregister_component(test_dev);
+}
+
+// TEST CASE
+// Test passing NULL fw as parameter to snd_soc_tplg_component_load
+
+/*
+ * need to override generic probe function with one using NULL pointer to fw
+ * when calling topology load during component initialization, we don't need
+ * .remove handler as load should fail
+ */
+static int d_probe_null_fw(struct snd_soc_component *component)
+{
+       struct kunit_soc_component *kunit_comp =
+                       container_of(component, struct kunit_soc_component, comp);
+       int ret;
+
+       /* instead of passing fw pointer as third argument, pass NULL here */
+       ret = snd_soc_tplg_component_load(component, NULL, NULL);
+       KUNIT_EXPECT_EQ_MSG(kunit_comp->kunit, kunit_comp->expect, ret,
+                           "Failed topology load");
+
+       return 0;
+}
+
+static const struct snd_soc_component_driver test_component_null_fw = {
+       .name = "sound-soc-topology-test",
+       .probe = d_probe_null_fw,
+       .non_legacy_dai_naming = 1,
+};
+
+static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test)
+{
+       struct kunit_soc_component *kunit_comp;
+       int ret;
+
+       /* prepare */
+       kunit_comp = kunit_kzalloc(test, sizeof(*kunit_comp), GFP_KERNEL);
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, kunit_comp);
+       kunit_comp->kunit = test;
+       kunit_comp->expect = -EINVAL; /* expect failure */
+
+       kunit_comp->card.dev = test_dev,
+       kunit_comp->card.name = "kunit-card",
+       kunit_comp->card.owner = THIS_MODULE,
+       kunit_comp->card.dai_link = kunit_dai_links,
+       kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links),
+       kunit_comp->card.fully_routed = true,
+
+       /* run test */
+       ret = snd_soc_register_card(&kunit_comp->card);
+       if (ret != 0 && ret != -EPROBE_DEFER)
+               KUNIT_FAIL(test, "Failed to register card");
+
+       ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       /* cleanup */
+       ret = snd_soc_unregister_card(&kunit_comp->card);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+
+       snd_soc_unregister_component(test_dev);
+}
+
+/* ===== KUNIT MODULE DEFINITIONS =========================================== */
+
+static struct kunit_case snd_soc_tplg_test_cases[] = {
+       KUNIT_CASE(snd_soc_tplg_test_load_with_null_comp),
+       KUNIT_CASE(snd_soc_tplg_test_load_with_null_ops),
+       KUNIT_CASE(snd_soc_tplg_test_load_with_null_fw),
+       {}
+};
+
+static struct kunit_suite snd_soc_tplg_test_suite = {
+       .name = "snd_soc_tplg_test",
+       .init = snd_soc_tplg_test_init,
+       .exit = snd_soc_tplg_test_exit,
+       .test_cases = snd_soc_tplg_test_cases,
+};
+
+kunit_test_suites(&snd_soc_tplg_test_suite);
+
+MODULE_LICENSE("GPL");