Re: [PATCH v6 02/13] t: import the clar unit testing framework

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Patrick

On 20/08/2024 15:02, Patrick Steinhardt wrote:
Import the clar unit testing framework at commit 1516124 (Merge pull
request #97 from pks-t/pks-whitespace-fixes, 2024-08-15). The framework
will be wired up in subsequent commits.

I would be good to explain why we're doing this so we have a record in the project history. A summary of the advantages and disadvantages of using clar vs our current unit test framework would be helpful as well.

> diff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c
> [...]
> +static void
> +clar_parse_args(int argc, char **argv)
> +{
> [...]
+		case 'q':
+			_clar.report_errors_only = 1;
+			break;

I think this option is incompatible with TAP output as when TAP output is selected the first error is printed as part of clar_print_ontest() and clar_print_errors() is a no-op. We should error out if it is given with '-t'
 > +void clar__fail(
+	const char *file,
+	const char *function,
+	size_t line,
+	const char *error_msg,
+	const char *description,
+	int should_abort)
+{
+	struct clar_error *error = calloc(1, sizeof(struct clar_error));

clar seems to take an inconsistent approach to memory allocation errors. Here "error" is de-referenced without checking if it is NULL and yet in other places there are checks for NULL after calling calloc() or strdup().

+void clar__assert_equal(
+	const char *file,
+	const char *function,
+	size_t line,
+	const char *err,
+	int should_abort,
+	const char *fmt,
+	...)
+{
+	va_list args;
+	char buf[4096];
+	int is_equal = 1;
+
+	va_start(args, fmt);
+
+	if (!strcmp("%s", fmt)) {
+		const char *s1 = va_arg(args, const char *);
+		const char *s2 = va_arg(args, const char *);
+		is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2);
+
+		if (!is_equal) {
+			if (s1 && s2) {
+				int pos;
+				for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
+					/* find differing byte offset */;
+				p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
+					s1, s2, pos);

s1 and s2 are passed to snprintf without checking if they are NULL. As 'snprintf(buf, sizeof(buf), "%s", NULL)' is undefined we should check for NULL here. The use of variadic arguments means there is no type checking so if one is checking the contents of an strbuf and writes

    cl_assert_equal_s(buf, "expect");

instead of

    cl_assert_equal_s(buf->buf, "expect");

the compiler does not warn you. This is a regression compared to check_str(). We can address this by having cl_assert_equal_s() wrap a function that takes two strings. Another regression is that if the string contains control characters they are printed verbatim.

diff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h
> [...]
+#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))

The (int) casts here mean that we'll silently truncate longer arguments and suppress any compiler warnings about comparing incompatible types.

> diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h
> [...]
+static void clar_print_tap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
+{
+	const struct clar_error *error = _clar.last_report->errors;
+
+	(void)test_name;
+	(void)test_number;
+
+	switch(status) {
+	case CL_TEST_OK:
+		printf("ok %d - %s::%s\n", test_number, suite_name, test_name);
+		break;
+	case CL_TEST_FAILURE:
+		printf("not ok %d - %s::%s\n", test_number, suite_name, test_name);
+
+		printf("    ---\n");
+		printf("    reason: |\n");
+		printf("      %s\n", error->error_msg);
+
+		if (error->description)
+			printf("      %s\n", error->description);
+
+		printf("    at:\n");
+		printf("      file: '"); print_escaped(error->file); printf("'\n");
+		printf("      line: %" PRIuZ "\n", error->line_number);
+		printf("      function: '%s'\n", error->function);
+		printf("    ---\n");

If a test calls cl_warning() only the message from the first call to that function is printed and any message from later calls to cl_warning() or clar__fail() is suppressed when TAP output is selected.

Best Wishes

Phillip




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux