aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/messenger
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/messenger')
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_Messenger.c189
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_Messenger.h88
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.c65
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.h114
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_Missive.c66
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_Missive.h112
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.c167
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.h123
-rw-r--r--metis/ccnx/forwarder/metis/messenger/metis_MissiveType.h51
-rw-r--r--metis/ccnx/forwarder/metis/messenger/test/CMakeLists.txt16
-rw-r--r--metis/ccnx/forwarder/metis/messenger/test/test_metis_Messenger.c272
-rw-r--r--metis/ccnx/forwarder/metis/messenger/test/test_metis_MessengerRecipient.c140
-rw-r--r--metis/ccnx/forwarder/metis/messenger/test/test_metis_Missive.c109
-rw-r--r--metis/ccnx/forwarder/metis/messenger/test/test_metis_MissiveDeque.c109
14 files changed, 1621 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_Messenger.c b/metis/ccnx/forwarder/metis/messenger/metis_Messenger.c
new file mode 100644
index 00000000..6313a3c1
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_Messenger.c
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+/**
+ *
+ * The messenger is contructued with a reference to the forwarder's dispatcher so it can
+ * schedule future events. When someone calls metisMessenger_Send(...), it will put the
+ * message on a queue. If the queue was empty, it will scheudle itself to be run.
+ * By running the queue in a future dispatcher slice, it guarantees that there will be
+ * no re-entrant behavior between callers and message listeners.
+ *
+ * A recipient will receive a reference counted copy of the missive, so it must call
+ * {@link metisMissive_Release} on it.
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_EventScheduler.h>
+#include <parc/algol/parc_Event.h>
+
+#include <ccnx/forwarder/metis/messenger/metis_Messenger.h>
+#include <ccnx/forwarder/metis/messenger/metis_MissiveDeque.h>
+
+struct metis_messenger {
+ PARCArrayList *callbacklist;
+ MetisDispatcher *dispatcher;
+ MetisMissiveDeque *eventQueue;
+
+ PARCEventTimer *timerEvent;
+};
+
+static void metisMessenger_Dequeue(int fd, PARCEventType which_event, void *messengerVoidPtr);
+
+// =========================================
+// Public API
+
+MetisMessenger *
+metisMessenger_Create(MetisDispatcher *dispatcher)
+{
+ MetisMessenger *messenger = parcMemory_AllocateAndClear(sizeof(MetisMessenger));
+ assertNotNull(messenger, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessenger));
+
+ // NULL destroyer because we're storing structures owned by the caller
+ messenger->dispatcher = dispatcher;
+ messenger->callbacklist = parcArrayList_Create(NULL);
+ messenger->eventQueue = metisMissiveDeque_Create();
+
+ // creates the timer, but does not start it
+ messenger->timerEvent = metisDispatcher_CreateTimer(dispatcher, false, metisMessenger_Dequeue, messenger);
+
+ return messenger;
+}
+
+void
+metisMessenger_Destroy(MetisMessenger **messengerPtr)
+{
+ assertNotNull(messengerPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*messengerPtr, "Parameter must dereference to non-null pointer");
+
+ MetisMessenger *messenger = *messengerPtr;
+ parcArrayList_Destroy(&messenger->callbacklist);
+ metisMissiveDeque_Release(&messenger->eventQueue);
+ metisDispatcher_DestroyTimerEvent(messenger->dispatcher, &messenger->timerEvent);
+ parcMemory_Deallocate((void **) &messenger);
+ *messengerPtr = NULL;
+}
+
+void
+metisMessenger_Send(MetisMessenger *messenger, MetisMissive *missive)
+{
+ assertNotNull(messenger, "Parameter messenger must be non-null");
+ assertNotNull(missive, "Parameter event must be non-null");
+
+ metisMissiveDeque_Append(messenger->eventQueue, missive);
+ if (metisMissiveDeque_Size(messenger->eventQueue) == 1) {
+ // We need to scheudle ourself when an event is added to an empty queue
+
+ // precondition: timer should not be running.
+ struct timeval immediateTimeout = { 0, 0 };
+ metisDispatcher_StartTimer(messenger->dispatcher, messenger->timerEvent, &immediateTimeout);
+ }
+}
+
+static void
+removeRecipient(MetisMessenger *messenger, const MetisMessengerRecipient *recipient)
+{
+ // don't increment i in the loop
+ for (size_t i = 0; i < parcArrayList_Size(messenger->callbacklist); ) {
+ const void *p = parcArrayList_Get(messenger->callbacklist, i);
+ if (p == recipient) {
+ // removing will compact the list, so next element will also be at i.
+ parcArrayList_RemoveAndDestroyAtIndex(messenger->callbacklist, i);
+ } else {
+ i++;
+ }
+ }
+}
+
+/**
+ * @function metisEventMessenger_Register
+ * @abstract Receive all event messages
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+void
+metisMessenger_Register(MetisMessenger *messenger, const MetisMessengerRecipient *recipient)
+{
+ assertNotNull(messenger, "Parameter messenger must be non-null");
+ assertNotNull(recipient, "Parameter recipient must be non-null");
+
+ // do not allow duplicates
+ removeRecipient(messenger, recipient);
+
+ parcArrayList_Add(messenger->callbacklist, recipient);
+}
+
+/**
+ * @function metisEventMessenger_Unregister
+ * @abstract Stop receiving event messages
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+void
+metisMessenger_Unregister(MetisMessenger *messenger, const MetisMessengerRecipient *recipient)
+{
+ assertNotNull(messenger, "Parameter messenger must be non-null");
+ assertNotNull(recipient, "Parameter recipient must be non-null");
+
+ removeRecipient(messenger, recipient);
+}
+
+/**
+ * Called by event scheduler to give us a slice in which to dequeue events
+ *
+ * Called inside an event callback, so we now have exclusive access to the system.
+ * Dequeues all pending events and calls all the listeners for each one.
+ *
+ * @param [in] fd unused, required for compliance with function prototype
+ * @param [in] which_event unused, required for compliance with function prototype
+ * @param [in] messengerVoidPtr A void* to MetisMessenger
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+static void
+metisMessenger_Dequeue(int fd, PARCEventType which_event, void *messengerVoidPtr)
+{
+ MetisMessenger *messenger = (MetisMessenger *) messengerVoidPtr;
+ assertNotNull(messenger, "Called with null messenger pointer");
+
+ MetisMissive *missive;
+ while ((missive = metisMissiveDeque_RemoveFirst(messenger->eventQueue)) != NULL) {
+ for (size_t i = 0; i < parcArrayList_Size(messenger->callbacklist); i++) {
+ MetisMessengerRecipient *recipient = parcArrayList_Get(messenger->callbacklist, i);
+ assertTrue(recipient, "Recipient is null at index %zu", i);
+
+ metisMessengerRecipient_Deliver(recipient, metisMissive_Acquire(missive));
+ }
+
+ // now let go of our reference to the missive
+ metisMissive_Release(&missive);
+ }
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_Messenger.h b/metis/ccnx/forwarder/metis/messenger/metis_Messenger.h
new file mode 100644
index 00000000..627cd8bc
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_Messenger.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * The EventMessenger is the system that messages events between
+ * producers and consumers.
+ *
+ * Events are delivered in a deferred event cycle to avoid event callbacks
+ * firing when the event generator is still running.
+ */
+
+#ifndef Metis_metis_Messenger_h
+#define Metis_metis_Messenger_h
+
+#include <ccnx/forwarder/metis/core/metis_Dispatcher.h>
+#include <ccnx/forwarder/metis/messenger/metis_Missive.h>
+#include <ccnx/forwarder/metis/messenger/metis_MessengerRecipient.h>
+
+struct metis_messenger;
+typedef struct metis_messenger MetisMessenger;
+
+/**
+ * @function metisEventmessenger_Create
+ * @abstract Creates an event notification system
+ * @discussion
+ * Typically there's only one of these managed by metisForwarder.
+ *
+ * @param dispatcher is the event dispatcher to use to schedule events.
+ * @return <#return#>
+ */
+MetisMessenger *metisMessenger_Create(MetisDispatcher *dispatcher);
+
+/**
+ * @function metisEventMessenger_Destroy
+ * @abstract Destroys the messenger system, no notification is sent
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+void metisMessenger_Destroy(MetisMessenger **messengerPtr);
+
+/**
+ * @function metisEventMessenger_Send
+ * @abstract Send an event message, takes ownership of the event memory
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ */
+void metisMessenger_Send(MetisMessenger *messenger, MetisMissive *missive);
+
+/**
+ * @function metisEventMessenger_Register
+ * @abstract Receive all event messages
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+void metisMessenger_Register(MetisMessenger *messenger, const MetisMessengerRecipient *recipient);
+
+/**
+ * @function metisEventMessenger_Unregister
+ * @abstract Stop receiving event messages
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+void metisMessenger_Unregister(MetisMessenger *messenger, const MetisMessengerRecipient *recipient);
+#endif // Metis_metis_Messenger_h
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.c b/metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.c
new file mode 100644
index 00000000..77967c9c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.c
@@ -0,0 +1,65 @@
+/*
+ * 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 <config.h>
+#include <stdio.h>
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <ccnx/forwarder/metis/messenger/metis_Messenger.h>
+#include <ccnx/forwarder/metis/messenger/metis_MessengerRecipient.h>
+
+struct metis_messenger_recipient {
+ void *context;
+ MetisMessengerRecipientCallback *notify;
+};
+
+MetisMessengerRecipient *
+metisMessengerRecipient_Create(void *recipientContext, MetisMessengerRecipientCallback *recipientCallback)
+{
+ assertNotNull(recipientCallback, "Parameter recipientCallback must be non-null");
+
+ MetisMessengerRecipient *recipient = parcMemory_AllocateAndClear(sizeof(MetisMessengerRecipient));
+ assertNotNull(recipient, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessengerRecipient));
+ recipient->context = recipientContext;
+ recipient->notify = recipientCallback;
+ return recipient;
+}
+
+void
+metisMessengerRecipient_Destroy(MetisMessengerRecipient **recipientPtr)
+{
+ assertNotNull(recipientPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*recipientPtr, "Parameter must dereference to non-null pointer");
+
+ parcMemory_Deallocate((void **) recipientPtr);
+ *recipientPtr = NULL;
+}
+
+void *
+metisMessengerRecipient_GetRecipientContext(MetisMessengerRecipient *recipient)
+{
+ assertNotNull(recipient, "Parameter must be non-null");
+
+ return recipient->context;
+}
+
+void
+metisMessengerRecipient_Deliver(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ assertNotNull(recipient, "Parameter must be non-null");
+ recipient->notify(recipient, missive);
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.h b/metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.h
new file mode 100644
index 00000000..6e92051a
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_MessengerRecipient.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file metis_MessengerRecipient.h
+ * @brief A recipient represents the entity that will recieve a Missive from the Messenger.
+ *
+ * A recipient is identified by the pair (contenxt, callback). The context is the recipients
+ * context, such as it's object pointer. The callback is the function the recipient uses
+ * to receive a Missive.
+ *
+ * If the receiver is going to do a lot of work or potentially send other missives, the receiver
+ * should queue the received notifications and process them in its own slice.
+ *
+ * A recipient will receive a reference counted copy of the missive, so it must call
+ * {@link metisMissive_Release} on it.
+ *
+ *
+ */
+
+#ifndef Metis_metis_MessengerRecipient_h
+#define Metis_metis_MessengerRecipient_h
+
+struct metis_messenger_recipient;
+typedef struct metis_messenger_recipient MetisMessengerRecipient;
+
+/**
+ * @typedef MetisMessengerRecipientCallback
+ * @abstract A recipient implements a callback to receive Missives.
+ * @constant recipient The recipient to recieve the missive
+ * @constant missive The missive, recipient must call {@link metisMissive_Release} on it
+ * @discussion <#Discussion#>
+ */
+typedef void (MetisMessengerRecipientCallback)(MetisMessengerRecipient *recipient, MetisMissive *missive);
+
+/**
+ * Creates a Recipient, which represents a reciever of missives.
+ *
+ * Creates a Recipient that can be registerd with the Messenger using {@link metisMessenger_Register}.
+ *
+ * @param [in] recipientContext This pointer will be passed back to the recipient with each missive, may be NULL
+ * @param [in] recipientCallback The function that receives the missive, must be non-NULL.
+ *
+ * @return non-null A recipient object
+ *
+ * Example:
+ * @code
+ * @endcode
+ */
+MetisMessengerRecipient *metisMessengerRecipient_Create(void *recipientContext, MetisMessengerRecipientCallback *recipientCallback);
+
+/**
+ * Destroys a recipient. You should unregister it first.
+ *
+ * Destroying a recipient does not unregister it, so be sure to call
+ * {@link metisMessenger_Unregister} first.
+ *
+ * @param [in,out] recipientPtr Double pointer to the recipient to destroy, will be NULL'd.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void metisMessengerRecipient_Destroy(MetisMessengerRecipient **recipientPtr);
+
+/**
+ * Returns the recipient context passed on Create
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] recipient The recipient object
+ *
+ * @return pointer The context pointer used to create the object, maybe NULL
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void *metisMessengerRecipient_GetRecipientContext(MetisMessengerRecipient *recipient);
+
+/**
+ * Delivers a Missive to the recipient
+ *
+ * Passes the missive to the recipients callback.
+ *
+ * A recipient will receive a reference counted copy of the missive, so it must call
+ * {@link metisMissive_Release} on it.
+ *
+ * @param [in] recipient The receiver
+ * @param [in] missive The message to send
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void metisMessengerRecipient_Deliver(MetisMessengerRecipient *recipient, MetisMissive *missive);
+#endif // Metis_metis_MessengerRecipient_h
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_Missive.c b/metis/ccnx/forwarder/metis/messenger/metis_Missive.c
new file mode 100644
index 00000000..9eace760
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_Missive.c
@@ -0,0 +1,66 @@
+/*
+ * 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 <config.h>
+#include <stdio.h>
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <ccnx/forwarder/metis/messenger/metis_Missive.h>
+
+struct metis_missive {
+ MetisMissiveType missiveType;
+ unsigned connectionid;
+};
+
+parcObject_Override(MetisMissive, PARCObject,
+ .isLockable = false);
+
+MetisMissive *
+metisMissive_Create(MetisMissiveType missiveType, unsigned connectionid)
+{
+ MetisMissive *missive = parcObject_CreateInstance(MetisMissive);
+ missive->missiveType = missiveType;
+ missive->connectionid = connectionid;
+ return missive;
+}
+
+MetisMissive *
+metisMissive_Acquire(const MetisMissive *missive)
+{
+ return parcObject_Acquire(missive);
+}
+
+void
+metisMissive_Release(MetisMissive **missivePtr)
+{
+ parcObject_Release((void **) missivePtr);
+}
+
+MetisMissiveType
+metisMissive_GetType(const MetisMissive *missive)
+{
+ assertNotNull(missive, "Parameter missive must be non-null");
+ return missive->missiveType;
+}
+
+unsigned
+metisMissive_GetConnectionId(const MetisMissive *missive)
+{
+ assertNotNull(missive, "Parameter missive must be non-null");
+ return missive->connectionid;
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_Missive.h b/metis/ccnx/forwarder/metis/messenger/metis_Missive.h
new file mode 100644
index 00000000..8f3b4906
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_Missive.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file metis_Missive.h
+ * @brief A Missive is a status message sent over a broadcast channel inside Metis
+ *
+ * Recipients use {@link metisMessenger_Register} to receive missives. They are
+ * broadcast to all recipients.
+ *
+ */
+#ifndef Metis_metis_missive_h
+#define Metis_metis_missive_h
+
+#include <ccnx/forwarder/metis/messenger/metis_MissiveType.h>
+
+struct metis_missive;
+typedef struct metis_missive MetisMissive;
+
+/**
+ * Creates a Missive and sets the reference count to 1
+ *
+ * A Missive may be sent to listeners of the MetisMessenger to inform them of events on a connection id.
+ *
+ * @param [in] MetisMissiveType The event type
+ * @param [in] connectionid The relevant conneciton id
+ *
+ * @return non-null A message
+ * @retrun null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+MetisMissive *metisMissive_Create(MetisMissiveType missiveType, unsigned connectionid);
+
+/**
+ * Acquire a reference counted copy
+ *
+ * Increases the reference count by 1 and returns the original object.
+ *
+ * @param [in] missive An allocated missive
+ *
+ * @return non-null The original missive with increased reference count
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+MetisMissive *metisMissive_Acquire(const MetisMissive *missive);
+
+/**
+ * Releases a reference counted copy.
+ *
+ * If it is the last reference, the missive is freed.
+ *
+ * @param [in,out] missivePtr Double pointer to a missive, will be nulled.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void metisMissive_Release(MetisMissive **missivePtr);
+
+/**
+ * Returns the type of the missive
+ *
+ * Returns the type of event the missive represents
+ *
+ * @param [in] missive An allocated missive
+ *
+ * @return MetisMissiveType The event type
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+MetisMissiveType metisMissive_GetType(const MetisMissive *missive);
+
+/**
+ * Returns the connection ID of the missive
+ *
+ * An event is usually associated with a connection id (i.e. the I/O channel
+ * that originaged the event).
+ *
+ * @param [in] missive An allocated missive
+ *
+ * @return number The relevant connection id.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+unsigned metisMissive_GetConnectionId(const MetisMissive *missive);
+#endif // Metis_metis_missive_h
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.c b/metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.c
new file mode 100644
index 00000000..a9501022
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+/**
+ * A type-safe wrapper for Missives around a {@link PARCDeque}. We only implement
+ * the subset of functions used.
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Deque.h>
+
+#include <ccnx/forwarder/metis/messenger/metis_Missive.h>
+#include <ccnx/forwarder/metis/messenger/metis_MissiveDeque.h>
+
+struct metis_missive_deque {
+ PARCDeque *queue;
+};
+
+/**
+ * Create a `PARCDeque` instance with the default element equals function.
+ *
+ * The queue is created with no elements.
+ *
+ * The default element equals function is used by the `parcDeque_Equals` function and
+ * simply compares the values using the `==` operator.
+ * Users that need more sophisticated comparisons of the elements need to supply their own
+ * function via the `parcDeque_CreateCustom` function.
+ *
+ * @return non-NULL A pointer to a PARCDeque instance.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+MetisMissiveDeque *
+metisMissiveDeque_Create(void)
+{
+ MetisMissiveDeque *missiveDeque = parcMemory_AllocateAndClear(sizeof(MetisMissiveDeque));
+ assertNotNull(missiveDeque, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMissiveDeque));
+ missiveDeque->queue = parcDeque_Create();
+ return missiveDeque;
+}
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void
+metisMissiveDeque_Release(MetisMissiveDeque **dequePtr)
+{
+ assertNotNull(dequePtr, "Double pointer must be non-null");
+ assertNotNull(*dequePtr, "Double pointer must dereference to non-null");
+ MetisMissiveDeque *missiveDeque = *dequePtr;
+
+ // flush the queue
+ while (!parcDeque_IsEmpty(missiveDeque->queue)) {
+ MetisMissive *missive = metisMissiveDeque_RemoveFirst(missiveDeque);
+ metisMissive_Release(&missive);
+ }
+
+ parcDeque_Release(&missiveDeque->queue);
+ parcMemory_Deallocate((void **) &missiveDeque);
+ *dequePtr = NULL;
+}
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+MetisMissiveDeque *
+metisMissiveDeque_Append(MetisMissiveDeque *deque, MetisMissive *missive)
+{
+ assertNotNull(deque, "Parameter deque must be non-null");
+ assertNotNull(missive, "Parameter missive must be non-null");
+
+ parcDeque_Append(deque->queue, missive);
+ return deque;
+}
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+MetisMissive *
+metisMissiveDeque_RemoveFirst(MetisMissiveDeque *deque)
+{
+ assertNotNull(deque, "Parameter deque must be non-null");
+ return (MetisMissive *) parcDeque_RemoveFirst(deque->queue);
+}
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+size_t
+metisMissiveDeque_Size(const MetisMissiveDeque *deque)
+{
+ assertNotNull(deque, "Parameter deque must be non-null");
+ return parcDeque_Size(deque->queue);
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.h b/metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.h
new file mode 100644
index 00000000..b616a75b
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_MissiveDeque.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file metis_MissiveDeque
+ * @brief Double ended queue of Missives
+ *
+ * Used to queue Missives. This is a type-safe wrapper around {@link PARCDeque}
+ *
+ */
+
+#ifndef Metis_metis_MissiveDeque_h
+#define Metis_metis_MissiveDeque_h
+
+struct metis_missive_deque;
+
+typedef struct metis_missive_deque MetisMissiveDeque;
+
+/**
+ * Create a `PARCDeque` instance with the default element equals function.
+ *
+ * The queue is created with no elements.
+ *
+ * The default element equals function is used by the `parcDeque_Equals` function and
+ * simply compares the values using the `==` operator.
+ * Users that need more sophisticated comparisons of the elements need to supply their own
+ * function via the `parcDeque_CreateCustom` function.
+ *
+ * @return non-NULL A pointer to a PARCDeque instance.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+MetisMissiveDeque *metisMissiveDeque_Create(void);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void metisMissiveDeque_Release(MetisMissiveDeque **dequePtr);
+
+/**
+ * Appends the missive to the queue, taking ownership of the memory
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+MetisMissiveDeque *metisMissiveDeque_Append(MetisMissiveDeque *deque, MetisMissive *missive);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+MetisMissive *metisMissiveDeque_RemoveFirst(MetisMissiveDeque *deque);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+size_t metisMissiveDeque_Size(const MetisMissiveDeque *deque);
+#endif // Metis_metis_MissiveDeque_h
diff --git a/metis/ccnx/forwarder/metis/messenger/metis_MissiveType.h b/metis/ccnx/forwarder/metis/messenger/metis_MissiveType.h
new file mode 100644
index 00000000..7b39acd3
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/metis_MissiveType.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file metis_MissiveType
+ * @brief Defines what a Missive represents
+ *
+ * Currently, missives only carry information about the state of a connection
+ * (created, up, down, closed, destroyed).
+ *
+ */
+
+#ifndef Metis_metis_MissiveType_h
+#define Metis_metis_MissiveType_h
+
+/**
+ * @typedef Represents the state of a connection
+ * @abstract CREATE is the initial state. UP & DOWN are recurrent states. CLOSED is transient. DESTROYED is the terminal state.
+ * @constant MetisMissiveType_ConnectionCreate Connection created (new)
+ * @constant MetisMissiveType_ConnectionUp Connection is active and passing data
+ * @constant MetisMissiveType_ConnectionDown Connection is inactive and cannot pass data
+ * @constant MetisMissiveType_ConnectionClosed Connection closed and will be destroyed
+ * @constant MetisMissiveType_ConnectionDestroyed Connection destroyed
+ * @discussion State transitions:
+ * initial -> CREATE
+ * CREATE -> (UP | DOWN)
+ * UP -> (DOWN | DESTROYED)
+ * DOWN -> (UP | CLOSED | DESTROYED)
+ * CLOSED -> DESTROYED
+ * DESTROYED -> terminal
+ */
+typedef enum {
+ MetisMissiveType_ConnectionCreate,
+ MetisMissiveType_ConnectionUp,
+ MetisMissiveType_ConnectionDown,
+ MetisMissiveType_ConnectionClosed,
+ MetisMissiveType_ConnectionDestroyed
+} MetisMissiveType;
+#endif // Metis_metis_MissiveType_h
diff --git a/metis/ccnx/forwarder/metis/messenger/test/CMakeLists.txt b/metis/ccnx/forwarder/metis/messenger/test/CMakeLists.txt
new file mode 100644
index 00000000..22a8c41d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/test/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Enable gcov output for the tests
+add_definitions(--coverage)
+set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
+
+set(TestsExpectedToPass
+ test_metis_Missive
+ test_metis_MissiveDeque
+ test_metis_Messenger
+ test_metis_MessengerRecipient
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/metis/ccnx/forwarder/metis/messenger/test/test_metis_Messenger.c b/metis/ccnx/forwarder/metis/messenger/test/test_metis_Messenger.c
new file mode 100644
index 00000000..7b85391f
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/test/test_metis_Messenger.c
@@ -0,0 +1,272 @@
+/*
+ * 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_Messenger.c"
+#include <inttypes.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+LONGBOW_TEST_RUNNER(metis_Messenger)
+{
+ // 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_Messenger)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_Messenger)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisMessenger_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, metisMessenger_Register);
+ LONGBOW_RUN_TEST_CASE(Global, metisMessenger_Register_Twice);
+ LONGBOW_RUN_TEST_CASE(Global, metisMessenger_Unregister);
+ LONGBOW_RUN_TEST_CASE(Global, metisMessenger_Send);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisMessenger_Create_Destroy)
+{
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisDispatcher *dispatcher = metisDispatcher_Create(logger);
+ metisLogger_Release(&logger);
+
+ MetisMessenger *messenger = metisMessenger_Create(dispatcher);
+ metisMessenger_Destroy(&messenger);
+ metisDispatcher_Destroy(&dispatcher);
+
+ assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Create/Destroy has memory leak");
+}
+
+// The callback will compare what its called back with these "truth" values
+static const MetisMissive *truth_missive;
+static const MetisMessengerRecipient *truth_recipient;
+
+static void
+test_notify(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ assertTrue(recipient == truth_recipient, "Got wrong recipient in callback expected %" PRIXPTR " got %" PRIXPTR, (uintptr_t) truth_recipient, (uintptr_t) recipient);
+ assertTrue(missive == truth_missive, "Got wrong event in callback expected %p got %p", (void *) truth_missive, (void *) missive);
+ metisMissive_Release(&missive);
+}
+
+LONGBOW_TEST_CASE(Global, metisMessenger_Register)
+{
+ int a = 1;
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&a, &test_notify);
+
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisDispatcher *dispatcher = metisDispatcher_Create(logger);
+ metisLogger_Release(&logger);
+ MetisMessenger *messenger = metisMessenger_Create(dispatcher);
+
+ metisMessenger_Register(messenger, recipient);
+ assertTrue(parcArrayList_Size(messenger->callbacklist) == 1,
+ "messenger array list wrong size, expected %u got %zu",
+ 1,
+ parcArrayList_Size(messenger->callbacklist));
+
+ const void *p = parcArrayList_Get(messenger->callbacklist, 0);
+ assertTrue(p == recipient,
+ "Messenger callbacklist contained wrong pointer, expected %p got %p",
+ (void *) recipient,
+ p);
+
+ metisMessenger_Destroy(&messenger);
+ metisDispatcher_Destroy(&dispatcher);
+ metisMessengerRecipient_Destroy(&recipient);
+}
+
+/**
+ * Register same callback twice, should only appear once in list
+ */
+LONGBOW_TEST_CASE(Global, metisMessenger_Register_Twice)
+{
+ int a = 1;
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&a, &test_notify);
+
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisDispatcher *dispatcher = metisDispatcher_Create(logger);
+ metisLogger_Release(&logger);
+ MetisMessenger *messenger = metisMessenger_Create(dispatcher);
+
+ metisMessenger_Register(messenger, recipient);
+ metisMessenger_Register(messenger, recipient);
+ assertTrue(parcArrayList_Size(messenger->callbacklist) == 1,
+ "messenger array list wrong size, expected %u got %zu",
+ 1,
+ parcArrayList_Size(messenger->callbacklist));
+
+ const void *p = parcArrayList_Get(messenger->callbacklist, 0);
+ assertTrue(p == recipient,
+ "Messenger callbacklist contained wrong pointer, expected %p got %p",
+ (void *) recipient,
+ p);
+
+ metisMessenger_Destroy(&messenger);
+ metisDispatcher_Destroy(&dispatcher);
+ metisMessengerRecipient_Destroy(&recipient);
+}
+
+LONGBOW_TEST_CASE(Global, metisMessenger_Unregister)
+{
+ int a = 1;
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&a, &test_notify);
+
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisDispatcher *dispatcher = metisDispatcher_Create(logger);
+ metisLogger_Release(&logger);
+ MetisMessenger *messenger = metisMessenger_Create(dispatcher);
+
+ metisMessenger_Register(messenger, recipient);
+ metisMessenger_Unregister(messenger, recipient);
+
+ assertTrue(parcArrayList_Size(messenger->callbacklist) == 0,
+ "messenger array list wrong size, expected %u got %zu",
+ 0,
+ parcArrayList_Size(messenger->callbacklist));
+
+ metisMessenger_Destroy(&messenger);
+ metisDispatcher_Destroy(&dispatcher);
+ metisMessengerRecipient_Destroy(&recipient);
+}
+
+LONGBOW_TEST_CASE(Global, metisMessenger_Send)
+{
+ int a = 1;
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&a, &test_notify);
+
+ truth_recipient = recipient;
+
+ MetisMissive *missive = metisMissive_Create(MetisMissiveType_ConnectionUp, 12);
+ truth_missive = missive;
+
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisDispatcher *dispatcher = metisDispatcher_Create(logger);
+ metisLogger_Release(&logger);
+ MetisMessenger *messenger = metisMessenger_Create(dispatcher);
+
+ metisMessenger_Send(messenger, missive);
+
+ metisDispatcher_RunDuration(dispatcher, &((struct timeval) {0, 10000}));
+
+ // if we didn't assert, it is correct.
+
+ metisMessenger_Destroy(&messenger);
+ metisDispatcher_Destroy(&dispatcher);
+ metisMessengerRecipient_Destroy(&recipient);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, removeCallback);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Local, removeCallback)
+{
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ MetisDispatcher *dispatcher = metisDispatcher_Create(logger);
+ metisLogger_Release(&logger);
+ MetisMessenger *messenger = metisMessenger_Create(dispatcher);
+
+ parcArrayList_Add(messenger->callbacklist, (void *) 1);
+ parcArrayList_Add(messenger->callbacklist, (void *) 2);
+ parcArrayList_Add(messenger->callbacklist, (void *) 3);
+
+ removeRecipient(messenger, (void *) 2);
+
+ assertTrue(parcArrayList_Size(messenger->callbacklist) == 2,
+ "messenger array list wrong size, expected %u got %zu",
+ 2,
+ parcArrayList_Size(messenger->callbacklist));
+
+ const void *p = parcArrayList_Get(messenger->callbacklist, 0);
+ assertTrue(p == (void *) 1,
+ "Messenger callbacklist contained wrong pointer at 0, expected %p got %p",
+ (void *) 1,
+ p);
+
+ p = parcArrayList_Get(messenger->callbacklist, 1);
+ assertTrue(p == (void *) 3,
+ "Messenger callbacklist contained wrong pointer at 1, expected %p got %p",
+ (void *) 3,
+ p);
+
+
+ metisMessenger_Destroy(&messenger);
+ metisDispatcher_Destroy(&dispatcher);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_Messenger);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/test/test_metis_MessengerRecipient.c b/metis/ccnx/forwarder/metis/messenger/test/test_metis_MessengerRecipient.c
new file mode 100644
index 00000000..fea68fe4
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/test/test_metis_MessengerRecipient.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_MessengerRecipient.c"
+
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(metis_MessengerRecipient)
+{
+ // 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_MessengerRecipient)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_MessengerRecipient)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisMessengerRecipient_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisMessengerRecipient_Deliver);
+ LONGBOW_RUN_TEST_CASE(Global, metisMessengerRecipient_GetRecipientContext);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+typedef struct my_context {
+ MetisMissive *lastMessage;
+} MyContext;
+
+static void
+testRecipientCallback(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ MyContext *mycontext = metisMessengerRecipient_GetRecipientContext(recipient);
+ mycontext->lastMessage = missive;
+}
+
+LONGBOW_TEST_CASE(Global, metisMessengerRecipient_Create)
+{
+ MyContext mycontext;
+
+ // create and destroy and make sure no memory leaks
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&mycontext, testRecipientCallback);
+ metisMessengerRecipient_Destroy(&recipient);
+ size_t balance = parcMemory_Outstanding();
+ assertTrue(balance == 0, "Memory imbalance, expected 0, got %zu", balance);
+}
+
+LONGBOW_TEST_CASE(Global, metisMessengerRecipient_Deliver)
+{
+ MyContext mycontext;
+ MetisMissive *truthMissive = metisMissive_Create(MetisMissiveType_ConnectionUp, 33);
+
+ // create and destroy and make sure no memory leaks
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&mycontext, testRecipientCallback);
+
+ metisMessengerRecipient_Deliver(recipient, truthMissive);
+
+ assertTrue(mycontext.lastMessage == truthMissive, "Recipient callback not right missve, expected %p got %p", (void *) truthMissive, (void *) mycontext.lastMessage);
+
+ metisMessengerRecipient_Destroy(&recipient);
+
+ metisMissive_Release(&truthMissive);
+}
+
+LONGBOW_TEST_CASE(Global, metisMessengerRecipient_GetRecipientContext)
+{
+ MyContext mycontext;
+
+ // create and destroy and make sure no memory leaks
+ MetisMessengerRecipient *recipient = metisMessengerRecipient_Create(&mycontext, testRecipientCallback);
+
+ void *testcontext = metisMessengerRecipient_GetRecipientContext(recipient);
+ assertTrue(testcontext == &mycontext, "Got wrong context back, expected %p got %p", (void *) &mycontext, (void *) testcontext);
+ metisMessengerRecipient_Destroy(&recipient);
+}
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_MessengerRecipient);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/test/test_metis_Missive.c b/metis/ccnx/forwarder/metis/messenger/test/test_metis_Missive.c
new file mode 100644
index 00000000..04403fbe
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/test/test_metis_Missive.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_Missive.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(metis_Event)
+{
+ // 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_Event)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_Event)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisMissive_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, metisMissive_GetType);
+ LONGBOW_RUN_TEST_CASE(Global, metisMissive_GetConnectionId);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisMissive_Create_Destroy)
+{
+ MetisMissive *missive = metisMissive_Create(MetisMissiveType_ConnectionUp, 5);
+ metisMissive_Release(&missive);
+}
+
+LONGBOW_TEST_CASE(Global, metisMissive_GetType)
+{
+ MetisMissive *missive = metisMissive_Create(MetisMissiveType_ConnectionUp, 5);
+ MetisMissiveType type = metisMissive_GetType(missive);
+ metisMissive_Release(&missive);
+
+ assertTrue(type == MetisMissiveType_ConnectionUp, "Got wrong type, expected %d got %d\n", MetisMissiveType_ConnectionUp, type);
+}
+
+LONGBOW_TEST_CASE(Global, metisMissive_GetConnectionId)
+{
+ MetisMissive *missive = metisMissive_Create(MetisMissiveType_ConnectionUp, 5);
+ unsigned connid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+
+ assertTrue(connid == 5, "Got wrong connection id, expected %u got %u\n", 5, connid);
+}
+
+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_Event);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/messenger/test/test_metis_MissiveDeque.c b/metis/ccnx/forwarder/metis/messenger/test/test_metis_MissiveDeque.c
new file mode 100644
index 00000000..e0a170e3
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/messenger/test/test_metis_MissiveDeque.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_MissiveDeque.c"
+
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(metis_MissiveDeque)
+{
+ // 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_MissiveDeque)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_MissiveDeque)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisMissiveDeque_Append);
+ LONGBOW_RUN_TEST_CASE(Global, metisMissiveDeque_Create);
+ LONGBOW_RUN_TEST_CASE(Global, metisMissiveDeque_Release);
+ LONGBOW_RUN_TEST_CASE(Global, metisMissiveDeque_RemoveFirst);
+ LONGBOW_RUN_TEST_CASE(Global, metisMissiveDeque_Size);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisMissiveDeque_Append)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, metisMissiveDeque_Create)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, metisMissiveDeque_Release)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, metisMissiveDeque_RemoveFirst)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, metisMissiveDeque_Size)
+{
+ testUnimplemented("");
+}
+
+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_MissiveDeque);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}