Loading...
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2024 Google LLC
 * Written by Simon Glass <sjg@chromium.org>
 */

#include <membuf.h>
#include <os.h>
#include <rand.h>
#include <string.h>
#include <test/lib.h>
#include <test/test.h>
#include <test/ut.h>

#define TEST_SIZE	16
#define TEST_COUNT	10000

static void membuf_zero(struct membuf *mb)
{
	memset(mb->start, '\0', mb->end - mb->start);
}

static int membuf_check(struct unit_test_state *uts, struct membuf *mb,
			int value)
{
	/* head is out of range */
	ut_assert(!(mb->head < mb->start || mb->head >= mb->end));

	/* tail is out of range */
	ut_assert(!(mb->tail < mb->start || mb->tail >= mb->end));

	return 0;
}

/* write from 1 to test_size bytes, and check they come back OK */
static int lib_test_membuf_one(struct unit_test_state *uts)
{
	char in[TEST_SIZE * 2], out[TEST_SIZE * 2];
	struct membuf mb;
	int size, ret, test_size, i;

	ut_assertok(membuf_new(&mb, TEST_SIZE));

	/* setup in test */
	for (i = 0; i < TEST_SIZE; i++) {
		in[i] = (i & 63) + '0';
		in[i + TEST_SIZE] = in[i];
	}

	test_size = TEST_SIZE;

	for (i = 1; i < TEST_COUNT; i++) {
		membuf_zero(&mb);
		size = rand() % test_size;

		// now write patterns and check they come back OK
		ret = membuf_put(&mb, in, 0);
		ret = membuf_put(&mb, in, size);
		ut_asserteq(size, ret);

		ret = membuf_put(&mb, in, 0);
		ut_assertok(membuf_check(uts, &mb, i));

		ret = membuf_get(&mb, out, 0);
		ret = membuf_get(&mb, out, size);
		ut_asserteq(size, ret);

		ret = membuf_get(&mb, out, 0);
		ut_assertok(membuf_check(uts, &mb, i));

		ut_asserteq_mem(in, out, size);
	}

	return 0;
}
LIB_TEST(lib_test_membuf_one, 0);

/* write random number of bytes, and check they come back OK */
static int lib_test_membuf_random(struct unit_test_state *uts)
{
	char in[TEST_SIZE * 2];
	char buf[TEST_SIZE * 2];
	struct membuf mb;
	int size, ret, test_size, i;
	char *inptr, *outptr;
	int max_avail, min_free;

	ut_assertok(membuf_new(&mb, TEST_SIZE));

	for (i = 0; i < TEST_SIZE; i++) {
		in[i] = (i & 63) + '0';
		in[i + TEST_SIZE] = in[i];
	}

	test_size = TEST_SIZE;

	inptr = in;
	outptr = in;
	min_free = TEST_COUNT;
	max_avail = 0;
	membuf_zero(&mb);
	for (i = 0; i < TEST_COUNT; i++) {
		size = rand() % test_size;

		if (membuf_free(&mb) < min_free)
			min_free = membuf_free(&mb);

		ret = membuf_put(&mb, inptr, size);
		ut_assertok(membuf_check(uts, &mb, i));
		inptr += ret;
		if (inptr >= in + TEST_SIZE)
			inptr -= TEST_SIZE;

		size = rand() % (test_size - 1);

		if (membuf_avail(&mb) > max_avail)
			max_avail = membuf_avail(&mb);

		ret = membuf_get(&mb, buf, size);
		ut_assertok(membuf_check(uts, &mb, i));
		ut_asserteq_mem(buf, outptr, ret);

		outptr += ret;
		if (outptr >= in + TEST_SIZE)
			outptr -= TEST_SIZE;
	}

	return 0;
}
LIB_TEST(lib_test_membuf_random, 0);

/* test membuf_extend() with split segments */
static int lib_test_membuf_extend(struct unit_test_state *uts)
{
	char in[TEST_SIZE * 2];
	char buf[TEST_SIZE * 2];
	struct membuf mb;
	int ret, test_size, i, cur;
	char *data;

	ut_assertok(membuf_new(&mb, TEST_SIZE));

	for (i = 0; i < TEST_SIZE; i++) {
		in[i] = (i & 63) + '0';
		in[i + TEST_SIZE] = in[i];
	}

	test_size = TEST_SIZE - 1;

	for (cur = 0; cur <= test_size; cur++) {
		ut_assertok(membuf_new(&mb, TEST_SIZE));

		membuf_zero(&mb);

		/*
		 * add some bytes, then remove them - this will force the membuf
		 * to have data split into two segments when we fill it
		 */
		ret = membuf_putraw(&mb, TEST_SIZE / 2, true, &data);
		membuf_getraw(&mb, ret, true, &data);
		ut_asserteq(TEST_SIZE / 2, ret);

		/* fill it */
		ret = membuf_put(&mb, in, cur);
		ut_assertok(membuf_check(uts, &mb, cur));
		ut_asserteq(cur, ret);

		/* extend the buffer */
		ut_assertok(membuf_extend_by(&mb, TEST_SIZE, -1));
		ut_assertok(membuf_check(uts, &mb, cur));

		/* check our data is still there */
		ret = membuf_get(&mb, buf, TEST_SIZE * 2);
		ut_assertok(membuf_check(uts, &mb, cur));
		ut_asserteq(cur, ret);
		ut_asserteq_mem(in, buf, cur);
		membuf_uninit(&mb);
	}

	return 0;
}
LIB_TEST(lib_test_membuf_extend, 0);

/* test membuf_readline() with generated data */
static int lib_test_membuf_readline(struct unit_test_state *uts)
{
	char *buf;
	int size, cur, i, ret, readptr, cmpptr;
	struct membuf mb;
	char *data;
	char str[256];
	char *s;

	ut_assertok(membuf_new(&mb, 1024));
	membuf_zero(&mb);

	/* Use the README as test data */
	ut_assertok(os_read_file("README", (void **)&buf, &size));

	cur = 0;
	readptr = 0;
	cmpptr = 0;
	for (i = 0; i < 100000; i++, cur += 1) {
		/* fill the buffer with up to 'cur' bytes */
		ret = membuf_putraw(&mb, cur, false, &data);

		if (ret > 0) {
			int can_read = min(ret, size - readptr);

			memcpy(data, &buf[readptr], can_read);
			readptr += can_read;

			membuf_putraw(&mb, can_read, true, &data);
			ut_assertok(membuf_check(uts, &mb, i));
		}

		/* read a line and compare */
		ret = membuf_readline(&mb, str, 256, 0, true);
		ut_assertok(membuf_check(uts, &mb, i));
		if (ret) {
			char *ptr;

			s = &buf[cmpptr];
			ptr = strchr(s, '\n');
			*ptr = '\0';

			ut_asserteq_str(s, str);
			cmpptr += strlen(s) + 1;
			*ptr = '\n';
		} else {
			ut_assert(membuf_free(&mb));
		}
	}
	membuf_dispose(&mb);
	os_free(buf);

	return 0;
}
LIB_TEST(lib_test_membuf_readline, 0);