Skip to content

Commit 1e9599d

Browse files
committed
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
2 parents 2a68c26 + de2fcb3 commit 1e9599d

File tree

8 files changed

+198
-24
lines changed

8 files changed

+198
-24
lines changed

Diff for: Documentation/dev-tools/kunit/tips.rst

+76-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,82 @@ Similarly to the above, it can be useful to add test-specific logic.
7878
void test_only_hook(void) { }
7979
#endif
8080
81-
TODO([email protected]): add an example of using ``current->kunit_test`` in
82-
such a hook when it's not only updated for ``CONFIG_KASAN=y``.
81+
This test-only code can be made more useful by accessing the current kunit
82+
test, see below.
83+
84+
Accessing the current test
85+
--------------------------
86+
87+
In some cases, you need to call test-only code from outside the test file, e.g.
88+
like in the example above or if you're providing a fake implementation of an
89+
ops struct.
90+
There is a ``kunit_test`` field in ``task_struct``, so you can access it via
91+
``current->kunit_test``.
92+
93+
Here's a slightly in-depth example of how one could implement "mocking":
94+
95+
.. code-block:: c
96+
97+
#include <linux/sched.h> /* for current */
98+
99+
struct test_data {
100+
int foo_result;
101+
int want_foo_called_with;
102+
};
103+
104+
static int fake_foo(int arg)
105+
{
106+
struct kunit *test = current->kunit_test;
107+
struct test_data *test_data = test->priv;
108+
109+
KUNIT_EXPECT_EQ(test, test_data->want_foo_called_with, arg);
110+
return test_data->foo_result;
111+
}
112+
113+
static void example_simple_test(struct kunit *test)
114+
{
115+
/* Assume priv is allocated in the suite's .init */
116+
struct test_data *test_data = test->priv;
117+
118+
test_data->foo_result = 42;
119+
test_data->want_foo_called_with = 1;
120+
121+
/* In a real test, we'd probably pass a pointer to fake_foo somewhere
122+
* like an ops struct, etc. instead of calling it directly. */
123+
KUNIT_EXPECT_EQ(test, fake_foo(1), 42);
124+
}
125+
126+
127+
Note: here we're able to get away with using ``test->priv``, but if you wanted
128+
something more flexible you could use a named ``kunit_resource``, see :doc:`api/test`.
129+
130+
Failing the current test
131+
------------------------
132+
133+
But sometimes, you might just want to fail the current test. In that case, we
134+
have ``kunit_fail_current_test(fmt, args...)`` which is defined in ``<kunit/test-bug.h>`` and
135+
doesn't require pulling in ``<kunit/test.h>``.
136+
137+
E.g. say we had an option to enable some extra debug checks on some data structure:
138+
139+
.. code-block:: c
140+
141+
#include <kunit/test-bug.h>
142+
143+
#ifdef CONFIG_EXTRA_DEBUG_CHECKS
144+
static void validate_my_data(struct data *data)
145+
{
146+
if (is_valid(data))
147+
return;
148+
149+
kunit_fail_current_test("data %p is invalid", data);
150+
151+
/* Normal, non-KUnit, error reporting code here. */
152+
}
153+
#else
154+
static void my_debug_function(void) { }
155+
#endif
156+
83157
84158
Customizing error messages
85159
--------------------------

Diff for: include/kunit/test-bug.h

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* KUnit API allowing dynamic analysis tools to interact with KUnit tests
4+
*
5+
* Copyright (C) 2020, Google LLC.
6+
* Author: Uriel Guajardo <[email protected]>
7+
*/
8+
9+
#ifndef _KUNIT_TEST_BUG_H
10+
#define _KUNIT_TEST_BUG_H
11+
12+
#define kunit_fail_current_test(fmt, ...) \
13+
__kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
14+
15+
#if IS_BUILTIN(CONFIG_KUNIT)
16+
17+
extern __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
18+
const char *fmt, ...);
19+
20+
#else
21+
22+
static inline __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
23+
const char *fmt, ...)
24+
{
25+
}
26+
27+
#endif
28+
29+
#endif /* _KUNIT_TEST_BUG_H */

Diff for: lib/kunit/.kunitconfig

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_KUNIT=y
2+
CONFIG_KUNIT_TEST=y
3+
CONFIG_KUNIT_EXAMPLE_TEST=y

Diff for: lib/kunit/assert.c

+44-17
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void kunit_base_assert_format(const struct kunit_assert *assert,
2525
}
2626

2727
string_stream_add(stream, "%s FAILED at %s:%d\n",
28-
expect_or_assert, assert->file, assert->line);
28+
expect_or_assert, assert->file, assert->line);
2929
}
3030
EXPORT_SYMBOL_GPL(kunit_base_assert_format);
3131

@@ -48,8 +48,9 @@ EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
4848
void kunit_unary_assert_format(const struct kunit_assert *assert,
4949
struct string_stream *stream)
5050
{
51-
struct kunit_unary_assert *unary_assert = container_of(
52-
assert, struct kunit_unary_assert, assert);
51+
struct kunit_unary_assert *unary_assert;
52+
53+
unary_assert = container_of(assert, struct kunit_unary_assert, assert);
5354

5455
kunit_base_assert_format(assert, stream);
5556
if (unary_assert->expected_true)
@@ -67,8 +68,10 @@ EXPORT_SYMBOL_GPL(kunit_unary_assert_format);
6768
void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
6869
struct string_stream *stream)
6970
{
70-
struct kunit_ptr_not_err_assert *ptr_assert = container_of(
71-
assert, struct kunit_ptr_not_err_assert, assert);
71+
struct kunit_ptr_not_err_assert *ptr_assert;
72+
73+
ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
74+
assert);
7275

7376
kunit_base_assert_format(assert, stream);
7477
if (!ptr_assert->value) {
@@ -111,8 +114,10 @@ static bool is_literal(struct kunit *test, const char *text, long long value,
111114
void kunit_binary_assert_format(const struct kunit_assert *assert,
112115
struct string_stream *stream)
113116
{
114-
struct kunit_binary_assert *binary_assert = container_of(
115-
assert, struct kunit_binary_assert, assert);
117+
struct kunit_binary_assert *binary_assert;
118+
119+
binary_assert = container_of(assert, struct kunit_binary_assert,
120+
assert);
116121

117122
kunit_base_assert_format(assert, stream);
118123
string_stream_add(stream,
@@ -137,8 +142,10 @@ EXPORT_SYMBOL_GPL(kunit_binary_assert_format);
137142
void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
138143
struct string_stream *stream)
139144
{
140-
struct kunit_binary_ptr_assert *binary_assert = container_of(
141-
assert, struct kunit_binary_ptr_assert, assert);
145+
struct kunit_binary_ptr_assert *binary_assert;
146+
147+
binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
148+
assert);
142149

143150
kunit_base_assert_format(assert, stream);
144151
string_stream_add(stream,
@@ -156,24 +163,44 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
156163
}
157164
EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format);
158165

166+
/* Checks if KUNIT_EXPECT_STREQ() args were string literals.
167+
* Note: `text` will have ""s where as `value` will not.
168+
*/
169+
static bool is_str_literal(const char *text, const char *value)
170+
{
171+
int len;
172+
173+
len = strlen(text);
174+
if (len < 2)
175+
return false;
176+
if (text[0] != '\"' || text[len - 1] != '\"')
177+
return false;
178+
179+
return strncmp(text + 1, value, len - 2) == 0;
180+
}
181+
159182
void kunit_binary_str_assert_format(const struct kunit_assert *assert,
160183
struct string_stream *stream)
161184
{
162-
struct kunit_binary_str_assert *binary_assert = container_of(
163-
assert, struct kunit_binary_str_assert, assert);
185+
struct kunit_binary_str_assert *binary_assert;
186+
187+
binary_assert = container_of(assert, struct kunit_binary_str_assert,
188+
assert);
164189

165190
kunit_base_assert_format(assert, stream);
166191
string_stream_add(stream,
167192
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
168193
binary_assert->left_text,
169194
binary_assert->operation,
170195
binary_assert->right_text);
171-
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s\n",
172-
binary_assert->left_text,
173-
binary_assert->left_value);
174-
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s",
175-
binary_assert->right_text,
176-
binary_assert->right_value);
196+
if (!is_str_literal(binary_assert->left_text, binary_assert->left_value))
197+
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"\n",
198+
binary_assert->left_text,
199+
binary_assert->left_value);
200+
if (!is_str_literal(binary_assert->right_text, binary_assert->right_value))
201+
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"",
202+
binary_assert->right_text,
203+
binary_assert->right_value);
177204
kunit_assert_print_msg(assert, stream);
178205
}
179206
EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);

