Add message_handler tests for inference cancellation

Change-Id: Ifdacc47024250e34549d45377795501c371c69f5
diff --git a/applications/message_handler/test/CMakeLists.txt b/applications/message_handler/test/CMakeLists.txt
index 44fd471..7cba4c3 100644
--- a/applications/message_handler/test/CMakeLists.txt
+++ b/applications/message_handler/test/CMakeLists.txt
@@ -21,30 +21,35 @@
 set(TEST_MESSAGE_HANDLER_MODEL_2 "" CACHE STRING "Path to built in model 2")
 set(TEST_MESSAGE_HANDLER_MODEL_3 "" CACHE STRING "Path to built in model 3")
 
-if(TARGET ethosu_core_driver)
-    file(GLOB models LIST_DIRECTORIES true "${CMAKE_CURRENT_SOURCE_DIR}/../../baremetal/models/${ETHOSU_TARGET_NPU_CONFIG}/*")
-endif()
+function(ethosu_add_message_handler_test testname)
+    if(TARGET ethosu_core_driver)
+        file(GLOB models LIST_DIRECTORIES true "${CMAKE_CURRENT_SOURCE_DIR}/../../baremetal/models/${ETHOSU_TARGET_NPU_CONFIG}/*")
+    endif()
 
-foreach(model ${models})
-    get_filename_component(modelname ${model} NAME)
-    ethosu_add_executable_test(message_handler_test_${modelname}
-        SOURCES
-        main.cpp
-        message_client.cpp
-        LIBRARIES
-        message_handler_lib
-        freertos_kernel
-        ethosu_mhu_dummy)
+    foreach(model ${models})
+        get_filename_component(modelname ${model} NAME)
+        ethosu_add_executable_test(mh_${testname}_${modelname}
+            SOURCES
+            ${testname}.cpp
+            message_client.cpp
+            LIBRARIES
+            message_handler_lib
+            freertos_kernel
+            ethosu_mhu_dummy)
 
-    target_include_directories(message_handler_test_${modelname} PRIVATE
-        ../indexed_networks
-        ${model}
-        ${LINUX_DRIVER_STACK_PATH}/kernel)
+        target_include_directories(mh_${testname}_${modelname} PRIVATE
+            ../indexed_networks
+            ${model}
+            ${LINUX_DRIVER_STACK_PATH}/kernel)
 
