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

Configure Feed

Select the types of activity you want to include in your feed.

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

Pull KUnit update from Shuah Khan:

- add Function Redirection API to isolate the code being tested from
other parts of the kernel.

Documentation/dev-tools/kunit/api/functionredirection.rst has the
details.

* tag 'linux-kselftest-kunit-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
kunit: Add printf attribute to fail_current_test_impl
lib/hashtable_test.c: add test for the hashtable structure
Documentation: Add Function Redirection API docs
kunit: Expose 'static stub' API to redirect functions
kunit: Add "hooks" to call into KUnit when it's built as a module
kunit: kunit.py extract handlers
tools/testing/kunit/kunit.py: remove redundant double check

+965 -122
+162
Documentation/dev-tools/kunit/api/functionredirection.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ======================== 4 + Function Redirection API 5 + ======================== 6 + 7 + Overview 8 + ======== 9 + 10 + When writing unit tests, it's important to be able to isolate the code being 11 + tested from other parts of the kernel. This ensures the reliability of the test 12 + (it won't be affected by external factors), reduces dependencies on specific 13 + hardware or config options (making the test easier to run), and protects the 14 + stability of the rest of the system (making it less likely for test-specific 15 + state to interfere with the rest of the system). 16 + 17 + While for some code (typically generic data structures, helpers, and other 18 + "pure functions") this is trivial, for others (like device drivers, 19 + filesystems, core subsystems) the code is heavily coupled with other parts of 20 + the kernel. 21 + 22 + This coupling is often due to global state in some way: be it a global list of 23 + devices, the filesystem, or some hardware state. Tests need to either carefully 24 + manage, isolate, and restore state, or they can avoid it altogether by 25 + replacing access to and mutation of this state with a "fake" or "mock" variant. 26 + 27 + By refactoring access to such state, such as by introducing a layer of 28 + indirection which can use or emulate a separate set of test state. However, 29 + such refactoring comes with its own costs (and undertaking significant 30 + refactoring before being able to write tests is suboptimal). 31 + 32 + A simpler way to intercept and replace some of the function calls is to use 33 + function redirection via static stubs. 34 + 35 + 36 + Static Stubs 37 + ============ 38 + 39 + Static stubs are a way of redirecting calls to one function (the "real" 40 + function) to another function (the "replacement" function). 41 + 42 + It works by adding a macro to the "real" function which checks to see if a test 43 + is running, and if a replacement function is available. If so, that function is 44 + called in place of the original. 45 + 46 + Using static stubs is pretty straightforward: 47 + 48 + 1. Add the KUNIT_STATIC_STUB_REDIRECT() macro to the start of the "real" 49 + function. 50 + 51 + This should be the first statement in the function, after any variable 52 + declarations. KUNIT_STATIC_STUB_REDIRECT() takes the name of the 53 + function, followed by all of the arguments passed to the real function. 54 + 55 + For example: 56 + 57 + .. code-block:: c 58 + 59 + void send_data_to_hardware(const char *str) 60 + { 61 + KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str); 62 + /* real implementation */ 63 + } 64 + 65 + 2. Write one or more replacement functions. 66 + 67 + These functions should have the same function signature as the real function. 68 + In the event they need to access or modify test-specific state, they can use 69 + kunit_get_current_test() to get a struct kunit pointer. This can then 70 + be passed to the expectation/assertion macros, or used to look up KUnit 71 + resources. 72 + 73 + For example: 74 + 75 + .. code-block:: c 76 + 77 + void fake_send_data_to_hardware(const char *str) 78 + { 79 + struct kunit *test = kunit_get_current_test(); 80 + KUNIT_EXPECT_STREQ(test, str, "Hello World!"); 81 + } 82 + 83 + 3. Activate the static stub from your test. 84 + 85 + From within a test, the redirection can be enabled with 86 + kunit_activate_static_stub(), which accepts a struct kunit pointer, 87 + the real function, and the replacement function. You can call this several 88 + times with different replacement functions to swap out implementations of the 89 + function. 90 + 91 + In our example, this would be 92 + 93 + .. code-block:: c 94 + 95 + kunit_activate_static_stub(test, 96 + send_data_to_hardware, 97 + fake_send_data_to_hardware); 98 + 99 + 4. Call (perhaps indirectly) the real function. 100 + 101 + Once the redirection is activated, any call to the real function will call 102 + the replacement function instead. Such calls may be buried deep in the 103 + implementation of another function, but must occur from the test's kthread. 104 + 105 + For example: 106 + 107 + .. code-block:: c 108 + 109 + send_data_to_hardware("Hello World!"); /* Succeeds */ 110 + send_data_to_hardware("Something else"); /* Fails the test. */ 111 + 112 + 5. (Optionally) disable the stub. 113 + 114 + When you no longer need it, disable the redirection (and hence resume the 115 + original behaviour of the 'real' function) using 116 + kunit_deactivate_static_stub(). Otherwise, it will be automatically disabled 117 + when the test exits. 118 + 119 + For example: 120 + 121 + .. code-block:: c 122 + 123 + kunit_deactivate_static_stub(test, send_data_to_hardware); 124 + 125 + 126 + It's also possible to use these replacement functions to test to see if a 127 + function is called at all, for example: 128 + 129 + .. code-block:: c 130 + 131 + void send_data_to_hardware(const char *str) 132 + { 133 + KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str); 134 + /* real implementation */ 135 + } 136 + 137 + /* In test file */ 138 + int times_called = 0; 139 + void fake_send_data_to_hardware(const char *str) 140 + { 141 + times_called++; 142 + } 143 + ... 144 + /* In the test case, redirect calls for the duration of the test */ 145 + kunit_activate_static_stub(test, send_data_to_hardware, fake_send_data_to_hardware); 146 + 147 + send_data_to_hardware("hello"); 148 + KUNIT_EXPECT_EQ(test, times_called, 1); 149 + 150 + /* Can also deactivate the stub early, if wanted */ 151 + kunit_deactivate_static_stub(test, send_data_to_hardware); 152 + 153 + send_data_to_hardware("hello again"); 154 + KUNIT_EXPECT_EQ(test, times_called, 1); 155 + 156 + 157 + 158 + API Reference 159 + ============= 160 + 161 + .. kernel-doc:: include/kunit/static_stub.h 162 + :internal:
+10 -3
Documentation/dev-tools/kunit/api/index.rst
··· 4 4 API Reference 5 5 ============= 6 6 .. toctree:: 7 + :hidden: 7 8 8 9 test 9 10 resource 11 + functionredirection 10 12 11 - This section documents the KUnit kernel testing API. It is divided into the 13 + 14 + This page documents the KUnit kernel testing API. It is divided into the 12 15 following sections: 13 16 14 17 Documentation/dev-tools/kunit/api/test.rst 15 18 16 - - documents all of the standard testing API 19 + - Documents all of the standard testing API 17 20 18 21 Documentation/dev-tools/kunit/api/resource.rst 19 22 20 - - documents the KUnit resource API 23 + - Documents the KUnit resource API 24 + 25 + Documentation/dev-tools/kunit/api/functionredirection.rst 26 + 27 + - Documents the KUnit Function Redirection API
+6 -9
Documentation/dev-tools/kunit/usage.rst
··· 648 648 access using the ``kunit_get_current_test()`` function in ``kunit/test-bug.h``. 649 649 650 650 ``kunit_get_current_test()`` is safe to call even if KUnit is not enabled. If 651 - KUnit is not enabled, was built as a module (``CONFIG_KUNIT=m``), or no test is 652 - running in the current task, it will return ``NULL``. This compiles down to 653 - either a no-op or a static key check, so will have a negligible performance 654 - impact when no test is running. 651 + KUnit is not enabled, or if no test is running in the current task, it will 652 + return ``NULL``. This compiles down to either a no-op or a static key check, 653 + so will have a negligible performance impact when no test is running. 655 654 656 655 The example below uses this to implement a "mock" implementation of a function, ``foo``: 657 656 ··· 725 726 #endif 726 727 727 728 ``kunit_fail_current_test()`` is safe to call even if KUnit is not enabled. If 728 - KUnit is not enabled, was built as a module (``CONFIG_KUNIT=m``), or no test is 729 - running in the current task, it will do nothing. This compiles down to either a 730 - no-op or a static key check, so will have a negligible performance impact when 731 - no test is running. 732 - 729 + KUnit is not enabled, or if no test is running in the current task, it will do 730 + nothing. This compiles down to either a no-op or a static key check, so will 731 + have a negligible performance impact when no test is running.
+113
include/kunit/static_stub.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * KUnit function redirection (static stubbing) API. 4 + * 5 + * Copyright (C) 2022, Google LLC. 6 + * Author: David Gow <davidgow@google.com> 7 + */ 8 + #ifndef _KUNIT_STATIC_STUB_H 9 + #define _KUNIT_STATIC_STUB_H 10 + 11 + #if !IS_ENABLED(CONFIG_KUNIT) 12 + 13 + /* If CONFIG_KUNIT is not enabled, these stubs quietly disappear. */ 14 + #define KUNIT_TRIGGER_STATIC_STUB(real_fn_name, args...) do {} while (0) 15 + 16 + #else 17 + 18 + #include <kunit/test.h> 19 + #include <kunit/test-bug.h> 20 + 21 + #include <linux/compiler.h> /* for {un,}likely() */ 22 + #include <linux/sched.h> /* for task_struct */ 23 + 24 + 25 + /** 26 + * KUNIT_STATIC_STUB_REDIRECT() - call a replacement 'static stub' if one exists 27 + * @real_fn_name: The name of this function (as an identifier, not a string) 28 + * @args: All of the arguments passed to this function 29 + * 30 + * This is a function prologue which is used to allow calls to the current 31 + * function to be redirected by a KUnit test. KUnit tests can call 32 + * kunit_activate_static_stub() to pass a replacement function in. The 33 + * replacement function will be called by KUNIT_TRIGGER_STATIC_STUB(), which 34 + * will then return from the function. If the caller is not in a KUnit context, 35 + * the function will continue execution as normal. 36 + * 37 + * Example: 38 + * 39 + * .. code-block:: c 40 + * 41 + * int real_func(int n) 42 + * { 43 + * KUNIT_STATIC_STUB_REDIRECT(real_func, n); 44 + * return 0; 45 + * } 46 + * 47 + * int replacement_func(int n) 48 + * { 49 + * return 42; 50 + * } 51 + * 52 + * void example_test(struct kunit *test) 53 + * { 54 + * kunit_activate_static_stub(test, real_func, replacement_func); 55 + * KUNIT_EXPECT_EQ(test, real_func(1), 42); 56 + * } 57 + * 58 + */ 59 + #define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...) \ 60 + do { \ 61 + typeof(&real_fn_name) replacement; \ 62 + struct kunit *current_test = kunit_get_current_test(); \ 63 + \ 64 + if (likely(!current_test)) \ 65 + break; \ 66 + \ 67 + replacement = kunit_hooks.get_static_stub_address(current_test, \ 68 + &real_fn_name); \ 69 + \ 70 + if (unlikely(replacement)) \ 71 + return replacement(args); \ 72 + } while (0) 73 + 74 + /* Helper function for kunit_activate_static_stub(). The macro does 75 + * typechecking, so use it instead. 76 + */ 77 + void __kunit_activate_static_stub(struct kunit *test, 78 + void *real_fn_addr, 79 + void *replacement_addr); 80 + 81 + /** 82 + * kunit_activate_static_stub() - replace a function using static stubs. 83 + * @test: A pointer to the 'struct kunit' test context for the current test. 84 + * @real_fn_addr: The address of the function to replace. 85 + * @replacement_addr: The address of the function to replace it with. 86 + * 87 + * When activated, calls to real_fn_addr from within this test (even if called 88 + * indirectly) will instead call replacement_addr. The function pointed to by 89 + * real_fn_addr must begin with the static stub prologue in 90 + * KUNIT_TRIGGER_STATIC_STUB() for this to work. real_fn_addr and 91 + * replacement_addr must have the same type. 92 + * 93 + * The redirection can be disabled again with kunit_deactivate_static_stub(). 94 + */ 95 + #define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do { \ 96 + typecheck_fn(typeof(&real_fn_addr), replacement_addr); \ 97 + __kunit_activate_static_stub(test, real_fn_addr, replacement_addr); \ 98 + } while (0) 99 + 100 + 101 + /** 102 + * kunit_deactivate_static_stub() - disable a function redirection 103 + * @test: A pointer to the 'struct kunit' test context for the current test. 104 + * @real_fn_addr: The address of the function to no-longer redirect 105 + * 106 + * Deactivates a redirection configured with kunit_activate_static_stub(). After 107 + * this function returns, calls to real_fn_addr() will execute the original 108 + * real_fn, not any previously-configured replacement. 109 + */ 110 + void kunit_deactivate_static_stub(struct kunit *test, void *real_fn_addr); 111 + 112 + #endif 113 + #endif
+11 -18
include/kunit/test-bug.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* 3 - * KUnit API allowing dynamic analysis tools to interact with KUnit tests 3 + * KUnit API providing hooks for non-test code to interact with tests. 4 4 * 5 5 * Copyright (C) 2020, Google LLC. 6 6 * Author: Uriel Guajardo <urielguajardo@google.com> ··· 9 9 #ifndef _KUNIT_TEST_BUG_H 10 10 #define _KUNIT_TEST_BUG_H 11 11 12 - #if IS_BUILTIN(CONFIG_KUNIT) 12 + #if IS_ENABLED(CONFIG_KUNIT) 13 13 14 14 #include <linux/jump_label.h> /* For static branch */ 15 15 #include <linux/sched.h> 16 16 17 17 /* Static key if KUnit is running any tests. */ 18 18 DECLARE_STATIC_KEY_FALSE(kunit_running); 19 + 20 + /* Hooks table: a table of function pointers filled in when kunit loads */ 21 + extern struct kunit_hooks_table { 22 + __printf(3, 4) void (*fail_current_test)(const char*, int, const char*, ...); 23 + void *(*get_static_stub_address)(struct kunit *test, void *real_fn_addr); 24 + } kunit_hooks; 19 25 20 26 /** 21 27 * kunit_get_current_test() - Return a pointer to the currently running ··· 49 43 * kunit_fail_current_test() - If a KUnit test is running, fail it. 50 44 * 51 45 * If a KUnit test is running in the current task, mark that test as failed. 52 - * 53 - * This macro will only work if KUnit is built-in (though the tests 54 - * themselves can be modules). Otherwise, it compiles down to nothing. 55 46 */ 56 47 #define kunit_fail_current_test(fmt, ...) do { \ 57 48 if (static_branch_unlikely(&kunit_running)) { \ 58 - __kunit_fail_current_test(__FILE__, __LINE__, \ 49 + /* Guaranteed to be non-NULL when kunit_running true*/ \ 50 + kunit_hooks.fail_current_test(__FILE__, __LINE__, \ 59 51 fmt, ##__VA_ARGS__); \ 60 52 } \ 61 53 } while (0) 62 - 63 - 64 - extern __printf(3, 4) void __kunit_fail_current_test(const char *file, int line, 65 - const char *fmt, ...); 66 54 67 55 #else 68 56 69 57 static inline struct kunit *kunit_get_current_test(void) { return NULL; } 70 58 71 - /* We define this with an empty helper function so format string warnings work */ 72 - #define kunit_fail_current_test(fmt, ...) \ 73 - __kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__) 74 - 75 - static inline __printf(3, 4) void __kunit_fail_current_test(const char *file, int line, 76 - const char *fmt, ...) 77 - { 78 - } 59 + #define kunit_fail_current_test(fmt, ...) do {} while (0) 79 60 80 61 #endif 81 62
+13
lib/Kconfig.debug
··· 2517 2517 2518 2518 If unsure, say N. 2519 2519 2520 + config HASHTABLE_KUNIT_TEST 2521 + tristate "KUnit Test for Kernel Hashtable structures" if !KUNIT_ALL_TESTS 2522 + depends on KUNIT 2523 + default KUNIT_ALL_TESTS 2524 + help 2525 + This builds the hashtable KUnit test suite. 2526 + It tests the basic functionality of the API defined in 2527 + include/linux/hashtable.h. For more information on KUnit and 2528 + unit tests in general please refer to the KUnit documentation 2529 + in Documentation/dev-tools/kunit/. 2530 + 2531 + If unsure, say N. 2532 + 2520 2533 config LINEAR_RANGES_TEST 2521 2534 tristate "KUnit test for linear_ranges" 2522 2535 depends on KUNIT
+9
lib/Makefile
··· 126 126 obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/ 127 127 128 128 obj-$(CONFIG_KUNIT) += kunit/ 129 + # Include the KUnit hooks unconditionally. They'll compile to nothing if 130 + # CONFIG_KUNIT=n, otherwise will be a small table of static data (static key, 131 + # function pointers) which need to be built-in even when KUnit is a module. 132 + ifeq ($(CONFIG_KUNIT), m) 133 + obj-y += kunit/hooks.o 134 + else 135 + obj-$(CONFIG_KUNIT) += kunit/hooks.o 136 + endif 129 137 130 138 ifeq ($(CONFIG_DEBUG_KOBJECT),y) 131 139 CFLAGS_kobject.o += -DDEBUG ··· 377 369 CFLAGS_bitfield_kunit.o := $(DISABLE_STRUCTLEAK_PLUGIN) 378 370 obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o 379 371 obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o 372 + obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o 380 373 obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o 381 374 obj-$(CONFIG_BITS_TEST) += test_bits.o 382 375 obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o
+317
lib/hashtable_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * KUnit test for the Kernel Hashtable structures. 4 + * 5 + * Copyright (C) 2022, Google LLC. 6 + * Author: Rae Moar <rmoar@google.com> 7 + */ 8 + #include <kunit/test.h> 9 + 10 + #include <linux/hashtable.h> 11 + 12 + struct hashtable_test_entry { 13 + int key; 14 + int data; 15 + struct hlist_node node; 16 + int visited; 17 + }; 18 + 19 + static void hashtable_test_hash_init(struct kunit *test) 20 + { 21 + /* Test the different ways of initialising a hashtable. */ 22 + DEFINE_HASHTABLE(hash1, 2); 23 + DECLARE_HASHTABLE(hash2, 3); 24 + 25 + /* When using DECLARE_HASHTABLE, must use hash_init to 26 + * initialize the hashtable. 27 + */ 28 + hash_init(hash2); 29 + 30 + KUNIT_EXPECT_TRUE(test, hash_empty(hash1)); 31 + KUNIT_EXPECT_TRUE(test, hash_empty(hash2)); 32 + } 33 + 34 + static void hashtable_test_hash_empty(struct kunit *test) 35 + { 36 + struct hashtable_test_entry a; 37 + DEFINE_HASHTABLE(hash, 1); 38 + 39 + KUNIT_EXPECT_TRUE(test, hash_empty(hash)); 40 + 41 + a.key = 1; 42 + a.data = 13; 43 + hash_add(hash, &a.node, a.key); 44 + 45 + /* Hashtable should no longer be empty. */ 46 + KUNIT_EXPECT_FALSE(test, hash_empty(hash)); 47 + } 48 + 49 + static void hashtable_test_hash_hashed(struct kunit *test) 50 + { 51 + struct hashtable_test_entry a, b; 52 + DEFINE_HASHTABLE(hash, 4); 53 + 54 + a.key = 1; 55 + a.data = 13; 56 + hash_add(hash, &a.node, a.key); 57 + b.key = 1; 58 + b.data = 2; 59 + hash_add(hash, &b.node, b.key); 60 + 61 + KUNIT_EXPECT_TRUE(test, hash_hashed(&a.node)); 62 + KUNIT_EXPECT_TRUE(test, hash_hashed(&b.node)); 63 + } 64 + 65 + static void hashtable_test_hash_add(struct kunit *test) 66 + { 67 + struct hashtable_test_entry a, b, *x; 68 + int bkt; 69 + DEFINE_HASHTABLE(hash, 3); 70 + 71 + a.key = 1; 72 + a.data = 13; 73 + a.visited = 0; 74 + hash_add(hash, &a.node, a.key); 75 + b.key = 2; 76 + b.data = 10; 77 + b.visited = 0; 78 + hash_add(hash, &b.node, b.key); 79 + 80 + hash_for_each(hash, bkt, x, node) { 81 + x->visited++; 82 + if (x->key == a.key) 83 + KUNIT_EXPECT_EQ(test, x->data, 13); 84 + else if (x->key == b.key) 85 + KUNIT_EXPECT_EQ(test, x->data, 10); 86 + else 87 + KUNIT_FAIL(test, "Unexpected key in hashtable."); 88 + } 89 + 90 + /* Both entries should have been visited exactly once. */ 91 + KUNIT_EXPECT_EQ(test, a.visited, 1); 92 + KUNIT_EXPECT_EQ(test, b.visited, 1); 93 + } 94 + 95 + static void hashtable_test_hash_del(struct kunit *test) 96 + { 97 + struct hashtable_test_entry a, b, *x; 98 + DEFINE_HASHTABLE(hash, 6); 99 + 100 + a.key = 1; 101 + a.data = 13; 102 + hash_add(hash, &a.node, a.key); 103 + b.key = 2; 104 + b.data = 10; 105 + b.visited = 0; 106 + hash_add(hash, &b.node, b.key); 107 + 108 + hash_del(&b.node); 109 + hash_for_each_possible(hash, x, node, b.key) { 110 + x->visited++; 111 + KUNIT_EXPECT_NE(test, x->key, b.key); 112 + } 113 + 114 + /* The deleted entry should not have been visited. */ 115 + KUNIT_EXPECT_EQ(test, b.visited, 0); 116 + 117 + hash_del(&a.node); 118 + 119 + /* The hashtable should be empty. */ 120 + KUNIT_EXPECT_TRUE(test, hash_empty(hash)); 121 + } 122 + 123 + static void hashtable_test_hash_for_each(struct kunit *test) 124 + { 125 + struct hashtable_test_entry entries[3]; 126 + struct hashtable_test_entry *x; 127 + int bkt, i, j, count; 128 + DEFINE_HASHTABLE(hash, 3); 129 + 130 + /* Add three entries to the hashtable. */ 131 + for (i = 0; i < 3; i++) { 132 + entries[i].key = i; 133 + entries[i].data = i + 10; 134 + entries[i].visited = 0; 135 + hash_add(hash, &entries[i].node, entries[i].key); 136 + } 137 + 138 + count = 0; 139 + hash_for_each(hash, bkt, x, node) { 140 + x->visited += 1; 141 + KUNIT_ASSERT_GE_MSG(test, x->key, 0, "Unexpected key in hashtable."); 142 + KUNIT_ASSERT_LT_MSG(test, x->key, 3, "Unexpected key in hashtable."); 143 + count++; 144 + } 145 + 146 + /* Should have visited each entry exactly once. */ 147 + KUNIT_EXPECT_EQ(test, count, 3); 148 + for (j = 0; j < 3; j++) 149 + KUNIT_EXPECT_EQ(test, entries[j].visited, 1); 150 + } 151 + 152 + static void hashtable_test_hash_for_each_safe(struct kunit *test) 153 + { 154 + struct hashtable_test_entry entries[3]; 155 + struct hashtable_test_entry *x; 156 + struct hlist_node *tmp; 157 + int bkt, i, j, count; 158 + DEFINE_HASHTABLE(hash, 3); 159 + 160 + /* Add three entries to the hashtable. */ 161 + for (i = 0; i < 3; i++) { 162 + entries[i].key = i; 163 + entries[i].data = i + 10; 164 + entries[i].visited = 0; 165 + hash_add(hash, &entries[i].node, entries[i].key); 166 + } 167 + 168 + count = 0; 169 + hash_for_each_safe(hash, bkt, tmp, x, node) { 170 + x->visited += 1; 171 + KUNIT_ASSERT_GE_MSG(test, x->key, 0, "Unexpected key in hashtable."); 172 + KUNIT_ASSERT_LT_MSG(test, x->key, 3, "Unexpected key in hashtable."); 173 + count++; 174 + 175 + /* Delete entry during loop. */ 176 + hash_del(&x->node); 177 + } 178 + 179 + /* Should have visited each entry exactly once. */ 180 + KUNIT_EXPECT_EQ(test, count, 3); 181 + for (j = 0; j < 3; j++) 182 + KUNIT_EXPECT_EQ(test, entries[j].visited, 1); 183 + } 184 + 185 + static void hashtable_test_hash_for_each_possible(struct kunit *test) 186 + { 187 + struct hashtable_test_entry entries[4]; 188 + struct hashtable_test_entry *x, *y; 189 + int buckets[2]; 190 + int bkt, i, j, count; 191 + DEFINE_HASHTABLE(hash, 5); 192 + 193 + /* Add three entries with key = 0 to the hashtable. */ 194 + for (i = 0; i < 3; i++) { 195 + entries[i].key = 0; 196 + entries[i].data = i; 197 + entries[i].visited = 0; 198 + hash_add(hash, &entries[i].node, entries[i].key); 199 + } 200 + 201 + /* Add an entry with key = 1. */ 202 + entries[3].key = 1; 203 + entries[3].data = 3; 204 + entries[3].visited = 0; 205 + hash_add(hash, &entries[3].node, entries[3].key); 206 + 207 + count = 0; 208 + hash_for_each_possible(hash, x, node, 0) { 209 + x->visited += 1; 210 + KUNIT_ASSERT_GE_MSG(test, x->data, 0, "Unexpected data in hashtable."); 211 + KUNIT_ASSERT_LT_MSG(test, x->data, 4, "Unexpected data in hashtable."); 212 + count++; 213 + } 214 + 215 + /* Should have visited each entry with key = 0 exactly once. */ 216 + for (j = 0; j < 3; j++) 217 + KUNIT_EXPECT_EQ(test, entries[j].visited, 1); 218 + 219 + /* Save the buckets for the different keys. */ 220 + hash_for_each(hash, bkt, y, node) { 221 + KUNIT_ASSERT_GE_MSG(test, y->key, 0, "Unexpected key in hashtable."); 222 + KUNIT_ASSERT_LE_MSG(test, y->key, 1, "Unexpected key in hashtable."); 223 + buckets[y->key] = bkt; 224 + } 225 + 226 + /* If entry with key = 1 is in the same bucket as the entries with 227 + * key = 0, check it was visited. Otherwise ensure that only three 228 + * entries were visited. 229 + */ 230 + if (buckets[0] == buckets[1]) { 231 + KUNIT_EXPECT_EQ(test, count, 4); 232 + KUNIT_EXPECT_EQ(test, entries[3].visited, 1); 233 + } else { 234 + KUNIT_EXPECT_EQ(test, count, 3); 235 + KUNIT_EXPECT_EQ(test, entries[3].visited, 0); 236 + } 237 + } 238 + 239 + static void hashtable_test_hash_for_each_possible_safe(struct kunit *test) 240 + { 241 + struct hashtable_test_entry entries[4]; 242 + struct hashtable_test_entry *x, *y; 243 + struct hlist_node *tmp; 244 + int buckets[2]; 245 + int bkt, i, j, count; 246 + DEFINE_HASHTABLE(hash, 5); 247 + 248 + /* Add three entries with key = 0 to the hashtable. */ 249 + for (i = 0; i < 3; i++) { 250 + entries[i].key = 0; 251 + entries[i].data = i; 252 + entries[i].visited = 0; 253 + hash_add(hash, &entries[i].node, entries[i].key); 254 + } 255 + 256 + /* Add an entry with key = 1. */ 257 + entries[3].key = 1; 258 + entries[3].data = 3; 259 + entries[3].visited = 0; 260 + hash_add(hash, &entries[3].node, entries[3].key); 261 + 262 + count = 0; 263 + hash_for_each_possible_safe(hash, x, tmp, node, 0) { 264 + x->visited += 1; 265 + KUNIT_ASSERT_GE_MSG(test, x->data, 0, "Unexpected data in hashtable."); 266 + KUNIT_ASSERT_LT_MSG(test, x->data, 4, "Unexpected data in hashtable."); 267 + count++; 268 + 269 + /* Delete entry during loop. */ 270 + hash_del(&x->node); 271 + } 272 + 273 + /* Should have visited each entry with key = 0 exactly once. */ 274 + for (j = 0; j < 3; j++) 275 + KUNIT_EXPECT_EQ(test, entries[j].visited, 1); 276 + 277 + /* Save the buckets for the different keys. */ 278 + hash_for_each(hash, bkt, y, node) { 279 + KUNIT_ASSERT_GE_MSG(test, y->key, 0, "Unexpected key in hashtable."); 280 + KUNIT_ASSERT_LE_MSG(test, y->key, 1, "Unexpected key in hashtable."); 281 + buckets[y->key] = bkt; 282 + } 283 + 284 + /* If entry with key = 1 is in the same bucket as the entries with 285 + * key = 0, check it was visited. Otherwise ensure that only three 286 + * entries were visited. 287 + */ 288 + if (buckets[0] == buckets[1]) { 289 + KUNIT_EXPECT_EQ(test, count, 4); 290 + KUNIT_EXPECT_EQ(test, entries[3].visited, 1); 291 + } else { 292 + KUNIT_EXPECT_EQ(test, count, 3); 293 + KUNIT_EXPECT_EQ(test, entries[3].visited, 0); 294 + } 295 + } 296 + 297 + static struct kunit_case hashtable_test_cases[] = { 298 + KUNIT_CASE(hashtable_test_hash_init), 299 + KUNIT_CASE(hashtable_test_hash_empty), 300 + KUNIT_CASE(hashtable_test_hash_hashed), 301 + KUNIT_CASE(hashtable_test_hash_add), 302 + KUNIT_CASE(hashtable_test_hash_del), 303 + KUNIT_CASE(hashtable_test_hash_for_each), 304 + KUNIT_CASE(hashtable_test_hash_for_each_safe), 305 + KUNIT_CASE(hashtable_test_hash_for_each_possible), 306 + KUNIT_CASE(hashtable_test_hash_for_each_possible_safe), 307 + {}, 308 + }; 309 + 310 + static struct kunit_suite hashtable_test_module = { 311 + .name = "hashtable", 312 + .test_cases = hashtable_test_cases, 313 + }; 314 + 315 + kunit_test_suites(&hashtable_test_module); 316 + 317 + MODULE_LICENSE("GPL");
+4
lib/kunit/Makefile
··· 2 2 3 3 kunit-objs += test.o \ 4 4 resource.o \ 5 + static_stub.o \ 5 6 string-stream.o \ 6 7 assert.o \ 7 8 try-catch.o \ ··· 11 10 ifeq ($(CONFIG_KUNIT_DEBUGFS),y) 12 11 kunit-objs += debugfs.o 13 12 endif 13 + 14 + # KUnit 'hooks' are built-in even when KUnit is built as a module. 15 + lib-y += hooks.o 14 16 15 17 obj-$(CONFIG_KUNIT_TEST) += kunit-test.o 16 18
+31
lib/kunit/hooks-impl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Declarations for hook implementations. 4 + * 5 + * These will be set as the function pointers in struct kunit_hook_table, 6 + * found in include/kunit/test-bug.h. 7 + * 8 + * Copyright (C) 2023, Google LLC. 9 + * Author: David Gow <davidgow@google.com> 10 + */ 11 + 12 + #ifndef _KUNIT_HOOKS_IMPL_H 13 + #define _KUNIT_HOOKS_IMPL_H 14 + 15 + #include <kunit/test-bug.h> 16 + 17 + /* List of declarations. */ 18 + void __printf(3, 4) __kunit_fail_current_test_impl(const char *file, 19 + int line, 20 + const char *fmt, ...); 21 + void *__kunit_get_static_stub_address_impl(struct kunit *test, void *real_fn_addr); 22 + 23 + /* Code to set all of the function pointers. */ 24 + static inline void kunit_install_hooks(void) 25 + { 26 + /* Install the KUnit hook functions. */ 27 + kunit_hooks.fail_current_test = __kunit_fail_current_test_impl; 28 + kunit_hooks.get_static_stub_address = __kunit_get_static_stub_address_impl; 29 + } 30 + 31 + #endif /* _KUNIT_HOOKS_IMPL_H */
+21
lib/kunit/hooks.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * KUnit 'Hooks' implementation. 4 + * 5 + * This file contains code / structures which should be built-in even when 6 + * KUnit itself is built as a module. 7 + * 8 + * Copyright (C) 2022, Google LLC. 9 + * Author: David Gow <davidgow@google.com> 10 + */ 11 + 12 + 13 + #include <kunit/test-bug.h> 14 + 15 + DEFINE_STATIC_KEY_FALSE(kunit_running); 16 + EXPORT_SYMBOL(kunit_running); 17 + 18 + /* Function pointers for hooks. */ 19 + struct kunit_hooks_table kunit_hooks; 20 + EXPORT_SYMBOL(kunit_hooks); 21 +
+38
lib/kunit/kunit-example-test.c
··· 7 7 */ 8 8 9 9 #include <kunit/test.h> 10 + #include <kunit/static_stub.h> 10 11 11 12 /* 12 13 * This is the most fundamental element of KUnit, the test case. A test case ··· 131 130 KUNIT_ASSERT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!"); 132 131 } 133 132 133 + /* This is a function we'll replace with static stubs. */ 134 + static int add_one(int i) 135 + { 136 + /* This will trigger the stub if active. */ 137 + KUNIT_STATIC_STUB_REDIRECT(add_one, i); 138 + 139 + return i + 1; 140 + } 141 + 142 + /* This is used as a replacement for the above function. */ 143 + static int subtract_one(int i) 144 + { 145 + /* We don't need to trigger the stub from the replacement. */ 146 + 147 + return i - 1; 148 + } 149 + 150 + /* 151 + * This test shows the use of static stubs. 152 + */ 153 + static void example_static_stub_test(struct kunit *test) 154 + { 155 + /* By default, function is not stubbed. */ 156 + KUNIT_EXPECT_EQ(test, add_one(1), 2); 157 + 158 + /* Replace add_one() with subtract_one(). */ 159 + kunit_activate_static_stub(test, add_one, subtract_one); 160 + 161 + /* add_one() is now replaced. */ 162 + KUNIT_EXPECT_EQ(test, add_one(1), 0); 163 + 164 + /* Return add_one() to normal. */ 165 + kunit_deactivate_static_stub(test, add_one); 166 + KUNIT_EXPECT_EQ(test, add_one(1), 2); 167 + } 168 + 134 169 /* 135 170 * Here we make a list of all the test cases we want to add to the test suite 136 171 * below. ··· 182 145 KUNIT_CASE(example_skip_test), 183 146 KUNIT_CASE(example_mark_skipped_test), 184 147 KUNIT_CASE(example_all_expect_macros_test), 148 + KUNIT_CASE(example_static_stub_test), 185 149 {} 186 150 }; 187 151
+123
lib/kunit/static_stub.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * KUnit function redirection (static stubbing) API. 4 + * 5 + * Copyright (C) 2022, Google LLC. 6 + * Author: David Gow <davidgow@google.com> 7 + */ 8 + 9 + #include <kunit/test.h> 10 + #include <kunit/static_stub.h> 11 + #include "hooks-impl.h" 12 + 13 + 14 + /* Context for a static stub. This is stored in the resource data. */ 15 + struct kunit_static_stub_ctx { 16 + void *real_fn_addr; 17 + void *replacement_addr; 18 + }; 19 + 20 + static void __kunit_static_stub_resource_free(struct kunit_resource *res) 21 + { 22 + kfree(res->data); 23 + } 24 + 25 + /* Matching function for kunit_find_resource(). match_data is real_fn_addr. */ 26 + static bool __kunit_static_stub_resource_match(struct kunit *test, 27 + struct kunit_resource *res, 28 + void *match_real_fn_addr) 29 + { 30 + /* This pointer is only valid if res is a static stub resource. */ 31 + struct kunit_static_stub_ctx *ctx = res->data; 32 + 33 + /* Make sure the resource is a static stub resource. */ 34 + if (res->free != &__kunit_static_stub_resource_free) 35 + return false; 36 + 37 + return ctx->real_fn_addr == match_real_fn_addr; 38 + } 39 + 40 + /* Hook to return the address of the replacement function. */ 41 + void *__kunit_get_static_stub_address_impl(struct kunit *test, void *real_fn_addr) 42 + { 43 + struct kunit_resource *res; 44 + struct kunit_static_stub_ctx *ctx; 45 + void *replacement_addr; 46 + 47 + res = kunit_find_resource(test, 48 + __kunit_static_stub_resource_match, 49 + real_fn_addr); 50 + 51 + if (!res) 52 + return NULL; 53 + 54 + ctx = res->data; 55 + replacement_addr = ctx->replacement_addr; 56 + kunit_put_resource(res); 57 + return replacement_addr; 58 + } 59 + 60 + void kunit_deactivate_static_stub(struct kunit *test, void *real_fn_addr) 61 + { 62 + struct kunit_resource *res; 63 + 64 + KUNIT_ASSERT_PTR_NE_MSG(test, real_fn_addr, NULL, 65 + "Tried to deactivate a NULL stub."); 66 + 67 + /* Look up the existing stub for this function. */ 68 + res = kunit_find_resource(test, 69 + __kunit_static_stub_resource_match, 70 + real_fn_addr); 71 + 72 + /* Error out if the stub doesn't exist. */ 73 + KUNIT_ASSERT_PTR_NE_MSG(test, res, NULL, 74 + "Tried to deactivate a nonexistent stub."); 75 + 76 + /* Free the stub. We 'put' twice, as we got a reference 77 + * from kunit_find_resource() 78 + */ 79 + kunit_remove_resource(test, res); 80 + kunit_put_resource(res); 81 + } 82 + EXPORT_SYMBOL_GPL(kunit_deactivate_static_stub); 83 + 84 + /* Helper function for kunit_activate_static_stub(). The macro does 85 + * typechecking, so use it instead. 86 + */ 87 + void __kunit_activate_static_stub(struct kunit *test, 88 + void *real_fn_addr, 89 + void *replacement_addr) 90 + { 91 + struct kunit_static_stub_ctx *ctx; 92 + struct kunit_resource *res; 93 + 94 + KUNIT_ASSERT_PTR_NE_MSG(test, real_fn_addr, NULL, 95 + "Tried to activate a stub for function NULL"); 96 + 97 + /* If the replacement address is NULL, deactivate the stub. */ 98 + if (!replacement_addr) { 99 + kunit_deactivate_static_stub(test, replacement_addr); 100 + return; 101 + } 102 + 103 + /* Look up any existing stubs for this function, and replace them. */ 104 + res = kunit_find_resource(test, 105 + __kunit_static_stub_resource_match, 106 + real_fn_addr); 107 + if (res) { 108 + ctx = res->data; 109 + ctx->replacement_addr = replacement_addr; 110 + 111 + /* We got an extra reference from find_resource(), so put it. */ 112 + kunit_put_resource(res); 113 + } else { 114 + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); 115 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 116 + ctx->real_fn_addr = real_fn_addr; 117 + ctx->replacement_addr = replacement_addr; 118 + res = kunit_alloc_resource(test, NULL, 119 + &__kunit_static_stub_resource_free, 120 + GFP_KERNEL, ctx); 121 + } 122 + } 123 + EXPORT_SYMBOL_GPL(__kunit_activate_static_stub);
+7 -8
lib/kunit/test.c
··· 17 17 #include <linux/sched.h> 18 18 19 19 #include "debugfs.h" 20 + #include "hooks-impl.h" 20 21 #include "string-stream.h" 21 22 #include "try-catch-impl.h" 22 23 23 - DEFINE_STATIC_KEY_FALSE(kunit_running); 24 - EXPORT_SYMBOL_GPL(kunit_running); 25 - 26 - #if IS_BUILTIN(CONFIG_KUNIT) 27 24 /* 28 - * Fail the current test and print an error message to the log. 25 + * Hook to fail the current test and print an error message to the log. 29 26 */ 30 - void __kunit_fail_current_test(const char *file, int line, const char *fmt, ...) 27 + void __printf(3, 4) __kunit_fail_current_test_impl(const char *file, int line, const char *fmt, ...) 31 28 { 32 29 va_list args; 33 30 int len; ··· 51 54 kunit_err(current->kunit_test, "%s:%d: %s", file, line, buffer); 52 55 kunit_kfree(current->kunit_test, buffer); 53 56 } 54 - EXPORT_SYMBOL_GPL(__kunit_fail_current_test); 55 - #endif 56 57 57 58 /* 58 59 * Enable KUnit tests to run. ··· 773 778 774 779 static int __init kunit_init(void) 775 780 { 781 + /* Install the KUnit hook functions. */ 782 + kunit_install_hooks(); 783 + 776 784 kunit_debugfs_init(); 777 785 #ifdef CONFIG_MODULES 778 786 return register_module_notifier(&kunit_mod_nb); ··· 787 789 788 790 static void __exit kunit_exit(void) 789 791 { 792 + memset(&kunit_hooks, 0, sizeof(kunit_hooks)); 790 793 #ifdef CONFIG_MODULES 791 794 unregister_module_notifier(&kunit_mod_nb); 792 795 #endif
+100 -84
tools/testing/kunit/kunit.py
··· 77 77 config_start = time.time() 78 78 success = linux.build_reconfig(request.build_dir, request.make_options) 79 79 config_end = time.time() 80 - if not success: 81 - return KunitResult(KunitStatus.CONFIG_FAILURE, 82 - config_end - config_start) 83 - return KunitResult(KunitStatus.SUCCESS, 84 - config_end - config_start) 80 + status = KunitStatus.SUCCESS if success else KunitStatus.CONFIG_FAILURE 81 + return KunitResult(status, config_end - config_start) 85 82 86 83 def build_tests(linux: kunit_kernel.LinuxSourceTree, 87 84 request: KunitBuildRequest) -> KunitResult: ··· 89 92 request.build_dir, 90 93 request.make_options) 91 94 build_end = time.time() 92 - if not success: 93 - return KunitResult(KunitStatus.BUILD_FAILURE, 94 - build_end - build_start) 95 - if not success: 96 - return KunitResult(KunitStatus.BUILD_FAILURE, 97 - build_end - build_start) 98 - return KunitResult(KunitStatus.SUCCESS, 99 - build_end - build_start) 95 + status = KunitStatus.SUCCESS if success else KunitStatus.BUILD_FAILURE 96 + return KunitResult(status, build_end - build_start) 100 97 101 98 def config_and_build_tests(linux: kunit_kernel.LinuxSourceTree, 102 99 request: KunitBuildRequest) -> KunitResult: ··· 136 145 tests = _list_tests(linux, request) 137 146 if request.run_isolated == 'test': 138 147 filter_globs = tests 139 - if request.run_isolated == 'suite': 148 + elif request.run_isolated == 'suite': 140 149 filter_globs = _suites_from_test_list(tests) 141 150 # Apply the test-part of the user's glob, if present. 142 151 if '.' in request.filter_glob: ··· 386 395 extra_qemu_args=qemu_args) 387 396 388 397 398 + def run_handler(cli_args): 399 + if not os.path.exists(cli_args.build_dir): 400 + os.mkdir(cli_args.build_dir) 401 + 402 + linux = tree_from_args(cli_args) 403 + request = KunitRequest(build_dir=cli_args.build_dir, 404 + make_options=cli_args.make_options, 405 + jobs=cli_args.jobs, 406 + raw_output=cli_args.raw_output, 407 + json=cli_args.json, 408 + timeout=cli_args.timeout, 409 + filter_glob=cli_args.filter_glob, 410 + kernel_args=cli_args.kernel_args, 411 + run_isolated=cli_args.run_isolated) 412 + result = run_tests(linux, request) 413 + if result.status != KunitStatus.SUCCESS: 414 + sys.exit(1) 415 + 416 + 417 + def config_handler(cli_args): 418 + if cli_args.build_dir and ( 419 + not os.path.exists(cli_args.build_dir)): 420 + os.mkdir(cli_args.build_dir) 421 + 422 + linux = tree_from_args(cli_args) 423 + request = KunitConfigRequest(build_dir=cli_args.build_dir, 424 + make_options=cli_args.make_options) 425 + result = config_tests(linux, request) 426 + stdout.print_with_timestamp(( 427 + 'Elapsed time: %.3fs\n') % ( 428 + result.elapsed_time)) 429 + if result.status != KunitStatus.SUCCESS: 430 + sys.exit(1) 431 + 432 + 433 + def build_handler(cli_args): 434 + linux = tree_from_args(cli_args) 435 + request = KunitBuildRequest(build_dir=cli_args.build_dir, 436 + make_options=cli_args.make_options, 437 + jobs=cli_args.jobs) 438 + result = config_and_build_tests(linux, request) 439 + stdout.print_with_timestamp(( 440 + 'Elapsed time: %.3fs\n') % ( 441 + result.elapsed_time)) 442 + if result.status != KunitStatus.SUCCESS: 443 + sys.exit(1) 444 + 445 + 446 + def exec_handler(cli_args): 447 + linux = tree_from_args(cli_args) 448 + exec_request = KunitExecRequest(raw_output=cli_args.raw_output, 449 + build_dir=cli_args.build_dir, 450 + json=cli_args.json, 451 + timeout=cli_args.timeout, 452 + filter_glob=cli_args.filter_glob, 453 + kernel_args=cli_args.kernel_args, 454 + run_isolated=cli_args.run_isolated) 455 + result = exec_tests(linux, exec_request) 456 + stdout.print_with_timestamp(( 457 + 'Elapsed time: %.3fs\n') % (result.elapsed_time)) 458 + if result.status != KunitStatus.SUCCESS: 459 + sys.exit(1) 460 + 461 + 462 + def parse_handler(cli_args): 463 + if cli_args.file is None: 464 + sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error 465 + kunit_output = sys.stdin 466 + else: 467 + with open(cli_args.file, 'r', errors='backslashreplace') as f: 468 + kunit_output = f.read().splitlines() 469 + # We know nothing about how the result was created! 470 + metadata = kunit_json.Metadata() 471 + request = KunitParseRequest(raw_output=cli_args.raw_output, 472 + json=cli_args.json) 473 + result, _ = parse_tests(request, metadata, kunit_output) 474 + if result.status != KunitStatus.SUCCESS: 475 + sys.exit(1) 476 + 477 + 478 + subcommand_handlers_map = { 479 + 'run': run_handler, 480 + 'config': config_handler, 481 + 'build': build_handler, 482 + 'exec': exec_handler, 483 + 'parse': parse_handler 484 + } 485 + 486 + 389 487 def main(argv): 390 488 parser = argparse.ArgumentParser( 391 489 description='Helps writing and running KUnit tests.') ··· 518 438 if get_kernel_root_path(): 519 439 os.chdir(get_kernel_root_path()) 520 440 521 - if cli_args.subcommand == 'run': 522 - if not os.path.exists(cli_args.build_dir): 523 - os.mkdir(cli_args.build_dir) 441 + subcomand_handler = subcommand_handlers_map.get(cli_args.subcommand, None) 524 442 525 - linux = tree_from_args(cli_args) 526 - request = KunitRequest(build_dir=cli_args.build_dir, 527 - make_options=cli_args.make_options, 528 - jobs=cli_args.jobs, 529 - raw_output=cli_args.raw_output, 530 - json=cli_args.json, 531 - timeout=cli_args.timeout, 532 - filter_glob=cli_args.filter_glob, 533 - kernel_args=cli_args.kernel_args, 534 - run_isolated=cli_args.run_isolated) 535 - result = run_tests(linux, request) 536 - if result.status != KunitStatus.SUCCESS: 537 - sys.exit(1) 538 - elif cli_args.subcommand == 'config': 539 - if cli_args.build_dir and ( 540 - not os.path.exists(cli_args.build_dir)): 541 - os.mkdir(cli_args.build_dir) 542 - 543 - linux = tree_from_args(cli_args) 544 - request = KunitConfigRequest(build_dir=cli_args.build_dir, 545 - make_options=cli_args.make_options) 546 - result = config_tests(linux, request) 547 - stdout.print_with_timestamp(( 548 - 'Elapsed time: %.3fs\n') % ( 549 - result.elapsed_time)) 550 - if result.status != KunitStatus.SUCCESS: 551 - sys.exit(1) 552 - elif cli_args.subcommand == 'build': 553 - linux = tree_from_args(cli_args) 554 - request = KunitBuildRequest(build_dir=cli_args.build_dir, 555 - make_options=cli_args.make_options, 556 - jobs=cli_args.jobs) 557 - result = config_and_build_tests(linux, request) 558 - stdout.print_with_timestamp(( 559 - 'Elapsed time: %.3fs\n') % ( 560 - result.elapsed_time)) 561 - if result.status != KunitStatus.SUCCESS: 562 - sys.exit(1) 563 - elif cli_args.subcommand == 'exec': 564 - linux = tree_from_args(cli_args) 565 - exec_request = KunitExecRequest(raw_output=cli_args.raw_output, 566 - build_dir=cli_args.build_dir, 567 - json=cli_args.json, 568 - timeout=cli_args.timeout, 569 - filter_glob=cli_args.filter_glob, 570 - kernel_args=cli_args.kernel_args, 571 - run_isolated=cli_args.run_isolated) 572 - result = exec_tests(linux, exec_request) 573 - stdout.print_with_timestamp(( 574 - 'Elapsed time: %.3fs\n') % (result.elapsed_time)) 575 - if result.status != KunitStatus.SUCCESS: 576 - sys.exit(1) 577 - elif cli_args.subcommand == 'parse': 578 - if cli_args.file is None: 579 - sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error 580 - kunit_output = sys.stdin 581 - else: 582 - with open(cli_args.file, 'r', errors='backslashreplace') as f: 583 - kunit_output = f.read().splitlines() 584 - # We know nothing about how the result was created! 585 - metadata = kunit_json.Metadata() 586 - request = KunitParseRequest(raw_output=cli_args.raw_output, 587 - json=cli_args.json) 588 - result, _ = parse_tests(request, metadata, kunit_output) 589 - if result.status != KunitStatus.SUCCESS: 590 - sys.exit(1) 591 - else: 443 + if subcomand_handler is None: 592 444 parser.print_help() 445 + return 446 + 447 + subcomand_handler(cli_args) 448 + 593 449 594 450 if __name__ == '__main__': 595 451 main(sys.argv[1:])