linux-kselftest-kunit-6.4-rc1

This KUnit update Linux 6.4-rc1 consists of:
 
 - several fixes to kunit tool
 - new klist structure test
 - support for m68k under QEMU
 - support for overriding the QEMU serial port
 - support for SH under QEMU
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmRFYFsACgkQCwJExA0N
 Qxw+PxAA1KHnHool3QbzZouFgLgTS2N/hxsOIoWKeUl6guUPX0XYu67FEIyt7p5k
 a1eFLjt+q4URW/heHKYdffP+Up6xhN5yVP8xJEcbn6GD13lz1clI9RAjObiPOehc
 KOV90PeAEfzosEGRIp97g4Gzu8NUMZqN7BsKBdzYJ4rEftlcjaILBVp4OfSuCyAi
 UbYBdRjK4eIOwGXuHVfhNqzH1HRSbzcoSRTywj5qW0Qhpe6KnZBRuZESXYBsxzGb
 G0nd4+OttjZyplI/xQYwaU0XGAI6roG5G4nAT5YGHLp5g8rTaHetTi+i3iK4iEru
 wEL0NgywkA0ujAge97RldOjtU97vvSFk7FwxdS9lxaMW/Ut2sN72I2ThI8dBvVRZ
 fcw8t8mmT1gUv3SCq+s1X13vz22IedXLOfvOY2o/fLk2zxOw5e8FirAz/aFeOf3K
 ++hK+IQvDmeMMv08bz0ORzdRQcjdwQNQ3klnfdrUVFN9yK+iAllOJ/nrXHLNIXu4
 c3ITlAMldcAf2W+LRWzvqqKyT4H8MCXL3L0bBc1M1reRu9nM89AZedO8MHCB0R9Q
 2ic0rOxIwZzPJuk0qPDxEVmN7Rpyx85I96YOwRemJTEfdkB/ZX+BfOU0KzinOVHC
 3qrHuIw/SyRTlUEDAr53gJ5WHbdjhKAmrd1/FuplyoOSX0w6VVA=
 =COQn
 -----END PGP SIGNATURE-----

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

Pull KUnit updates from Shuah Khan:

 - several fixes to kunit tool

 - new klist structure test

 - support for m68k under QEMU

 - support for overriding the QEMU serial port

 - support for SH under QEMU

* tag 'linux-kselftest-kunit-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  kunit: add tests for using current KUnit test field
  kunit: tool: Add support for SH under QEMU
  kunit: tool: Add support for overriding the QEMU serial port
  .gitignore: Unignore .kunitconfig
  list: test: Test the klist structure
  kunit: increase KUNIT_LOG_SIZE to 2048 bytes
  kunit: Use gfp in kunit_alloc_resource() kernel-doc
  kunit: tool: fix pre-existing `mypy --strict` errors and update run_checks.py
  kunit: tool: remove unused imports and variables
  kunit: tool: add subscripts for type annotations where appropriate
  kunit: fix bug of extra newline characters in debugfs logs
  kunit: fix bug in the order of lines in debugfs logs
  kunit: fix bug in debugfs logs of parameterized tests
  kunit: tool: Add support for m68k under QEMU
This commit is contained in:
Linus Torvalds 2023-04-24 12:31:32 -07:00
commit 1be89faab3
17 changed files with 491 additions and 72 deletions

1
.gitignore vendored
View File

@ -103,6 +103,7 @@ modules.order
!.get_maintainer.ignore !.get_maintainer.ignore
!.gitattributes !.gitattributes
!.gitignore !.gitignore
!.kunitconfig
!.mailmap !.mailmap
!.rustfmt.toml !.rustfmt.toml

View File

@ -72,7 +72,7 @@ typedef void (*kunit_resource_free_t)(struct kunit_resource *);
* params.gfp = gfp; * params.gfp = gfp;
* *
* return kunit_alloc_resource(test, kunit_kmalloc_init, * return kunit_alloc_resource(test, kunit_kmalloc_init,
* kunit_kmalloc_free, &params); * kunit_kmalloc_free, gfp, &params);
* } * }
* *
* Resources can also be named, with lookup/removal done on a name * Resources can also be named, with lookup/removal done on a name

View File

@ -34,7 +34,7 @@ DECLARE_STATIC_KEY_FALSE(kunit_running);
struct kunit; struct kunit;
/* Size of log associated with test. */ /* Size of log associated with test. */
#define KUNIT_LOG_SIZE 512 #define KUNIT_LOG_SIZE 2048
/* Maximum size of parameter description string. */ /* Maximum size of parameter description string. */
#define KUNIT_PARAM_DESC_SIZE 128 #define KUNIT_PARAM_DESC_SIZE 128
@ -420,7 +420,7 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
#define kunit_log(lvl, test_or_suite, fmt, ...) \ #define kunit_log(lvl, test_or_suite, fmt, ...) \
do { \ do { \
printk(lvl fmt, ##__VA_ARGS__); \ printk(lvl fmt, ##__VA_ARGS__); \
kunit_log_append((test_or_suite)->log, fmt "\n", \ kunit_log_append((test_or_suite)->log, fmt, \
##__VA_ARGS__); \ ##__VA_ARGS__); \
} while (0) } while (0)

View File

@ -55,14 +55,24 @@ static int debugfs_print_results(struct seq_file *seq, void *v)
enum kunit_status success = kunit_suite_has_succeeded(suite); enum kunit_status success = kunit_suite_has_succeeded(suite);
struct kunit_case *test_case; struct kunit_case *test_case;
if (!suite || !suite->log) if (!suite)
return 0; return 0;
seq_printf(seq, "%s", suite->log); /* Print KTAP header so the debugfs log can be parsed as valid KTAP. */
seq_puts(seq, "KTAP version 1\n");
seq_puts(seq, "1..1\n");
/* Print suite header because it is not stored in the test logs. */
seq_puts(seq, KUNIT_SUBTEST_INDENT "KTAP version 1\n");
seq_printf(seq, KUNIT_SUBTEST_INDENT "# Subtest: %s\n", suite->name);
seq_printf(seq, KUNIT_SUBTEST_INDENT "1..%zd\n", kunit_suite_num_test_cases(suite));
kunit_suite_for_each_test_case(suite, test_case) kunit_suite_for_each_test_case(suite, test_case)
debugfs_print_result(seq, suite, test_case); debugfs_print_result(seq, suite, test_case);
if (suite->log)
seq_printf(seq, "%s", suite->log);
seq_printf(seq, "%s %d %s\n", seq_printf(seq, "%s %d %s\n",
kunit_status_to_ok_not_ok(success), 1, suite->name); kunit_status_to_ok_not_ok(success), 1, suite->name);
return 0; return 0;

View File

@ -6,6 +6,7 @@
* Author: Brendan Higgins <brendanhiggins@google.com> * Author: Brendan Higgins <brendanhiggins@google.com>
*/ */
#include <kunit/test.h> #include <kunit/test.h>
#include <kunit/test-bug.h>
#include "try-catch-impl.h" #include "try-catch-impl.h"
@ -443,18 +444,6 @@ static struct kunit_suite kunit_resource_test_suite = {
.test_cases = kunit_resource_test_cases, .test_cases = kunit_resource_test_cases,
}; };
static void kunit_log_test(struct kunit *test);
static struct kunit_case kunit_log_test_cases[] = {
KUNIT_CASE(kunit_log_test),
{}
};
static struct kunit_suite kunit_log_test_suite = {
.name = "kunit-log-test",
.test_cases = kunit_log_test_cases,
};
static void kunit_log_test(struct kunit *test) static void kunit_log_test(struct kunit *test)
{ {
struct kunit_suite suite; struct kunit_suite suite;
@ -481,6 +470,29 @@ static void kunit_log_test(struct kunit *test)
#endif #endif
} }
static void kunit_log_newline_test(struct kunit *test)
{
kunit_info(test, "Add newline\n");
if (test->log) {
KUNIT_ASSERT_NOT_NULL_MSG(test, strstr(test->log, "Add newline\n"),
"Missing log line, full log:\n%s", test->log);
KUNIT_EXPECT_NULL(test, strstr(test->log, "Add newline\n\n"));
} else {
kunit_skip(test, "only useful when debugfs is enabled");
}
}
static struct kunit_case kunit_log_test_cases[] = {
KUNIT_CASE(kunit_log_test),
KUNIT_CASE(kunit_log_newline_test),
{}
};
static struct kunit_suite kunit_log_test_suite = {
.name = "kunit-log-test",
.test_cases = kunit_log_test_cases,
};
static void kunit_status_set_failure_test(struct kunit *test) static void kunit_status_set_failure_test(struct kunit *test)
{ {
struct kunit fake; struct kunit fake;
@ -521,7 +533,46 @@ static struct kunit_suite kunit_status_test_suite = {
.test_cases = kunit_status_test_cases, .test_cases = kunit_status_test_cases,
}; };
static void kunit_current_test(struct kunit *test)
{
/* Check results of both current->kunit_test and
* kunit_get_current_test() are equivalent to current test.
*/
KUNIT_EXPECT_PTR_EQ(test, test, current->kunit_test);
KUNIT_EXPECT_PTR_EQ(test, test, kunit_get_current_test());
}
static void kunit_current_fail_test(struct kunit *test)
{
struct kunit fake;
kunit_init_test(&fake, "fake test", NULL);
KUNIT_EXPECT_EQ(test, fake.status, KUNIT_SUCCESS);
/* Set current->kunit_test to fake test. */
current->kunit_test = &fake;
kunit_fail_current_test("This should make `fake` test fail.");
KUNIT_EXPECT_EQ(test, fake.status, (enum kunit_status)KUNIT_FAILURE);
kunit_cleanup(&fake);
/* Reset current->kunit_test to current test. */
current->kunit_test = test;
}
static struct kunit_case kunit_current_test_cases[] = {
KUNIT_CASE(kunit_current_test),
KUNIT_CASE(kunit_current_fail_test),
{}
};
static struct kunit_suite kunit_current_test_suite = {
.name = "kunit_current",
.test_cases = kunit_current_test_cases,
};
kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite, kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite,
&kunit_log_test_suite, &kunit_status_test_suite); &kunit_log_test_suite, &kunit_status_test_suite,
&kunit_current_test_suite);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -108,28 +108,51 @@ static void kunit_print_test_stats(struct kunit *test,
stats.total); stats.total);
} }
/**
* kunit_log_newline() - Add newline to the end of log if one is not
* already present.
* @log: The log to add the newline to.
*/
static void kunit_log_newline(char *log)
{
int log_len, len_left;
log_len = strlen(log);
len_left = KUNIT_LOG_SIZE - log_len - 1;
if (log_len > 0 && log[log_len - 1] != '\n')
strncat(log, "\n", len_left);
}
/* /*
* 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).
*/ */
void kunit_log_append(char *log, const char *fmt, ...) void kunit_log_append(char *log, const char *fmt, ...)
{ {
char line[KUNIT_LOG_SIZE];
va_list args; va_list args;
int len_left; int len, log_len, len_left;
if (!log) if (!log)
return; return;
len_left = KUNIT_LOG_SIZE - strlen(log) - 1; log_len = strlen(log);
len_left = KUNIT_LOG_SIZE - log_len - 1;
if (len_left <= 0) if (len_left <= 0)
return; return;
/* Evaluate length of line to add to log */
va_start(args, fmt); va_start(args, fmt);
vsnprintf(line, sizeof(line), fmt, args); len = vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args); va_end(args);
strncat(log, line, len_left); /* Print formatted line to the log */
va_start(args, fmt);
vsnprintf(log + log_len, min(len, len_left), fmt, args);
va_end(args);
/* Add newline to end of log if not already present. */
kunit_log_newline(log);
} }
EXPORT_SYMBOL_GPL(kunit_log_append); EXPORT_SYMBOL_GPL(kunit_log_append);
@ -147,10 +170,18 @@ EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
static void kunit_print_suite_start(struct kunit_suite *suite) static void kunit_print_suite_start(struct kunit_suite *suite)
{ {
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "KTAP version 1\n"); /*
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "# Subtest: %s", * We do not log the test suite header as doing so would
* mean debugfs display would consist of the test suite
* header prior to individual test results.
* Hence directly printk the suite status, and we will
* separately seq_printf() the suite header for the debugfs
* representation.
*/
pr_info(KUNIT_SUBTEST_INDENT "KTAP version 1\n");
pr_info(KUNIT_SUBTEST_INDENT "# Subtest: %s\n",
suite->name); suite->name);
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "1..%zd", pr_info(KUNIT_SUBTEST_INDENT "1..%zd\n",
kunit_suite_num_test_cases(suite)); kunit_suite_num_test_cases(suite));
} }
@ -167,10 +198,9 @@ static void kunit_print_ok_not_ok(void *test_or_suite,
/* /*
* We do not log the test suite results as doing so would * We do not log the test suite results as doing so would
* mean debugfs display would consist of the test suite * mean debugfs display would consist of an incorrect test
* description and status prior to individual test results. * number. Hence directly printk the suite result, and we will
* Hence directly printk the suite status, and we will * separately seq_printf() the suite results for the debugfs
* separately seq_printf() the suite status for the debugfs
* representation. * representation.
*/ */
if (suite) if (suite)
@ -437,7 +467,6 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
struct kunit_try_catch_context context; struct kunit_try_catch_context context;
struct kunit_try_catch *try_catch; struct kunit_try_catch *try_catch;
kunit_init_test(test, test_case->name, test_case->log);
try_catch = &test->try_catch; try_catch = &test->try_catch;
kunit_try_catch_init(try_catch, kunit_try_catch_init(try_catch,
@ -533,6 +562,8 @@ int kunit_run_tests(struct kunit_suite *suite)
struct kunit_result_stats param_stats = { 0 }; struct kunit_result_stats param_stats = { 0 };
test_case->status = KUNIT_SKIPPED; test_case->status = KUNIT_SKIPPED;
kunit_init_test(&test, test_case->name, test_case->log);
if (!test_case->generate_params) { if (!test_case->generate_params) {
/* Non-parameterised test. */ /* Non-parameterised test. */
kunit_run_case_catch_errors(suite, test_case, &test); kunit_run_case_catch_errors(suite, test_case, &test);

View File

@ -8,6 +8,7 @@
#include <kunit/test.h> #include <kunit/test.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/klist.h>
struct list_test_struct { struct list_test_struct {
int data; int data;
@ -1199,6 +1200,303 @@ static struct kunit_suite hlist_test_module = {
.test_cases = hlist_test_cases, .test_cases = hlist_test_cases,
}; };
kunit_test_suites(&list_test_module, &hlist_test_module);
struct klist_test_struct {
int data;
struct klist klist;
struct klist_node klist_node;
};
static int node_count;
static struct klist_node *last_node;
static void check_node(struct klist_node *node_ptr)
{
node_count++;
last_node = node_ptr;
}
static void check_delete_node(struct klist_node *node_ptr)
{
node_count--;
last_node = node_ptr;
}
static void klist_test_add_tail(struct kunit *test)
{
struct klist_node a, b;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, NULL);
klist_add_tail(&a, &mylist);
KUNIT_EXPECT_EQ(test, node_count, 1);
KUNIT_EXPECT_PTR_EQ(test, last_node, &a);
klist_add_tail(&b, &mylist);
KUNIT_EXPECT_EQ(test, node_count, 2);
KUNIT_EXPECT_PTR_EQ(test, last_node, &b);
/* should be [list] -> a -> b */
klist_iter_init(&mylist, &i);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
KUNIT_EXPECT_NULL(test, klist_next(&i));
klist_iter_exit(&i);
}
static void klist_test_add_head(struct kunit *test)
{
struct klist_node a, b;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, NULL);
klist_add_head(&a, &mylist);
KUNIT_EXPECT_EQ(test, node_count, 1);
KUNIT_EXPECT_PTR_EQ(test, last_node, &a);
klist_add_head(&b, &mylist);
KUNIT_EXPECT_EQ(test, node_count, 2);
KUNIT_EXPECT_PTR_EQ(test, last_node, &b);
/* should be [list] -> b -> a */
klist_iter_init(&mylist, &i);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_NULL(test, klist_next(&i));
klist_iter_exit(&i);
}
static void klist_test_add_behind(struct kunit *test)
{
struct klist_node a, b, c, d;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, NULL);
klist_add_head(&a, &mylist);
klist_add_head(&b, &mylist);
klist_add_behind(&c, &a);
KUNIT_EXPECT_EQ(test, node_count, 3);
KUNIT_EXPECT_PTR_EQ(test, last_node, &c);
klist_add_behind(&d, &b);
KUNIT_EXPECT_EQ(test, node_count, 4);
KUNIT_EXPECT_PTR_EQ(test, last_node, &d);
klist_iter_init(&mylist, &i);
/* should be [list] -> b -> d -> a -> c*/
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &d);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &c);
KUNIT_EXPECT_NULL(test, klist_next(&i));
klist_iter_exit(&i);
}
static void klist_test_add_before(struct kunit *test)
{
struct klist_node a, b, c, d;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, NULL);
klist_add_head(&a, &mylist);
klist_add_head(&b, &mylist);
klist_add_before(&c, &a);
KUNIT_EXPECT_EQ(test, node_count, 3);
KUNIT_EXPECT_PTR_EQ(test, last_node, &c);
klist_add_before(&d, &b);
KUNIT_EXPECT_EQ(test, node_count, 4);
KUNIT_EXPECT_PTR_EQ(test, last_node, &d);
klist_iter_init(&mylist, &i);
/* should be [list] -> b -> d -> a -> c*/
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &d);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &c);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_NULL(test, klist_next(&i));
klist_iter_exit(&i);
}
/*
* Verify that klist_del() delays the deletion of a node until there
* are no other references to it
*/
static void klist_test_del_refcount_greater_than_zero(struct kunit *test)
{
struct klist_node a, b, c, d;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, &check_delete_node);
/* Add nodes a,b,c,d to the list*/
klist_add_tail(&a, &mylist);
klist_add_tail(&b, &mylist);
klist_add_tail(&c, &mylist);
klist_add_tail(&d, &mylist);
klist_iter_init(&mylist, &i);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
/* Advance the iterator to point to node c*/
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &c);
/* Try to delete node c while there is a reference to it*/
klist_del(&c);
/*
* Verify that node c is still attached to the list even after being
* deleted. Since the iterator still points to c, the reference count is not
* decreased to 0
*/
KUNIT_EXPECT_TRUE(test, klist_node_attached(&c));
/* Check that node c has not been removed yet*/
KUNIT_EXPECT_EQ(test, node_count, 4);
KUNIT_EXPECT_PTR_EQ(test, last_node, &d);
klist_iter_exit(&i);
/*
* Since the iterator is no longer pointing to node c, node c is removed
* from the list
*/
KUNIT_EXPECT_EQ(test, node_count, 3);
KUNIT_EXPECT_PTR_EQ(test, last_node, &c);
}
/*
* Verify that klist_del() deletes a node immediately when there are no
* other references to it.
*/
static void klist_test_del_refcount_zero(struct kunit *test)
{
struct klist_node a, b, c, d;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, &check_delete_node);
/* Add nodes a,b,c,d to the list*/
klist_add_tail(&a, &mylist);
klist_add_tail(&b, &mylist);
klist_add_tail(&c, &mylist);
klist_add_tail(&d, &mylist);
/* Delete node c*/
klist_del(&c);
/* Check that node c is deleted from the list*/
KUNIT_EXPECT_EQ(test, node_count, 3);
KUNIT_EXPECT_PTR_EQ(test, last_node, &c);
/* Should be [list] -> a -> b -> d*/
klist_iter_init(&mylist, &i);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &d);
KUNIT_EXPECT_NULL(test, klist_next(&i));
klist_iter_exit(&i);
}
static void klist_test_remove(struct kunit *test)
{
/* This test doesn't check correctness under concurrent access */
struct klist_node a, b, c, d;
struct klist mylist;
struct klist_iter i;
node_count = 0;
klist_init(&mylist, &check_node, &check_delete_node);
/* Add nodes a,b,c,d to the list*/
klist_add_tail(&a, &mylist);
klist_add_tail(&b, &mylist);
klist_add_tail(&c, &mylist);
klist_add_tail(&d, &mylist);
/* Delete node c*/
klist_remove(&c);
/* Check the nodes in the list*/
KUNIT_EXPECT_EQ(test, node_count, 3);
KUNIT_EXPECT_PTR_EQ(test, last_node, &c);
/* should be [list] -> a -> b -> d*/
klist_iter_init(&mylist, &i);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &a);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &b);
KUNIT_EXPECT_PTR_EQ(test, klist_next(&i), &d);
KUNIT_EXPECT_NULL(test, klist_next(&i));
klist_iter_exit(&i);
}
static void klist_test_node_attached(struct kunit *test)
{
struct klist_node a = {};
struct klist mylist;
klist_init(&mylist, NULL, NULL);
KUNIT_EXPECT_FALSE(test, klist_node_attached(&a));
klist_add_head(&a, &mylist);
KUNIT_EXPECT_TRUE(test, klist_node_attached(&a));
klist_del(&a);
KUNIT_EXPECT_FALSE(test, klist_node_attached(&a));
}
static struct kunit_case klist_test_cases[] = {
KUNIT_CASE(klist_test_add_tail),
KUNIT_CASE(klist_test_add_head),
KUNIT_CASE(klist_test_add_behind),
KUNIT_CASE(klist_test_add_before),
KUNIT_CASE(klist_test_del_refcount_greater_than_zero),
KUNIT_CASE(klist_test_del_refcount_zero),
KUNIT_CASE(klist_test_remove),
KUNIT_CASE(klist_test_node_attached),
{},
};
static struct kunit_suite klist_test_module = {
.name = "klist",
.test_cases = klist_test_cases,
};
kunit_test_suites(&list_test_module, &hlist_test_module, &klist_test_module);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -123,7 +123,7 @@ def _suites_from_test_list(tests: List[str]) -> List[str]:
parts = t.split('.', maxsplit=2) parts = t.split('.', maxsplit=2)
if len(parts) != 2: if len(parts) != 2:
raise ValueError(f'internal KUnit error, test name should be of the form "<suite>.<test>", got "{t}"') raise ValueError(f'internal KUnit error, test name should be of the form "<suite>.<test>", got "{t}"')
suite, case = parts suite, _ = parts
if not suites or suites[-1] != suite: if not suites or suites[-1] != suite:
suites.append(suite) suites.append(suite)
return suites return suites
@ -269,7 +269,7 @@ def massage_argv(argv: Sequence[str]) -> Sequence[str]:
def get_default_jobs() -> int: def get_default_jobs() -> int:
return len(os.sched_getaffinity(0)) return len(os.sched_getaffinity(0))
def add_common_opts(parser) -> None: def add_common_opts(parser: argparse.ArgumentParser) -> None:
parser.add_argument('--build_dir', parser.add_argument('--build_dir',
help='As in the make command, it specifies the build ' help='As in the make command, it specifies the build '
'directory.', 'directory.',
@ -320,13 +320,13 @@ def add_common_opts(parser) -> None:
help='Additional QEMU arguments, e.g. "-smp 8"', help='Additional QEMU arguments, e.g. "-smp 8"',
action='append', metavar='') action='append', metavar='')
def add_build_opts(parser) -> None: def add_build_opts(parser: argparse.ArgumentParser) -> None:
parser.add_argument('--jobs', parser.add_argument('--jobs',
help='As in the make command, "Specifies the number of ' help='As in the make command, "Specifies the number of '
'jobs (commands) to run simultaneously."', 'jobs (commands) to run simultaneously."',
type=int, default=get_default_jobs(), metavar='N') type=int, default=get_default_jobs(), metavar='N')
def add_exec_opts(parser) -> None: def add_exec_opts(parser: argparse.ArgumentParser) -> None:
parser.add_argument('--timeout', parser.add_argument('--timeout',
help='maximum number of seconds to allow for all tests ' help='maximum number of seconds to allow for all tests '
'to run. This does not include time taken to build the ' 'to run. This does not include time taken to build the '
@ -351,7 +351,7 @@ def add_exec_opts(parser) -> None:
type=str, type=str,
choices=['suite', 'test']) choices=['suite', 'test'])
def add_parse_opts(parser) -> None: def add_parse_opts(parser: argparse.ArgumentParser) -> None:
parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. ' parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. '
'By default, filters to just KUnit output. Use ' 'By default, filters to just KUnit output. Use '
'--raw_output=all to show everything', '--raw_output=all to show everything',
@ -386,7 +386,7 @@ def tree_from_args(cli_args: argparse.Namespace) -> kunit_kernel.LinuxSourceTree
extra_qemu_args=qemu_args) extra_qemu_args=qemu_args)
def run_handler(cli_args): def run_handler(cli_args: argparse.Namespace) -> None:
if not os.path.exists(cli_args.build_dir): if not os.path.exists(cli_args.build_dir):
os.mkdir(cli_args.build_dir) os.mkdir(cli_args.build_dir)
@ -405,7 +405,7 @@ def run_handler(cli_args):
sys.exit(1) sys.exit(1)
def config_handler(cli_args): def config_handler(cli_args: argparse.Namespace) -> None:
if cli_args.build_dir and ( if cli_args.build_dir and (
not os.path.exists(cli_args.build_dir)): not os.path.exists(cli_args.build_dir)):
os.mkdir(cli_args.build_dir) os.mkdir(cli_args.build_dir)
@ -421,7 +421,7 @@ def config_handler(cli_args):
sys.exit(1) sys.exit(1)
def build_handler(cli_args): def build_handler(cli_args: argparse.Namespace) -> None:
linux = tree_from_args(cli_args) linux = tree_from_args(cli_args)
request = KunitBuildRequest(build_dir=cli_args.build_dir, request = KunitBuildRequest(build_dir=cli_args.build_dir,
make_options=cli_args.make_options, make_options=cli_args.make_options,
@ -434,7 +434,7 @@ def build_handler(cli_args):
sys.exit(1) sys.exit(1)
def exec_handler(cli_args): def exec_handler(cli_args: argparse.Namespace) -> None:
linux = tree_from_args(cli_args) linux = tree_from_args(cli_args)
exec_request = KunitExecRequest(raw_output=cli_args.raw_output, exec_request = KunitExecRequest(raw_output=cli_args.raw_output,
build_dir=cli_args.build_dir, build_dir=cli_args.build_dir,
@ -450,10 +450,10 @@ def exec_handler(cli_args):
sys.exit(1) sys.exit(1)
def parse_handler(cli_args): def parse_handler(cli_args: argparse.Namespace) -> None:
if cli_args.file is None: if cli_args.file is None:
sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error sys.stdin.reconfigure(errors='backslashreplace') # type: ignore
kunit_output = sys.stdin kunit_output = sys.stdin # type: Iterable[str]
else: else:
with open(cli_args.file, 'r', errors='backslashreplace') as f: with open(cli_args.file, 'r', errors='backslashreplace') as f:
kunit_output = f.read().splitlines() kunit_output = f.read().splitlines()
@ -475,7 +475,7 @@ subcommand_handlers_map = {
} }
def main(argv): def main(argv: Sequence[str]) -> None:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Helps writing and running KUnit tests.') description='Helps writing and running KUnit tests.')
subparser = parser.add_subparsers(dest='subcommand') subparser = parser.add_subparsers(dest='subcommand')

View File

@ -8,7 +8,7 @@
from dataclasses import dataclass from dataclasses import dataclass
import re import re
from typing import Dict, Iterable, List, Set, Tuple from typing import Any, Dict, Iterable, List, Tuple
CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$' CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$'
CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$' CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$'
@ -34,7 +34,7 @@ class Kconfig:
def __init__(self) -> None: def __init__(self) -> None:
self._entries = {} # type: Dict[str, str] self._entries = {} # type: Dict[str, str]
def __eq__(self, other) -> bool: def __eq__(self, other: Any) -> bool:
if not isinstance(other, self.__class__): if not isinstance(other, self.__class__):
return False return False
return self._entries == other._entries return self._entries == other._entries

View File

@ -16,9 +16,9 @@ import shutil
import signal import signal
import threading import threading
from typing import Iterator, List, Optional, Tuple from typing import Iterator, List, Optional, Tuple
from types import FrameType
import kunit_config import kunit_config
from kunit_printer import stdout
import qemu_config import qemu_config
KCONFIG_PATH = '.config' KCONFIG_PATH = '.config'
@ -57,7 +57,7 @@ class LinuxSourceTreeOperations:
def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig: def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig:
return base_kunitconfig return base_kunitconfig
def make_olddefconfig(self, build_dir: str, make_options) -> None: def make_olddefconfig(self, build_dir: str, make_options: Optional[List[str]]) -> None:
command = ['make', 'ARCH=' + self._linux_arch, 'O=' + build_dir, 'olddefconfig'] command = ['make', 'ARCH=' + self._linux_arch, 'O=' + build_dir, 'olddefconfig']
if self._cross_compile: if self._cross_compile:
command += ['CROSS_COMPILE=' + self._cross_compile] command += ['CROSS_COMPILE=' + self._cross_compile]
@ -71,7 +71,7 @@ class LinuxSourceTreeOperations:
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise ConfigError(e.output.decode()) raise ConfigError(e.output.decode())
def make(self, jobs, build_dir: str, make_options) -> None: def make(self, jobs: int, build_dir: str, make_options: Optional[List[str]]) -> None:
command = ['make', 'ARCH=' + self._linux_arch, 'O=' + build_dir, '--jobs=' + str(jobs)] command = ['make', 'ARCH=' + self._linux_arch, 'O=' + build_dir, '--jobs=' + str(jobs)]
if make_options: if make_options:
command.extend(make_options) command.extend(make_options)
@ -92,7 +92,7 @@ class LinuxSourceTreeOperations:
if stderr: # likely only due to build warnings if stderr: # likely only due to build warnings
print(stderr.decode()) print(stderr.decode())
def start(self, params: List[str], build_dir: str) -> subprocess.Popen: def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]:
raise RuntimeError('not implemented!') raise RuntimeError('not implemented!')
@ -106,13 +106,14 @@ class LinuxSourceTreeOperationsQemu(LinuxSourceTreeOperations):
self._kernel_path = qemu_arch_params.kernel_path self._kernel_path = qemu_arch_params.kernel_path
self._kernel_command_line = qemu_arch_params.kernel_command_line + ' kunit_shutdown=reboot' self._kernel_command_line = qemu_arch_params.kernel_command_line + ' kunit_shutdown=reboot'
self._extra_qemu_params = qemu_arch_params.extra_qemu_params self._extra_qemu_params = qemu_arch_params.extra_qemu_params
self._serial = qemu_arch_params.serial
def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig: def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig:
kconfig = kunit_config.parse_from_string(self._kconfig) kconfig = kunit_config.parse_from_string(self._kconfig)
kconfig.merge_in_entries(base_kunitconfig) kconfig.merge_in_entries(base_kunitconfig)
return kconfig return kconfig
def start(self, params: List[str], build_dir: str) -> subprocess.Popen: def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]:
kernel_path = os.path.join(build_dir, self._kernel_path) kernel_path = os.path.join(build_dir, self._kernel_path)
qemu_command = ['qemu-system-' + self._qemu_arch, qemu_command = ['qemu-system-' + self._qemu_arch,
'-nodefaults', '-nodefaults',
@ -121,7 +122,7 @@ class LinuxSourceTreeOperationsQemu(LinuxSourceTreeOperations):
'-append', ' '.join(params + [self._kernel_command_line]), '-append', ' '.join(params + [self._kernel_command_line]),
'-no-reboot', '-no-reboot',
'-nographic', '-nographic',
'-serial', 'stdio'] + self._extra_qemu_params '-serial', self._serial] + self._extra_qemu_params
# Note: shlex.join() does what we want, but requires python 3.8+. # Note: shlex.join() does what we want, but requires python 3.8+.
print('Running tests with:\n$', ' '.join(shlex.quote(arg) for arg in qemu_command)) print('Running tests with:\n$', ' '.join(shlex.quote(arg) for arg in qemu_command))
return subprocess.Popen(qemu_command, return subprocess.Popen(qemu_command,
@ -133,7 +134,7 @@ class LinuxSourceTreeOperationsQemu(LinuxSourceTreeOperations):
class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations): class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations):
"""An abstraction over command line operations performed on a source tree.""" """An abstraction over command line operations performed on a source tree."""
def __init__(self, cross_compile=None): def __init__(self, cross_compile: Optional[str]=None):
super().__init__(linux_arch='um', cross_compile=cross_compile) super().__init__(linux_arch='um', cross_compile=cross_compile)
def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig: def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig:
@ -141,7 +142,7 @@ class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations):
kconfig.merge_in_entries(base_kunitconfig) kconfig.merge_in_entries(base_kunitconfig)
return kconfig return kconfig
def start(self, params: List[str], build_dir: str) -> subprocess.Popen: def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]:
"""Runs the Linux UML binary. Must be named 'linux'.""" """Runs the Linux UML binary. Must be named 'linux'."""
linux_bin = os.path.join(build_dir, 'linux') linux_bin = os.path.join(build_dir, 'linux')
params.extend(['mem=1G', 'console=tty', 'kunit_shutdown=halt']) params.extend(['mem=1G', 'console=tty', 'kunit_shutdown=halt'])
@ -216,7 +217,7 @@ def _get_qemu_ops(config_path: str,
if not hasattr(config, 'QEMU_ARCH'): if not hasattr(config, 'QEMU_ARCH'):
raise ValueError('qemu_config module missing "QEMU_ARCH": ' + config_path) raise ValueError('qemu_config module missing "QEMU_ARCH": ' + config_path)
params: qemu_config.QemuArchParams = config.QEMU_ARCH # type: ignore params: qemu_config.QemuArchParams = config.QEMU_ARCH
if extra_qemu_args: if extra_qemu_args:
params.extra_qemu_params.extend(extra_qemu_args) params.extra_qemu_params.extend(extra_qemu_args)
return params.linux_arch, LinuxSourceTreeOperationsQemu( return params.linux_arch, LinuxSourceTreeOperationsQemu(
@ -230,10 +231,10 @@ class LinuxSourceTree:
build_dir: str, build_dir: str,
kunitconfig_paths: Optional[List[str]]=None, kunitconfig_paths: Optional[List[str]]=None,
kconfig_add: Optional[List[str]]=None, kconfig_add: Optional[List[str]]=None,
arch=None, arch: Optional[str]=None,
cross_compile=None, cross_compile: Optional[str]=None,
qemu_config_path=None, qemu_config_path: Optional[str]=None,
extra_qemu_args=None) -> None: extra_qemu_args: Optional[List[str]]=None) -> None:
signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGINT, self.signal_handler)
if qemu_config_path: if qemu_config_path:
self._arch, self._ops = _get_qemu_ops(qemu_config_path, extra_qemu_args, cross_compile) self._arch, self._ops = _get_qemu_ops(qemu_config_path, extra_qemu_args, cross_compile)
@ -276,7 +277,7 @@ class LinuxSourceTree:
logging.error(message) logging.error(message)
return False return False
def build_config(self, build_dir: str, make_options) -> bool: def build_config(self, build_dir: str, make_options: Optional[List[str]]) -> bool:
kconfig_path = get_kconfig_path(build_dir) kconfig_path = get_kconfig_path(build_dir)
if build_dir and not os.path.exists(build_dir): if build_dir and not os.path.exists(build_dir):
os.mkdir(build_dir) os.mkdir(build_dir)
@ -304,7 +305,7 @@ class LinuxSourceTree:
old_kconfig = kunit_config.parse_file(old_path) old_kconfig = kunit_config.parse_file(old_path)
return old_kconfig != self._kconfig return old_kconfig != self._kconfig
def build_reconfig(self, build_dir: str, make_options) -> bool: def build_reconfig(self, build_dir: str, make_options: Optional[List[str]]) -> bool:
"""Creates a new .config if it is not a subset of the .kunitconfig.""" """Creates a new .config if it is not a subset of the .kunitconfig."""
kconfig_path = get_kconfig_path(build_dir) kconfig_path = get_kconfig_path(build_dir)
if not os.path.exists(kconfig_path): if not os.path.exists(kconfig_path):
@ -320,7 +321,7 @@ class LinuxSourceTree:
os.remove(kconfig_path) os.remove(kconfig_path)
return self.build_config(build_dir, make_options) return self.build_config(build_dir, make_options)
def build_kernel(self, jobs, build_dir: str, make_options) -> bool: def build_kernel(self, jobs: int, build_dir: str, make_options: Optional[List[str]]) -> bool:
try: try:
self._ops.make_olddefconfig(build_dir, make_options) self._ops.make_olddefconfig(build_dir, make_options)
self._ops.make(jobs, build_dir, make_options) self._ops.make(jobs, build_dir, make_options)
@ -329,7 +330,7 @@ class LinuxSourceTree:
return False return False
return self.validate_config(build_dir) return self.validate_config(build_dir)
def run_kernel(self, args=None, build_dir='', filter_glob='', timeout=None) -> Iterator[str]: def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', timeout: Optional[int]=None) -> Iterator[str]:
if not args: if not args:
args = [] args = []
if filter_glob: if filter_glob:
@ -340,7 +341,7 @@ class LinuxSourceTree:
assert process.stdout is not None # tell mypy it's set assert process.stdout is not None # tell mypy it's set
# Enforce the timeout in a background thread. # Enforce the timeout in a background thread.
def _wait_proc(): def _wait_proc() -> None:
try: try:
process.wait(timeout=timeout) process.wait(timeout=timeout)
except Exception as e: except Exception as e:
@ -366,6 +367,6 @@ class LinuxSourceTree:
waiter.join() waiter.join()
subprocess.call(['stty', 'sane']) subprocess.call(['stty', 'sane'])
def signal_handler(self, unused_sig, unused_frame) -> None: def signal_handler(self, unused_sig: int, unused_frame: Optional[FrameType]) -> None:
logging.error('Build interruption occurred. Cleaning console.') logging.error('Build interruption occurred. Cleaning console.')
subprocess.call(['stty', 'sane']) subprocess.call(['stty', 'sane'])