-    target_compile_definitions(message_handler_test_${modelname} PRIVATE
-        TENSOR_ARENA_SIZE=${MESSAGE_HANDLER_ARENA_SIZE}
-        $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_0}>:MODEL_0=${TEST_MESSAGE_HANDLER_MODEL_0}>
-        $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_1}>:MODEL_1=${TEST_MESSAGE_HANDLER_MODEL_1}>
-        $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_2}>:MODEL_2=${TEST_MESSAGE_HANDLER_MODEL_2}>
-        $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_3}>:MODEL_3=${TEST_MESSAGE_HANDLER_MODEL_3}>)
-endforeach()
+        target_compile_definitions(mh_${testname}_${modelname} PRIVATE
+            TENSOR_ARENA_SIZE=${MESSAGE_HANDLER_ARENA_SIZE}
+            $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_0}>:MODEL_0=${TEST_MESSAGE_HANDLER_MODEL_0}>
+            $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_1}>:MODEL_1=${TEST_MESSAGE_HANDLER_MODEL_1}>
+            $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_2}>:MODEL_2=${TEST_MESSAGE_HANDLER_MODEL_2}>
+            $<$<BOOL:${TEST_MESSAGE_HANDLER_MODEL_3}>:MODEL_3=${TEST_MESSAGE_HANDLER_MODEL_3}>)
+    endforeach()
+endfunction()
+
+ethosu_add_message_handler_test(run_inference_test)
+ethosu_add_message_handler_test(cancel_reject_inference_test)
diff --git a/applications/message_handler/test/cancel_reject_inference_test.cpp b/applications/message_handler/test/cancel_reject_inference_test.cpp
new file mode 100644
index 0000000..9f4f9b4
--- /dev/null
+++ b/applications/message_handler/test/cancel_reject_inference_test.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2022 Arm Limited.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ *
+ * 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.
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "FreeRTOS.h"
+#include "queue.h"
+#include "semphr.h"
+#include "task.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "ethosu_core_interface.h"
+#include "indexed_networks.hpp"
+#include "message_client.hpp"
+#include "message_handler.hpp"
+#include "message_queue.hpp"
+#include "networks.hpp"
+#include "test_assertions.hpp"
+#include "test_helpers.hpp"
+
+#include <mailbox.hpp>
+#include <mhu_dummy.hpp>
+
+/* Disable semihosting */
+__asm(".global __use_no_semihosting\n\t");
+
+using namespace EthosU;
+using namespace MessageHandler;
+
+/****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+// TensorArena static initialisation
+constexpr size_t arenaSize = TENSOR_ARENA_SIZE;
+
+__attribute__((section(".bss.tensor_arena"), aligned(16))) uint8_t tensorArena[arenaSize];
+
+// Message queue from remote host
+__attribute__((section("ethosu_core_in_queue"))) MessageQueue::Queue<1000> inputMessageQueue;
+
+// Message queue to remote host
+__attribute__((section("ethosu_core_out_queue"))) MessageQueue::Queue<1000> outputMessageQueue;
+
+namespace {
+Mailbox::MHUDummy mailbox;
+} // namespace
+
+/****************************************************************************
+ * Application
+ ****************************************************************************/
+namespace {
+
+struct TaskParams {
+    TaskParams() :
+        messageNotify(xSemaphoreCreateBinary()),
+        inferenceInputQueue(std::make_shared<Queue<ethosu_core_inference_req>>()),
+        inferenceOutputQueue(xQueueCreate(5, sizeof(ethosu_core_inference_rsp))),
+        networks(std::make_shared<WithIndexedNetworks>()) {}
+
+    SemaphoreHandle_t messageNotify;
+    // Used to pass inference requests to the inference runner task
+    std::shared_ptr<Queue<ethosu_core_inference_req>> inferenceInputQueue;
+    // Queue for message responses to the remote host
+    QueueHandle_t inferenceOutputQueue;
+    // Networks provider
+    std::shared_ptr<Networks> networks;
+};
+
+void messageTask(void *pvParameters) {
+    printf("Starting message task\n");
+    TaskParams *params = reinterpret_cast<TaskParams *>(pvParameters);
+
+    IncomingMessageHandler process(*inputMessageQueue.toQueue(),
+                                   *outputMessageQueue.toQueue(),
+                                   mailbox,
+                                   params->inferenceInputQueue,
+                                   params->inferenceOutputQueue,
+                                   params->messageNotify,
+                                   params->networks);
+    process.run();
+}
+
+void testCancelInference(MessageClient client) {
+    const uint64_t fake_inference_user_arg = 42;
+    const uint32_t network_index           = 0;
+    ethosu_core_inference_req inference_req =
+        inferenceIndexedRequest(fake_inference_user_arg, network_index, nullptr, 0, nullptr, 0);
+
+    const uint64_t fake_cancel_inference_user_arg = 55;
+    ethosu_core_cancel_inference_req cancel_req   = {fake_cancel_inference_user_arg, fake_inference_user_arg};
+
+    ethosu_core_inference_rsp inference_rsp;
+    ethosu_core_cancel_inference_rsp cancel_rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, inference_req));
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_REQ, cancel_req));
+
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, inference_rsp));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_RSP, cancel_rsp));
+
+    TEST_ASSERT(inference_req.user_arg == inference_rsp.user_arg);
+    TEST_ASSERT(inference_rsp.status == ETHOSU_CORE_STATUS_ABORTED);
+
+    TEST_ASSERT(cancel_req.user_arg == cancel_rsp.user_arg);
+    TEST_ASSERT(cancel_rsp.status == ETHOSU_CORE_STATUS_OK);
+}
+
+void testCancelNonExistentInference(MessageClient client) {
+    const uint64_t fake_inference_user_arg        = 42;
+    const uint64_t fake_cancel_inference_user_arg = 55;
+    ethosu_core_cancel_inference_req cancel_req   = {fake_cancel_inference_user_arg, fake_inference_user_arg};
+    ethosu_core_cancel_inference_rsp cancel_rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_REQ, cancel_req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_RSP, cancel_rsp));
+
+    TEST_ASSERT(cancel_req.user_arg == cancel_rsp.user_arg);
+    TEST_ASSERT(cancel_rsp.status == ETHOSU_CORE_STATUS_ERROR);
+}
+
+void testCannotCancelRunningInference(MessageClient client,
+                                      std::shared_ptr<Queue<ethosu_core_inference_req>> inferenceInputQueue) {
+    const uint64_t fake_inference_user_arg = 42;
+    const uint32_t network_index           = 0;
+    ethosu_core_inference_req inference_req =
+        inferenceIndexedRequest(fake_inference_user_arg, network_index, nullptr, 0, nullptr, 0);
+
+    const uint64_t fake_cancel_inference_user_arg = 55;
+    ethosu_core_cancel_inference_req cancel_req   = {fake_cancel_inference_user_arg, fake_inference_user_arg};
+    ethosu_core_cancel_inference_rsp cancel_rsp;
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, inference_req));
+
+    // fake start of the inference by removing the inference from the queue
+    ethosu_core_inference_req start_req;
+    inferenceInputQueue->pop(start_req);
+    TEST_ASSERT(inference_req.user_arg == start_req.user_arg);
+
+    TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_REQ, cancel_req));
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_RSP, cancel_rsp));
+
+    TEST_ASSERT(cancel_req.user_arg == cancel_rsp.user_arg);
+    TEST_ASSERT(cancel_rsp.status == ETHOSU_CORE_STATUS_ERROR);
+}
+
+void testRejectInference(MessageClient client) {
+    int runs                                      = 6;
+    const uint64_t fake_inference_user_arg        = 42;
+    const uint32_t network_index                  = 0;
+    const uint64_t fake_cancel_inference_user_arg = 55;
+    ethosu_core_inference_req req;
+    ethosu_core_inference_rsp rsp;
+
+    for (int i = 0; i < runs; i++) {
+
+        req = inferenceIndexedRequest(fake_inference_user_arg + i, network_index, nullptr, 0, nullptr, 0);
+        TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
+        vTaskDelay(150);
+    }
+
+    TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, rsp));
+    TEST_ASSERT(uint64_t(fake_inference_user_arg + runs - 1) == rsp.user_arg);
+    TEST_ASSERT(rsp.status == ETHOSU_CORE_STATUS_REJECTED);
+
+    // let's cleanup the queue
+    ethosu_core_cancel_inference_req cancel_req = {0, 0};
+    ethosu_core_cancel_inference_rsp cancel_rsp;
+    ethosu_core_inference_rsp inference_rsp;
+
+    for (int i = 0; i < runs - 1; i++) {
+        cancel_req.user_arg         = fake_cancel_inference_user_arg + i;
+        cancel_req.inference_handle = fake_inference_user_arg + i;
+        TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_REQ, cancel_req));
+
+        TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_INFERENCE_RSP, inference_rsp));
+        TEST_ASSERT(inference_rsp.user_arg = cancel_req.inference_handle);
+
+        TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_CANCEL_INFERENCE_RSP, cancel_rsp));
+        TEST_ASSERT(cancel_req.user_arg == cancel_rsp.user_arg);
+        TEST_ASSERT(cancel_rsp.status == ETHOSU_CORE_STATUS_OK);
+    }
+}
+
+void clientTask(void *pvParameters) {
+    printf("Starting client task\n");
+    TaskParams *params = reinterpret_cast<TaskParams *>(pvParameters);
+
+    MessageClient client(*inputMessageQueue.toQueue(), *outputMessageQueue.toQueue(), mailbox);
+
+    vTaskDelay(50);
+
+    testCancelInference(client);
+    testCancelNonExistentInference(client);
+    testCannotCancelRunningInference(client, params->inferenceInputQueue);
+    testRejectInference(client);
+
+    exit(0);
+}
+
+/*
+ * Keep task parameters as global data as FreeRTOS resets the stack when the
+ * scheduler is started.
+ */
+TaskParams taskParams;
+
+} // namespace
+
+// FreeRTOS application. NOTE: Additional tasks may require increased heap size.
+int main() {
+    BaseType_t ret;
+
+    if (!mailbox.verifyHardware()) {
+        printf("Failed to verify mailbox hardware\n");
+        return 1;
+    }
+
+    // Task for handling incoming /outgoing messages from the remote host
+    ret = xTaskCreate(messageTask, "messageTask", 1024, &taskParams, 2, nullptr);
+    if (ret != pdPASS) {
+        printf("Failed to create 'messageTask'\n");
+        return ret;
+    }
+
+    // Task for handling incoming /outgoing messages from the remote host
+    ret = xTaskCreate(clientTask, "clientTask", 1024, &taskParams, 2, nullptr);
+    if (ret != pdPASS) {
+        printf("Failed to create 'messageTask'\n");
+        return ret;
+    }
+
+    // Start Scheduler
+    vTaskStartScheduler();
+
+    return 1;
+}
diff --git a/applications/message_handler/test/main.cpp b/applications/message_handler/test/run_inference_test.cpp
similarity index 77%
rename from applications/message_handler/test/main.cpp
rename to applications/message_handler/test/run_inference_test.cpp
index 6a4d26d..d05224f 100644
--- a/applications/message_handler/test/main.cpp
+++ b/applications/message_handler/test/run_inference_test.cpp
@@ -36,6 +36,8 @@
 #include "message_queue.hpp"
 #include "networks.hpp"
 #include "output.h"
