Add cancel inference tests

These tests are run with a special custom firmware

Change-Id: I0dfcae1fbaa1287d4b52fd8f6c802bebb5248d3b
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 37b7d8b..4b18862 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -20,18 +20,23 @@
 
 file(GLOB models LIST_DIRECTORIES true "${CORE_PLATFORM_PATH}/applications/baremetal/models/${ETHOSU_TARGET_NPU_CONFIG}/*")
 
-# Build executable
-foreach(model ${models})
-    get_filename_component(modelname ${model} NAME)
+function(add_test_executable testname)
+    # Build executable
+    foreach(model ${models})
+        get_filename_component(modelname ${model} NAME)
 
-    add_executable(lds_${modelname}_tests "main.cpp")
+        add_executable(lds_${testname}_${modelname} ${testname}.cpp)
 
-    target_include_directories(lds_${modelname}_tests PRIVATE
-        ${model})
+        target_include_directories(lds_${testname}_${modelname} PRIVATE
+            ${model})
 
-    # Link agains ethosu library
-    target_link_libraries(lds_${modelname}_tests PRIVATE ethosu)
+        # Link agains ethosu library
+        target_link_libraries(lds_${testname}_${modelname} PRIVATE ethosu)
 
-    # Install target
-    install(TARGETS lds_${modelname}_tests DESTINATION "bin")
-endforeach()
+        # Install target
+        install(TARGETS lds_${testname}_${modelname} DESTINATION "bin")
+    endforeach()
+endfunction()
+
+add_test_executable(run_inference_test)
+add_test_executable(cancel_inference_test)
diff --git a/tests/cancel_inference_test.cpp b/tests/cancel_inference_test.cpp
new file mode 100644
index 0000000..36514f4
--- /dev/null
+++ b/tests/cancel_inference_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#include <ethosu.hpp>
+#include <uapi/ethosu.h>
+
+#include <cstring>
+#include <iostream>
+#include <list>
+#include <memory>
+#include <sstream>
+#include <stdio.h>
+#include <string>
+#include <unistd.h>
+
+#include "input.h"
+#include "model.h"
+#include "output.h"
+#include "test_assertions.hpp"
+
+using namespace EthosU;
+
+namespace {
+
+int64_t defaultTimeout = 60000000000;
+
+void testCancelInference(const Device &device) {
+    try {
+        auto networkBuffer = std::make_shared<Buffer>(device, sizeof(networkModelData));
+        networkBuffer->resize(sizeof(networkModelData));
+        std::memcpy(networkBuffer->data(), networkModelData, sizeof(networkModelData));
+        auto network = std::make_shared<Network>(device, networkBuffer);
+
+        std::vector<std::shared_ptr<Buffer>> inputBuffers;
+        std::vector<std::shared_ptr<Buffer>> outputBuffers;
+
+        auto inputBuffer = std::make_shared<Buffer>(device, sizeof(inputData));
+        inputBuffer->resize(sizeof(inputData));
+        std::memcpy(inputBuffer->data(), inputData, sizeof(inputData));
+
+        inputBuffers.push_back(inputBuffer);
+        outputBuffers.push_back(std::make_shared<Buffer>(device, sizeof(expectedOutputData)));
+        std::vector<uint8_t> enabledCounters(Inference::getMaxPmuEventCounters());
+
+        auto inference = std::make_shared<Inference>(network,
+                                                     inputBuffers.begin(),
+                                                     inputBuffers.end(),
+                                                     outputBuffers.begin(),
+                                                     outputBuffers.end(),
+                                                     enabledCounters,
+                                                     false);
+
+        InferenceStatus status = inference->status();
+        TEST_ASSERT(status == InferenceStatus::RUNNING);
+
+        bool success = inference->cancel();
+        TEST_ASSERT(success);
+
+        status = inference->status();
+        TEST_ASSERT(status == InferenceStatus::ABORTED);
+
+        bool timedout = inference->wait(defaultTimeout);
+        TEST_ASSERT(!timedout);
+
+    } catch (std::exception &e) { throw TestFailureException("Inference run test: ", e.what()); }
+}
+
+void testRejectInference(const Device &device) {
+    try {
+        auto networkBuffer = std::make_shared<Buffer>(device, sizeof(networkModelData));
+        networkBuffer->resize(sizeof(networkModelData));
+        std::memcpy(networkBuffer->data(), networkModelData, sizeof(networkModelData));
+        auto network = std::make_shared<Network>(device, networkBuffer);
+
+        std::vector<std::shared_ptr<Buffer>> inputBuffers;
+        std::vector<std::shared_ptr<Buffer>> outputBuffers;
+
+        auto inputBuffer = std::make_shared<Buffer>(device, sizeof(inputData));
+        inputBuffer->resize(sizeof(inputData));
+        std::memcpy(inputBuffer->data(), inputData, sizeof(inputData));
+
+        inputBuffers.push_back(inputBuffer);
+        outputBuffers.push_back(std::make_shared<Buffer>(device, sizeof(expectedOutputData)));
+        std::vector<uint8_t> enabledCounters(Inference::getMaxPmuEventCounters());
+
+        std::shared_ptr<Inference> inferences[5];
+
+        for (int i = 0; i < 5; i++) {
+            inferences[i] = std::make_shared<Inference>(network,
+                                                        inputBuffers.begin(),
+                                                        inputBuffers.end(),
+                                                        outputBuffers.begin(),
+                                                        outputBuffers.end(),
+                                                        enabledCounters,
+                                                        false);
+
+            InferenceStatus status = inferences[i]->status();
+            TEST_ASSERT(status == InferenceStatus::RUNNING);
+        }
+
+        auto inference = std::make_shared<Inference>(network,
+                                                     inputBuffers.begin(),
+                                                     inputBuffers.end(),
+                                                     outputBuffers.begin(),
+                                                     outputBuffers.end(),
+                                                     enabledCounters,
+                                                     false);
+
+        bool timedout = inference->wait(defaultTimeout);
+        TEST_ASSERT(!timedout);
+
+        InferenceStatus status = inference->status();
+        TEST_ASSERT(status == InferenceStatus::REJECTED);
+
+        for (int i = 0; i < 5; i++) {
+
+            bool success = inferences[i]->cancel();
+            TEST_ASSERT(success);
+
+            InferenceStatus status = inferences[i]->status();
+            TEST_ASSERT(status == InferenceStatus::ABORTED);
+
+            bool timedout = inference->wait(defaultTimeout);
+            TEST_ASSERT(!timedout);
+        }
+    } catch (std::exception &e) { throw TestFailureException("Inference run test: ", e.what()); }
+}
+
+} // namespace
+
+int main() {
+    Device device;
+
+    try {
+        testCancelInference(device);
+        testRejectInference(device);
+    } catch (TestFailureException &e) {
+        std::cerr << "Test failure: " << e.what() << std::endl;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/tests/main.cpp b/tests/run_inference_test.cpp
similarity index 80%
rename from tests/main.cpp
rename to tests/run_inference_test.cpp
index 43300d3..94bb499 100644
--- a/tests/main.cpp
+++ b/tests/run_inference_test.cpp
@@ -31,15 +31,7 @@
 #include "input.h"
 #include "model.h"
 #include "output.h"
-
-#define TEST_ASSERT(v)                                                                             \
-    do {                                                                                           \
-        if (!(v)) {                                                                                \
-            throw TestFailureException(__FILE__, ":", __LINE__, " ERROR test failed: '", #v, "'"); \
-        }                                                                                          \
-    } while (0)
-
-#define FAIL() TEST_ASSERT(false)
+#include "test_assertions.hpp"
 
 using namespace EthosU;
 
@@ -47,32 +39,6 @@
 
 int64_t defaultTimeout = 60000000000;
 
-template <typename... Args>
-std::string string_format(std::ostringstream &stringStream) {
-    return stringStream.str();
-}
-
-template <typename T, typename... Args>
-std::string string_format(std::ostringstream &stringStream, T t, Args... args) {
-    stringStream << t;
-    return string_format(stringStream, args...);
-}
-
-class TestFailureException : public std::exception {
-public:
-    template <typename... Args>
-    TestFailureException(const char *msg, Args... args) {
-        std::ostringstream stringStream;
-        this->msg = string_format(stringStream, msg, args...);
-    }
-    const char *what() const throw() {
-        return msg.c_str();
-    }
-
-private:
-    std::string msg;
-};
-
 void testPing(const Device &device) {
     int r;
     try {
@@ -165,6 +131,12 @@
         bool timedout = inference->wait(defaultTimeout);
         TEST_ASSERT(!timedout);
 
+        InferenceStatus status = inference->status();
+        TEST_ASSERT(status == InferenceStatus::OK);
+
+        bool success = inference->cancel();
+        TEST_ASSERT(!success);
+
         TEST_ASSERT(std::memcmp(expectedOutputData, outputBuffers[0]->data(), sizeof(expectedOutputData)) == 0);
 
     } catch (std::exception &e) { throw TestFailureException("Inference run test: ", e.what()); }
diff --git a/tests/test_assertions.hpp b/tests/test_assertions.hpp
new file mode 100644
index 0000000..f90325f
--- /dev/null
+++ b/tests/test_assertions.hpp
@@ -0,0 +1,62 @@
+/*
+ * 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>
+
+namespace {
+template <typename... Args>
+std::string string_format(std::ostringstream &stringStream) {
+    return stringStream.str();
+}
+
+template <typename T, typename... Args>
+std::string string_format(std::ostringstream &stringStream, T t, Args... args) {
+    stringStream << t;
+    return string_format(stringStream, args...);
+}
+
+class TestFailureException : public std::exception {
+public:
+    template <typename... Args>
+    TestFailureException(const char *msg, Args... args) {
+        std::ostringstream stringStream;
+        this->msg = string_format(stringStream, msg, args...);
+    }
+    const char *what() const throw() {
+        return msg.c_str();
+    }
+
+private:
+    std::string msg;
+};
+} // namespace
+
+#define TEST_ASSERT(v)                                                                             \
+    do {                                                                                           \
+        if (!(v)) {                                                                                \
+            throw TestFailureException(__FILE__, ":", __LINE__, " ERROR test failed: '", #v, "'"); \
+        }                                                                                          \
+    } while (0)
+
+#define FAIL() TEST_ASSERT(false)
+
+#endif