Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+198 -24
+76 -2
Documentation/dev-tools/kunit/tips.rst
··· 78 78 void test_only_hook(void) { } 79 79 #endif 80 80 81 - TODO(dlatypov@google.com): 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 + 83 157 84 158 Customizing error messages 85 159 --------------------------
+29
include/kunit/test-bug.h
··· 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 <urielguajardo@google.com> 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 */
+3
lib/kunit/.kunitconfig
··· 1 + CONFIG_KUNIT=y 2 + CONFIG_KUNIT_TEST=y 3 + CONFIG_KUNIT_EXAMPLE_TEST=y
+44 -17
lib/kunit/assert.c
··· 25 25 } 26 26 27 27 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); 29 29 } 30 30 EXPORT_SYMBOL_GPL(kunit_base_assert_format); 31 31 ··· 48 48 void kunit_unary_assert_format(const struct kunit_assert *assert, 49 49 struct string_stream *stream) 50 50 { 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); 53 54 54 55 kunit_base_assert_format(assert, stream); 55 56 if (unary_assert->expected_true) ··· 68 67 void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert, 69 68 struct string_stream *stream) 70 69 { 71 - struct kunit_ptr_not_err_assert *ptr_assert = container_of( 72 - assert, struct kunit_ptr_not_err_assert, assert); 70 + struct kunit_ptr_not_err_assert *ptr_assert; 71 + 72 + ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert, 73 + assert); 73 74 74 75 kunit_base_assert_format(assert, stream); 75 76 if (!ptr_assert->value) { ··· 114 111 void kunit_binary_assert_format(const struct kunit_assert *assert, 115 112 struct string_stream *stream) 116 113 { 117 - struct kunit_binary_assert *binary_assert = container_of( 118 - assert, struct kunit_binary_assert, assert); 114 + struct kunit_binary_assert *binary_assert; 115 + 116 + binary_assert = container_of(assert, struct kunit_binary_assert, 117 + assert); 119 118 120 119 kunit_base_assert_format(assert, stream); 121 120 string_stream_add(stream, ··· 142 137 void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, 143 138 struct string_stream *stream) 144 139 { 145 - struct kunit_binary_ptr_assert *binary_assert = container_of( 146 - assert, struct kunit_binary_ptr_assert, assert); 140 + struct kunit_binary_ptr_assert *binary_assert; 141 + 142 + binary_assert = container_of(assert, struct kunit_binary_ptr_assert, 143 + assert); 147 144 148 145 kunit_base_assert_format(assert, stream); 149 146 string_stream_add(stream, ··· 163 156 } 164 157 EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format); 165 158 159 + /* Checks if KUNIT_EXPECT_STREQ() args were string literals. 160 + * Note: `text` will have ""s where as `value` will not. 161 + */ 162 + static bool is_str_literal(const char *text, const char *value) 163 + { 164 + int len; 165 + 166 + len = strlen(text); 167 + if (len < 2) 168 + return false; 169 + if (text[0] != '\"' || text[len - 1] != '\"') 170 + return false; 171 + 172 + return strncmp(text + 1, value, len - 2) == 0; 173 + } 174 + 166 175 void kunit_binary_str_assert_format(const struct kunit_assert *assert, 167 176 struct string_stream *stream) 168 177 { 169 - struct kunit_binary_str_assert *binary_assert = container_of( 170 - assert, struct kunit_binary_str_assert, assert); 178 + struct kunit_binary_str_assert *binary_assert; 179 + 180 + binary_assert = container_of(assert, struct kunit_binary_str_assert, 181 + assert); 171 182 172 183 kunit_base_assert_format(assert, stream); 173 184 string_stream_add(stream, ··· 193 168 binary_assert->left_text, 194 169 binary_assert->operation, 195 170 binary_assert->right_text); 196 - string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s\n", 197 - binary_assert->left_text, 198 - binary_assert->left_value); 199 - string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %s", 200 - binary_assert->right_text, 201 - binary_assert->right_value); 171 + if (!is_str_literal(binary_assert->left_text, binary_assert->left_value)) 172 + string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"\n", 173 + binary_assert->left_text, 174 + binary_assert->left_value); 175 + if (!is_str_literal(binary_assert->right_text, binary_assert->right_value)) 176 + string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"", 177 + binary_assert->right_text, 178 + binary_assert->right_value); 202 179 kunit_assert_print_msg(assert, stream); 203 180 } 204 181 EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
+35 -4
lib/kunit/test.c
··· 7 7 */ 8 8 9 9 #include <kunit/test.h> 10 + #include <kunit/test-bug.h> 10 11 #include <linux/kernel.h> 11 12 #include <linux/kref.h> 12 13 #include <linux/sched/debug.h> ··· 16 15 #include "debugfs.h" 17 16 #include "string-stream.h" 18 17 #include "try-catch-impl.h" 18 + 19 + #if IS_BUILTIN(CONFIG_KUNIT) 20 + /* 21 + * Fail the current test and print an error message to the log. 22 + */ 23 + void __kunit_fail_current_test(const char *file, int line, const char *fmt, ...) 24 + { 25 + va_list args; 26 + int len; 27 + char *buffer; 28 + 29 + if (!current->kunit_test) 30 + return; 31 + 32 + kunit_set_failure(current->kunit_test); 33 + 34 + /* kunit_err() only accepts literals, so evaluate the args first. */ 35 + va_start(args, fmt); 36 + len = vsnprintf(NULL, 0, fmt, args) + 1; 37 + va_end(args); 38 + 39 + buffer = kunit_kmalloc(current->kunit_test, len, GFP_KERNEL); 40 + if (!buffer) 41 + return; 42 + 43 + va_start(args, fmt); 44 + vsnprintf(buffer, len, fmt, args); 45 + va_end(args); 46 + 47 + kunit_err(current->kunit_test, "%s:%d: %s", file, line, buffer); 48 + kunit_kfree(current->kunit_test, buffer); 49 + } 50 + EXPORT_SYMBOL_GPL(__kunit_fail_current_test); 51 + #endif 19 52 20 53 /* 21 54 * Append formatted message to log, size of which is limited to ··· 308 273 struct kunit_suite *suite = ctx->suite; 309 274 struct kunit_case *test_case = ctx->test_case; 310 275 311 - #if (IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT)) 312 276 current->kunit_test = test; 313 - #endif /* IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT) */ 314 277 315 278 /* 316 279 * kunit_run_case_internal may encounter a fatal error; if it does, ··· 657 624 spin_unlock(&test->lock); 658 625 kunit_remove_resource(test, res); 659 626 } 660 - #if (IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT)) 661 627 current->kunit_test = NULL; 662 - #endif /* IS_ENABLED(CONFIG_KASAN) && IS_ENABLED(CONFIG_KUNIT)*/ 663 628 } 664 629 EXPORT_SYMBOL_GPL(kunit_cleanup); 665 630
+3 -1
tools/testing/kunit/kunit.py
··· 184 184 help='Run all KUnit tests through allyesconfig', 185 185 action='store_true') 186 186 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.', 188 190 metavar='kunitconfig') 189 191 190 192 def add_build_opts(parser) -> None:
+2
tools/testing/kunit/kunit_kernel.py
··· 132 132 return 133 133 134 134 if kunitconfig_path: 135 + if os.path.isdir(kunitconfig_path): 136 + kunitconfig_path = os.path.join(kunitconfig_path, KUNITCONFIG_PATH) 135 137 if not os.path.exists(kunitconfig_path): 136 138 raise ConfigError(f'Specified kunitconfig ({kunitconfig_path}) does not exist') 137 139 else:
+6
tools/testing/kunit/kunit_tool_test.py
··· 251 251 with tempfile.NamedTemporaryFile('wt') as kunitconfig: 252 252 tree = kunit_kernel.LinuxSourceTree('', kunitconfig_path=kunitconfig.name) 253 253 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 + 254 260 # TODO: add more test cases. 255 261 256 262