// 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 */ #include #include #include 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 KUNIT_RESOURCE_T *res, void *context) { struct test_stream *stream; struct KUNIT_T *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 KUNIT_RESOURCE_T *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 KUNIT_T *test) { struct KUNIT_RESOURCE_T *res; res = test_alloc_resource(test, test_stream_init, test_stream_free, test); if (res) return res->allocation; else return NULL; } EXPORT_SYMBOL(test_new_stream);