/* Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "serialization/TestDeserializationContext.h" #include #include #include #include #define stemobject_implementation TestDeserializationContext stemobject_vtable_begin(); stemobject_vtable_entry(dispose); stemobject_vtable_entry(beginStructure); stemobject_vtable_entry(beginDictionary); stemobject_vtable_entry(beginArray); stemobject_vtable_entry(endStructure); stemobject_vtable_entry(endDictionary); stemobject_vtable_entry(endArray); stemobject_vtable_entry(readBoolean); stemobject_vtable_entry(readInt8); stemobject_vtable_entry(readUInt8); stemobject_vtable_entry(readInt16); stemobject_vtable_entry(readUInt16); stemobject_vtable_entry(readInt32); stemobject_vtable_entry(readUInt32); stemobject_vtable_entry(readInt64); stemobject_vtable_entry(readUInt64); stemobject_vtable_entry(readFloat); stemobject_vtable_entry(readDouble); stemobject_vtable_entry(readFixed16_16); stemobject_vtable_entry(readEnumeration); stemobject_vtable_entry(readEnumeration8); stemobject_vtable_entry(readEnumeration16); stemobject_vtable_entry(readEnumeration32); stemobject_vtable_entry(readBitfield8); stemobject_vtable_entry(readBitfield16); stemobject_vtable_entry(readBitfield32); stemobject_vtable_entry(readBitfield64); stemobject_vtable_entry(readString); stemobject_vtable_entry(readStringNullable); stemobject_vtable_entry(readBlob); stemobject_vtable_entry(readNextDictionaryKey); stemobject_vtable_entry(hasDictionaryKey); stemobject_vtable_entry(expectCall); stemobject_vtable_entry(failNthCall); stemobject_vtable_entry(finish); stemobject_vtable_entry(reset); stemobject_vtable_end(); TestDeserializationContext * TestDeserializationContext_create(jmp_buf * sequenceBreakJmpEnv) { stemobject_create_implementation(init, sequenceBreakJmpEnv) } bool TestDeserializationContext_init(TestDeserializationContext * self, jmp_buf * sequenceBreakJmpEnv) { call_super(init, self); self->sequenceBreakJmpEnv = sequenceBreakJmpEnv; self->error[0] = '\x00'; self->callIndexToFail = UINT_MAX; self->failStatus = 0; self->expectedCalls = NULL; self->numExpectedCalls = 0; self->nextExpectedCallIndex = 0; return true; } void TestDeserializationContext_dispose(TestDeserializationContext * self) { unsigned int expectedCallIndex; for (expectedCallIndex = 0; expectedCallIndex < self->numExpectedCalls; expectedCallIndex++) { free(self->expectedCalls[expectedCallIndex].additionalArgs); } free(self->expectedCalls); call_super(dispose, self); } static char * functionNameForPtr(TestDeserializationContext * self, void * functionPtr) { #define tryFunctionName(function) if (functionPtr == self->vtable->function) {return #function;} tryFunctionName(beginStructure) tryFunctionName(beginDictionary) tryFunctionName(beginArray) tryFunctionName(endStructure) tryFunctionName(endDictionary) tryFunctionName(endArray) tryFunctionName(readBoolean) tryFunctionName(readInt8) tryFunctionName(readUInt8) tryFunctionName(readInt16) tryFunctionName(readUInt16) tryFunctionName(readInt32) tryFunctionName(readUInt32) tryFunctionName(readInt64) tryFunctionName(readUInt64) tryFunctionName(readFloat) tryFunctionName(readDouble) tryFunctionName(readFixed16_16) tryFunctionName(readEnumeration) tryFunctionName(readEnumeration8) tryFunctionName(readEnumeration16) tryFunctionName(readEnumeration32) tryFunctionName(readBitfield8) tryFunctionName(readBitfield16) tryFunctionName(readBitfield32) tryFunctionName(readBitfield64) tryFunctionName(readString) tryFunctionName(readStringNullable) tryFunctionName(readBlob) tryFunctionName(readNextDictionaryKey) tryFunctionName(hasDictionaryKey) #undef tryFunctionName return ""; } static void verifyCallIsInSequence(TestDeserializationContext * self, void * functionPtr, ...) { if (self->nextExpectedCallIndex >= self->numExpectedCalls) { snprintf(self->error, sizeof(self->error), "Additional function %s called after expected end of calls", functionNameForPtr(self, functionPtr)); longjmp(*self->sequenceBreakJmpEnv, 1); } if (self->expectedCalls[self->nextExpectedCallIndex].functionPtr != functionPtr) { snprintf(self->error, sizeof(self->error), "Function %s called when %s was expected (index %u, line %u)", functionNameForPtr(self, functionPtr), functionNameForPtr(self, self->expectedCalls[self->nextExpectedCallIndex].functionPtr), self->nextExpectedCallIndex, self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 2); } va_list args; va_start(args, functionPtr); if (functionPtr != self->vtable->endStructure && functionPtr != self->vtable->endDictionary && functionPtr != self->vtable->endArray && functionPtr != self->vtable->readNextDictionaryKey) { char * key = va_arg(args, char *); if (((self->expectedCalls[self->nextExpectedCallIndex].key == NULL || key == NULL) && self->expectedCalls[self->nextExpectedCallIndex].key != key) || (self->expectedCalls[self->nextExpectedCallIndex].key != NULL && key != NULL && strcmp(self->expectedCalls[self->nextExpectedCallIndex].key, key))) { snprintf(self->error, sizeof(self->error), "Arg 2 to call %d (%s) was expected to be \"%s\", but was \"%s\" instead (line %u)", self->nextExpectedCallIndex, functionNameForPtr(self, functionPtr), self->expectedCalls[self->nextExpectedCallIndex].key, key, self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 3); } } if (functionPtr == self->vtable->readEnumeration || functionPtr == self->vtable->readEnumeration8 || functionPtr == self->vtable->readEnumeration16 || functionPtr == self->vtable->readEnumeration32) { unsigned int valueCount = va_arg(args, unsigned int); Serialization_enumKeyValue * values = va_arg(args, Serialization_enumKeyValue *); if (self->expectedCalls[self->nextExpectedCallIndex].additionalArgs[0].count != valueCount) { snprintf(self->error, sizeof(self->error), "Arg 2 to call %d (%s) was expected to be %u, but was %u instead (line %u)", self->nextExpectedCallIndex, functionNameForPtr(self, functionPtr), self->expectedCalls[self->nextExpectedCallIndex].additionalArgs[0].count, valueCount, self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 3); } Serialization_enumKeyValue * expectedValues = self->expectedCalls[self->nextExpectedCallIndex].additionalArgs[1].values; for (unsigned int valueIndex = 0; valueIndex < valueCount; valueIndex++) { if (expectedValues[valueIndex].value != values[valueIndex].value || values[valueIndex].key == NULL || strcmp(expectedValues[valueIndex].key, values[valueIndex].key)) { snprintf(self->error, sizeof(self->error), "Enumeration key/value mismatch at index %u in call %d (%s); expected {\"%s\", %d} but got {\"%s\", %d} (line %u)", valueIndex, self->nextExpectedCallIndex, functionNameForPtr(self, functionPtr), expectedValues[valueIndex].key, expectedValues[valueIndex].value, values[valueIndex].key, values[valueIndex].value, self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 3); } } } else if (functionPtr == self->vtable->readBitfield8 || functionPtr == self->vtable->readBitfield16 || functionPtr == self->vtable->readBitfield32 || functionPtr == self->vtable->readBitfield64) { unsigned int bitCount = va_arg(args, unsigned int); Serialization_bitName * bitNames = va_arg(args, Serialization_bitName *); if (self->expectedCalls[self->nextExpectedCallIndex].additionalArgs[0].count != bitCount) { snprintf(self->error, sizeof(self->error), "Arg 2 to call %d (%s) was expected to be %u, but was %u instead (line %u)", self->nextExpectedCallIndex, functionNameForPtr(self, functionPtr), self->expectedCalls[self->nextExpectedCallIndex].additionalArgs[0].count, bitCount, self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 3); } Serialization_bitName * expectedBitNames = self->expectedCalls[self->nextExpectedCallIndex].additionalArgs[1].bitNames; for (unsigned int bitIndex = 0; bitIndex < bitCount; bitIndex++) { if (expectedBitNames[bitIndex].index != bitNames[bitIndex].index || bitNames[bitIndex].name == NULL || strcmp(expectedBitNames[bitIndex].name, bitNames[bitIndex].name)) { snprintf(self->error, sizeof(self->error), "Bit name/index mismatch at index %u in call %d (%s); expected {\"%s\", %d} but got {\"%s\", %d} (line %u)", bitIndex, self->nextExpectedCallIndex, functionNameForPtr(self, functionPtr), expectedBitNames[bitIndex].name, expectedBitNames[bitIndex].index, bitNames[bitIndex].name, bitNames[bitIndex].index, self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 3); } } } va_end(args); self->nextExpectedCallIndex++; } static void failIfRequested(TestDeserializationContext * self) { if (self->nextExpectedCallIndex - 1 == self->callIndexToFail) { self->status = self->failStatus; if (self->jmpBuf != NULL) { longjmp(*self->jmpBuf, self->status); } } } void TestDeserializationContext_beginStructure(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->beginStructure, key); failIfRequested(self); } size_t TestDeserializationContext_beginDictionary(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->beginDictionary, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.sizeValue; } size_t TestDeserializationContext_beginArray(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->beginArray, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.sizeValue; } void TestDeserializationContext_endStructure(TestDeserializationContext * self) { verifyCallIsInSequence(self, self->vtable->endStructure); failIfRequested(self); } void TestDeserializationContext_endDictionary(TestDeserializationContext * self) { verifyCallIsInSequence(self, self->vtable->endDictionary); failIfRequested(self); } void TestDeserializationContext_endArray(TestDeserializationContext * self) { verifyCallIsInSequence(self, self->vtable->endArray); failIfRequested(self); } bool TestDeserializationContext_readBoolean(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readBoolean, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.boolValue; } int8_t TestDeserializationContext_readInt8(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readInt8, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int8Value; } uint8_t TestDeserializationContext_readUInt8(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readUInt8, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint8Value; } int16_t TestDeserializationContext_readInt16(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readInt16, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int16Value; } uint16_t TestDeserializationContext_readUInt16(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readUInt16, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint16Value; } int32_t TestDeserializationContext_readInt32(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readInt32, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int32Value; } uint32_t TestDeserializationContext_readUInt32(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readUInt32, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint32Value; } int64_t TestDeserializationContext_readInt64(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readInt64, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int64Value; } uint64_t TestDeserializationContext_readUInt64(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readUInt64, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint64Value; } float TestDeserializationContext_readFloat(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readFloat, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.floatValue; } double TestDeserializationContext_readDouble(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readDouble, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.doubleValue; } fixed16_16 TestDeserializationContext_readFixed16_16(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readFixed16_16, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.fixed16_16Value; } int TestDeserializationContext_readEnumeration(TestDeserializationContext * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values) { verifyCallIsInSequence(self, self->vtable->readEnumeration, key, valueCount, values); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.intValue; } int8_t TestDeserializationContext_readEnumeration8(TestDeserializationContext * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values) { verifyCallIsInSequence(self, self->vtable->readEnumeration, key, valueCount, values); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int8Value; } int16_t TestDeserializationContext_readEnumeration16(TestDeserializationContext * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values) { verifyCallIsInSequence(self, self->vtable->readEnumeration, key, valueCount, values); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int16Value; } int32_t TestDeserializationContext_readEnumeration32(TestDeserializationContext * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values) { verifyCallIsInSequence(self, self->vtable->readEnumeration, key, valueCount, values); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.int32Value; } uint8_t TestDeserializationContext_readBitfield8(TestDeserializationContext * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames) { verifyCallIsInSequence(self, self->vtable->readBitfield8, key, bitNameCount, bitNames); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint8Value; } uint16_t TestDeserializationContext_readBitfield16(TestDeserializationContext * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames) { verifyCallIsInSequence(self, self->vtable->readBitfield16, key, bitNameCount, bitNames); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint16Value; } uint32_t TestDeserializationContext_readBitfield32(TestDeserializationContext * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames) { verifyCallIsInSequence(self, self->vtable->readBitfield32, key, bitNameCount, bitNames); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint32Value; } uint64_t TestDeserializationContext_readBitfield64(TestDeserializationContext * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames) { verifyCallIsInSequence(self, self->vtable->readBitfield64, key, bitNameCount, bitNames); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.uint64Value; } const char * TestDeserializationContext_readString(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readString, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.stringValue; } const char * TestDeserializationContext_readStringNullable(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->readStringNullable, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.stringValue; } const void * TestDeserializationContext_readBlob(TestDeserializationContext * self, const char * key, size_t * outLength) { verifyCallIsInSequence(self, self->vtable->readBlob, key); failIfRequested(self); if (outLength != NULL) { *outLength = self->expectedCalls[self->nextExpectedCallIndex - 1].additionalArgs[0].length; } return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.stringValue; } const char * TestDeserializationContext_readNextDictionaryKey(TestDeserializationContext * self) { verifyCallIsInSequence(self, self->vtable->readNextDictionaryKey); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.stringValue; } bool TestDeserializationContext_hasDictionaryKey(TestDeserializationContext * self, const char * key) { verifyCallIsInSequence(self, self->vtable->hasDictionaryKey, key); failIfRequested(self); return self->expectedCalls[self->nextExpectedCallIndex - 1].returnValue.boolValue; } void TestDeserializationContext_expectCall(TestDeserializationContext * self, unsigned int lineNumber, void * functionPtr, ...) { va_list args; self->expectedCalls = realloc(self->expectedCalls, sizeof(struct TestDeserializationContext_expectedCall) * (self->numExpectedCalls + 1)); self->expectedCalls[self->numExpectedCalls].functionPtr = functionPtr; self->expectedCalls[self->numExpectedCalls].lineNumber = lineNumber; self->expectedCalls[self->numExpectedCalls].additionalArgs = NULL; va_start(args, functionPtr); if (functionPtr != self->vtable->endStructure && functionPtr != self->vtable->endDictionary && functionPtr != self->vtable->endArray && functionPtr != self->vtable->readNextDictionaryKey) { self->expectedCalls[self->numExpectedCalls].key = va_arg(args, char *); } if (functionPtr == self->vtable->beginDictionary || functionPtr == self->vtable->beginArray) { self->expectedCalls[self->numExpectedCalls].returnValue.sizeValue = va_arg(args, size_t); } else if (functionPtr == self->vtable->readBoolean) { self->expectedCalls[self->numExpectedCalls].returnValue.boolValue = va_arg(args, int); } else if (functionPtr == self->vtable->readInt8) { self->expectedCalls[self->numExpectedCalls].returnValue.int8Value = va_arg(args, int); } else if (functionPtr == self->vtable->readUInt8 || functionPtr == self->vtable->readBitfield8) { self->expectedCalls[self->numExpectedCalls].returnValue.uint8Value = va_arg(args, int); } else if (functionPtr == self->vtable->readInt16) { self->expectedCalls[self->numExpectedCalls].returnValue.int16Value = va_arg(args, int); } else if (functionPtr == self->vtable->readUInt16 || functionPtr == self->vtable->readBitfield16) { self->expectedCalls[self->numExpectedCalls].returnValue.uint16Value = va_arg(args, int); } else if (functionPtr == self->vtable->readInt32) { self->expectedCalls[self->numExpectedCalls].returnValue.int32Value = va_arg(args, int32_t); } else if (functionPtr == self->vtable->readUInt32 || functionPtr == self->vtable->readBitfield32) { self->expectedCalls[self->numExpectedCalls].returnValue.uint32Value = va_arg(args, uint32_t); } else if (functionPtr == self->vtable->readInt64) { self->expectedCalls[self->numExpectedCalls].returnValue.int64Value = va_arg(args, int64_t); } else if (functionPtr == self->vtable->readUInt64 || functionPtr == self->vtable->readBitfield64) { self->expectedCalls[self->numExpectedCalls].returnValue.uint64Value = va_arg(args, uint64_t); } else if (functionPtr == self->vtable->readFloat) { self->expectedCalls[self->numExpectedCalls].returnValue.floatValue = va_arg(args, double); } else if (functionPtr == self->vtable->readDouble) { self->expectedCalls[self->numExpectedCalls].returnValue.doubleValue = va_arg(args, double); } else if (functionPtr == self->vtable->readFixed16_16) { self->expectedCalls[self->numExpectedCalls].returnValue.fixed16_16Value = va_arg(args, fixed16_16); } else if (functionPtr == self->vtable->readEnumeration) { self->expectedCalls[self->numExpectedCalls].returnValue.intValue = va_arg(args, int); } else if (functionPtr == self->vtable->readEnumeration8) { self->expectedCalls[self->numExpectedCalls].returnValue.int8Value = va_arg(args, int); } else if (functionPtr == self->vtable->readEnumeration16) { self->expectedCalls[self->numExpectedCalls].returnValue.int16Value = va_arg(args, int); } else if (functionPtr == self->vtable->readEnumeration32) { self->expectedCalls[self->numExpectedCalls].returnValue.int32Value = va_arg(args, int32_t); } else if (functionPtr == self->vtable->readString) { self->expectedCalls[self->numExpectedCalls].returnValue.stringValue = va_arg(args, char *); } else if (functionPtr == self->vtable->readStringNullable) { self->expectedCalls[self->numExpectedCalls].returnValue.stringValue = va_arg(args, char *); } else if (functionPtr == self->vtable->readBlob) { self->expectedCalls[self->numExpectedCalls].returnValue.blobValue = va_arg(args, char *); self->expectedCalls[self->numExpectedCalls].additionalArgs = malloc(sizeof(union TestDeserializationContext_additionalArg)); self->expectedCalls[self->numExpectedCalls].additionalArgs[0].length = va_arg(args, size_t); } else if (functionPtr == self->vtable->readNextDictionaryKey) { self->expectedCalls[self->numExpectedCalls].returnValue.stringValue = va_arg(args, char *); } else if (functionPtr == self->vtable->hasDictionaryKey) { self->expectedCalls[self->numExpectedCalls].returnValue.boolValue = va_arg(args, int); } if (functionPtr == self->vtable->readEnumeration || functionPtr == self->vtable->readEnumeration8 || functionPtr == self->vtable->readEnumeration16 || functionPtr == self->vtable->readEnumeration32) { self->expectedCalls[self->numExpectedCalls].additionalArgs = malloc(sizeof(union TestDeserializationContext_additionalArg) * 2); self->expectedCalls[self->numExpectedCalls].additionalArgs[0].count = va_arg(args, unsigned int); self->expectedCalls[self->numExpectedCalls].additionalArgs[1].values = va_arg(args, Serialization_enumKeyValue *); } else if (functionPtr == self->vtable->readBitfield8 || functionPtr == self->vtable->readBitfield16 || functionPtr == self->vtable->readBitfield32 || functionPtr == self->vtable->readBitfield64) { self->expectedCalls[self->numExpectedCalls].additionalArgs = malloc(sizeof(union TestDeserializationContext_additionalArg) * 2); self->expectedCalls[self->numExpectedCalls].additionalArgs[0].count = va_arg(args, unsigned int); self->expectedCalls[self->numExpectedCalls].additionalArgs[1].bitNames = va_arg(args, Serialization_bitName *); } va_end(args); self->numExpectedCalls++; } void TestDeserializationContext_failNthCall(TestDeserializationContext * self, unsigned int callIndex, int status) { self->callIndexToFail = callIndex; self->failStatus = status; } void TestDeserializationContext_finish(TestDeserializationContext * self) { if (self->nextExpectedCallIndex < self->numExpectedCalls) { snprintf(self->error, sizeof(self->error), "%d expected call%s still left in queue at end (next expected call is %s, line %u)", self->numExpectedCalls - self->nextExpectedCallIndex, self->numExpectedCalls - self->nextExpectedCallIndex == 1 ? "" : "s", functionNameForPtr(self, self->expectedCalls[self->nextExpectedCallIndex].functionPtr), self->expectedCalls[self->nextExpectedCallIndex].lineNumber); longjmp(*self->sequenceBreakJmpEnv, 4); } } void TestDeserializationContext_reset(TestDeserializationContext * self) { self->nextExpectedCallIndex = 0; self->error[0] = '\x00'; self->status = SERIALIZATION_ERROR_OK; }