linux/lib/kunit/assert.c
Daniel Latypov 21957f90b2 kunit: split out part of kunit_assert into a static const
This is per Linus's suggestion in [1].

The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a
kunit_assert object onto the stack. Normally we rely on compilers to
elide this, but when that doesn't work out, this blows up the stack
usage of kunit test functions.

We can move some data off the stack by making it static.
This change introduces a new `struct kunit_loc` to hold the file and
line number and then just passing assert_type (EXPECT or ASSERT) as an
argument.

In [1], it was suggested to also move out the format string as well, but
users could theoretically craft a format string at runtime, so we can't.

This change leaves a copy of `assert_type` in kunit_assert for now
because cleaning up all the macros to not pass it around is a bit more
involved.

Here's an example of the expanded code for KUNIT_FAIL():
if (__builtin_expect(!!(!(false)), 0)) {
  static const struct kunit_loc loc = { .file = ... };
  struct kunit_fail_assert __assertion = { .assert = { .type ...  };
  kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...);
};

[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ

Signed-off-by: Daniel Latypov <dlatypov@google.com>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Reviewed-by: David Gow <davidgow@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2022-01-25 12:49:59 -07:00

202 lines
5.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Assertion and expectation serialization API.
*
* Copyright (C) 2019, Google LLC.
* Author: Brendan Higgins <brendanhiggins@google.com>
*/
#include <kunit/assert.h>
#include <kunit/test.h>
#include "string-stream.h"
void kunit_assert_prologue(const struct kunit_loc *loc,
enum kunit_assert_type type,
struct string_stream *stream)
{
const char *expect_or_assert = NULL;
switch (type) {
case KUNIT_EXPECTATION:
expect_or_assert = "EXPECTATION";
break;
case KUNIT_ASSERTION:
expect_or_assert = "ASSERTION";
break;
}
string_stream_add(stream, "%s FAILED at %s:%d\n",
expect_or_assert, loc->file, loc->line);
}
EXPORT_SYMBOL_GPL(kunit_assert_prologue);
void kunit_assert_print_msg(const struct kunit_assert *assert,
struct string_stream *stream)
{
if (assert->message.fmt)
string_stream_add(stream, "\n%pV", &assert->message);
}
EXPORT_SYMBOL_GPL(kunit_assert_print_msg);
void kunit_fail_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
string_stream_add(stream, "%pV", &assert->message);
}
EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
void kunit_unary_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
struct kunit_unary_assert *unary_assert;
unary_assert = container_of(assert, struct kunit_unary_assert, assert);
if (unary_assert->expected_true)
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s to be true, but is false\n",
unary_assert->condition);
else
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s to be false, but is true\n",
unary_assert->condition);
kunit_assert_print_msg(assert, stream);
}
EXPORT_SYMBOL_GPL(kunit_unary_assert_format);
void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
struct kunit_ptr_not_err_assert *ptr_assert;
ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
assert);
if (!ptr_assert->value) {
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
ptr_assert->text);
} else if (IS_ERR(ptr_assert->value)) {
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s is not error, but is: %ld\n",
ptr_assert->text,
PTR_ERR(ptr_assert->value));
}
kunit_assert_print_msg(assert, stream);
}
EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format);
/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */
static bool is_literal(struct kunit *test, const char *text, long long value,
gfp_t gfp)
{
char *buffer;
int len;
bool ret;
len = snprintf(NULL, 0, "%lld", value);
if (strlen(text) != len)
return false;
buffer = kunit_kmalloc(test, len+1, gfp);
if (!buffer)
return false;
snprintf(buffer, len+1, "%lld", value);
ret = strncmp(buffer, text, len) == 0;
kunit_kfree(test, buffer);
return ret;
}
void kunit_binary_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
struct kunit_binary_assert *binary_assert;
binary_assert = container_of(assert, struct kunit_binary_assert,
assert);
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
binary_assert->left_text,
binary_assert->operation,
binary_assert->right_text);
if (!is_literal(stream->test, binary_assert->left_text,
binary_assert->left_value, stream->gfp))
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld\n",
binary_assert->left_text,
binary_assert->left_value);
if (!is_literal(stream->test, binary_assert->right_text,
binary_assert->right_value, stream->gfp))
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld",
binary_assert->right_text,
binary_assert->right_value);
kunit_assert_print_msg(assert, stream);
}
EXPORT_SYMBOL_GPL(kunit_binary_assert_format);
void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
struct kunit_binary_ptr_assert *binary_assert;
binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
assert);
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
binary_assert->left_text,
binary_assert->operation,
binary_assert->right_text);
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %px\n",
binary_assert->left_text,
binary_assert->left_value);
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %px",
binary_assert->right_text,
binary_assert->right_value);
kunit_assert_print_msg(assert, stream);
}
EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format);
/* Checks if KUNIT_EXPECT_STREQ() args were string literals.
* Note: `text` will have ""s where as `value` will not.
*/
static bool is_str_literal(const char *text, const char *value)
{
int len;
len = strlen(text);
if (len < 2)
return false;
if (text[0] != '\"' || text[len - 1] != '\"')
return false;
return strncmp(text + 1, value, len - 2) == 0;
}
void kunit_binary_str_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
struct kunit_binary_str_assert *binary_assert;
binary_assert = container_of(assert, struct kunit_binary_str_assert,
assert);
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
binary_assert->left_text,
binary_assert->operation,
binary_assert->right_text);
if (!is_str_literal(binary_assert->left_text, binary_assert->left_value))
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"\n",
binary_assert->left_text,
binary_assert->left_value);
if (!is_str_literal(binary_assert->right_text, binary_assert->right_value))
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"",
binary_assert->right_text,
binary_assert->right_value);
kunit_assert_print_msg(assert, stream);
}
EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);