Diff for: lib/kunit/test.c

+35-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
#include <kunit/test.h>
10+
#include <kunit/test-bug.h>
1011
#include <linux/kernel.h>
1112
#include <linux/kref.h>
1213
#include <linux/sched/debug.h>
@@ -16,6 +17,40 @@
1617
#include "string-stream.h"
1718
#include "try-catch-impl.h"
1819

20+
#if IS_BUILTIN(CONFIG_KUNIT)
21+
/*
22+
* Fail the current test and print an error message to the log.
23+
*/
24+
void __kunit_fail_current_test(const char *file, int line, const char *fmt, ...)
25+
{
26+
va_list args;
27+
int len;
28+
char *buffer;
29+
30+
if (!current->kunit_test)
31+
return;
32+
33+
kunit_set_failure(current->kunit_test);
34+
35+
/* kunit_err() only accepts literals, so evaluate the args first. */
36+
va_start(args, fmt);
37+
len = vsnprintf(NULL, 0, fmt, args) + 1;
38+
va_end(args);
39+
40+
buffer = kunit_kmalloc(current->kunit_test, len, GFP_KERNEL);
41+
if (!buffer)
42+
return;
43+
44+
va_start(args, fmt);
45+
vsnprintf(buffer, len, fmt, args);
46+
va_end(args);
47+
48+
kunit_err(current->kunit_test, "%s:%d: %s", file, line, buffer);
49+
kunit_kfree(current->kunit_test, buffer);
50+
}
51+
EXPORT_SYMBOL_GPL(__kunit_fail_current_test);
52+
#endif
53+
1954
/*
2055
* Append formatted message to log, size of which is limited to
2156
* KUNIT_LOG_SIZE bytes (including null terminating byte).
@@ -273,9 +308,7 @@ static void kunit_try_run_case(void *data)
273308
struct kunit_suite *suite = ctx->suite;
274309
struct kunit_case *test_case = ctx->test_case;
275310

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

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

Diff for: tools/testing/kunit/kunit.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ def add_common_opts(parser) -> None:
184184
help='Run all KUnit tests through allyesconfig',
185185
action='store_true')
186186
parser.add_argument('--kunitconfig',
187-
help='Path to Kconfig fragment that enables KUnit tests',
187+
help='Path to Kconfig fragment that enables KUnit tests.'
188+
' If given a directory, (e.g. lib/kunit), "/.kunitconfig" '
189+
'will get automatically appended.',
188190
metavar='kunitconfig')
189191

190192
def add_build_opts(parser) -> None:

Diff for: tools/testing/kunit/kunit_kernel.py

+2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ def __init__(self, build_dir: str, load_config=True, kunitconfig_path='') -> Non
132132
return
133133

134134
if kunitconfig_path:
135+
if os.path.isdir(kunitconfig_path):
136+
kunitconfig_path = os.path.join(kunitconfig_path, KUNITCONFIG_PATH)
135137
if not os.path.exists(kunitconfig_path):
136138
raise ConfigError(f'Specified kunitconfig ({kunitconfig_path}) does not exist')
137139
else:

Diff for: tools/testing/kunit/kunit_tool_test.py

+6
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,12 @@ def test_valid_kunitconfig(self):
251251
with tempfile.NamedTemporaryFile('wt') as kunitconfig:
252252
tree = kunit_kernel.LinuxSourceTree('', kunitconfig_path=kunitconfig.name)
253253

254+
def test_dir_kunitconfig(self):
255+
with tempfile.TemporaryDirectory('') as dir:
256+
with open(os.path.join(dir, '.kunitconfig'), 'w') as f:
257+
pass
258+
tree = kunit_kernel.LinuxSourceTree('', kunitconfig_path=dir)
259+
254260
# TODO: add more test cases.
255261

256262

0 commit comments

Comments
 (0)