View File

@ -12,7 +12,6 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
import re import re
import sys
import textwrap import textwrap
from enum import Enum, auto from enum import Enum, auto

View File

@ -15,7 +15,7 @@ _RESET = '\033[0;0m'
class Printer: class Printer:
"""Wraps a file object, providing utilities for coloring output, etc.""" """Wraps a file object, providing utilities for coloring output, etc."""
def __init__(self, output: typing.IO): def __init__(self, output: typing.IO[str]):
self._output = output self._output = output
self._use_color = output.isatty() self._use_color = output.isatty()

View File

@ -328,7 +328,7 @@ class KUnitParserTest(unittest.TestCase):
def test_parse_subtest_header(self): def test_parse_subtest_header(self):
ktap_log = test_data_path('test_parse_subtest_header.log') ktap_log = test_data_path('test_parse_subtest_header.log')
with open(ktap_log) as file: with open(ktap_log) as file:
result = kunit_parser.parse_run_tests(file.readlines()) kunit_parser.parse_run_tests(file.readlines())
self.print_mock.assert_any_call(StrContains('suite (1 subtest)')) self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))
def test_show_test_output_on_failure(self): def test_show_test_output_on_failure(self):

View File

@ -17,3 +17,4 @@ class QemuArchParams:
kernel_path: str kernel_path: str
kernel_command_line: str kernel_command_line: str
extra_qemu_params: List[str] extra_qemu_params: List[str]
serial: str = 'stdio'

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
from ..qemu_config import QemuArchParams
QEMU_ARCH = QemuArchParams(linux_arch='m68k',
kconfig='''
CONFIG_VIRT=y''',
qemu_arch='m68k',
kernel_path='vmlinux',
kernel_command_line='console=hvc0',
extra_qemu_params=['-machine', 'virt'])

View File

@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only
from ..qemu_config import QemuArchParams
QEMU_ARCH = QemuArchParams(linux_arch='sh',
kconfig='''
CONFIG_CPU_SUBTYPE_SH7751R=y
CONFIG_MEMORY_START=0x0c000000
CONFIG_SH_RTS7751R2D=y
CONFIG_RTS7751R2D_PLUS=y
CONFIG_SERIAL_SH_SCI=y''',
qemu_arch='sh4',
kernel_path='arch/sh/boot/zImage',
kernel_command_line='console=ttySC1',
serial='null',
extra_qemu_params=[
'-machine', 'r2d',
'-serial', 'mon:stdio'])

View File

@ -23,7 +23,7 @@ commands: Dict[str, Sequence[str]] = {
'kunit_tool_test.py': ['./kunit_tool_test.py'], 'kunit_tool_test.py': ['./kunit_tool_test.py'],
'kunit smoke test': ['./kunit.py', 'run', '--kunitconfig=lib/kunit', '--build_dir=kunit_run_checks'], 'kunit smoke test': ['./kunit.py', 'run', '--kunitconfig=lib/kunit', '--build_dir=kunit_run_checks'],
'pytype': ['/bin/sh', '-c', 'pytype *.py'], 'pytype': ['/bin/sh', '-c', 'pytype *.py'],
'mypy': ['/bin/sh', '-c', 'mypy *.py'], 'mypy': ['mypy', '--strict', '--exclude', '_test.py$', '--exclude', 'qemu_configs/', '.'],
} }
# The user might not have mypy or pytype installed, skip them if so. # The user might not have mypy or pytype installed, skip them if so.
@ -37,7 +37,7 @@ def main(argv: Sequence[str]) -> None:
if argv: if argv:
raise RuntimeError('This script takes no arguments') raise RuntimeError('This script takes no arguments')
future_to_name: Dict[futures.Future, str] = {} future_to_name: Dict[futures.Future[None], str] = {}
executor = futures.ThreadPoolExecutor(max_workers=len(commands)) executor = futures.ThreadPoolExecutor(max_workers=len(commands))
for name, argv in commands.items(): for name, argv in commands.items():
if name in necessary_deps and shutil.which(necessary_deps[name]) is None: if name in necessary_deps and shutil.which(necessary_deps[name]) is None:
@ -73,7 +73,7 @@ def main(argv: Sequence[str]) -> None:
sys.exit(1) sys.exit(1)
def run_cmd(argv: Sequence[str]): def run_cmd(argv: Sequence[str]) -> None:
subprocess.check_output(argv, stderr=subprocess.STDOUT, cwd=ABS_TOOL_PATH, timeout=TIMEOUT) subprocess.check_output(argv, stderr=subprocess.STDOUT, cwd=ABS_TOOL_PATH, timeout=TIMEOUT)