aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c')
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c1087
1 files changed, 1087 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c b/metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c
new file mode 100644
index 00000000..5be165c5
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../metis_HopByHopFragmenter.c"
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <LongBow/unit-test.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+typedef struct test_data {
+ unsigned mtu;
+ MetisLogger *logger;
+ MetisHopByHopFragmenter *fragmenter;
+} TestData;
+
+static TestData *
+_createTestData(void)
+{
+ TestData *data = parcMemory_Allocate(sizeof(TestData));
+
+ data->mtu = 2000;
+
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ data->logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ metisLogger_SetLogLevel(data->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug);
+ parcLogReporter_Release(&reporter);
+ data->fragmenter = metisHopByHopFragmenter_Create(data->logger, data->mtu);
+ return data;
+}
+
+static void
+_destroyTestData(TestData **dataPtr)
+{
+ TestData *data = *dataPtr;
+ metisHopByHopFragmenter_Release(&data->fragmenter);
+ metisLogger_Release(&data->logger);
+ parcMemory_Deallocate((void **) dataPtr);
+}
+
+/*
+ * Create a well-formed packet with the given length.
+ * length is the total packet length, including fixed header.
+ */
+static uint8_t *
+_conjurePacket(size_t length)
+{
+ _HopByHopHeader header;
+ memset(&header, 0, sizeof(header));
+
+ size_t payloadLength = length - sizeof(header);
+
+ header.version = 1;
+ header.packetType = 2; // interest return -- does not require a name.
+ header.packetLength = htons(length);
+ header.headerLength = 8;
+ header.tlvType = 0;
+ header.tlvLength = htons(payloadLength);
+
+ uint8_t *packet = parcMemory_Allocate(length);
+ memcpy(packet, &header, sizeof(header));
+ return packet;
+}
+
+// ============================================================
+
+LONGBOW_TEST_RUNNER(metis_HopByHopFragmenter)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_HopByHopFragmenter)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_HopByHopFragmenter)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ============================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Receive_NotHopByHop);
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Receive_ReceiveQueueFull);
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Receive_Ok);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Send_OneMtu);
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Send_ReceiveQueueFull);
+ LONGBOW_RUN_TEST_CASE(Global, metisHopByHopFragmenter_Send_Ok);
+}
+
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ TestData *data = _createTestData();
+ longBowTestCase_SetClipBoardData(testCase, data);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _destroyTestData(&data);
+
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Create)
+{
+ // nothing really to do here, just need to make sure there's no memory leak in teardown
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ assertNotNull(data->fragmenter, "Got null fragmenter");
+}
+
+/*
+ * Receive a non-hop-by-hop packet. Should go straight in to the receive queue
+ */
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Receive_NotHopByHop)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ MetisTicks startTicks = 1111111;
+ unsigned ingressId = 77;
+
+ MetisMessage *message = metisMessage_CreateFromArray(metisTestDataV1_Interest_AllFields, sizeof(metisTestDataV1_Interest_AllFields), ingressId, startTicks, data->logger);
+ metisHopByHopFragmenter_Receive(data->fragmenter, message);
+
+ /*
+ * 1) Make a metis message out of the reassembly buffer,
+ * 2) put the message in the receive queue (discard if queue full)
+ * 3) allocate a new reassembly buffer
+ * 4) reset the parser
+ */
+
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Got null reassembled message");
+
+ assertTrue(test == message, "Message not in receive queue, expected %p got %p", (void *) message, (void *) test);
+
+ metisMessage_Release(&message);
+ metisMessage_Release(&test);
+}
+
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Receive_ReceiveQueueFull)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // create a full recieve queue
+ parcRingBuffer1x1_Release(&data->fragmenter->receiveQueue);
+ data->fragmenter->receiveQueue = parcRingBuffer1x1_Create(2, _ringBufferDestroyer);
+
+ void *fakeData = (void *) 1;
+ parcRingBuffer1x1_Put(data->fragmenter->receiveQueue, fakeData);
+
+ assertTrue(parcRingBuffer1x1_Remaining(data->fragmenter->receiveQueue) == 0, "expected queue to be full");
+
+ // === run test
+ MetisTicks startTicks = 1111111;
+ unsigned ingressId = 77;
+
+ data->fragmenter->nextReceiveFragSequenceNumber = 1;
+
+ MetisMessage *message = metisMessage_CreateFromArray(metisTestDataV1_Interest_AllFields, sizeof(metisTestDataV1_Interest_AllFields), ingressId, startTicks, data->logger);
+ metisHopByHopFragmenter_Receive(data->fragmenter, message);
+
+ // should still only be the fake data in the queue
+ void *test = NULL;
+ parcRingBuffer1x1_Get(data->fragmenter->receiveQueue, &test);
+ assertTrue(test == fakeData, "Wrong pointer, expected %p got %p", fakeData, test);
+
+ metisMessage_Release(&message);
+}
+
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Receive_Ok)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ MetisTicks startTicks = 1111111;
+ unsigned ingressId = 77;
+
+ data->fragmenter->nextReceiveFragSequenceNumber = 1;
+
+ MetisMessage *message = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Begin, sizeof(metisTestDataV1_HopByHopFrag_Begin), ingressId, startTicks, data->logger);
+ metisHopByHopFragmenter_Receive(data->fragmenter, message);
+
+ /*
+ * We should now be in the Busy state
+ */
+ assertTrue(data->fragmenter->parserState == _ParserState_Busy, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+
+ metisMessage_Release(&message);
+}
+
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Send_OneMtu)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // make a packet shorter than one MTU (so it will fit with the fragment overhead)
+ size_t length = data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+ bool success = metisHopByHopFragmenter_Send(data->fragmenter, message);
+
+ assertTrue(success, "Failed to send fragments");
+ MetisMessage *fragment = metisHopByHopFragmenter_PopSendQueue(data->fragmenter);
+ assertNotNull(fragment, "Did not find a fragment in the send queue");
+
+ // ===
+ // defragment it
+
+ metisHopByHopFragmenter_Receive(data->fragmenter, fragment);
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Should have gotten the original message back");
+ assertTrue(metisMessage_Length(test) == metisMessage_Length(message),
+ "Reconstructed message length wrong expected %zu got %zu",
+ metisMessage_Length(message),
+ metisMessage_Length(test));
+
+ // ===
+ // cleanup
+
+ metisMessage_Release(&fragment);
+ metisMessage_Release(&message);
+ metisMessage_Release(&test);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Send_ReceiveQueueFull)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // create a full send queue
+ parcRingBuffer1x1_Release(&data->fragmenter->sendQueue);
+ data->fragmenter->sendQueue = parcRingBuffer1x1_Create(2, _ringBufferDestroyer);
+
+ void *fakeData = (void *) 1;
+ parcRingBuffer1x1_Put(data->fragmenter->sendQueue, fakeData);
+
+
+ // less than 1 MTU
+ size_t length = data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+
+ bool success = metisHopByHopFragmenter_Send(data->fragmenter, message);
+ assertFalse(success, "Should have failed to send fragments");
+
+ // ===
+ // cleanup
+
+ // manually pop this off as it is not a proper MetisMessage
+ parcRingBuffer1x1_Get(data->fragmenter->sendQueue, &fakeData);
+ metisMessage_Release(&message);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+LONGBOW_TEST_CASE(Global, metisHopByHopFragmenter_Send_Ok)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // Take up 2 MTUs (minus a little for fragmentation overhead)
+ size_t length = 2 * data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+
+ bool success = metisHopByHopFragmenter_Send(data->fragmenter, message);
+ assertTrue(success, "Failed to send fragments");
+
+ // ===
+ // defragment it
+
+ MetisMessage *fragment;
+ while ((fragment = metisHopByHopFragmenter_PopSendQueue(data->fragmenter)) != NULL) {
+ metisHopByHopFragmenter_Receive(data->fragmenter, fragment);
+ metisMessage_Release(&fragment);
+ }
+ ;
+
+
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Should have gotten the original message back");
+ assertTrue(metisMessage_Length(test) == metisMessage_Length(message),
+ "Reconstructed message length wrong expected %zu got %zu",
+ metisMessage_Length(message),
+ metisMessage_Length(test));
+
+ // ===
+ // cleanup
+
+ metisMessage_Release(&message);
+ metisMessage_Release(&test);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+// ============================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _compareSequenceNumbers);
+ LONGBOW_RUN_TEST_CASE(Local, _incrementSequenceNumber);
+ LONGBOW_RUN_TEST_CASE(Local, _resetParser);
+ LONGBOW_RUN_TEST_CASE(Local, _applySequenceNumberRules_InOrder);
+ LONGBOW_RUN_TEST_CASE(Local, _applySequenceNumberRules_Early);
+ LONGBOW_RUN_TEST_CASE(Local, _applySequenceNumberRules_Late);
+ LONGBOW_RUN_TEST_CASE(Local, _finalizeReassemblyBuffer_NotFull);
+ LONGBOW_RUN_TEST_CASE(Local, _finalizeReassemblyBuffer_Full);
+ LONGBOW_RUN_TEST_CASE(Local, _appendFragmentToReassemblyBuffer_Once);
+ LONGBOW_RUN_TEST_CASE(Local, _appendFragmentToReassemblyBuffer_Multiple);
+
+ LONGBOW_RUN_TEST_CASE(Local, _receiveInIdleState_BFrame);
+ LONGBOW_RUN_TEST_CASE(Local, _receiveInIdleState_BEFrame);
+ LONGBOW_RUN_TEST_CASE(Local, _receiveInIdleState_OtherFrame);
+
+ LONGBOW_RUN_TEST_CASE(Local, _receiveInBusyState_EFrame);
+ LONGBOW_RUN_TEST_CASE(Local, _receiveInBusyState_NoFlagFrame);
+ LONGBOW_RUN_TEST_CASE(Local, _receiveInBusyState_OtherFrame);
+
+ LONGBOW_RUN_TEST_CASE(Local, _receiveFragment_IdleState);
+ LONGBOW_RUN_TEST_CASE(Local, _receiveFragment_BusyState);
+
+ LONGBOW_RUN_TEST_CASE(Local, _sendFragments_OneFragment);
+ LONGBOW_RUN_TEST_CASE(Local, _sendFragments_TwoFragments);
+ LONGBOW_RUN_TEST_CASE(Local, _sendFragments_ThreeFragments);
+ LONGBOW_RUN_TEST_CASE(Local, _sendFragments_SendQueueFull);
+
+ LONGBOW_RUN_TEST_CASE(Local, _ringBufferDestroyer);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ TestData *data = _createTestData();
+ longBowTestCase_SetClipBoardData(testCase, data);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _destroyTestData(&data);
+
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Local, _compareSequenceNumbers)
+{
+ struct test_vector {
+ uint32_t a;
+ uint32_t b;
+ int signum;
+ bool sentinel;
+ } testVectors[] = {
+ // compared to b = 0, then a = {1 ... 0x07FFFF} is greater than b
+ // compared to b = 0, then a = {0x080000 ... 0x0FFFFF} is less than b
+
+ { .a = 0x00000000, .b = 0x00000000, .signum = 0, .sentinel = false },
+ { .a = 0x00000001, .b = 0x00000000, .signum = +1, .sentinel = false },
+ { .a = 0x0007FFFF, .b = 0x00000000, .signum = +1, .sentinel = false },
+ { .a = 0x00080000, .b = 0x00000000, .signum = -1, .sentinel = false },
+ { .a = 0x000FFFFF, .b = 0x00000000, .signum = -1, .sentinel = false },
+
+ // now do the same thing but use b = 0x00040000
+ { .a = 0x00040000, .b = 0x00040000, .signum = 0, .sentinel = false },
+ { .a = 0x00040001, .b = 0x00040000, .signum = +1, .sentinel = false },
+ { .a = 0x000BFFFF, .b = 0x00040000, .signum = +1, .sentinel = false },
+ { .a = 0x000C0000, .b = 0x00040000, .signum = -1, .sentinel = false },
+ { .a = 0x0003FFFF, .b = 0x00040000, .signum = -1, .sentinel = false },
+
+ // end test set
+ { .a = 0x00000000, .b = 0x00000000, .signum = 0, .sentinel = true },
+ };
+
+ for (int i = 0; !testVectors[i].sentinel; i++) {
+ int result = _compareSequenceNumbers(testVectors[i].a, testVectors[i].b);
+
+ if (testVectors[i].signum == 0) {
+ assertTrue(result == 0, "Wrong result, expected 0 got %d index %d a 0x%08x b 0x%08x",
+ result, i, testVectors[i].a, testVectors[i].b);
+ }
+
+ if (testVectors[i].signum < 0) {
+ assertTrue(result < 0, "Wrong result, expected negative got %d index %d a 0x%08x b 0x%08x",
+ result, i, testVectors[i].a, testVectors[i].b);
+ }
+
+ if (testVectors[i].signum > 0) {
+ assertTrue(result > 0, "Wrong result, expected positive got %d index %d a 0x%08x b 0x%08x",
+ result, i, testVectors[i].a, testVectors[i].b);
+ }
+ }
+}
+
+LONGBOW_TEST_CASE(Local, _incrementSequenceNumber)
+{
+ struct test_vector {
+ uint32_t a;
+ uint32_t b;
+ bool sentinel;
+ } testVectors[] = {
+ { .a = 0x00000000, .b = 0x00000001, .sentinel = false },
+ { .a = 0x00000001, .b = 0x00000002, .sentinel = false },
+ { .a = 0x0007FFFF, .b = 0x00080000, .sentinel = false },
+ { .a = 0x000FFFFF, .b = 0x00000000, .sentinel = false },
+ // end test set
+ { .a = 0x00000000, .b = 0x00000000, .sentinel = true },
+ };
+
+ for (int i = 0; !testVectors[i].sentinel; i++) {
+ uint32_t result = _incrementSequenceNumber(testVectors[i].a, 0x000FFFFF);
+
+ assertTrue(result == testVectors[i].b, "Wrong result 0x%08X index %d, got for a 0x%08x b 0x%08x",
+ result, i, testVectors[i].a, testVectors[i].b);
+ }
+}
+
+LONGBOW_TEST_CASE(Local, _resetParser)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // put something in the buffer and set the parser state to Busy
+ data->fragmenter->parserState = _ParserState_Busy;
+ parcEventBuffer_Append(data->fragmenter->currentReceiveBuffer, data, sizeof(data));
+
+ _resetParser(data->fragmenter);
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Idle, data->fragmenter->parserState);
+
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == 0, "Wrong length, expected 0 bytes, got %zu", length);
+}
+
+LONGBOW_TEST_CASE(Local, _applySequenceNumberRules_InOrder)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ data->fragmenter->parserState = _ParserState_Busy;
+ data->fragmenter->nextReceiveFragSequenceNumber = 1000;
+
+ _HopByHopHeader header;
+ memset(&header, 0, sizeof(header));
+
+ _hopByHopHeader_SetSeqnum(&header, 1000);
+
+ _applySequenceNumberRules(data->fragmenter, &header);
+
+ // should still be in Busy mode and expecting 1001
+ assertTrue(data->fragmenter->parserState == _ParserState_Busy, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ assertTrue(data->fragmenter->nextReceiveFragSequenceNumber == 1001, "Wrong next seqnum, expected 1001 got %u", data->fragmenter->nextReceiveFragSequenceNumber);
+}
+
+LONGBOW_TEST_CASE(Local, _applySequenceNumberRules_Early)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ data->fragmenter->parserState = _ParserState_Busy;
+ data->fragmenter->nextReceiveFragSequenceNumber = 1000;
+
+ _HopByHopHeader header;
+ memset(&header, 0, sizeof(header));
+
+ _hopByHopHeader_SetSeqnum(&header, 998);
+
+ _applySequenceNumberRules(data->fragmenter, &header);
+
+ // should reset state and set next to 999
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ assertTrue(data->fragmenter->nextReceiveFragSequenceNumber == 999, "Wrong next seqnum, expected 999 got %u", data->fragmenter->nextReceiveFragSequenceNumber);
+}
+
+LONGBOW_TEST_CASE(Local, _applySequenceNumberRules_Late)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ data->fragmenter->parserState = _ParserState_Busy;
+ data->fragmenter->nextReceiveFragSequenceNumber = 1000;
+
+ _HopByHopHeader header;
+ memset(&header, 0, sizeof(header));
+
+ _hopByHopHeader_SetSeqnum(&header, 1001);
+
+ _applySequenceNumberRules(data->fragmenter, &header);
+
+ // should reset state and set next to 1002
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ assertTrue(data->fragmenter->nextReceiveFragSequenceNumber == 1002, "Wrong next seqnum, expected 1002 got %u", data->fragmenter->nextReceiveFragSequenceNumber);
+}
+
+LONGBOW_TEST_CASE(Local, _finalizeReassemblyBuffer_NotFull)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ MetisTicks startTicks = 1111111;
+ unsigned ingressId = 77;
+
+ // set up as just finished with a message, so the currentReceiveBuffer has
+ // a complete CCNx message array in it
+ data->fragmenter->parserState = _ParserState_Busy;
+ data->fragmenter->currentReceiveBufferIngressId = ingressId;
+ data->fragmenter->currentReceiveBufferStartTicks = startTicks;
+ parcEventBuffer_Append(data->fragmenter->currentReceiveBuffer, metisTestDataV1_Interest_AllFields, sizeof(metisTestDataV1_Interest_AllFields));
+
+ _finalizeReassemblyBuffer(data->fragmenter);
+
+ /*
+ * 1) Make a metis message out of the reassembly buffer,
+ * 2) put the message in the receive queue (discard if queue full)
+ * 3) allocate a new reassembly buffer
+ * 4) reset the parser
+ */
+
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Got null reassembled message");
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ assertNotNull(data->fragmenter->currentReceiveBuffer, "Current receive buffer should not be null");
+ assertTrue(parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer) == 0, "Current receive buffer should be empty, got %zu bytes", parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer));
+
+ assertTrue(metisMessage_GetIngressConnectionId(test) == ingressId, "Wrong ingress id expected %u got %u", ingressId, metisMessage_GetIngressConnectionId(test));
+ assertTrue(metisMessage_GetReceiveTime(test) == startTicks, "Wrong receive time expected %" PRIu64 " got %" PRIu64,
+ startTicks, metisMessage_GetReceiveTime(test));
+
+ metisMessage_Release(&test);
+}
+
+LONGBOW_TEST_CASE(Local, _finalizeReassemblyBuffer_Full)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ MetisTicks startTicks = 1111111;
+ unsigned ingressId = 77;
+
+ // set up as just finished with a message, so the currentReceiveBuffer has
+ // a complete CCNx message array in it
+ data->fragmenter->parserState = _ParserState_Busy;
+ data->fragmenter->currentReceiveBufferIngressId = ingressId;
+ data->fragmenter->currentReceiveBufferStartTicks = startTicks;
+ parcEventBuffer_Append(data->fragmenter->currentReceiveBuffer, metisTestDataV1_Interest_AllFields, sizeof(metisTestDataV1_Interest_AllFields));
+
+ // create a full recieve queue
+ parcRingBuffer1x1_Release(&data->fragmenter->receiveQueue);
+ data->fragmenter->receiveQueue = parcRingBuffer1x1_Create(2, _ringBufferDestroyer);
+
+ void *fakeData = (void *) 1;
+ parcRingBuffer1x1_Put(data->fragmenter->receiveQueue, fakeData);
+
+ assertTrue(parcRingBuffer1x1_Remaining(data->fragmenter->receiveQueue) == 0, "expected queue to be full");
+
+ /*
+ * Call with a full receive queue
+ */
+ _finalizeReassemblyBuffer(data->fragmenter);
+
+ void *test = NULL;
+ parcRingBuffer1x1_Get(data->fragmenter->receiveQueue, &test);
+ assertTrue(test == fakeData, "Wrong pointer, expected %p got %p", fakeData, test);
+
+ // teardown should show no memory leak
+}
+
+LONGBOW_TEST_CASE(Local, _appendFragmentToReassemblyBuffer_Once)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+
+ MetisMessage *fragment = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Begin, sizeof(metisTestDataV1_HopByHopFrag_Begin), connid, receiveTime, data->logger);
+ _appendFragmentToReassemblyBuffer(data->fragmenter, fragment);
+
+ int fragmentLength = sizeof(metisTestDataV1_HopByHopFrag_Begin_Fragment);
+
+ assertTrue(parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer) == fragmentLength,
+ "currentReceiveBuffer wrong lenth, expected %d got %zu",
+ fragmentLength,
+ parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer));
+
+ uint8_t *test = parcEventBuffer_Pullup(data->fragmenter->currentReceiveBuffer, -1);
+ assertTrue(memcmp(test, metisTestDataV1_HopByHopFrag_Begin_Fragment, fragmentLength) == 0, "Fragment payload did not match");
+
+ metisMessage_Release(&fragment);
+}
+
+LONGBOW_TEST_CASE(Local, _appendFragmentToReassemblyBuffer_Multiple)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Begin, sizeof(metisTestDataV1_HopByHopFrag_Begin), connid, receiveTime, data->logger);
+ _appendFragmentToReassemblyBuffer(data->fragmenter, fragment1);
+
+ MetisMessage *fragment2 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Middle, sizeof(metisTestDataV1_HopByHopFrag_Middle), connid, receiveTime, data->logger);
+ _appendFragmentToReassemblyBuffer(data->fragmenter, fragment2);
+
+ MetisMessage *fragment3 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_End, sizeof(metisTestDataV1_HopByHopFrag_End), connid, receiveTime, data->logger);
+ _appendFragmentToReassemblyBuffer(data->fragmenter, fragment3);
+
+ int fragmentLength = sizeof(metisTestDataV1_HopByHopFrag_BeginEnd_Fragment);
+
+ assertTrue(parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer) == fragmentLength,
+ "currentReceiveBuffer wrong lenth, expected %d got %zu",
+ fragmentLength,
+ parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer));
+
+ uint8_t *test = parcEventBuffer_Pullup(data->fragmenter->currentReceiveBuffer, -1);
+
+ // compares against the fragment metisTestDataV1_HopByHopFrag_BeginEnd which has the whole payload
+ assertTrue(memcmp(test, metisTestDataV1_HopByHopFrag_BeginEnd_Fragment, fragmentLength) == 0, "Fragment payload did not match");
+
+ metisMessage_Release(&fragment1);
+ metisMessage_Release(&fragment2);
+ metisMessage_Release(&fragment3);
+}
+
+
+/*
+ * B frame should be added to currentReceiveBuffer and state should become Busy.
+ * Also, the currentReceiveBufferIngressId and currentReceiveBufferReceiveTime should be set.
+ */
+LONGBOW_TEST_CASE(Local, _receiveInIdleState_BFrame)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // ensure we're in Idle state
+ _resetParser(data->fragmenter);
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Begin, sizeof(metisTestDataV1_HopByHopFrag_Begin), connid, receiveTime, data->logger);
+
+ const _HopByHopHeader *header = (const _HopByHopHeader *) metisTestDataV1_HopByHopFrag_Begin;
+ _receiveInIdleState(data->fragmenter, fragment1, header);
+
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == sizeof(metisTestDataV1_HopByHopFrag_Begin_Fragment), "Wrong reassembly buffer length expected %zu got %zu", sizeof(metisTestDataV1_HopByHopFrag_Begin_Fragment), length);
+ assertTrue(data->fragmenter->parserState == _ParserState_Busy, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ assertTrue(data->fragmenter->currentReceiveBufferIngressId == connid, "Wrong ingress id expected %u got %u", connid, data->fragmenter->currentReceiveBufferIngressId);
+ assertTrue(data->fragmenter->currentReceiveBufferStartTicks == receiveTime, "Wrong receive time expected %" PRIu64 " got %" PRIu64,
+ receiveTime, data->fragmenter->currentReceiveBufferStartTicks);
+ metisMessage_Release(&fragment1);
+}
+
+/*
+ * BE frame should be added to currentReceiveBuffer and finalized.
+ * State should stay in Idle but the receiveQueue should have the frame in it.
+ */
+LONGBOW_TEST_CASE(Local, _receiveInIdleState_BEFrame)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // ensure we're in Idle state
+ _resetParser(data->fragmenter);
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_BeginEnd, sizeof(metisTestDataV1_HopByHopFrag_BeginEnd), connid, receiveTime, data->logger);
+
+ const _HopByHopHeader *header = (const _HopByHopHeader *) metisTestDataV1_HopByHopFrag_BeginEnd;
+ _receiveInIdleState(data->fragmenter, fragment1, header);
+
+ // should not be in the reassembly buffer
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == 0, "Wrong reassembly buffer length expected 0 got %zu", length);
+
+ // it should be in the receive queue
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Message was not in receive queue");
+ metisMessage_Release(&test);
+
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Idle, data->fragmenter->parserState);
+
+
+ assertTrue(data->fragmenter->currentReceiveBufferIngressId == connid, "Wrong ingress id expected %u got %u", connid, data->fragmenter->currentReceiveBufferIngressId);
+ assertTrue(data->fragmenter->currentReceiveBufferStartTicks == receiveTime, "Wrong receive time expected %" PRIu64 " got %" PRIu64,
+ receiveTime, data->fragmenter->currentReceiveBufferStartTicks);
+ metisMessage_Release(&fragment1);
+}
+
+/*
+ * Not B and Not BE frames should be ignored
+ */
+LONGBOW_TEST_CASE(Local, _receiveInIdleState_OtherFrame)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ struct test_vector {
+ uint8_t flags;
+ bool sentinel;
+ } testVectors[] = {
+ // All combinations except 0x40 and 0x60
+ { .flags = 0x00, .sentinel = false },
+ { .flags = 0x10, .sentinel = false },
+ { .flags = 0x20, .sentinel = false },
+ { .flags = 0x30, .sentinel = false },
+ { .flags = 0x80, .sentinel = false },
+ { .flags = 0x90, .sentinel = false },
+ { .flags = 0xA0, .sentinel = false },
+ { .flags = 0xB0, .sentinel = false },
+ { .flags = 0x00, .sentinel = true },
+ };
+
+ for (int i = 0; !testVectors[i].sentinel; i++) {
+ _HopByHopHeader header;
+ memset(&header, 0, sizeof(header));
+
+ header.blob[0] |= testVectors[i].flags;
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_BeginEnd, sizeof(metisTestDataV1_HopByHopFrag_BeginEnd), connid, receiveTime, data->logger);
+
+ _receiveInIdleState(data->fragmenter, fragment1, &header);
+
+ metisMessage_Release(&fragment1);
+
+ // should not be in the reassembly buffer
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == 0, "Wrong reassembly buffer length expected 0 got %zu", length);
+
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Idle, data->fragmenter->parserState);
+ }
+}
+
+/*
+ * 2) If E flag
+ * 2a) append to reassembly buffer
+ * 2b) finalize the buffer (side effect: will reset the parser and place in receive queue)
+ */
+LONGBOW_TEST_CASE(Local, _receiveInBusyState_EFrame)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+
+ // ensure we're in Busy state (the precondition of this test)
+ _resetParser(data->fragmenter);
+ data->fragmenter->parserState = _ParserState_Busy;
+
+ // and put the Begin and Middle fragments in the reassembly buffer so the packet will decode properly
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Begin, sizeof(metisTestDataV1_HopByHopFrag_Begin), connid, receiveTime, data->logger);
+ _appendFragmentToReassemblyBuffer(data->fragmenter, fragment1);
+
+ MetisMessage *fragment2 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Middle, sizeof(metisTestDataV1_HopByHopFrag_Middle), connid, receiveTime, data->logger);
+ _appendFragmentToReassemblyBuffer(data->fragmenter, fragment2);
+
+ // ====
+ // Now do the test
+
+ MetisMessage *fragment3 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_End, sizeof(metisTestDataV1_HopByHopFrag_End), connid, receiveTime, data->logger);
+
+ const _HopByHopHeader *header = (const _HopByHopHeader *) metisTestDataV1_HopByHopFrag_End;
+ _receiveInBusyState(data->fragmenter, fragment3, header);
+
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == 0, "Wrong reassembly buffer length expected 0 got %zu", length);
+
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Idle, data->fragmenter->parserState);
+
+ // it should be in the receive queue
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Message was not in receive queue");
+ metisMessage_Release(&test);
+
+ metisMessage_Release(&fragment1);
+ metisMessage_Release(&fragment2);
+ metisMessage_Release(&fragment3);
+}
+
+/*
+ * 1) If no flags
+ * 1a) append to reassembly buffer
+ */
+LONGBOW_TEST_CASE(Local, _receiveInBusyState_NoFlagFrame)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // ensure we're in Busy state (the precondition of this test)
+ _resetParser(data->fragmenter);
+ data->fragmenter->parserState = _ParserState_Busy;
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Middle, sizeof(metisTestDataV1_HopByHopFrag_Middle), connid, receiveTime, data->logger);
+
+ const _HopByHopHeader *header = (const _HopByHopHeader *) metisTestDataV1_HopByHopFrag_Middle;
+ _receiveInBusyState(data->fragmenter, fragment1, header);
+
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == sizeof(metisTestDataV1_HopByHopFrag_Middle_Fragment), "Wrong reassembly buffer length expected %zu got %zu", sizeof(metisTestDataV1_HopByHopFrag_Middle_Fragment), length);
+
+ assertTrue(data->fragmenter->parserState == _ParserState_Busy, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ metisMessage_Release(&fragment1);
+}
+
+LONGBOW_TEST_CASE(Local, _receiveInBusyState_OtherFrame)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ struct test_vector {
+ uint8_t flags;
+ bool sentinel;
+ } testVectors[] = {
+ // All combinations except 0x00 and 0x20
+ { .flags = 0x10, .sentinel = false },
+ { .flags = 0x40, .sentinel = false },
+ { .flags = 0x80, .sentinel = false },
+ { .flags = 0x50, .sentinel = false },
+ { .flags = 0x90, .sentinel = false },
+ { .flags = 0xC0, .sentinel = false },
+ { .flags = 0x00, .sentinel = true },
+ };
+
+ for (int i = 0; !testVectors[i].sentinel; i++) {
+ _HopByHopHeader header;
+ memset(&header, 0, sizeof(header));
+
+ header.blob[0] |= testVectors[i].flags;
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_BeginEnd, sizeof(metisTestDataV1_HopByHopFrag_BeginEnd), connid, receiveTime, data->logger);
+
+ // ensure we're in Busy state (the precondition of this test)
+ _resetParser(data->fragmenter);
+ data->fragmenter->parserState = _ParserState_Busy;
+
+ _receiveInBusyState(data->fragmenter, fragment1, &header);
+
+ metisMessage_Release(&fragment1);
+
+ // should not be in the reassembly buffer
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == 0, "Wrong reassembly buffer length expected 0 got %zu", length);
+
+ assertTrue(data->fragmenter->parserState == _ParserState_Idle, "Wrong parser state, exepcted %d got %d", _ParserState_Idle, data->fragmenter->parserState);
+ }
+}
+
+/*
+ * Receive a B frame in Idle state
+ */
+LONGBOW_TEST_CASE(Local, _receiveFragment_IdleState)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Begin, sizeof(metisTestDataV1_HopByHopFrag_Begin), connid, receiveTime, data->logger);
+
+ _receiveFragment(data->fragmenter, fragment1);
+
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == sizeof(metisTestDataV1_HopByHopFrag_Begin_Fragment), "Wrong reassembly buffer length expected %zu got %zu", sizeof(metisTestDataV1_HopByHopFrag_Begin_Fragment), length);
+ assertTrue(data->fragmenter->parserState == _ParserState_Busy, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ assertTrue(data->fragmenter->currentReceiveBufferIngressId == connid, "Wrong ingress id expected %u got %u", connid, data->fragmenter->currentReceiveBufferIngressId);
+ assertTrue(data->fragmenter->currentReceiveBufferStartTicks == receiveTime, "Wrong receive time expected %" PRIu64 " got %" PRIu64,
+ receiveTime, data->fragmenter->currentReceiveBufferStartTicks);
+ metisMessage_Release(&fragment1);
+}
+
+LONGBOW_TEST_CASE(Local, _receiveFragment_BusyState)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // ensure we're in Busy state (the precondition of this test)
+ // Make sure the packet will be in-order by setting the next expected seqnum.
+ _resetParser(data->fragmenter);
+ data->fragmenter->parserState = _ParserState_Busy;
+ data->fragmenter->nextReceiveFragSequenceNumber = 2;
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Middle, sizeof(metisTestDataV1_HopByHopFrag_Middle), connid, receiveTime, data->logger);
+
+ _receiveFragment(data->fragmenter, fragment1);
+
+ size_t length = parcEventBuffer_GetLength(data->fragmenter->currentReceiveBuffer);
+ assertTrue(length == sizeof(metisTestDataV1_HopByHopFrag_Middle_Fragment), "Wrong reassembly buffer length expected %zu got %zu", sizeof(metisTestDataV1_HopByHopFrag_Middle_Fragment), length);
+
+ assertTrue(data->fragmenter->parserState == _ParserState_Busy, "Wrong parser state, exepcted %d got %d", _ParserState_Busy, data->fragmenter->parserState);
+ metisMessage_Release(&fragment1);
+}
+
+LONGBOW_TEST_CASE(Local, _sendFragments_OneFragment)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // make a packet shorter than one MTU (so it will fit with the fragment overhead)
+ size_t length = data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+
+ bool success = _sendFragments(data->fragmenter, message);
+ assertTrue(success, "Failed to send fragments");
+ MetisMessage *fragment = metisHopByHopFragmenter_PopSendQueue(data->fragmenter);
+ assertNotNull(fragment, "Did not find a fragment in the send queue");
+
+ // ===
+ // defragment it
+
+ _receiveFragment(data->fragmenter, fragment);
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Should have gotten the original message back");
+ assertTrue(metisMessage_Length(test) == metisMessage_Length(message),
+ "Reconstructed message length wrong expected %zu got %zu",
+ metisMessage_Length(message),
+ metisMessage_Length(test));
+
+ // ===
+ // cleanup
+
+ metisMessage_Release(&message);
+ metisMessage_Release(&fragment);
+ metisMessage_Release(&test);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+LONGBOW_TEST_CASE(Local, _sendFragments_TwoFragments)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // Take up 2 MTUs (minus a little for fragmentation overhead)
+ size_t length = 2 * data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+
+ bool success = _sendFragments(data->fragmenter, message);
+ assertTrue(success, "Failed to send fragments");
+
+ // ===
+ // defragment it
+
+ MetisMessage *fragment;
+ while ((fragment = metisHopByHopFragmenter_PopSendQueue(data->fragmenter)) != NULL) {
+ _receiveFragment(data->fragmenter, fragment);
+ metisMessage_Release(&fragment);
+ }
+ ;
+
+
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Should have gotten the original message back");
+ assertTrue(metisMessage_Length(test) == metisMessage_Length(message),
+ "Reconstructed message length wrong expected %zu got %zu",
+ metisMessage_Length(message),
+ metisMessage_Length(test));
+
+ // ===
+ // cleanup
+
+ metisMessage_Release(&message);
+ metisMessage_Release(&test);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+LONGBOW_TEST_CASE(Local, _sendFragments_ThreeFragments)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // Take up 2 MTUs (minus a little for fragmentation overhead)
+ size_t length = 3 * data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+
+ bool success = _sendFragments(data->fragmenter, message);
+ assertTrue(success, "Failed to send fragments");
+
+ // ===
+ // defragment it
+
+ MetisMessage *fragment;
+ while ((fragment = metisHopByHopFragmenter_PopSendQueue(data->fragmenter)) != NULL) {
+ _receiveFragment(data->fragmenter, fragment);
+ metisMessage_Release(&fragment);
+ }
+ ;
+
+
+ MetisMessage *test = metisHopByHopFragmenter_PopReceiveQueue(data->fragmenter);
+ assertNotNull(test, "Should have gotten the original message back");
+ assertTrue(metisMessage_Length(test) == metisMessage_Length(message),
+ "Reconstructed message length wrong expected %zu got %zu",
+ metisMessage_Length(message),
+ metisMessage_Length(test));
+
+ // ===
+ // cleanup
+
+ metisMessage_Release(&message);
+ metisMessage_Release(&test);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+LONGBOW_TEST_CASE(Local, _sendFragments_SendQueueFull)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // create a full send queue
+ parcRingBuffer1x1_Release(&data->fragmenter->sendQueue);
+ data->fragmenter->sendQueue = parcRingBuffer1x1_Create(2, _ringBufferDestroyer);
+
+ void *fakeData = (void *) 1;
+ parcRingBuffer1x1_Put(data->fragmenter->sendQueue, fakeData);
+
+
+ // Take up 2 MTUs (minus a little for fragmentation overhead)
+ size_t length = 3 * data->fragmenter->mtu - 100;
+ uint8_t *packet = _conjurePacket(length);
+ MetisMessage *message = metisMessage_CreateFromArray(packet, length, 1, 2, data->logger);
+ assertNotNull(message, "Could not conjure packet");
+
+
+ bool success = _sendFragments(data->fragmenter, message);
+ assertFalse(success, "Should have failed to send fragments");
+ // ===
+ // cleanup
+
+ // manually pop this off as it is not a proper MetisMessage
+ parcRingBuffer1x1_Get(data->fragmenter->sendQueue, &fakeData);
+
+ metisMessage_Release(&message);
+ parcMemory_Deallocate((void **) &packet);
+}
+
+LONGBOW_TEST_CASE(Local, _ringBufferDestroyer)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ unsigned connid = 7;
+ MetisTicks receiveTime = 9999;
+ MetisMessage *fragment1 = metisMessage_CreateFromArray(metisTestDataV1_HopByHopFrag_Middle, sizeof(metisTestDataV1_HopByHopFrag_Middle), connid, receiveTime, data->logger);
+
+ bool success = parcRingBuffer1x1_Put(data->fragmenter->receiveQueue, fragment1);
+ assertTrue(success, "Failed to put test message in queue");
+
+ // nothing to do here. When the fragmenter is destroyed it should destroy the message
+ // and we will not trip a memory imbalance
+}
+
+// ============================================================
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_HopByHopFragmenter);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}