+#include "test_assertions.hpp"
+#include "test_helpers.hpp"
 
 #include <mailbox.hpp>
 #include <mhu_dummy.hpp>
@@ -50,14 +52,6 @@
  * Defines
  ****************************************************************************/
 
-#define TEST_ASSERT(v)                                                                  \
-    do {                                                                                \
-        if (!(v)) {                                                                     \
-            fprintf(stderr, "%s:%d ERROR test failed: '%s'\n", __FILE__, __LINE__, #v); \
-            exit(1);                                                                    \
-        }                                                                               \
-    } while (0)
-
 // TensorArena static initialisation
 constexpr size_t arenaSize = TENSOR_ARENA_SIZE;
 
@@ -122,97 +116,6 @@
     process.run();
 }
 
-ethosu_core_network_info_req networkInfoIndexedRequest(uint64_t user_arg, uint32_t index) {
-    ethosu_core_network_info_req req = {user_arg,                   // user_arg
-                                        {                           // network
-                                         ETHOSU_CORE_NETWORK_INDEX, // type
-                                         {{
-                                             index, // index
-                                             0      // ignored padding of union
-                                         }}}};
-    return req;
-}
-
-ethosu_core_network_info_req networkInfoBufferRequest(uint64_t user_arg, unsigned char *ptr, uint32_t ptr_size) {
-    ethosu_core_network_info_req req = {user_arg,                    // user_arg
-                                        {                            // network
-                                         ETHOSU_CORE_NETWORK_BUFFER, // type
-                                         {{
-                                             reinterpret_cast<uint32_t>(ptr), // ptr
-                                             ptr_size                         // size
-                                         }}}};
-    return req;
-}
-
-ethosu_core_network_info_rsp networkInfoResponse(uint64_t user_arg) {
-    ethosu_core_network_info_rsp rsp = {
-        user_arg,               // user_arg
-        "Vela Optimised",       // description
-        1,                      // ifm_count
-        {/* not comparable */}, // ifm_sizes
-        1,                      // ofm_count
-        {/* not comparable */}, // ofm_sizes
-        ETHOSU_CORE_STATUS_OK   // status
-    };
-    return rsp;
-}
-
-ethosu_core_inference_req
-inferenceIndexedRequest(uint64_t user_arg, uint32_t index, uint8_t *data, uint32_t data_size) {
-    ethosu_core_inference_req req = {
-        user_arg, // user_arg
-        1,        // ifm_count
-        {         // ifm:
-         {
-             reinterpret_cast<uint32_t>(&inputData[0]), // ptr
-             sizeof(inputData)                          // size
-         }},
-        1, // ofm_count
-        {  // ofm
-         {
-             reinterpret_cast<uint32_t>(data), // ptr
-             data_size                         // size
-         }},
-        {                           // network
-         ETHOSU_CORE_NETWORK_INDEX, // type
-         {{
-             index, // index
-             0      // ignored padding of union
-         }}},
-        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
-        0                         // pmu_cycle_counter_enable
-    };
-    return req;
-}
-
-ethosu_core_inference_req
-inferenceBufferRequest(uint64_t user_arg, unsigned char *ptr, uint32_t ptr_size, uint8_t *data, uint32_t data_size) {
-    ethosu_core_inference_req req = {
-        user_arg, // user_arg
-        1,        // ifm_count
-        {         // ifm:
-         {
-             reinterpret_cast<uint32_t>(&inputData[0]), // ptr
-             sizeof(inputData)                          // size
-         }},
-        1, // ofm_count
-        {  // ofm
-         {
-             reinterpret_cast<uint32_t>(data), // ptr
-             data_size                         // size
-         }},
-        {                            // network
-         ETHOSU_CORE_NETWORK_BUFFER, // type
-         {{
-             reinterpret_cast<uint32_t>(ptr), // ptr
-             ptr_size                         // size
-         }}},
-        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
-        0                         // pmu_cycle_counter_enable
-    };
-    return req;
-}
-
 void testPing(MessageClient client) {
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_PING));
     TEST_ASSERT(client.waitAndReadOutputMessage(ETHOSU_CORE_MSG_PONG));
@@ -355,7 +258,8 @@
     const uint64_t fake_user_arg = 42;
     const uint32_t network_index = 0;
     uint8_t data[sizeof(expectedOutputData)];
-    ethosu_core_inference_req req = inferenceIndexedRequest(fake_user_arg, network_index, data, sizeof(data));
+    ethosu_core_inference_req req =
+        inferenceIndexedRequest(fake_user_arg, network_index, inputData, sizeof(inputData), data, sizeof(data));
     ethosu_core_inference_rsp rsp;
 
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
@@ -373,7 +277,8 @@
     const uint64_t fake_user_arg = 42;
     const uint32_t network_index = 1;
     uint8_t data[sizeof(expectedOutputData)];
-    ethosu_core_inference_req req = inferenceIndexedRequest(fake_user_arg, network_index, data, sizeof(data));
+    ethosu_core_inference_req req =
+        inferenceIndexedRequest(fake_user_arg, network_index, inputData, sizeof(inputData), data, sizeof(data));
     ethosu_core_inference_rsp rsp;
 
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
@@ -388,8 +293,8 @@
     uint32_t network_size        = sizeof(Model0::networkModelData);
     unsigned char *network_ptr   = Model0::networkModelData;
     uint8_t data[sizeof(expectedOutputData)];
-    ethosu_core_inference_req req =
-        inferenceBufferRequest(fake_user_arg, network_ptr, network_size, data, sizeof(data));
+    ethosu_core_inference_req req = inferenceBufferRequest(
+        fake_user_arg, network_ptr, network_size, inputData, sizeof(inputData), data, sizeof(data));
     ethosu_core_inference_rsp rsp;
 
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
@@ -408,8 +313,8 @@
     uint32_t network_size        = sizeof(Model0::networkModelData) / 4;
     unsigned char *network_ptr   = Model0::networkModelData + network_size;
     uint8_t data[sizeof(expectedOutputData)];
-    ethosu_core_inference_req req =
-        inferenceBufferRequest(fake_user_arg, network_ptr, network_size, data, sizeof(data));
+    ethosu_core_inference_req req = inferenceBufferRequest(
+        fake_user_arg, network_ptr, network_size, inputData, sizeof(inputData), data, sizeof(data));
     ethosu_core_inference_rsp rsp;
 
     TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
@@ -430,7 +335,8 @@
     for (int i = 0; i < runs; i++) {
         vTaskDelay(150);
 
-        req = inferenceIndexedRequest(fake_user_arg + i, network_index, data[i], sizeof(expectedOutputData));
+        req = inferenceIndexedRequest(
+            fake_user_arg + i, network_index, inputData, sizeof(inputData), data[i], sizeof(data[i]));
         TEST_ASSERT(client.sendInputMessage(ETHOSU_CORE_MSG_INFERENCE_REQ, req));
     }
 
diff --git a/applications/message_handler/test/test_assertions.hpp b/applications/message_handler/test/test_assertions.hpp
new file mode 100644
index 0000000..7c4cb5c
--- /dev/null
+++ b/applications/message_handler/test/test_assertions.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2022 Arm Limited.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ *
+ * 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.
+ */
+
+#ifndef TEST_ASSERTIONS_H
+#define TEST_ASSERTIONS_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+#define TEST_ASSERT(v)                                                                  \
+    do {                                                                                \
+        if (!(v)) {                                                                     \
+            fprintf(stderr, "%s:%d ERROR test failed: '%s'\n", __FILE__, __LINE__, #v); \
+            exit(1);                                                                    \
+        }                                                                               \
+    } while (0)
+
+#endif
diff --git a/applications/message_handler/test/test_helpers.hpp b/applications/message_handler/test/test_helpers.hpp
new file mode 100644
index 0000000..0440b58
--- /dev/null
+++ b/applications/message_handler/test/test_helpers.hpp
@@ -0,0 +1,131 @@
+
+/*
+ * Copyright (c) 2022 Arm Limited.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ *
+ * 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.
+ */
+
+#ifndef TEST_HELPERS_H
+#define TEST_HELPERS_H
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "ethosu_core_interface.h"
+
+namespace MessageHandler {
+
+ethosu_core_network_info_req networkInfoIndexedRequest(uint64_t user_arg, uint32_t index) {
+    ethosu_core_network_info_req req = {user_arg,                   // user_arg
+                                        {                           // network
+                                         ETHOSU_CORE_NETWORK_INDEX, // type
+                                         {{
+                                             index, // index
+                                             0      // ignored padding of union
+                                         }}}};
+    return req;
+}
+
+ethosu_core_network_info_req networkInfoBufferRequest(uint64_t user_arg, unsigned char *ptr, uint32_t ptr_size) {
+    ethosu_core_network_info_req req = {user_arg,                    // user_arg
+                                        {                            // network
+                                         ETHOSU_CORE_NETWORK_BUFFER, // type
+                                         {{
+                                             reinterpret_cast<uint32_t>(ptr), // ptr
+                                             ptr_size                         // size
+                                         }}}};
+    return req;
+}
+
+ethosu_core_network_info_rsp networkInfoResponse(uint64_t user_arg) {
+    ethosu_core_network_info_rsp rsp = {
+        user_arg,               // user_arg
+        "Vela Optimised",       // description
+        1,                      // ifm_count
+        {/* not comparable */}, // ifm_sizes
+        1,                      // ofm_count
+        {/* not comparable */}, // ofm_sizes
+        ETHOSU_CORE_STATUS_OK   // status
+    };
+    return rsp;
+}
+
+ethosu_core_inference_req inferenceIndexedRequest(uint64_t user_arg,
+                                                  uint32_t index,
+                                                  unsigned char *input_data,
+                                                  uint32_t input_data_size,
+                                                  uint8_t *output_data,
+                                                  uint32_t output_data_size) {
+    ethosu_core_inference_req req = {
+        user_arg, // user_arg
+        1,        // ifm_count
+        {         // ifm
+         {
+             reinterpret_cast<uint32_t>(input_data), // ptr
+             input_data_size                         // size
+         }},
+        1, // ofm_count
+        {  // ofm
+         {
+             reinterpret_cast<uint32_t>(output_data), // ptr
+             output_data_size                         // size
+         }},
+        {                           // network
+         ETHOSU_CORE_NETWORK_INDEX, // type
+         {{
+             index, // index
+             0      // ignored padding of union
+         }}},
+        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
+        0                         // pmu_cycle_counter_enable
+    };
+    return req;
+}
+
+ethosu_core_inference_req inferenceBufferRequest(uint64_t user_arg,
+                                                 unsigned char *ptr,
+                                                 uint32_t ptr_size,
+                                                 unsigned char *input_data,
+                                                 uint32_t input_data_size,
+                                                 uint8_t *output_data,
+                                                 uint32_t output_data_size) {
+    ethosu_core_inference_req req = {
+        user_arg, // user_arg
+        1,        // ifm_count
+        {         // ifm
+         {
+             reinterpret_cast<uint32_t>(input_data), // ptr
+             input_data_size                         // size
+         }},
+        1, // ofm_count
+        {  // ofm
+         {
+             reinterpret_cast<uint32_t>(output_data), // ptr
+             output_data_size                         // size
+         }},
+        {                            // network
+         ETHOSU_CORE_NETWORK_BUFFER, // type
+         {{
+             reinterpret_cast<uint32_t>(ptr), // ptr
+             ptr_size                         // size
+         }}},
+        {0, 0, 0, 0, 0, 0, 0, 0}, // pmu_event_config
+        0                         // pmu_cycle_counter_enable
+    };
+    return req;
+}
+} // namespace MessageHandler
+
+#endif