linux-kselftest-kunit-5.13-rc1
This KUnit update for Linux 5.13-rc1 consists of several fixes and 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.). -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmCIpAEACgkQCwJExA0N QxyPtRAAgA9/X6zSvT1LftnTkgl0Zn+4Krbr7eNyvxcVdJkYrmW31Pk8PaTK02Z/ OVDzw0ESHaAN2SwDOmKFUyiob9qBZjf4lEHDkGWLnSkV3GHd9JrM+kceyX/0yr8E bpYvGZHBKe+SGtAr1H9DcUcfzCZlDUhUI2HOMwm/h29mFmmJOlrjvNUAGY4pnmti 6Mqh4GAoFxqQDDUANUIWinWU56rWrcIPQgxPD6/r2VvrC8gHtQ+2dBR8+3i2qSQO Ydzgno/8OmXfl606NMKh5DPS6E8uRrLw67fBB+YoEGK6E9w5yyXKKAbV3M3ekB/Y 3ze/SUi6IKrvdF6OEH4mX3rMNEKgRQoJLpQ8KyMvDMuIAdmq3xQQFDlSc2+gEnTj VnEREBhrIOh24GxZFTM6VvNmjNqdAq4/BMTt/LoSHEKwOASZi9udAnKjo67P/LB1 1+rcoKdn/OA7p9/Zo/ETpTkFvEDyF2TscaEgYz2aRiV0YgcncPc1RSDAzbghdlFP y10Dk3uARXdzqrd0Hb1B3HL+cAPZEINerqqAUl0ggWejcJjfUDMi7sQuKEK8I5yU 6sSXyVhCHDNmOUjuJjt5JdqeadLUDCkZqEnMMvwqKz00WKioQ2pqYy/UJ2rpWy+G +pymI17nRcOVfIIqnXitSCgch7cS0FZCjiOqZBBCCs9Axuv7mJU= =aP5e -----END PGP SIGNATURE----- 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
This commit is contained in:
Коммит
1e9599dfc4
|
@ -78,8 +78,82 @@ Similarly to the above, it can be useful to add test-specific logic.
|
||||||
void test_only_hook(void) { }
|
void test_only_hook(void) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TODO(dlatypov@google.com): add an example of using ``current->kunit_test`` in
|
This test-only code can be made more useful by accessing the current kunit
|
||||||
such a hook when it's not only updated for ``CONFIG_KASAN=y``.
|
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
|
Customizing error messages
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* 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 */
|
|
@ -0,0 +1,3 @@
|
||||||
|
CONFIG_KUNIT=y
|
||||||
|
CONFIG_KUNIT_TEST=y
|
||||||
|
CONFIG_KUNIT_EXAMPLE_TEST=y
|
|
@ -25,7 +25,7 @@ void kunit_base_assert_format(const struct kunit_assert *assert,
|
||||||
}
|
}
|
||||||
|
|
||||||
string_stream_add(stream, "%s FAILED at %s:%d\n",
|
string_stream_add(stream, "%s FAILED at %s:%d\n",
|
||||||
expect_or_assert, assert->file, assert->line);
|
expect_or_assert, assert->file, assert->line);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kunit_base_assert_format);
|
EXPORT_SYMBOL_GPL(kunit_base_assert_format);
|
||||||
|
|
||||||
|
@ -48,8 +48,9 @@ EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
|
||||||
void kunit_unary_assert_format(const struct kunit_assert *assert,
|
void kunit_unary_assert_format(const struct kunit_assert *assert,
|
||||||
struct string_stream *stream)
|
struct string_stream *stream)
|
||||||
{
|
{
|
||||||
struct kunit_unary_assert *unary_assert = container_of(
|
struct kunit_unary_assert *unary_assert;
|
||||||
assert, struct kunit_unary_assert, assert);
|
|
||||||
|
unary_assert = container_of(assert, struct kunit_unary_assert, assert);
|
||||||
|
|
||||||
kunit_base_assert_format(assert, stream);
|
kunit_base_assert_format(assert, stream);
|
||||||
if (unary_assert->expected_true)
|
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,
|
void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
|
||||||
struct string_stream *stream)
|
struct string_stream *stream)
|
||||||
{
|
{
|
||||||
struct kunit_ptr_not_err_assert *ptr_assert = container_of(
|
struct kunit_ptr_not_err_assert *ptr_assert;
|
||||||
assert, struct kunit_ptr_not_err_assert, assert);
|
|
||||||
|
ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
|
||||||
|
assert);
|
||||||
|
|
||||||
kunit_base_assert_format(assert, stream);
|
kunit_base_assert_format(assert, stream);
|
||||||
if (!ptr_assert->value) {
|
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,
|
void kunit_binary_assert_format(const struct kunit_assert *assert,
|
||||||
struct string_stream *stream)
|
struct string_stream *stream)
|
||||||
{
|
{
|
||||||
struct kunit_binary_assert *binary_assert = container_of(
|
struct kunit_binary_assert *binary_assert;
|
||||||
assert, struct kunit_binary_assert, assert);
|
|
||||||
|
binary_assert = container_of(assert, struct kunit_binary_assert,
|
||||||
|
assert);
|
||||||
|
|
||||||
kunit_base_assert_format(assert, stream);
|
kunit_base_assert_format(assert, stream);
|
||||||
string_stream_add(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,
|
void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
|
||||||
struct string_stream *stream)
|
struct string_stream *stream)
|
||||||
{
|
{
|
||||||
struct kunit_binary_ptr_assert *binary_assert = container_of(
|
struct kunit_binary_ptr_assert *binary_assert;
|
||||||
assert, struct kunit_binary_ptr_assert, assert);
|
|
||||||
|
binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
|
||||||
|
assert);
|
||||||
|
|
||||||
kunit_base_assert_format(assert, stream);
|
kunit_base_assert_format(assert, stream);
|
||||||
string_stream_add(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);
|
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,
|
void kunit_binary_str_assert_format(const struct kunit_assert *assert,
|
||||||
struct string_stream *stream)
|
struct string_stream *stream)
|
||||||
{
|
{
|
||||||
struct kunit_binary_str_assert *binary_assert = container_of(
|
struct kunit_binary_str_assert *binary_assert;
|
||||||
assert, struct kunit_binary_str_assert, assert);
|
|
||||||
|
binary_assert = container_of(assert, struct kunit_binary_str_assert,
|
||||||
|
assert);
|
||||||
|
|
||||||
kunit_base_assert_format(assert, stream);
|
kunit_base_assert_format(assert, stream);
|
||||||
string_stream_add(stream,
|
string_stream_add(stream,
|
||||||
|
@ -168,12 +193,14 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
|
||||||
binary_assert->left_text,
|
binary_assert->left_text,
|
||||||
binary_assert->operation,
|
binary_assert->operation,
|
||||||
binary_assert->right_text);
|
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))
|
||||||
binary_assert->left_text,
|
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"\n",
|
||||||
binary_assert->left_value);
|
binary_assert->left_text,
|
||||||
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s",
|
binary_assert->left_value);
|
||||||
binary_assert->right_text,
|
if (!is_str_literal(binary_assert->right_text, binary_assert->right_value))
|
||||||
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);
|
kunit_assert_print_msg(assert, stream);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
|
EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <kunit/test.h>
|
#include <kunit/test.h>
|
||||||
|
#include <kunit/test-bug.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/kref.h>
|
#include <linux/kref.h>
|
||||||
#include <linux/sched/debug.h>
|
#include <linux/sched/debug.h>
|
||||||
|
@ -16,6 +17,40 @@
|
||||||
#include "string-stream.h"
|
#include "string-stream.h"
|
||||||
#include "try-catch-impl.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
|
* Append formatted message to log, size of which is limited to
|
||||||
* KUNIT_LOG_SIZE bytes (including null terminating byte).
|
* 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_suite *suite = ctx->suite;
|
||||||
struct kunit_case *test_case = ctx->test_case;
|
struct kunit_case *test_case = ctx->test_case;
|
||||||
|
|
||||||
#if (IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT))
|
|
||||||
current->kunit_test = test;
|
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,
|
* 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);
|
spin_unlock(&test->lock);
|
||||||
kunit_remove_resource(test, res);
|
kunit_remove_resource(test, res);
|
||||||
}
|
}
|
||||||
#if (IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT))
|
|
||||||
current->kunit_test = NULL;
|
current->kunit_test = NULL;
|
||||||
#endif /* IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT)*/
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kunit_cleanup);
|
EXPORT_SYMBOL_GPL(kunit_cleanup);
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,9 @@ def add_common_opts(parser) -> None:
|
||||||
help='Run all KUnit tests through allyesconfig',
|
help='Run all KUnit tests through allyesconfig',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
parser.add_argument('--kunitconfig',
|
parser.add_argument('--kunitconfig',
|
||||||
help='Path to Kconfig fragment that enables KUnit tests',
|
help='Path to Kconfig fragment that enables KUnit tests.'
|
||||||
|
' If given a directory, (e.g. lib/kunit), "/.kunitconfig" '
|
||||||
|
'will get automatically appended.',
|
||||||
metavar='kunitconfig')
|
metavar='kunitconfig')
|
||||||
|
|
||||||
def add_build_opts(parser) -> None:
|
def add_build_opts(parser) -> None:
|
||||||
|
|
|
@ -132,6 +132,8 @@ class LinuxSourceTree(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
if kunitconfig_path:
|
if kunitconfig_path:
|
||||||
|
if os.path.isdir(kunitconfig_path):
|
||||||
|
kunitconfig_path = os.path.join(kunitconfig_path, KUNITCONFIG_PATH)
|
||||||
if not os.path.exists(kunitconfig_path):
|
if not os.path.exists(kunitconfig_path):
|
||||||
raise ConfigError(f'Specified kunitconfig ({kunitconfig_path}) does not exist')
|
raise ConfigError(f'Specified kunitconfig ({kunitconfig_path}) does not exist')
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -251,6 +251,12 @@ class LinuxSourceTreeTest(unittest.TestCase):
|
||||||
with tempfile.NamedTemporaryFile('wt') as kunitconfig:
|
with tempfile.NamedTemporaryFile('wt') as kunitconfig:
|
||||||
tree = kunit_kernel.LinuxSourceTree('', kunitconfig_path=kunitconfig.name)
|
tree = kunit_kernel.LinuxSourceTree('', kunitconfig_path=kunitconfig.name)
|
||||||
|
|
||||||
|
def test_dir_kunitconfig(self):
|
||||||
|
with tempfile.TemporaryDirectory('') as dir:
|
||||||
|
with open(os.path.join(dir, '.kunitconfig'), 'w') as f:
|
||||||
|
pass
|
||||||
|
tree = kunit_kernel.LinuxSourceTree('', kunitconfig_path=dir)
|
||||||
|
|
||||||
# TODO: add more test cases.
|
# TODO: add more test cases.
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче