#include <string.h>
#include "serialization/TestDeserializationContext.h"
#include "serialization/TestSerializationContext.h"
#include "unittest/TestSuite.h"
#include "inputcontroller/InputMap.h"

static void verifyInit(InputMap * inputMap, int callingLine) {
	TestCase_assert(inputMap != NULL, "Expected non-NULL but got NULL (line %d)", callingLine);
	TestCase_assert(inputMap->keyboardBindingCount == 0, "Expected 0 but got %u (line %d)", inputMap->keyboardBindingCount, callingLine);
	TestCase_assert(inputMap->keyModifierBindingCount == 0, "Expected 0 but got %u (line %d)", inputMap->keyModifierBindingCount, callingLine);
	TestCase_assert(inputMap->gamepadPhysicalMapCount == 0, "Expected 0 but got %u (line %d)", inputMap->gamepadPhysicalMapCount, callingLine);
	TestCase_assert(inputMap->vtable == &InputMap_class, "Expected %p but got %p (line %d)", &InputMap_class, inputMap->vtable, callingLine);
}

static void testInit() {
	InputMap inputMap, * inputMapPtr;
	
	memset(&inputMap, 0x00, sizeof(InputMap));
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	verifyInit(&inputMap, __LINE__);
	InputMap_dispose(&inputMap);
	
	memset(&inputMap, 0xFF, sizeof(InputMap));
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	verifyInit(&inputMap, __LINE__);
	InputMap_dispose(&inputMap);
	
	inputMapPtr = InputMap_create();
	verifyInit(inputMapPtr, __LINE__);
	InputMap_dispose(inputMapPtr);
}

static bool isKeyBound(InputMap * inputMap, Atom actionID, unsigned int keyCode) {
	unsigned int bindingIndex;
	
	for (bindingIndex = 0; bindingIndex < inputMap->keyboardBindingCount; bindingIndex++) {
		if (inputMap->keyboardBindings[bindingIndex].actionID == actionID &&
		    inputMap->keyboardBindings[bindingIndex].keyCode == keyCode) {
			return true;
		}
	}
	return false;
}

static void verifyKeyNotBound(InputMap * inputMap, Atom actionID, unsigned int keyCode, int callingLine) {
	TestCase_assert(!InputMap_isKeyBound(inputMap, actionID, keyCode), "Key unexpectedly reported as bound (keyCode = %u, actionID = \"%s\") (line %d)", keyCode, actionID, callingLine);
	TestCase_assert(!isKeyBound(inputMap, actionID, keyCode), "Unexpectedly found bound key (keyCode = %u, actionID = \"%s\") (line %d)", keyCode, actionID, callingLine);
}

static void verifyKeyBound(InputMap * inputMap, Atom actionID, unsigned int keyCode, int callingLine) {
	TestCase_assert(InputMap_isKeyBound(inputMap, actionID, keyCode), "Key unexpectedly reported as unbound (keyCode = %u, actionID = \"%s\") (line %d)", keyCode, actionID, callingLine);
	TestCase_assert(isKeyBound(inputMap, actionID, keyCode), "Couldn't find bound key (keyCode = %u, actionID = \"%s\") (line %d)", keyCode, actionID, callingLine);
}

static void testKeyboardBindings() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	verifyKeyNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyNotBound(inputMap, ATOM("b"), 1, __LINE__);
	
	InputMap_bindKey(inputMap, ATOM("a"), 1);
	verifyKeyBound(inputMap, ATOM("a"), 1, __LINE__);
	
	InputMap_bindKey(inputMap, ATOM("a"), 2);
	verifyKeyBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyBound(inputMap, ATOM("a"), 2, __LINE__);
	
	InputMap_bindKey(inputMap, ATOM("b"), 1);
	verifyKeyBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyBound(inputMap, ATOM("b"), 1, __LINE__);
	
	InputMap_unbindKey(inputMap, ATOM("a"), 1);
	verifyKeyNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyBound(inputMap, ATOM("b"), 1, __LINE__);
}

static bool isKeyWithModifiersBound(InputMap * inputMap, Atom actionID, unsigned int keyCode, unsigned int modifierFlags) {
	unsigned int bindingIndex;
	
	for (bindingIndex = 0; bindingIndex < inputMap->keyboardBindingWithModifiersCount; bindingIndex++) {
		if (inputMap->keyboardBindingsWithModifiers[bindingIndex].actionID == actionID &&
		    inputMap->keyboardBindingsWithModifiers[bindingIndex].keyCode == keyCode &&
		    inputMap->keyboardBindingsWithModifiers[bindingIndex].modifierFlags == modifierFlags) {
			return true;
		}
	}
	return false;
}

static void verifyKeyWithModifiersNotBound(InputMap * inputMap, Atom actionID, unsigned int keyCode, unsigned int modifierFlags, int callingLine) {
	TestCase_assert(!InputMap_isKeyWithModifiersBound(inputMap, actionID, keyCode, modifierFlags), "Key with modifiers unexpectedly reported as bound (keyCode = %u, modifierFlags = 0x%X, actionID = \"%s\") (line %d)", keyCode, modifierFlags, actionID, callingLine);
	TestCase_assert(!isKeyWithModifiersBound(inputMap, actionID, keyCode, modifierFlags), "Unexpectedly found bound key with modifiers (keyCode = %u, modifierFlags = 0x%X, actionID = \"%s\") (line %d)", keyCode, modifierFlags, actionID, callingLine);
}

static void verifyKeyWithModifiersBound(InputMap * inputMap, Atom actionID, unsigned int keyCode, unsigned int modifierFlags, int callingLine) {
	TestCase_assert(InputMap_isKeyWithModifiersBound(inputMap, actionID, keyCode, modifierFlags), "Key with modifiers unexpectedly reported as unbound (keyCode = %u, modifierFlags = 0x%X, actionID = \"%s\") (line %d)", keyCode, modifierFlags, actionID, callingLine);
	TestCase_assert(isKeyWithModifiersBound(inputMap, actionID, keyCode, modifierFlags), "Couldn't find bound key with modifiers (keyCode = %u, modifierFlags = 0x%X, actionID = \"%s\") (line %d)", keyCode, modifierFlags, actionID, callingLine);
}

static void testKeyboardBindingsWithModifiers() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	verifyKeyWithModifiersNotBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	verifyKeyWithModifiersNotBound(inputMap, ATOM("a"), 2, 0x0, __LINE__);
	verifyKeyWithModifiersNotBound(inputMap, ATOM("b"), 1, 0x1, __LINE__);
	verifyKeyWithModifiersNotBound(inputMap, ATOM("b"), 1, 0x2, __LINE__);
	
	InputMap_bindKeyWithModifiers(inputMap, ATOM("a"), 1, 0x0);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	
	InputMap_bindKeyWithModifiers(inputMap, ATOM("a"), 2, 0x0);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 2, 0x0, __LINE__);
	
	InputMap_bindKeyWithModifiers(inputMap, ATOM("b"), 1, 0x1);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 2, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("b"), 1, 0x1, __LINE__);
	
	InputMap_bindKeyWithModifiers(inputMap, ATOM("b"), 1, 0x2);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 2, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("b"), 1, 0x1, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("b"), 1, 0x2, __LINE__);
	
	InputMap_unbindKeyWithModifiers(inputMap, ATOM("a"), 1, 0x0);
	verifyKeyWithModifiersNotBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 2, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("b"), 1, 0x1, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("b"), 1, 0x2, __LINE__);
	
	InputMap_unbindKeyWithModifiers(inputMap, ATOM("b"), 1, 0x1);
	verifyKeyWithModifiersNotBound(inputMap, ATOM("a"), 1, 0x0, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("a"), 2, 0x0, __LINE__);
	verifyKeyWithModifiersNotBound(inputMap, ATOM("b"), 1, 0x1, __LINE__);
	verifyKeyWithModifiersBound(inputMap, ATOM("b"), 1, 0x2, __LINE__);
}

static bool isKeyModifierBound(InputMap * inputMap, Atom actionID, unsigned int modifierBit) {
	unsigned int bindingIndex;
	
	for (bindingIndex = 0; bindingIndex < inputMap->keyModifierBindingCount; bindingIndex++) {
		if (inputMap->keyModifierBindings[bindingIndex].actionID == actionID &&
		    inputMap->keyModifierBindings[bindingIndex].modifierBit == modifierBit) {
			return true;
		}
	}
	return false;
}

static void verifyKeyModifierNotBound(InputMap * inputMap, Atom actionID, int modifierBit, int callingLine) {
	TestCase_assert(!InputMap_isKeyModifierBound(inputMap, actionID, modifierBit), "Modifier unexpectedly reported as bound (modifierBit = 0x%X, actionID = \"%s\") (line %d)", modifierBit, actionID, callingLine);
	TestCase_assert(!isKeyModifierBound(inputMap, actionID, modifierBit), "Unexpectedly found bound modifier (modifierBit = 0x%X, actionID = \"%s\") (line %d)", modifierBit, actionID, callingLine);
}

static void verifyKeyModifierBound(InputMap * inputMap, Atom actionID, int modifierBit, int callingLine) {
	TestCase_assert(InputMap_isKeyModifierBound(inputMap, actionID, modifierBit), "Modifier unexpectedly reported as unbound (modifierBit = 0x%X, actionID = \"%s\") (line %d)", modifierBit, actionID, callingLine);
	TestCase_assert(isKeyModifierBound(inputMap, actionID, modifierBit), "Couldn't find bound modifier (modifierBit = 0x%X, actionID = \"%s\") (line %d)", modifierBit, actionID, callingLine);
}

static void testKeyModifierBindings() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	verifyKeyModifierNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierNotBound(inputMap, ATOM("b"), 2, __LINE__);
	
	InputMap_bindKeyModifier(inputMap, ATOM("a"), 1);
	verifyKeyModifierBound(inputMap, ATOM("a"), 1, __LINE__);
	
	InputMap_bindKeyModifier(inputMap, ATOM("a"), 4);
	verifyKeyModifierBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("a"), 4, __LINE__);
	
	InputMap_bindKeyModifier(inputMap, ATOM("b"), 2);
	verifyKeyModifierBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("a"), 4, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("b"), 2, __LINE__);
	
	InputMap_unbindKeyModifier(inputMap, ATOM("a"), 1);
	verifyKeyModifierNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("a"), 4, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("b"), 2, __LINE__);
}

static bool isButtonBound(InputMap * inputMap, Atom actionID, int vendorID, int productID, unsigned int buttonID) {
	for (unsigned int gamepadMapIndex = 0; gamepadMapIndex < inputMap->gamepadPhysicalMapCount; gamepadMapIndex++) {
		if (inputMap->gamepadPhysicalMaps[gamepadMapIndex].vendorID != vendorID ||
		    inputMap->gamepadPhysicalMaps[gamepadMapIndex].productID != productID) {
			continue;
		}
		for (unsigned int bindingIndex = 0; bindingIndex < inputMap->gamepadPhysicalMaps[gamepadMapIndex].buttonActionBindingCount; bindingIndex++) {
			if (inputMap->gamepadPhysicalMaps[gamepadMapIndex].buttonActionBindings[bindingIndex].actionID == actionID &&
					inputMap->gamepadPhysicalMaps[gamepadMapIndex].buttonActionBindings[bindingIndex].buttonID == buttonID) {
				return true;
			}
		}
	}
	return false;
}

static void verifyButtonNotBound(InputMap * inputMap, Atom actionID, int vendorID, int productID, unsigned int buttonID, int callingLine) {
	TestCase_assert(!InputMap_isPhysicalButtonActionBound(inputMap, actionID, vendorID, productID, buttonID), "Button unexpectedly reported as bound (vendorID = %u, productID = %u, buttonID = %u, actionID = \"%s\") (line %d)", vendorID, productID, buttonID, actionID, callingLine);
	TestCase_assert(!isButtonBound(inputMap, actionID, vendorID, productID, buttonID), "Unexpectedly found bound button (vendorID = %u, productID = %u, buttonID = %u, actionID = \"%s\") (line %d)", vendorID, productID, buttonID, actionID, callingLine);
}

static void verifyButtonBound(InputMap * inputMap, Atom actionID, int vendorID, int productID, unsigned int buttonID, int callingLine) {
	TestCase_assert(InputMap_isPhysicalButtonActionBound(inputMap, actionID, vendorID, productID, buttonID), "Button unexpectedly reported as unbound (vendorID = %u, productID = %u, buttonID = %u, actionID = \"%s\") (line %d)", vendorID, productID, buttonID, actionID, callingLine);
	TestCase_assert(isButtonBound(inputMap, actionID, vendorID, productID, buttonID), "Couldn't find bound button (vendorID = %u, productID = %u, buttonID = %u, actionID = \"%s\") (line %d)", vendorID, productID, buttonID, actionID, callingLine);
}

static void testButtonBindings() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("a"), 1, 1, 1, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 0);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 1);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 1, 1, 1);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 1, 1, 1, __LINE__);
	
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("b"), 0, 0, 0);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 1, 1, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	
	InputMap_unbindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 0);
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 1, 1, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
}

static bool isAxisBound(InputMap * inputMap, Atom actionID, int vendorID, int productID, unsigned int axisID, float activateThreshold, float releaseThreshold) {
	for (unsigned int gamepadMapIndex = 0; gamepadMapIndex < inputMap->gamepadPhysicalMapCount; gamepadMapIndex++) {
		if (inputMap->gamepadPhysicalMaps[gamepadMapIndex].vendorID != vendorID ||
		    inputMap->gamepadPhysicalMaps[gamepadMapIndex].productID != productID) {
			continue;
		}
		for (unsigned int bindingIndex = 0; bindingIndex < inputMap->gamepadPhysicalMaps[gamepadMapIndex].axisActionBindingCount; bindingIndex++) {
			if (inputMap->gamepadPhysicalMaps[gamepadMapIndex].axisActionBindings[bindingIndex].actionID == actionID &&
					inputMap->gamepadPhysicalMaps[gamepadMapIndex].axisActionBindings[bindingIndex].axisID == axisID &&
					inputMap->gamepadPhysicalMaps[gamepadMapIndex].axisActionBindings[bindingIndex].activateThreshold == activateThreshold &&
					inputMap->gamepadPhysicalMaps[gamepadMapIndex].axisActionBindings[bindingIndex].releaseThreshold == releaseThreshold) {
				return true;
			}
		}
	}
	return false;
}

static void verifyAxisNotBound(InputMap * inputMap, Atom actionID, int vendorID, int productID, unsigned int axisID, float activateThreshold, float releaseThreshold, int callingLine) {
	TestCase_assert(!InputMap_isPhysicalAxisActionBound(inputMap, actionID, vendorID, productID, axisID), "Axis unexpectedly reported as bound (vendorID = %u, productID = %u, axisID = %u, activateThreshold = %f, releaseThreshold = %f, actionID = \"%s\") (line %d)", vendorID, productID, axisID, activateThreshold, releaseThreshold, actionID, callingLine);
	TestCase_assert(!isAxisBound(inputMap, actionID, vendorID, productID, axisID, activateThreshold, releaseThreshold), "Unexpectedly found bound axis (vendorID = %u, productID = %u, axisID = %u, activateThreshold = %f, releaseThreshold = %f, actionID = \"%s\") (line %d)", vendorID, productID, axisID, activateThreshold, releaseThreshold, actionID, callingLine);
}

static void verifyAxisBound(InputMap * inputMap, Atom actionID, int vendorID, int productID, unsigned int axisID, float activateThreshold, float releaseThreshold, int callingLine) {
	TestCase_assert(InputMap_isPhysicalAxisActionBound(inputMap, actionID, vendorID, productID, axisID), "axis unexpectedly reported as unbound (vendorID = %u, productID = %u, axisID = %u, activateThreshold = %f, releaseThreshold = %f, actionID = \"%s\") (line %d)", vendorID, productID, axisID, activateThreshold, releaseThreshold, actionID, callingLine);
	TestCase_assert(isAxisBound(inputMap, actionID, vendorID, productID, axisID, activateThreshold, releaseThreshold), "Couldn't find bound axis (vendorID = %u, productID = %u, axisID = %u, actionID, activateThreshold = %f, releaseThreshold = %f = \"%s\") (line %d)", vendorID, productID, axisID, activateThreshold, releaseThreshold, actionID, callingLine);
}

static void testAxisBindings() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("b"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.0f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.0f, __LINE__);
	TestCase_assert(!isAxisBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f), "Expected false but got true");
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	TestCase_assert(!isAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.0f), "Expected false but got true");
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f, __LINE__);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("b"), 0, 0, 0, 1.0f, 0.0f);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	
	InputMap_unbindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 0);
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 0.5f, 0.5f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
}

static void testSerialization() {
	InputMap inputMap;
	TestSerializationContext * context;
	jmp_buf jmpEnv;
	
	context = TestSerializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	
	call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, "input_map");
	call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "format_type", INPUT_MAP_SERIALIZATION_FORMAT_TYPE);
	call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt16, "format_version", INPUT_MAP_SERIALIZATION_FORMAT_VERSION);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings_with_modifiers");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "key_modifier_bindings");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_buttons");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_scroll");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_1d");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_2d");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "gamepad_maps");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "button_actions");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_actions");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_1d");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_2d");
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
	
	InputMap_serialize(&inputMap, context);
	
	call_virtual(finish, context);
	call_virtual(dispose, context);
	InputMap_dispose(&inputMap);
	
	context = TestSerializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	InputMap_bindKey(&inputMap, ATOM("a"), 1);
	InputMap_bindKey(&inputMap, ATOM("b"), 3);
	InputMap_bindPhysicalButtonAction(&inputMap, ATOM("a"), 1, 2, 3);
	InputMap_bindPhysicalButtonAction(&inputMap, ATOM("b"), 1, 2, 4);
	InputMap_bindPhysicalButtonAction(&inputMap, ATOM("c"), 4, 5, 6);
	InputMap_bindPhysicalAxisAction(&inputMap, ATOM("a"), 1, 2, 3, 0.5f, 0.5f);
	InputMap_bindPhysicalAxisAction(&inputMap, ATOM("b"), 1, 2, 4, 0.5f, 0.5f);
	InputMap_bindPhysicalAxisAction(&inputMap, ATOM("c"), 4, 5, 6, 1.0f, 0.0f);
	
	call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, "input_map");
		call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "format_type", INPUT_MAP_SERIALIZATION_FORMAT_TYPE);
		call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt16, "format_version", INPUT_MAP_SERIALIZATION_FORMAT_VERSION);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings");
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "action", "a");
				call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "key_code", 1);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "action", "b");
				call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "key_code", 3);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings_with_modifiers");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "key_modifier_bindings");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_buttons");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_scroll");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_1d");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_2d");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "gamepad_maps");
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->writeInt32, "vendor_id", 1);
				call_virtual(expectCall, context, __LINE__, context->vtable->writeInt32, "product_id", 2);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginDictionary, "button_bindings");
					call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "a", 3);
					call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "b", 4);
				call_virtual(expectCall, context, __LINE__, context->vtable->endDictionary);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_bindings");
					call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "action", "a");
						call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "axis_id", 3);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeFloat, "activate_threshold", 0.5f);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeFloat, "release_threshold", 0.5f);
					call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
					call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "action", "b");
						call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "axis_id", 4);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeFloat, "activate_threshold", 0.5f);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeFloat, "release_threshold", 0.5f);
					call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motion_bindings");
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->writeInt32, "vendor_id", 4);
				call_virtual(expectCall, context, __LINE__, context->vtable->writeInt32, "product_id", 5);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginDictionary, "button_bindings");
					call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "c", 6);
				call_virtual(expectCall, context, __LINE__, context->vtable->endDictionary);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_bindings");
					call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeString, "action", "c");
						call_virtual(expectCall, context, __LINE__, context->vtable->writeUInt32, "axis_id", 6);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeFloat, "activate_threshold", 1.0f);
						call_virtual(expectCall, context, __LINE__, context->vtable->writeFloat, "release_threshold", 0.0f);
					call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motion_bindings");
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "button_actions");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_actions");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_1d");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_2d");
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
	
	InputMap_serialize(&inputMap, context);
	
	call_virtual(finish, context);
	call_virtual(dispose, context);
	InputMap_dispose(&inputMap);
}

static void setUpBlankDeserializationContext(TestDeserializationContext * context) {
	call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, "input_map");
	call_virtual(expectCall, context, __LINE__, context->vtable->readString, "format_type", INPUT_MAP_SERIALIZATION_FORMAT_TYPE);
	call_virtual(expectCall, context, __LINE__, context->vtable->readUInt16, "format_version", INPUT_MAP_SERIALIZATION_FORMAT_VERSION);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings_with_modifiers", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "key_modifier_bindings", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_buttons", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_scroll", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_1d", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_2d", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "gamepad_maps", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "button_actions", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_actions", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_1d", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_2d", 0);
	call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
}

static void verifyBlankInputMap(InputMap * inputMap, unsigned int lineNumber) {
	TestCase_assert(inputMap != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->keyboardBindingCount == 0, "Expected 0 but got %u (%u)", inputMap->keyboardBindingCount, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMapCount == 0, "Expected 0 but got %u (%u)", inputMap->gamepadPhysicalMapCount, lineNumber);
}

static void setUpBasicDeserializationContext(TestDeserializationContext * context) {
	call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, "input_map");
		call_virtual(expectCall, context, __LINE__, context->vtable->readString, "format_type", INPUT_MAP_SERIALIZATION_FORMAT_TYPE);
		call_virtual(expectCall, context, __LINE__, context->vtable->readUInt16, "format_version", INPUT_MAP_SERIALIZATION_FORMAT_VERSION);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings", 2);
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->readString, "action", "a");
				call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "key_code", 1);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->readString, "action", "b");
				call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "key_code", 3);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "keyboard_bindings_with_modifiers", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "key_modifier_bindings", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_buttons", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_scroll", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_1d", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "mouse_motion_2d", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "gamepad_maps", 2);
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->readInt32, "vendor_id", 1);
				call_virtual(expectCall, context, __LINE__, context->vtable->readInt32, "product_id", 2);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginDictionary, "button_bindings", 2);
					call_virtual(expectCall, context, __LINE__, context->vtable->readNextDictionaryKey, "a");
					call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "a", 3);
					call_virtual(expectCall, context, __LINE__, context->vtable->readNextDictionaryKey, "b");
					call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "b", 4);
				call_virtual(expectCall, context, __LINE__, context->vtable->endDictionary);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_bindings", 2);
					call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
						call_virtual(expectCall, context, __LINE__, context->vtable->readString, "action", "a");
						call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "axis_id", 3);
						call_virtual(expectCall, context, __LINE__, context->vtable->readFloat, "activate_threshold", 0.5f);
						call_virtual(expectCall, context, __LINE__, context->vtable->readFloat, "release_threshold", 0.5f);
					call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
					call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
						call_virtual(expectCall, context, __LINE__, context->vtable->readString, "action", "b");
						call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "axis_id", 4);
						call_virtual(expectCall, context, __LINE__, context->vtable->readFloat, "activate_threshold", 0.5f);
						call_virtual(expectCall, context, __LINE__, context->vtable->readFloat, "release_threshold", 0.5f);
					call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motion_bindings", 0);
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
			call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
				call_virtual(expectCall, context, __LINE__, context->vtable->readInt32, "vendor_id", 4);
				call_virtual(expectCall, context, __LINE__, context->vtable->readInt32, "product_id", 5);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginDictionary, "button_bindings", 1);
					call_virtual(expectCall, context, __LINE__, context->vtable->readNextDictionaryKey, "c");
					call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "c", 6);
				call_virtual(expectCall, context, __LINE__, context->vtable->endDictionary);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_bindings", 1);
					call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, NULL);
						call_virtual(expectCall, context, __LINE__, context->vtable->readString, "action", "c");
						call_virtual(expectCall, context, __LINE__, context->vtable->readUInt32, "axis_id", 6);
						call_virtual(expectCall, context, __LINE__, context->vtable->readFloat, "activate_threshold", 1.0f);
						call_virtual(expectCall, context, __LINE__, context->vtable->readFloat, "release_threshold", 0.0f);
					call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
				call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motion_bindings", 0);
				call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
			call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "button_actions", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_actions", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_1d", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
		call_virtual(expectCall, context, __LINE__, context->vtable->beginArray, "axis_motions_2d", 0);
		call_virtual(expectCall, context, __LINE__, context->vtable->endArray);
	call_virtual(expectCall, context, __LINE__, context->vtable->endStructure);
}

