root/lib/kunit/assert.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 <kunit/visibility.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);

VISIBLE_IF_KUNIT
void kunit_assert_print_msg(const struct va_format *message,
                            struct string_stream *stream)
{
        if (message->fmt)
                string_stream_add(stream, "\n%pV", message);
}

void kunit_fail_assert_format(const struct kunit_assert *assert,
                              const struct va_format *message,
                              struct string_stream *stream)
{
        string_stream_add(stream, "%pV", message);
}
EXPORT_SYMBOL_GPL(kunit_fail_assert_format);

void kunit_unary_assert_format(const struct kunit_assert *assert,
                               const struct va_format *message,
                               struct string_stream *stream)
{
        const 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(message, stream);
}
EXPORT_SYMBOL_GPL(kunit_unary_assert_format);

void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
                                     const struct va_format *message,
                                     struct string_stream *stream)
{
        const 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(message, stream);
}
EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format);

/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */
VISIBLE_IF_KUNIT bool is_literal(const char *text, long long value)
{
        char *buffer;
        int len;
        bool ret;

        len = snprintf(NULL, 0, "%lld", value);
        if (strlen(text) != len)
                return false;

        buffer = kmalloc(len+1, GFP_KERNEL);
        if (!buffer)
                return false;

        snprintf(buffer, len+1, "%lld", value);
        ret = strncmp(buffer, text, len) == 0;

        kfree(buffer);

        return ret;
}

void kunit_binary_assert_format(const struct kunit_assert *assert,
                                const struct va_format *message,
                                struct string_stream *stream)
{
        const 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->text->left_text,
                          binary_assert->text->operation,
                          binary_assert->text->right_text);
        if (!is_literal(binary_assert->text->left_text, binary_assert->left_value))
                string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld (0x%llx)\n",
                                  binary_assert->text->left_text,
                                  binary_assert->left_value,
                                  binary_assert->left_value);
        if (!is_literal(binary_assert->text->right_text, binary_assert->right_value))
                string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld (0x%llx)",
                                  binary_assert->text->right_text,
                                  binary_assert->right_value,
                                  binary_assert->right_value);
        kunit_assert_print_msg(message, stream);
}
EXPORT_SYMBOL_GPL(kunit_binary_assert_format);

void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
                                    const struct va_format *message,
                                    struct string_stream *stream)
{
        const 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->text->left_text,
                          binary_assert->text->operation,
                          binary_assert->text->right_text);
        string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %px\n",
                          binary_assert->text->left_text,
                          binary_assert->left_value);
        string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %px",
                          binary_assert->text->right_text,
                          binary_assert->right_value);
        kunit_assert_print_msg(message, 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.
 */
VISIBLE_IF_KUNIT 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,
                                    const struct va_format *message,
                                    struct string_stream *stream)
{
        const 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->text->left_text,
                          binary_assert->text->operation,
                          binary_assert->text->right_text);
        if (!is_str_literal(binary_assert->text->left_text, binary_assert->left_value))
                string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"\n",
                                  binary_assert->text->left_text,
                                  binary_assert->left_value);
        if (!is_str_literal(binary_assert->text->right_text, binary_assert->right_value))
                string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == \"%s\"",
                                  binary_assert->text->right_text,
                                  binary_assert->right_value);
        kunit_assert_print_msg(message, stream);
}
EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);

/* Adds a hexdump of a buffer to a string_stream comparing it with
 * a second buffer. The different bytes are marked with <>.
 */
VISIBLE_IF_KUNIT
void kunit_assert_hexdump(struct string_stream *stream,
                          const void *buf,
                          const void *compared_buf,
                          const size_t len)
{
        size_t i;
        const u8 *buf1 = buf;
        const u8 *buf2 = compared_buf;

        string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT);

        for (i = 0; i < len; ++i) {
                if (!(i % 16) && i)
                        string_stream_add(stream, "\n" KUNIT_SUBSUBTEST_INDENT);

                if (buf1[i] != buf2[i])
                        string_stream_add(stream, "<%02x>", buf1[i]);
                else
                        string_stream_add(stream, " %02x ", buf1[i]);
        }
}

void kunit_mem_assert_format(const struct kunit_assert *assert,
                             const struct va_format *message,
                             struct string_stream *stream)
{
        const struct kunit_mem_assert *mem_assert;

        mem_assert = container_of(assert, struct kunit_mem_assert,
                                  assert);

        if (!mem_assert->left_value) {
                string_stream_add(stream,
                                  KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
                                  mem_assert->text->left_text);
        } else if (!mem_assert->right_value) {
                string_stream_add(stream,
                                  KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
                                  mem_assert->text->right_text);
        } else {
                string_stream_add(stream,
                                KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
                                mem_assert->text->left_text,
                                mem_assert->text->operation,
                                mem_assert->text->right_text);

                string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s ==\n",
                                mem_assert->text->left_text);
                kunit_assert_hexdump(stream, mem_assert->left_value,
                                        mem_assert->right_value, mem_assert->size);

                string_stream_add(stream, "\n");

                string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s ==\n",
                                mem_assert->text->right_text);
                kunit_assert_hexdump(stream, mem_assert->right_value,
                                        mem_assert->left_value, mem_assert->size);

                kunit_assert_print_msg(message, stream);
        }
}
EXPORT_SYMBOL_GPL(kunit_mem_assert_format);