/* libmisc/tests/test_vcall.c - Tests for <libmisc/vcall.h>
 *
 * Copyright (C) 2024  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <libmisc/assert.h>
#include <libmisc/vcall.h>

#include "test.h"

/******************************************************************************/

struct frobber_vtable;

typedef struct {
	struct frobber_vtable *vtable;
} implements_frobber;

struct frobber_vtable {
	int (*frob)(implements_frobber *);
	int (*frob1)(implements_frobber *, int);
	void (*frob0)(implements_frobber *);
};

/******************************************************************************/

struct myclass {
	int a;
	implements_frobber;
};
static_assert(offsetof(struct myclass, implements_frobber) != 0);

static int myclass_frob(implements_frobber *_self) {
	struct myclass *self = VCALL_SELF(struct myclass, implements_frobber, _self);
	test_assert(self);
	test_assert((void*)self != (void*)_self);
	return self->a;
}

static int myclass_frob1(implements_frobber *_self, int arg) {
	struct myclass *self = VCALL_SELF(struct myclass, implements_frobber, _self);
	test_assert(self);
	test_assert((void*)self != (void*)_self);
	return arg;
}

static void myclass_frob0(implements_frobber *_self) {
	struct myclass *self = VCALL_SELF(struct myclass, implements_frobber, _self);
	test_assert(self);
	test_assert((void*)self != (void*)_self);
}

struct frobber_vtable myclass_vtable = {
	.frob = myclass_frob,
	.frob1 = myclass_frob1,
	.frob0 = myclass_frob0,
};

/******************************************************************************/

#define MAGIC1 909837
#define MAGIC2 657441

int main() {
	struct myclass obj = {
		.implements_frobber = { .vtable = &myclass_vtable },
		.a = MAGIC1,
	};
	test_assert(VCALL(&obj, frob) == MAGIC1);
	test_assert(VCALL(&obj, frob1, MAGIC2) == MAGIC2);
	VCALL(&obj, frob0);
	return 0;
}