static void verifyBasicInputMap(InputMap * inputMap, unsigned int lineNumber) {
	TestCase_assert(inputMap != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->keyboardBindingCount == 2, "Expected 2 but got %u (%u)", inputMap->keyboardBindingCount, lineNumber);
	TestCase_assert(inputMap->keyboardBindings != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->keyboardBindings[0].actionID == ATOM("a"), "Expected \"a\" (%p) but got \"%s\" (%p) (%u)", ATOM("a"), inputMap->keyboardBindings[0].actionID, inputMap->keyboardBindings[0].actionID, lineNumber);
	TestCase_assert(inputMap->keyboardBindings[0].keyCode == 1, "Expected 1 but got %u (%u)", inputMap->keyboardBindings[0].keyCode, lineNumber);
	TestCase_assert(inputMap->keyboardBindings[1].actionID == ATOM("b"), "Expected \"a\" (%p) but got \"%s\" (%p) (%u)", ATOM("b"), inputMap->keyboardBindings[1].actionID, inputMap->keyboardBindings[1].actionID, lineNumber);
	TestCase_assert(inputMap->keyboardBindings[1].keyCode == 3, "Expected 3 but got %u (%u)", inputMap->keyboardBindings[1].keyCode, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMapCount == 2, "Expected 2 but got %u (%u)", inputMap->gamepadPhysicalMapCount, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].vendorID == 1, "Expected 1 but got %d (%u)", inputMap->gamepadPhysicalMaps[0].vendorID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].productID == 2, "Expected 2 but got %d (%u)", inputMap->gamepadPhysicalMaps[0].productID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].buttonActionBindingCount == 2, "Expected 2 but got %d (%u)", inputMap->gamepadPhysicalMaps[0].buttonActionBindingCount, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].buttonActionBindings != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].buttonActionBindings[0].actionID == ATOM("a"), "Expected \"a\" (%p) but got \"%s\" (%p) (%u)", ATOM("a"), inputMap->gamepadPhysicalMaps[0].buttonActionBindings[0].actionID, inputMap->gamepadPhysicalMaps[0].buttonActionBindings[0].actionID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].buttonActionBindings[0].buttonID == 3, "Expected 3 but got %u (%u)", inputMap->gamepadPhysicalMaps[0].buttonActionBindings[0].buttonID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].buttonActionBindings[1].actionID == ATOM("b"), "Expected \"b\" (%p) but got \"%s\" (%p) (%u)", ATOM("b"), inputMap->gamepadPhysicalMaps[0].buttonActionBindings[1].actionID, inputMap->gamepadPhysicalMaps[0].buttonActionBindings[1].actionID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].buttonActionBindings[1].buttonID == 4, "Expected 4 but got %u (%u)", inputMap->gamepadPhysicalMaps[0].buttonActionBindings[1].buttonID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindingCount == 2, "Expected 2 but got %d (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindingCount, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].actionID == ATOM("a"), "Expected \"a\" (%p) but got \"%s\" (%p) (%u)", ATOM("a"), inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].actionID, inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].actionID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].axisID == 3, "Expected 3 but got %u (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].axisID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].activateThreshold == 0.5f, "Expected 0.5 but got %f (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].activateThreshold, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].releaseThreshold == 0.5f, "Expected 0.5 but got %f (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindings[0].releaseThreshold, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].actionID == ATOM("b"), "Expected \"b\" (%p) but got \"%s\" (%p) (%u)", ATOM("b"), inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].actionID, inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].actionID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].axisID == 4, "Expected 4 but got %u (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].axisID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].activateThreshold == 0.5f, "Expected 0.5 but got %f (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].activateThreshold, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].releaseThreshold == 0.5f, "Expected 0.5 but got %f (%u)", inputMap->gamepadPhysicalMaps[0].axisActionBindings[1].releaseThreshold, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].vendorID == 4, "Expected 4 but got %d (%u)", inputMap->gamepadPhysicalMaps[1].vendorID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].productID == 5, "Expected 5 but got %d (%u)", inputMap->gamepadPhysicalMaps[1].productID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].buttonActionBindingCount == 1, "Expected 1 but got %d (%u)", inputMap->gamepadPhysicalMaps[1].buttonActionBindingCount, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].buttonActionBindings != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].buttonActionBindings[0].actionID == ATOM("c"), "Expected \"c\" (%p) but got \"%s\" (%p) (%u)", ATOM("c"), inputMap->gamepadPhysicalMaps[1].buttonActionBindings[0].actionID, inputMap->gamepadPhysicalMaps[1].buttonActionBindings[0].actionID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].buttonActionBindings[0].buttonID == 6, "Expected 6 but got %u (%u)", inputMap->gamepadPhysicalMaps[1].buttonActionBindings[0].buttonID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].axisActionBindingCount == 1, "Expected 1 but got %d (%u)", inputMap->gamepadPhysicalMaps[1].axisActionBindingCount, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].axisActionBindings != NULL, "Expected non-NULL but got NULL (%u)", lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].actionID == ATOM("c"), "Expected \"c\" (%p) but got \"%s\" (%p) (%u)", ATOM("c"), inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].actionID, inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].actionID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].axisID == 6, "Expected 6 but got %u (%u)", inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].axisID, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].activateThreshold == 1.0f, "Expected 1.0 but got %f (%u)", inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].activateThreshold, lineNumber);
	TestCase_assert(inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].releaseThreshold == 0.0f, "Expected 0.0 but got %f (%u)", inputMap->gamepadPhysicalMaps[1].axisActionBindings[0].releaseThreshold, lineNumber);
}

