Commit 05e2006c authored by Richard Fitzgerald's avatar Richard Fitzgerald Committed by Shuah Khan

kunit: Use string_stream for test log

Replace the fixed-size log buffer with a string_stream so that the
log can grow as lines are added.

The existing kunit log tests have been updated for using a
string_stream as the log. No new test have been added because there
are already tests for the underlying string_stream.

As the log tests now depend on string_stream functions they cannot
build when kunit-test is a module. They have been surrounded by
a #if to replace them with skipping version when the test is
build as a module. Though this isn't pretty, it avoids moving
code to another file while that code is also being changed.
Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Reviewed-by: default avatarDavid Gow <davidgow@google.com>
Signed-off-by: default avatarShuah Khan <skhan@linuxfoundation.org>
parent d1a0d699
...@@ -33,9 +33,7 @@ ...@@ -33,9 +33,7 @@
DECLARE_STATIC_KEY_FALSE(kunit_running); DECLARE_STATIC_KEY_FALSE(kunit_running);
struct kunit; struct kunit;
struct string_stream;
/* Size of log associated with test. */
#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
...@@ -133,7 +131,7 @@ struct kunit_case { ...@@ -133,7 +131,7 @@ struct kunit_case {
/* private: internal use only. */ /* private: internal use only. */
enum kunit_status status; enum kunit_status status;
char *module_name; char *module_name;
char *log; struct string_stream *log;
}; };
static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
...@@ -253,7 +251,7 @@ struct kunit_suite { ...@@ -253,7 +251,7 @@ struct kunit_suite {
/* private: internal use only */ /* private: internal use only */
char status_comment[KUNIT_STATUS_COMMENT_SIZE]; char status_comment[KUNIT_STATUS_COMMENT_SIZE];
struct dentry *debugfs; struct dentry *debugfs;
char *log; struct string_stream *log;
int suite_init_err; int suite_init_err;
}; };
...@@ -279,7 +277,7 @@ struct kunit { ...@@ -279,7 +277,7 @@ struct kunit {
/* private: internal use only. */ /* private: internal use only. */
const char *name; /* Read only after initialization! */ const char *name; /* Read only after initialization! */
char *log; /* Points at case log after initialization */ struct string_stream *log; /* Points at case log after initialization */
struct kunit_try_catch try_catch; struct kunit_try_catch try_catch;
/* param_value is the current parameter value for a test case. */ /* param_value is the current parameter value for a test case. */
const void *param_value; const void *param_value;
...@@ -315,7 +313,7 @@ const char *kunit_filter_glob(void); ...@@ -315,7 +313,7 @@ const char *kunit_filter_glob(void);
char *kunit_filter(void); char *kunit_filter(void);
char *kunit_filter_action(void); char *kunit_filter_action(void);
void kunit_init_test(struct kunit *test, const char *name, char *log); void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log);
int kunit_run_tests(struct kunit_suite *suite); int kunit_run_tests(struct kunit_suite *suite);
...@@ -473,7 +471,7 @@ static inline void *kunit_kcalloc(struct kunit *test, size_t n, size_t size, gfp ...@@ -473,7 +471,7 @@ static inline void *kunit_kcalloc(struct kunit *test, size_t n, size_t size, gfp
void kunit_cleanup(struct kunit *test); void kunit_cleanup(struct kunit *test);
void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...); void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, ...);
/** /**
* kunit_mark_skipped() - Marks @test_or_suite as skipped * kunit_mark_skipped() - Marks @test_or_suite as skipped
......
...@@ -37,14 +37,21 @@ void kunit_debugfs_init(void) ...@@ -37,14 +37,21 @@ void kunit_debugfs_init(void)
debugfs_rootdir = debugfs_create_dir(KUNIT_DEBUGFS_ROOT, NULL); debugfs_rootdir = debugfs_create_dir(KUNIT_DEBUGFS_ROOT, NULL);
} }
static void debugfs_print_result(struct seq_file *seq, static void debugfs_print_result(struct seq_file *seq, struct string_stream *log)
struct kunit_suite *suite,
struct kunit_case *test_case)
{ {
if (!test_case || !test_case->log) struct string_stream_fragment *frag_container;
if (!log)
return; return;
seq_printf(seq, "%s", test_case->log); /*
* Walk the fragments so we don't need to allocate a temporary
* buffer to hold the entire string.
*/
spin_lock(&log->lock);
list_for_each_entry(frag_container, &log->fragments, node)
seq_printf(seq, "%s", frag_container->fragment);
spin_unlock(&log->lock);
} }
/* /*
...@@ -69,10 +76,9 @@ static int debugfs_print_results(struct seq_file *seq, void *v) ...@@ -69,10 +76,9 @@ static int debugfs_print_results(struct seq_file *seq, void *v)
seq_printf(seq, KUNIT_SUBTEST_INDENT "1..%zd\n", kunit_suite_num_test_cases(suite)); 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, test_case->log);
if (suite->log) debugfs_print_result(seq, 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);
...@@ -105,9 +111,13 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite) ...@@ -105,9 +111,13 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite)
struct kunit_case *test_case; struct kunit_case *test_case;
/* Allocate logs before creating debugfs representation. */ /* Allocate logs before creating debugfs representation. */
suite->log = kzalloc(KUNIT_LOG_SIZE, GFP_KERNEL); suite->log = alloc_string_stream(GFP_KERNEL);
kunit_suite_for_each_test_case(suite, test_case) string_stream_set_append_newlines(suite->log, true);
test_case->log = kzalloc(KUNIT_LOG_SIZE, GFP_KERNEL);
kunit_suite_for_each_test_case(suite, test_case) {
test_case->log = alloc_string_stream(GFP_KERNEL);
string_stream_set_append_newlines(test_case->log, true);
}
suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir); suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir);
...@@ -121,7 +131,7 @@ void kunit_debugfs_destroy_suite(struct kunit_suite *suite) ...@@ -121,7 +131,7 @@ void kunit_debugfs_destroy_suite(struct kunit_suite *suite)
struct kunit_case *test_case; struct kunit_case *test_case;
debugfs_remove_recursive(suite->debugfs); debugfs_remove_recursive(suite->debugfs);
kfree(suite->log); string_stream_destroy(suite->log);
kunit_suite_for_each_test_case(suite, test_case) kunit_suite_for_each_test_case(suite, test_case)
kfree(test_case->log); string_stream_destroy(test_case->log);
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <kunit/test.h> #include <kunit/test.h>
#include <kunit/test-bug.h> #include <kunit/test-bug.h>
#include "string-stream.h"
#include "try-catch-impl.h" #include "try-catch-impl.h"
struct kunit_try_catch_test_context { struct kunit_try_catch_test_context {
...@@ -530,12 +531,27 @@ static struct kunit_suite kunit_resource_test_suite = { ...@@ -530,12 +531,27 @@ static struct kunit_suite kunit_resource_test_suite = {
.test_cases = kunit_resource_test_cases, .test_cases = kunit_resource_test_cases,
}; };
/*
* Log tests call string_stream functions, which aren't exported. So only
* build this code if this test is built-in.
*/
#if IS_BUILTIN(CONFIG_KUNIT_TEST)
/* This avoids a cast warning if kfree() is passed direct to kunit_add_action(). */
static void kfree_wrapper(void *p)
{
kfree(p);
}
static void kunit_log_test(struct kunit *test) static void kunit_log_test(struct kunit *test)
{ {
struct kunit_suite suite; struct kunit_suite suite;
#ifdef CONFIG_KUNIT_DEBUGFS
suite.log = kunit_kzalloc(test, KUNIT_LOG_SIZE, GFP_KERNEL); char *full_log;
#endif
suite.log = kunit_alloc_string_stream(test, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, suite.log); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, suite.log);
string_stream_set_append_newlines(suite.log, true);
kunit_log(KERN_INFO, test, "put this in log."); kunit_log(KERN_INFO, test, "put this in log.");
kunit_log(KERN_INFO, test, "this too."); kunit_log(KERN_INFO, test, "this too.");
...@@ -543,14 +559,21 @@ static void kunit_log_test(struct kunit *test) ...@@ -543,14 +559,21 @@ static void kunit_log_test(struct kunit *test)
kunit_log(KERN_INFO, &suite, "along with this."); kunit_log(KERN_INFO, &suite, "along with this.");
#ifdef CONFIG_KUNIT_DEBUGFS #ifdef CONFIG_KUNIT_DEBUGFS
KUNIT_EXPECT_TRUE(test, test->log->append_newlines);
full_log = string_stream_get_string(test->log);
kunit_add_action(test, (kunit_action_t *)kfree, full_log);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, KUNIT_EXPECT_NOT_ERR_OR_NULL(test,
strstr(test->log, "put this in log.")); strstr(full_log, "put this in log."));
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, KUNIT_EXPECT_NOT_ERR_OR_NULL(test,
strstr(test->log, "this too.")); strstr(full_log, "this too."));
full_log = string_stream_get_string(suite.log);
kunit_add_action(test, kfree_wrapper, full_log);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, KUNIT_EXPECT_NOT_ERR_OR_NULL(test,
strstr(suite.log, "add to suite log.")); strstr(full_log, "add to suite log."));
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, KUNIT_EXPECT_NOT_ERR_OR_NULL(test,
strstr(suite.log, "along with this.")); strstr(full_log, "along with this."));
#else #else
KUNIT_EXPECT_NULL(test, test->log); KUNIT_EXPECT_NULL(test, test->log);
#endif #endif
...@@ -558,15 +581,30 @@ static void kunit_log_test(struct kunit *test) ...@@ -558,15 +581,30 @@ static void kunit_log_test(struct kunit *test)
static void kunit_log_newline_test(struct kunit *test) static void kunit_log_newline_test(struct kunit *test)
{ {
char *full_log;
kunit_info(test, "Add newline\n"); kunit_info(test, "Add newline\n");
if (test->log) { if (test->log) {
KUNIT_ASSERT_NOT_NULL_MSG(test, strstr(test->log, "Add newline\n"), full_log = string_stream_get_string(test->log);
"Missing log line, full log:\n%s", test->log); kunit_add_action(test, kfree_wrapper, full_log);
KUNIT_EXPECT_NULL(test, strstr(test->log, "Add newline\n\n")); KUNIT_ASSERT_NOT_NULL_MSG(test, strstr(full_log, "Add newline\n"),
"Missing log line, full log:\n%s", full_log);
KUNIT_EXPECT_NULL(test, strstr(full_log, "Add newline\n\n"));
} else { } else {
kunit_skip(test, "only useful when debugfs is enabled"); kunit_skip(test, "only useful when debugfs is enabled");
} }
} }
#else
static void kunit_log_test(struct kunit *test)
{
kunit_skip(test, "Log tests only run when built-in");
}
static void kunit_log_newline_test(struct kunit *test)
{
kunit_skip(test, "Log tests only run when built-in");
}
#endif /* IS_BUILTIN(CONFIG_KUNIT_TEST) */
static struct kunit_case kunit_log_test_cases[] = { static struct kunit_case kunit_log_test_cases[] = {
KUNIT_CASE(kunit_log_test), KUNIT_CASE(kunit_log_test),
......
...@@ -109,51 +109,17 @@ static void kunit_print_test_stats(struct kunit *test, ...@@ -109,51 +109,17 @@ static void kunit_print_test_stats(struct kunit *test,
stats.total); stats.total);
} }
/** /* Append formatted message to log. */
* kunit_log_newline() - Add newline to the end of log if one is not void kunit_log_append(struct string_stream *log, const char *fmt, ...)
* 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
* KUNIT_LOG_SIZE bytes (including null terminating byte).
*/
void kunit_log_append(char *log, const char *fmt, ...)
{ {
va_list args; va_list args;
int len, log_len, len_left;
if (!log) if (!log)
return; return;
log_len = strlen(log);
len_left = KUNIT_LOG_SIZE - log_len - 1;
if (len_left <= 0)
return;
/* Evaluate length of line to add to log */
va_start(args, fmt); va_start(args, fmt);
len = vsnprintf(NULL, 0, fmt, args) + 1; string_stream_vadd(log, fmt, args);
va_end(args); va_end(args);
/* 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);
...@@ -359,14 +325,14 @@ void __kunit_do_failed_assertion(struct kunit *test, ...@@ -359,14 +325,14 @@ void __kunit_do_failed_assertion(struct kunit *test,
} }
EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion); EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
void kunit_init_test(struct kunit *test, const char *name, char *log) void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
{ {
spin_lock_init(&test->lock); spin_lock_init(&test->lock);
INIT_LIST_HEAD(&test->resources); INIT_LIST_HEAD(&test->resources);
test->name = name; test->name = name;
test->log = log; test->log = log;
if (test->log) if (test->log)
test->log[0] = '\0'; string_stream_clear(log);
test->status = KUNIT_SUCCESS; test->status = KUNIT_SUCCESS;
test->status_comment[0] = '\0'; test->status_comment[0] = '\0';
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment