aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/config/test
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/config/test')
-rw-r--r--metis/ccnx/forwarder/metis/config/test/CMakeLists.txt37
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_Add.c133
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_AddConnection.c473
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_AddListener.c327
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_AddRoute.c170
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_List.c130
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_ListConnections.c172
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_ListInterfaces.c177
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_ListRoutes.c187
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_Quit.c119
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_Remove.c130
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveConnection.c118
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c118
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_Root.c130
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_Set.c130
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_SetDebug.c153
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_Unset.c130
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metisControl_UnsetDebug.c153
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_CommandLineInterface.c181
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_CommandOps.c104
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_CommandParser.c259
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_Configuration.c779
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationFile.c403
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationListeners.c644
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_ControlState.c253
-rw-r--r--metis/ccnx/forwarder/metis/config/test/test_metis_SymbolicNameTable.c140
-rw-r--r--metis/ccnx/forwarder/metis/config/test/testrig_MetisControl.c191
27 files changed, 5941 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/config/test/CMakeLists.txt b/metis/ccnx/forwarder/metis/config/test/CMakeLists.txt
new file mode 100644
index 00000000..7e14e1d5
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Enable gcov output for the tests
+add_definitions(--coverage)
+set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
+
+set(TestsExpectedToPass
+ test_metis_ControlState
+ #test_metis_CommandLineInterface
+ test_metis_CommandOps
+ test_metis_CommandParser
+ test_metis_Configuration
+ test_metis_ConfigurationFile
+ test_metis_ConfigurationListeners
+ test_metis_SymbolicNameTable
+ test_metisControl_Add
+ test_metisControl_AddConnection
+ test_metisControl_AddListener
+ test_metisControl_AddRoute
+ test_metisControl_List
+ test_metisControl_ListConnections
+ test_metisControl_ListInterfaces
+ test_metisControl_ListRoutes
+ test_metisControl_Quit
+ test_metisControl_Remove
+ test_metisControl_RemoveConnection
+ test_metisControl_RemoveRoute
+ test_metisControl_Root
+ test_metisControl_Set
+ test_metisControl_SetDebug
+ test_metisControl_Unset
+ test_metisControl_UnsetDebug
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_Add.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Add.c
new file mode 100644
index 00000000..c4001621
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Add.c
@@ -0,0 +1,133 @@
+/*
+ * 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 "../metisControl_Add.c"
+
+#include <LongBow/unit-test.h>
+
+#include "testrig_MetisControl.c"
+
+LONGBOW_TEST_RUNNER(metisControl_Add)
+{
+ // 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(metisControl_Add)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_Add)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAdd_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAdd_CreateHelp);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlAdd_Create)
+{
+ testCommandCreate(testCase, metisControlAdd_Create, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlAdd_CreateHelp)
+{
+ testCommandCreate(testCase, metisControlAdd_CreateHelp, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAdd_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAdd_Init);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAdd_HelpExecute);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, _metisControlAdd_Execute)
+{
+ testHelpExecute(testCase, metisControlAdd_Create, __func__, MetisCommandReturn_Success);
+}
+
+/**
+ * Init should add 4 commands to the command parser
+ */
+LONGBOW_TEST_CASE(Local, _metisControlAdd_Init)
+{
+ testInit(testCase, metisControlAdd_Create, __func__,
+ (const char *[]) {
+ "add connection", "add route",
+ "help add connection", "help add route",
+ NULL
+ });
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAdd_HelpExecute)
+{
+ testHelpExecute(testCase, metisControlAdd_CreateHelp, __func__, MetisCommandReturn_Success);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_Add);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddConnection.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddConnection.c
new file mode 100644
index 00000000..c98ab268
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddConnection.c
@@ -0,0 +1,473 @@
+/*
+ * 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 "../metisControl_AddConnection.c"
+#include "testrig_MetisControl.c"
+
+LONGBOW_TEST_RUNNER(metisControl_AddConnection)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ // 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(metisControl_AddConnection)
+{
+ 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(metisControl_AddConnection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAddConnection_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAddConnection_HelpCreate);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlAddConnection_Create)
+{
+ testCommandCreate(testCase, &metisControlAddConnection_Create, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlAddConnection_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlAddConnection_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_EtherCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_EtherExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_McastCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_McastExecute);
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_TcpCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_TcpExecute);
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_UdpCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_UdpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_Init);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_ConvertStringsToCpiAddress);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_CreateTunnel);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_EtherHelpCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_EtherHelpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_McastHelpCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_McastHelpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_TcpHelpCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_TcpHelpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_UdpHelpCreate);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_UdpHelpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_HelpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddConnection_IpHelp);
+
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_TooFewArgs);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_TooManyArgs);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_BadRemoteIp);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_GoodRemoteIp);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_WithLocalIp);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_WithLocalIpAndPort);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_BadLocalIp);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ParseIPCommandLine_MismatchLocalAndRemote);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, _metisControlAddConnection_EtherCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_EtherCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_EtherExecute)
+{
+ const char *argv[] = { "add", "connection", "ether", "conn3", "e8-06-88-cd-28-de", "em3" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = _metisControlAddConnection_EtherCreate(data->state);
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ assertTrue(result == MetisCommandReturn_Success, "Valid command line should succeed");
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_McastCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_McastCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_McastExecute)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = _metisControlAddConnection_McastCreate(data->state);
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, NULL);
+ metisCommandOps_Destroy(&ops);
+ assertTrue(result == MetisCommandReturn_Failure, "Unimplemented execute should have failed");
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_TcpCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_TcpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_TcpExecute)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "add", "connection", "tcp", "conn3", "1.2.3.4", "123" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Success, "Unimplemented execute should have failed");
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_UdpCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_UdpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_UdpExecute)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "add", "connection", "tcp", "conn3", "1.2.3.4", "123" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_UdpCreate(data->state);
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Success, "Unimplemented execute should have failed");
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_Execute)
+{
+ // this just prints a Help message
+ testHelpExecute(testCase, metisControlAddConnection_Create, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_Init)
+{
+ testInit(testCase, metisControlAddConnection_Create, __func__,
+ (const char *[]) {
+ _commandAddConnectionTcp, _commandAddConnectionUdp, _commandAddConnectionEther, _commandAddConnectionMcast,
+ _commandAddConnectionTcpHelp, _commandAddConnectionUdpHelp, _commandAddConnectionEtherHelp, _commandAddConnectionMcastHelp,
+ NULL
+ });
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_ConvertStringsToCpiAddress)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_CreateTunnel)
+{
+ // this is actully testred in the Tcp_Execute and Udp_Execute
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_EtherHelpCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_EtherHelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_EtherHelpExecute)
+{
+ testHelpExecute(testCase, _metisControlAddConnection_EtherHelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_McastHelpCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_McastHelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_McastHelpExecute)
+{
+ testHelpExecute(testCase, _metisControlAddConnection_McastHelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_TcpHelpCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_TcpHelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_TcpHelpExecute)
+{
+ testHelpExecute(testCase, _metisControlAddConnection_TcpHelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_UdpHelpCreate)
+{
+ testCommandCreate(testCase, &_metisControlAddConnection_UdpHelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_UdpHelpExecute)
+{
+ testHelpExecute(testCase, _metisControlAddConnection_UdpHelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_HelpExecute)
+{
+ testHelpExecute(testCase, metisControlAddConnection_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+/**
+ * Expectes 5 to 7 options
+ */
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_TooFewArgs)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "a", "b", "c" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 3, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+
+ CPIAddress *remote;
+ CPIAddress *local;
+ char *symbolic = NULL;
+
+ MetisCommandReturn result = _metisControlAddConnection_ParseIPCommandLine(data->state->parser, ops, args, &remote, &local, &symbolic);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Failure, "ParseIPCommandLine with 3 args should have returned %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+/**
+ * Expects 5 to 7 options
+ */
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_TooManyArgs)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 9, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+
+ CPIAddress *remote;
+ CPIAddress *local;
+ char *symbolic = NULL;
+
+ MetisCommandReturn result = _metisControlAddConnection_ParseIPCommandLine(data->state->parser, ops, args, &remote, &local, &symbolic);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Failure, "ParseIPCommandLine with 3 args should have returned %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_BadRemoteIp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "a", "b", "c", "tun0", "555.555.555.555", "123", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+
+ CPIAddress *remote;
+ CPIAddress *local;
+ char *symbolic = NULL;
+
+ MetisCommandReturn result = _metisControlAddConnection_ParseIPCommandLine(data->state->parser, ops, args, &remote, &local, &symbolic);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Failure, "ParseIPCommandLine with invalid IP address should have returned %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+/**
+ * Pass a set of args to metisControl_ParseIPCommandLine, then verify:
+ * Successful
+ * remote_ip is what we gave it
+ * remote_port is what we gave it
+ * local_ip is 0.0.0.0 or what we gave it
+ * local_pot is 0 or what we gave it.
+ */
+static void
+verifyParseIpWithGoodAddress(TestData *data, int argc, const char *remote_ip, const char *remote_port, const char *local_ip, const char *local_port)
+{
+ const char *argv[] = { "a", "b", "c", "tun0", remote_ip, remote_port, local_ip, local_port };
+
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+
+ CPIAddress *remote;
+ CPIAddress *local;
+ char *symbolic = NULL;
+
+ MetisCommandReturn result = _metisControlAddConnection_ParseIPCommandLine(data->state->parser, ops, args, &remote, &local, &symbolic);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Success, "ParseIPCommandLine with invalid IP address should have returned %d, got %d", MetisCommandReturn_Failure, result);
+
+ struct sockaddr *sockaddr_remote = parcNetwork_SockAddress(remote_ip, atoi(remote_port));
+ struct sockaddr *sockaddr_local = parcNetwork_SockAddress(local_ip, atoi(local_port));
+ CPIAddress *truth_remote = cpiAddress_CreateFromInet((struct sockaddr_in *) sockaddr_remote);
+ CPIAddress *truth_local = cpiAddress_CreateFromInet((struct sockaddr_in *) sockaddr_local);
+ parcMemory_Deallocate((void **) &sockaddr_local);
+ parcMemory_Deallocate((void **) &sockaddr_remote);
+
+ assertTrue(cpiAddress_Equals(truth_remote, remote), "Got wrong remote address");
+ assertTrue(cpiAddress_Equals(truth_local, local), "Got wrong local address");
+ cpiAddress_Destroy(&truth_remote);
+ cpiAddress_Destroy(&truth_local);
+ cpiAddress_Destroy(&remote);
+ cpiAddress_Destroy(&local);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_GoodRemoteIp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ verifyParseIpWithGoodAddress(data, 6, "1.2.3.4", "123", "0.0.0.0", "0");
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_WithLocalIp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ verifyParseIpWithGoodAddress(data, 7, "1.2.3.4", "123", "10.11.12.13", "0");
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_WithLocalIpAndPort)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ verifyParseIpWithGoodAddress(data, 8, "1.2.3.4", "123", "10.11.12.13", "456");
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_BadLocalIp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "a", "b", "c", "tun0", "1.2.3.4", "123", "666.666.666.666", "123", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 8, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+
+ CPIAddress *remote;
+ CPIAddress *local;
+ char *symbolic = NULL;
+
+ MetisCommandReturn result = _metisControlAddConnection_ParseIPCommandLine(data->state->parser, ops, args, &remote, &local, &symbolic);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Failure, "ParseIPCommandLine with invalid local IP address should have returned %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+/**
+ * One's an IPv4 and one's an IPv6.
+ */
+LONGBOW_TEST_CASE(Local, metisControl_ParseIPCommandLine_MismatchLocalAndRemote)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "a", "b", "c", "tun0", "1.2.3.4", "123", "2001:720:1500:1::a100", "123", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 8, (void **) &argv[0]);
+
+ MetisCommandOps *ops = _metisControlAddConnection_TcpCreate(data->state);
+
+ CPIAddress *remote;
+ CPIAddress *local;
+ char *symbolic = NULL;
+
+ MetisCommandReturn result = _metisControlAddConnection_ParseIPCommandLine(data->state->parser, ops, args, &remote, &local, &symbolic);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+
+ assertTrue(result == MetisCommandReturn_Failure, "ParseIPCommandLine with invalid local IP address should have returned %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddConnection_IpHelp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = _metisControlAddConnection_McastHelpCreate(data->state);
+ MetisCommandReturn result = _metisControlAddConnection_IpHelp(NULL, ops, NULL, "WIZARD");
+ assertTrue(result == MetisCommandReturn_Success, "Wrong return, got %d expected %d", result, MetisCommandReturn_Success);
+ metisCommandOps_Destroy(&ops);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_AddConnection);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddListener.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddListener.c
new file mode 100644
index 00000000..b5319aa4
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddListener.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.
+ */
+
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metisControl_AddListener.c"
+#include "testrig_MetisControl.c"
+
+LONGBOW_TEST_RUNNER(metisControl_AddListener)
+{
+ 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(metisControl_AddListener)
+{
+ 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(metisControl_AddListener)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAddListener_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAddListener_HelpCreate);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlAddListener_Create)
+{
+ testCommandCreate(testCase, &metisControlAddListener_Create, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlAddListener_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlAddListener_HelpCreate, __func__);
+}
+
+// ===========================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_Tcp);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_Udp);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_Udp6);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_Ether);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_UnknownProtocol);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_BadSymbolic);
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_Execute_BadSymbolic_NotAlphaNum);
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisControlAddListener_HelpExecute);
+ LONGBOW_RUN_TEST_CASE(Local, _createTcpListener);
+ LONGBOW_RUN_TEST_CASE(Local, _createUdpListener);
+ LONGBOW_RUN_TEST_CASE(Local, _createEtherListener);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, _createTcpListener)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "tcp", "public0", "13.14.15.16", "9596", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _createTcpListener(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _createUdpListener)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "udp", "public0", "13.14.15.16", "9596", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _createUdpListener(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _createEtherListener)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "ether", "nic3", "eth3", "0x0801", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _createEtherListener(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_WrongArgCount)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "ether" "nic3", "eth3", "0x0801", "foobar" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 7, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Failure, "Command did not return failure: %d", test);
+ assertTrue(data->writeread_count == 0, "Wrong write/read count, expected %d got %u", 0, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_Tcp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+ metisControlState_SetDebug(data->state, true);
+
+ const char *argv[] = { "add", "listener", "tcp", "public0", "13.14.15.16", "9596", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_Udp)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+ metisControlState_SetDebug(data->state, true);
+
+ const char *argv[] = { "add", "listener", "udp", "public0", "13.14.15.16", "9596", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_Udp6)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+ metisControlState_SetDebug(data->state, true);
+
+ // INET6 address
+ const char *argv[] = { "add", "listener", "udp", "public0", "::1", "9596", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_Ether)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "ether", "nic3", "eth3", "0x0801", };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Success, "Command did not return success: %d", test);
+ assertTrue(data->writeread_count == 1, "Wrong write/read count, expected %d got %u", 1, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_UnknownProtocol)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "pup", "nic3", "eth3", "0x0801" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Failure, "Command did not return failure: %d", test);
+ assertTrue(data->writeread_count == 0, "Wrong write/read count, expected %d got %u", 0, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_BadSymbolic)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "ether" "111", "eth3", "0x0801" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Failure, "Command did not return failure: %d", test);
+ assertTrue(data->writeread_count == 0, "Wrong write/read count, expected %d got %u", 0, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_Execute_BadSymbolic_NotAlphaNum)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = metisControlAddListener_Create(data->state);
+
+ const char *argv[] = { "add", "listener", "ether", "n()t", "eth3", "0x0801" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 6, (void **) &argv[0]);
+
+ MetisCommandReturn test = _metisControlAddListener_Execute(data->state->parser, ops, args);
+
+ assertTrue(test == MetisCommandReturn_Failure, "Command did not return failure: %d", test);
+ assertTrue(data->writeread_count == 0, "Wrong write/read count, expected %d got %u", 0, data->writeread_count);
+
+ parcList_Release(&args);
+ ops->destroyer(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, _metisControlAddListener_HelpExecute)
+{
+ _metisControlAddListener_HelpExecute(NULL, NULL, NULL);
+}
+
+// ===========================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_AddListener);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddRoute.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddRoute.c
new file mode 100644
index 00000000..4a57dd64
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_AddRoute.c
@@ -0,0 +1,170 @@
+/*
+ * 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 "../metisControl_AddRoute.c"
+#include "testrig_MetisControl.c"
+
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_AddRoute)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ // 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(metisControl_AddRoute)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_AddRoute)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAddRoute_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlAddRoute_HelpCreate);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlAddRoute_Create)
+{
+ testCommandCreate(testCase, &metisControlAddRoute_Create, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlAddRoute_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlAddRoute_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_AddRoute_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_AddRoute_Execute_ZeroCost);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_AddRoute_Execute_BadPrefix);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_AddRoute_Execute_Good);
+
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_AddRoute_Execute);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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;
+}
+
+static MetisCommandReturn
+testAddRoute(const LongBowTestCase *testCase, int argc, const char *prefix, const char *nexthop, const char *cost)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ metisControlState_SetDebug(data->state, true);
+
+ const char *argv[] = { "add", "route", nexthop, prefix, cost };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ MetisCommandOps *ops = metisControlAddRoute_Create(data->state);
+
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ return result;
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_AddRoute_Execute_WrongArgCount)
+{
+ // argc is wrong, needs to be 5.
+ MetisCommandReturn result = testAddRoute(testCase, 2, "lci:/foo", "703", "1");
+
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_AddRoute with wrong argc should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_AddRoute_Execute_ZeroCost)
+{
+ MetisCommandReturn result = testAddRoute(testCase, 5, "lci:/foo", "702", "0");
+
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_AddRoute with zero cost should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_AddRoute_Execute_BadPrefix)
+{
+ MetisCommandReturn result = testAddRoute(testCase, 5, "blah", "701", "1");
+
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_AddRoute with zero cost should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_AddRoute_Execute_Good)
+{
+ MetisCommandReturn result = testAddRoute(testCase, 5, "lci:/foo", "700", "1");
+
+ assertTrue(result == MetisCommandReturn_Success,
+ "metisControl_AddRoute should return %d, got %d", MetisCommandReturn_Success, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Help_AddRoute_Execute)
+{
+ testHelpExecute(testCase, metisControlAddRoute_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_AddRoute);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_List.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_List.c
new file mode 100644
index 00000000..1d8f347f
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_List.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "../metisControl_List.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_List)
+{
+ // 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(metisControl_List)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_List)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlList_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlList_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlList_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlList_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlList_Create)
+{
+ testCommandCreate(testCase, &metisControlList_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_List_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_List_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_List_Init);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_List_Execute)
+{
+ testHelpExecute(testCase, metisControlList_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_List_Execute)
+{
+ // this just prints the Help function
+ testHelpExecute(testCase, metisControlList_Create, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_List_Init)
+{
+ testInit(testCase, metisControlList_Create, __func__,
+ (const char *[]) {
+ "list connections", "list interfaces", "list routes",
+ "help list connections", "help list interfaces", "help list routes",
+ NULL
+ });
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_List);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListConnections.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListConnections.c
new file mode 100644
index 00000000..56d691e1
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListConnections.c
@@ -0,0 +1,172 @@
+/*
+ * 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 "../metisControl_ListConnections.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_ListConnections)
+{
+ // 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(metisControl_ListConnections)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_ListConnections)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlListConnections_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlListConnections_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlListConnections_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlListConnections_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlListConnections_Create)
+{
+ testCommandCreate(testCase, &metisControlListConnections_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_ListConnections_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ListConnections_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ListConnections_Execute_Good);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_ListConnections_Execute)
+{
+ testHelpExecute(testCase, &metisControlListConnections_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+static CCNxControl *
+customWriteReadResponse(void *userdata, CCNxMetaMessage *messageToWrite)
+{
+ CPIConnectionList *connlist = cpiConnectionList_Create();
+ CPIConnection *conn = cpiConnection_Create(1, cpiAddress_CreateFromInterface(1), cpiAddress_CreateFromInterface(2), cpiConnection_L2);
+ cpiConnectionList_Append(connlist, conn);
+
+ PARCJSON *connectionListAsJson = cpiConnectionList_ToJson(connlist);
+
+ CCNxControl *inboundControlMessage = ccnxMetaMessage_GetControl(messageToWrite);
+
+ // Create a response to the inbound Control message.
+ CCNxControl *outboundControlMessage = cpi_CreateResponse(inboundControlMessage, connectionListAsJson);
+ parcJSON_Release(&connectionListAsJson);
+
+ ccnxControl_Release(&inboundControlMessage);
+
+ cpiConnectionList_Destroy(&connlist);
+
+ return outboundControlMessage;
+}
+
+static MetisCommandReturn
+testListConnections(const LongBowTestCase *testCase, int argc)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ metisControlState_SetDebug(data->state, true);
+ data->customWriteReadReply = &customWriteReadResponse;
+
+ const char *argv[] = { "list", "interfaces" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ MetisCommandOps *ops = metisControlListConnections_Create(data->state);
+
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ return result;
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListConnections_Execute_WrongArgCount)
+{
+ // argc is wrong, needs to be 2.
+ MetisCommandReturn result = testListConnections(testCase, 3);
+
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_ListConnections with wrong argc should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListConnections_Execute_Good)
+{
+ MetisCommandReturn result = testListConnections(testCase, 2);
+
+ assertTrue(result == MetisCommandReturn_Success,
+ "metisControl_ListConnections should return %d, got %d", MetisCommandReturn_Success, result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_ListConnections);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListInterfaces.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListInterfaces.c
new file mode 100644
index 00000000..0e1e13d2
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListInterfaces.c
@@ -0,0 +1,177 @@
+/*
+ * 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 "../metisControl_ListInterfaces.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_ListInterfaces)
+{
+ // 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(metisControl_ListInterfaces)
+{
+ 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(metisControl_ListInterfaces)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlListInterfaces_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlListInterfaces_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlListInterfaces_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlListInterfaces_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlListInterfaces_Create)
+{
+ testCommandCreate(testCase, &metisControlListInterfaces_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_ListInterfaces_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ListInterfaces_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ListInterfaces_Execute_Good);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_ListInterfaces_Execute)
+{
+ testHelpExecute(testCase, &metisControlListInterfaces_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListInterfaces_Execute)
+{
+ testUnimplemented("");
+}
+
+static CCNxControl *
+customWriteReadResponse(void *userdata, CCNxMetaMessage *messageToWrite)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+ cpiInterfaceSet_Add(set, cpiInterface_Create("abc0", 1, false, true, 1500));
+ cpiInterfaceSet_Add(set, cpiInterface_Create("abc1", 2, false, true, 1500));
+ PARCJSON *setJson = cpiInterfaceSet_ToJson(set);
+
+ CCNxControl *inboundControlMessage = ccnxMetaMessage_GetControl(messageToWrite);
+
+ // Create a response to the inbound Control message.
+ CCNxControl *outboundControlMessage = cpi_CreateResponse(inboundControlMessage, setJson);
+ parcJSON_Release(&setJson);
+
+ ccnxControl_Release(&inboundControlMessage);
+ cpiInterfaceSet_Destroy(&set);
+
+ return outboundControlMessage;
+}
+
+static MetisCommandReturn
+testListInterfaces(const LongBowTestCase *testCase, int argc)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ metisControlState_SetDebug(data->state, true);
+ data->customWriteReadReply = &customWriteReadResponse;
+
+ const char *argv[] = { "list", "connections" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ MetisCommandOps *ops = metisControlListInterfaces_Create(data->state);
+
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ return result;
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListInterfaces_Execute_WrongArgCount)
+{
+ // argc is wrong, needs to be 2.
+ MetisCommandReturn result = testListInterfaces(testCase, 3);
+
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_ListInterfaces with wrong argc should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListInterfaces_Execute_Good)
+{
+ MetisCommandReturn result = testListInterfaces(testCase, 2);
+
+ assertTrue(result == MetisCommandReturn_Success,
+ "metisControl_ListInterfaces should return %d, got %d", MetisCommandReturn_Success, result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_ListInterfaces);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListRoutes.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListRoutes.c
new file mode 100644
index 00000000..ca71f93f
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_ListRoutes.c
@@ -0,0 +1,187 @@
+/*
+ * 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 "../metisControl_ListRoutes.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_ListRoutes)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ // 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(metisControl_ListRoutes)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_ListRoutes)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlListRoutes_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlListRoutes_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlListRoutes_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlListRoutes_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlListRoutes_Create)
+{
+ testCommandCreate(testCase, &metisControlListRoutes_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_ListRoutes_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ListRoutes_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_ListRoutes_Execute_Good);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_ListRoutes_Execute)
+{
+ testHelpExecute(testCase, &metisControlListRoutes_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListRoutes_Execute)
+{
+ testUnimplemented("");
+}
+
+static CCNxControl *
+customWriteReadResponse(void *userdata, CCNxMetaMessage *messageToWrite)
+{
+ CPIRouteEntryList *routeEntryList = cpiRouteEntryList_Create();
+ CPIAddress *nexthop = cpiAddress_CreateFromInterface(10);
+ CPIRouteEntry *route = cpiRouteEntry_Create(ccnxName_CreateFromCString("lci:/foo"),
+ 1,
+ nexthop,
+ cpiNameRouteProtocolType_STATIC,
+ cpiNameRouteType_LONGEST_MATCH,
+ &((struct timeval) { 100, 0 }), // lifetime
+ 1); // cost
+
+ cpiRouteEntryList_Append(routeEntryList, route);
+ PARCJSON *setJson = cpiRouteEntryList_ToJson(routeEntryList);
+
+ CCNxControl *inboundControlMessage = ccnxMetaMessage_GetControl(messageToWrite);
+
+ // Create a response to the inbound Control message.
+ CCNxControl *outboundControlMessage = cpi_CreateResponse(inboundControlMessage, setJson);
+ parcJSON_Release(&setJson);
+
+ ccnxControl_Release(&inboundControlMessage);
+
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntryList_Destroy(&routeEntryList);
+
+ return outboundControlMessage;
+}
+
+static MetisCommandReturn
+testListRoutes(const LongBowTestCase *testCase, int argc)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ metisControlState_SetDebug(data->state, true);
+ data->customWriteReadReply = &customWriteReadResponse;
+
+ const char *argv[] = { "list", "connections" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ MetisCommandOps *ops = metisControlListRoutes_Create(data->state);
+
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ return result;
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListRoutes_Execute_WrongArgCount)
+{
+ // argc is wrong, needs to be 2.
+ MetisCommandReturn result = testListRoutes(testCase, 3);
+
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_ListRoutes with wrong argc should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_ListRoutes_Execute_Good)
+{
+ MetisCommandReturn result = testListRoutes(testCase, 2);
+
+ assertTrue(result == MetisCommandReturn_Success,
+ "metisControl_ListRoutes should return %d, got %d", MetisCommandReturn_Success, result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_ListRoutes);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_Quit.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Quit.c
new file mode 100644
index 00000000..192bdf86
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Quit.c
@@ -0,0 +1,119 @@
+/*
+ * 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 "../metisControl_Quit.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_Quit)
+{
+ // 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(metisControl_Quit)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_Quit)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlQuit_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlQuit_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlQuit_HelpCreate)
+{
+ testCommandCreate(testCase, metisControlQuit_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlQuit_Create)
+{
+ testCommandCreate(testCase, metisControlQuit_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_Quit_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Quit_Execute);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_Quit_Execute)
+{
+ testHelpExecute(testCase, metisControlQuit_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Quit_Execute)
+{
+ // This only displays the Help message
+ testHelpExecute(testCase, metisControlQuit_Create, __func__, MetisCommandReturn_Exit);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_Quit);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_Remove.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Remove.c
new file mode 100644
index 00000000..b5878797
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Remove.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "../metisControl_Remove.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_Remove)
+{
+ // 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(metisControl_Remove)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_Remove)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRemove_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRemove_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlRemove_HelpCreate)
+{
+ testCommandCreate(testCase, metisControlRemove_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlRemove_Create)
+{
+ testCommandCreate(testCase, metisControlRemove_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_Remove_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Remove_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Remove_Init);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_Remove_Execute)
+{
+ testHelpExecute(testCase, metisControlRemove_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Remove_Execute)
+{
+ // this only displays the help menu
+ testHelpExecute(testCase, metisControlRemove_Create, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Remove_Init)
+{
+ testInit(testCase, metisControlRemove_Create, __func__,
+ (const char *[]) {
+ "remove connection", "remove route",
+ "help remove connection", "help remove route",
+ NULL
+ });
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_Remove);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveConnection.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveConnection.c
new file mode 100644
index 00000000..76340b77
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveConnection.c
@@ -0,0 +1,118 @@
+/*
+ * 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 "../metisControl_RemoveConnection.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_RemoveConnection)
+{
+ // 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(metisControl_RemoveConnection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_RemoveConnection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRemoveConnection_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRemoveConnection_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlRemoveConnection_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlRemoveConnection_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlRemoveConnection_Create)
+{
+ testCommandCreate(testCase, &metisControlRemoveConnection_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_RemoveConnection_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_RemoveConnection_Execute);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_RemoveConnection_Execute)
+{
+ testHelpExecute(testCase, &metisControlRemoveConnection_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_RemoveConnection_Execute)
+{
+ testHelpExecute(testCase, &metisControlRemoveConnection_Create, __func__, MetisCommandReturn_Success);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_RemoveConnection);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c
new file mode 100644
index 00000000..636d04b5
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c
@@ -0,0 +1,118 @@
+/*
+ * 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 "../metisControl_RemoveRoute.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_RemoveRoute)
+{
+ // 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(metisControl_RemoveRoute)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_RemoveRoute)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRemoveRoute_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRemoveRoute_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlRemoveRoute_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlRemoveRoute_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlRemoveRoute_Create)
+{
+ testCommandCreate(testCase, &metisControlRemoveRoute_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_RemoveRoute_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_RemoveRoute_Execute);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_RemoveRoute_Execute)
+{
+ testHelpExecute(testCase, &metisControlRemoveRoute_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_RemoveRoute_Execute)
+{
+ testUnimplemented("");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_RemoveRoute);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_Root.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Root.c
new file mode 100644
index 00000000..3adf736c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Root.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "../metisControl_Root.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_Root)
+{
+ // 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(metisControl_Root)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_Root)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRoot_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlRoot_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlRoot_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlRoot_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlRoot_Create)
+{
+ testCommandCreate(testCase, &metisControlRoot_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_Root_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Root_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Root_Init);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_Root_Execute)
+{
+ testHelpExecute(testCase, &metisControlRoot_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Root_Execute)
+{
+ // this command only displays Help
+ testHelpExecute(testCase, &metisControlRoot_Create, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Root_Init)
+{
+ testInit(testCase, &metisControlRoot_Create, __func__,
+ (const char *[]) {
+ "add", "list", "quit", "remove", "set", "unset",
+ "help add", "help list", "help quit", "help remove", "help set", "help unset",
+ NULL
+ });
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_Root);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_Set.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Set.c
new file mode 100644
index 00000000..b224e49f
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Set.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "../metisControl_Set.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_Set)
+{
+ // 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(metisControl_Set)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_Set)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlSet_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlSet_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlSet_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlSet_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlSet_Create)
+{
+ testCommandCreate(testCase, &metisControlSet_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_Set_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Set_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Set_Init);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_Set_Execute)
+{
+ testHelpExecute(testCase, &metisControlSet_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Set_Execute)
+{
+ // Only prints a help menu
+ testHelpExecute(testCase, &metisControlSet_Create, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Set_Init)
+{
+ testInit(testCase, &metisControlSet_Create, __func__,
+ (const char *[]) {
+ "set debug",
+ "help set debug",
+ NULL
+ });
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_Set);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_SetDebug.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_SetDebug.c
new file mode 100644
index 00000000..5f7f4805
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_SetDebug.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 "../metisControl_SetDebug.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_SetDebug)
+{
+ // 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(metisControl_SetDebug)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_SetDebug)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlSetDebug_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlSetDebug_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlSetDebug_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlSetDebug_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlSetDebug_Create)
+{
+ testCommandCreate(testCase, &metisControlSetDebug_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_SetDebug_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_SetDebug_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_SetDebug_Execute_Good);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_SetDebug_Execute)
+{
+ testHelpExecute(testCase, &metisControlSetDebug_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+static MetisCommandReturn
+testDebug(const LongBowTestCase *testCase, MetisCommandOps * (*create)(MetisControlState * state), int argc, bool initialDebugSetting, bool expectedDebugSetting)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "blah", "blah" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ metisControlState_SetDebug(data->state, initialDebugSetting);
+ MetisCommandOps *ops = create(data->state);
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ if (result == MetisCommandReturn_Success) {
+ assertTrue(data->state->debugFlag == expectedDebugSetting,
+ "Debug flag wrong, expected %d got %d",
+ expectedDebugSetting,
+ data->state->debugFlag);
+ }
+
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ return result;
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_SetDebug_Execute_WrongArgCount)
+{
+ MetisCommandReturn result = testDebug(testCase, metisControlSetDebug_Create, 3, false, true);
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_SetDebug_Execute should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+
+LONGBOW_TEST_CASE(Local, metisControl_SetDebug_Execute_Good)
+{
+ MetisCommandReturn result = testDebug(testCase, metisControlSetDebug_Create, 2, false, true);
+ assertTrue(result == MetisCommandReturn_Success,
+ "metisControl_SetDebug_Execute should return %d, got %d", MetisCommandReturn_Success, result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_SetDebug);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_Unset.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Unset.c
new file mode 100644
index 00000000..f7e27daf
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_Unset.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "../metisControl_Unset.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_Unset)
+{
+ // 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(metisControl_Unset)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_Unset)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlUnset_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlUnset_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlUnset_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlUnset_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlUnset_Create)
+{
+ testCommandCreate(testCase, &metisControlUnset_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_Unset_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Unset_Init);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Unset_Execute);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_Unset_Execute)
+{
+ testHelpExecute(testCase, &metisControlUnset_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Unset_Init)
+{
+ testInit(testCase, &metisControlUnset_Create, __func__,
+ (const char *[]) {
+ "unset debug",
+ "help unset debug",
+ NULL
+ });
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_Unset_Execute)
+{
+ // Only prints a help menu
+ testHelpExecute(testCase, &metisControlUnset_Create, __func__, MetisCommandReturn_Success);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_Unset);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metisControl_UnsetDebug.c b/metis/ccnx/forwarder/metis/config/test/test_metisControl_UnsetDebug.c
new file mode 100644
index 00000000..bb31b917
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metisControl_UnsetDebug.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 "../metisControl_UnsetDebug.c"
+#include "testrig_MetisControl.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metisControl_UnsetDebug)
+{
+ // 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(metisControl_UnsetDebug)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metisControl_UnsetDebug)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlUnsetDebug_HelpCreate);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlUnsetDebug_Create);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ testrigMetisControl_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, metisControlUnsetDebug_HelpCreate)
+{
+ testCommandCreate(testCase, &metisControlUnsetDebug_HelpCreate, __func__);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlUnsetDebug_Create)
+{
+ testCommandCreate(testCase, &metisControlUnsetDebug_Create, __func__);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_UnsetDebug_Execute);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_UnsetDebug_Execute_WrongArgCount);
+ LONGBOW_RUN_TEST_CASE(Local, metisControl_UnsetDebug_Execute_Good);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ testrigMetisControl_commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ testrigMetisControl_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, metisControl_Help_UnsetDebug_Execute)
+{
+ testHelpExecute(testCase, &metisControlUnsetDebug_HelpCreate, __func__, MetisCommandReturn_Success);
+}
+
+static MetisCommandReturn
+testDebug(const LongBowTestCase *testCase, MetisCommandOps * (*create)(MetisControlState * state), int argc, bool initialDebugSetting, bool expectedDebugSetting)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ const char *argv[] = { "blah", "blah" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ metisControlState_SetDebug(data->state, initialDebugSetting);
+ MetisCommandOps *ops = create(data->state);
+ MetisCommandReturn result = ops->execute(data->state->parser, ops, args);
+ if (result == MetisCommandReturn_Success) {
+ assertTrue(data->state->debugFlag == expectedDebugSetting,
+ "Debug flag wrong, expected %d got %d",
+ expectedDebugSetting,
+ data->state->debugFlag);
+ }
+
+ metisCommandOps_Destroy(&ops);
+ parcList_Release(&args);
+ return result;
+}
+
+LONGBOW_TEST_CASE(Local, metisControl_UnsetDebug_Execute_WrongArgCount)
+{
+ MetisCommandReturn result = testDebug(testCase, metisControlUnsetDebug_Create, 3, true, false);
+ assertTrue(result == MetisCommandReturn_Failure,
+ "metisControl_UnsetDebug_Execute should return %d, got %d", MetisCommandReturn_Failure, result);
+}
+
+
+LONGBOW_TEST_CASE(Local, metisControl_UnsetDebug_Execute_Good)
+{
+ MetisCommandReturn result = testDebug(testCase, metisControlUnsetDebug_Create, 2, true, false);
+ assertTrue(result == MetisCommandReturn_Success,
+ "metisControl_UnsetDebug_Execute should return %d, got %d", MetisCommandReturn_Success, result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metisControl_UnsetDebug);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_CommandLineInterface.c b/metis/ccnx/forwarder/metis/config/test/test_metis_CommandLineInterface.c
new file mode 100644
index 00000000..fe8e911c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_CommandLineInterface.c
@@ -0,0 +1,181 @@
+/*
+ * 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_CommandLineInterface.c"
+
+#include <errno.h>
+#include <string.h>
+
+#include <LongBow/unit-test.h>
+#include <LongBow/debugging.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(test_metis_CommandLineInterface)
+{
+// 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);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(test_metis_CommandLineInterface)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(test_metis_CommandLineInterface)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ //LONGBOW_RUN_TEST_CASE(Global, myTest);
+ LONGBOW_RUN_TEST_CASE(Global, Version);
+}
+
+typedef struct test_state {
+ MetisForwarder *metis;
+ MetisDispatcher *dispatcher;
+ MetisCommandLineInterface *cli;
+
+ int clientFd;
+} TestState;
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ TestState *state = malloc(sizeof(TestState));
+
+ state->metis = metisForwarder_Create(NULL);
+ state->dispatcher = metisForwarder_GetDispatcher(state->metis);
+
+// we create our own CLI, because the one built in to metisForwarder is not started
+// until the forwarder is running.
+
+ state->cli = metisCommandLineInterface_Create(state->metis, 2001);
+ metisCommandLineInterface_Start(state->cli);
+
+ metisDispatcher_RunCount(state->dispatcher, 1);
+
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PF_INET;
+ addr.sin_port = htons(2001);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ state->clientFd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(state->clientFd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ int failure = connect(state->clientFd, (struct sockaddr *) &addr, sizeof(addr));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+// crank the handle once
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(state->metis), &((struct timeval) { 0, 1000 }));
+
+ longBowTestCase_SetClipBoardData(testCase, state);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ TestState *state = longBowTestCase_GetClipBoardData(testCase);
+
+ close(state->clientFd);
+ metisCommandLineInterface_Destroy(&state->cli);
+ metisForwarder_Destroy(&state->metis);
+ free(state);
+
+ 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;
+}
+
+/**
+ * The CLI has a secret command "~~" (two of them) that will echo back whatever the next
+ * words are. The string "~~ hello world" would echo back "success: hello world" followed by
+ * the next command prompt. This lets us test that the 1st level of parsing is working. It
+ * differentiates "~~" as the command and the rest of the string as parameters.
+ */
+LONGBOW_TEST_CASE(Global, myTest)
+{
+ TestState *state = longBowTestCase_GetClipBoardData(testCase);
+
+
+ char readbuffer[1024];
+
+// Skipover the MOTD
+ ssize_t nread = read(state->clientFd, readbuffer, 1024);
+ assertTrue(nread > -1, "Error read");
+ printf("read:\n%s\n", readbuffer);
+
+ // send special command "~~" followed by a string. It should be repeated back
+ // as "success: see no hands\nmetis> ", where the stuff after the \n is the next command prompt
+ char magic[] = "~~ see no hands\r\n";
+ ssize_t nwritten = write(state->clientFd, magic, sizeof(magic));
+ assertTrue(nwritten == sizeof(magic), "Error write, expected %zu got %zd", sizeof(magic), nwritten);
+
+ metisDispatcher_RunDuration(state->dispatcher, &((struct timeval) { 0, 1000 }));
+
+ memset(readbuffer, 0, 1024);
+ nread = read(state->clientFd, readbuffer, 1024);
+ assertTrue(nread > -1, "Error read");
+
+ // we look for the answer without the "\nmetis> " part.
+ char answer[] = "success: see no hands";
+ assertTrue(strncasecmp(readbuffer, answer, sizeof(answer) - 1) == 0, "Got wrong string: %s", readbuffer);
+}
+
+LONGBOW_TEST_CASE(Global, Version)
+{
+ TestState *state = longBowTestCase_GetClipBoardData(testCase);
+
+ char readbuffer[1024];
+
+ // Skipover the MOTD
+ ssize_t nread = read(state->clientFd, readbuffer, 1024);
+ assertTrue(nread > -1, "Error read");
+
+ printf("read:\n%s\n", readbuffer);
+
+ // send special command "~~" followed by a string. It should be repeated back
+ // as "success: see no hands\nmetis> ", where the stuff after the \n is the next command prompt
+ char magic[] = "ver\r\n";
+ ssize_t nwritten = write(state->clientFd, magic, sizeof(magic));
+ assertTrue(nwritten == sizeof(magic), "Error write, expected %zu got %zd", sizeof(magic), nwritten);
+
+ metisDispatcher_RunDuration(state->dispatcher, &((struct timeval) { 0, 1000 }));
+
+ memset(readbuffer, 0, 1024);
+ nread = read(state->clientFd, readbuffer, 1024);
+ assertTrue(nread > -1, "Error read");
+
+ printf("%s", readbuffer);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_metis_CommandLineInterface);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_CommandOps.c b/metis/ccnx/forwarder/metis/config/test/test_metis_CommandOps.c
new file mode 100644
index 00000000..4d6a5c13
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_CommandOps.c
@@ -0,0 +1,104 @@
+/*
+ * 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_CommandOps.c"
+
+#include <inttypes.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(metis_CommandOps)
+{
+ // 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);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_CommandOps)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_CommandOps)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandOps_Create);
+}
+
+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;
+}
+
+static void
+_init(struct metis_command_parser *parser, MetisCommandOps *ops)
+{
+}
+
+static MetisCommandReturn
+_execute(struct metis_command_parser *parser, MetisCommandOps *ops, PARCList *args)
+{
+ return MetisCommandReturn_Success;
+}
+
+static void
+_destroyer(MetisCommandOps **opsPtr)
+{
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandOps_Create)
+{
+ char hello[] = "hello";
+ char command[] = "test";
+
+ MetisCommandOps *ops = metisCommandOps_Create(hello, command, _init, _execute, _destroyer);
+
+ assertTrue(ops->closure == hello, "closure wrong expected %p got %p", (void *) hello, (void *) ops->closure);
+ assertTrue(strcmp(ops->command, command) == 0, "command wrong expected '%s' got '%s'", command, ops->command);
+ assertTrue(ops->init == _init, "Wrong init, expected %" PRIXPTR " got %" PRIXPTR, (uintptr_t) _init, (uintptr_t) ops->init);
+ assertTrue(ops->execute == _execute, "Wrong execute, expected %" PRIXPTR " got %" PRIXPTR, (uintptr_t) _execute, (uintptr_t) ops->execute);
+ assertTrue(ops->destroyer == _destroyer, "Wrong destroyer, expected %" PRIXPTR " got %" PRIXPTR, (uintptr_t) _destroyer, (uintptr_t) ops->destroyer);
+
+ metisCommandOps_Destroy(&ops);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_CommandOps);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_CommandParser.c b/metis/ccnx/forwarder/metis/config/test/test_metis_CommandParser.c
new file mode 100644
index 00000000..b7e6edae
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_CommandParser.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_CommandParser.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metis_CommandParser)
+{
+ // 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_CommandParser)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_CommandParser)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_Create_Destroy);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_DispatchCommand_Exact);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_DispatchCommand_Longer);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_DispatchCommand_Shorter);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_DispatchCommand_Sibling);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_GetDebug);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_Interactive);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_RegisterCommand_NullInit);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_RegisterCommand_WithInit);
+ LONGBOW_RUN_TEST_CASE(Global, metisCommandParser_SetDebug);
+}
+
+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, metisCommandParser_Create_Destroy)
+{
+ MetisCommandParser *parser = metisCommandParser_Create();
+ assertNotNull(parser, "Got null parser from metisCommandParser_Create");
+ metisCommandParser_Destroy(&parser);
+ assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance!");
+ assertNull(parser, "metisCommandParser_Destroy did not null pointer");
+}
+
+static MetisCommandReturn
+test_execute(MetisCommandParser *parser, MetisCommandOps *ops, PARCList *args)
+{
+ bool *execute_called_ptr = (bool *) ops->closure;
+ *execute_called_ptr = true;
+ return MetisCommandReturn_Success;
+}
+
+/**
+ * argc = the exact number of args, don't include the command name
+ * example: argc = 2, argv = {"Hello", "World"}
+ *
+ * expectedResult true means the execute function is called
+ */
+static void
+dispatchCommand(const char *command_string, int argc, char **argv, bool expectedResult)
+{
+ MetisCommandParser *parser = metisCommandParser_Create();
+
+ bool execute_called = false;
+
+ MetisCommandOps *ops = metisCommandOps_Create(&execute_called, command_string, NULL, test_execute, metisCommandOps_Destroy);
+
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, argc, (void **) &argv[0]);
+
+ execute_called = false;
+ metisCommandParser_RegisterCommand(parser, ops);
+ metisCommandParser_DispatchCommand(parser, args);
+ if (expectedResult) {
+ assertTrue(execute_called, "Did not call the execute function");
+ } else {
+ assertFalse(execute_called, "The execute function should not have been called but was");
+ }
+
+ metisCommandParser_Destroy(&parser);
+ parcList_Release(&args);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_DispatchCommand_Exact)
+{
+ // note that it is not case sensitive
+ dispatchCommand("hello world", 2, (char *[]) { "Hello", "World" }, true);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_DispatchCommand_Sibling)
+{
+ // note that it is not case sensitive
+ dispatchCommand("hello world", 2, (char *[]) { "Hello", "Universe" }, false);
+}
+
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_DispatchCommand_Longer)
+{
+ // note that it is not case sensitive
+ dispatchCommand("hello world", 3, (char *[]) { "Hello", "World", "Again" }, true);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_DispatchCommand_Shorter)
+{
+ // note that it is not case sensitive
+ dispatchCommand("hello world", 1, (char *[]) { "Hello" }, false);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_GetDebug)
+{
+ MetisCommandParser *parser = metisCommandParser_Create();
+ bool test = metisCommandParser_GetDebug(parser);
+ assertTrue(test == parser->debugFlag, "Got %d expected %d", test, parser->debugFlag);
+ metisCommandParser_Destroy(&parser);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_Interactive)
+{
+ testUnimplemented("");
+}
+
+static bool called_init = false;
+static void
+test_init_command(MetisCommandParser *parser, MetisCommandOps *ops)
+{
+ called_init = true;
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_RegisterCommand_WithInit)
+{
+ MetisCommandParser *parser = metisCommandParser_Create();
+
+ MetisCommandOps *ops = metisCommandOps_Create(NULL, "hello world", test_init_command, test_execute, metisCommandOps_Destroy);
+
+ called_init = false;
+ metisCommandParser_RegisterCommand(parser, ops);
+
+ MetisCommandOps *test = parcTreeRedBlack_Get(parser->commandTree, ops->command);
+ assertNotNull(test, "Got null looking up command in tree");
+ assertTrue(test == ops, "Wrong pointer, got %p expected %p", (void *) test, (void *) ops);
+ assertTrue(called_init, "Did not call the init function");
+
+ metisCommandParser_Destroy(&parser);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_RegisterCommand_NullInit)
+{
+ MetisCommandParser *parser = metisCommandParser_Create();
+
+ MetisCommandOps command = {
+ .command = "hello world",
+ .init = NULL,
+ .execute = NULL
+ };
+
+ called_init = false;
+ metisCommandParser_RegisterCommand(parser, &command);
+
+ MetisCommandOps *test = parcTreeRedBlack_Get(parser->commandTree, command.command);
+ assertNotNull(test, "Got null looking up command in tree");
+ assertTrue(test == &command, "Wrong pointer, got %p expected %p", (void *) test, (void *) &command);
+ assertFalse(called_init, "Somehow called the init function");
+
+ metisCommandParser_Destroy(&parser);
+}
+
+LONGBOW_TEST_CASE(Global, metisCommandParser_SetDebug)
+{
+ MetisCommandParser *parser = metisCommandParser_Create();
+ // flip the setting
+ bool truth = ~parser->debugFlag;
+ metisCommandParser_SetDebug(parser, truth);
+ assertTrue(truth == parser->debugFlag, "Got %d expected %d", parser->debugFlag, truth);
+ metisCommandParser_Destroy(&parser);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisCommandParser_MatchCommand);
+ LONGBOW_RUN_TEST_CASE(Local, parseStringIntoTokens);
+ LONGBOW_RUN_TEST_CASE(Local, stringCompare);
+}
+
+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, metisCommandParser_MatchCommand)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, parseStringIntoTokens)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Local, stringCompare)
+{
+ testUnimplemented("");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_CommandParser);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_Configuration.c b/metis/ccnx/forwarder/metis/config/test/test_metis_Configuration.c
new file mode 100644
index 00000000..09b9a576
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_Configuration.c
@@ -0,0 +1,779 @@
+/*
+ * 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.
+ */
+
+/*
+ * Some of these tests might not execute on certain systems, as they
+ * depend on having INET and INET6 addresses available. If you system
+ * does not have one or both of those, the corresponding tests will not
+ * execute.
+ */
+
+// We need to specifically include the Ethernet mockup and set the proper define so
+// we do not need an actual Ethernet listener
+
+#define METIS_MOCK_ETHERNET 1
+#include "../../io/test/testrig_GenericEther.c"
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_Configuration.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <signal.h>
+
+// so we can mock up an interface
+#include "../../core/test/testrig_MetisIoOperations.h"
+
+// so we can test content store size
+#include "../../core/metis_Forwarder.c"
+#include "../../processor/metis_MessageProcessor.c"
+#include "../../content_store/metis_ContentStoreInterface.h"
+
+struct sigaction save_sigchld;
+struct sigaction save_sigpipe;
+
+/**
+ * Add a connection to the connection table to mock the "ingress" port of a control message
+ *
+ * You must release the return value
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @retval non-null A mockup of a connection
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * {
+ * unsigned mockConnectionId = 7;
+ * MetisIoOperations *ops = _addIngressMockConnection(metis, mockConnectionId);
+ * MockIoOperationsData *data = ops->context;
+ * mockIoOperationsData_Destroy(&ops);
+ * }
+ * @endcode
+ */
+static MetisIoOperations *
+_addIngressMockConnection(MetisForwarder *metis, unsigned mockup_id)
+{
+ MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, mockup_id, true, true, true);
+
+ MetisConnection *conn = metisConnection_Create(ops);
+ MetisConnectionTable *connTable = metisForwarder_GetConnectionTable(metis);
+ metisConnectionTable_Add(connTable, conn);
+ return ops;
+}
+
+// returns a strdup() of the interface name, use free(3)
+static char *
+_pickInterfaceName(MetisForwarder *metis)
+{
+ char *ifname = NULL;
+
+ CPIInterfaceSet *set = metisSystem_Interfaces(metis);
+ size_t length = cpiInterfaceSet_Length(set);
+ assertTrue(length > 0, "metisSystem_Interfaces returned no interfaces");
+
+ for (size_t i = 0; i < length; i++) {
+ CPIInterface *iface = cpiInterfaceSet_GetByOrdinalIndex(set, i);
+ const CPIAddressList *addressList = cpiInterface_GetAddresses(iface);
+
+ size_t length = cpiAddressList_Length(addressList);
+ for (size_t i = 0; i < length && !ifname; i++) {
+ const CPIAddress *a = cpiAddressList_GetItem(addressList, i);
+ if (cpiAddress_GetType(a) == cpiAddressType_LINK) {
+ ifname = strdup(cpiInterface_GetName(iface));
+ }
+ }
+ }
+
+ cpiInterfaceSet_Destroy(&set);
+ return ifname;
+}
+
+/**
+ * Adds a mock ethernet connection to the given peer address with a symbolic name.
+ * You must have previously added an Ethernet listener.
+ *
+ * @return true Added
+ * @return false An error
+ */
+static bool
+_addEthernetConnection(MetisForwarder *metis, unsigned connid, const char *symbolicName, MetisListenerOps *listener, uint8_t peerEther[6])
+{
+ // Create a CPIConnectionEthernet Add control message
+ char *ifname = _pickInterfaceName(metis);
+
+ uint16_t etherType = 0x0801;
+
+ CPIAddress *peerAddress = cpiAddress_CreateFromLink(peerEther, 6);
+ CPIConnectionEthernet *etherConn = cpiConnectionEthernet_Create(ifname, peerAddress, etherType, symbolicName);
+ bool success = _metisConfiguration_AddConnectionEthernet(metisForwarder_GetConfiguration(metis), etherConn, peerAddress, listener);
+
+
+ cpiAddress_Destroy(&peerAddress);
+ free(ifname);
+ cpiConnectionEthernet_Release(&etherConn);
+
+ return success;
+}
+
+// =========================================================================
+
+LONGBOW_TEST_RUNNER(metis_Configuration)
+{
+ // 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.
+ printf("line 140\n");
+ 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_Configuration)
+{
+ printf("line 148\n");
+ 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_Configuration)
+{
+ printf("line 156\n");
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==============================================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisConfiguration_SetupAllListeners);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfiguration_Receive);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfiguration_SetObjectStoreSize);
+}
+
+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, metisConfiguration_SetupAllListeners)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Global, metisConfiguration_Receive)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Add a connection to apply the route to
+ unsigned mockConnectionId = 7000;
+ MetisIoOperations *ops = _addIngressMockConnection(metis, mockConnectionId);
+ MockIoOperationsData *data = metisIoOperations_GetClosure(ops);
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/foo");
+ CPIRouteEntry *routeEntry = cpiRouteEntry_Create(prefix, mockConnectionId, NULL,
+ cpiNameRouteProtocolType_STATIC,
+ cpiNameRouteType_LONGEST_MATCH, NULL, 4);
+ CCNxControl *request = ccnxControl_CreateAddRouteRequest(routeEntry);
+ cpiRouteEntry_Destroy(&routeEntry);
+
+ PARCBuffer *buffer = metisTlv_EncodeControlPlaneInformation(request);
+
+ MetisMessage *message = metisMessage_CreateFromArray(parcBuffer_Overlay(buffer, 0), parcBuffer_Limit(buffer), mockConnectionId, 2, metisForwarder_GetLogger(metis));
+ parcBuffer_Release(&buffer);
+
+ // this takes ownership of message and disposes of it
+ metisConfiguration_Receive(metisForwarder_GetConfiguration(metis), message);
+
+ // crank the handle to lets the ACKs or NACKs move
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ assertTrue(data->sendCount == 1, "Did not send a response message, expected 1 got %u", data->sendCount);
+ CCNxControl *response = metisMessage_CreateControlMessage(data->lastMessage);
+
+ assertTrue(cpi_GetMessageType(response) == CPI_ACK,
+ "CPI message not a response: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ metisForwarder_Destroy(&metis);
+
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Global, metisConfiguration_SetObjectStoreSize)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ MetisContentStoreInterface *store = metis->processor->contentStore;
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+ size_t current_capacity = metisContentStoreInterface_GetObjectCapacity(store);
+ size_t new_capacity = current_capacity + 5;
+
+ metisConfiguration_SetObjectStoreSize(config, new_capacity);
+
+ // Get the store pointer again, as it may have changed.
+ store = metis->processor->contentStore;
+ assertTrue(new_capacity == metisContentStoreInterface_GetObjectCapacity(store),
+ "Object Store is wrong capacity, got %zu expected %zu",
+ metisContentStoreInterface_GetObjectCapacity(store), new_capacity);
+
+ metisForwarder_Destroy(&metis);
+}
+
+// ==============================================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_Receive_AddConnectionEthernet);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_Receive_AddConnectionEthernet_Dup);
+
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessUnregisterPrefix);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessRegisterPrefix);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessRegisterPrefix_Symbolic);
+
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessInterfaceList);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessRegistrationList);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessCreateTunnel_Dup);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessCreateTunnel_TCP);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessCreateTunnel_UDP);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessConnectionList);
+
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessAddConnectionEthernet);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_ProcessRemoveConnectionEthernet);
+
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_Receive_AddConnectionEthernet);
+ LONGBOW_RUN_TEST_CASE(Local, metisConfiguration_Receive_RemoveConnectionEthernet);
+}
+
+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, metisConfiguration_ProcessInterfaceList)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ CCNxControl *request = ccnxControl_CreateInterfaceListRequest();
+
+ unsigned mockConnectionId = 7;
+
+ CCNxControl *response = metisConfiguration_ProcessInterfaceList(metisForwarder_GetConfiguration(metis), request, mockConnectionId);
+
+ assertNotNull(response, "Got null response");
+
+ assertTrue(cpi_GetMessageType(response) == CPI_RESPONSE,
+ "CPI message not a response: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ assertTrue(cpi_GetMessageOperation(response) == CPI_INTERFACE_LIST,
+ "CPI message not an interface list: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessUnregisterPrefix)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessRegisterPrefix)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Add a connection to apply the route to
+ unsigned mockConnectionId = 7000;
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/foo");
+ CPIRouteEntry *routeEntry = cpiRouteEntry_Create(prefix, mockConnectionId, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 4);
+ CCNxControl *request = ccnxControl_CreateAddRouteRequest(routeEntry);
+ cpiRouteEntry_Destroy(&routeEntry);
+
+ CCNxControl *response = metisConfiguration_ProcessRegisterPrefix(metisForwarder_GetConfiguration(metis), request, mockConnectionId);
+
+ // crank the handle to lets the ACKs or NACKs move
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ assertNotNull(response, "LastMessage is not set in the test rig");
+
+ assertTrue(cpi_GetMessageType(response) == CPI_ACK,
+ "CPI message not a response: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessRegisterPrefix_Symbolic)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Add a connection to apply the route to
+ unsigned mockConnectionId = 7000;
+
+ // hack in the symbolic name because _addIngressMockConnection does not do that
+ metisSymbolicNameTable_Add(metisForwarder_GetConfiguration(metis)->symbolicNameTable, "foo0", mockConnectionId);
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/foo");
+ CPIRouteEntry *routeEntry = cpiRouteEntry_CreateSymbolic(prefix, "foo0", cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 4);
+ CCNxControl *request = ccnxControl_CreateAddRouteRequest(routeEntry);
+ cpiRouteEntry_Destroy(&routeEntry);
+
+ CCNxControl *response = metisConfiguration_ProcessRegisterPrefix(metisForwarder_GetConfiguration(metis), request, mockConnectionId);
+
+ // crank the handle to lets the ACKs or NACKs move
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ assertNotNull(response, "Response is NULL");
+
+ assertTrue(cpi_GetMessageType(response) == CPI_ACK,
+ "CPI message not a response: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ metisForwarder_Destroy(&metis);
+}
+
+/**
+ * Add a route, then verify the route shows up in a list
+ */
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessRegistrationList)
+{
+ printf("\n%s starting\n", __func__);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7;
+
+ // Add a route to the forwarding table
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/pancakes/for/all");
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, 3, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 2);
+ metisForwarder_AddOrUpdateRoute(metis, route);
+ cpiRouteEntry_Destroy(&route);
+
+ // Create a request and send it in to MetisConfiguration. The response will be
+ // sent out the "mockup_id" interface
+
+ CCNxControl *request = ccnxControl_CreateRouteListRequest();
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+ CCNxControl *response = metisConfiguration_ProcessRegistrationList(config, request, mockup_id);
+
+ assertNotNull(response, "Got null response");
+
+ assertTrue(cpi_GetMessageType(response) == CPI_RESPONSE,
+ "CPI message not a response: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ assertTrue(cpi_GetMessageOperation(response) == CPI_PREFIX_REGISTRATION_LIST,
+ "CPI message not an interface list: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessCreateTunnel_TCP)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7;
+
+ // -----
+ // Issue a command to create a TCP tunnel. We should be able to verify that it's in
+ // the connection table and we'll see the ACK come back to our mock interface.
+
+ // ---------------------------
+ // Tunnel addresses
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(PORT_NUMBER);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ // ---------------------------
+
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_TCP, "tun0");
+ CCNxControl *request = ccnxControl_CreateIPTunnelRequest(iptun);
+
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+
+ CCNxControl *response = metisConfiguration_ProcessCreateTunnel(config, request, mockup_id);
+
+ // crank the handle to lets the ACKs or NACKs move
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ // Validate the ACK
+ assertNotNull(response, "Got null response");
+
+ assertTrue(cpi_GetMessageType(response) == CPI_ACK,
+ "CPI message not an ACK: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ cpiInterfaceIPTunnel_Release(&iptun);
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessCreateTunnel_Dup)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7000;
+ MetisIoOperations *ops = _addIngressMockConnection(metis, mockup_id);
+
+ // ---------------------------
+ // Tunnel addresses
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(PORT_NUMBER);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ // ---------------------------
+
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_TCP, "tun0");
+ CCNxControl *request = ccnxControl_CreateIPTunnelRequest(iptun);
+
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+
+ CCNxControl *response_1 = metisConfiguration_ProcessCreateTunnel(config, request, mockup_id);
+ assertNotNull(response_1, "got null response");
+ assertTrue(ccnxControl_IsACK(response_1), "Did not get ACK response for first tunnel");
+ ccnxControl_Release(&response_1);
+
+ CCNxControl *response_2 = metisConfiguration_ProcessCreateTunnel(config, request, mockup_id);
+ assertNotNull(response_2, "got null response");
+ assertTrue(ccnxControl_IsNACK(response_2), "Did not get NACK response for second tunnel");
+
+ ccnxControl_Release(&response_2);
+
+ ccnxControl_Release(&request);
+ cpiInterfaceIPTunnel_Release(&iptun);
+ metisForwarder_Destroy(&metis);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessCreateTunnel_UDP)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7;
+
+ // -----
+ // Issue a command to create a UDP tunnel. We should be able to verify that it's in
+ // the connection table and we'll see the ACK come back to our mock interface.
+
+ // ---------------------------
+ // Tunnel addresses
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(PORT_NUMBER);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ // ---------------------------
+
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_UDP, "conn0");
+ CCNxControl *request = ccnxControl_CreateIPTunnelRequest(iptun);
+
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+
+ CCNxControl *response = metisConfiguration_ProcessCreateTunnel(config, request, mockup_id);
+
+ // Validate the ACK
+ assertNotNull(response, "Got null response");
+
+ assertTrue(cpi_GetMessageType(response) == CPI_ACK,
+ "CPI message not an ACK: %s",
+ parcJSON_ToString(ccnxControl_GetJson(response)));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ cpiInterfaceIPTunnel_Release(&iptun);
+ metisForwarder_Destroy(&metis);
+}
+
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessConnectionList)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7;
+ MetisIoOperations *ops = _addIngressMockConnection(metis, mockup_id);
+
+ CCNxControl *request = ccnxControl_CreateConnectionListRequest();
+
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+ CCNxControl *response = metisConfiguration_ProcessConnectionList(config, request, mockup_id);
+
+ // Validate the response
+ assertNotNull(response, "Got null response");
+
+ // Get the CPI response out of the test mock up
+ CPIConnectionList *list = cpiLinks_ConnectionListFromControlMessage(response);
+ assertTrue(cpiConnectionList_Length(list) == 1, "Wrong list size, expected %u got %zu", 1, cpiConnectionList_Length(list));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ cpiConnectionList_Destroy(&list);
+ metisForwarder_Destroy(&metis);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessAddConnectionEthernet)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 77;
+
+ // create the listener
+ char *ifname = _pickInterfaceName(metis);
+ CPIListener *cpiListener = cpiListener_CreateEther(ifname, 0x0801, "fake0");
+ CCNxControl *control = cpiListener_CreateAddMessage(cpiListener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.");
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&cpiListener);
+
+ // ========
+ uint8_t peerEther[6] = { 0x02, 0x33, 0x44, 0x55, 0x66, 0x77 };
+ CPIAddress *peerAddress = cpiAddress_CreateFromLink(peerEther, sizeof(peerEther));
+ CPIConnectionEthernet *etherconn = cpiConnectionEthernet_Create(ifname, peerAddress, 0x0801, "conn3");
+ CCNxControl *addRequest = cpiConnectionEthernet_CreateAddMessage(etherconn);
+
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+
+ CCNxControl *response = metisConfiguration_ProcessAddConnectionEthernet(config, addRequest, mockup_id);
+
+ // crank the handle to lets the ACKs or NACKs move
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+
+ // Get the CPI response out of the test mock up
+ assertNotNull(response, "Got null response");
+
+ assertTrue(ccnxControl_IsACK(response), "Response is not an ACK")
+ {
+ ccnxControl_Display(response, 3);
+ }
+
+ // we must manually destroy a Mock connection
+ ccnxControl_Release(&response);
+ free(ifname);
+ cpiConnectionEthernet_Release(&etherconn);
+ cpiAddress_Destroy(&peerAddress);
+ ccnxControl_Release(&addRequest);
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_ProcessRemoveConnectionEthernet)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_Receive_AddConnectionEthernet)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7;
+ MetisIoOperations *ops = _addIngressMockConnection(metis, mockup_id);
+
+ // create the listener
+ char *ifname = _pickInterfaceName(metis);
+ CPIListener *cpiListener = cpiListener_CreateEther(ifname, 0x0801, "fake0");
+ CCNxControl *control = cpiListener_CreateAddMessage(cpiListener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.");
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&cpiListener);
+
+ // create the connection
+ uint8_t linkAddrArray[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+ uint16_t etherType = 0x0801;
+
+ CPIAddress *peerAddress = cpiAddress_CreateFromLink(linkAddrArray, sizeof(linkAddrArray));
+ CPIConnectionEthernet *etherconn = cpiConnectionEthernet_Create(ifname, peerAddress, etherType, "conn3");
+ CCNxControl *addRequest = cpiConnectionEthernet_CreateAddMessage(etherconn);
+
+ // Translate the control message to a MetisMessage
+ PARCBuffer *buffer = metisTlv_EncodeControlPlaneInformation(addRequest);
+
+ MetisMessage *message = metisMessage_CreateFromArray(parcBuffer_Overlay(buffer, 0), parcBuffer_Limit(buffer), mockup_id, 2, metisForwarder_GetLogger(metis));
+
+ MetisConfiguration *config = metisForwarder_GetConfiguration(metis);
+
+ // this will release the message
+ metisConfiguration_Receive(config, message);
+
+ // ==== Verify it's in the connection table
+
+ MetisConnectionList *connList = metisConnectionTable_GetEntries(metisForwarder_GetConnectionTable(metis));
+ assertNotNull(connList, "Got null connection list");
+
+ bool found = false;
+ for (size_t i = 0; i < metisConnectionList_Length(connList) && !found; i++) {
+ MetisConnection *conn = metisConnectionList_Get(connList, i);
+ const MetisAddressPair *pair = metisConnection_GetAddressPair(conn);
+ const CPIAddress *remote = metisAddressPair_GetRemote(pair);
+ if (cpiAddress_Equals(remote, peerAddress)) {
+ found = true;
+ }
+ }
+
+ assertTrue(found, "Could not find peer address in the connection table as a remote");
+
+ // ==== Cleanup
+
+ parcBuffer_Release(&buffer);
+ ccnxControl_Release(&addRequest);
+ cpiConnectionEthernet_Release(&etherconn);
+ cpiAddress_Destroy(&peerAddress);
+ free(ifname);
+ metisConnectionList_Destroy(&connList);
+ metisForwarder_Destroy(&metis);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+/*
+ * Try to add a second connection with same symbolic name
+ */
+LONGBOW_TEST_CASE(Local, metisConfiguration_Receive_AddConnectionEthernet_Dup)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ uint8_t peerEther[6] = { 7, 8, 9, 10, 11, 12 };
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 7000;
+ MetisIoOperations *ops = _addIngressMockConnection(metis, mockup_id);
+
+ char *ifname = _pickInterfaceName(metis);
+ CPIListener *cpiListener = cpiListener_CreateEther(ifname, 0x0801, "fake0");
+ CCNxControl *control = cpiListener_CreateAddMessage(cpiListener);
+ metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ ccnxControl_Release(&control);
+ cpiListener_Release(&cpiListener);
+ free(ifname);
+
+ MetisListenerOps *listener = metisListenerSet_Get(metisForwarder_GetListenerSet(metis), 0);
+
+ // Create a mock up of an interface so we can see the response
+ bool success = _addEthernetConnection(metis, 1000, "conn3", listener, peerEther);
+ assertTrue(success, "Failed to add first instance of connection");
+
+ // now add again, should fail
+ bool failure = _addEthernetConnection(metis, 1001, "conn3", listener, peerEther);
+ assertFalse(failure, "Should have failed to add it a second time");
+
+ metisForwarder_Destroy(&metis);
+ mockIoOperationsData_Destroy(&ops);
+}
+
+LONGBOW_TEST_CASE(Local, metisConfiguration_Receive_RemoveConnectionEthernet)
+{
+}
+
+// ======================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_Configuration);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationFile.c b/metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationFile.c
new file mode 100644
index 00000000..6c7e19df
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationFile.c
@@ -0,0 +1,403 @@
+/*
+ * 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_ConfigurationFile.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// =========================================================================
+
+LONGBOW_TEST_RUNNER(metis_ConfigurationFile)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Create);
+ LONGBOW_RUN_TEST_FIXTURE(Process);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_ConfigurationFile)
+{
+ 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_ConfigurationFile)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==============================================================================
+
+LONGBOW_TEST_FIXTURE(Create)
+{
+ LONGBOW_RUN_TEST_CASE(Create, metisConfigurationFile_Create);
+ LONGBOW_RUN_TEST_CASE(Create, metisConfigurationFile_Create_CantRead);
+ LONGBOW_RUN_TEST_CASE(Create, metisConfigurationFile_Create_Missing);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Create)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Create)
+{
+ 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;
+}
+
+static void
+_writeConfigFile(FILE *fh)
+{
+ ssize_t nwritten = fprintf(fh, "add listener udp conn0 127.0.0.1 9696\n");
+ assertTrue(nwritten > 0, "Bad fprintf");
+ fflush(fh);
+}
+
+LONGBOW_TEST_CASE(Create, metisConfigurationFile_Create)
+{
+ char template[] = "/tmp/test_metis_ConfigurationFile.XXXXXX";
+ int fd = mkstemp(template);
+ assertTrue(fd > -1, "Error creating temp file: (%d) %s", errno, strerror(errno));
+
+ FILE *fh = fdopen(fd, "w");
+ _writeConfigFile(fh);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(metis, template);
+
+ assertNotNull(cf, "Should have returned non-null for good configuration file");
+
+ metisConfigurationFile_Release(&cf);
+ metisForwarder_Destroy(&metis);
+ fclose(fh);
+ unlink(template);
+}
+
+LONGBOW_TEST_CASE(Create, metisConfigurationFile_Create_CantRead)
+{
+ char template[] = "/tmp/test_metis_ConfigurationFile.XXXXXX";
+ int fd = mkstemp(template);
+ assertTrue(fd > -1, "Error creating temp file: (%d) %s", errno, strerror(errno));
+
+ chmod(template, 0);
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(metis, template);
+
+ chmod(template, 0600);
+ unlink(template);
+
+ uid_t uid = getuid(), euid = geteuid();
+ if (uid <= 0 || uid != euid) {
+ metisConfigurationFile_Release(&cf);
+ } else {
+ assertNull(cf, "Should have returned null configuration file for non-readable file");
+ }
+
+ metisForwarder_Destroy(&metis);
+ close(fd);
+}
+
+LONGBOW_TEST_CASE(Create, metisConfigurationFile_Create_Missing)
+{
+ char template[] = "/tmp/test_metis_ConfigurationFile.ZZZZZZZZZ";
+
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(metis, template);
+
+ assertNull(cf, "Should have returned null configuration file for missing file");
+
+ metisForwarder_Destroy(&metis);
+}
+
+// ======================================================
+
+typedef struct test_data {
+ MetisForwarder *metis;
+ char template[1024];
+ int fd;
+ FILE *fh;
+} TestData;
+
+LONGBOW_TEST_FIXTURE(Process)
+{
+ LONGBOW_RUN_TEST_CASE(Process, metisConfigurationFile_Process_NoErrors);
+ LONGBOW_RUN_TEST_CASE(Process, metisConfigurationFile_Process_WithErrors);
+ LONGBOW_RUN_TEST_CASE(Process, metisConfigurationFile_Process_WithComments);
+ LONGBOW_RUN_TEST_CASE(Process, metisConfigurationFile_Process_Whitespace);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Process)
+{
+ TestData *data = parcMemory_Allocate(sizeof(TestData));
+ data->metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ sprintf(data->template, "/tmp/test_metis_ConfigurationFile.XXXXXX");
+
+ data->fd = mkstemp(data->template);
+ assertTrue(data->fd > -1, "Error creating temp file: (%d) %s", errno, strerror(errno));
+
+ data->fh = fdopen(data->fd, "w");
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Process)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ fclose(data->fh);
+ unlink(data->template);
+ metisForwarder_Destroy(&data->metis);
+ parcMemory_Deallocate((void **) &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(Process, metisConfigurationFile_Process_NoErrors)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _writeConfigFile(data->fh);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(data->metis, data->template);
+
+ bool success = metisConfigurationFile_Process(cf);
+ assertTrue(success, "Failed to execute configuration file.");
+ assertTrue(cf->linesRead == 1, "Should have read 1 line, got %zu", cf->linesRead);
+
+ metisConfigurationFile_Release(&cf);
+}
+
+LONGBOW_TEST_CASE(Process, metisConfigurationFile_Process_WithErrors)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _writeConfigFile(data->fh);
+
+ ssize_t nwritten = fprintf(data->fh, "blah blah\n");
+ assertTrue(nwritten > 0, "Bad write");
+
+ // this should not be executed
+ nwritten = fprintf(data->fh, "add listener conn3 tcp 127.0.0.1 9696\n");
+ assertTrue(nwritten > 0, "Bad write");
+
+ fflush(data->fh);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(data->metis, data->template);
+
+ bool success = metisConfigurationFile_Process(cf);
+ assertFalse(success, "Should have failed to execute configuration file.") {
+ int res;
+ res = system("netstat -an -p tcp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+ assertTrue(cf->linesRead == 2, "Should have read 2 lines, got %zu", cf->linesRead);
+
+ metisConfigurationFile_Release(&cf);
+}
+
+LONGBOW_TEST_CASE(Process, metisConfigurationFile_Process_WithComments)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _writeConfigFile(data->fh);
+
+ ssize_t nwritten = fprintf(data->fh, "# ignore this\n");
+ assertTrue(nwritten > 0, "Bad write");
+
+ nwritten = fprintf(data->fh, "add listener tcp conn3 127.0.0.1 9696\n");
+ assertTrue(nwritten > 0, "Bad write");
+
+ fflush(data->fh);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(data->metis, data->template);
+
+ bool success = metisConfigurationFile_Process(cf);
+ assertTrue(success, "Should have failed to execute configuration file.") {
+ int res;
+ res = system("netstat -an -p tcp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+ assertTrue(cf->linesRead == 3, "Should have read 3 lines, got %zu", cf->linesRead);
+
+ metisConfigurationFile_Release(&cf);
+}
+
+LONGBOW_TEST_CASE(Process, metisConfigurationFile_Process_Whitespace)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _writeConfigFile(data->fh);
+
+ ssize_t nwritten = fprintf(data->fh, "add listener tcp conn3 127.0.0.1 9696\n");
+ assertTrue(nwritten > 0, "Bad write");
+
+ fflush(data->fh);
+
+ MetisConfigurationFile *cf = metisConfigurationFile_Create(data->metis, data->template);
+
+ bool success = metisConfigurationFile_Process(cf);
+ assertTrue(success, "Should have failed to execute configuration file.") {
+ int res;
+ res = system("netstat -an -p tcp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+ assertTrue(cf->linesRead == 2, "Should have read 2 lines, got %zu", cf->linesRead);
+
+ metisConfigurationFile_Release(&cf);
+}
+
+
+// ==============================================================================
+
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _stripLeadingWhitespace);
+ LONGBOW_RUN_TEST_CASE(Local, _stripTrailingWhitespace);
+ LONGBOW_RUN_TEST_CASE(Local, _trim);
+}
+
+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;
+}
+
+typedef struct test_vector {
+ const char *input;
+ const char *output;
+ bool sentinel;
+} TestVector;
+
+LONGBOW_TEST_CASE(Local, _stripLeadingWhitespace)
+{
+ TestVector vectors[] = {
+ { .input = "", .output = "" },
+ { .input = " ", .output = "" },
+ { .input = "\t", .output = "" },
+ { .input = "a", .output = "a" },
+ { .input = "abc", .output = "abc" },
+ { .input = " a c ", .output = "a c " },
+ { .input = " bc", .output = "bc" },
+ { .input = "\tbc", .output = "bc" },
+ { .input = " \tbc", .output = "bc" },
+ { .input = "\t\tbc ", .output = "bc " },
+ { .input = NULL, .output = NULL },
+ };
+
+ for (int i = 0; vectors[i].input != NULL; i++) {
+ char *copy = parcMemory_StringDuplicate(vectors[i].input, strlen(vectors[i].input));
+ char *test = _stripLeadingWhitespace(copy);
+ assertTrue(strcmp(test, vectors[i].output) == 0, "Bad output index %d. input = '%s' expected = '%s' actual = '%s'", i, vectors[i].input, vectors[i].output, test);
+ parcMemory_Deallocate((void **) &copy);
+ }
+}
+
+LONGBOW_TEST_CASE(Local, _stripTrailingWhitespace)
+{
+ TestVector vectors[] = {
+ { .input = "", .output = "" },
+ { .input = " ", .output = "" },
+ { .input = "\t", .output = "" },
+ { .input = "a", .output = "a" },
+ { .input = "abc", .output = "abc" },
+ { .input = " a c ", .output = " a c" },
+ { .input = "bc ", .output = "bc" },
+ { .input = "bc\t", .output = "bc" },
+ { .input = "bc \t", .output = "bc" },
+ { .input = " bc\t\t", .output = " bc" },
+ { .input = NULL, .output = NULL },
+ };
+
+ for (int i = 0; vectors[i].input != NULL; i++) {
+ char *copy = parcMemory_StringDuplicate(vectors[i].input, strlen(vectors[i].input));
+ char *test = _stripTrailingWhitespace(copy);
+ assertTrue(strcmp(test, vectors[i].output) == 0, "Bad output index %d. input = '%s' expected = '%s' actual = '%s'", i, vectors[i].input, vectors[i].output, test);
+ parcMemory_Deallocate((void **) &copy);
+ }
+}
+
+LONGBOW_TEST_CASE(Local, _trim)
+{
+ TestVector vectors[] = {
+ { .input = "", .output = "" },
+ { .input = " ", .output = "" },
+ { .input = "\t", .output = "" },
+ { .input = "a", .output = "a" },
+ { .input = "abc", .output = "abc" },
+ { .input = " a c ", .output = "a c" },
+ { .input = "bc ", .output = "bc" },
+ { .input = "bc\t", .output = "bc" },
+ { .input = "bc \t", .output = "bc" },
+ { .input = " bc\t\t", .output = "bc" },
+ { .input = NULL, .output = NULL },
+ };
+
+ for (int i = 0; vectors[i].input != NULL; i++) {
+ char *copy = parcMemory_StringDuplicate(vectors[i].input, strlen(vectors[i].input));
+ char *test = _trim(copy);
+ assertTrue(strcmp(test, vectors[i].output) == 0, "Bad output index %d. input = '%s' expected = '%s' actual = '%s'", i, vectors[i].input, vectors[i].output, test);
+ parcMemory_Deallocate((void **) &copy);
+ }
+}
+
+// ======================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_ConfigurationFile);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationListeners.c b/metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationListeners.c
new file mode 100644
index 00000000..8ac2c82d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_ConfigurationListeners.c
@@ -0,0 +1,644 @@
+/*
+ * 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.
+ */
+
+/*
+ * Some of these tests might not execute on certain systems, as they
+ * depend on having INET and INET6 addresses available. If you system
+ * does not have one or both of those, the corresponding tests will not
+ * execute.
+ */
+
+// We need to specifically include the Ethernet mockup and set the proper define so
+// we do not need an actual Ethernet listener
+
+#define METIS_MOCK_ETHERNET 1
+#include "../../io/test/testrig_GenericEther.c"
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_ConfigurationListeners.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <signal.h>
+#include <net/ethernet.h>
+
+#define TEST_PORT 9697
+static const CPIAddress *
+getFirstAddressOfType(CPIInterfaceSet *set, CPIAddressType type)
+{
+ for (int i = 0; i < cpiInterfaceSet_Length(set); i++) {
+ CPIInterface *iface = cpiInterfaceSet_GetByOrdinalIndex(set, i);
+ const CPIAddressList *list = cpiInterface_GetAddresses(iface);
+ for (int j = 0; j < cpiAddressList_Length(list); j++) {
+ const CPIAddress *address = cpiAddressList_GetItem(list, j);
+ if (cpiAddress_GetType(address) == type) {
+ return address;
+ }
+ }
+ }
+ return NULL;
+}
+
+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);
+}
+
+static bool
+verifyInNetstat(const char *addressString, int port)
+{
+ // now verify that we are listening
+ // tcp4 0 0 127.0.0.1.49009 *.* LISTEN
+
+ FILE *fp = popen("netstat -an", "r");
+ assertNotNull(fp, "Got null opening netstat for reading");
+
+ char buffer[4][1024];
+
+ sprintf(buffer[0], "%s.%d", addressString, port);
+ sprintf(buffer[1], "%s:%d", addressString, port);
+ sprintf(buffer[2], "%s%%lo0.%d", addressString, port);
+ sprintf(buffer[3], "%s%%lo0:%d", addressString, port);
+
+ char str[1035];
+ bool found = false;
+ while (!found && (fgets(str, sizeof(str) - 1, fp) != NULL)) {
+ for (int i = 0; i < 4; i++) {
+ if (strstr(str, buffer[i]) != NULL) {
+ found = true;
+ }
+ }
+ }
+
+ blockSigChild();
+ pclose(fp);
+ unblockSigChild();
+
+ return found;
+}
+
+// returns a strdup() of the interface name, use free(3)
+static char *
+_pickInterfaceName(MetisForwarder *metis)
+{
+ char *ifname = NULL;
+
+ CPIInterfaceSet *set = metisSystem_Interfaces(metis);
+ size_t length = cpiInterfaceSet_Length(set);
+ assertTrue(length > 0, "metisSystem_Interfaces returned no interfaces");
+
+ for (size_t i = 0; i < length; i++) {
+ CPIInterface *iface = cpiInterfaceSet_GetByOrdinalIndex(set, i);
+ const CPIAddressList *addressList = cpiInterface_GetAddresses(iface);
+
+ size_t length = cpiAddressList_Length(addressList);
+ for (size_t i = 0; i < length && !ifname; i++) {
+ const CPIAddress *a = cpiAddressList_GetItem(addressList, i);
+ if (cpiAddress_GetType(a) == cpiAddressType_LINK) {
+ ifname = strdup(cpiInterface_GetName(iface));
+ }
+ }
+ }
+
+ cpiInterfaceSet_Destroy(&set);
+ return ifname;
+}
+
+// =========================================================================
+
+LONGBOW_TEST_RUNNER(metis_Configuration)
+{
+ 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_Configuration)
+{
+ 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_Configuration)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==============================================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_SetupAll);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_Add_Ether);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_Add_IP_UDP4);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_Add_IP_UDP6);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_Add_IP_TCP4);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_Add_IP_TCP6);
+ LONGBOW_RUN_TEST_CASE(Global, metisConfigurationListeners_Remove);
+}
+
+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, metisConfigurationListeners_SetupAll)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ metisConfigurationListeners_SetupAll(metisForwarder_GetConfiguration(metis), TEST_PORT, NULL);
+
+ MetisListenerSet *set = metisForwarder_GetListenerSet(metis);
+ size_t len = metisListenerSet_Length(set);
+ assertTrue(len > 0, "Bad listener set size, expected positive, got %zu", len);
+
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Global, metisConfigurationListeners_Add_Ether)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 77;
+
+ // create the listener
+ char *ifname = _pickInterfaceName(metis);
+ CPIListener *cpiListener = cpiListener_CreateEther(ifname, 0x0801, "fake0");
+ CCNxControl *control = cpiListener_CreateAddMessage(cpiListener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.");
+ free(ifname);
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&cpiListener);
+
+ MetisListenerSet *set = metisForwarder_GetListenerSet(metis);
+ size_t len = metisListenerSet_Length(set);
+ assertTrue(len == 1, "Bad listener set size, expected 1, got %zu", len);
+
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Global, metisConfigurationListeners_Add_IP_UDP4)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 77;
+
+ // create the listener
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(TEST_PORT);
+ int result = inet_aton("127.0.0.1", &sin.sin_addr);
+ assertTrue(result == 1, "failed inet_aton: (%d) %s", errno, strerror(errno));
+
+ CPIAddress *address = cpiAddress_CreateFromInet(&sin);
+ CPIListener *listener = cpiListener_CreateIP(IPTUN_UDP, address, "conn1");
+ cpiAddress_Destroy(&address);
+
+
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.") {
+ int res;
+ res = system("netstat -an -p udp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+
+ MetisListenerSet *set = metisForwarder_GetListenerSet(metis);
+ size_t len = metisListenerSet_Length(set);
+ assertTrue(len == 1, "Bad listener set size, expected 1, got %zu", len);
+
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Global, metisConfigurationListeners_Add_IP_UDP6)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 77;
+
+ // create the listener
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(TEST_PORT);
+ int result = inet_pton(AF_INET6, "::1", &(sin6.sin6_addr));
+ if (result == 1) {
+ CPIAddress *address = cpiAddress_CreateFromInet6(&sin6);
+ CPIListener *listener = cpiListener_CreateIP(IPTUN_UDP, address, "conn1");
+ cpiAddress_Destroy(&address);
+
+
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.") {
+ int res;
+ res = system("netstat -an -p udp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+
+ MetisListenerSet *set = metisForwarder_GetListenerSet(metis);
+ size_t len = metisListenerSet_Length(set);
+ assertTrue(len == 1, "Bad listener set size, expected 1, got %zu", len);
+ metisForwarder_Destroy(&metis);
+ } else {
+ metisForwarder_Destroy(&metis);
+ testSkip("IPv6 not supported");
+ }
+}
+
+LONGBOW_TEST_CASE(Global, metisConfigurationListeners_Add_IP_TCP4)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 77;
+
+ // create the listener
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(TEST_PORT);
+ int result = inet_aton("127.0.0.1", &sin.sin_addr);
+ assertTrue(result == 1, "failed inet_aton: (%d) %s", errno, strerror(errno));
+
+ CPIAddress *address = cpiAddress_CreateFromInet(&sin);
+ CPIListener *listener = cpiListener_CreateIP(IPTUN_TCP, address, "conn1");
+ cpiAddress_Destroy(&address);
+
+
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.") {
+ int res;
+ res = system("netstat -an -p tcp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+
+ MetisListenerSet *set = metisForwarder_GetListenerSet(metis);
+ size_t len = metisListenerSet_Length(set);
+ assertTrue(len == 1, "Bad listener set size, expected 1, got %zu", len);
+
+ metisForwarder_Destroy(&metis);
+}
+
+LONGBOW_TEST_CASE(Global, metisConfigurationListeners_Add_IP_TCP6)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+
+ // Create a mock up of an interface so we can see the response
+ unsigned mockup_id = 77;
+
+ // create the listener
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(TEST_PORT);
+ int result = inet_pton(AF_INET6, "::1", &(sin6.sin6_addr));
+ if (result == 1) {
+ CPIAddress *address = cpiAddress_CreateFromInet6(&sin6);
+ CPIListener *listener = cpiListener_CreateIP(IPTUN_TCP, address, "conn1");
+ cpiAddress_Destroy(&address);
+
+
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ bool listenerOk = metisConfigurationListeners_Add(metisForwarder_GetConfiguration(metis), control, mockup_id);
+ assertTrue(listenerOk, "Failed to setup ether listener.") {
+ int res;
+ res = system("netstat -an -p tcp");
+ assertTrue(res != -1, "Error on system call");
+ res = system("ps -el");
+ assertTrue(res != -1, "Error on system call");
+ }
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+
+ MetisListenerSet *set = metisForwarder_GetListenerSet(metis);
+ size_t len = metisListenerSet_Length(set);
+ assertTrue(len == 1, "Bad listener set size, expected 1, got %zu", len);
+ metisForwarder_Destroy(&metis);
+ } else {
+ metisForwarder_Destroy(&metis);
+ testSkip("IPv6 not supported");
+ }
+}
+
+LONGBOW_TEST_CASE(Global, metisConfigurationListeners_Remove)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+// ==============================================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, setupEthernetListenerOnLink);
+ LONGBOW_RUN_TEST_CASE(Local, setupEthernetListenerOnLink_SecondEthertype);
+ LONGBOW_RUN_TEST_CASE(Local, setupIPMulticastListenerOnInet);
+ LONGBOW_RUN_TEST_CASE(Local, setupListenersOnAddress);
+ LONGBOW_RUN_TEST_CASE(Local, setupListenersOnInet);
+ LONGBOW_RUN_TEST_CASE(Local, setupListenersOnInet6);
+ LONGBOW_RUN_TEST_CASE(Local, setupListenersOnLink);
+ LONGBOW_RUN_TEST_CASE(Local, setupLocalListener);
+ LONGBOW_RUN_TEST_CASE(Local, setupTcpListenerOnInet);
+ LONGBOW_RUN_TEST_CASE(Local, setupTcpListenerOnInet6);
+ LONGBOW_RUN_TEST_CASE(Local, setupUdpListenerOnInet);
+ LONGBOW_RUN_TEST_CASE(Local, setupUdpListenerOnInet6);
+}
+
+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, setupEthernetListenerOnLink)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ uint8_t addr[ETHER_ADDR_LEN] = { 1, 2, 3, 4, 5, 6 };
+ CPIAddress *localAddress = cpiAddress_CreateFromLink(addr, ETHER_ADDR_LEN);
+
+ char *ifname = _pickInterfaceName(metis);
+ MetisListenerOps *listenerops = _setupEthernetListenerOnLink(metis, localAddress, ifname, 0x0801);
+ assertNotNull(listenerops, "Got null result from _setupEthernetListenerOnLink");
+
+ free(ifname);
+ cpiAddress_Destroy(&localAddress);
+ metisForwarder_Destroy(&metis);
+}
+
+/*
+ * The current system does not allow multiple ether listeners on a single interface.
+ * even if they are different ethertypes
+ */
+LONGBOW_TEST_CASE(Local, setupEthernetListenerOnLink_SecondEthertype)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ uint8_t addr[ETHER_ADDR_LEN] = { 1, 2, 3, 4, 5, 6 };
+ CPIAddress *localAddress = cpiAddress_CreateFromLink(addr, ETHER_ADDR_LEN);
+
+ char *ifname = _pickInterfaceName(metis);
+ MetisListenerOps *listenerops = _setupEthernetListenerOnLink(metis, localAddress, ifname, 0x0801);
+ assertNotNull(listenerops, "Got null result from _setupEthernetListenerOnLink");
+
+ // now try to add again with different ethertype
+ MetisListenerOps *second = _setupEthernetListenerOnLink(metis, localAddress, ifname, 0x0802);
+ assertNull(second, "Should have gotten null for second listener");
+
+ free(ifname);
+ cpiAddress_Destroy(&localAddress);
+ metisForwarder_Destroy(&metis);
+}
+
+
+LONGBOW_TEST_CASE(Local, setupIPMulticastListenerOnInet)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, setupListenersOnAddress)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, setupListenersOnInet)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, setupListenersOnInet6)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, setupListenersOnLink)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, setupLocalListener)
+{
+ testUnimplemented("This test is unimplemented");
+}
+
+LONGBOW_TEST_CASE(Local, setupTcpListenerOnInet)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ CPIInterfaceSet *set = metisSystem_Interfaces(metis);
+ const CPIAddress *address = getFirstAddressOfType(set, cpiAddressType_INET);
+ if (address != NULL) {
+ char valueToFind[1024];
+ struct sockaddr_in sin;
+ cpiAddress_GetInet(address, &sin);
+ _setupTcpListenerOnInet(metis, address, PORT_NUMBER);
+ bool found = verifyInNetstat(inet_ntoa(sin.sin_addr), PORT_NUMBER);
+ if (!found) {
+ // extra diagnostics
+ int ret = system("netstat -an -p tcp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+ assertTrue(found, "Did not find value %s in netstat", valueToFind);
+ }
+
+ cpiInterfaceSet_Destroy(&set);
+ metisForwarder_Destroy(&metis);
+
+ if (address == NULL) {
+ testSkip("No network interfaces of type INET found");
+ }
+}
+
+LONGBOW_TEST_CASE(Local, setupTcpListenerOnInet6)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ CPIInterfaceSet *set = metisSystem_Interfaces(metis);
+ const CPIAddress *address = getFirstAddressOfType(set, cpiAddressType_INET6);
+ if (address != NULL) {
+ char valueToFind[1024];
+ char inet6str[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 sin6;
+ cpiAddress_GetInet6(address, &sin6);
+ inet_ntop(AF_INET6, &sin6.sin6_addr, inet6str, INET6_ADDRSTRLEN);
+ _setupTcpListenerOnInet6(metis, address, PORT_NUMBER);
+ bool found = verifyInNetstat(inet6str, PORT_NUMBER);
+ if (!found) {
+ // extra diagnostics
+ int ret = system("netstat -an -p tcp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+ assertTrue(found, "Did not find value %s in netstat", valueToFind);
+ }
+
+ cpiInterfaceSet_Destroy(&set);
+ metisForwarder_Destroy(&metis);
+
+ if (address == NULL) {
+ testSkip("No network interfaces of type INET found");
+ }
+}
+
+LONGBOW_TEST_CASE(Local, setupUdpListenerOnInet)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ CPIInterfaceSet *set = metisSystem_Interfaces(metis);
+ const CPIAddress *address = getFirstAddressOfType(set, cpiAddressType_INET);
+ if (address != NULL) {
+ char valueToFind[1024];
+ struct sockaddr_in sin;
+ cpiAddress_GetInet(address, &sin);
+ _setupUdpListenerOnInet(metis, address, PORT_NUMBER);
+ bool found = verifyInNetstat(inet_ntoa(sin.sin_addr), PORT_NUMBER);
+ if (!found) {
+ // extra diagnostics
+ int ret = system("netstat -an -p udp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+ assertTrue(found, "Did not find value %s in netstat", valueToFind);
+ }
+
+ cpiInterfaceSet_Destroy(&set);
+ metisForwarder_Destroy(&metis);
+
+ if (address == NULL) {
+ testSkip("No network interfaces of type INET found");
+ }
+}
+
+LONGBOW_TEST_CASE(Local, setupUdpListenerOnInet6)
+{
+ MetisForwarder *metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug);
+
+ CPIInterfaceSet *set = metisSystem_Interfaces(metis);
+ const CPIAddress *address = getFirstAddressOfType(set, cpiAddressType_INET6);
+ if (address != NULL) {
+ char valueToFind[1024];
+ char inet6str[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 sin6;
+ cpiAddress_GetInet6(address, &sin6);
+ inet_ntop(AF_INET6, &sin6.sin6_addr, inet6str, INET6_ADDRSTRLEN);
+ _setupUdpListenerOnInet6(metis, address, PORT_NUMBER);
+ bool found = verifyInNetstat(inet6str, PORT_NUMBER);
+ if (!found) {
+ // extra diagnostics
+ int ret = system("netstat -an -p udp");
+ assertTrue(ret > -1, "Error on system call");
+ }
+ assertTrue(found, "Did not find value %s in netstat", valueToFind);
+ }
+
+ cpiInterfaceSet_Destroy(&set);
+ metisForwarder_Destroy(&metis);
+
+ if (address == NULL) {
+ testSkip("No network interfaces of type INET found");
+ }
+}
+
+
+// ======================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_Configuration);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_ControlState.c b/metis/ccnx/forwarder/metis/config/test/test_metis_ControlState.c
new file mode 100644
index 00000000..088118d4
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_ControlState.c
@@ -0,0 +1,253 @@
+/*
+ * 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_ControlState.c"
+
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(metis_Control)
+{
+ // 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_Control)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_Control)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==================================================
+
+static CCNxMetaMessage *_testWriteMessage = NULL;
+static CCNxMetaMessage *_testReadMessage = NULL;
+
+/**
+ * For testing purposes writes a message to a local buffer and reads response from local buffer
+ *
+ * _testWriteMessage will be an allocated reference to what is written
+ * _testReadMessage will be sent back (returend). You must put an allocated message there
+ * before calling this test function.
+ */
+static CCNxMetaMessage *
+_testWriteRead(void *userdata, CCNxMetaMessage *msg)
+{
+ _testWriteMessage = ccnxMetaMessage_Acquire(msg);
+ return ccnxMetaMessage_Acquire(_testReadMessage);
+}
+
+static unsigned _testCommandExecuteCount = 0;
+
+static MetisCommandReturn
+_testCommand(MetisCommandParser *parser, MetisCommandOps *ops, PARCList *args)
+{
+ _testCommandExecuteCount++;
+ return MetisCommandReturn_Success;
+}
+
+static MetisCommandOps _testCommandOps = {
+ .command = "test", // empty string for root
+ .init = NULL,
+ .execute = _testCommand
+};
+
+// ==================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_DispatchCommand);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_GetDebug);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_Interactive);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_RegisterCommand);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_SetDebug);
+ LONGBOW_RUN_TEST_CASE(Global, metisControlState_WriteRead);
+ LONGBOW_RUN_TEST_CASE(Global, _metisControlState_ParseStringIntoTokens);
+}
+
+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, metisControlState_Create)
+{
+ char hello[] = "hello";
+ MetisControlState *state = metisControlState_Create(hello, _testWriteRead);
+ metisControlState_Destroy(&state);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlState_DispatchCommand)
+{
+ char hello[] = "hello";
+ MetisControlState *state = metisControlState_Create(hello, _testWriteRead);
+
+ metisControlState_RegisterCommand(state, &_testCommandOps);
+
+ const char *argv[] = { "test", "foobar" };
+ PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(args, 2, (void **) &argv[0]);
+
+ _testCommandExecuteCount = 0;
+
+ metisControlState_DispatchCommand(state, args);
+
+ assertTrue(_testCommandExecuteCount == 1, "Incorrect execution count, expected 1 got %u", _testCommandExecuteCount);
+ parcList_Release(&args);
+ metisControlState_Destroy(&state);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlState_GetDebug)
+{
+ char hello[] = "hello";
+ MetisControlState *state = metisControlState_Create(hello, _testWriteRead);
+
+ bool test = metisControlState_GetDebug(state);
+ assertTrue(test == state->debugFlag, "debug flag in unexpected state");
+
+ metisControlState_Destroy(&state);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlState_Interactive)
+{
+ // this reads commands from stdin. not sure how to test this.
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, metisControlState_RegisterCommand)
+{
+ char hello[] = "hello";
+ MetisControlState *state = metisControlState_Create(hello, _testWriteRead);
+
+ metisControlState_RegisterCommand(state, &_testCommandOps);
+
+ bool match = metisCommandParser_ContainsCommand(state->parser, "test");
+ assertTrue(match, "Command not found in parser");
+
+ metisControlState_Destroy(&state);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlState_SetDebug)
+{
+ char hello[] = "hello";
+ MetisControlState *state = metisControlState_Create(hello, _testWriteRead);
+
+ assertFalse(state->debugFlag, "debug flag in unexpected true state");
+ metisControlState_SetDebug(state, true);
+ assertTrue(state->debugFlag, "debug flag in unexpected false state");
+
+ metisControlState_Destroy(&state);
+}
+
+LONGBOW_TEST_CASE(Global, metisControlState_WriteRead)
+{
+ char hello[] = "hello";
+ MetisControlState *state = metisControlState_Create(hello, _testWriteRead);
+
+ CCNxName *appleName = ccnxName_CreateFromCString("lci:/apple");
+ CCNxInterest *appleInterest = ccnxInterest_CreateSimple(appleName);
+ _testReadMessage = ccnxMetaMessage_CreateFromInterest(appleInterest);
+ ccnxInterest_Release(&appleInterest);
+ ccnxName_Release(&appleName);
+
+ CCNxName *pieName = ccnxName_CreateFromCString("lci:/pie");
+ CCNxInterest *pieInterest = ccnxInterest_CreateSimple(pieName);
+ CCNxMetaMessage *writeMessage = ccnxMetaMessage_CreateFromInterest(pieInterest);;
+ ccnxInterest_Release(&pieInterest);
+ ccnxName_Release(&pieName);
+
+ CCNxMetaMessage *test = metisControlState_WriteRead(state, writeMessage);
+
+ assertTrue(_testWriteMessage == writeMessage, "write message incorrect, expected %p got %p", (void *) writeMessage, (void *) _testWriteMessage);
+ assertTrue(_testReadMessage == test, "read message incorrect, expected %p got %p", (void *) _testReadMessage, (void *) test);
+
+ ccnxMetaMessage_Release(&test);
+ ccnxMetaMessage_Release(&writeMessage);
+
+ ccnxMetaMessage_Release(&_testReadMessage);
+ ccnxMetaMessage_Release(&_testWriteMessage);
+
+ metisControlState_Destroy(&state);
+}
+
+LONGBOW_TEST_CASE(Global, _metisControlState_ParseStringIntoTokens)
+{
+ const char *string = "the quick brown fox";
+
+ const char *argv[] = { "the", "quick", "brown", "fox" };
+ PARCList *truth = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+ parcList_AddAll(truth, 4, (void **) &argv[0]);
+
+ PARCList *test = _metisControlState_ParseStringIntoTokens(string);
+
+ assertTrue(parcList_Size(test) == parcList_Size(truth), "list wrong size, expected %zu got %zu", parcList_Size(truth), parcList_Size(test));
+
+ for (int i = 0; i < parcList_Size(truth); i++) {
+ const char *testString = parcList_GetAtIndex(test, i);
+ const char *truthString = parcList_GetAtIndex(truth, i);
+ assertTrue(strcmp(testString, truthString) == 0, "index %d not equal, expected '%s' got '%s'", i, truthString, testString);
+ }
+
+ parcList_Release(&test);
+ parcList_Release(&truth);
+}
+
+// ========================================================================
+
+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_Control);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/test_metis_SymbolicNameTable.c b/metis/ccnx/forwarder/metis/config/test/test_metis_SymbolicNameTable.c
new file mode 100644
index 00000000..811f5b0f
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/test_metis_SymbolicNameTable.c
@@ -0,0 +1,140 @@
+/*
+ * 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_SymbolicNameTable.c"
+
+#include <stdio.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metis_SymbolicNameTable)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_SymbolicNameTable)
+{
+ 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_SymbolicNameTable)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==============================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Exists_True);
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Exists_False);
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Add_Unique);
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Add_Duplicate);
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Get_Exists);
+ LONGBOW_RUN_TEST_CASE(Global, metisSymbolicNameTable_Get_Missing);
+}
+
+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, metisSymbolicNameTable_Create)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ assertNotNull(table, "Got null table");
+ assertNotNull(table->symbolicNameTable, "Table did not have an inner hash table allocated");
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+LONGBOW_TEST_CASE(Global, metisSymbolicNameTable_Exists_True)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ metisSymbolicNameTable_Add(table, "foo", 3);
+ bool exists = metisSymbolicNameTable_Exists(table, "foo");
+ assertTrue(exists, "Failed to find existing key");
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+LONGBOW_TEST_CASE(Global, metisSymbolicNameTable_Exists_False)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ bool exists = metisSymbolicNameTable_Exists(table, "foo");
+ assertFalse(exists, "Found non-existent key!");
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+LONGBOW_TEST_CASE(Global, metisSymbolicNameTable_Add_Unique)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ bool success = metisSymbolicNameTable_Add(table, "foo", 3);
+ assertTrue(success, "Failed to add a unique key");
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+LONGBOW_TEST_CASE(Global, metisSymbolicNameTable_Add_Duplicate)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ metisSymbolicNameTable_Add(table, "foo", 3);
+ bool failure = metisSymbolicNameTable_Add(table, "foo", 4);
+ assertFalse(failure, "Should have failed to add a duplicate key");
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+LONGBOW_TEST_CASE(Global, metisSymbolicNameTable_Get_Exists)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ metisSymbolicNameTable_Add(table, "foo", 3);
+ unsigned value = metisSymbolicNameTable_Get(table, "foo");
+ assertTrue(value == 3, "Wrong value, expected %u got %u", 3, value);
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+LONGBOW_TEST_CASE(Global, metisSymbolicNameTable_Get_Missing)
+{
+ MetisSymbolicNameTable *table = metisSymbolicNameTable_Create();
+ unsigned value = metisSymbolicNameTable_Get(table, "foo");
+ assertTrue(value == UINT32_MAX, "Wrong value, expected %u got %u", UINT32_MAX, value);
+ metisSymbolicNameTable_Destroy(&table);
+}
+
+
+// ==============================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_SymbolicNameTable);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/config/test/testrig_MetisControl.c b/metis/ccnx/forwarder/metis/config/test/testrig_MetisControl.c
new file mode 100644
index 00000000..f3f18000
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/config/test/testrig_MetisControl.c
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+/**
+ * Common operations for the metisControl tests. This C module
+ * is intended to be #include'd in to each test.
+ *
+ */
+
+#include <LongBow/unit-test.h>
+
+#include "../metis_ControlState.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <ccnx/forwarder/metis/config/metis_CommandParser.h>
+#include <ccnx/api/control/controlPlaneInterface.h>
+
+typedef struct test_data {
+ MetisControlState *state;
+ unsigned writeread_count;
+
+ // If the user specifies this, it will be used as the reply to all test_WriteRead calls
+ CCNxControl * (*customWriteReadReply)(void *userdata, CCNxMetaMessage * messageToWrite);
+} TestData;
+
+/**
+ * As part of the testrig, we simply create a CPIAck of the request message.
+ * We also increment the call count in TestData.
+ *
+ * If the user specified a customWriteReadReply function, we will call that to get
+ * the specific response to send.
+ */
+static CCNxMetaMessage *
+test_WriteRead(void *userdata, CCNxMetaMessage *messageToWrite)
+{
+ TestData *data = (TestData *) userdata;
+ data->writeread_count++;
+
+ assertTrue(ccnxMetaMessage_IsControl(messageToWrite), "messageToWrite is not a control message");
+
+ CCNxControl *response;
+ CCNxMetaMessage *result;
+
+ if (data->customWriteReadReply == NULL) {
+ CCNxControl *request = ccnxMetaMessage_GetControl(messageToWrite);
+ PARCJSON *json = ccnxControl_GetJson(request);
+ PARCJSON *jsonAck = cpiAcks_CreateAck(json);
+
+ response = ccnxControl_CreateCPIRequest(jsonAck);
+ result = ccnxMetaMessage_CreateFromControl(response);
+
+ parcJSON_Release(&jsonAck);
+ ccnxControl_Release(&response);
+ } else {
+ response = data->customWriteReadReply(userdata, messageToWrite);
+ assertTrue(ccnxMetaMessage_IsControl(response), "response is not a control message");
+ result = ccnxMetaMessage_CreateFromControl(response);
+ ccnxControl_Release(&response);
+ }
+
+ return result;
+}
+
+static void
+testrigMetisControl_commonSetup(const LongBowTestCase *testCase)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+ assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData));
+ memset(data, 0, sizeof(TestData));
+
+ data->state = metisControlState_Create(data, test_WriteRead);
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+testrigMetisControl_CommonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ metisControlState_Destroy(&data->state);
+ parcMemory_Deallocate((void **) &data);
+}
+
+/**
+ * Verify that a Command Create operated correctly
+ *
+ * We verify the basic properties of what a Create returns. Will assert if a failure.
+ *
+ * @param [in] testCase The LongBow test case (used for the clipboard)
+ * @param [in] create The command create function pointer to test
+ * @param [in] title The descriptive title to display in case of error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void
+testCommandCreate(const LongBowTestCase *testCase, MetisCommandOps * (*create)(MetisControlState * state), const char *title)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = create(data->state);
+ assertNotNull(ops, "%s: Got null ops", title);
+ assertNotNull(ops->execute, "%s: Ops execute must not be null", title);
+ assertNotNull(ops->command, "%s: Ops command must not be null", title);
+ assertTrue(ops->closure == data->state, "%s: ops closure should be data->state, got wrong pointer", title);
+
+ metisCommandOps_Destroy(&ops);
+ assertNull(ops, "Ops not nulled by Destroy");
+}
+
+/**
+ * Test a Help command's execution.
+ *
+ * A Help execution will display text (which we don't test). We make sure there
+ * is no memory leak and that it returns successfully. We will call the passed create method
+ * to create the Help command then execute its execute.
+ *
+ * @param [in] testCase The LongBow test case (used for the clipboard)
+ * @param [in] create The command create function pointer to test
+ * @param [in] title The descriptive title to display in case of error
+ * @param [in] expected A MetisCommandReturn to use as the expected result
+ *
+ * Example:
+ * @code
+ * {
+ * // expectes MetisCommandReturn_Success
+ * testHelpExecute(testCase, metisControl_Add_Create, __func__, MetisCommandReturn_Success);
+ *
+ * // expectes MetisCommandReturn_Exit
+ * testHelpExecute(testCase, metisControl_Quit_Create, __func__, MetisCommandReturn_Exit);
+ * }
+ * @endcode
+ */
+void
+testHelpExecute(const LongBowTestCase *testCase, MetisCommandOps * (*create)(MetisControlState * state), const char *title, MetisCommandReturn expected)
+{
+ uint32_t beforeMemory = parcMemory_Outstanding();
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = create(data->state);
+ MetisCommandReturn result = ops->execute(NULL, ops, NULL);
+ assertTrue(result == expected, "Wrong return, got %d expected %d", result, expected);
+ metisCommandOps_Destroy(&ops);
+ uint32_t afterMemory = parcMemory_Outstanding();
+
+ assertTrue(beforeMemory == afterMemory, "Memory leak by %d\n", (int) (afterMemory - beforeMemory));
+}
+
+/**
+ * Verify that a list of commands is added by the Init function
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] testCase The LongBow test case (used for the clipboard)
+ * @param [in] create We will create one of these and call it's init() function
+ * @param [in] title The descriptive title to display in case of error
+ * @param [in] commandList Null terminated list of commands
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void
+testInit(const LongBowTestCase *testCase, MetisCommandOps * (*create)(MetisControlState * state), const char *title, const char **commandList)
+{
+ // this will register 8 commands, so check they exist
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ MetisCommandOps *ops = create(data->state);
+ assertNotNull(ops, "%s got null ops from the create function", title);
+ assertNotNull(ops->init, "%s got null ops->init from the create function", title);
+
+ ops->init(data->state->parser, ops);
+
+ for (int i = 0; commandList[i] != NULL; i++) {
+ bool success = metisCommandParser_ContainsCommand(data->state->parser, commandList[i]);
+ assertTrue(success, "%s: Missing: %s", title, commandList[i]);
+ }
+
+ metisCommandOps_Destroy(&ops);
+}