static void testDeserialization() {
	InputMap inputMap, * inputMapPtr;
	TestDeserializationContext * context;
	jmp_buf jmpEnv;
	bool success;
	unsigned int failIndex;
	
	context = TestDeserializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	setUpBlankDeserializationContext(context);
	success = InputMap_loadSerializedData(&inputMap, context, false);
	call_virtual(finish, context);
	call_virtual(dispose, context);
	TestCase_assert(success, "Expected true but got false");
	verifyBlankInputMap(&inputMap, __LINE__);
	InputMap_dispose(&inputMap);
	
	context = TestDeserializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	setUpBlankDeserializationContext(context);
	inputMapPtr = InputMap_deserialize(context, false);
	call_virtual(finish, context);
	call_virtual(dispose, context);
	verifyBlankInputMap(inputMapPtr, __LINE__);
	InputMap_dispose(inputMapPtr);
	
	context = TestDeserializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	setUpBasicDeserializationContext(context);
	success = InputMap_loadSerializedData(&inputMap, context, false);
	call_virtual(finish, context);
	call_virtual(dispose, context);
	TestCase_assert(success, "Expected true but got false");
	verifyBasicInputMap(&inputMap, __LINE__);
	InputMap_dispose(&inputMap);
	
	context = TestDeserializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	setUpBasicDeserializationContext(context);
	inputMapPtr = InputMap_deserialize(context, false);
	call_virtual(finish, context);
	call_virtual(dispose, context);
	verifyBasicInputMap(inputMapPtr, __LINE__);
	InputMap_dispose(inputMapPtr);
	
	for (failIndex = 0; failIndex < 56; failIndex++) {
		context = TestDeserializationContext_create(&jmpEnv);
		if (setjmp(jmpEnv) != 0) {
			TestCase_assert(false, "%s", context->error);
		}
		
		stemobject_assign_vtable(inputMap, InputMap);
		InputMap_init(&inputMap);
		setUpBasicDeserializationContext(context);
		call_virtual(failNthCall, context, failIndex, 1);
		success = InputMap_loadSerializedData(&inputMap, context, false);
		//call_virtual(finish, context);
		call_virtual(dispose, context);
		TestCase_assert(!success, "InputMap_loadSerializedData didn't return false when deserialization call %d failed", failIndex);
		InputMap_dispose(&inputMap);
		
		context = TestDeserializationContext_create(&jmpEnv);
		if (setjmp(jmpEnv) != 0) {
			TestCase_assert(false, "%s", context->error);
		}
		
		setUpBasicDeserializationContext(context);
		call_virtual(failNthCall, context, failIndex, 1);
		inputMapPtr = InputMap_deserialize(context, false);
		//call_virtual(finish, context);
		call_virtual(dispose, context);
		TestCase_assert(inputMapPtr == NULL, "InputMap_deserialize didn't return NULL when deserialization call %d failed", failIndex);
	}
	
	context = TestDeserializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	stemobject_assign_vtable(inputMap, InputMap);
	InputMap_init(&inputMap);
	call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, "input_map");
	call_virtual(expectCall, context, __LINE__, context->vtable->readString, "format_type", INPUT_MAP_SERIALIZATION_FORMAT_TYPE);
	call_virtual(expectCall, context, __LINE__, context->vtable->readUInt16, "format_version", INPUT_MAP_SERIALIZATION_FORMAT_VERSION + 1);
	InputMap_loadSerializedData(&inputMap, context, false);
	call_virtual(finish, context);
	call_virtual(dispose, context);
	TestCase_assert(!success, "InputMap_loadSerializedData didn't return false when format version was too new");
	InputMap_dispose(&inputMap);
	
	context = TestDeserializationContext_create(&jmpEnv);
	if (setjmp(jmpEnv) != 0) {
		TestCase_assert(false, "%s", context->error);
	}
	call_virtual(expectCall, context, __LINE__, context->vtable->beginStructure, "input_map");
	call_virtual(expectCall, context, __LINE__, context->vtable->readString, "format_type", INPUT_MAP_SERIALIZATION_FORMAT_TYPE);
	call_virtual(expectCall, context, __LINE__, context->vtable->readUInt16, "format_version", INPUT_MAP_SERIALIZATION_FORMAT_VERSION + 1);
	inputMapPtr = InputMap_deserialize(context, false);
	call_virtual(finish, context);
	call_virtual(dispose, context);
	TestCase_assert(inputMapPtr == NULL, "InputMap_deserialize didn't return NULL when format version was too new");
}

static void testUnbindAllInputsForAction() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	InputMap_bindKey(inputMap, ATOM("a"), 1);
	InputMap_bindKey(inputMap, ATOM("a"), 2);
	InputMap_bindKey(inputMap, ATOM("b"), 1);
	
	InputMap_bindKeyModifier(inputMap, ATOM("a"), 1);
	InputMap_bindKeyModifier(inputMap, ATOM("a"), 2);
	InputMap_bindKeyModifier(inputMap, ATOM("b"), 1);
	
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 0);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 1);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("b"), 0, 0, 0);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f);
	
	verifyKeyBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllKeysForAction(inputMap, ATOM("a"));
	verifyKeyNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyNotBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllKeysForAction(inputMap, ATOM("b"));
	verifyKeyNotBound(inputMap, ATOM("b"), 1, __LINE__);
	
	verifyKeyModifierBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllKeyModifiersForAction(inputMap, ATOM("a"));
	verifyKeyModifierNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierNotBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllKeyModifiersForAction(inputMap, ATOM("b"));
	verifyKeyModifierNotBound(inputMap, ATOM("b"), 1, __LINE__);
	
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	InputMap_unbindAllPhysicalButtonsForAction(inputMap, ATOM("a"));
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	InputMap_unbindAllPhysicalButtonsForAction(inputMap, ATOM("b"));
	verifyButtonNotBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllPhysicalAxesForAction(inputMap, ATOM("a"));
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllPhysicalAxesForAction(inputMap, ATOM("b"));
	verifyAxisNotBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	
	InputMap_dispose(inputMap);
}

