From patchwork Sun Dec 14 17:54:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 906 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765734957; bh=JaQB2xO/Q0Vv8B0t0sVQMFGIr+WoEGVhXutxcPYwBZw=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=D4Msd2x2W1kxodyJ2ybV0owbr6rkZqtZbIDC+k4ZAjud5OHOfW6ZWt50VkN9qNjuG /Srx4OMdOfjiDcpZHHGMnFF4wWQeXTkVuMPnN3zGUpDfzSN9tB/jm6Qie1eQoSz5dH mVTKQtcljVqZKnur0gIiriZfG7GqjBqnOtnxKFa52NoVS5Vl+87b+RutndOwWWsK1R O2UQf/GkbWNsz2w2rccOHRWwTmVYY9SS/nwBzjF/iMxArakfOGe6xO7ynPuOPFEBd7 KQTQhhP3MXIOITPwzJeyJfzWPr44IR/iESZPcEg/HHuTenvaSySLbnzuTyf4Spp82L XGlewjXubpGSQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 435BF68A96 for ; Sun, 14 Dec 2025 10:55:57 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id ReNPpfrdDMBF for ; Sun, 14 Dec 2025 10:55:57 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765734957; bh=JaQB2xO/Q0Vv8B0t0sVQMFGIr+WoEGVhXutxcPYwBZw=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=D4Msd2x2W1kxodyJ2ybV0owbr6rkZqtZbIDC+k4ZAjud5OHOfW6ZWt50VkN9qNjuG /Srx4OMdOfjiDcpZHHGMnFF4wWQeXTkVuMPnN3zGUpDfzSN9tB/jm6Qie1eQoSz5dH mVTKQtcljVqZKnur0gIiriZfG7GqjBqnOtnxKFa52NoVS5Vl+87b+RutndOwWWsK1R O2UQf/GkbWNsz2w2rccOHRWwTmVYY9SS/nwBzjF/iMxArakfOGe6xO7ynPuOPFEBd7 KQTQhhP3MXIOITPwzJeyJfzWPr44IR/iESZPcEg/HHuTenvaSySLbnzuTyf4Spp82L XGlewjXubpGSQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2E63F68ADF for ; Sun, 14 Dec 2025 10:55:57 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765734955; bh=dkMe7uAhjYOv8pW/l5Eqx/SHmaELk6r4/ggg6+AfLqU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HWYCtXG22vuZIkAMD5zc4feYl7pSeHyn/fk7qudaUEeH9gwW539bA8AYYXU71ijyQ yKuduXf1bnepu9IS2RHD1SDVK5uEogF+c3xTTbuBSy7vT/oE8ugcY00GhnIOQp4Il4 qYHrp/fjtf6/iYX0iROUOzC3RYEfLN3I7MYWvYtgegLgRJrVDw0cIFfuE4uYF/G9sy bAbSDaypT3P8jWe7hrPtCWNeUcwdM5tBBR8h//CMzM/FIOhxbKKd2AtIPxlEaY7hLd kJEfFt2K4F5FB2athowfTlixspaJ5TA1BeoucQCFKbcFC1bInWlk2fap8oxpiUaLh0 Gutoxx3dmX6yg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3403C68A91; Sun, 14 Dec 2025 10:55:55 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id QisGQ7I14cBd; Sun, 14 Dec 2025 10:55:55 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765734951; bh=JqpN1v2Tki4WFot9SBddjhSh1QpbpSr/eaBQ6n7E/ig=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oTTcqZrAH/jFVXfNgVIfGCVylSK08IPYeKRBKS1bYlC/PMGwmx099XgwiynVDQKCY AxXd4OjIcMqQtRYLLVTlt1BgG0fx/fCCaesA29bxLVzR2BrjHXDpwi3tL0pYJM7ty6 5rjv8Lj4YjCQ3Mg9JCL7yNaeq4hOCLJfvXXLwxe6A5Q6xU/5yi9mKhg2Qvamcb+Z5C LUIzNhwzRV701ILodOqVuIsoEhf5wbrXlwncuavPgWMWwGU25H+UEuayKDJfYRUcAB wm/IokPaSQAML06GJp+5CHRrQipRTJCgBQy3/demt+U1tpccpvXRpWwClCwQeWEepY qRM6/hTMS+xUA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id C240468A89; Sun, 14 Dec 2025 10:55:50 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Sun, 14 Dec 2025 10:54:35 -0700 Message-ID: <20251214175449.3799539-14-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251214175449.3799539-1-sjg@u-boot.org> References: <20251214175449.3799539-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: BGSSZCPJXYUKQYS4OHL5WRUPKNACKQSY X-Message-ID-Hash: BGSSZCPJXYUKQYS4OHL5WRUPKNACKQSY X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , "Claude Opus 4 . 5" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 13/21] test: Add support for passing arguments to C tests List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add support for passing key=value arguments to unit tests. The test framework parses arguments based on definitions provided by each test and makes them available via uts->args[] For now the 'ut' command does not support this new feature. Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- arch/sandbox/cpu/spl.c | 3 +- include/test/test.h | 6 ++ include/test/ut.h | 4 +- test/cmd_ut.c | 3 +- test/test-main.c | 169 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 178 insertions(+), 7 deletions(-) diff --git a/arch/sandbox/cpu/spl.c b/arch/sandbox/cpu/spl.c index 7ee4975523e..162b678314e 100644 --- a/arch/sandbox/cpu/spl.c +++ b/arch/sandbox/cpu/spl.c @@ -152,7 +152,8 @@ void spl_board_init(void) ut_init_state(&uts); ret = ut_run_list(&uts, "spl", NULL, tests, count, - state->select_unittests, 1, false, NULL); + state->select_unittests, 1, false, NULL, + 0, NULL); ut_report(&uts.cur, 1); ut_uninit_state(&uts); /* continue execution into U-Boot */ diff --git a/include/test/test.h b/include/test/test.h index cb0539e3bdd..da38b8ee4f0 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -9,6 +9,8 @@ #include #include +#define UT_MAX_ARGS 8 + /** * struct ut_stats - Statistics about tests run * @@ -90,6 +92,8 @@ struct ut_arg { * @soft_fail: continue execution of the test even after it fails * @expect_str: Temporary string used to hold expected string value * @actual_str: Temporary string used to hold actual string value + * @args: Parsed argument values for current test + * @arg_count: Number of parsed arguments */ struct unit_test_state { struct ut_stats cur; @@ -116,6 +120,8 @@ struct unit_test_state { bool soft_fail; char expect_str[1024]; char actual_str[1024]; + struct ut_arg args[UT_MAX_ARGS]; + int arg_count; }; /* Test flags for each test */ diff --git a/include/test/ut.h b/include/test/ut.h index 7098c9be7d6..90b9bf79929 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -618,12 +618,14 @@ void ut_uninit_state(struct unit_test_state *uts); * name is the name of the test to run. This is used to find which test causes * another test to fail. If the one test fails, testing stops immediately. * Pass NULL to disable this + * @argc: Number of test arguments (key=value pairs), 0 if none + * @argv: Test argument array, NULL if none * Return: 0 if all tests passed, -1 if any failed */ int ut_run_list(struct unit_test_state *uts, const char *category, const char *prefix, struct unit_test *tests, int count, const char *select_name, int runs_per_test, bool force_run, - const char *test_insert); + const char *test_insert, int argc, char *const argv[]); /** * ut_report() - Report stats on a test run diff --git a/test/cmd_ut.c b/test/cmd_ut.c index d8b3c325c41..bad123a75fd 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -132,7 +132,8 @@ static int run_suite(struct unit_test_state *uts, struct suite *ste, snprintf(prefix, sizeof(prefix), "%s_test_", ste->name); ret = ut_run_list(uts, ste->name, prefix, ste->start, n_ents, - select_name, runs_per_test, force_run, test_insert); + select_name, runs_per_test, force_run, test_insert, + 0, NULL); return ret; } diff --git a/test/test-main.c b/test/test-main.c index 941b883e156..ac5680d77d0 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -89,6 +89,154 @@ void ut_uninit_state(struct unit_test_state *uts) } } +/** + * ut_count_args() - Count the number of arguments in a NULL-terminated array + * + * @defs: Argument definitions array (NULL-terminated) + * Return: Number of arguments + */ +static int ut_count_args(const struct ut_arg_def *defs) +{ + int count = 0; + + if (defs) { + while (defs[count].name) + count++; + } + + return count; +} + +/** + * ut_set_arg() - Find and set an argument value + * + * Search through argument definitions to find a matching key and set its value. + * + * @defs: Argument definitions array + * @args: Argument values array to update + * @count: Number of argument definitions + * @key: Key name to search for + * @key_len: Length of key name + * @val: Value string to parse + * Return: true if argument was found and set, false otherwise + */ +static bool ut_set_arg(const struct ut_arg_def *defs, struct ut_arg *args, + int count, const char *key, int key_len, const char *val) +{ + int j; + + for (j = 0; j < count; j++) { + if (strlen(defs[j].name) == key_len && + !strncmp(defs[j].name, key, key_len)) { + switch (defs[j].type) { + case UT_ARG_INT: + args[j].vint = simple_strtol(val, NULL, 0); + break; + case UT_ARG_BOOL: + args[j].vbool = *val == '1'; + break; + case UT_ARG_STR: + args[j].vstr = val; + break; + } + args[j].provided = true; + return true; + } + } + + return false; +} + +/** + * ut_parse_args() - Parse command-line arguments for a test + * + * Parse key=value arguments from the command line and set up uts->args based on + * the test's argument definitions. + * + * @uts: Unit test state (args and arg_count will be set) + * @test: Test being run (provides arg_defs) + * @argc: Number of arguments + * @argv: Argument array (key=value strings) + * Return: 0 on success, -EINVAL on parse error + */ +static int ut_parse_args(struct unit_test_state *uts, struct unit_test *test, + int argc, char *const argv[]) +{ + const struct ut_arg_def *defs = test->arg_defs; + struct ut_arg *args = uts->args; + int count = ut_count_args(defs); + int i; + + uts->arg_count = 0; + + /* No arguments expected */ + if (!count) { + if (argc > 0) { + printf("Test '%s' does not accept arguments\n", + test->name); + return -EINVAL; + } + return 0; + } + + if (count > UT_MAX_ARGS) { + printf("Test '%s' has too many arguments (%d > %d)\n", + test->name, count, UT_MAX_ARGS); + return -EINVAL; + } + + /* Initialise from defaults */ + for (i = 0; i < count; i++) { + args[i].name = defs[i].name; + args[i].type = defs[i].type; + args[i].provided = false; + switch (defs[i].type) { + case UT_ARG_INT: + args[i].vint = defs[i].def.vint; + break; + case UT_ARG_BOOL: + args[i].vbool = defs[i].def.vbool; + break; + case UT_ARG_STR: + args[i].vstr = defs[i].def.vstr; + break; + } + } + + /* Parse command-line key=value pairs */ + for (i = 0; i < argc; i++) { + const char *arg = argv[i]; + const char *eq = strchr(arg, '='); + int key_len; + + if (!eq) { + printf("Invalid argument '%s' (expected key=value)\n", + arg); + return -EINVAL; + } + key_len = eq - arg; + + if (!ut_set_arg(defs, args, count, arg, key_len, eq + 1)) { + printf("Unknown argument '%.*s' for test '%s'\n", + key_len, arg, test->name); + return -EINVAL; + } + } + + /* Check required arguments are provided */ + for (i = 0; i < count; i++) { + if (!args[i].provided && !(defs[i].flags & UT_ARGF_OPTIONAL)) { + printf("Missing required argument '%s' for test '%s'\n", + defs[i].name, test->name); + return -EINVAL; + } + } + + uts->arg_count = count; + + return 0; +} + /** * dm_test_pre_run() - Get ready to run a driver model test * @@ -593,12 +741,15 @@ static int ut_run_test_live_flat(struct unit_test_state *uts, * @test_insert: String describing a test to run after n other tests run, in the * format n:name where n is the number of tests to run before this one and * name is the name of the test to run + * @argc: Number of test arguments (key=value pairs) + * @argv: Test argument array * Return: 0 if all tests passed, -ENOENT if test @select_name was not found, * -EBADF if any failed */ static int ut_run_tests(struct unit_test_state *uts, const char *prefix, struct unit_test *tests, int count, - const char *select_name, const char *test_insert) + const char *select_name, const char *test_insert, + int argc, char *const argv[]) { int prefix_len = prefix ? strlen(prefix) : 0; struct unit_test *test, *one; @@ -654,6 +805,11 @@ static int ut_run_tests(struct unit_test_state *uts, const char *prefix, uts->cur.test_count++; if (one && upto == pos) { + ret = ut_parse_args(uts, one, argc, argv); + if (ret) { + uts->cur.fail_count++; + return ret; + } ret = ut_run_test_live_flat(uts, one, NULL); if (uts->cur.fail_count != old_fail_count) { printf("Test '%s' failed %d times (position %d)\n", @@ -667,6 +823,12 @@ static int ut_run_tests(struct unit_test_state *uts, const char *prefix, if (prefix_len && !strncmp(test_name, prefix, prefix_len)) test_name = test_name + prefix_len; + ret = ut_parse_args(uts, test, argc, argv); + if (ret) { + found++; + uts->cur.fail_count++; + continue; + } for (i = 0; i < uts->runs_per_test; i++) ret = ut_run_test_live_flat(uts, test, test_name); if (uts->cur.fail_count != old_fail_count) { @@ -706,9 +868,8 @@ void ut_report(struct ut_stats *stats, int run_count) int ut_run_list(struct unit_test_state *uts, const char *category, const char *prefix, struct unit_test *tests, int count, const char *select_name, int runs_per_test, bool force_run, - const char *test_insert) + const char *test_insert, int argc, char *const argv[]) { - ; bool was_bypassed, has_dm_tests = false; ulong start_offset = 0; ulong test_offset = 0; @@ -751,7 +912,7 @@ int ut_run_list(struct unit_test_state *uts, const char *category, uts->force_run = force_run; was_bypassed = pager_set_test_bypass(gd_pager(), true); ret = ut_run_tests(uts, prefix, tests, count, select_name, - test_insert); + test_insert, argc, argv); pager_set_test_bypass(gd_pager(), was_bypassed); /* Best efforts only...ignore errors */