Commit 1e9599df authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'linux-kselftest-kunit-5.13-rc1' of...

Merge tag 'linux-kselftest-kunit-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull KUnit updates from Shuah Khan:
 "Several fixes and a new feature to support failure from dynamic
  analysis tools such as UBSAN and fake ops for testing.

   - a fake ops struct for testing a "free" function to complain if it
     was called with an invalid argument, or caught a double-free. Most
     return void and have no normal means of signalling failure (e.g.
     super_operations, iommu_ops, etc.)"

* tag 'linux-kselftest-kunit-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  Documentation: kunit: add tips for using current->kunit_test
  kunit: fix -Wunused-function warning for __kunit_fail_current_test
  kunit: support failure from dynamic analysis tools
  kunit: tool: make --kunitconfig accept dirs, add lib/kunit fragment
  kunit: make KUNIT_EXPECT_STREQ() quote values, don't print literals
  kunit: Match parenthesis alignment to improve code readability
parents 2a68c268 de2fcb3e
Loading
Loading
Loading
Loading
+76 −2
Original line number Diff line number Diff line
@@ -78,8 +78,82 @@ Similarly to the above, it can be useful to add test-specific logic.
	void test_only_hook(void) { }
	#endif

TODO(dlatypov@google.com): add an example of using ``current->kunit_test`` in
such a hook when it's not only updated for ``CONFIG_KASAN=y``.
This test-only code can be made more useful by accessing the current kunit
test, see below.

Accessing the current test
--------------------------

In some cases, you need to call test-only code from outside the test file, e.g.
like in the example above or if you're providing a fake implementation of an
ops struct.
There is a ``kunit_test`` field in ``task_struct``, so you can access it via
``current->kunit_test``.

Here's a slightly in-depth example of how one could implement "mocking":

.. code-block:: c

	#include <linux/sched.h> /* for current */

	struct test_data {
		int foo_result;
		int want_foo_called_with;
	};

	static int fake_foo(int arg)
	{
		struct kunit *test = current->kunit_test;
		struct test_data *test_data = test->priv;

		KUNIT_EXPECT_EQ(test, test_data->want_foo_called_with, arg);
		return test_data->foo_result;
	}

	static void example_simple_test(struct kunit *test)
	{
		/* Assume priv is allocated in the suite's .init */
		struct test_data *test_data = test->priv;

		test_data->foo_result = 42;
		test_data->want_foo_called_with = 1;

		/* In a real test, we'd probably pass a pointer to fake_foo somewhere
		 * like an ops struct, etc. instead of calling it directly. */
		KUNIT_EXPECT_EQ(test, fake_foo(1), 42);
	}


Note: here we're able to get away with using ``test->priv``, but if you wanted
something more flexible you could use a named ``kunit_resource``, see :doc:`api/test`.

Failing the current test
------------------------

But sometimes, you might just want to fail the current test. In that case, we
have ``kunit_fail_current_test(fmt, args...)`` which is defined in ``<kunit/test-bug.h>`` and
doesn't require pulling in ``<kunit/test.h>``.

E.g. say we had an option to enable some extra debug checks on some data structure:

.. code-block:: c

	#include <kunit/test-bug.h>

	#ifdef CONFIG_EXTRA_DEBUG_CHECKS
	static void validate_my_data(struct data *data)
	{
		if (is_valid(data))
			return;

		kunit_fail_current_test("data %p is invalid", data);

		/* Normal, non-KUnit, error reporting code here. */
	}
	#else
	static void my_debug_function(void) { }
	#endif


Customizing error messages
--------------------------
+29 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * KUnit API allowing dynamic analysis tools to interact with KUnit tests
 *
 * Copyright (C) 2020, Google LLC.
 * Author: Uriel Guajardo <urielguajardo@google.com>
 */

#ifndef _KUNIT_TEST_BUG_H
#define _KUNIT_TEST_BUG_H

#define kunit_fail_current_test(fmt, ...) \
	__kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__)

#if IS_BUILTIN(CONFIG_KUNIT)

extern __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
						    const char *fmt, ...);

#else

static inline __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
							    const char *fmt, ...)
{
}

#endif

#endif /* _KUNIT_TEST_BUG_H */

lib/kunit/.kunitconfig

0 → 100644
+3 −0
Original line number Diff line number Diff line
CONFIG_KUNIT=y
CONFIG_KUNIT_TEST=y
CONFIG_KUNIT_EXAMPLE_TEST=y
+44 −17
Original line number Diff line number Diff line
@@ -48,8 +48,9 @@ EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
void kunit_unary_assert_format(const struct kunit_assert *assert,
			       struct string_stream *stream)
{
	struct kunit_unary_assert *unary_assert = container_of(
			assert, struct kunit_unary_assert, assert);
	struct kunit_unary_assert *unary_assert;

	unary_assert = container_of(assert, struct kunit_unary_assert, assert);

	kunit_base_assert_format(assert, stream);
	if (unary_assert->expected_true)
@@ -67,8 +68,10 @@ EXPORT_SYMBOL_GPL(kunit_unary_assert_format);
void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
				     struct string_stream *stream)
{
	struct kunit_ptr_not_err_assert *ptr_assert = container_of(
			assert, struct kunit_ptr_not_err_assert, assert);
	struct kunit_ptr_not_err_assert *ptr_assert;

	ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
				  assert);

	kunit_base_assert_format(assert, stream);
	if (!ptr_assert->value) {
@@ -111,8 +114,10 @@ static bool is_literal(struct kunit *test, const char *text, long long value,
void kunit_binary_assert_format(const struct kunit_assert *assert,
				struct string_stream *stream)
{
	struct kunit_binary_assert *binary_assert = container_of(
			assert, struct kunit_binary_assert, assert);
	struct kunit_binary_assert *binary_assert;

	binary_assert = container_of(assert, struct kunit_binary_assert,
				     assert);

	kunit_base_assert_format(assert, stream);
	string_stream_add(stream,
@@ -137,8 +142,10 @@ EXPORT_SYMBOL_GPL(kunit_binary_assert_format);
void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
				    struct string_stream *stream)
{
	struct kunit_binary_ptr_assert *binary_assert = container_of(
			assert, struct kunit_binary_ptr_assert, assert);
	struct kunit_binary_ptr_assert *binary_assert;

	binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
				     assert);

	kunit_base_assert_format(assert, stream);
	string_stream_add(stream,
@@ -156,11 +163,29 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
}
EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format);

/* Checks if KUNIT_EXPECT_STREQ() args were string literals.
 * Note: `text` will have ""s where as `value` will not.
 */
static bool is_str_literal(const char *text, const char *value)
{
	int len;

	len = strlen(text);
	if (len < 2)
		return false;
	if (text[0] != '\"' || text[len - 1] != '\"')
		return false;

	return strncmp(text + 1, value, len - 2) == 0;
}

void kunit_binary_str_assert_format(const struct kunit_assert *assert,
				    struct string_stream *stream)
{
	struct kunit_binary_str_assert *binary_assert = container_of(
			assert, struct kunit_binary_str_assert, assert);
	struct kunit_binary_str_assert *binary_assert;

	binary_assert = container_of(assert, struct kunit_binary_str_assert,
				     assert);

	kunit_base_assert_format(assert, stream);
	string_stream_add(stream,
@@ -168,10 +193,12 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
			  binary_assert->left_text,
			  binary_assert->operation,
			  binary_assert->right_text);
	string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s\n",
	if (!is_str_literal(binary_assert->left_text, binary_assert->left_value))
		string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"\n",
				  binary_assert->left_text,
				  binary_assert->left_value);
	string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s",
	if (!is_str_literal(binary_assert->right_text, binary_assert->right_value))
		string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"",
				  binary_assert->right_text,
				  binary_assert->right_value);
	kunit_assert_print_msg(assert, stream);
+35 −4
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
 */

#include <kunit/test.h>
#include <kunit/test-bug.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/sched/debug.h>
@@ -16,6 +17,40 @@
#include "string-stream.h"
#include "try-catch-impl.h"

#if IS_BUILTIN(CONFIG_KUNIT)
/*
 * Fail the current test and print an error message to the log.
 */
void __kunit_fail_current_test(const char *file, int line, const char *fmt, ...)
{
	va_list args;
	int len;
	char *buffer;

	if (!current->kunit_test)
		return;

	kunit_set_failure(current->kunit_test);

	/* kunit_err() only accepts literals, so evaluate the args first. */
	va_start(args, fmt);
	len = vsnprintf(NULL, 0, fmt, args) + 1;
	va_end(args);

	buffer = kunit_kmalloc(current->kunit_test, len, GFP_KERNEL);
	if (!buffer)
		return;

	va_start(args, fmt);
	vsnprintf(buffer, len, fmt, args);
	va_end(args);

	kunit_err(current->kunit_test, "%s:%d: %s", file, line, buffer);
	kunit_kfree(current->kunit_test, buffer);
}
EXPORT_SYMBOL_GPL(__kunit_fail_current_test);
#endif

/*
 * Append formatted message to log, size of which is limited to
 * KUNIT_LOG_SIZE bytes (including null terminating byte).
@@ -273,9 +308,7 @@ static void kunit_try_run_case(void *data)
	struct kunit_suite *suite = ctx->suite;
	struct kunit_case *test_case = ctx->test_case;

#if (IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT))
	current->kunit_test = test;
#endif /* IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT) */

	/*
	 * kunit_run_case_internal may encounter a fatal error; if it does,
@@ -624,9 +657,7 @@ void kunit_cleanup(struct kunit *test)
		spin_unlock(&test->lock);
		kunit_remove_resource(test, res);
	}
#if (IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT))
	current->kunit_test = NULL;
#endif /* IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT)*/
}
EXPORT_SYMBOL_GPL(kunit_cleanup);

Loading