| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * C++ stream style string formatter and printer used in KUnit for outputting |
| * KUnit messages. |
| * |
| * Copyright (C) 2018, Google LLC. |
| * Author: Brendan Higgins <brendanhiggins@google.com> |
| */ |
| |
| #include <test/test.h> |
| #include <test/test-stream.h> |
| #include <test/string-stream.h> |
| |
| static void test_stream_set_level(struct test_stream *this, |
| const char *level) |
| { |
| this->level = level; |
| } |
| |
| static void test_stream_add(struct test_stream *this, const char *fmt, ...) |
| { |
| va_list args; |
| struct string_stream *stream = this->internal_stream; |
| |
| va_start(args, fmt); |
| if (stream->vadd(stream, fmt, args) < 0) |
| test_err(this->test, "Failed to allocate fragment: %s", fmt); |
| |
| va_end(args); |
| } |
| |
| static void test_stream_append(struct test_stream *this, |
| struct test_stream *other) |
| { |
| struct string_stream *other_stream = other->internal_stream; |
| const char *other_content; |
| |
| other_content = other_stream->get_string(other_stream); |
| |
| if (!other_content) { |
| test_err(this->test, |
| "Failed to get string from second argument for appending."); |
| return; |
| } |
| |
| this->add(this, other_content); |
| kfree(other_content); |
| } |
| |
| static void test_stream_clear(struct test_stream *this) |
| { |
| this->internal_stream->clear(this->internal_stream); |
| } |
| |
| static void test_stream_commit(struct test_stream *this) |
| { |
| char *buf; |
| struct string_stream_fragment *fragment; |
| struct string_stream *stream = this->internal_stream; |
| |
| if (!this->level) { |
| test_err(this->test, |
| "Stream was committed without a specified log level."); |
| this->set_level(this, KERN_ERR); |
| } |
| |
| buf = stream->get_string(stream); |
| if (!buf) { |
| test_err(this->test, |
| "Could not allocate buffer, dumping stream:"); |
| list_for_each_entry(fragment, &stream->fragments, node) { |
| test_err(this->test, fragment->fragment); |
| } |
| goto cleanup; |
| } |
| |
| test_printk(this->level, this->test, buf); |
| kfree(buf); |
| |
| cleanup: |
| this->clear(this); |
| } |
| |
| static int test_stream_init(struct test_resource *res, void *context) |
| { |
| struct test_stream *stream; |
| struct test *test = context; |
| |
| stream = kzalloc(sizeof(*stream), GFP_KERNEL); |
| if (!stream) |
| return -ENOMEM; |
| res->allocation = stream; |
| stream->test = test; |
| stream->level = NULL; |
| stream->internal_stream = new_string_stream(); |
| |
| if (!stream->internal_stream) |
| return -ENOMEM; |
| |
| stream->set_level = test_stream_set_level; |
| stream->add = test_stream_add; |
| stream->append = test_stream_append; |
| stream->commit = test_stream_commit; |
| stream->clear = test_stream_clear; |
| return 0; |
| } |
| |
| static void test_stream_free(struct test_resource *res) |
| { |
| struct test_stream *stream = res->allocation; |
| |
| if (!stream->internal_stream->is_empty(stream->internal_stream)) { |
| test_err(stream->test, |
| "End of test case reached with uncommitted stream entries."); |
| stream->commit(stream); |
| } |
| |
| destroy_string_stream(stream->internal_stream); |
| kfree(stream); |
| } |
| |
| struct test_stream *test_new_stream(struct test *test) |
| { |
| struct test_resource *res; |
| |
| res = test_alloc_resource(test, |
| test_stream_init, |
| test_stream_free, |
| test); |
| |
| if (res) |
| return res->allocation; |
| else |
| return NULL; |
| } |