[Concept,15/21] test: Add type-checked argument accessor functions

Message ID 20251214175449.3799539-16-sjg@u-boot.org
State New
Headers
Series test: Add support for passing arguments to C unit tests |

Commit Message

Simon Glass Dec. 14, 2025, 5:54 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add ut_get_str(), ut_get_int(), and ut_get_bool() functions with
corresponding ut_str(), ut_int(), and ut_bool() macros for accessing
test arguments with type checking.

These functions check that the argument index is within bounds and the
type matches what was requested.

The first failure for a test is reported via ut_failf() which should
make it fairly easy to debug the test.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 include/test/test.h |  2 ++
 include/test/ut.h   | 50 ++++++++++++++++++++++++++++++++++
 test/test-main.c    |  1 +
 test/ut.c           | 66 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 119 insertions(+)
  

Patch

diff --git a/include/test/test.h b/include/test/test.h
index da38b8ee4f0..15ed8b37890 100644
--- a/include/test/test.h
+++ b/include/test/test.h
@@ -94,6 +94,7 @@  struct ut_arg {
  * @actual_str: Temporary string used to hold actual string value
  * @args: Parsed argument values for current test
  * @arg_count: Number of parsed arguments
+ * @arg_error: Set if ut_str/int/bool() detects a type mismatch
  */
 struct unit_test_state {
 	struct ut_stats cur;
@@ -122,6 +123,7 @@  struct unit_test_state {
 	char actual_str[1024];
 	struct ut_arg args[UT_MAX_ARGS];
 	int arg_count;
+	bool arg_error;
 };
 
 /* Test flags for each test */
diff --git a/include/test/ut.h b/include/test/ut.h
index 90b9bf79929..4a9a401cf48 100644
--- a/include/test/ut.h
+++ b/include/test/ut.h
@@ -635,4 +635,54 @@  int ut_run_list(struct unit_test_state *uts, const char *category,
  */
 void ut_report(struct ut_stats *stats, int run_count);
 
+/**
+ * ut_get_str() - Get a string test argument
+ *
+ * Fails the test if the argument type is not UT_ARG_STR.
+ *
+ * @uts: Test state
+ * @n: Argument index
+ * @file: Filename of caller
+ * @line: Line number of caller
+ * @func: Function name of caller
+ * Return: String value, or NULL if type mismatch
+ */
+const char *ut_get_str(struct unit_test_state *uts, int n, const char *file,
+		       int line, const char *func);
+
+/**
+ * ut_get_int() - Get an integer test argument
+ *
+ * Fails the test if the argument type is not UT_ARG_INT.
+ *
+ * @uts: Test state
+ * @n: Argument index
+ * @file: Filename of caller
+ * @line: Line number of caller
+ * @func: Function name of caller
+ * Return: Integer value, or 0 if type mismatch
+ */
+long ut_get_int(struct unit_test_state *uts, int n, const char *file,
+		int line, const char *func);
+
+/**
+ * ut_get_bool() - Get a boolean test argument
+ *
+ * Fails the test if the argument type is not UT_ARG_BOOL.
+ *
+ * @uts: Test state
+ * @n: Argument index
+ * @file: Filename of caller
+ * @line: Line number of caller
+ * @func: Function name of caller
+ * Return: Boolean value, or false if type mismatch
+ */
+bool ut_get_bool(struct unit_test_state *uts, int n, const char *file,
+		 int line, const char *func);
+
+/* Helpers for accessing test arguments with type checking */
+#define ut_str(n)	ut_get_str(uts, n, __FILE__, __LINE__, __func__)
+#define ut_int(n)	ut_get_int(uts, n, __FILE__, __LINE__, __func__)
+#define ut_bool(n)	ut_get_bool(uts, n, __FILE__, __LINE__, __func__)
+
 #endif
diff --git a/test/test-main.c b/test/test-main.c
index ac5680d77d0..c9e164da678 100644
--- a/test/test-main.c
+++ b/test/test-main.c
@@ -639,6 +639,7 @@  static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
 	if (ret)
 		return ret;
 
+	uts->arg_error = false;
 	ret = test->func(uts);
 	if (ret == -EAGAIN)
 		skip_test(uts);
diff --git a/test/ut.c b/test/ut.c
index aed59cae0b9..fe9a177ab53 100644
--- a/test/ut.c
+++ b/test/ut.c
@@ -295,3 +295,69 @@  void ut_set_skip_delays(struct unit_test_state *uts, bool skip_delays)
 	state_set_skip_delays(skip_delays);
 #endif
 }
+
+const char *ut_get_str(struct unit_test_state *uts, int n, const char *file,
+		       int line, const char *func)
+{
+	if (n < 0 || n >= uts->arg_count) {
+		if (!uts->arg_error)
+			ut_failf(uts, file, line, func, "ut_str() arg check",
+				 "arg %d is invalid (arg_count=%d)", n,
+				 uts->arg_count);
+		uts->arg_error = true;
+		return NULL;
+	}
+	if (uts->args[n].type != UT_ARG_STR) {
+		if (!uts->arg_error)
+			ut_failf(uts, file, line, func, "ut_str() type check",
+				 "arg %d is not a string", n);
+		uts->arg_error = true;
+		return NULL;
+	}
+
+	return uts->args[n].vstr;
+}
+
+long ut_get_int(struct unit_test_state *uts, int n, const char *file,
+		int line, const char *func)
+{
+	if (n < 0 || n >= uts->arg_count) {
+		if (!uts->arg_error)
+			ut_failf(uts, file, line, func, "ut_int() arg check",
+				 "arg %d is invalid (arg_count=%d)", n,
+				 uts->arg_count);
+		uts->arg_error = true;
+		return 0;
+	}
+	if (uts->args[n].type != UT_ARG_INT) {
+		if (!uts->arg_error)
+			ut_failf(uts, file, line, func, "ut_int() type check",
+				 "arg %d is not an int", n);
+		uts->arg_error = true;
+		return 0;
+	}
+
+	return uts->args[n].vint;
+}
+
+bool ut_get_bool(struct unit_test_state *uts, int n, const char *file,
+		 int line, const char *func)
+{
+	if (n < 0 || n >= uts->arg_count) {
+		if (!uts->arg_error)
+			ut_failf(uts, file, line, func, "ut_bool() arg check",
+				 "arg %d is invalid (arg_count=%d)", n,
+				 uts->arg_count);
+		uts->arg_error = true;
+		return false;
+	}
+	if (uts->args[n].type != UT_ARG_BOOL) {
+		if (!uts->arg_error)
+			ut_failf(uts, file, line, func, "ut_bool() type check",
+				 "arg %d is not a bool", n);
+		uts->arg_error = true;
+		return false;
+	}
+
+	return uts->args[n].vbool;
+}