/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <string.h>

#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFValueTests: OTTestCase
@end

@implementation OFValueTests
- (void)testObjCType
{
	OFRange range = OFMakeRange(1, 64);
	OFValue *value = [OFValue valueWithBytes: &range
					objCType: @encode(OFRange)];

	OTAssertEqual(strcmp(value.objCType, @encode(OFRange)), 0);
}

- (void)testGetValueSize
{
	OFRange range = OFMakeRange(1, 64), range2;
	OFValue *value = [OFValue valueWithBytes: &range
					objCType: @encode(OFRange)];

	[value getValue: &range2 size: sizeof(OFRange)];
	OTAssert(OFEqualRanges(range2, range));
}

- (void)testGetValueSizeThrowsOnWrongSize
{
	OFRange range = OFMakeRange(1, 64);
	OFValue *value = [OFValue valueWithBytes: &range
					objCType: @encode(OFRange)];

	OTAssertThrowsSpecific(
	    [value getValue: &range size: sizeof(OFRange) - 1],
	    OFOutOfRangeException);
}

- (void)testPointer
{
	void *pointer = &pointer;
	OFValue *value = [OFValue valueWithPointer: pointer];

	OTAssertEqual(value.pointerValue, pointer);
	OTAssertEqual([[OFValue valueWithBytes: &pointer
				      objCType: @encode(void *)] pointerValue],
	    pointer);

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] pointerValue],
	    OFOutOfRangeException);
}

- (void)testNonretainedObject
{
	id object = (id)&object;
	OFValue *value = [OFValue valueWithNonretainedObject: object];

	OTAssertEqual(value.nonretainedObjectValue, object);
	OTAssertEqual([[OFValue
	    valueWithBytes: &object
		  objCType: @encode(id)] nonretainedObjectValue], object);

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] nonretainedObjectValue],
	    OFOutOfRangeException);
}

- (void)testRange
{
	OFRange range = OFMakeRange(1, 64), range2;
	OFValue *value = [OFValue valueWithRange: range];

	OTAssert(OFEqualRanges(value.rangeValue, range));
	OTAssert(OFEqualRanges(
	    [[OFValue valueWithBytes: &range
			    objCType: @encode(OFRange)] rangeValue], range));

	[value getValue: &range2 size: sizeof(range2)];
	OTAssert(OFEqualRanges(range2, range));

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] rangeValue],
	    OFOutOfRangeException);
}

- (void)testPoint
{
	OFPoint point = OFMakePoint(1.5f, 3.0f), point2;
	OFValue *value = [OFValue valueWithPoint: point];

	OTAssert(OFEqualPoints(value.pointValue, point));
	OTAssert(OFEqualPoints(
	    [[OFValue valueWithBytes: &point
			    objCType: @encode(OFPoint)] pointValue], point));

	[value getValue: &point2 size: sizeof(point2)];
	OTAssert(OFEqualPoints(point2, point));

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] pointValue],
	    OFOutOfRangeException);
}

- (void)testSize
{
	OFSize size = OFMakeSize(4.5f, 5.0f), size2;
	OFValue *value = [OFValue valueWithSize: size];

	OTAssert(OFEqualSizes(value.sizeValue, size));
	OTAssert(OFEqualSizes(
	    [[OFValue valueWithBytes: &size
			    objCType: @encode(OFSize)] sizeValue], size));

	[value getValue: &size2 size: sizeof(size2)];
	OTAssert(OFEqualSizes(size2, size));

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] sizeValue],
	    OFOutOfRangeException);
}

- (void)testRect
{
	OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f), rect2;
	OFValue *value = [OFValue valueWithRect: rect];

	OTAssert(OFEqualRects(value.rectValue, rect));
	OTAssert(OFEqualRects(
	    [[OFValue valueWithBytes: &rect
			    objCType: @encode(OFRect)] rectValue], rect));

	[value getValue: &rect2 size: sizeof(rect2)];
	OTAssert(OFEqualRects(rect2, rect));

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a" objCType: @encode(char)] rectValue],
	    OFOutOfRangeException);
}

- (void)testIsEqual
{
	OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f);

	OTAssertEqualObjects([OFValue valueWithRect: rect],
	    [OFValue valueWithBytes: &rect
			   objCType: @encode(OFRect)]);
	OTAssertNotEqualObjects(
	    [OFValue valueWithBytes: "a" objCType: @encode(signed char)],
	    [OFValue valueWithBytes: "a" objCType: @encode(unsigned char)]);
	OTAssertNotEqualObjects(
	    [OFValue valueWithBytes: "a" objCType: @encode(char)],
	    [OFValue valueWithBytes: "b" objCType: @encode(char)]);
}
@end
