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:
commit
1e9599dfc4
@ -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
include/kunit/test-bug.h
Normal file
29
include/kunit/test-bug.h
Normal file
@ -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 */
|
3
lib/kunit/.kunitconfig
Normal file
3
lib/kunit/.kunitconfig
Normal file
@ -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",
|
||||
expect_or_assert, assert->file, assert->line);
|
||||
expect_or_assert, assert->file, assert->line);
|
||||
}
|
||||
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,
|
||||
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,12 +193,14 @@ 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",
|
||||
binary_assert->left_text,
|
||||
binary_assert->left_value);
|
||||
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s",
|
||||
binary_assert->right_text,
|
||||
binary_assert->right_value);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -184,7 +184,9 @@ def add_common_opts(parser) -> None:
|
||||
help='Run all KUnit tests through allyesconfig',
|
||||
action='store_true')
|
||||
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')
|
||||
|
||||
def add_build_opts(parser) -> None:
|
||||
|
@ -132,6 +132,8 @@ class LinuxSourceTree(object):
|
||||
return
|
||||
|
||||
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):
|
||||
raise ConfigError(f'Specified kunitconfig ({kunitconfig_path}) does not exist')
|
||||
else:
|
||||
|
@ -251,6 +251,12 @@ class LinuxSourceTreeTest(unittest.TestCase):
|
||||
with tempfile.NamedTemporaryFile('wt') as kunitconfig:
|
||||
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.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user