961 lines · 23.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Base unit test (KUnit) API. * * Copyright (C) 2019, Google LLC. * Author: Brendan Higgins <brendanhiggins@google.com> */ #include <kunit/resource.h>#include <kunit/test.h>#include <kunit/test-bug.h>#include <kunit/attributes.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/mutex.h>#include <linux/panic.h>#include <linux/sched/debug.h>#include <linux/sched.h>#include <linux/mm.h> #include "debugfs.h"#include "device-impl.h"#include "hooks-impl.h"#include "string-stream.h"#include "try-catch-impl.h" static DEFINE_MUTEX(kunit_run_lock); /* * Hook to fail the current test and print an error message to the log. */void __printf(3, 4) __kunit_fail_current_test_impl(const char *file, int line, const char *fmt, ...){	va_list args;	int len;	char *buffer; 	if (!current->kunit_test)		return; 	kunit_set_failure(current->kunit_test); 	/* kunit_err() only accepts literals, so evaluate the args first. */	va_start(args, fmt);	len = vsnprintf(NULL, 0, fmt, args) + 1;	va_end(args); 	buffer = kunit_kmalloc(current->kunit_test, len, GFP_KERNEL);	if (!buffer)		return; 	va_start(args, fmt);	vsnprintf(buffer, len, fmt, args);	va_end(args); 	kunit_err(current->kunit_test, "%s:%d: %s", file, line, buffer);	kunit_kfree(current->kunit_test, buffer);} /* * Enable KUnit tests to run. */#ifdef CONFIG_KUNIT_DEFAULT_ENABLEDstatic bool enable_param = true;#elsestatic bool enable_param;#endifmodule_param_named(enable, enable_param, bool, 0);MODULE_PARM_DESC(enable, "Enable KUnit tests"); /* * KUnit statistic mode: * 0 - disabled * 1 - only when there is more than one subtest * 2 - enabled */static int kunit_stats_enabled = 1;module_param_named(stats_enabled, kunit_stats_enabled, int, 0644);MODULE_PARM_DESC(stats_enabled,		  "Print test stats: never (0), only for multiple subtests (1), or always (2)"); struct kunit_result_stats {	unsigned long passed;	unsigned long skipped;	unsigned long failed;	unsigned long total;}; static bool kunit_should_print_stats(struct kunit_result_stats stats){	if (kunit_stats_enabled == 0)		return false; 	if (kunit_stats_enabled == 2)		return true; 	return (stats.total > 1);} static void kunit_print_test_stats(struct kunit *test,				   struct kunit_result_stats stats){	if (!kunit_should_print_stats(stats))		return; 	kunit_log(KERN_INFO, test,		  KUNIT_SUBTEST_INDENT		  "# %s: pass:%lu fail:%lu skip:%lu total:%lu",		  test->name,		  stats.passed,		  stats.failed,		  stats.skipped,		  stats.total);} /* Append formatted message to log. */void kunit_log_append(struct string_stream *log, const char *fmt, ...){	va_list args; 	if (!log)		return; 	va_start(args, fmt);	string_stream_vadd(log, fmt, args);	va_end(args);}EXPORT_SYMBOL_GPL(kunit_log_append); size_t kunit_suite_num_test_cases(struct kunit_suite *suite){	struct kunit_case *test_case;	size_t len = 0; 	kunit_suite_for_each_test_case(suite, test_case)		len++; 	return len;}EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases); /* Currently supported test levels */enum {	KUNIT_LEVEL_SUITE = 0,	KUNIT_LEVEL_CASE,	KUNIT_LEVEL_CASE_PARAM,}; static void kunit_print_suite_start(struct kunit_suite *suite){	/*	 * 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);	kunit_print_attr((void *)suite, false, KUNIT_LEVEL_CASE);	pr_info(KUNIT_SUBTEST_INDENT "1..%zd\n",		  kunit_suite_num_test_cases(suite));} static void kunit_print_ok_not_ok(struct kunit *test,				  unsigned int test_level,				  enum kunit_status status,				  size_t test_number,				  const char *description,				  const char *directive){	const char *directive_header = (status == KUNIT_SKIPPED) ? " # SKIP " : "";	const char *directive_body = (status == KUNIT_SKIPPED) ? directive : ""; 	/*	 * When test is NULL assume that results are from the suite	 * and today suite results are expected at level 0 only.	 */	WARN(!test && test_level, "suite test level can't be %u!\n", test_level); 	/*	 * We do not log the test suite results as doing so would	 * mean debugfs display would consist of an incorrect test	 * number. Hence directly printk the suite result, and we will	 * separately seq_printf() the suite results for the debugfs	 * representation.	 */	if (!test)		pr_info("%s %zd %s%s%s\n",			kunit_status_to_ok_not_ok(status),			test_number, description, directive_header,			directive_body);	else		kunit_log(KERN_INFO, test,			  "%*s%s %zd %s%s%s",			  KUNIT_INDENT_LEN * test_level, "",			  kunit_status_to_ok_not_ok(status),			  test_number, description, directive_header,			  directive_body);} enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite){	const struct kunit_case *test_case;	enum kunit_status status = KUNIT_SKIPPED; 	if (suite->suite_init_err)		return KUNIT_FAILURE; 	kunit_suite_for_each_test_case(suite, test_case) {		if (test_case->status == KUNIT_FAILURE)			return KUNIT_FAILURE;		else if (test_case->status == KUNIT_SUCCESS)			status = KUNIT_SUCCESS;	} 	return status;}EXPORT_SYMBOL_GPL(kunit_suite_has_succeeded); static size_t kunit_suite_counter = 1; static void kunit_print_suite_end(struct kunit_suite *suite){	kunit_print_ok_not_ok(NULL, KUNIT_LEVEL_SUITE,			      kunit_suite_has_succeeded(suite),			      kunit_suite_counter++,			      suite->name,			      suite->status_comment);} unsigned int kunit_test_case_num(struct kunit_suite *suite,				 struct kunit_case *test_case){	struct kunit_case *tc;	unsigned int i = 1; 	kunit_suite_for_each_test_case(suite, tc) {		if (tc == test_case)			return i;		i++;	} 	return 0;}EXPORT_SYMBOL_GPL(kunit_test_case_num); static void kunit_print_string_stream(struct kunit *test,				      struct string_stream *stream){	struct string_stream_fragment *fragment;	char *buf; 	if (string_stream_is_empty(stream))		return; 	buf = string_stream_get_string(stream);	if (!buf) {		kunit_err(test,			  "Could not allocate buffer, dumping stream:\n");		list_for_each_entry(fragment, &stream->fragments, node) {			kunit_err(test, "%s", fragment->fragment);		}		kunit_err(test, "\n");	} else {		kunit_err(test, "%s", buf);		kfree(buf);	}} static void kunit_fail(struct kunit *test, const struct kunit_loc *loc,		       enum kunit_assert_type type, const struct kunit_assert *assert,		       assert_format_t assert_format, const struct va_format *message){	struct string_stream *stream; 	kunit_set_failure(test); 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);	if (IS_ERR(stream)) {		WARN(true,		     "Could not allocate stream to print failed assertion in %s:%d\n",		     loc->file,		     loc->line);		return;	} 	kunit_assert_prologue(loc, type, stream);	assert_format(assert, message, stream); 	kunit_print_string_stream(test, stream); 	kunit_free_string_stream(test, stream);} void __noreturn __kunit_abort(struct kunit *test){	kunit_try_catch_throw(&test->try_catch); /* Does not return. */ 	/*	 * Throw could not abort from test.	 *	 * XXX: we should never reach this line! As kunit_try_catch_throw is	 * marked __noreturn.	 */	WARN_ONCE(true, "Throw could not abort from test!\n");}EXPORT_SYMBOL_GPL(__kunit_abort); void __kunit_do_failed_assertion(struct kunit *test,			       const struct kunit_loc *loc,			       enum kunit_assert_type type,			       const struct kunit_assert *assert,			       assert_format_t assert_format,			       const char *fmt, ...){	va_list args;	struct va_format message;	va_start(args, fmt); 	message.fmt = fmt;	message.va = &args; 	kunit_fail(test, loc, type, assert, assert_format, &message); 	va_end(args);}EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion); void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log){	spin_lock_init(&test->lock);	INIT_LIST_HEAD(&test->resources);	test->name = name;	test->log = log;	if (test->log)		string_stream_clear(log);	test->status = KUNIT_SUCCESS;	test->status_comment[0] = '\0';}EXPORT_SYMBOL_GPL(kunit_init_test); /* Only warn when a test takes more than twice the threshold */#define KUNIT_SPEED_WARNING_MULTIPLIER	2 /* Slow tests are defined as taking more than 1s */#define KUNIT_SPEED_SLOW_THRESHOLD_S	1 #define KUNIT_SPEED_SLOW_WARNING_THRESHOLD_S	\	(KUNIT_SPEED_WARNING_MULTIPLIER * KUNIT_SPEED_SLOW_THRESHOLD_S) #define s_to_timespec64(s) ns_to_timespec64((s) * NSEC_PER_SEC) static void kunit_run_case_check_speed(struct kunit *test,				       struct kunit_case *test_case,				       struct timespec64 duration){	struct timespec64 slow_thr =		s_to_timespec64(KUNIT_SPEED_SLOW_WARNING_THRESHOLD_S);	enum kunit_speed speed = test_case->attr.speed; 	if (timespec64_compare(&duration, &slow_thr) < 0)		return; 	if (speed == KUNIT_SPEED_VERY_SLOW || speed == KUNIT_SPEED_SLOW)		return; 	kunit_warn(test,		   "Test should be marked slow (runtime: %lld.%09lds)",		   duration.tv_sec, duration.tv_nsec);} /* * Initializes and runs test case. Does not clean up or do post validations. */static void kunit_run_case_internal(struct kunit *test,				    struct kunit_suite *suite,				    struct kunit_case *test_case){	struct timespec64 start, end; 	if (suite->init) {		int ret; 		ret = suite->init(test);		if (ret) {			kunit_err(test, "failed to initialize: %d\n", ret);			kunit_set_failure(test);			return;		}	} 	ktime_get_ts64(&start); 	test_case->run_case(test); 	ktime_get_ts64(&end); 	kunit_run_case_check_speed(test, test_case, timespec64_sub(end, start));} static void kunit_case_internal_cleanup(struct kunit *test){	kunit_cleanup(test);} /* * Performs post validations and cleanup after a test case was run. * XXX: Should ONLY BE CALLED AFTER kunit_run_case_internal! */static void kunit_run_case_cleanup(struct kunit *test,				   struct kunit_suite *suite){	if (suite->exit)		suite->exit(test); 	kunit_case_internal_cleanup(test);} struct kunit_try_catch_context {	struct kunit *test;	struct kunit_suite *suite;	struct kunit_case *test_case;}; static void kunit_try_run_case(void *data){	struct kunit_try_catch_context *ctx = data;	struct kunit *test = ctx->test;	struct kunit_suite *suite = ctx->suite;	struct kunit_case *test_case = ctx->test_case; 	current->kunit_test = test; 	/*	 * kunit_run_case_internal may encounter a fatal error; if it does,	 * abort will be called, this thread will exit, and finally the parent	 * thread will resume control and handle any necessary clean up.	 */	kunit_run_case_internal(test, suite, test_case);} static void kunit_try_run_case_cleanup(void *data){	struct kunit_try_catch_context *ctx = data;	struct kunit *test = ctx->test;	struct kunit_suite *suite = ctx->suite; 	current->kunit_test = test; 	kunit_run_case_cleanup(test, suite);} static void kunit_catch_run_case_cleanup(void *data){	struct kunit_try_catch_context *ctx = data;	struct kunit *test = ctx->test;	int try_exit_code = kunit_try_catch_get_result(&test->try_catch); 	/* It is always a failure if cleanup aborts. */	kunit_set_failure(test); 	if (try_exit_code) {		/*		 * Test case could not finish, we have no idea what state it is		 * in, so don't do clean up.		 */		if (try_exit_code == -ETIMEDOUT) {			kunit_err(test, "test case cleanup timed out\n");		/*		 * Unknown internal error occurred preventing test case from		 * running, so there is nothing to clean up.		 */		} else {			kunit_err(test, "internal error occurred during test case cleanup: %d\n",				  try_exit_code);		}		return;	} 	kunit_err(test, "test aborted during cleanup. continuing without cleaning up\n");}  static void kunit_catch_run_case(void *data){	struct kunit_try_catch_context *ctx = data;	struct kunit *test = ctx->test;	int try_exit_code = kunit_try_catch_get_result(&test->try_catch); 	if (try_exit_code) {		kunit_set_failure(test);		/*		 * Test case could not finish, we have no idea what state it is		 * in, so don't do clean up.		 */		if (try_exit_code == -ETIMEDOUT) {

Lines 1–500 of 961.