static void testUnbindAllInputsOnDeviceForAction() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 0);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 1);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("b"), 0, 0, 0);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 1, 1, 0);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 1, 1, 1);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("b"), 1, 1, 0);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 1, 1, 0, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("b"), 1, 1, 0, -1.0f, 0.0f);
	
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	InputMap_unbindAllPhysicalButtonsOnDeviceForAction(inputMap, 0, 0, ATOM("a"));
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	InputMap_unbindAllPhysicalButtonsOnDeviceForAction(inputMap, 0, 0, ATOM("b"));
	verifyButtonNotBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllPhysicalAxesOnDeviceForAction(inputMap, 0, 0, ATOM("a"));
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllPhysicalAxesOnDeviceForAction(inputMap, 0, 0, ATOM("b"));
	verifyAxisNotBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	
	verifyButtonBound(inputMap, ATOM("a"), 1, 1, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 1, 1, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 1, 1, 0, __LINE__);
	InputMap_unbindAllPhysicalButtonsOnDeviceForAction(inputMap, 1, 1, ATOM("a"));
	verifyButtonNotBound(inputMap, ATOM("a"), 1, 1, 0, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("a"), 1, 1, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 1, 1, 0, __LINE__);
	InputMap_unbindAllPhysicalButtonsOnDeviceForAction(inputMap, 1, 1, ATOM("b"));
	verifyButtonNotBound(inputMap, ATOM("b"), 1, 1, 0, __LINE__);
	
	verifyAxisBound(inputMap, ATOM("a"), 1, 1, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 1, 1, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllPhysicalAxesOnDeviceForAction(inputMap, 1, 1, ATOM("a"));
	verifyAxisNotBound(inputMap, ATOM("a"), 1, 1, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("a"), 1, 1, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 1, 1, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllPhysicalAxesOnDeviceForAction(inputMap, 1, 1, ATOM("b"));
	verifyAxisNotBound(inputMap, ATOM("b"), 1, 1, 0, -1.0f, 0.0f, __LINE__);
	
	InputMap_dispose(inputMap);
}

static void testUnbindAllActionsForInput() {
	InputMap * inputMap;
	
	inputMap = InputMap_create();
	InputMap_bindKey(inputMap, ATOM("a"), 1);
	InputMap_bindKey(inputMap, ATOM("a"), 2);
	InputMap_bindKey(inputMap, ATOM("b"), 1);
	
	InputMap_bindKeyModifier(inputMap, ATOM("a"), 1);
	InputMap_bindKeyModifier(inputMap, ATOM("a"), 2);
	InputMap_bindKeyModifier(inputMap, ATOM("b"), 1);
	
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 0);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("a"), 0, 0, 1);
	InputMap_bindPhysicalButtonAction(inputMap, ATOM("b"), 0, 0, 0);
	
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 0, -1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("a"), 0, 0, 1, -1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("c"), 0, 0, 0, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("c"), 0, 0, 1, 1.0f, 0.0f);
	InputMap_bindPhysicalAxisAction(inputMap, ATOM("d"), 0, 0, 0, 1.0f, 0.0f);
	
	verifyKeyBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllActionsForKey(inputMap, 1);
	verifyKeyNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyNotBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllActionsForKey(inputMap, 2);
	verifyKeyNotBound(inputMap, ATOM("a"), 2, __LINE__);
	
	verifyKeyModifierBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllActionsForKeyModifier(inputMap, 1);
	verifyKeyModifierNotBound(inputMap, ATOM("a"), 1, __LINE__);
	verifyKeyModifierBound(inputMap, ATOM("a"), 2, __LINE__);
	verifyKeyModifierNotBound(inputMap, ATOM("b"), 1, __LINE__);
	InputMap_unbindAllActionsForKeyModifier(inputMap, 2);
	verifyKeyModifierNotBound(inputMap, ATOM("a"), 2, __LINE__);
	
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	InputMap_unbindAllActionsForPhysicalButton(inputMap, 0, 0, 0);
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 0, __LINE__);
	verifyButtonBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	verifyButtonNotBound(inputMap, ATOM("b"), 0, 0, 0, __LINE__);
	InputMap_unbindAllActionsForPhysicalButton(inputMap, 0, 0, 1);
	verifyButtonNotBound(inputMap, ATOM("a"), 0, 0, 1, __LINE__);
	
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, -1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllActionsForPhysicalAxis(inputMap, 0, 0, 0, false);
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("a"), 0, 0, 1, -1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("b"), 0, 0, 0, -1.0f, 0.0f, __LINE__);
	InputMap_unbindAllActionsForPhysicalAxis(inputMap, 0, 0, 1, false);
	verifyAxisNotBound(inputMap, ATOM("a"), 0, 0, 1, -1.0f, 0.0f, __LINE__);
	
	verifyAxisBound(inputMap, ATOM("c"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("c"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("d"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	InputMap_unbindAllActionsForPhysicalAxis(inputMap, 0, 0, 0, true);
	verifyAxisNotBound(inputMap, ATOM("c"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	verifyAxisBound(inputMap, ATOM("c"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	verifyAxisNotBound(inputMap, ATOM("d"), 0, 0, 0, 1.0f, 0.0f, __LINE__);
	InputMap_unbindAllActionsForPhysicalAxis(inputMap, 0, 0, 1, true);
	verifyAxisNotBound(inputMap, ATOM("c"), 0, 0, 1, 1.0f, 0.0f, __LINE__);
	
	InputMap_dispose(inputMap);
}

TEST_SUITE(InputMapTest,
           testInit,
           testKeyboardBindings,
           testKeyboardBindingsWithModifiers,
           testKeyModifierBindings,
           testButtonBindings,
           testAxisBindings,
           testSerialization,
           testDeserialization,
           testUnbindAllInputsForAction,
           testUnbindAllInputsOnDeviceForAction,
           testUnbindAllActionsForInput)
