/* SPDX-License-Identifier: GPL-2.0-only */

#include "../lib/cbmem_console.c"

#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <tests/test.h>


#if ENV_ROMSTAGE_OR_BEFORE
/* Weak references have to be here, so TEST_REGION macro will work properly */
__weak extern u8 _preram_cbmem_console[];
__weak extern u8 _epreram_cbmem_console[];

#define PRERAM_CBMEM_CONSOLE_SIZE (1 * KiB)
TEST_REGION(preram_cbmem_console, PRERAM_CBMEM_CONSOLE_SIZE);
#endif

/* Disable init hooks. This test does not need them. */
void cbmem_run_init_hooks(int is_recovery)
{
	(void)is_recovery;
}

int setup_cbmemc(void **state)
{
	cbmemc_init();
	return 0;
}

int teardown_cbmemc(void **state)
{
	current_console->size = 0;
	return 0;
}

void test_cbmemc_init(void **state)
{
	cbmemc_init();

	/* Check if console structure was created. */
	assert_non_null(current_console);
}

void test_cbmemc_tx_byte(void **state)
{
	int i;
	u32 cursor;
	const unsigned char data[] =
		"Random testing string\n"
		"`1234567890-=~!@#$%^&*()_+\n"
		"abcdefghijklmnopqrstuvwxyz\n"
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ\n";

	for (i = 0; i < ARRAY_SIZE(data); ++i)
		cbmemc_tx_byte(data[i]);

	cursor = current_console->cursor & CURSOR_MASK;

	assert_int_equal(ARRAY_SIZE(data), cursor);

	/* Check if all characters were added correctly. */
	assert_memory_equal(data, current_console->body, ARRAY_SIZE(data));
}

void test_cbmemc_tx_byte_overflow(void **state)
{
	int i;
	u32 cursor;
	u32 flags;
	const uint32_t console_size = current_console->size;
	const unsigned char data[] =
		"Another random string\n"
		"abcdefghijklmnopqrstuvwxyz\n"
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
		"`1234567890-=~!@#$%^&*()_+\n";
	const int data_size = ARRAY_SIZE(data) - 1;
	const int data_stream_length = console_size + data_size;
	const int overflow_bytes = data_stream_length % console_size;
	unsigned char *check_buffer =
		(unsigned char *)malloc(sizeof(unsigned char) * console_size);

	/* Fill console buffer */
	for (i = 0; i < console_size; ++i)
		cbmemc_tx_byte(data[i % data_size]);

	/* Store buffer for checking */
	memcpy(check_buffer, current_console->body, console_size);

	assert_memory_equal(current_console->body, data, overflow_bytes);

	/* Add more data to console buffer to override older bytes */
	for (; i < data_stream_length; ++i)
		cbmemc_tx_byte(data[i % data_size]);

	cursor = current_console->cursor & CURSOR_MASK;
	flags = current_console->cursor & ~CURSOR_MASK;

	/* Check if there was a overflow in buffer */
	assert_int_equal(OVERFLOW, flags & OVERFLOW);

	/* Check if cursor got back to the beginning of a buffer */
	assert_int_equal(data_size, cursor);

	/* Check if overflow buffer was overwritten */
	assert_memory_not_equal(current_console->body, data, overflow_bytes);

	/* Check if rest of the buffer contents, that should not be overridden,
	 * is the same.
	 */
	assert_memory_equal(&current_console->body[overflow_bytes],
			    check_buffer + overflow_bytes, console_size - overflow_bytes);

	free(check_buffer);
}

int main(void)
{
	const struct CMUnitTest tests[] = {
		cmocka_unit_test_teardown(test_cbmemc_init, teardown_cbmemc),
		cmocka_unit_test_setup_teardown(test_cbmemc_tx_byte, setup_cbmemc,
						teardown_cbmemc),
		cmocka_unit_test_setup_teardown(test_cbmemc_tx_byte_overflow, setup_cbmemc,
						teardown_cbmemc),
	};

	return cb_run_group_tests(tests, NULL, NULL);
}