aboutsummaryrefslogtreecommitdiffstats
path: root/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c
diff options
context:
space:
mode:
Diffstat (limited to 'libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c')
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c1350
1 files changed, 1350 insertions, 0 deletions
diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c
new file mode 100644
index 00000000..6fe9e3d2
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c
@@ -0,0 +1,1350 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ * This test will setup a server socket so the Metis connector can connect to it. We can
+ * then see the packets the connector thinks it is sending to Metis.
+ */
+
+#define DEBUG 1
+#include "../connector_Forwarder_Metis.c"
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/security/parc_Pkcs12KeyStore.h>
+#include <parc/security/parc_Security.h>
+
+#include <ccnx/api/control/cpi_ControlFacade.h>
+#include <ccnx/api/control/controlPlaneInterface.h>
+#include <ccnx/api/control/cpi_Forwarding.h>
+
+#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c>
+#include <ccnx/transport/transport_rta/core/rta_Framework_private.h>
+#include <ccnx/transport/transport_rta/config/config_All.h>
+
+#include <ccnx/common/codec/ccnxCodec_TlvPacket.h>
+#include <ccnx/transport/test_tools/traffic_tools.h>
+
+#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h>
+#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h>
+#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h>
+#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h>
+#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h>
+#include <ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h>
+
+#include <ccnx/common/ccnx_WireFormatMessage.h>
+
+// inet_pton
+#include <arpa/inet.h>
+
+#include <LongBow/unit-test.h>
+
+static char keystorename[1024];
+static const char keystorepass[] = "2398472983479234";
+
+#ifndef INPORT_ANY
+#define INPORT_ANY 0
+#endif
+
+typedef struct test_data {
+ PARCRingBuffer1x1 *commandRingBuffer;
+ PARCNotifier *commandNotifier;
+
+ // we will bind to a random port, this is what we end up binding to
+ // Its in host byte order
+ uint16_t metis_port;
+
+ // server_socket is a socket we listen to like the Metis forwarder, so
+ // we can see all the traffic that comes out the bottom of the connector.
+ int server_socket;
+
+ // when we accept a client on the server socket, this is his socket
+ int client_socket;
+
+ RtaFramework *framework;
+ CCNxTransportConfig *params;
+
+ char keystoreName[1024];
+ char keystorePassword[1024];
+} TestData;
+
+/*
+ * @function setup_server
+ * @abstract Bind to 127.0.0.1 on a random port, returns the socket and port
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param portOutput is the port bound to in host byte order
+ * @return <#return#>
+ */
+static int
+_setup_server(uint16_t *portOutput)
+{
+ struct sockaddr_in address;
+
+ /* listen on 127.0.0.1 random port */
+ address.sin_family = PF_INET;
+ address.sin_port = INPORT_ANY;
+ inet_pton(AF_INET, "127.0.0.1", &(address.sin_addr));
+
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(fd < 0, "error on bind: (%d) %s", errno, strerror(errno));
+
+ // Set non-blocking flag
+ int flags = fcntl(fd, F_GETFL, NULL);
+ assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno);
+ int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno);
+
+ failure = bind(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_in));
+ assertFalse(failure, "error on bind: (%d) %s", errno, strerror(errno));
+
+ failure = listen(fd, 16);
+ assertFalse(failure, "error on listen: (%d) %s", errno, strerror(errno));
+
+ socklen_t x = sizeof(address);
+ failure = getsockname(fd, (struct sockaddr *) &address, &x);
+ assertFalse(failure, "error on getsockname: (%d) %s", errno, strerror(errno));
+
+ *portOutput = htons(address.sin_port);
+
+ printf("test server setup on port %d\n", *portOutput);
+ return fd;
+}
+
+static int
+_accept_client(int server_socket)
+{
+ socklen_t addrlen;
+ struct sockaddr_in address;
+ int client_socket;
+
+ addrlen = sizeof(struct sockaddr_in);
+ client_socket = accept(server_socket, (struct sockaddr *) &address, &addrlen);
+ assertFalse(client_socket < 0, "accept error: %s", strerror(errno));
+
+ printf("%s accepted client on socket %d\n", __func__, client_socket);
+ return client_socket;
+}
+
+static RtaConnection *
+_openConnection(TestData *data, int stack_id, int fds[2])
+{
+ RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(stack_id, fds[0], fds[1],
+ ccnxConnectionConfig_GetJson(ccnxTransportConfig_GetConnectionConfig(data->params)));
+ _rtaFramework_ExecuteOpenConnection(data->framework, openConnection);
+ rtaCommandOpenConnection_Release(&openConnection);
+
+ return rtaConnectionTable_GetByApiFd(data->framework->connectionTable, fds[0]);
+}
+
+static void
+_createStack(TestData *data, int stack_id)
+{
+ RtaCommandCreateProtocolStack *createStack =
+ rtaCommandCreateProtocolStack_Create(stack_id, ccnxTransportConfig_GetStackConfig(data->params));
+ _rtaFramework_ExecuteCreateStack(data->framework, createStack);
+ rtaCommandCreateProtocolStack_Release(&createStack);
+}
+
+static CCNxTransportConfig *
+_createParams(int port, const char *keystore_name, const char *keystore_passwd)
+{
+ CCNxStackConfig *stackConfig;
+ CCNxConnectionConfig *connConfig;
+
+ assertNotNull(keystore_name, "Got null keystore name\n");
+ assertNotNull(keystore_passwd, "Got null keystore passwd\n");
+
+ stackConfig = apiConnector_ProtocolStackConfig(
+ testingUpper_ProtocolStackConfig(
+ metisForwarder_ProtocolStackConfig(
+ protocolStack_ComponentsConfigArgs(ccnxStackConfig_Create(),
+ apiConnector_GetName(),
+ testingUpper_GetName(),
+ metisForwarder_GetName(), NULL))));
+
+ connConfig = apiConnector_ConnectionConfig(
+ testingUpper_ConnectionConfig(
+ metisForwarder_ConnectionConfig(
+ tlvCodec_ConnectionConfig(
+ ccnxConnectionConfig_Create()), port)));
+
+ publicKeySigner_ConnectionConfig(connConfig, keystore_name, keystore_passwd);
+
+ CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig);
+ ccnxStackConfig_Release(&stackConfig);
+ return result;
+}
+
+static TestData *
+_commonSetup(void)
+{
+ parcSecurity_Init();
+
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+ assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData));
+ memset(data, 0, sizeof(TestData));
+
+ data->server_socket = _setup_server(&data->metis_port);
+
+ // printf("%s listening on port %u\n", __func__, data->metis_port);
+
+ sprintf(data->keystoreName, "%s", keystorename);
+ sprintf(data->keystorePassword, keystorepass);
+
+ data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL);
+ data->commandNotifier = parcNotifier_Create();
+ data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier);
+
+ data->params = _createParams(data->metis_port, data->keystoreName, keystorepass);
+ // we will always create stack #1 as the default stack
+ _createStack(data, 1);
+ return data;
+}
+
+static void
+_commonTeardown(TestData *data)
+{
+ if (data != NULL) {
+ if (data->server_socket > 0) {
+ close(data->server_socket);
+ }
+
+ if (data->client_socket > 0) {
+ close(data->client_socket);
+ }
+
+ ccnxTransportConfig_Destroy(&data->params);
+ rtaFramework_Teardown(data->framework);
+
+ parcRingBuffer1x1_Release(&data->commandRingBuffer);
+ parcNotifier_Release(&data->commandNotifier);
+ rtaFramework_Destroy(&data->framework);
+ parcMemory_Deallocate((void **) &data);
+ }
+ parcSecurity_Fini();
+}
+
+// ======================================================
+// helper functions
+
+/**
+ * Wait for a READ event on the specifid socket. Has a 1 second timeout.
+ *
+ * @return true if READ event
+ * @return false otherwise
+ */
+static bool
+_waitForSelect(int fd)
+{
+ fd_set readset;
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ int result = select(fd + 1, &readset, NULL, NULL, &(struct timeval) { 1, 0 });
+ assertFalse(result < 0, "Error on select: (%d) %s", errno, strerror(errno));
+ assertFalse(result == 0, "Timeout waiting for connection attempt");
+ assertTrue(FD_ISSET(fd, &readset), "server_socket was not set by select");
+
+ return true;
+}
+
+
+
+static size_t
+_sendPacketToConnectorV1(int fd, size_t payloadLength)
+{
+ // Setup the header
+ uint8_t headerLength = 13;
+ uint16_t packetLength = payloadLength + headerLength;
+ uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest;
+
+ CCNxCodecSchemaV1FixedHeader hdr = { .version = 1, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength };
+
+ // put header in packet and write the packet
+ uint8_t packet[1024];
+ memcpy(packet, &hdr, sizeof(hdr));
+
+ // write out exactly the number of bytes we need
+ size_t writeSize = packetLength;
+
+ ssize_t nwritten = write(fd, packet, writeSize);
+ assertTrue(nwritten == writeSize, "Wrong write size, expected %zu got %zd", writeSize, nwritten);
+ return writeSize;
+}
+
+static RtaConnection *
+setupConnectionAndClientSocket(TestData *data, int *apiSocketOuptut, int *clientSocketOutput)
+{
+ // Open a listener and accept the forwarders connection
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+ RtaConnection *conn = _openConnection(data, 1, fds);
+ assertNotNull(conn, "Got null connection opening on stack 1");
+
+ rtaFramework_NonThreadedStepCount(data->framework, 2);
+
+ // we should now see a connection request
+ _waitForSelect(data->server_socket);
+
+ // accept the client and set a 1 second read timeout on the socket
+ int client_fd = _accept_client(data->server_socket);
+ struct timeval readTimeout = { 1, 0 };
+ setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &readTimeout, sizeof(readTimeout));
+
+ *apiSocketOuptut = fds[0];
+ *clientSocketOutput = client_fd;
+ return conn;
+}
+
+// throw away the first control message
+static void
+_throwAwayControlMessage(PARCEventQueue *out)
+{
+ TransportMessage *control_tm = rtaComponent_GetMessage(out);
+ assertNotNull(control_tm, "Did not receive a transport message out of the top of the connector");
+ assertTrue(transportMessage_IsControl(control_tm),
+ "transport message is not a control message")
+ {
+ ccnxTlvDictionary_Display(transportMessage_GetDictionary(control_tm), 0);
+ }
+ transportMessage_Destroy(&control_tm);
+}
+
+static void
+_testReadPacketV1(size_t extraBytes)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+
+ // this replaces "_openSocket"
+ fwd_state->fd = fds[STACK];
+
+ _setupSocket(fwd_state);
+
+ // Setup the header
+ uint16_t packetLength = 24;
+ uint8_t headerLength = 13;
+ uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest;
+
+ CCNxCodecSchemaV1FixedHeader hdr = { .version = 1, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength };
+
+ // put header in packet and write the packet
+ uint8_t packet[1024];
+ memcpy(packet, &hdr, sizeof(hdr));
+
+ // write out exactly the number of bytes we need
+ size_t firstWrite = packetLength;
+
+ ssize_t nwritten = write(fds[REMOTE], packet, firstWrite + extraBytes);
+ assertTrue(nwritten == firstWrite + extraBytes, "Wrong write size, expected %zu got %zd",
+ firstWrite + extraBytes, nwritten);
+
+ ReadReturnCode readCode = _readPacket(fwd_state);
+
+ assertTrue(readCode == ReadReturnCode_Finished, "readCode should be %d got %d", ReadReturnCode_Finished, readCode);
+
+ // should indicate there's nothing left to read of the header
+ assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength);
+
+ // we should be at position "firstWrite" in the packet buffer
+ assertNotNull(fwd_state->nextMessage.packet, "Packet buffer is null");
+ assertTrue(parcBuffer_Position(fwd_state->nextMessage.packet) == firstWrite,
+ "Wrong position, expected %zu got %zu", firstWrite, parcBuffer_Position(fwd_state->nextMessage.packet));
+
+ // cleanup
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+ close(fds[REMOTE]);
+}
+
+
+
+static void
+_testReadFromMetisFromArray(TestData *data, size_t length, uint8_t buffer[length])
+{
+ // create our connection. This will become part of the RTA framework, so will be
+ // cleaned up in the teardown
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+
+ ssize_t nwritten = write(client_fd, buffer, length);
+ assertTrue(nwritten == length, "Wrong write size, expected %zu got %zd", length, nwritten);
+
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);;
+
+ _readFromMetis(fwd_state, conn);
+
+ // now crank the handle to pop those messages up the stack
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN);
+ _throwAwayControlMessage(out);
+
+ // verify the wire format is what we wrote
+ TransportMessage *test_tm = rtaComponent_GetMessage(out);
+ assertNotNull(test_tm, "Did not receive a transport message out of the top of the connector");
+
+ CCNxTlvDictionary *testDictionary = transportMessage_GetDictionary(test_tm);
+ PARCBuffer *writeFormat = ccnxWireFormatMessage_GetWireFormatBuffer(testDictionary);
+ assertNotNull(writeFormat,
+ "transport message does not have a wire format");
+
+ PARCBuffer *truth = parcBuffer_Wrap(buffer, length, 0, length);
+ assertTrue(parcBuffer_Equals(truth, writeFormat), "Wire format does not match expected")
+ {
+ printf("Expected:\n");
+ parcBuffer_Display(truth, 3);
+ printf("Received:\n");
+ parcBuffer_Display(writeFormat, 3);
+ }
+
+ parcBuffer_Release(&truth);
+ transportMessage_Destroy(&test_tm);
+}
+
+
+// ======================================================
+
+LONGBOW_TEST_RUNNER(connector_Forwarder_Metis)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+
+ LONGBOW_RUN_TEST_FIXTURE(UpDirectionV1);
+ LONGBOW_RUN_TEST_FIXTURE(DownDirectionV1);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(connector_Forwarder_Metis)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ snprintf(keystorename, 1024, "/tmp/keystore_%d.p12", getpid());
+
+ // init + fini here so there's no memory imbalance
+ parcSecurity_Init();
+ parcPkcs12KeyStore_CreateFile(keystorename, keystorepass, "ccnxuser", 1024, 365);
+ parcSecurity_Fini();
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(connector_Forwarder_Metis)
+{
+ unlink(keystorename);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ======================================================
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, connector_Fwd_Metis_Init_Release);
+ LONGBOW_RUN_TEST_CASE(Local, connector_Fwd_Metis_Opener_GoodPort);
+ LONGBOW_RUN_TEST_CASE(Local, _fwdMetisState_Release);
+ LONGBOW_RUN_TEST_CASE(Local, _readInEnvironmentConnectionSpecification);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ longBowTestCase_SetClipBoardData(testCase, _commonSetup());
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ _commonTeardown(longBowTestCase_GetClipBoardData(testCase));
+
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding());
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ====================================================================
+
+LONGBOW_TEST_CASE(Local, connector_Fwd_Metis_Init_Release)
+{
+ // nothing to do, just checking that memory is in balance in teardown
+}
+
+/**
+ * Call the opener with the right port. We should see a connection attempt on
+ * the server socket and be able to accept it.
+ */
+LONGBOW_TEST_CASE(Local, connector_Fwd_Metis_Opener_GoodPort)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+ RtaConnection *conn = _openConnection(data, 1, fds);
+ assertNotNull(conn, "Got null connection opening on stack 1");
+
+ rtaFramework_NonThreadedStepCount(data->framework, 2);
+
+ // we should now see a connection request
+ _waitForSelect(data->server_socket);
+
+ close(fds[1]);
+}
+
+/**
+ * Make sure everything is released and file descriptor is closed
+ */
+LONGBOW_TEST_CASE(Local, _fwdMetisState_Release)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+
+ // this replaces "_openSocket"
+ fwd_state->fd = fds[STACK];
+
+ _setupSocket(fwd_state);
+
+
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+
+ // ensure that fds[STACK] is closed by _fwdMetisState_Release
+ uint8_t buffer[16];
+ ssize_t nread = recv(fds[STACK], buffer, 16, 0);
+ assertTrue(nread == -1 && errno == EBADF,
+ "read from closed socket %d should be EBADF, got return %zd and errno (%d) %s",
+ fds[STACK], nread, errno, strerror(errno));
+
+ close(fds[REMOTE]);
+}
+
+LONGBOW_TEST_CASE(Local, _readInEnvironmentConnectionSpecification)
+{
+ char *oldEnv = getenv(FORWARDER_CONNECTION_ENV);
+ setenv(FORWARDER_CONNECTION_ENV, "tcp://127.0.0.1:9999", 1);
+ struct sockaddr_in addr_in;
+ _readInEnvironmentConnectionSpecification(&addr_in);
+ assertTrue(addr_in.sin_port == htons(9999), "Port specification incorrectly parsed");
+ assertTrue(addr_in.sin_addr.s_addr == inet_addr("127.0.0.1"), "Address specification incorrectly parsed");;
+ if (oldEnv) {
+ setenv(FORWARDER_CONNECTION_ENV, oldEnv, 1);
+ } else {
+ unsetenv(FORWARDER_CONNECTION_ENV);
+ }
+}
+
+
+
+// ====================================================================
+
+
+LONGBOW_TEST_FIXTURE(UpDirectionV1)
+{
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_ExactFit);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_TwoReads);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _setupNextPacket);
+
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacket_PartialMessage);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacket_ExactlyOneMessage);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacket_MoreThanOneMessage);
+
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_ThreeMessages);
+
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_InterestV1);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_ContentObjectV1);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_ControlV1);
+
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_Error);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketBody_Error);
+
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_Closed);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketBody_Closed);
+ LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_Closed);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(UpDirectionV1)
+{
+ longBowTestCase_SetClipBoardData(testCase, _commonSetup());
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(UpDirectionV1)
+{
+ _commonTeardown(longBowTestCase_GetClipBoardData(testCase));
+
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding());
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+/**
+ * Put in exactly 8 bytes.
+ * This should return NULL, but will set nextMessageLength to be the right thing.
+ * Does not drain the buffer
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_ExactFit)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+
+ // this replaces "_openSocket"
+ fwd_state->fd = fds[STACK];
+
+ _setupSocket(fwd_state);
+
+ // Setup the header
+ uint16_t packetLength = 24;
+ uint8_t headerLength = 13;
+ uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest;
+
+ CCNxCodecSchemaV1FixedHeader hdr = { .version = 1, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength };
+
+ // put header in packet and write the packet
+ uint8_t packet[1024];
+ size_t bufferReadLength = sizeof(hdr);
+ memcpy(packet, &hdr, bufferReadLength);
+
+ // write out exactly the number of bytes we need
+ ssize_t nwritten = write(fds[REMOTE], packet, sizeof(CCNxCodecSchemaV1FixedHeader));
+ assertTrue(nwritten == sizeof(CCNxCodecSchemaV1FixedHeader), "Wrong write size, expected %zu got %zd",
+ sizeof(CCNxCodecSchemaV1FixedHeader), nwritten);
+
+ // test the function
+ ReadReturnCode readCode = _readPacketHeader(fwd_state);
+ assertTrue(readCode == ReadReturnCode_Finished, "readCode should be %d got %d", ReadReturnCode_Finished, readCode);
+ assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength);
+
+ // other properties are tested as part of _setupNextPacket
+
+ // cleanup
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+ close(fds[REMOTE]);
+}
+
+/*
+ * Write the fixed header in two 4 byte writes
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_TwoReads)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+
+ // this replaces "_openSocket"
+ fwd_state->fd = fds[STACK];
+
+ _setupSocket(fwd_state);
+
+ // Setup the header
+ uint16_t packetLength = 24;
+ uint8_t headerLength = 13;
+ uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest;
+
+ CCNxCodecSchemaV1FixedHeader hdr = {
+ .version = 1,
+ .packetType = packetType,
+ .packetLength = htons(packetLength),
+ .headerLength = headerLength
+ };
+
+ // put header in packet and write the packet
+ uint8_t packet[1024];
+ size_t bufferReadLength = sizeof(hdr);
+ memcpy(packet, &hdr, bufferReadLength);
+
+ // write out exactly the number of bytes we need
+ size_t firstWrite = 4;
+ size_t secondWrite = sizeof(CCNxCodecSchemaV1FixedHeader) - firstWrite;
+
+ ssize_t nwritten = write(fds[REMOTE], packet, firstWrite);
+ assertTrue(nwritten == firstWrite, "Wrong write size, expected %zu got %zd", firstWrite, nwritten);
+
+ ReadReturnCode readCode = _readPacketHeader(fwd_state);
+ assertTrue(readCode == ReadReturnCode_PartialRead, "readCode should be %d got %d", ReadReturnCode_PartialRead, readCode);
+
+ nwritten = write(fds[REMOTE], packet + firstWrite, secondWrite);
+ assertTrue(nwritten == secondWrite, "Wrong write size, expected %zu got %zd", secondWrite, nwritten);
+
+ readCode = _readPacketHeader(fwd_state);
+ assertTrue(readCode == ReadReturnCode_Finished, "readCode should be %d got %d", ReadReturnCode_Finished, readCode);
+
+ assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength);
+
+ // other properties are tested as part of _setupNextPacket
+
+ // cleanup
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+ close(fds[REMOTE]);
+}
+
+LONGBOW_TEST_CASE(UpDirectionV1, _setupNextPacket)
+{
+ uint16_t packetLength = 24;
+ uint8_t headerLength = 13;
+ uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest;
+ uint8_t version = 1;
+ CCNxCodecSchemaV1FixedHeader hdr = { .version = version, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength };
+
+ // setup fwd_state->nextMessage like we just read a header
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+ fwd_state->nextMessage.remainingReadLength = 0;
+ memcpy(&fwd_state->nextMessage.fixedHeader, &hdr, sizeof(hdr));
+
+ // this is the truth we will test against
+ size_t nextMessageLength = packetLength;
+
+ _setupNextPacket(fwd_state);
+
+ size_t allocatedLength = parcBuffer_Capacity(fwd_state->nextMessage.packet);
+ size_t position = parcBuffer_Position(fwd_state->nextMessage.packet);
+ parcBuffer_Flip(fwd_state->nextMessage.packet);
+ void *buffer = parcBuffer_Overlay(fwd_state->nextMessage.packet, 0);
+
+ assertTrue(fwd_state->nextMessage.length == nextMessageLength, "Wrong packet length, expected %zu got %zu", nextMessageLength, fwd_state->nextMessage.length);
+ assertTrue(fwd_state->nextMessage.packetType == packetType, "Wrong packetType, expected %u got %u", packetType, fwd_state->nextMessage.packetType);
+ assertTrue(fwd_state->nextMessage.version == version, "Wrong version, expected %u got %u", version, fwd_state->nextMessage.version);
+ assertTrue(allocatedLength == nextMessageLength, "Wrong packet buffer length, expected %zu got %zu", nextMessageLength, allocatedLength);
+
+ // and make sure the beginning of the buffer is the fixed header
+ assertTrue(position == sizeof(hdr), "Wrong write position, expected %zu got %zu", sizeof(hdr), position);
+ assertTrue(memcmp(buffer, &hdr, sizeof(hdr)) == 0, "Beginning of buffer not the fixed header");
+
+ // TODO: Finish me
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+}
+
+/**
+ * Write the fixed header plus part of the message body.
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacket_PartialMessage)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+
+ // this replaces "_openSocket"
+ fwd_state->fd = fds[STACK];
+
+ _setupSocket(fwd_state);
+
+ // Setup the header
+ uint16_t packetLength = 160;
+ uint8_t headerLength = 13;
+ uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest;
+ uint8_t version = 1;
+ CCNxCodecSchemaV1FixedHeader hdr = { .version = version, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength };
+
+ // put header in packet and write the packet
+ uint8_t packet[1024];
+ memcpy(packet, &hdr, sizeof(hdr));
+
+ // write out exactly the number of bytes we need
+ size_t firstWrite = 100;
+
+ ssize_t nwritten = write(fds[REMOTE], packet, firstWrite);
+ assertTrue(nwritten == firstWrite, "Wrong write size, expected %zu got %zd", firstWrite, nwritten);
+
+ ReadReturnCode readCode = _readPacket(fwd_state);
+
+ assertTrue(readCode == ReadReturnCode_PartialRead, "return value should be %d got %d", ReadReturnCode_PartialRead, readCode);
+
+ // should indicate there's nothing left to read of the header
+ assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength);
+
+ // we should be at position "firstWrite" in the packet buffer
+ assertNotNull(fwd_state->nextMessage.packet, "Packet buffer is null");
+ assertTrue(parcBuffer_Position(fwd_state->nextMessage.packet) == firstWrite,
+ "Wrong position, expected %zu got %zu", firstWrite, parcBuffer_Position(fwd_state->nextMessage.packet));
+
+ // cleanup
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+ close(fds[REMOTE]);
+}
+
+/**
+ * Write exactly one message
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacket_ExactlyOneMessage)
+{
+ _testReadPacketV1(0);
+}
+
+/**
+ * Write more than one message.
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacket_MoreThanOneMessage)
+{
+ _testReadPacketV1(100);
+}
+
+/**
+ * Make 3 messages pending on the read socket and make sure _readFromMetis delivers all
+ * 3 up the stack. _readFromMetis requires an RtaConnection, so we need a mock framework.
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_ThreeMessages)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // create our connection. This will become part of the RTA framework, so will be
+ // cleaned up in the teardown
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+
+ // Write three wire format packets up the bottom of the connector
+ const int loopCount = 3;
+ size_t writeSizes[loopCount];
+
+ for (int i = 0; i < loopCount; i++) {
+ writeSizes[i] = _sendPacketToConnectorV1(client_fd, (i + 1) * 100);
+ }
+
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);;
+
+ _readFromMetis(fwd_state, conn);
+
+ // now crank the handle to pop those messages up the stack
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ // now read the message out of the test component
+ PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN);
+
+ // throw away the first control message
+ _throwAwayControlMessage(out);
+
+ // Now read the actual messages we want to test
+ for (int i = 0; i < loopCount; i++) {
+ TransportMessage *test_tm = rtaComponent_GetMessage(out);
+ assertNotNull(test_tm, "Did not receive a transport message %d out of %d out of the top of the connector", i + 1, loopCount);
+
+ assertTrue(transportMessage_IsInterest(test_tm),
+ "second transport message is not an interest")
+ {
+ ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0);
+ }
+
+ // Make sure the transport message has the right properties
+ CCNxTlvDictionary *testDictionary = transportMessage_GetDictionary(test_tm);
+ PARCBuffer *writeFormat = ccnxWireFormatMessage_GetWireFormatBuffer(testDictionary);
+ assertNotNull(writeFormat,
+ "transport message does not have a wire format");
+
+ assertTrue(parcBuffer_Remaining(writeFormat) == writeSizes[i],
+ "Raw format message wrong length, expected %zu got %zu",
+ writeSizes[i],
+ parcBuffer_Remaining(writeFormat));
+
+ // cleanup
+ transportMessage_Destroy(&test_tm);
+ }
+
+ // no extra cleanup, done in teardown
+}
+
+LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_InterestV1)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _testReadFromMetisFromArray(data, sizeof(v1_interest_nameA), v1_interest_nameA);
+}
+
+LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_ContentObjectV1)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _testReadFromMetisFromArray(data, sizeof(v1_content_nameA_crc32c), v1_content_nameA_crc32c);
+}
+
+LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_ControlV1)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ _testReadFromMetisFromArray(data, sizeof(v1_cpi_add_route_crc32c), v1_cpi_add_route_crc32c);
+}
+
+/*
+ * read from a closed socket
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_Closed)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+ fwd_state->fd = fds[STACK];
+ _setupSocket(fwd_state);
+
+ // close remote side then try to write to it
+ close(fds[REMOTE]);
+
+ ReadReturnCode readCode = _readPacketHeader(fwd_state);
+
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+
+ assertTrue(readCode == ReadReturnCode_Closed, "Wrong return code, expected %d got %d", ReadReturnCode_Closed, readCode);
+}
+
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacketBody_Closed)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+ fwd_state->fd = fds[STACK];
+ _setupSocket(fwd_state);
+
+ ssize_t nwritten = write(fds[REMOTE], v1_interest_nameA, 8);
+ assertTrue(nwritten == 8, "Wrong write size, expected 8 got %zd", nwritten);
+
+ // read the header to setup the read of the body
+ ReadReturnCode readCode;
+
+ readCode = _readPacketHeader(fwd_state);
+ assertTrue(readCode == ReadReturnCode_Finished, "Did not read entire header");
+
+ // close remote side then try to write to it
+ close(fds[REMOTE]);
+
+ // now try 2nd read
+ readCode = _readPacketBody(fwd_state);
+
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+
+ assertTrue(readCode == ReadReturnCode_Closed, "Wrong return code, expected %d got %d", ReadReturnCode_Closed, readCode);
+}
+
+/*
+ * Set the socket to -1 to cause and error
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_Error)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+ fwd_state->fd = fds[STACK];
+ _setupSocket(fwd_state);
+
+ fwd_state->fd = -1;
+
+ // close remote side then try to write to it
+
+ ReadReturnCode readCode = _readPacketHeader(fwd_state);
+
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+ close(fds[STACK]);
+ close(fds[REMOTE]);
+
+ assertTrue(readCode == ReadReturnCode_Error, "Wrong return code, expected %d got %d", ReadReturnCode_Error, readCode);
+}
+
+/*
+ * Set the socket to -1 to cause and error
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readPacketBody_Error)
+{
+ const int REMOTE = 0;
+ const int STACK = 1;
+ int fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+ fwd_state->fd = fds[STACK];
+ _setupSocket(fwd_state);
+
+ ssize_t nwritten = write(fds[REMOTE], v1_interest_nameA, 8);
+ assertTrue(nwritten == 8, "Wrong write size, expected 8 got %zd", nwritten);
+
+ // read the header to setup the read of the body
+ ReadReturnCode readCode;
+
+ readCode = _readPacketHeader(fwd_state);
+ assertTrue(readCode == ReadReturnCode_Finished, "Did not read entire header");
+
+ // invalidate to cause an error
+ fwd_state->fd = -1;
+
+ // now try 2nd read
+ readCode = _readPacketBody(fwd_state);
+
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+ close(fds[STACK]);
+ close(fds[REMOTE]);
+
+ assertTrue(readCode == ReadReturnCode_Error, "Wrong return code, expected %d got %d", ReadReturnCode_Error, readCode);
+}
+
+/*
+ * read from a closed socket.
+ * This should generate a Notify message that the connection is closed
+ */
+LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_Closed)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // create our connection. This will become part of the RTA framework, so will be
+ // cleaned up in the teardown
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);;
+
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ close(client_fd);
+
+ _readFromMetis(fwd_state, conn);
+
+ // now crank the handle to pop those messages up the stack
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ // now read the message out of the test component
+ PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN);
+
+ // throw away the first control message
+ _throwAwayControlMessage(out);
+
+ TransportMessage *test_tm = rtaComponent_GetMessage(out);
+ assertNotNull(test_tm, "Did not receive a transport message out of the top of the connector");
+
+ assertTrue(transportMessage_IsControl(test_tm),
+ "second transport message is not a control")
+ {
+ ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0);
+ }
+
+ // Make sure the transport message has the right properties
+ CCNxTlvDictionary *testDictionary = transportMessage_GetDictionary(test_tm);
+ assertTrue(ccnxControlFacade_IsNotification(testDictionary), "Control message is not Notification")
+ {
+ ccnxTlvDictionary_Display(testDictionary, 3);
+ }
+
+ PARCJSON *json = ccnxControlFacade_GetJson(testDictionary);
+ NotifyStatus *notify = notifyStatus_ParseJSON(json);
+ assertTrue(notifyStatus_GetStatusCode(notify) == notifyStatusCode_CONNECTION_CLOSED,
+ "Wrong code, expected %d got %d",
+ notifyStatusCode_CONNECTION_CLOSED,
+ notifyStatus_GetStatusCode(notify));
+ notifyStatus_Release(&notify);
+
+ // verify other properties
+ assertFalse(fwd_state->isConnected, "Forwarder state should show connection closed");
+
+ // cleanup
+ transportMessage_Destroy(&test_tm);
+
+ // no extra cleanup, done in teardown
+}
+
+LONGBOW_TEST_FIXTURE(DownDirectionV1)
+{
+ LONGBOW_RUN_TEST_CASE(DownDirectionV1, _queueMessageToMetis);
+ LONGBOW_RUN_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis);
+ LONGBOW_RUN_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_TwoWrites);
+ LONGBOW_RUN_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_Closed);
+
+ LONGBOW_RUN_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_Interst);
+ LONGBOW_RUN_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_CPIRequest);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(DownDirectionV1)
+{
+ longBowTestCase_SetClipBoardData(testCase, _commonSetup());
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(DownDirectionV1)
+{
+ _commonTeardown(longBowTestCase_GetClipBoardData(testCase));
+
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding());
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+/*
+ * _queueMessageToMetis postconditions:
+ * - increases the reference count to the wireFormat
+ * - adds the reference to fwd_output buffer
+ * - increments the debugging counter fwd_metis_references_queued
+ */
+LONGBOW_TEST_CASE(DownDirectionV1, _queueMessageToMetis)
+{
+ PARCEventScheduler *scheduler = parcEventScheduler_Create();
+ FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler);
+ PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA));
+ size_t expectedRefCount = parcObject_GetReferenceCount(wireFormat);
+
+ _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue);
+
+ assertTrue(parcObject_GetReferenceCount(wireFormat) == expectedRefCount,
+ "Did not get right ref count for wire format, expected %zu got %" PRIu64, expectedRefCount, parcObject_GetReferenceCount(wireFormat));
+ assertTrue(parcEventBuffer_GetLength(fwd_state->metisOutputQueue) == parcBuffer_Remaining(wireFormat),
+ "Wrong output buffer length, expected %zu got %zu", parcBuffer_Remaining(wireFormat), parcEventBuffer_GetLength(fwd_state->metisOutputQueue));
+
+ parcBuffer_Release(&wireFormat);
+ parcEventBuffer_Destroy(&fwd_state->metisOutputQueue);
+ _fwdMetisState_Release(&fwd_state);
+ parcEventScheduler_Destroy(&scheduler);
+}
+
+/*
+ * Dequeue a small message to metis, should all be written out.
+ */
+LONGBOW_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);;
+
+ // Put data in the output queue
+ PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA));
+ _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue);
+
+ // write it out
+ _dequeueMessagesToMetis(fwd_state);
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ // we should now be able to read it
+ bool readReady = _waitForSelect(client_fd);
+ assertTrue(readReady, "client socket %d not ready for read", client_fd);
+
+ uint8_t testArray[sizeof(v1_interest_nameA) + 1];
+ ssize_t nrecv = recv(client_fd, testArray, sizeof(testArray), 0);
+
+ assertTrue(nrecv == sizeof(v1_interest_nameA), "Wrong read length, expected %zu got %zd", sizeof(v1_interest_nameA), nrecv);
+ assertTrue(memcmp(testArray, v1_interest_nameA, sizeof(v1_interest_nameA)) == 0, "Read memory does not compare");
+ assertTrue(parcEventBuffer_GetLength(fwd_state->metisOutputQueue) == 0, "Metis output buffer not zero length, got %zu", parcEventBuffer_GetLength(fwd_state->metisOutputQueue));
+ parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue));
+ parcBuffer_Release(&wireFormat);
+}
+
+/*
+ * Set the forwarder's send buffer small so it will take two writes to send the packet.
+ * This will test that when _dequeueMessagesToMetis cannot write the whole thing it will enable the
+ * write event and that metis will then trigger a second write when there's buffer space.
+ */
+LONGBOW_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_TwoWrites)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);;
+
+ // set the send buffer
+ {
+ // make it slightly bigger than 1/2
+ const int sendBufferSize = sizeof(v1_interest_nameA) / 2 + 1;
+ int res = setsockopt(fwd_state->fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, sizeof(int));
+ if (res < 0) {
+ if (DEBUG_OUTPUT) {
+ printf("%9c %s failed to set SO_SNDBUF to %d: (%d) %s\n",
+ ' ', __func__, sendBufferSize, errno, strerror(errno));
+ }
+ // This is a non-fatal error
+ }
+ }
+
+ // Put data in the output queue
+ PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA));
+ _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue);
+
+ // write it out
+ _dequeueMessagesToMetis(fwd_state);
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ // we should now be able to read it
+ bool readReady = _waitForSelect(client_fd);
+ assertTrue(readReady, "client socket %d not ready for read", client_fd);
+
+ uint8_t testArray[sizeof(v1_interest_nameA) + 1];
+ ssize_t nrecv = recv(client_fd, testArray, sizeof(testArray), 0);
+
+ assertTrue(nrecv == sizeof(v1_interest_nameA), "Wrong read length, expected %zu got %zd", sizeof(v1_interest_nameA), nrecv);
+ assertTrue(memcmp(testArray, v1_interest_nameA, sizeof(v1_interest_nameA)) == 0, "Read memory does not compare");
+ assertTrue(parcEventBuffer_GetLength(fwd_state->metisOutputQueue) == 0, "Metis output buffer not zero length, got %zu", parcEventBuffer_GetLength(fwd_state->metisOutputQueue));
+ parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue));
+ parcBuffer_Release(&wireFormat);
+}
+
+/*
+ * Dequeue a message to a closed socket
+ */
+LONGBOW_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_Closed)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);;
+ PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA));
+ _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue);
+
+ // close remote side then try to write to it
+ close(client_fd);
+
+ _dequeueMessagesToMetis(fwd_state);
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue));
+ parcBuffer_Release(&wireFormat);
+}
+
+/**
+ * Sends an Interest down the stack. We need to create an Interest and encode its TLV wire format,
+ * then send it down the stack and make sure we receive it on a client socket. We don't actually
+ * run Metis in this test.
+ */
+LONGBOW_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_Interst)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // create our connection. This will become part of the RTA framework, so will be
+ // cleaned up in the teardown
+ int api_fd;
+ int client_fd;
+ RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+ FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);
+
+ // Create the interest with wire format and send it down the stack
+ TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryInterest(conn, CCNxTlvDictionary_SchemaVersion_V1);
+ CCNxCodecNetworkBufferIoVec *vec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(transportMessage_GetDictionary(tm), NULL);
+
+ ccnxWireFormatMessage_PutIoVec(transportMessage_GetDictionary(tm), vec);
+ ccnxCodecNetworkBufferIoVec_Release(&vec);
+
+ // send it down the stack
+ PARCEventQueue *in = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN);
+ rtaComponent_PutMessage(in, tm);
+ rtaFramework_NonThreadedStepCount(data->framework, 5);
+
+ bool readReady = _waitForSelect(client_fd);
+ assertTrue(readReady, "select did not indicate read ready");
+
+ // now read it from out listener. It has a read timeout so if we dont get it in a reasonable amount
+ // of time, read will return an error about the timeout
+
+ const size_t maxPacketLength = 1024;
+ uint8_t packet[maxPacketLength];
+
+ ssize_t readBytes = read(client_fd, packet, maxPacketLength);
+ assertFalse(readBytes < 0, "Got error on read: (%d) %s", errno, strerror(errno));
+
+ parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue));
+ close(client_fd);
+}
+
+/**
+ * Send an AddRoute command down the stack. It does not need a wire format in the transport message, its
+ * the job of the forwarder to create the Metis-specific message.
+ */
+LONGBOW_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_CPIRequest)
+{
+ testUnimplemented("No way to create a v1 CPI message yet");
+
+// TestData *data = longBowTestCase_GetClipBoardData(testCase);
+//
+// // create our connection. This will become part of the RTA framework, so will be
+// // cleaned up in the teardown
+// int api_fd;
+// int client_fd;
+// RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd);
+// FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);
+//
+// // now make the control message
+// TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryControl(conn, CCNxTlvDictionary_SchemaVersion_V1);
+// CCNxCodecNetworkBufferIoVec *vec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(transportMessage_GetDictionary(tm), NULL);
+// ccnxWireFormatFacade_PutIoVec(transportMessage_GetDictionary(tm), vec);
+// ccnxCodecNetworkBufferIoVec_Release(&vec);
+//
+// // send it down the stack
+// PARCEventQueue *in = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN);
+// rtaComponent_PutMessage(in, tm);
+// rtaFramework_NonThreadedStepCount(data->framework, 5);
+//
+// // now read it from out listener. It has a read timeout so if we dont get it in a reasonable amount
+// // of time, read will return an error about the timeout
+//
+// const size_t maxPacketLength = 1024;
+// uint8_t packet[maxPacketLength];
+//
+// ssize_t readBytes = read(client_fd, packet, maxPacketLength);
+// assertFalse(readBytes < 0, "Got error on read: (%d) %s", errno, strerror(errno));
+// parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue));
+}
+
+// ====================================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(connector_Forwarder_Metis);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}