aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/io/test
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/io/test')
-rw-r--r--metis/ccnx/forwarder/metis/io/test/CMakeLists.txt25
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_AddressPair.c153
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_EtherConnection.c271
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_EtherListener.c387
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_HopByHopFragmenter.c1087
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_IPMulticastListener.c80
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_IoOperations.c185
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_ListenerSet.c259
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_LocalListener.c97
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_StreamConnection.c767
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_TcpListener.c364
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_TcpTunnel.c216
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_UdpConnection.c535
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_UdpListener.c458
-rw-r--r--metis/ccnx/forwarder/metis/io/test/test_metis_UdpTunnel.c327
-rw-r--r--metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.c236
-rw-r--r--metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.h109
-rw-r--r--metis/ccnx/forwarder/metis/io/test/testrig_MetisListenerOps.c113
18 files changed, 5669 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/io/test/CMakeLists.txt b/metis/ccnx/forwarder/metis/io/test/CMakeLists.txt
new file mode 100644
index 00000000..1e22e32e
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Enable gcov output for the tests
+add_definitions(--coverage)
+set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
+
+set(TestsExpectedToPass
+ test_metis_AddressPair
+ test_metis_EtherConnection
+ test_metis_EtherListener
+ test_metis_HopByHopFragmenter
+ test_metis_IPMulticastListener
+ test_metis_ListenerSet
+ test_metis_LocalListener
+ test_metis_StreamConnection
+ test_metis_TcpListener
+ test_metis_TcpTunnel
+ test_metis_UdpConnection
+ test_metis_UdpListener
+ test_metis_UdpTunnel
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_AddressPair.c b/metis/ccnx/forwarder/metis/io/test/test_metis_AddressPair.c
new file mode 100644
index 00000000..34ff9d9d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_AddressPair.c
@@ -0,0 +1,153 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_AddressPair.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(metis_AddressPair)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_AddressPair)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_AddressPair)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisAddressPair_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, metisAddressPair_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, metisAddressPair_Equals_NotEqual);
+ LONGBOW_RUN_TEST_CASE(Global, metisAddressPair_EqualsAddresses);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisAddressPair_Create_Destroy)
+{
+ CPIAddress *a = cpiAddress_CreateFromInterface(1);
+ CPIAddress *b = cpiAddress_CreateFromInterface(2);
+
+ size_t allocbase = parcSafeMemory_Outstanding();
+ MetisAddressPair *pair = metisAddressPair_Create(a, b);
+ metisAddressPair_Release(&pair);
+
+ assertTrue(parcSafeMemory_Outstanding() == allocbase,
+ "Memory out of balance, expected %zu got %u",
+ allocbase,
+ parcSafeMemory_Outstanding());
+
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, metisAddressPair_Equals)
+{
+ CPIAddress *a = cpiAddress_CreateFromInterface(1);
+ CPIAddress *b = cpiAddress_CreateFromInterface(2);
+
+ MetisAddressPair *pair_a = metisAddressPair_Create(a, b);
+ MetisAddressPair *pair_b = metisAddressPair_Create(a, b);
+
+ assertTrue(metisAddressPair_Equals(pair_a, pair_b), "Two equal address pairs did not compare equal");
+
+ metisAddressPair_Release(&pair_a);
+ metisAddressPair_Release(&pair_b);
+
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, metisAddressPair_Equals_NotEqual)
+{
+ CPIAddress *a = cpiAddress_CreateFromInterface(1);
+ CPIAddress *b = cpiAddress_CreateFromInterface(2);
+
+ MetisAddressPair *pair_a = metisAddressPair_Create(a, b);
+ MetisAddressPair *pair_b = metisAddressPair_Create(b, a);
+
+ assertFalse(metisAddressPair_Equals(pair_a, pair_b), "Two unequal address pairs compared equal");
+
+ metisAddressPair_Release(&pair_a);
+ metisAddressPair_Release(&pair_b);
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, metisAddressPair_EqualsAddresses)
+{
+ CPIAddress *a = cpiAddress_CreateFromInterface(1);
+ CPIAddress *b = cpiAddress_CreateFromInterface(2);
+
+ MetisAddressPair *pair_a = metisAddressPair_Create(a, b);
+
+ assertTrue(metisAddressPair_EqualsAddresses(pair_a, a, b), "Two equal address pairs did not compare equal");
+
+ metisAddressPair_Release(&pair_a);
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_AddressPair);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_EtherConnection.c b/metis/ccnx/forwarder/metis/io/test/test_metis_EtherConnection.c
new file mode 100644
index 00000000..081903b1
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_EtherConnection.c
@@ -0,0 +1,271 @@
+/*
+ * 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 "testrig_GenericEther.c"
+#include "../metis_EtherConnection.c"
+
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+typedef struct test_data {
+ MetisForwarder *metis;
+ MetisGenericEther *ether;
+ MetisAddressPair *pair;
+ MetisIoOperations *io_ops;
+} TestData;
+
+static void
+commonSetup(const LongBowTestCase *testCase, uint16_t ethertype)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+
+ data->metis = metisForwarder_Create(NULL);
+ data->ether = metisGenericEther_Create(data->metis, "foo", ethertype);
+ data->io_ops = NULL;
+
+ // crank the libevent handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) {0, 10000}));
+
+ PARCBuffer *localMacBuffer = metisGenericEther_GetMacAddress(data->ether);
+ CPIAddress *local = cpiAddress_CreateFromLink(parcBuffer_Overlay(localMacBuffer, 0), parcBuffer_Remaining(localMacBuffer));
+ CPIAddress *remote = cpiAddress_CreateFromLink((uint8_t []) { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6);
+
+ data->pair = metisAddressPair_Create(local, remote);
+
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+commonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) {0, 10000}));
+ metisGenericEther_Release(&data->ether);
+ metisAddressPair_Release(&data->pair);
+
+ if (data->io_ops) {
+ data->io_ops->destroy(&data->io_ops);
+ }
+
+ // destroy metis last
+ metisForwarder_Destroy(&data->metis);
+ parcMemory_Deallocate((void **) &data);
+}
+
+
+LONGBOW_TEST_RUNNER(metis_EtherConnection)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_EtherConnection)
+{
+ 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_EtherConnection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ===========================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisEtherConnection_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ commonSetup(testCase, 0x0801);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ commonTeardown(testCase);
+ 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, metisEtherConnection_Create)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ data->io_ops = metisEtherConnection_Create(data->metis, data->ether, data->pair);
+ assertNotNull(data->io_ops, "Got null MetisIoOperations from metisEtherConnection_Create");
+}
+
+// ===========================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_DestroyOperations);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_FillInMacAddress);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_GetAddressPair);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_GetConnectionId);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_GetRemoteAddress);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_IsLocal);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_IsUp);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_Send);
+ LONGBOW_RUN_TEST_CASE(Local, _setConnectionState);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherConnection_getConnectionType);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ commonSetup(testCase, 0x0801);
+
+ // for the local tests we also pre-create the EtherConnection
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ data->io_ops = metisEtherConnection_Create(data->metis, data->ether, data->pair);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ commonTeardown(testCase);
+ 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, _metisEtherConnection_DestroyOperations)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_FillInMacAddress)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_GetAddressPair)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherState *etherConn = (_MetisEtherState *) metisIoOperations_GetClosure(data->io_ops);
+
+ const MetisAddressPair *test = _metisEtherConnection_GetAddressPair(data->io_ops);
+
+ assertTrue(metisAddressPair_Equals(test, etherConn->addressPair), "Address pair did not compare");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_GetConnectionId)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherState *etherConn = (_MetisEtherState *) metisIoOperations_GetClosure(data->io_ops);
+ unsigned connid = _metisEtherConnection_GetConnectionId(data->io_ops);
+
+ assertTrue(connid == etherConn->id, "Wrong connection id, got %u exepected %u", connid, etherConn->id);
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_GetRemoteAddress)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ const CPIAddress *test = _metisEtherConnection_GetRemoteAddress(data->io_ops);
+
+ _MetisEtherState *etherConn = (_MetisEtherState *) metisIoOperations_GetClosure(data->io_ops);
+
+ assertTrue(cpiAddress_Equals(test, metisAddressPair_GetRemote(etherConn->addressPair)), "Remote addresses did not compare");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_IsLocal)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ bool isLocal = _metisEtherConnection_IsLocal(data->io_ops);
+ assertFalse(isLocal, "Ethernet should always be remote");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_IsUp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ bool isUp = _metisEtherConnection_IsUp(data->io_ops);
+ assertTrue(isUp, "Ethernet should be up");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_Send)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ MetisMessage *message = metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest), 1, 2, metisForwarder_GetLogger(data->metis));
+
+ bool success = _metisEtherConnection_Send(data->io_ops, NULL, message);
+ assertTrue(success, "Failed to write message to ethernet");
+
+ // we should now be able to read the ethernet frame from the test socket
+ int testSocket = mockGenericEther_GetTestDescriptor(data->ether);
+ assertTrue(testSocket > 0, "Error getting test socket from mock ethernet");
+
+ uint32_t testBufferSize = 2048;
+ uint8_t testBuffer[testBufferSize];
+ ssize_t bytesRead = read(testSocket, testBuffer, testBufferSize);
+
+ size_t expectedRead = sizeof(struct ether_header) + sizeof(metisTestDataV0_EncodedInterest);
+
+ assertTrue(bytesRead == expectedRead, "Wrong read size, got %zd expected %zu", bytesRead, expectedRead);
+
+ uint8_t *frame = testBuffer + sizeof(struct ether_header);
+ assertTrue(memcmp(frame, metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest)) == 0, "Buffers do not match");
+
+ metisMessage_Release(&message);
+}
+
+LONGBOW_TEST_CASE(Local, _setConnectionState)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherState *etherConn = (_MetisEtherState *) metisIoOperations_GetClosure(data->io_ops);
+
+ _setConnectionState(etherConn, false);
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherConnection_getConnectionType)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CPIConnectionType connType = _metisEtherConnection_getConnectionType(data->io_ops);
+
+ assertTrue(connType == cpiConnection_L2, "Wrong connection type expected %d got %d", cpiConnection_L2, connType);
+}
+
+// ===========================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_EtherConnection);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_EtherListener.c b/metis/ccnx/forwarder/metis/io/test/test_metis_EtherListener.c
new file mode 100644
index 00000000..2ae7596c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_EtherListener.c
@@ -0,0 +1,387 @@
+/*
+ * 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.
+ */
+
+
+// force the use of the generic ethernet mockup
+#include "testrig_GenericEther.c"
+#include "../metis_EtherListener.c"
+#include "testrig_GenericEther.h"
+
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <LongBow/unit-test.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+
+typedef struct test_data {
+ MetisForwarder *metis;
+ MetisListenerOps *ops;
+} TestData;
+
+static void
+commonSetup(const LongBowTestCase *testCase, uint16_t ethertype)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+
+ data->metis = metisForwarder_Create(NULL);
+ data->ops = metisEtherListener_Create(data->metis, "test0", ethertype);
+
+ // crank the libevent handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) {0, 10000}));
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+commonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) {0, 10000}));
+ data->ops->destroy(&data->ops);
+ metisForwarder_Destroy(&data->metis);
+
+ parcMemory_Deallocate((void **) &data);
+}
+
+// ============================================================
+
+LONGBOW_TEST_RUNNER(metis_EtherListener)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_EtherListener)
+{
+ 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_EtherListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ============================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisEtherListener_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ commonSetup(testCase, 0x0801);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ commonTeardown(testCase);
+ 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, metisEtherListener_Create)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ assertNotNull(data->ops, "Null return from metisEtherListener_Create");
+}
+
+// ============================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisEtherListener_Destroy);
+ LONGBOW_RUN_TEST_CASE(Local, metisEtherListener_OpsDestroy);
+ LONGBOW_RUN_TEST_CASE(Local, metisEtherListener_OpsGetInterfaceIndex);
+ LONGBOW_RUN_TEST_CASE(Local, metisEtherListener_OpsGetListenAddress);
+ LONGBOW_RUN_TEST_CASE(Local, metisEtherListener_OpsGetEncapType);
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ReadCallback);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ReadCallback_FragmentBegin);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ReadCallback_FragmentEnd);
+
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ReadEtherFrame_EmptyQueue);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ReadEtherFrame_PacketWaiting);
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurSourceAddress_True);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurSourceAddress_False);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_Unicast);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_Group);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_Broadcast);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_False);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_IsOurProtocol);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ParseEtherFrame);
+
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_FillInEthernetAddresses);
+ LONGBOW_RUN_TEST_CASE(Local, _metisEtherListener_ReleaseEthernetAddresses);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ commonSetup(testCase, 0x0801);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ commonTeardown(testCase);
+ 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, metisEtherListener_Destroy)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisListenerOps *ops = metisEtherListener_Create(data->metis, "fake0", 0x0801);
+
+ _metisEtherListener_Destroy((_MetisEtherListener **) &ops->context);
+ assertNull(ops->context, "Destory did not null context");
+ parcMemory_Deallocate((void **) &ops);
+}
+
+LONGBOW_TEST_CASE(Local, metisEtherListener_OpsDestroy)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisListenerOps *ops = metisEtherListener_Create(data->metis, "fake1", 0x0801);
+ _metisEtherListener_OpsDestroy(&ops);
+ assertNull(ops, "OpsDestroy did not null ops");
+}
+
+LONGBOW_TEST_CASE(Local, metisEtherListener_OpsGetInterfaceIndex)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, metisEtherListener_OpsGetListenAddress)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, metisEtherListener_OpsGetEncapType)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ReadCallback)
+{
+ testUnimplemented("");
+}
+
+/*
+ * Read only a B frame, so its not a complete reassembly
+ */
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ReadCallback_FragmentBegin)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t headerArray[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x08, 0x01 };
+
+ PARCBuffer *frameBuffer = parcBuffer_Allocate(sizeof(struct ether_header) + sizeof(metisTestDataV1_HopByHopFrag_Begin));
+ parcBuffer_PutArray(frameBuffer, sizeof(struct ether_header), headerArray);
+ parcBuffer_PutArray(frameBuffer, sizeof(metisTestDataV1_HopByHopFrag_Begin), metisTestDataV1_HopByHopFrag_Begin);
+
+ parcBuffer_Flip(frameBuffer);
+
+ mockGenericEther_QueueFrame(etherListener->genericEther, frameBuffer);
+
+ _metisEtherListener_ReadCallback(0, PARCEventType_Read, data->ops->context);
+
+ assertTrue(etherListener->stats.framesIn == 1, "Wrong framesIn count, expected 1 got %" PRIu64, etherListener->stats.framesIn);
+ assertTrue(etherListener->stats.framesReceived == 1, "Wrong framesReceived count, expected 1 got %" PRIu64, etherListener->stats.framesReceived);
+ assertTrue(etherListener->stats.framesReassembled == 0, "Wrong framesReassembled count, expected 0 got %" PRIu64, etherListener->stats.framesReassembled);
+
+ parcBuffer_Release(&frameBuffer);
+}
+
+/*
+ * Read a B and middle and E frame, so it is complete reassembly
+ */
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ReadCallback_FragmentEnd)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t headerArray[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x08, 0x01 };
+
+ PARCBuffer *frameBuffer = parcBuffer_Allocate(sizeof(struct ether_header) + sizeof(metisTestDataV1_HopByHopFrag_BeginEnd));
+ parcBuffer_PutArray(frameBuffer, sizeof(struct ether_header), headerArray);
+ parcBuffer_PutArray(frameBuffer, sizeof(metisTestDataV1_HopByHopFrag_BeginEnd), metisTestDataV1_HopByHopFrag_BeginEnd);
+
+ parcBuffer_Flip(frameBuffer);
+
+ mockGenericEther_QueueFrame(etherListener->genericEther, frameBuffer);
+
+ _metisEtherListener_ReadCallback(0, PARCEventType_Read, data->ops->context);
+
+ assertTrue(etherListener->stats.framesIn == 1, "Wrong framesIn count, expected 1 got %" PRIu64, etherListener->stats.framesIn);
+ assertTrue(etherListener->stats.framesReceived == 1, "Wrong framesReceived count, expected 1 got %" PRIu64, etherListener->stats.framesReceived);
+ assertTrue(etherListener->stats.framesReassembled == 1, "Wrong framesReassembled count, expected 1 got %" PRIu64, etherListener->stats.framesReassembled);
+
+ parcBuffer_Release(&frameBuffer);
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ReadEtherFrame_PacketWaiting)
+{
+ // create a frame and queue it
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ // Ethernet frame addressed to us with a 0-length CCNx TLV packet (i.e. just the fixed header)
+ uint8_t frame[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 't', 'e', 's', 't', '0', 0xA0, 0x08, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
+
+ PARCBuffer *frameBuffer = parcBuffer_Wrap(frame, sizeof(frame), 0, sizeof(frame));
+
+ mockGenericEther_QueueFrame(etherListener->genericEther, frameBuffer);
+
+ PARCEventBuffer *buffer = _metisEtherListener_ReadEtherFrame(etherListener);
+ assertNotNull(buffer, "Got null buffer from ReadEtherFrame with a frame queued");
+
+ assertTrue(parcEventBuffer_GetLength(buffer) == sizeof(frame), "Wrong size, got %zu expected %zu", parcEventBuffer_GetLength(buffer), sizeof(frame));
+
+ parcEventBuffer_Destroy(&buffer);
+ parcBuffer_Release(&frameBuffer);
+}
+
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ReadEtherFrame_EmptyQueue)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+ PARCEventBuffer *buffer = _metisEtherListener_ReadEtherFrame(etherListener);
+ assertNull(buffer, "Should get null buffer from ReadEtherFrame without a frame queued");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurSourceAddress_True)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t frame[] = { 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 't', 'e', 's', 't', '0', 0x06, 0x08, 0x01 };
+ struct ether_header *header = (struct ether_header *) frame;
+
+ bool success = _metisEtherListener_IsOurSourceAddress(etherListener, header);
+ assertTrue(success, "Did not match our source address.");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurSourceAddress_False)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t frame[] = { 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x11, 0x22, 0x33, 0x44, 0x05, 0x06, 0x08, 0x01 };
+ struct ether_header *header = (struct ether_header *) frame;
+
+ bool success = _metisEtherListener_IsOurSourceAddress(etherListener, header);
+ assertFalse(success, "Should not match our address.");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_Unicast)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t frame[] = { 't', 'e', 's', 't', '0', 0x06, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x08, 0x01 };
+ struct ether_header *header = (struct ether_header *) frame;
+
+ bool success = _metisEtherListener_IsOurDestinationAddress(etherListener, header);
+ assertTrue(success, "Did not match our destination address.");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_Group)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t frame[] = { 0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x08, 0x01 };
+ struct ether_header *header = (struct ether_header *) frame;
+
+ bool success = _metisEtherListener_IsOurDestinationAddress(etherListener, header);
+ assertTrue(success, "Did not match group address.");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_Broadcast)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t frame[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x08, 0x01 };
+ struct ether_header *header = (struct ether_header *) frame;
+
+ bool success = _metisEtherListener_IsOurDestinationAddress(etherListener, header);
+ assertTrue(success, "Did not match broadcast address.");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurDestinationAddress_False)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _MetisEtherListener *etherListener = data->ops->context;
+
+ uint8_t frame[] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x08, 0x01 };
+ struct ether_header *header = (struct ether_header *) frame;
+
+ bool success = _metisEtherListener_IsOurDestinationAddress(etherListener, header);
+ assertFalse(success, "Should not match one of our addresses.");
+}
+
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_IsOurProtocol)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ParseEtherFrame)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_FillInEthernetAddresses)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisEtherListener_ReleaseEthernetAddresses)
+{
+ testUnimplemented("");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_EtherListener);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
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);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_IPMulticastListener.c b/metis/ccnx/forwarder/metis/io/test/test_metis_IPMulticastListener.c
new file mode 100644
index 00000000..12dc17dc
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_IPMulticastListener.c
@@ -0,0 +1,80 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_IPMulticastListener.c"
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metis_IPMulticastListener)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_IPMulticastListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_IPMulticastListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_IPMulticastListener);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_IoOperations.c b/metis/ccnx/forwarder/metis/io/test/test_metis_IoOperations.c
new file mode 100644
index 00000000..a78715c1
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_IoOperations.c
@@ -0,0 +1,185 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_IoOperations.c"
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <LongBow/unit-test.h>
+
+#include <ccnx/forwarder/metis/core/test/testrig_MetisIoOperations.h>
+
+// ===========================================
+
+LONGBOW_TEST_RUNNER(metis_IoOperations)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_IoOperations)
+{
+ 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_IoOperations)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_GetClosure);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_Send);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_GetRemoteAddress);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_GetAddressPair);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_IsUp);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_IsLocal);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_GetConnectionId);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_Release);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_Class);
+ LONGBOW_RUN_TEST_CASE(Global, metisIoOperations_GetConnectionType);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ 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, metisIoOperations_GetClosure)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ void *closure = metisIoOperations_GetClosure(ops);
+ assertTrue(closure == ops->closure, "Wrong closure, expected %p got %p", ops->closure, closure);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_Send)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_Send(ops, NULL, NULL);
+ assertTrue(data->sendCount == 1, "Wrong metisIoOperations_Send count expected 1 got %u", data->sendCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_GetRemoteAddress)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_GetRemoteAddress(ops);
+ assertTrue(data->getRemoteAddressCount == 1, "Wrong metisIoOperations_GetRemoteAddress count expected 1 got %u", data->getRemoteAddressCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_GetAddressPair)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_GetAddressPair(ops);
+ assertTrue(data->getAddressPairCount == 1, "Wrong metisIoOperations_GetAddressPairexpected count 1 got %u", data->getAddressPairCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_IsUp)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_IsUp(ops);
+ assertTrue(data->isUpCount == 1, "Wrong metisIoOperations_IsUp count expected 1 got %u", data->isUpCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_IsLocal)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_IsLocal(ops);
+ assertTrue(data->isLocalCount == 1, "Wrong metisIoOperations_IsLocal count expected 1 got %u", data->isLocalCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_GetConnectionId)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_GetConnectionId(ops);
+ assertTrue(data->getConnectionIdCount == 1, "Wrong metisIoOperations_GetConnectionId count expected 1 got %u", data->getConnectionIdCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_Release)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MetisIoOperations *copy = ops;
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_Release(&ops);
+ assertTrue(data->destroyCount == 1, "Wrong metisIoOperations_Release count expected 1 got %u", data->destroyCount);
+ mockIoOperationsData_Destroy(&copy);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_Class)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_Class(ops);
+ assertTrue(data->classCount == 1, "Wrong metisIoOperations_Class count expected 1 got %u", data->classCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisIoOperations_GetConnectionType)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 3, true, true, true);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ metisIoOperations_GetConnectionType(ops);
+ assertTrue(data->getConnectionTypeCount == 1, "Wrong getConnectionTypeCount count expected 1 got %u", data->getConnectionTypeCount);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+
+
+// ===========================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_IoOperations);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_ListenerSet.c b/metis/ccnx/forwarder/metis/io/test/test_metis_ListenerSet.c
new file mode 100644
index 00000000..b786d9b3
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_ListenerSet.c
@@ -0,0 +1,259 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_ListenerSet.c"
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <LongBow/unit-test.h>
+
+#include "testrig_MetisListenerOps.c"
+
+
+LONGBOW_TEST_RUNNER(metis_ListenerSet)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_ListenerSet)
+{
+ 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_ListenerSet)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Add_Single);
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Add_Unique);
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Add_Duplicate);
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Create_Destroy);
+
+
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Length);
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Get);
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Find_InSet);
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerSet_Find_NotInSet);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ 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;
+}
+
+// Adds a single MockListenerData to the listener set.
+static MockListenerData *
+addSingle(MetisListenerSet *set)
+{
+ CPIAddress *listenAddress = cpiAddress_CreateFromInterface(44);
+ MockListenerData *data = mockListenData_Create(1, listenAddress, METIS_ENCAP_ETHER);
+ MetisListenerOps *listenerOps = mockListener_Create(data);
+
+ bool success = metisListenerSet_Add(set, listenerOps);
+ assertTrue(success, "Got failure adding one listener to the set");
+ assertTrue(parcArrayList_Size(set->listOfListeners) == 1,
+ "Got wrong list length, got %zu expected %u",
+ parcArrayList_Size(set->listOfListeners), 1);
+
+ cpiAddress_Destroy(&listenAddress);
+ return data;
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Add_Single)
+{
+ MetisListenerSet *set = metisListenerSet_Create();
+
+ MockListenerData *data = addSingle(set);
+
+ metisListenerSet_Destroy(&set);
+ assertTrue(data->destroyCount == 1,
+ "Wrong destroy count, got %u expected %u",
+ data->destroyCount, 1);
+
+ mockListenerData_Destroy(&data);
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Add_Unique)
+{
+ CPIAddress *listenAddress_A = cpiAddress_CreateFromInterface(44);
+ MockListenerData *data_A = mockListenData_Create(1, listenAddress_A, METIS_ENCAP_ETHER);
+ MetisListenerOps *listenerOps_A = mockListener_Create(data_A);
+
+ CPIAddress *listenAddress_B = cpiAddress_CreateFromInterface(55);
+ MockListenerData *data_B = mockListenData_Create(1, listenAddress_B, METIS_ENCAP_ETHER);
+ MetisListenerOps *listenerOps_B = mockListener_Create(data_B);
+
+
+ MetisListenerSet *set = metisListenerSet_Create();
+ bool success_A = metisListenerSet_Add(set, listenerOps_A);
+ assertTrue(success_A, "Got failure adding listener A to the set");
+
+ bool success_B = metisListenerSet_Add(set, listenerOps_B);
+ assertTrue(success_B, "Got failure adding listener B to the set");
+
+ cpiAddress_Destroy(&listenAddress_A);
+ cpiAddress_Destroy(&listenAddress_B);
+ metisListenerSet_Destroy(&set);
+
+ mockListenerData_Destroy(&data_A);
+ mockListenerData_Destroy(&data_B);
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Add_Duplicate)
+{
+ CPIAddress *listenAddress_A = cpiAddress_CreateFromInterface(44);
+ MockListenerData *data_A = mockListenData_Create(1, listenAddress_A, METIS_ENCAP_ETHER);
+ MetisListenerOps *listenerOps_A = mockListener_Create(data_A);
+
+ CPIAddress *listenAddress_B = cpiAddress_CreateFromInterface(44);
+ MockListenerData *data_B = mockListenData_Create(1, listenAddress_B, METIS_ENCAP_ETHER);
+ MetisListenerOps *listenerOps_B = mockListener_Create(data_B);
+
+
+ MetisListenerSet *set = metisListenerSet_Create();
+ bool success_A = metisListenerSet_Add(set, listenerOps_A);
+ assertTrue(success_A, "Got failure adding listener A to the set");
+
+ bool success_B = metisListenerSet_Add(set, listenerOps_B);
+ assertFalse(success_B, "Got success adding listener B to the set, duplicate should have failed");
+
+ cpiAddress_Destroy(&listenAddress_A);
+ cpiAddress_Destroy(&listenAddress_B);
+ metisListenerSet_Destroy(&set);
+
+ mockListener_Destroy(&listenerOps_B);
+ mockListenerData_Destroy(&data_A);
+ mockListenerData_Destroy(&data_B);
+}
+
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Create_Destroy)
+{
+ MetisListenerSet *set = metisListenerSet_Create();
+ assertNotNull(set, "Got null from Create");
+
+ metisListenerSet_Destroy(&set);
+ assertNull(set, "Destroy did not null parameter");
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Length)
+{
+ MetisListenerSet *set = metisListenerSet_Create();
+ MockListenerData *data = addSingle(set);
+
+ size_t length = metisListenerSet_Length(set);
+
+ metisListenerSet_Destroy(&set);
+ mockListenerData_Destroy(&data);
+
+ assertTrue(length == 1,
+ "Wrong length, got %zu expected %u",
+ length, 1);
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Get)
+{
+ MetisListenerSet *set = metisListenerSet_Create();
+ MockListenerData *data = addSingle(set);
+
+ MetisListenerOps *ops = metisListenerSet_Get(set, 0);
+
+ assertNotNull(ops, "Did not fetch the listener ops");
+
+ metisListenerSet_Destroy(&set);
+ mockListenerData_Destroy(&data);
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Find_InSet)
+{
+ MetisListenerSet *set = metisListenerSet_Create();
+ MockListenerData *data = addSingle(set);
+
+ MetisListenerOps *ops = metisListenerSet_Find(set, data->encapType, data->listenAddress);
+ assertNotNull(ops, "Did not retrieve the listener that is in the set");
+
+ metisListenerSet_Destroy(&set);
+ mockListenerData_Destroy(&data);
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerSet_Find_NotInSet)
+{
+ MetisListenerSet *set = metisListenerSet_Create();
+ MockListenerData *data = addSingle(set);
+
+ // use wrong encap type
+ MetisListenerOps *ops = metisListenerSet_Find(set, data->encapType + 1, data->listenAddress);
+ assertNull(ops, "Should not have found anything with wrong encap type");
+
+ metisListenerSet_Destroy(&set);
+ mockListenerData_Destroy(&data);
+}
+
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisListenerSet_DestroyListenerOps);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ 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, metisListenerSet_DestroyListenerOps)
+{
+ testUnimplemented("");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_ListenerSet);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_LocalListener.c b/metis/ccnx/forwarder/metis/io/test/test_metis_LocalListener.c
new file mode 100644
index 00000000..2852d253
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_LocalListener.c
@@ -0,0 +1,97 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_LocalListener.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(metis_LocalListener)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_LocalListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_LocalListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisListenerLocal_Create_Destroy);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisListenerLocal_Create_Destroy)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisListenerLocal_Listen);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Local, metisListenerLocal_Listen)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_LocalListener);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_StreamConnection.c b/metis/ccnx/forwarder/metis/io/test/test_metis_StreamConnection.c
new file mode 100644
index 00000000..5ae50bcd
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_StreamConnection.c
@@ -0,0 +1,767 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_StreamConnection.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+// inet_pton
+#include <arpa/inet.h>
+
+#include <fcntl.h>
+
+#include <stdio.h>
+
+#ifndef INPORT_ANY
+#define INPORT_ANY 0
+#endif
+
+// we hand-code some packets in the unit tests
+typedef struct __attribute__ ((__packed__)) metis_tlv_fixed_header {
+ uint8_t version;
+ uint8_t packetType;
+ uint16_t payloadLength;
+ uint16_t reserved;
+ uint16_t headerLength;
+} _MetisTlvFixedHeaderV0;
+
+
+LONGBOW_TEST_RUNNER(metis_StreamConnection)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_StreamConnection)
+{
+ 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_StreamConnection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ================================================================================
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisStreamConnection_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisStreamConnection_OpenConnection);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ 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, metisStreamConnection_Create)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fd, pair, false);
+
+ ops->destroy(&ops);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ metisForwarder_Destroy(&metis);
+ close(fd);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+ assertTrue(parcSafeMemory_Outstanding() == 0, "Got memory imbalance: %u", parcSafeMemory_Outstanding());
+}
+
+static int
+listenToInet(struct sockaddr_in *server)
+{
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(fd < 0, "error on bind: (%d) %s", errno, strerror(errno));
+
+ // Set non-blocking flag
+ int flags = fcntl(fd, F_GETFL, NULL);
+ assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno);
+ int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno);
+
+ failure = bind(fd, (struct sockaddr *) server, sizeof(struct sockaddr_in));
+ assertFalse(failure, "error on bind: (%d) %s", errno, strerror(errno));
+
+ failure = listen(fd, 16);
+ assertFalse(failure, "error on listen: (%d) %s", errno, strerror(errno));
+
+ return fd;
+}
+
+LONGBOW_TEST_CASE(Global, metisStreamConnection_OpenConnection)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+
+ struct sockaddr_in serverAddr;
+ memset(&serverAddr, 0, sizeof(serverAddr));
+ serverAddr.sin_family = PF_INET;
+ serverAddr.sin_port = INPORT_ANY;
+ inet_pton(AF_INET, "127.0.0.1", &(serverAddr.sin_addr));
+
+ int serverSocket = listenToInet(&serverAddr);
+ socklen_t x = sizeof(serverAddr);
+ int failure = getsockname(serverSocket, (struct sockaddr *) &serverAddr, &x);
+ assertFalse(failure, "error on getsockname: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in localAddr;
+ memset(&localAddr, 0, sizeof(localAddr));
+ localAddr.sin_family = PF_INET;
+ localAddr.sin_addr.s_addr = INADDR_ANY;
+ localAddr.sin_port = INPORT_ANY;
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&localAddr);
+
+ // change from 0.0.0.0 to 127.0.0.1
+ inet_pton(AF_INET, "127.0.0.1", &(serverAddr.sin_addr));
+
+ CPIAddress *remote = cpiAddress_CreateFromInet(&serverAddr);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+
+ MetisIoOperations *ops = metisStreamConnection_OpenConnection(metis, pair, false);
+ assertNotNull(ops, "Got null ops from metisStreamConnection_OpenConnection");
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+}
+
+// =======================================================================
+
+// ==================================================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, conn_eventcb_Connected);
+ LONGBOW_RUN_TEST_CASE(Local, conn_eventcb_EOF);
+ LONGBOW_RUN_TEST_CASE(Local, conn_eventcb_ERROR);
+
+ LONGBOW_RUN_TEST_CASE(Local, conn_readcb);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_Equals);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_GetAddress);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_GetAddressPair);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_GetConnectionId);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_HashCode);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_IsUp);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_Send);
+ LONGBOW_RUN_TEST_CASE(Local, metisStreamConnection_GetConnectionType);
+ LONGBOW_RUN_TEST_CASE(Local, printConnection);
+ LONGBOW_RUN_TEST_CASE(Local, readMessage);
+ LONGBOW_RUN_TEST_CASE(Local, setConnectionState);
+ LONGBOW_RUN_TEST_CASE(Local, single_read_ZeroNextMessageLength);
+ LONGBOW_RUN_TEST_CASE(Local, single_read_PartialRead);
+ LONGBOW_RUN_TEST_CASE(Local, single_read_FullRead);
+ LONGBOW_RUN_TEST_CASE(Local, startNewMessage);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ 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, conn_eventcb_Connected)
+{
+ int fds[2];
+ int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
+ assertFalse(failure, "Error socketpair: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fds[0], pair, false);
+ _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);
+
+ stream->isUp = false;
+
+ // ---- the actual test
+ _conn_eventcb(stream->bufferEventVector, PARCEventQueueEventType_Connected, ops);
+ assertTrue(stream->isUp, "PARCEventQueueEventType_Connected did not trigger stream to up state");
+ // ----
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ close(fds[0]);
+ close(fds[1]);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+}
+
+LONGBOW_TEST_CASE(Local, conn_eventcb_EOF)
+{
+ int fds[2];
+ int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
+ assertFalse(failure, "Error socketpair: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fds[0], pair, false);
+ _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);
+
+ stream->isUp = true;
+
+ // ---- the actual test
+ _conn_eventcb(stream->bufferEventVector, PARCEventQueueEventType_EOF, ops);
+ assertFalse(stream->isUp, "PARCEventQueueEventType_EOF did not trigger stream to down state");
+ // ----
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ close(fds[0]);
+ close(fds[1]);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+}
+
+LONGBOW_TEST_CASE(Local, conn_eventcb_ERROR)
+{
+ int fds[2];
+ int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
+ assertFalse(failure, "Error socketpair: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fds[0], pair, false);
+ _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);
+
+ stream->isUp = true;
+
+ // ---- the actual test
+ _conn_eventcb(stream->bufferEventVector, PARCEventQueueEventType_Error, ops);
+ assertFalse(stream->isUp, "PARCEventQueueEventType_Error did not trigger stream to down state");
+ // ----
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ close(fds[0]);
+ close(fds[1]);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+}
+
+LONGBOW_TEST_CASE(Local, conn_readcb)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_Equals)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_GetAddress)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fd, pair, false);
+
+ const CPIAddress *test_addr = ops->getRemoteAddress(ops);
+
+ assertTrue(cpiAddress_Equals(remote, test_addr), "ops->getAddress incorrect");
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_GetAddressPair)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fd, pair, false);
+
+ const MetisAddressPair *test_pair = ops->getAddressPair(ops);
+
+ assertTrue(metisAddressPair_Equals(pair, test_pair), "ops->getRemoteAddress incorrect");
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_GetConnectionId)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ unsigned truth_connid = metisForwarder_GetNextConnectionId(metis) + 1;
+
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fd, pair, false);
+
+ assertTrue(ops->getConnectionId(ops) == truth_connid, "Got wrong connection id, expected %u got %u", truth_connid, ops->getConnectionId(ops));
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_HashCode)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_IsUp)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fd, pair, false);
+
+ assertTrue(ops->isUp(ops), "isUp incorrect, expected true, got false");
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_Send)
+{
+ // StreamConnection_Create needs a socket and address to represent the peer
+ // we use a socket pair so we can actaully read from it and verify what is sent.
+
+ int fds[2];
+ int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
+ assertFalse(failure, "Error socketpair: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fds[0], pair, false);
+
+ // ----------
+ // Create a fake message. Send does not care what the message is, it just writes it out.
+ // We include a real header, but it is not needed.
+
+ char message_str[] = "\x00Once upon a jiffie, in a stack far away, a dangling pointer found its way to the top of the heap.";
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) message_str;
+ hdr->payloadLength = htons(92);
+ hdr->headerLength = htons(0);
+
+ MetisMessage *sendmessage = metisMessage_CreateFromArray((uint8_t *) message_str, sizeof(message_str), 1, 2, metisForwarder_GetLogger(metis));
+
+ // ----------
+ // actually send it
+ ops->send(ops, NULL, sendmessage);
+ metisMessage_Release(&sendmessage);
+
+ // ----------
+ // turn the handleto crank
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ // ----------
+ // Now read the result from our end of the socket pair.
+
+ uint8_t read_buffer[1024];
+
+ // read it and verify
+ ssize_t read_length = read(fds[1], read_buffer, 1024);
+ assertTrue(read_length == sizeof(message_str),
+ "Incorrect read length, expected %zu got %zd: (%d) %s",
+ sizeof(message_str), read_length, errno, strerror(errno));
+
+ assertTrue(memcmp(read_buffer, message_str, sizeof(message_str)) == 0, "read_buffer does not match message_str");
+
+ // ----------
+ // hurray, no messages where harmed in this experiment
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ close(fds[0]);
+ close(fds[1]);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+}
+
+LONGBOW_TEST_CASE(Local, metisStreamConnection_GetConnectionType)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr_local;
+ addr_local.sin_addr.s_addr = htonl(0x01020304);
+ addr_local.sin_family = AF_INET;
+ addr_local.sin_port = htons(56);
+
+ struct sockaddr_in addr_remote;
+ addr_remote.sin_addr.s_addr = htonl(0x0708090A);
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(12);
+
+ CPIAddress *local = cpiAddress_CreateFromInet(&addr_local);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(local, remote);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ MetisIoOperations *ops = metisStreamConnection_AcceptConnection(metis, fd, pair, false);
+
+ CPIConnectionType connType = _metisStreamConnection_GetConnectionType(ops);
+ assertTrue(connType == cpiConnection_TCP, "Wrong connection type expected %d got %d", cpiConnection_TCP, connType);
+
+ ops->destroy(&ops);
+ metisForwarder_Destroy(&metis);
+ cpiAddress_Destroy(&local);
+ cpiAddress_Destroy(&remote);
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Local, printConnection)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, readMessage)
+{
+ char message_str[] = "\x00Once upon a jiffie, in a stack far away, a dangling pointer found its way to the top of the heap.";
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) message_str;
+ hdr->payloadLength = htons(92);
+ hdr->headerLength = htons(0);
+
+ PARCEventBuffer *buff = parcEventBuffer_Create();
+ parcEventBuffer_Append(buff, message_str, sizeof(message_str));
+
+ _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
+ assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));
+ stream->nextMessageLength = parcEventBuffer_GetLength(buff);
+ stream->id = 77;
+
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ stream->logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+
+ MetisMessage *message = _readMessage(stream, 444, buff);
+
+ assertNotNull(message, "Got null message from readMessage");
+ assertTrue(parcEventBuffer_GetLength(buff) == 0, "Did not drain input buffer, expected 0 got %zu", parcEventBuffer_GetLength(buff));
+ //assertTrue(metisMessage_Length(message) == sizeof(message_str),
+ //"Message length wrong, expected %zu got %zu",
+ //sizeof(message_str),
+ //metisMessage_Length(message));
+
+ metisMessage_Release(&message);
+ parcEventBuffer_Destroy(&buff);
+ metisLogger_Release(&stream->logger);
+ parcMemory_Deallocate((void **) &stream);
+}
+
+LONGBOW_TEST_CASE(Local, setConnectionState)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+/**
+ * Call like the beignning of a new packet, with stream->nextMessageLength set to 0
+ */
+LONGBOW_TEST_CASE(Local, single_read_ZeroNextMessageLength)
+{
+ PARCEventBuffer *buff = parcEventBuffer_Create();
+
+ // do it like a short read, only 12 bytes
+ parcEventBuffer_Append(buff, metisTestDataV0_EncodedInterest, 12);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
+ assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));
+ stream->metis = metis;
+ stream->nextMessageLength = 0;
+ stream->id = 77;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ stream->logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisMessage *message = _single_read(buff, stream);
+
+ assertNull(message, "message should be null, its a short read");
+ assertTrue(parcEventBuffer_GetLength(buff) == 12, "Should not have drained buffer, expected %d got %zu", 12, parcEventBuffer_GetLength(buff));
+ assertTrue(stream->nextMessageLength == sizeof(metisTestDataV0_EncodedInterest),
+ "NextMessageLength not set correctly, expected %zu got %zu",
+ sizeof(metisTestDataV0_EncodedInterest),
+ stream->nextMessageLength);
+
+ parcEventBuffer_Destroy(&buff);
+ metisLogger_Release(&stream->logger);
+ parcMemory_Deallocate((void **) &stream);
+ metisForwarder_Destroy(&metis);
+}
+
+/**
+ * Call with stream->nextMessageLength set correctly, but not enough bytes in the buffer
+ */
+LONGBOW_TEST_CASE(Local, single_read_PartialRead)
+{
+ PARCEventBuffer *buff = parcEventBuffer_Create();
+
+ // do it like a short read, only 12 bytes
+ parcEventBuffer_Append(buff, metisTestDataV0_EncodedInterest, 12);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
+ assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));
+ stream->metis = metis;
+ stream->nextMessageLength = sizeof(metisTestDataV0_EncodedInterest);
+ stream->id = 77;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ stream->logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisMessage *message = _single_read(buff, stream);
+
+ assertNull(message, "message should be null, its a short read");
+ assertTrue(parcEventBuffer_GetLength(buff) == 12, "Should not have drained buffer, expected %d got %zu", 12, parcEventBuffer_GetLength(buff));
+ assertTrue(stream->nextMessageLength == sizeof(metisTestDataV0_EncodedInterest),
+ "NextMessageLength not set correctly, expected %zu got %zu",
+ sizeof(metisTestDataV0_EncodedInterest),
+ stream->nextMessageLength);
+
+ parcEventBuffer_Destroy(&buff);
+ metisLogger_Release(&stream->logger);
+ parcMemory_Deallocate((void **) &stream);
+ metisForwarder_Destroy(&metis);
+}
+
+/**
+ * Call with enough bytes in the buffer to read the whole message
+ */
+LONGBOW_TEST_CASE(Local, single_read_FullRead)
+{
+ PARCEventBuffer *buff = parcEventBuffer_Create();
+
+ // do it like a full read
+ parcEventBuffer_Append(buff, metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest));
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
+ assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));
+ stream->metis = metis;
+ stream->nextMessageLength = sizeof(metisTestDataV0_EncodedInterest);
+ stream->id = 77;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ stream->logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisMessage *message = _single_read(buff, stream);
+
+ assertNotNull(message, "message should not be null, its a short read");
+ assertTrue(parcEventBuffer_GetLength(buff) == 0, "Should have drained buffer, expected %d got %zu", 0, parcEventBuffer_GetLength(buff));
+
+ // should reset the next message length after reading a whole packet
+ assertTrue(stream->nextMessageLength == 0,
+ "NextMessageLength not set correctly, expected %u got %zu",
+ 0,
+ stream->nextMessageLength);
+
+ metisMessage_Release(&message);
+ parcEventBuffer_Destroy(&buff);
+ metisLogger_Release(&stream->logger);
+ parcMemory_Deallocate((void **) &stream);
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Local, startNewMessage)
+{
+ _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
+ assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));
+
+ // add data to the buffer to fake out having read from the network
+ PARCEventBuffer *buff = parcEventBuffer_Create();
+ uint8_t *truth_message = parcMemory_Allocate(100);
+ assertNotNull(truth_message, "parcMemory_Allocate(%u) returned NULL", 100);
+
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) truth_message;
+ hdr->version = 0;
+ hdr->payloadLength = htons(92);
+ hdr->headerLength = htons(0);
+
+ parcEventBuffer_Append(buff, truth_message, 100);
+
+ stream->nextMessageLength = 0;
+
+ _startNewMessage(stream, buff, 100);
+
+ assertTrue(stream->nextMessageLength == 100, "nextMessageLength wrong, expected %d got %zu", 100, stream->nextMessageLength);
+
+ parcEventBuffer_Destroy(&buff);
+ parcMemory_Deallocate((void **) &stream);
+ parcMemory_Deallocate((void **) &truth_message);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_StreamConnection);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_TcpListener.c b/metis/ccnx/forwarder/metis/io/test/test_metis_TcpListener.c
new file mode 100644
index 00000000..87e51ae2
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_TcpListener.c
@@ -0,0 +1,364 @@
+/*
+ * 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.
+ */
+
+/*
+ * hard-coded in port 49009 on localhost
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TcpListener.c"
+#include <LongBow/unit-test.h>
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+#include <parc/algol/parc_Network.h>
+
+// for inet_pton
+#include <arpa/inet.h>
+
+#include <signal.h>
+
+struct test_set {
+ CPIAddress *listenAddress;
+ MetisForwarder *metis;
+ MetisListenerOps *ops;
+} TestSet;
+
+static void
+setupInetListener()
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(49009);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ TestSet.metis = metisForwarder_Create(NULL);
+ TestSet.ops = metisTcpListener_CreateInet(TestSet.metis, addr);
+ TestSet.listenAddress = cpiAddress_CreateFromInet(&addr);
+
+ // crank the event scheduler once
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) {0, 10000}));
+}
+
+static void
+setupInet6Listener()
+{
+ struct sockaddr_in6 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(49009);
+
+ // "::1" is the ipv6 loopback address
+ inet_pton(AF_INET6, "::1", &(addr.sin6_addr));
+
+ TestSet.metis = metisForwarder_Create(NULL);
+ TestSet.ops = metisTcpListener_CreateInet6(TestSet.metis, addr);
+ TestSet.listenAddress = cpiAddress_CreateFromInet6(&addr);
+
+ // crank the event scheduler once
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) {0, 10000}));
+}
+
+static void
+teardownListener()
+{
+ cpiAddress_Destroy(&TestSet.listenAddress);
+ TestSet.ops->destroy(&TestSet.ops);
+ metisForwarder_Destroy(&TestSet.metis);
+}
+
+struct sigaction save_sigchld;
+struct sigaction save_sigpipe;
+
+static void
+blockSigChild()
+{
+ struct sigaction ignore_action;
+ ignore_action.sa_handler = SIG_IGN;
+ sigemptyset(&ignore_action.sa_mask);
+ ignore_action.sa_flags = 0;
+
+ sigaction(SIGCHLD, NULL, &save_sigchld);
+ sigaction(SIGPIPE, NULL, &save_sigpipe);
+
+ sigaction(SIGCHLD, &ignore_action, NULL);
+ sigaction(SIGPIPE, &ignore_action, NULL);
+}
+
+static void
+unblockSigChild()
+{
+ sigaction(SIGCHLD, &save_sigchld, NULL);
+ sigaction(SIGPIPE, &save_sigpipe, NULL);
+}
+
+LONGBOW_TEST_RUNNER(metis_TcpListener)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global_Inet);
+ LONGBOW_RUN_TEST_FIXTURE(Global_Inet6);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_TcpListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_TcpListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ===========================================================================
+
+LONGBOW_TEST_FIXTURE(Global_Inet)
+{
+ LONGBOW_RUN_TEST_CASE(Global_Inet, metisListenerTcp_CreateInet);
+ LONGBOW_RUN_TEST_CASE(Global_Inet, metisListenerTcp_Connect);
+ LONGBOW_RUN_TEST_CASE(Global_Inet, metisListenerTcp_SendPacket);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global_Inet)
+{
+ setupInetListener();
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global_Inet)
+{
+ teardownListener();
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global_Inet, metisListenerTcp_CreateInet)
+{
+ // now verify that we are listening
+ // tcp4 0 0 127.0.0.1.49009 *.* LISTEN
+
+ blockSigChild();
+ FILE *fp = popen("netstat -an -p tcp", "r");
+ assertNotNull(fp, "Got null opening netstat for reading");
+
+ char str[1035];
+ bool found = false;
+ while (fgets(str, sizeof(str) - 1, fp) != NULL) {
+ if (strstr(str, "127.0.0.1.49009") != NULL) {
+ found = true;
+ break;
+ }
+ if (strstr(str, "127.0.0.1:49009") != NULL) {
+ found = true;
+ break;
+ }
+ }
+
+ pclose(fp);
+ unblockSigChild();
+
+ if (!found) {
+ int ret = system("netstat -an -p tcp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+
+ assertTrue(found, "Did not find 127.0.0.1.49009 in netstat output");
+}
+
+LONGBOW_TEST_CASE(Global_Inet, metisListenerTcp_Connect)
+{
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in serverAddress;
+ cpiAddress_GetInet(TestSet.listenAddress, &serverAddress);
+
+ int failure = connect(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) {0, 10000}));
+
+ struct sockaddr_in connectAddress;
+ socklen_t connectAddressLength = sizeof(connectAddress);
+ failure = getsockname(fd, (struct sockaddr *) &connectAddress, &connectAddressLength);
+ assertFalse(failure, "Error on getsockname: (%d) %s", errno, strerror(errno));
+ assertTrue(connectAddressLength == sizeof(struct sockaddr_in),
+ "connectAddressLength wrong size, expected %zu got %u",
+ sizeof(struct sockaddr_in), connectAddressLength);
+
+ // make sure its in the connection table
+ MetisConnectionTable *table = metisForwarder_GetConnectionTable(TestSet.metis);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&connectAddress);
+ MetisAddressPair *pair = metisAddressPair_Create(TestSet.listenAddress, remote);
+ const MetisConnection *conn = metisConnectionTable_FindByAddressPair(table, pair);
+ assertNotNull(conn, "Did not find connection in connection table");
+
+ cpiAddress_Destroy(&remote);
+ metisAddressPair_Release(&pair);
+
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Global_Inet, metisListenerTcp_SendPacket)
+{
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in serverAddress;
+ cpiAddress_GetInet(TestSet.listenAddress, &serverAddress);
+
+ int failure = connect(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) {0, 10000}));
+
+ ssize_t write_length = write(fd, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertFalse(write_length < 0, "Error on write: (%d) %s", errno, strerror(errno));
+ assertTrue(write_length == sizeof(metisTestDataV0_InterestWithName), "Got partial write, expected %zu got %zd", sizeof(metisTestDataV0_InterestWithName), write_length);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) {0, 10000}));
+
+ close(fd);
+}
+
+// ===========================================================================
+
+LONGBOW_TEST_FIXTURE(Global_Inet6)
+{
+ LONGBOW_RUN_TEST_CASE(Global_Inet6, metisListenerTcp_CreateInet6);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global_Inet6)
+{
+ setupInet6Listener();
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global_Inet6)
+{
+ teardownListener();
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global_Inet6, metisListenerTcp_CreateInet6)
+{
+ // now verify that we are listening
+ // tcp6 0 0 ::1.49009 *.* LISTEN
+
+ blockSigChild();
+ FILE *fp = popen("netstat -an -p tcp", "r");
+ assertNotNull(fp, "Got null opening netstat for reading");
+
+ char str[1035];
+ bool found = false;
+ while (fgets(str, sizeof(str) - 1, fp) != NULL) {
+ if (strstr(str, "::1.49009") != NULL) {
+ found = true;
+ break;
+ }
+ if (strstr(str, "::1:49009") != NULL) {
+ found = true;
+ break;
+ }
+ }
+
+ pclose(fp);
+ unblockSigChild();
+
+ if (!found) {
+ int ret = system("netstat -an -p tcp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+
+ assertTrue(found, "Did not find ::1.49009 in netstat output");
+}
+
+// ===========================================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisListenerTcp_Listen);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+/**
+ * Create a TCP INET listener then connect to it.
+ */
+LONGBOW_TEST_CASE(Local, metisListenerTcp_Listen)
+{
+ setupInetListener();
+
+ struct sockaddr_in addr_remote;
+
+ memset(&addr_remote, 0, sizeof(addr_remote));
+ addr_remote.sin_family = AF_INET;
+ addr_remote.sin_port = htons(49010);
+ inet_pton(AF_INET, "127.0.0.1", &(addr_remote.sin_addr));
+
+ _MetisTcpListener *tcp = (_MetisTcpListener *) TestSet.ops->context;
+
+ int fds[2];
+ int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
+ assertFalse(failure, "Failed with socketpair: (%d) %s", errno, strerror(errno));
+
+ _metisTcpListener_Listen(fds[0], (struct sockaddr *) &addr_remote, sizeof(addr_remote), tcp);
+
+ // now verify the connection is in the connection table
+ MetisConnectionTable *table = metisForwarder_GetConnectionTable(TestSet.metis);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&addr_remote);
+ MetisAddressPair *pair = metisAddressPair_Create(tcp->localAddress, remote);
+ const MetisConnection *conn = metisConnectionTable_FindByAddressPair(table, pair);
+ assertNotNull(conn, "Did not find connection in connection table");
+
+ cpiAddress_Destroy(&remote);
+ metisAddressPair_Release(&pair);
+ teardownListener();
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_TcpListener);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_TcpTunnel.c b/metis/ccnx/forwarder/metis/io/test/test_metis_TcpTunnel.c
new file mode 100644
index 00000000..2d86fb17
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_TcpTunnel.c
@@ -0,0 +1,216 @@
+/*
+ * 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 the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TcpTunnel.c"
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+// so we can see packet events
+#include "../../processor/test/testrig_MockTap.h"
+
+// inet_pton
+#include <arpa/inet.h>
+
+#include <fcntl.h>
+
+#ifndef INPORT_ANY
+#define INPORT_ANY 0
+#endif
+
+LONGBOW_TEST_RUNNER(metis_TcpTunnel)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ 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_TcpTunnel)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_TcpTunnel)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+typedef struct test_data {
+ MetisForwarder *metis;
+ MetisDispatcher *dispatcher;
+
+ int serverSocket;
+ struct sockaddr_in serverAddr;
+ struct sockaddr_in localAddr;
+
+ CPIAddress *localCpiAddress;
+ CPIAddress *remoteCpiAddress;
+
+ MetisIoOperations *tunnelOps;
+} TestData;
+
+static void
+listenToInet(TestData *data)
+{
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(fd < 0, "error on bind: (%d) %s", errno, strerror(errno));
+
+ // Set non-blocking flag
+ int flags = fcntl(fd, F_GETFL, NULL);
+ assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno);
+ int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno);
+
+ failure = bind(fd, (struct sockaddr *) &data->serverAddr, sizeof(struct sockaddr_in));
+ assertFalse(failure, "error on bind: (%d) %s", errno, strerror(errno));
+
+ failure = listen(fd, 16);
+ assertFalse(failure, "error on listen: (%d) %s", errno, strerror(errno));
+
+ data->serverSocket = fd;
+ socklen_t x = sizeof(data->serverAddr);
+ getsockname(fd, (struct sockaddr *) &data->serverAddr, &x);
+}
+
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisTcpTunnel_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisTcpTunnel_Create_ConnectionStartsDown);
+ LONGBOW_RUN_TEST_CASE(Global, metisTcpTunnel_Create_UpStateAfterAccept);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ memset(&testTap, 0, sizeof(testTap));
+
+ TestData *data = malloc(sizeof(TestData));
+ memset(data, 0, sizeof(TestData));
+
+ data->metis = metisForwarder_Create(NULL);
+ data->serverAddr.sin_family = PF_INET;
+ data->serverAddr.sin_port = INPORT_ANY;
+ inet_pton(AF_INET, "127.0.0.1", &(data->serverAddr.sin_addr));
+
+ data->localAddr.sin_family = PF_INET;
+ data->localAddr.sin_addr.s_addr = INADDR_ANY;
+ data->localAddr.sin_port = INPORT_ANY;
+
+ listenToInet(data);
+
+ data->localCpiAddress = cpiAddress_CreateFromInet(&data->localAddr);
+ data->remoteCpiAddress = cpiAddress_CreateFromInet(&data->serverAddr);
+
+ data->dispatcher = metisForwarder_GetDispatcher(data->metis);
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ cpiAddress_Destroy(&data->localCpiAddress);
+ cpiAddress_Destroy(&data->remoteCpiAddress);
+
+ close(data->serverSocket);
+ metisForwarder_Destroy(&data->metis);
+ free(data);
+
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisTcpTunnel_Create)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ data->tunnelOps = metisTcpTunnel_Create(data->metis, data->localCpiAddress, data->remoteCpiAddress);
+ assertNotNull(data->tunnelOps, "Got null IO operations for the tunnel");
+ data->tunnelOps->destroy(&data->tunnelOps);
+}
+
+
+LONGBOW_TEST_CASE(Global, metisTcpTunnel_Create_ConnectionStartsDown)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ data->tunnelOps = metisTcpTunnel_Create(data->metis, data->localCpiAddress, data->remoteCpiAddress);
+ assertFalse(data->tunnelOps->isUp(data->tunnelOps), "Connection is not down on start");
+ data->tunnelOps->destroy(&data->tunnelOps);
+}
+
+LONGBOW_TEST_CASE(Global, metisTcpTunnel_Create_UpStateAfterAccept)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ data->tunnelOps = metisTcpTunnel_Create(data->metis, data->localCpiAddress, data->remoteCpiAddress);
+
+ // run for a milli second
+ metisDispatcher_RunDuration(data->dispatcher, &((struct timeval) { 0, 1000 }));
+
+ // we should be able to accept
+ struct sockaddr_in clientAddr;
+ socklen_t clientAddrLength = sizeof(clientAddr);
+
+ int clientSocket = accept(data->serverSocket, (struct sockaddr *) &clientAddr, &clientAddrLength);
+ assertFalse(clientSocket < 0, "error on accept: (%d) %s", errno, strerror(errno));
+
+ // run for a milli second
+ metisDispatcher_RunDuration(data->dispatcher, &((struct timeval) { 0, 1000 }));
+
+ assertTrue(data->tunnelOps->isUp(data->tunnelOps), "Connection is not up after accept");
+ data->tunnelOps->destroy(&data->tunnelOps);
+}
+
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_TcpTunnel);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_UdpConnection.c b/metis/ccnx/forwarder/metis/io/test/test_metis_UdpConnection.c
new file mode 100644
index 00000000..db1f9c97
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_UdpConnection.c
@@ -0,0 +1,535 @@
+/*
+ * 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_UdpConnection.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/forwarder/metis/io/metis_UdpListener.h>
+#include <ccnx/forwarder/metis/config/metis_Configuration.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+// for inet_pton
+#include <arpa/inet.h>
+
+#define ALICE_PORT 49018
+#define BOB_PORT 49019
+
+// ---- Used to monitor Missive messages so we know when a connection is up
+typedef struct test_notifier_data {
+ MetisMissiveType type;
+ unsigned connectionid;
+} TestNotifierData;
+
+static void
+testNotifier(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ struct test_notifier_data *data = metisMessengerRecipient_GetRecipientContext(recipient);
+ data->type = metisMissive_GetType(missive);
+ data->connectionid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+}
+
+// ----
+
+typedef struct test_tap_data {
+ unsigned onReceiveCount;
+ MetisMessage *message;
+} TestTapData;
+
+static bool
+testTap_IsTapOnReceive(const MetisTap *tap)
+{
+ return true;
+}
+
+static void
+testTap_TapOnReceive(MetisTap *tap, const MetisMessage *message)
+{
+ TestTapData *mytap = (TestTapData *) tap->context;
+ mytap->onReceiveCount++;
+ if (mytap->message) {
+ metisMessage_Release(&mytap->message);
+ }
+
+ mytap->message = metisMessage_Acquire(message);
+}
+
+static MetisTap testTapTemplate = {
+ .context = NULL,
+ .isTapOnReceive = &testTap_IsTapOnReceive,
+ .isTapOnSend = NULL,
+ .isTapOnDrop = NULL,
+ .tapOnReceive = &testTap_TapOnReceive,
+ .tapOnSend = NULL,
+ .tapOnDrop = NULL
+};
+
+// --- Used to inspect packets received
+
+
+typedef struct test_data {
+ int remoteSocket;
+
+#define ALICE 0
+#define BOB 1
+
+ MetisForwarder *metis[2];
+ MetisListenerOps *listener[2];
+ MetisMessengerRecipient *recipient[2];
+ TestNotifierData notifierData[2];
+ MetisTap taps[2];
+ TestTapData tapData[2];
+} TestData;
+
+
+
+static void
+_crankHandle(TestData *data)
+{
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis[ALICE]), &((struct timeval) {0, 10000}));
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis[BOB]), &((struct timeval) {0, 10000}));
+}
+
+static void
+_setup(TestData *data, int side, uint16_t port)
+{
+ data->metis[side] = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis[side]), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ data->listener[side] = metisUdpListener_CreateInet(data->metis[side], addr);
+
+ // snoop events
+ data->recipient[side] = metisMessengerRecipient_Create(&data->notifierData[side], testNotifier);
+ MetisMessenger *messenger = metisForwarder_GetMessenger(data->metis[side]);
+ metisMessenger_Register(messenger, data->recipient[side]);
+
+ // snoop packets
+ memcpy(&data->taps[side], &testTapTemplate, sizeof(testTapTemplate));
+ data->taps[side].context = &data->tapData[side];
+ metisForwarder_AddTap(data->metis[side], &data->taps[side]);
+
+ // save in Metis
+ metisListenerSet_Add(metisForwarder_GetListenerSet(data->metis[side]), data->listener[side]);
+}
+
+/*
+ * Create a UDP socket pair
+ */
+static void
+_commonSetup(const LongBowTestCase *testCase)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+
+ _setup(data, ALICE, ALICE_PORT);
+ _setup(data, BOB, BOB_PORT);
+
+ _crankHandle(data);
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+_commonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // the listeners are stored in the respectie Metis, so we don't need to
+ // destroy those separately.
+
+ metisMessengerRecipient_Destroy(&data->recipient[ALICE]);
+ metisMessengerRecipient_Destroy(&data->recipient[BOB]);
+
+ if (data->tapData[ALICE].message) {
+ metisMessage_Release(&data->tapData[ALICE].message);
+ }
+
+ if (data->tapData[BOB].message) {
+ metisMessage_Release(&data->tapData[BOB].message);
+ }
+
+ metisForwarder_Destroy(&data->metis[ALICE]);
+ metisForwarder_Destroy(&data->metis[BOB]);
+
+ parcMemory_Deallocate((void **) &data);
+}
+
+LONGBOW_TEST_RUNNER(metis_UdpConnection)
+{
+ 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_UdpConnection)
+{
+ 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_UdpConnection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ //XXX: this test does not work anymore because we do not create the connection in metis
+ //LONGBOW_RUN_TEST_CASE(Global, metisUdpConnection_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ _commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ _commonTeardown(testCase);
+ 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;
+}
+
+/*
+ * Create connection from ALICE to BOB
+ */
+LONGBOW_TEST_CASE(Global, metisUdpConnection_Create)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, false);
+ metisAddressPair_Release(&pair);
+
+ _crankHandle(data);
+
+ // send a data packet to bring it up on BOB
+ MetisMessage *message = metisMessage_CreateFromArray(metisTestDataV1_Interest_NameA_Crc32c, sizeof(metisTestDataV1_Interest_NameA_Crc32c), 2, 3, metisForwarder_GetLogger(data->metis[ALICE]));
+
+ ops->send(ops, NULL, message);
+
+ metisMessage_Release(&message);
+
+ // wait until we indicate that the connection is up on Bob's side
+ while (data->notifierData[BOB].type == MetisMissiveType_ConnectionDestroyed) {
+ _crankHandle(data);
+ }
+
+ // and verify that the message was sent in to the message processor on Bob's side
+ assertTrue(data->tapData[BOB].onReceiveCount == 1, "Wrong receive count, expected 1 got %u", data->tapData[BOB].onReceiveCount);
+
+ ops->destroy(&ops);
+}
+
+// ===========================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _saveSockaddr_INET);
+ LONGBOW_RUN_TEST_CASE(Local, _saveSockaddr_INET6);
+ LONGBOW_RUN_TEST_CASE(Local, _send);
+ LONGBOW_RUN_TEST_CASE(Local, _getRemoteAddress);
+ LONGBOW_RUN_TEST_CASE(Local, _getAddressPair);
+ LONGBOW_RUN_TEST_CASE(Local, _getConnectionId);
+ LONGBOW_RUN_TEST_CASE(Local, _isUp);
+ LONGBOW_RUN_TEST_CASE(Local, _isLocal_True);
+ LONGBOW_RUN_TEST_CASE(Local, _setConnectionState);
+ LONGBOW_RUN_TEST_CASE(Local, _getConnectionType);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ _commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ _commonTeardown(testCase);
+ 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, _saveSockaddr_INET)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ _MetisUdpState *udpConnState = parcMemory_AllocateAndClear(sizeof(_MetisUdpState));
+ assertNotNull(udpConnState, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisUdpState));
+
+ udpConnState->metis = data->metis[ALICE];
+ udpConnState->logger = metisLogger_Acquire(metisForwarder_GetLogger(udpConnState->metis));
+
+ struct sockaddr_in sin1, sin2;
+
+ memset(&sin1, 0, sizeof(sin1));
+ sin1.sin_family = AF_INET;
+ sin1.sin_port = htons(ALICE_PORT);
+ inet_pton(AF_INET, "127.0.0.1", &(sin1.sin_addr));
+
+ memset(&sin2, 0, sizeof(sin2));
+ sin2.sin_family = AF_INET;
+ sin2.sin_port = htons(BOB_PORT);
+ inet_pton(AF_INET, "127.0.0.1", &(sin2.sin_addr));
+
+ CPIAddress *aliceAddress = cpiAddress_CreateFromInet(&sin1);
+ CPIAddress *bobAddress = cpiAddress_CreateFromInet(&sin2);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+
+ bool saved = _saveSockaddr(udpConnState, pair);
+ assertTrue(saved, "Failed to save address");
+ assertTrue(udpConnState->peerAddressLength == sizeof(sin1), "Wrong length, expected %zu got %u", sizeof(sin1), udpConnState->peerAddressLength);
+
+ cpiAddress_Destroy(&aliceAddress);
+ cpiAddress_Destroy(&bobAddress);
+
+ metisAddressPair_Release(&pair);
+ metisLogger_Release(&udpConnState->logger);
+ parcMemory_Deallocate((void **) &udpConnState->peerAddress);
+ parcMemory_Deallocate((void **) &udpConnState);
+}
+
+LONGBOW_TEST_CASE(Local, _saveSockaddr_INET6)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ _MetisUdpState *udpConnState = parcMemory_AllocateAndClear(sizeof(_MetisUdpState));
+ assertNotNull(udpConnState, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisUdpState));
+
+ udpConnState->metis = data->metis[ALICE];
+ udpConnState->logger = metisLogger_Acquire(metisForwarder_GetLogger(udpConnState->metis));
+
+ struct sockaddr_in6 sin1, sin2;
+
+ memset(&sin1, 0, sizeof(sin1));
+ sin1.sin6_family = AF_INET6;
+ sin1.sin6_port = htons(ALICE_PORT);
+ int ok = inet_pton(AF_INET6, "::1", &(sin1.sin6_addr));
+
+ if (ok) {
+ memset(&sin2, 0, sizeof(sin2));
+ sin2.sin6_family = AF_INET6;
+ sin2.sin6_port = htons(BOB_PORT);
+ inet_pton(AF_INET6, "::1", &(sin2.sin6_addr));
+
+ CPIAddress *aliceAddress = cpiAddress_CreateFromInet6(&sin1);
+ CPIAddress *bobAddress = cpiAddress_CreateFromInet6(&sin2);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+
+ bool saved = _saveSockaddr(udpConnState, pair);
+ assertTrue(saved, "Failed to save address");
+ assertTrue(udpConnState->peerAddressLength == sizeof(sin1), "Wrong length, expected %zu got %u", sizeof(sin1), udpConnState->peerAddressLength);
+
+ cpiAddress_Destroy(&aliceAddress);
+ cpiAddress_Destroy(&bobAddress);
+
+ metisAddressPair_Release(&pair);
+ } else {
+ testSkip("Skipping inet6 test");
+ }
+
+ metisLogger_Release(&udpConnState->logger);
+ parcMemory_Deallocate((void **) &udpConnState->peerAddress);
+ parcMemory_Deallocate((void **) &udpConnState);
+}
+
+LONGBOW_TEST_CASE(Local, _send)
+{
+}
+
+LONGBOW_TEST_CASE(Local, _getRemoteAddress)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, false);
+ metisAddressPair_Release(&pair);
+
+ // now run test
+ const CPIAddress *test = _getRemoteAddress(ops);
+ assertNotNull(test, "Got null remote address");
+ assertTrue(cpiAddress_Equals(test, bobAddress), "Addresses do not match");
+ ops->destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _getAddressPair)
+{
+}
+
+LONGBOW_TEST_CASE(Local, _getConnectionId)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, false);
+ metisAddressPair_Release(&pair);
+
+ // now run test
+ unsigned connid = _getConnectionId(ops);
+
+ assertTrue(connid > 0, "Expected positive connid, got 0");
+ ops->destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _isUp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, false);
+ metisAddressPair_Release(&pair);
+
+ // now run test
+ bool isup = _isUp(ops);
+
+ assertTrue(isup, "Expected connection to be up");
+ ops->destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _isLocal_True)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, true);
+ metisAddressPair_Release(&pair);
+
+ // now run test
+ bool islocal = _isLocal(ops);
+
+ assertTrue(islocal, "Expected connection to be local");
+ ops->destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _setConnectionState)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, true);
+ metisAddressPair_Release(&pair);
+
+ // now run test
+ _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops);
+
+ _setConnectionState(udpConnState, false);
+ bool isup = _isUp(ops);
+
+ assertFalse(isup, "Expected connection to be down");
+ ops->destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _getConnectionType)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisAddressPair *pair = metisAddressPair_Create(aliceAddress, bobAddress);
+ int fd = data->listener[ALICE]->getSocket(data->listener[ALICE]);
+ MetisIoOperations *ops = metisUdpConnection_Create(data->metis[ALICE], fd, pair, false);
+ metisAddressPair_Release(&pair);
+
+ // now run test
+ CPIConnectionType connType = _getConnectionType(ops);
+
+ assertTrue(connType == cpiConnection_UDP, "Expected connection to be %d got %d", cpiConnection_UDP, connType);
+ ops->destroy(&ops);
+}
+
+
+// ===========================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_UdpConnection);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_UdpListener.c b/metis/ccnx/forwarder/metis/io/test/test_metis_UdpListener.c
new file mode 100644
index 00000000..96dfc4fd
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_UdpListener.c
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ */
+
+/**
+ */
+
+/*
+ * hard-coded in port 49009 on localhost
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_UdpListener.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+// for inet_pton
+#include <arpa/inet.h>
+
+#include <signal.h>
+
+// ========================================================
+
+struct test_set {
+ CPIAddress *listenAddress;
+ MetisForwarder *metis;
+ MetisListenerOps *ops;
+} TestSet;
+
+static void
+setupInetListener()
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(49009);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ TestSet.metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(TestSet.metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ TestSet.ops = metisUdpListener_CreateInet(TestSet.metis, addr);
+ TestSet.listenAddress = cpiAddress_CreateFromInet(&addr);
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+}
+
+static void
+teardownListener()
+{
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+ cpiAddress_Destroy(&TestSet.listenAddress);
+ TestSet.ops->destroy(&TestSet.ops);
+ metisForwarder_Destroy(&TestSet.metis);
+}
+
+static bool
+setupInet6Listener()
+{
+ struct sockaddr_in6 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(49009);
+
+ // "::1" is the ipv6 loopback address
+ int ok = inet_pton(AF_INET6, "::1", &(addr.sin6_addr));
+ if (ok > 0) {
+ TestSet.metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(TestSet.metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ TestSet.ops = metisUdpListener_CreateInet6(TestSet.metis, addr);
+ TestSet.listenAddress = cpiAddress_CreateFromInet6(&addr);
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+ return true;
+ }
+ return false;
+}
+
+struct sigaction save_sigchld;
+struct sigaction save_sigpipe;
+
+static void
+blockSigChild()
+{
+ struct sigaction ignore_action;
+ ignore_action.sa_handler = SIG_IGN;
+ sigemptyset(&ignore_action.sa_mask);
+ ignore_action.sa_flags = 0;
+
+ sigaction(SIGCHLD, NULL, &save_sigchld);
+ sigaction(SIGPIPE, NULL, &save_sigpipe);
+
+ sigaction(SIGCHLD, &ignore_action, NULL);
+ sigaction(SIGPIPE, &ignore_action, NULL);
+}
+
+static void
+unblockSigChild()
+{
+ sigaction(SIGCHLD, &save_sigchld, NULL);
+ sigaction(SIGPIPE, &save_sigpipe, NULL);
+}
+
+// ========================================================
+
+LONGBOW_TEST_RUNNER(metis_UdpListener)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global_Inet);
+ // XXX: Udp code has issues. It should check return values from calls.
+ // There are bugs in the UDP code that need to be fixed. These are shown in
+ // this test. The code needs to be fixed first.
+ //LONGBOW_RUN_TEST_FIXTURE(Global_Inet6);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_UdpListener)
+{
+ 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_UdpListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ================================================================================
+
+LONGBOW_TEST_FIXTURE(Global_Inet)
+{
+ LONGBOW_RUN_TEST_CASE(Global_Inet, metisListenerUdp_CreateInet);
+ //XXX: this does not work anymore because we do not create the udp connection
+ //LONGBOW_RUN_TEST_CASE(Global_Inet, metisListenerUdp_Connect);
+ LONGBOW_RUN_TEST_CASE(Global_Inet, metisListenerUdp_SendPacket);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global_Inet)
+{
+ setupInetListener();
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global_Inet)
+{
+ teardownListener();
+ 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_Inet, metisListenerUdp_CreateInet)
+{
+ // now verify that we are listening
+ // udp4 0 0 127.0.0.1.49009 *.* LISTEN
+
+ blockSigChild();
+ FILE *fp = popen("netstat -an -p udp", "r");
+ assertNotNull(fp, "Got null opening netstat for reading");
+
+ char str[1035];
+ bool found = false;
+ while (fgets(str, sizeof(str) - 1, fp) != NULL) {
+ if (strstr(str, "127.0.0.1.49009") != NULL) {
+ found = true;
+ break;
+ }
+ if (strstr(str, "127.0.0.1:49009") != NULL) {
+ found = true;
+ break;
+ }
+ }
+
+ pclose(fp);
+ unblockSigChild();
+
+ if (!found) {
+ int ret = system("netstat -an -p udp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+
+ assertTrue(found, "Did not find 127.0.0.1.49009 in netstat output");
+}
+
+LONGBOW_TEST_CASE(Global_Inet, metisListenerUdp_Connect)
+{
+ int fd = socket(PF_INET, SOCK_DGRAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in serverAddress;
+ cpiAddress_GetInet(TestSet.listenAddress, &serverAddress);
+
+ int failure = connect(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ struct sockaddr_in connectAddress;
+ socklen_t connectAddressLength = sizeof(connectAddress);
+ failure = getsockname(fd, (struct sockaddr *) &connectAddress, &connectAddressLength);
+ assertFalse(failure, "Error on getsockname: (%d) %s", errno, strerror(errno));
+ assertTrue(connectAddressLength == sizeof(struct sockaddr_in),
+ "connectAddressLength wrong size, expected %zu got %u",
+ sizeof(struct sockaddr_in), connectAddressLength);
+
+ // Unlike TCP, we need to actually send something
+ ssize_t nwritten = write(fd, metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest));
+ assertTrue(nwritten == sizeof(metisTestDataV0_EncodedInterest), "Error on write expected %zu got %zd", sizeof(metisTestDataV0_EncodedInterest), nwritten);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ // make sure its in the connection table
+ MetisConnectionTable *table = metisForwarder_GetConnectionTable(TestSet.metis);
+ CPIAddress *remote = cpiAddress_CreateFromInet(&connectAddress);
+ MetisAddressPair *pair = metisAddressPair_Create(TestSet.listenAddress, remote);
+ const MetisConnection *conn = metisConnectionTable_FindByAddressPair(table, pair);
+ assertNotNull(conn, "Did not find connection in connection table");
+
+ cpiAddress_Destroy(&remote);
+ metisAddressPair_Release(&pair);
+
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Global_Inet, metisListenerUdp_SendPacket)
+{
+ int fd = socket(PF_INET, SOCK_DGRAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in serverAddress;
+ cpiAddress_GetInet(TestSet.listenAddress, &serverAddress);
+
+ int failure = connect(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ assertFalse(failure, "Error on connect socket %d: (%d) %s\n", fd, errno, strerror(errno));
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ ssize_t write_length = write(fd, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertFalse(write_length < 0, "Error on write: (%d) %s", errno, strerror(errno));
+ assertTrue(write_length == sizeof(metisTestDataV0_InterestWithName), "Got partial write, expected %zu got %zd", sizeof(metisTestDataV0_InterestWithName), write_length);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ close(fd);
+}
+
+// ================================================================================
+
+LONGBOW_TEST_FIXTURE(Global_Inet6)
+{
+ LONGBOW_RUN_TEST_CASE(Global_Inet6, metisListenerUdp_CreateInet6);
+ LONGBOW_RUN_TEST_CASE(Global_Inet6, metisListenerUdp_Connect);
+ LONGBOW_RUN_TEST_CASE(Global_Inet6, metisListenerUdp_SendPacket);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global_Inet6)
+{
+ bool success = setupInet6Listener();
+ if (!success) {
+ return LONGBOW_STATUS_SKIPPED;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global_Inet6)
+{
+ teardownListener();
+ 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_Inet6, metisListenerUdp_CreateInet6)
+{
+ // now verify that we are listening
+ // udp6 0 0 ::1.49009 *.*
+
+ blockSigChild();
+ FILE *fp = popen("netstat -an -p udp", "r");
+ assertNotNull(fp, "Got null opening netstat for reading");
+
+ char str[1035];
+ bool found = false;
+ while (fgets(str, sizeof(str) - 1, fp) != NULL) {
+ //printf("%s\n", str);
+ if (strstr(str, "::1.49009") != NULL) {
+ found = true;
+ break;
+ }
+ if (strstr(str, "::1:49009") != NULL) {
+ found = true;
+ break;
+ }
+ }
+
+ pclose(fp);
+ unblockSigChild();
+
+ if (!found) {
+ int ret = system("netstat -an -p udp");
+ assertTrue(ret != -1, "Error on system() call");
+ }
+
+ assertTrue(found, "Did not find 127.0.0.1.49009 in netstat output");
+}
+
+LONGBOW_TEST_CASE(Global_Inet6, metisListenerUdp_Connect)
+{
+ int fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in6 serverAddress;
+ cpiAddress_GetInet6(TestSet.listenAddress, &serverAddress);
+
+ int failure = connect(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ struct sockaddr_in6 connectAddress;
+ socklen_t connectAddressLength = sizeof(connectAddress);
+ failure = getsockname(fd, (struct sockaddr *) &connectAddress, &connectAddressLength);
+ assertFalse(failure, "Error on getsockname: (%d) %s", errno, strerror(errno));
+ assertTrue(connectAddressLength == sizeof(struct sockaddr_in6),
+ "connectAddressLength wrong size, expected %zu got %u",
+ sizeof(struct sockaddr_in6), connectAddressLength);
+
+ // Unlike TCP, we need to actually send something
+ ssize_t nwritten = write(fd, metisTestDataV1_Interest_NameA_Crc32c, sizeof(metisTestDataV1_Interest_NameA_Crc32c));
+ assertTrue(nwritten == sizeof(metisTestDataV1_Interest_NameA_Crc32c), "Write failed, expected %zu got %zd", sizeof(metisTestDataV1_Interest_NameA_Crc32c), nwritten);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ // make sure its in the connection table
+ MetisConnectionTable *table = metisForwarder_GetConnectionTable(TestSet.metis);
+ CPIAddress *remote = cpiAddress_CreateFromInet6(&connectAddress);
+ MetisAddressPair *pair = metisAddressPair_Create(TestSet.listenAddress, remote);
+ const MetisConnection *conn = metisConnectionTable_FindByAddressPair(table, pair);
+ assertNotNull(conn, "Did not find connection in connection table");
+
+ cpiAddress_Destroy(&remote);
+ metisAddressPair_Release(&pair);
+
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Global_Inet6, metisListenerUdp_SendPacket)
+{
+ int fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ struct sockaddr_in6 serverAddress;
+ cpiAddress_GetInet6(TestSet.listenAddress, &serverAddress);
+
+ int failure = connect(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ assertFalse(failure, "Error on connect socket %d: (%d) %s\n", fd, errno, strerror(errno));
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ ssize_t write_length = write(fd, metisTestDataV1_Interest_NameA_Crc32c, sizeof(metisTestDataV1_Interest_NameA_Crc32c));
+ assertFalse(write_length < 0, "Error on write: (%d) %s", errno, strerror(errno));
+ assertTrue(write_length == sizeof(metisTestDataV1_Interest_NameA_Crc32c),
+ "Got partial write, expected %zu got %zd",
+ sizeof(metisTestDataV1_Interest_NameA_Crc32c), write_length);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(TestSet.metis), &((struct timeval) { 0, 10000 }));
+
+ close(fd);
+}
+
+// ================================================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _getInterfaceIndex);
+ LONGBOW_RUN_TEST_CASE(Local, _getListenAddress);
+ LONGBOW_RUN_TEST_CASE(Local, _getEncapType);
+ LONGBOW_RUN_TEST_CASE(Local, _getSocket);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ setupInetListener();
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ teardownListener();
+ 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, _getInterfaceIndex)
+{
+ unsigned test = _getInterfaceIndex(TestSet.ops);
+ assertTrue(test > 0, "Unexpected interface index: %u", test);
+}
+
+LONGBOW_TEST_CASE(Local, _getListenAddress)
+{
+ const CPIAddress *listenAddr = _getListenAddress(TestSet.ops);
+ assertNotNull(listenAddr, "Got null listen address");
+}
+
+LONGBOW_TEST_CASE(Local, _getEncapType)
+{
+ MetisEncapType type = _getEncapType(TestSet.ops);
+ assertTrue(type == METIS_ENCAP_UDP, "Unexpected address type, got %d expected %d", type, METIS_ENCAP_UDP);
+}
+
+LONGBOW_TEST_CASE(Local, _getSocket)
+{
+ int fd = _getSocket(TestSet.ops);
+ assertTrue(fd > 0, "Unexpected socket, got %d, expected positive", fd);
+}
+
+
+// ================================================================================
+
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_UdpListener);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/test_metis_UdpTunnel.c b/metis/ccnx/forwarder/metis/io/test/test_metis_UdpTunnel.c
new file mode 100644
index 00000000..cba18ed3
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/test_metis_UdpTunnel.c
@@ -0,0 +1,327 @@
+/*
+ * 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.
+ */
+
+/**
+ * Need a solution to avoid hard-coding port numbers
+ *
+ */
+
+#include "../metis_UdpTunnel.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/forwarder/metis/io/metis_UdpListener.h>
+#include <ccnx/forwarder/metis/config/metis_Configuration.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+// for inet_pton
+#include <arpa/inet.h>
+
+#define ALICE_PORT 49028
+#define BOB_PORT 49029
+
+// ---- Used to monitor Missive messages so we know when a connection is up
+typedef struct test_notifier_data {
+ MetisMissiveType type;
+ unsigned connectionid;
+} TestNotifierData;
+
+static void
+testNotifier(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ struct test_notifier_data *data = metisMessengerRecipient_GetRecipientContext(recipient);
+ data->type = metisMissive_GetType(missive);
+ data->connectionid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+}
+
+// ----
+
+typedef struct test_tap_data {
+ unsigned onReceiveCount;
+ MetisMessage *message;
+} TestTapData;
+
+static bool
+testTap_IsTapOnReceive(const MetisTap *tap)
+{
+ return true;
+}
+
+static void
+testTap_TapOnReceive(MetisTap *tap, const MetisMessage *message)
+{
+ TestTapData *mytap = (TestTapData *) tap->context;
+ mytap->onReceiveCount++;
+ if (mytap->message) {
+ metisMessage_Release(&mytap->message);
+ }
+
+ mytap->message = metisMessage_Acquire(message);
+}
+
+static MetisTap testTapTemplate = {
+ .context = NULL,
+ .isTapOnReceive = &testTap_IsTapOnReceive,
+ .isTapOnSend = NULL,
+ .isTapOnDrop = NULL,
+ .tapOnReceive = &testTap_TapOnReceive,
+ .tapOnSend = NULL,
+ .tapOnDrop = NULL
+};
+
+// --- Used to inspect packets received
+
+
+typedef struct test_data {
+ int remoteSocket;
+
+#define ALICE 0
+#define BOB 1
+
+ MetisForwarder *metis[2];
+ MetisListenerOps *listener[2];
+ MetisMessengerRecipient *recipient[2];
+ TestNotifierData notifierData[2];
+ MetisTap taps[2];
+ TestTapData tapData[2];
+} TestData;
+
+
+
+static void
+_crankHandle(TestData *data)
+{
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis[ALICE]), &((struct timeval) {0, 10000}));
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis[BOB]), &((struct timeval) {0, 10000}));
+}
+
+static void
+_setup(TestData *data, int side, uint16_t port)
+{
+ data->metis[side] = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis[side]), MetisLoggerFacility_IO, PARCLogLevel_Error);
+
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ data->listener[side] = metisUdpListener_CreateInet(data->metis[side], addr);
+
+ // snoop events
+ data->recipient[side] = metisMessengerRecipient_Create(&data->notifierData[side], testNotifier);
+ MetisMessenger *messenger = metisForwarder_GetMessenger(data->metis[side]);
+ metisMessenger_Register(messenger, data->recipient[side]);
+
+ // snoop packets
+ memcpy(&data->taps[side], &testTapTemplate, sizeof(testTapTemplate));
+ data->taps[side].context = &data->tapData[side];
+ metisForwarder_AddTap(data->metis[side], &data->taps[side]);
+
+ // save in Metis
+ metisListenerSet_Add(metisForwarder_GetListenerSet(data->metis[side]), data->listener[side]);
+}
+
+/*
+ * Create a UDP socket pair
+ */
+static void
+_commonSetup(const LongBowTestCase *testCase)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+
+ _setup(data, ALICE, ALICE_PORT);
+ _setup(data, BOB, BOB_PORT);
+
+ _crankHandle(data);
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+_commonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // the listeners are stored in the respectie Metis, so we don't need to
+ // destroy those separately.
+
+ metisMessengerRecipient_Destroy(&data->recipient[ALICE]);
+ metisMessengerRecipient_Destroy(&data->recipient[BOB]);
+
+ if (data->tapData[ALICE].message) {
+ metisMessage_Release(&data->tapData[ALICE].message);
+ }
+
+ if (data->tapData[BOB].message) {
+ metisMessage_Release(&data->tapData[BOB].message);
+ }
+
+ metisForwarder_Destroy(&data->metis[ALICE]);
+ metisForwarder_Destroy(&data->metis[BOB]);
+
+ parcMemory_Deallocate((void **) &data);
+}
+
+// ==================================
+
+LONGBOW_TEST_RUNNER(metis_UdpTunnel)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_UdpTunnel)
+{
+ 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_UdpTunnel)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisUdpTunnel_CreateOnListener);
+ //XXX: this test does not work anymore beacuase we don't create the connection in metis
+ //LONGBOW_RUN_TEST_CASE(Global, metisUdpTunnel_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisUdpTunnel_Create_MismatchedTypes);
+ LONGBOW_RUN_TEST_CASE(Global, metisUdpTunnel_Create_NotFound);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ _commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ _commonTeardown(testCase);
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+
+LONGBOW_TEST_CASE(Global, metisUdpTunnel_CreateOnListener)
+{
+}
+
+/*
+ * Create from Alice to Bob
+ */
+LONGBOW_TEST_CASE(Global, metisUdpTunnel_Create)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+ const CPIAddress *bobAddress = data->listener[BOB]->getListenAddress(data->listener[BOB]);
+
+ MetisIoOperations *ops = metisUdpTunnel_Create(data->metis[ALICE], aliceAddress, bobAddress);
+
+ _crankHandle(data);
+
+ // send a data packet to bring it up on BOB
+ MetisMessage *message = metisMessage_CreateFromArray(metisTestDataV1_Interest_NameA_Crc32c, sizeof(metisTestDataV1_Interest_NameA_Crc32c), 2, 3, metisForwarder_GetLogger(data->metis[ALICE]));
+
+ ops->send(ops, NULL, message);
+
+ metisMessage_Release(&message);
+
+ // wait until we indicate that the connection is up on Bob's side
+ while (data->notifierData[BOB].type == MetisMissiveType_ConnectionDestroyed) {
+ _crankHandle(data);
+ }
+
+ // and verify that the message was sent in to the message processor on Bob's side
+ assertTrue(data->tapData[BOB].onReceiveCount == 1, "Wrong receive count, expected 1 got %u", data->tapData[BOB].onReceiveCount);
+
+ ops->destroy(&ops);
+}
+
+/*
+ * sockets not same address family
+ */
+LONGBOW_TEST_CASE(Global, metisUdpTunnel_Create_MismatchedTypes)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ const CPIAddress *aliceAddress = data->listener[ALICE]->getListenAddress(data->listener[ALICE]);
+
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ CPIAddress *bobAddress = cpiAddress_CreateFromInet6(&sin6);
+
+ MetisIoOperations *ops = metisUdpTunnel_Create(data->metis[ALICE], aliceAddress, bobAddress);
+ assertNull(ops, "Should have gotten null return for mismatched address types");
+
+ cpiAddress_Destroy(&bobAddress);
+}
+
+/*
+ * Listener not found
+ */
+LONGBOW_TEST_CASE(Global, metisUdpTunnel_Create_NotFound)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // set Bob's notifier to a known bad state
+ data->notifierData[BOB].type = MetisMissiveType_ConnectionDestroyed;
+
+ // Create a connection from Alice to Bob
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ CPIAddress *aliceAddress = cpiAddress_CreateFromInet(&sin);
+ CPIAddress *bobAddress = cpiAddress_CreateFromInet(&sin);
+
+ MetisIoOperations *ops = metisUdpTunnel_Create(data->metis[ALICE], aliceAddress, bobAddress);
+ assertNull(ops, "Should have gotten null return for mismatched address types");
+
+ cpiAddress_Destroy(&aliceAddress);
+ cpiAddress_Destroy(&bobAddress);
+}
+
+
+
+// ==================================
+
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_UdpTunnel);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.c b/metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.c
new file mode 100644
index 00000000..d5c641d2
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.c
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+/**
+ * GenericEther mockup for testing metis_EtherListener.
+ *
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Deque.h>
+#include <parc/algol/parc_Buffer.h>
+#include <parc/algol/parc_EventBuffer.h>
+
+#include <ccnx/forwarder/metis/core/metis_System.h>
+#include <ccnx/forwarder/metis/io/metis_GenericEther.h>
+
+#include <LongBow/runtime.h>
+
+struct metis_generic_ether {
+ PARCDeque *inputQueue;
+ int testSocket;
+ int etherSocket;
+ PARCBuffer *macAddress;
+ uint16_t ethertype;
+ unsigned refcount;
+ MetisLogger *logger;
+ unsigned mtu;
+};
+
+void
+_metisGenericEther_Destroy(MetisGenericEther **etherPtr)
+{
+ MetisGenericEther *ether = *etherPtr;
+ PARCBuffer *buffer;
+
+ // the input queue is simple byte arrays of ethernet frames
+ while ((buffer = parcDeque_RemoveFirst(ether->inputQueue))) {
+ parcBuffer_Release(&buffer);
+ }
+
+ metisLogger_Release(&ether->logger);
+ parcDeque_Release(&ether->inputQueue);
+ parcBuffer_Release(&ether->macAddress);
+
+ close(ether->testSocket);
+ close(ether->etherSocket);
+}
+
+parcObject_ExtendPARCObject(MetisGenericEther, _metisGenericEther_Destroy, NULL, NULL, NULL, NULL, NULL, NULL);
+
+parcObject_ImplementAcquire(metisGenericEther, MetisGenericEther);
+
+parcObject_ImplementRelease(metisGenericEther, MetisGenericEther);
+
+MetisGenericEther *
+metisGenericEther_Create(MetisForwarder *metis, const char *deviceName, uint16_t etherType)
+{
+ assertNotNull(deviceName, "Parameter deviceName must be non-null");
+ assertTrue(etherType >= 0x0600, "EtherType must be greater than or equal to 0x0600");
+
+ MetisGenericEther *ether = parcObject_CreateInstance(MetisGenericEther);
+
+ int fd[2];
+ int failure = socketpair(PF_LOCAL, SOCK_DGRAM, 0, fd);
+ assertFalse(failure, "Error on socketpair");
+
+ ether->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis));
+ ether->testSocket = fd[0];
+ ether->etherSocket = fd[1];
+ ether->ethertype = etherType;
+ ether->inputQueue = parcDeque_Create();
+ ether->mtu = 4000;
+
+ // Set non-blocking flag
+ int flags = fcntl(ether->testSocket, F_GETFL, NULL);
+ assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno);
+ failure = fcntl(ether->testSocket, F_SETFL, flags | O_NONBLOCK);
+ assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno);
+ flags = fcntl(ether->etherSocket, F_GETFL, NULL);
+ assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno);
+ failure = fcntl(ether->etherSocket, F_SETFL, flags | O_NONBLOCK);
+ assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno);
+
+ // If we are passed a real interface name, use its MAC address otherwise make up something
+ CPIAddress *realMacAddress = metisSystem_GetMacAddressByName(metis, deviceName);
+
+ if (realMacAddress) {
+ PARCBuffer *realMac = cpiAddress_GetLinkAddress(realMacAddress);
+ ether->macAddress = parcBuffer_Copy(realMac);
+ cpiAddress_Destroy(&realMacAddress);
+ } else {
+ uint8_t macAddress[] = { 1, 2, 3, 4, 5, 6 };
+
+ // copy up to 6 bytes of the deviceName in to the mac address, for debugging
+ size_t maxbytes = (strlen(deviceName) > 6) ? 6 : strlen(deviceName);
+ memcpy(macAddress, deviceName, maxbytes);
+ ether->macAddress = parcBuffer_Allocate(sizeof(macAddress));
+ parcBuffer_PutArray(ether->macAddress, sizeof(macAddress), macAddress);
+ parcBuffer_Flip(ether->macAddress);
+ }
+
+ return ether;
+}
+
+int
+metisGenericEther_GetDescriptor(const MetisGenericEther *ether)
+{
+ return ether->etherSocket;
+}
+
+bool
+metisGenericEther_ReadNextFrame(MetisGenericEther *ether, PARCEventBuffer *buffer)
+{
+ // read a byte off the etherSocket if available to clear the notification.
+ // its non-blocking so no harm if nothing there.
+ uint8_t one;
+ ssize_t nread = read(ether->etherSocket, &one, 1);
+ assertTrue(errno == EWOULDBLOCK || nread > -1, "Error on read");
+
+ if (!parcDeque_IsEmpty(ether->inputQueue)) {
+ PARCBuffer *frame = parcDeque_RemoveFirst(ether->inputQueue);
+ uint8_t *bytes = parcBuffer_Overlay(frame, 0);
+ size_t length = parcBuffer_Remaining(frame);
+
+ parcEventBuffer_Append(buffer, bytes, length);
+ parcBuffer_Release(&frame);
+ return true;
+ }
+ return false;
+}
+
+bool
+metisGenericEther_SendFrame(MetisGenericEther *ether, PARCEventBuffer *buffer)
+{
+ assertNotNull(ether, "Parameter ether must be non-null");
+
+ // cannot use parcEventBuffer_WriteToFileDescriptor because we need to write the length in one go, not use the
+ // iovec approach in parcEventBuffer_WriteToFileDescriptor. It can cause problems on some platforms.
+
+ uint8_t *linear = parcEventBuffer_Pullup(buffer, -1);
+ size_t length = parcEventBuffer_GetLength(buffer);
+
+ ssize_t written = write(ether->etherSocket, linear, length);
+ if (written == length) {
+ return true;
+ }
+
+ return false;
+}
+
+PARCBuffer *
+metisGenericEther_GetMacAddress(const MetisGenericEther *ether)
+{
+ return ether->macAddress;
+}
+
+uint16_t
+metisGenericEther_GetEtherType(const MetisGenericEther *ether)
+{
+ assertNotNull(ether, "Parameter ether must be non-null");
+ return ether->ethertype;
+}
+
+unsigned
+metisGenericEther_GetMTU(const MetisGenericEther *ether)
+{
+ return ether->mtu;
+}
+
+// =========
+// Extra functions for testing
+
+int
+mockGenericEther_GetTestDescriptor(MetisGenericEther *ether)
+{
+ return ether->testSocket;
+}
+
+void
+mockGenericEther_QueueFrame(MetisGenericEther *ether, PARCBuffer *ethernetFrame)
+{
+ parcDeque_Append(ether->inputQueue, parcBuffer_Acquire(ethernetFrame));
+}
+
+void
+mockGenericEther_Notify(MetisGenericEther *ether)
+{
+ // notify the etherSocket by writing to the testSocket
+ uint8_t one = 1;
+ ssize_t nwritten = write(ether->testSocket, &one, 1);
+ assertTrue(nwritten == 1, "Error on write, expected 1, got %zd", nwritten);
+}
+
+/*
+ * Create an Ethernet frame enclosing the ccnxPacket. does not include the FCS.
+ */
+PARCBuffer *
+mockGenericEther_createFrame(size_t length, const uint8_t *ccnxPacket, const uint8_t dmac[ETHER_ADDR_LEN], const uint8_t smac[ETHER_ADDR_LEN], uint16_t ethertype)
+{
+ size_t totalLength = sizeof(struct ether_header) + length;
+ PARCBuffer *buffer = parcBuffer_Allocate(totalLength);
+
+ struct ether_header hdr;
+
+ memcpy(hdr.ether_dhost, dmac, ETHER_ADDR_LEN);
+ memcpy(hdr.ether_shost, smac, ETHER_ADDR_LEN);
+ hdr.ether_type = htons(ethertype);
+
+ parcBuffer_PutArray(buffer, sizeof(hdr), (uint8_t *) &hdr);
+
+ parcBuffer_PutArray(buffer, length, ccnxPacket);
+
+ parcBuffer_Flip(buffer);
+ return buffer;
+}
+
diff --git a/metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.h b/metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.h
new file mode 100644
index 00000000..dc89841e
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/testrig_GenericEther.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file testrig_GenericEther.h
+ * @brief A mockup of a platform Ethernet
+ *
+ * The mockup is connected to a socketpair, so you can read frames that the metis_EtherListener sends.
+ * It also has an input queue so you can queue frames to be read by metis_EtherListener.
+ *
+ * This mockup implements the metis_GenericEther.h API plus two additional functions for the mockup.
+ *
+ */
+
+#ifndef Metis_testrig_GenericEther_h
+#define Metis_testrig_GenericEther_h
+
+#include <ccnx/forwarder/metis/io/metis_GenericEther.h>
+#include <parc/algol/parc_Buffer.h>
+
+/**
+ * Returns the other end of a socketpair that mocks up the ethernet wire
+ *
+ * The mockup does not connect to a RAW or BPF socket, it connects to a socketpair.
+ * This function gets the remote end of the socket pair, which is where you can read
+ * frames that you send.
+ *
+ * DO NOT WRITE PACKETS HERE. To queue packets for input, use mockGenericEther_QueueFrame().
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @retval number System socketpair
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+int mockGenericEther_GetTestDescriptor(MetisGenericEther *ether);
+
+/**
+ * Queue an Ethernet frame to be read
+ *
+ * The mockup maintains an input queue (deque) for input frames. These should be full
+ * Ethernet frames (not including the frame check sequence).
+ *
+ * This stores a reference, so caller must also release the PARCBuffer.
+ *
+ * This function will not notify the etherSocket being watched by Libevent in Metis.
+ * To notify Libevent, use mockGenericEther_Notify() after queuing packets.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void mockGenericEther_QueueFrame(MetisGenericEther *ether, PARCBuffer *ethernetFrame);
+
+/**
+ * Writes a byte to the etherSocket
+ *
+ * Tickles Libevent by writing a byte to the etherSocket.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void mockGenericEther_Notify(MetisGenericEther *ether);
+
+/**
+ * Convenience function to encapsulate a packet in an ethernet frame
+ *
+ * Creates a PARCBuffer that has an Ethernet header followed by a user-provided byte array.
+ * Does not include the frame check sequence.
+ *
+ * @param [in] length The length of the ccnxPacket to be encapsulated
+ * @param [in] ccnxPacket the byte array to put after the ethernet header
+ * @param [in] dmac[6] The destination mac
+ * @param [in] smac[6] The source mac
+ * @param [in] ethertype The ethertype in host byte order
+ *
+ * @retval non-null An allocated PARCBuffer wrapping an ethernet frame, ready to read
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCBuffer *mockGenericEther_createFrame(size_t length, const uint8_t ccnxPacket[length], const uint8_t dmac[ETHER_ADDR_LEN], const uint8_t smac[ETHER_ADDR_LEN], uint16_t ethertype);
+
+#endif // Metis_testrig_GenericEther_h
diff --git a/metis/ccnx/forwarder/metis/io/test/testrig_MetisListenerOps.c b/metis/ccnx/forwarder/metis/io/test/testrig_MetisListenerOps.c
new file mode 100644
index 00000000..d0a1f4c0
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/io/test/testrig_MetisListenerOps.c
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+/**
+ * This is a mock for MetisListenerOps
+ *
+ */
+
+/**
+ * You should #include this C file in your unit test code
+ */
+
+// ===============================
+// Setup a mock for the MetisListenerOps
+
+typedef struct mock_listener_data {
+ unsigned destroyCount;
+ unsigned getInterfaceIndexCount;
+ unsigned getListenAddressCount;
+ unsigned getEncapTypeCount;
+
+ // These values will be returned by the appropriate getter
+ unsigned interfaceIndex;
+ CPIAddress *listenAddress;
+ MetisEncapType encapType;
+} MockListenerData;
+
+static void
+mockListener_Destroy(MetisListenerOps **opsPtr)
+{
+ // Don't actually destroy the data, we want to keep the counts
+ MetisListenerOps *ops = *opsPtr;
+ MockListenerData *data = ops->context;
+ data->destroyCount++;
+ parcMemory_Deallocate((void **) &ops);
+ *opsPtr = NULL;
+}
+
+static unsigned
+mockListener_GetInterfaceIndex(const MetisListenerOps *ops)
+{
+ MockListenerData *data = ops->context;
+ data->getInterfaceIndexCount++;
+ return data->interfaceIndex;
+}
+
+static const CPIAddress *
+mockListener_GetListenAddress(const MetisListenerOps *ops)
+{
+ MockListenerData *data = ops->context;
+ data->getListenAddressCount++;
+ return data->listenAddress;
+}
+
+static MetisEncapType
+mockListener_GetEncapType(const MetisListenerOps *ops)
+{
+ MockListenerData *data = ops->context;
+ data->getEncapTypeCount++;
+ return data->encapType;
+}
+
+static MetisListenerOps
+ mockListenerTemplate = {
+ .context = NULL,
+ .destroy = &mockListener_Destroy,
+ .getInterfaceIndex = &mockListener_GetInterfaceIndex,
+ .getListenAddress = &mockListener_GetListenAddress,
+ .getEncapType = &mockListener_GetEncapType
+};
+
+MockListenerData *
+mockListenData_Create(unsigned interfaceIndex, CPIAddress *listenAddress, MetisEncapType encapType)
+{
+ MockListenerData *data = parcMemory_AllocateAndClear(sizeof(MockListenerData));
+ assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MockListenerData));
+ memset(data, 0, sizeof(MockListenerData));
+ data->encapType = encapType;
+ data->interfaceIndex = interfaceIndex;
+ data->listenAddress = cpiAddress_Copy(listenAddress);
+ return data;
+}
+
+MetisListenerOps *
+mockListener_Create(MockListenerData *data)
+{
+ MetisListenerOps *ops = parcMemory_AllocateAndClear(sizeof(MetisListenerOps));
+ assertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisListenerOps));
+ memcpy(ops, &mockListenerTemplate, sizeof(MetisListenerOps));
+ ops->context = data;
+ return ops;
+}
+
+void
+mockListenerData_Destroy(MockListenerData **dataPtr)
+{
+ MockListenerData *data = *dataPtr;
+ cpiAddress_Destroy(&data->listenAddress);
+ parcMemory_Deallocate((void **) &data);
+ *dataPtr = NULL;
+}