/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */

#include "test-lib.h"

static bool mem_has_bytes(const void *mem, size_t size, uint8_t b)
{
	const uint8_t *bytes = mem;
	unsigned int i;

	for (i = 0; i < size; i++) {
		if (bytes[i] != b)
			return FALSE;
	}
	return TRUE;
}

void test_mempool_alloconly(void)
{
#define SENTRY_SIZE 32
#define SENTRY_CHAR 0xDE
#define PMALLOC_MAX_COUNT 128
	pool_t pool;
	unsigned int i, j, k;
	void *mem[PMALLOC_MAX_COUNT + 1];
	char *sentry;

	test_begin("mempool_alloconly");
	for (i = 0; i < 64; i++) {
		for (j = 1; j <= 128; j++) {
			pool = pool_alloconly_create(MEMPOOL_GROWING"test", i);
			/* make sure p_malloc() doesn't overwrite unallocated
			   data in data stack. parts of the code relies on
			   this. */
			sentry = t_buffer_get(SENTRY_SIZE);
			memset(sentry, SENTRY_CHAR, SENTRY_SIZE);

			mem[0] = p_malloc(pool, j);
			memset(mem[0], j, j);

			for (k = 1; k <= PMALLOC_MAX_COUNT; k++) {
				mem[k] = p_malloc(pool, k);
				memset(mem[k], k, k);
			}
			test_assert(mem_has_bytes(sentry, SENTRY_SIZE, SENTRY_CHAR));
			test_assert(t_buffer_get(SENTRY_SIZE) == sentry);

			test_assert(mem_has_bytes(mem[0], j, j));
			for (k = 1; k <= PMALLOC_MAX_COUNT; k++)
				test_assert(mem_has_bytes(mem[k], k, k));
			pool_unref(&pool);
		}
	}
	test_end();
}

enum fatal_test_state fatal_mempool_alloconly(unsigned int stage)
{
	static pool_t pool;

	if (pool == NULL && stage != 0)
		return FATAL_TEST_FAILURE;

	switch(stage) {
	case 0: /* forbidden size */
		test_begin("fatal_mempool_alloconly");
		pool = pool_alloconly_create(MEMPOOL_GROWING"fatal", 1);
		test_expect_fatal_string("Trying to allocate 0 bytes");
		(void)p_malloc(pool, 0);
		return FATAL_TEST_FAILURE;

	case 1: /* logically impossible size */
		test_expect_fatal_string("Trying to allocate");
		(void)p_malloc(pool, POOL_MAX_ALLOC_SIZE + 1ULL);
		return FATAL_TEST_FAILURE;

#if SIZEOF_SIZE_T > 4 /* malloc(POOL_MAX_ALLOC_SIZE) may succeed with 32bit */
	case 2: /* physically impossible size */
		test_expect_fatal_string("Out of memory");
		(void)p_malloc(pool, POOL_MAX_ALLOC_SIZE);
		return FATAL_TEST_FAILURE;
#endif

	/* Continue with other tests as follows:
	case 3:
		something_fatal();
		return FATAL_TEST_FAILURE;
	*/
	}

	/* Either our tests have finished, or the test suite has got confused. */
	pool_unref(&pool);
	test_end();
	return FATAL_TEST_FINISHED;
}
