// // Copyright © 2017 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #include #endif #if !defined(DYNAMIC_BACKEND_BUILD_DIR) #define DYNAMIC_BACKEND_BUILD_DIR fs::path("./") #endif static std::string g_TestDirCLI = "--dynamic-backend-build-dir"; static std::string g_TestBaseDir = "src/backends/backendsCommon/test/"; static std::string g_TestSharedObjectSubDir = "testSharedObject/"; static std::string g_TestDynamicBackendSubDir = "testDynamicBackend/"; static std::string g_TestSharedObjectFileName = "libTestSharedObject.so"; static std::string g_TestNoSharedObjectFileName = "libNoSharedObject.txt"; static std::string g_TestValidTestDynamicBackendFileName = "libValidTestDynamicBackend.so"; static std::string g_TestInvalidTestDynamicBackend1FileName = "libInvalidTestDynamicBackend1.so"; static std::string g_TestInvalidTestDynamicBackend2FileName = "libInvalidTestDynamicBackend2.so"; static std::string g_TestInvalidTestDynamicBackend3FileName = "libInvalidTestDynamicBackend3.so"; static std::string g_TestInvalidTestDynamicBackend4FileName = "libInvalidTestDynamicBackend4.so"; static std::string g_TestInvalidTestDynamicBackend5FileName = "libInvalidTestDynamicBackend5.so"; static std::string g_TestInvalidTestDynamicBackend6FileName = "libInvalidTestDynamicBackend6.so"; static std::string g_TestInvalidTestDynamicBackend7FileName = "libInvalidTestDynamicBackend7.so"; static std::string g_TestValidBackend2FileName = "Arm_TestValid2_backend.so"; static std::string g_TestValidBackend3FileName = "Arm_TestValid3_backend.so"; static std::string g_TestValidBackend4FileName = "Arm_TestValid4_backend.so"; static std::string g_TestValidBackend5FileName = "Arm_TestValid5_backend.so"; static std::string g_TestInvalidBackend8FileName = "Arm_TestInvalid8_backend.so"; static std::string g_TestInvalidBackend9FileName = "Arm_TestInvalid9_backend.so"; static std::string g_TestInvalidBackend10FileName = "Arm_TestInvalid10_backend.so"; static std::string g_TestInvalidBackend11FileName = "Arm_TestInvalid11_backend.so"; static std::string g_TestDynamicBackendsSubDir1 = "backendsTestPath1/"; static std::string g_TestDynamicBackendsSubDir2 = "backendsTestPath2/"; static std::string g_TestDynamicBackendsSubDir3 = "backendsTestPath3/"; static std::string g_TestDynamicBackendsSubDir4 = "backendsTestPath4/"; static std::string g_TestDynamicBackendsSubDir5 = "backendsTestPath5/"; static std::string g_TestDynamicBackendsSubDir6 = "backendsTestPath6/"; static std::string g_TestDynamicBackendsSubDir7 = "backendsTestPath7/"; static std::string g_TestDynamicBackendsSubDir8 = "backendsTestPath8/"; static std::string g_TestDynamicBackendsSubDir9 = "backendsTestPath9/"; static std::string g_DynamicBackendsBaseDir = "src/backends/dynamic"; static std::string g_ReferenceDynamicBackendSubDir = "reference/"; static std::string g_ReferenceBackendFileName = "Arm_CpuRef_backend.so"; // DynamicBackendUtils wrapper class used for testing (allows to directly invoke the protected methods) class TestDynamicBackendUtils : public armnn::DynamicBackendUtils { public: static bool IsBackendCompatibleTest(const armnn::BackendVersion& backendApiVersion, const armnn::BackendVersion& backendVersion) { return IsBackendCompatibleImpl(backendApiVersion, backendVersion); } static std::vector GetBackendPathsImplTest(const std::string& path) { return GetBackendPathsImpl(path); } static armnn::BackendIdSet RegisterDynamicBackendsImplTest( armnn::BackendRegistry& backendRegistry, const std::vector& dynamicBackends) { return RegisterDynamicBackendsImpl(backendRegistry, dynamicBackends); } }; // BackendRegistry wrapper class used for testing (swaps the underlying factory storage) class TestBackendRegistry : public armnn::BackendRegistry { public: TestBackendRegistry() : armnn::BackendRegistry() { Swap(armnn::BackendRegistryInstance(), m_TempStorage); } ~TestBackendRegistry() { Swap(armnn::BackendRegistryInstance(), m_TempStorage); } private: FactoryStorage m_TempStorage; }; #if defined(_MSC_VER) std::string GetUnitTestExecutablePath() { char buffer[MAX_PATH] = ""; GetModuleFileNameA(NULL, buffer, MAX_PATH); fs::path executablePath(buffer); return executablePath.parent_path(); } #else std::string GetUnitTestExecutablePath() { char buffer[PATH_MAX] = ""; if (readlink("/proc/self/exe", buffer, PATH_MAX) != -1) { fs::path executablePath(buffer); return executablePath.parent_path(); } return ""; } #endif std::string GetBasePath(const std::string& basePath) { using namespace fs; // What we're looking for here is the location of the UnitTests executable. // Fall back value of current directory. path programLocation = GetUnitTestExecutablePath(); if (!exists(programLocation)) { programLocation = DYNAMIC_BACKEND_BUILD_DIR; } // This is the base path from the build where the test libraries were built. path sharedObjectPath = programLocation.append(basePath); REQUIRE_MESSAGE(exists(sharedObjectPath), ("Base path for shared objects does not exist: " + sharedObjectPath.string())); return sharedObjectPath.string(); } std::string GetTestDirectoryBasePath() { return GetBasePath(g_TestBaseDir); } std::string GetDynamicBackendsBasePath() { return GetBasePath(g_DynamicBackendsBaseDir); } std::string GetTestSubDirectory(const std::string& subdir) { using namespace fs; std::string testDynamicBackendsBaseDir = GetTestDirectoryBasePath(); path testDynamicBackendsBasePath(testDynamicBackendsBaseDir); path testDynamicBackendsSubDir = testDynamicBackendsBasePath.append(subdir); // Do not check that the sub-directory exists because for testing reasons we may use non-existing paths return testDynamicBackendsSubDir.string(); } std::string GetTestSubDirectory(const std::string& basePath, const std::string& subdir) { using namespace fs; path testDynamicBackendsBasePath(basePath); path testDynamicBackendsSubDir = testDynamicBackendsBasePath.append(subdir); // Do not check that the sub-directory exists because for testing reasons we may use non-existing paths return testDynamicBackendsSubDir.string(); } std::string GetTestFilePath(const std::string& directory, const std::string& fileName) { using namespace fs; path directoryPath(directory); path fileNamePath = directoryPath.append(fileName); CHECK(exists(fileNamePath)); return fileNamePath.string(); } void OpenCloseHandleTestImpl() { using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestSharedObjectSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestSharedObjectFileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendUtils::CloseHandle(sharedObjectHandle); } void CloseInvalidHandleTestImpl() { using namespace armnn; // This calls must silently handle invalid handles and complete successfully (no segfaults, etc.) DynamicBackendUtils::CloseHandle(nullptr); } void OpenEmptyFileNameTestImpl() { using namespace armnn; void* sharedObjectHandle = nullptr; CHECK_THROWS_AS(sharedObjectHandle = DynamicBackendUtils::OpenHandle(""), RuntimeException); CHECK((sharedObjectHandle == nullptr)); } void OpenNotExistingFileTestImpl() { using namespace armnn; void* sharedObjectHandle = nullptr; CHECK_THROWS_AS(sharedObjectHandle = DynamicBackendUtils::OpenHandle("NotExistingFileName"), RuntimeException); CHECK((sharedObjectHandle == nullptr)); } void OpenNotSharedObjectTestImpl() { using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestSharedObjectSubDir); std::string notSharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestNoSharedObjectFileName); void* sharedObjectHandle = nullptr; CHECK_THROWS_AS(sharedObjectHandle = DynamicBackendUtils::OpenHandle(notSharedObjectFilePath), RuntimeException); CHECK((sharedObjectHandle == nullptr)); } void GetValidEntryPointTestImpl() { using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestSharedObjectSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestSharedObjectFileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); using TestFunctionType = int(*)(int); TestFunctionType testFunctionPointer = nullptr; CHECK_NOTHROW(testFunctionPointer = DynamicBackendUtils::GetEntryPoint(sharedObjectHandle, "TestFunction1")); CHECK((testFunctionPointer != nullptr)); CHECK(testFunctionPointer(7) == 7); DynamicBackendUtils::CloseHandle(sharedObjectHandle); } void GetNameMangledEntryPointTestImpl() { using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestSharedObjectSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestSharedObjectFileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); using TestFunctionType = int(*)(int); TestFunctionType testFunctionPointer = nullptr; CHECK_THROWS_AS(testFunctionPointer = DynamicBackendUtils::GetEntryPoint(sharedObjectHandle, "TestFunction2"), RuntimeException); CHECK((testFunctionPointer == nullptr)); DynamicBackendUtils::CloseHandle(sharedObjectHandle); } void GetNoExternEntryPointTestImpl() { using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestSharedObjectSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestSharedObjectFileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); using TestFunctionType = int(*)(int); TestFunctionType testFunctionPointer = nullptr; CHECK_THROWS_AS(testFunctionPointer = DynamicBackendUtils::GetEntryPoint(sharedObjectHandle, "TestFunction3"), RuntimeException); CHECK((testFunctionPointer == nullptr)); DynamicBackendUtils::CloseHandle(sharedObjectHandle); } void GetNotExistingEntryPointTestImpl() { using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestSharedObjectSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestSharedObjectFileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); using TestFunctionType = int(*)(int); TestFunctionType testFunctionPointer = nullptr; CHECK_THROWS_AS(testFunctionPointer = DynamicBackendUtils::GetEntryPoint(sharedObjectHandle, "TestFunction4"), RuntimeException); CHECK((testFunctionPointer == nullptr)); DynamicBackendUtils::CloseHandle(sharedObjectHandle); } void BackendVersioningTestImpl() { using namespace armnn; // The backend API version used for the tests BackendVersion backendApiVersion{ 2, 4 }; // Same backend and backend API versions are compatible with the backend API BackendVersion sameBackendVersion{ 2, 4 }; CHECK(sameBackendVersion == backendApiVersion); CHECK(sameBackendVersion <= backendApiVersion); CHECK(TestDynamicBackendUtils::IsBackendCompatibleTest(backendApiVersion, sameBackendVersion) == true); // Backend versions that differ from the backend API version by major revision are not compatible // with the backend API BackendVersion laterMajorBackendVersion{ 3, 4 }; CHECK(!(laterMajorBackendVersion == backendApiVersion)); CHECK(!(laterMajorBackendVersion <= backendApiVersion)); CHECK(TestDynamicBackendUtils::IsBackendCompatibleTest(backendApiVersion, laterMajorBackendVersion) == false); BackendVersion earlierMajorBackendVersion{ 1, 4 }; CHECK(!(earlierMajorBackendVersion == backendApiVersion)); CHECK(earlierMajorBackendVersion <= backendApiVersion); CHECK(TestDynamicBackendUtils::IsBackendCompatibleTest(backendApiVersion, earlierMajorBackendVersion) == false); // Backend versions with the same major revision but later minor revision than // the backend API version are not compatible with the backend API BackendVersion laterMinorBackendVersion{ 2, 5 }; CHECK(!(laterMinorBackendVersion == backendApiVersion)); CHECK(!(laterMinorBackendVersion <= backendApiVersion)); CHECK(TestDynamicBackendUtils::IsBackendCompatibleTest(backendApiVersion, laterMinorBackendVersion) == false); // Backend versions with the same major revision but earlier minor revision than // the backend API version are compatible with the backend API BackendVersion earlierMinorBackendVersion{ 2, 3 }; CHECK(!(earlierMinorBackendVersion == backendApiVersion)); CHECK(earlierMinorBackendVersion <= backendApiVersion); CHECK(TestDynamicBackendUtils::IsBackendCompatibleTest(backendApiVersion, earlierMinorBackendVersion) == true); } #if defined(ARMNNREF_ENABLED) void CreateValidDynamicBackendObjectTestImpl() { // Valid shared object handle // Correct name mangling // Correct interface // Correct backend implementation using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); // We expect this path to exists so we can load a valid dynamic backend. CHECK_MESSAGE(fs::exists(testSubDirectory), ("Base path for shared objects does not exist: " + testSubDirectory)); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestValidTestDynamicBackendFileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_NOTHROW(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle))); CHECK((dynamicBackend != nullptr)); BackendId dynamicBackendId; CHECK_NOTHROW(dynamicBackendId = dynamicBackend->GetBackendId()); CHECK((dynamicBackendId == "ValidTestDynamicBackend")); BackendVersion dynamicBackendVersion; CHECK_NOTHROW(dynamicBackendVersion = dynamicBackend->GetBackendVersion()); CHECK((dynamicBackendVersion == IBackendInternal::GetApiVersion())); IBackendInternalUniquePtr dynamicBackendInstance1; CHECK_NOTHROW(dynamicBackendInstance1 = dynamicBackend->GetBackend()); CHECK((dynamicBackendInstance1 != nullptr)); BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr; CHECK_NOTHROW(dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction()); CHECK((dynamicBackendFactoryFunction != nullptr)); IBackendInternalUniquePtr dynamicBackendInstance2; CHECK_NOTHROW(dynamicBackendInstance2 = dynamicBackendFactoryFunction()); CHECK((dynamicBackendInstance2 != nullptr)); CHECK((dynamicBackendInstance1->GetId() == "ValidTestDynamicBackend")); CHECK((dynamicBackendInstance2->GetId() == "ValidTestDynamicBackend")); } #endif void CreateDynamicBackendObjectInvalidHandleTestImpl() { // Invalid (null) shared object handle using namespace armnn; void* sharedObjectHandle = nullptr; DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), InvalidArgumentException); CHECK((dynamicBackend == nullptr)); } void CreateDynamicBackendObjectInvalidInterface1TestImpl() { // Valid shared object handle // Wrong (not C-style) name mangling using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend1FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), RuntimeException); CHECK((dynamicBackend == nullptr)); } void CreateDynamicBackendObjectInvalidInterface2TestImpl() { // Valid shared object handle // Correct name mangling // Wrong interface (missing GetBackendId()) using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend2FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), RuntimeException); CHECK((dynamicBackend == nullptr)); } void CreateDynamicBackendObjectInvalidInterface3TestImpl() { // Valid shared object handle // Correct name mangling // Wrong interface (missing GetVersion()) using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend3FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), RuntimeException); CHECK((dynamicBackend == nullptr)); } void CreateDynamicBackendObjectInvalidInterface4TestImpl() { // Valid shared object handle // Correct name mangling // Wrong interface (missing BackendFactory()) using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend4FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), RuntimeException); CHECK((dynamicBackend == nullptr)); } void CreateDynamicBackendObjectInvalidInterface5TestImpl() { // Valid shared object handle // Correct name mangling // Correct interface // Invalid (null) backend id returned by GetBackendId() using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend5FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), RuntimeException); CHECK((dynamicBackend == nullptr)); } void CreateDynamicBackendObjectInvalidInterface6TestImpl() { // Valid shared object handle // Correct name mangling // Correct interface // Invalid (null) backend instance returned by BackendFactory() using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend6FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_NOTHROW(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle))); CHECK((dynamicBackend != nullptr)); BackendId dynamicBackendId; CHECK_NOTHROW(dynamicBackendId = dynamicBackend->GetBackendId()); CHECK((dynamicBackendId == "InvalidTestDynamicBackend")); BackendVersion dynamicBackendVersion; CHECK_NOTHROW(dynamicBackendVersion = dynamicBackend->GetBackendVersion()); CHECK((dynamicBackendVersion >= BackendVersion({ 1, 0 }))); IBackendInternalUniquePtr dynamicBackendInstance1; CHECK_THROWS_AS(dynamicBackendInstance1 = dynamicBackend->GetBackend(), RuntimeException); CHECK((dynamicBackendInstance1 == nullptr)); BackendRegistry::FactoryFunction dynamicBackendFactoryFunction = nullptr; CHECK_NOTHROW(dynamicBackendFactoryFunction = dynamicBackend->GetFactoryFunction()); CHECK((dynamicBackendFactoryFunction != nullptr)); IBackendInternalUniquePtr dynamicBackendInstance2; CHECK_THROWS_AS(dynamicBackendInstance2 = dynamicBackendFactoryFunction(), RuntimeException); CHECK((dynamicBackendInstance2 == nullptr)); } void CreateDynamicBackendObjectInvalidInterface7TestImpl() { // Valid shared object handle // Correct name mangling // Correct interface // Invalid (incompatible backend API version) backend instance returned by BackendFactory() using namespace armnn; std::string testSubDirectory = GetTestSubDirectory(g_TestDynamicBackendSubDir); std::string sharedObjectFilePath = GetTestFilePath(testSubDirectory, g_TestInvalidTestDynamicBackend7FileName); void* sharedObjectHandle = nullptr; CHECK_NOTHROW(sharedObjectHandle = DynamicBackendUtils::OpenHandle(sharedObjectFilePath)); CHECK((sharedObjectHandle != nullptr)); DynamicBackendPtr dynamicBackend; CHECK_THROWS_AS(dynamicBackend.reset(new DynamicBackend(sharedObjectHandle)), RuntimeException); CHECK((dynamicBackend == nullptr)); } void GetBackendPathsTestImpl() { using namespace armnn; using namespace fs; // The test covers four directories: // /src/backends/backendsCommon/test/ // ├─ backendsTestPath1/ -> exists, contains files // ├─ backendsTestPath2/ -> exists, contains files // ├─ backendsTestPath3/ -> exists, but empty // └─ backendsTestPath4/ -> does not exist std::string subDir1 = GetTestSubDirectory(g_TestDynamicBackendsSubDir1); std::string subDir2 = GetTestSubDirectory(g_TestDynamicBackendsSubDir2); std::string subDir3 = GetTestSubDirectory(g_TestDynamicBackendsSubDir3); std::string subDir4 = GetTestSubDirectory(g_TestDynamicBackendsSubDir4); CHECK(exists(subDir1)); CHECK(exists(subDir2)); CHECK(exists(subDir3)); CHECK(!exists(subDir4)); // No path CHECK(TestDynamicBackendUtils::GetBackendPathsImplTest("").empty()); // Malformed path std::string malformedDir(subDir1 + "/" + subDir1); CHECK(TestDynamicBackendUtils::GetBackendPathsImplTest(malformedDir).size()==0); // Single valid path std::vector DynamicBackendPaths2 = TestDynamicBackendUtils::GetBackendPathsImplTest(subDir1); CHECK(DynamicBackendPaths2.size() == 1); CHECK(DynamicBackendPaths2[0] == subDir1); // Multiple equal and valid paths std::string multipleEqualDirs(subDir1 + ":" + subDir1); std::vector DynamicBackendPaths3 = TestDynamicBackendUtils::GetBackendPathsImplTest(multipleEqualDirs); CHECK(DynamicBackendPaths3.size() == 1); CHECK(DynamicBackendPaths3[0] == subDir1); // Multiple empty paths CHECK(TestDynamicBackendUtils::GetBackendPathsImplTest(":::").empty()); // Multiple valid paths std::string multipleValidPaths(subDir1 + ":" + subDir2 + ":" + subDir3); std::vector DynamicBackendPaths5 = TestDynamicBackendUtils::GetBackendPathsImplTest(multipleValidPaths); CHECK(DynamicBackendPaths5.size() == 3); CHECK(DynamicBackendPaths5[0] == subDir1); CHECK(DynamicBackendPaths5[1] == subDir2); CHECK(DynamicBackendPaths5[2] == subDir3); // Valid among empty paths std::string validAmongEmptyDirs("::" + subDir1 + ":"); std::vector DynamicBackendPaths6 = TestDynamicBackendUtils::GetBackendPathsImplTest(validAmongEmptyDirs); CHECK(DynamicBackendPaths6.size() == 1); CHECK(DynamicBackendPaths6[0] == subDir1); // Invalid among empty paths std::string invalidAmongEmptyDirs(":" + subDir4 + "::"); CHECK(TestDynamicBackendUtils::GetBackendPathsImplTest(invalidAmongEmptyDirs).empty()); // Valid, invalid and empty paths std::string validInvalidEmptyDirs(subDir1 + ":" + subDir4 + ":"); std::vector DynamicBackendPaths8 = TestDynamicBackendUtils::GetBackendPathsImplTest(validInvalidEmptyDirs); CHECK(DynamicBackendPaths8.size() == 1); CHECK(DynamicBackendPaths8[0] == subDir1); // Mix of duplicates of valid, invalid and empty paths std::string duplicateValidInvalidEmptyDirs(validInvalidEmptyDirs + ":" + validInvalidEmptyDirs + ":" + subDir2 + ":" + subDir2); std::vector DynamicBackendPaths9 = TestDynamicBackendUtils::GetBackendPathsImplTest(duplicateValidInvalidEmptyDirs); CHECK(DynamicBackendPaths9.size() == 2); CHECK(DynamicBackendPaths9[0] == subDir1); CHECK(DynamicBackendPaths9[1] == subDir2); } void GetBackendPathsOverrideTestImpl() { using namespace armnn; using namespace fs; std::string subDir1 = GetTestSubDirectory(g_TestDynamicBackendsSubDir1); std::string subDir4 = GetTestSubDirectory(g_TestDynamicBackendsSubDir4); CHECK(exists(subDir1)); CHECK(!exists(subDir4)); // Override with valid path std::vector validResult = DynamicBackendUtils::GetBackendPaths(subDir1); CHECK(validResult.size() == 1); CHECK(validResult[0] == subDir1); // Override with invalid path std::vector invalidResult = DynamicBackendUtils::GetBackendPaths(subDir4); CHECK(invalidResult.empty()); } void GetSharedObjectsTestImpl() { using namespace armnn; using namespace fs; // The test covers four directories: // /src/backends/backendsCommon/test/ // ├─ backendsTestPath1/ -> exists, contains files // ├─ backendsTestPath2/ -> exists, contains files // ├─ backendsTestPath3/ -> exists, but empty // └─ backendsTestPath4/ -> does not exist // // The test sub-directory backendsTestPath1/ contains the following test files: // // Arm_GpuAcc_backend.so -> valid (basic backend name) // Arm_GpuAcc_backend.so.1 -> valid (single field version number) // Arm_GpuAcc_backend.so.1.2 -> valid (multiple field version number) // Arm_GpuAcc_backend.so.1.2.3 -> valid (multiple field version number) // Arm_GpuAcc_backend.so.10.1.27 -> valid (Multiple digit version) // Arm_GpuAcc_backend.so.10.1.33. -> not valid (dot not followed by version number) // Arm_GpuAcc_backend.so.3.4..5 -> not valid (dot not followed by version number) // Arm_GpuAcc_backend.so.1,1.1 -> not valid (comma instead of dot in the version) // // Arm123_GpuAcc_backend.so -> valid (digits in vendor name are allowed) // Arm_GpuAcc456_backend.so -> valid (digits in backend id are allowed) // Arm%Co_GpuAcc_backend.so -> not valid (invalid character in vendor name) // Arm_Gpu.Acc_backend.so -> not valid (invalid character in backend id) // // GpuAcc_backend.so -> not valid (missing vendor name) // _GpuAcc_backend.so -> not valid (missing vendor name) // Arm__backend.so -> not valid (missing backend id) // Arm_GpuAcc.so -> not valid (missing "backend" at the end) // __backend.so -> not valid (missing vendor name and backend id) // __.so -> not valid (missing all fields) // // Arm_GpuAcc_backend -> not valid (missing at least ".so" at the end) // Arm_GpuAcc_backend_v1.2.so -> not valid (extra version info at the end) // // The test sub-directory backendsTestPath1/ contains the following test files: // // Arm_CpuAcc_backend.so -> valid (basic backend name) // Arm_CpuAcc_backend.so.1 -> Arm_CpuAcc_backend.so -> valid (symlink to valid backend file) // Arm_CpuAcc_backend.so.1.2 -> Arm_CpuAcc_backend.so.1 -> valid (symlink to valid symlink) // Arm_CpuAcc_backend.so.1.2.3 -> Arm_CpuAcc_backend.so.1.2 -> valid (symlink to valid symlink) // // Arm_no_backend.so -> nothing -> not valid (symlink resolves to non-existent file) // // Arm_GpuAcc_backend.so -> valid (but duplicated from backendsTestPath1/) std::string testDynamicBackendsSubDir1 = GetTestSubDirectory(g_TestDynamicBackendsSubDir1); std::string testDynamicBackendsSubDir2 = GetTestSubDirectory(g_TestDynamicBackendsSubDir2); std::string testDynamicBackendsSubDir3 = GetTestSubDirectory(g_TestDynamicBackendsSubDir3); std::string testDynamicBackendsSubDir4 = GetTestSubDirectory(g_TestDynamicBackendsSubDir4); CHECK(exists(testDynamicBackendsSubDir1)); CHECK(exists(testDynamicBackendsSubDir2)); CHECK(exists(testDynamicBackendsSubDir3)); CHECK(!exists(testDynamicBackendsSubDir4)); std::vector backendPaths { testDynamicBackendsSubDir1, testDynamicBackendsSubDir2, testDynamicBackendsSubDir3, testDynamicBackendsSubDir4 }; std::vector sharedObjects = DynamicBackendUtils::GetSharedObjects(backendPaths); std::vector expectedSharedObjects { path(testDynamicBackendsSubDir1 + "Arm123_GpuAcc_backend.so"), // Digits in vendor name are allowed path(testDynamicBackendsSubDir1 + "Arm_GpuAcc456_backend.so"), // Digits in backend id are allowed path(testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so"), // Basic backend name path(testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.1"), // Single field version number path(testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.1.2"), // Multiple field version number path(testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.1.2.3"), // Multiple field version number path(testDynamicBackendsSubDir1 + "Arm_GpuAcc_backend.so.10.1.27"), // Multiple digit version path(testDynamicBackendsSubDir2 + "Arm_CpuAcc_backend.so"), // Duplicate symlinks removed path(testDynamicBackendsSubDir2 + "Arm_GpuAcc_backend.so") // Duplicates on different paths are allowed }; CHECK(sharedObjects.size() == expectedSharedObjects.size()); CHECK(fs::equivalent(path(sharedObjects[0]), expectedSharedObjects[0])); CHECK(fs::equivalent(path(sharedObjects[1]), expectedSharedObjects[1])); CHECK(fs::equivalent(path(sharedObjects[2]), expectedSharedObjects[2])); CHECK(fs::equivalent(path(sharedObjects[3]), expectedSharedObjects[3])); CHECK(fs::equivalent(path(sharedObjects[4]), expectedSharedObjects[4])); CHECK(fs::equivalent(path(sharedObjects[5]), expectedSharedObjects[5])); CHECK(fs::equivalent(path(sharedObjects[6]), expectedSharedObjects[6])); CHECK(fs::equivalent(path(sharedObjects[7]), expectedSharedObjects[7])); CHECK(fs::equivalent(path(sharedObjects[8]), expectedSharedObjects[8])); } void CreateDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // The test covers four directories: // /src/backends/backendsCommon/test/ // ├─ backendsTestPath5/ -> exists, contains files // ├─ backendsTestPath6/ -> exists, contains files // ├─ backendsTestPath7/ -> exists, but empty // └─ backendsTestPath8/ -> does not exist // // The test sub-directory backendsTestPath5/ contains the following test files: // // Arm_TestValid2_backend.so -> valid (basic backend name) // Arm_TestValid3_backend.so -> valid (basic backend name) // Arm_TestInvalid8_backend.so -> not valid (invalid backend id) // // The test sub-directory backendsTestPath6/ contains the following test files: // // Arm_TestValid2_backend.so -> valid (but duplicated from backendsTestPath5/) // Arm_TestValid4_backend.so -> valid (it has a different filename, // but it has the same backend id of Arm_TestValid2_backend.so // and the same version) // Arm_TestValid5_backend.so -> valid (basic backend name) // Arm_TestInvalid9_backend.so -> not valid (it has a different filename, // but it has the same backend id of Arm_TestValid2_backend.so // and a version incompatible with the Backend API) std::string testDynamicBackendsSubDir5 = GetTestSubDirectory(g_TestDynamicBackendsSubDir5); std::string testDynamicBackendsSubDir6 = GetTestSubDirectory(g_TestDynamicBackendsSubDir6); std::string testDynamicBackendsSubDir7 = GetTestSubDirectory(g_TestDynamicBackendsSubDir7); std::string testDynamicBackendsSubDir8 = GetTestSubDirectory(g_TestDynamicBackendsSubDir8); CHECK(exists(testDynamicBackendsSubDir5)); CHECK(exists(testDynamicBackendsSubDir6)); CHECK(exists(testDynamicBackendsSubDir7)); CHECK(!exists(testDynamicBackendsSubDir8)); std::vector backendPaths { testDynamicBackendsSubDir5, testDynamicBackendsSubDir6, testDynamicBackendsSubDir7, testDynamicBackendsSubDir8 }; std::vector sharedObjects = DynamicBackendUtils::GetSharedObjects(backendPaths); std::vector dynamicBackends = DynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.size() == 5); CHECK((dynamicBackends[0] != nullptr)); CHECK((dynamicBackends[1] != nullptr)); CHECK((dynamicBackends[2] != nullptr)); CHECK((dynamicBackends[3] != nullptr)); CHECK((dynamicBackends[4] != nullptr)); // Duplicates are allowed here, they will be skipped later during the backend registration CHECK((dynamicBackends[0]->GetBackendId() == "TestValid2")); CHECK((dynamicBackends[1]->GetBackendId() == "TestValid3")); CHECK((dynamicBackends[2]->GetBackendId() == "TestValid2")); // From duplicate Arm_TestValid2_backend.so CHECK((dynamicBackends[3]->GetBackendId() == "TestValid2")); // From Arm_TestValid4_backend.so CHECK((dynamicBackends[4]->GetBackendId() == "TestValid5")); } void CreateDynamicBackendsNoPathsTestImpl() { using namespace armnn; std::vector dynamicBackends = DynamicBackendUtils::CreateDynamicBackends({}); CHECK(dynamicBackends.empty()); } void CreateDynamicBackendsAllInvalidTestImpl() { using namespace armnn; std::vector sharedObjects { "InvalidSharedObject1", "InvalidSharedObject2", "InvalidSharedObject3", }; std::vector dynamicBackends = DynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.empty()); } void CreateDynamicBackendsMixedTypesTestImpl() { using namespace armnn; using namespace fs; std::string testDynamicBackendsSubDir5 = GetTestSubDirectory(g_TestDynamicBackendsSubDir5); std::string testDynamicBackendsSubDir6 = GetTestSubDirectory(g_TestDynamicBackendsSubDir6); CHECK(exists(testDynamicBackendsSubDir5)); CHECK(exists(testDynamicBackendsSubDir6)); std::string testValidBackend2FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestValidBackend2FileName); std::string testInvalidBackend8FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestInvalidBackend8FileName); std::string testInvalidBackend9FilePath = GetTestFilePath(testDynamicBackendsSubDir6, g_TestInvalidBackend9FileName); CHECK(exists(testValidBackend2FilePath)); CHECK(exists(testInvalidBackend8FilePath)); CHECK(exists(testInvalidBackend9FilePath)); std::vector sharedObjects { testValidBackend2FilePath, // Arm_TestValid2_backend.so -> valid (basic backend name) testInvalidBackend8FilePath, // Arm_TestInvalid8_backend.so -> not valid (invalid backend id) testInvalidBackend9FilePath, // Arm_TestInvalid9_backend.so -> not valid (incompatible version) "InvalidSharedObject", // The file does not exist }; std::vector dynamicBackends = DynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.size() == 1); CHECK((dynamicBackends[0] != nullptr)); CHECK((dynamicBackends[0]->GetBackendId() == "TestValid2")); } #if defined(ARMNNREF_ENABLED) void RegisterSingleDynamicBackendTestImpl() { using namespace armnn; using namespace fs; // Register one valid dynamic backend // Dummy registry used for testing BackendRegistry backendRegistry; CHECK(backendRegistry.Size() == 0); std::string testDynamicBackendsSubDir5 = GetTestSubDirectory(g_TestDynamicBackendsSubDir5); CHECK(exists(testDynamicBackendsSubDir5)); std::string testValidBackend2FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestValidBackend2FileName); CHECK(exists(testValidBackend2FilePath)); std::vector sharedObjects{ testValidBackend2FilePath }; std::vector dynamicBackends = TestDynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.size() == 1); CHECK((dynamicBackends[0] != nullptr)); BackendId dynamicBackendId = dynamicBackends[0]->GetBackendId(); CHECK((dynamicBackendId == "TestValid2")); BackendVersion dynamicBackendVersion = dynamicBackends[0]->GetBackendVersion(); CHECK(TestDynamicBackendUtils::IsBackendCompatible(dynamicBackendVersion)); BackendIdSet registeredBackendIds = TestDynamicBackendUtils::RegisterDynamicBackendsImplTest(backendRegistry, dynamicBackends); CHECK(backendRegistry.Size() == 1); CHECK(registeredBackendIds.size() == 1); BackendIdSet backendIds = backendRegistry.GetBackendIds(); CHECK(backendIds.size() == 1); CHECK((backendIds.find(dynamicBackendId) != backendIds.end())); CHECK((registeredBackendIds.find(dynamicBackendId) != registeredBackendIds.end())); auto dynamicBackendFactoryFunction = backendRegistry.GetFactory(dynamicBackendId); CHECK((dynamicBackendFactoryFunction != nullptr)); IBackendInternalUniquePtr dynamicBackend = dynamicBackendFactoryFunction(); CHECK((dynamicBackend != nullptr)); CHECK((dynamicBackend->GetId() == dynamicBackendId)); } void RegisterMultipleDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // Register many valid dynamic backends std::string testDynamicBackendsSubDir5 = GetTestSubDirectory(g_TestDynamicBackendsSubDir5); std::string testDynamicBackendsSubDir6 = GetTestSubDirectory(g_TestDynamicBackendsSubDir6); CHECK(exists(testDynamicBackendsSubDir5)); CHECK(exists(testDynamicBackendsSubDir6)); std::string testValidBackend2FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestValidBackend2FileName); std::string testValidBackend3FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestValidBackend3FileName); std::string testValidBackend5FilePath = GetTestFilePath(testDynamicBackendsSubDir6, g_TestValidBackend5FileName); CHECK(exists(testValidBackend2FilePath)); CHECK(exists(testValidBackend3FilePath)); CHECK(exists(testValidBackend5FilePath)); std::vector sharedObjects { testValidBackend2FilePath, testValidBackend3FilePath, testValidBackend5FilePath }; std::vector dynamicBackends = TestDynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.size() == 3); CHECK((dynamicBackends[0] != nullptr)); CHECK((dynamicBackends[1] != nullptr)); CHECK((dynamicBackends[2] != nullptr)); BackendId dynamicBackendId1 = dynamicBackends[0]->GetBackendId(); BackendId dynamicBackendId2 = dynamicBackends[1]->GetBackendId(); BackendId dynamicBackendId3 = dynamicBackends[2]->GetBackendId(); CHECK((dynamicBackendId1 == "TestValid2")); CHECK((dynamicBackendId2 == "TestValid3")); CHECK((dynamicBackendId3 == "TestValid5")); for (size_t i = 0; i < dynamicBackends.size(); i++) { BackendVersion dynamicBackendVersion = dynamicBackends[i]->GetBackendVersion(); CHECK(TestDynamicBackendUtils::IsBackendCompatible(dynamicBackendVersion)); } // Dummy registry used for testing BackendRegistry backendRegistry; CHECK(backendRegistry.Size() == 0); BackendIdSet registeredBackendIds = TestDynamicBackendUtils::RegisterDynamicBackendsImplTest(backendRegistry, dynamicBackends); CHECK(backendRegistry.Size() == 3); CHECK(registeredBackendIds.size() == 3); BackendIdSet backendIds = backendRegistry.GetBackendIds(); CHECK(backendIds.size() == 3); CHECK((backendIds.find(dynamicBackendId1) != backendIds.end())); CHECK((backendIds.find(dynamicBackendId2) != backendIds.end())); CHECK((backendIds.find(dynamicBackendId3) != backendIds.end())); CHECK((registeredBackendIds.find(dynamicBackendId1) != registeredBackendIds.end())); CHECK((registeredBackendIds.find(dynamicBackendId2) != registeredBackendIds.end())); CHECK((registeredBackendIds.find(dynamicBackendId3) != registeredBackendIds.end())); for (size_t i = 0; i < dynamicBackends.size(); i++) { BackendId dynamicBackendId = dynamicBackends[i]->GetBackendId(); auto dynamicBackendFactoryFunction = backendRegistry.GetFactory(dynamicBackendId); CHECK((dynamicBackendFactoryFunction != nullptr)); IBackendInternalUniquePtr dynamicBackend = dynamicBackendFactoryFunction(); CHECK((dynamicBackend != nullptr)); CHECK((dynamicBackend->GetId() == dynamicBackendId)); } } void RegisterMixedDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // The test covers five directories: // /src/backends/backendsCommon/test/ // ├─ backendsTestPath5/ -> exists, contains files // ├─ backendsTestPath6/ -> exists, contains files // ├─ backendsTestPath7/ -> exists, but empty // ├─ backendsTestPath8/ -> does not exist // └─ backendsTestPath9/ -> exists, contains files // // The test sub-directory backendsTestPath5/ contains the following test files: // // Arm_TestValid2_backend.so -> valid (basic backend name) // Arm_TestValid3_backend.so -> valid (basic backend name) // Arm_TestInvalid8_backend.so -> not valid (invalid backend id) // // The test sub-directory backendsTestPath6/ contains the following test files: // // Arm_TestValid2_backend.so -> valid (but duplicated from backendsTestPath5/) // Arm_TestValid4_backend.so -> valid (it has a different filename, // but it has the same backend id of Arm_TestValid2_backend.so // and the same version) // Arm_TestValid5_backend.so -> valid (basic backend name) // Arm_TestInvalid9_backend.so -> not valid (it has a different filename, // but it has the same backend id of Arm_TestValid2_backend.so // and a version incompatible with the Backend API) // // The test sub-directory backendsTestPath9/ contains the following test files: // // Arm_TestInvalid10_backend.so -> not valid (empty backend id) // Arm_TestInvalid11_backend.so -> not valid ("Unknown" backend id) std::string testDynamicBackendsSubDir5 = GetTestSubDirectory(g_TestDynamicBackendsSubDir5); std::string testDynamicBackendsSubDir6 = GetTestSubDirectory(g_TestDynamicBackendsSubDir6); std::string testDynamicBackendsSubDir7 = GetTestSubDirectory(g_TestDynamicBackendsSubDir7); std::string testDynamicBackendsSubDir8 = GetTestSubDirectory(g_TestDynamicBackendsSubDir8); std::string testDynamicBackendsSubDir9 = GetTestSubDirectory(g_TestDynamicBackendsSubDir9); CHECK(exists(testDynamicBackendsSubDir5)); CHECK(exists(testDynamicBackendsSubDir6)); CHECK(exists(testDynamicBackendsSubDir7)); CHECK(!exists(testDynamicBackendsSubDir8)); CHECK(exists(testDynamicBackendsSubDir9)); std::string testValidBackend2FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestValidBackend2FileName); std::string testValidBackend3FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestValidBackend3FileName); std::string testValidBackend2DupFilePath = GetTestFilePath(testDynamicBackendsSubDir6, g_TestValidBackend2FileName); std::string testValidBackend4FilePath = GetTestFilePath(testDynamicBackendsSubDir6, g_TestValidBackend4FileName); std::string testValidBackend5FilePath = GetTestFilePath(testDynamicBackendsSubDir6, g_TestValidBackend5FileName); std::string testInvalidBackend8FilePath = GetTestFilePath(testDynamicBackendsSubDir5, g_TestInvalidBackend8FileName); std::string testInvalidBackend9FilePath = GetTestFilePath(testDynamicBackendsSubDir6, g_TestInvalidBackend9FileName); std::string testInvalidBackend10FilePath = GetTestFilePath(testDynamicBackendsSubDir9, g_TestInvalidBackend10FileName); std::string testInvalidBackend11FilePath = GetTestFilePath(testDynamicBackendsSubDir9, g_TestInvalidBackend11FileName); CHECK(exists(testValidBackend2FilePath)); CHECK(exists(testValidBackend3FilePath)); CHECK(exists(testValidBackend2DupFilePath)); CHECK(exists(testValidBackend4FilePath)); CHECK(exists(testValidBackend5FilePath)); CHECK(exists(testInvalidBackend8FilePath)); CHECK(exists(testInvalidBackend9FilePath)); CHECK(exists(testInvalidBackend10FilePath)); CHECK(exists(testInvalidBackend11FilePath)); std::vector sharedObjects { testValidBackend2FilePath, testValidBackend3FilePath, testValidBackend2DupFilePath, testValidBackend4FilePath, testValidBackend5FilePath, testInvalidBackend8FilePath, testInvalidBackend9FilePath, testInvalidBackend10FilePath, testInvalidBackend11FilePath, "InvalidSharedObject" }; std::vector dynamicBackends = TestDynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.size() == 7); CHECK((dynamicBackends[0] != nullptr)); CHECK((dynamicBackends[1] != nullptr)); CHECK((dynamicBackends[2] != nullptr)); CHECK((dynamicBackends[3] != nullptr)); CHECK((dynamicBackends[4] != nullptr)); CHECK((dynamicBackends[5] != nullptr)); CHECK((dynamicBackends[6] != nullptr)); BackendId dynamicBackendId1 = dynamicBackends[0]->GetBackendId(); BackendId dynamicBackendId2 = dynamicBackends[1]->GetBackendId(); BackendId dynamicBackendId3 = dynamicBackends[2]->GetBackendId(); BackendId dynamicBackendId4 = dynamicBackends[3]->GetBackendId(); BackendId dynamicBackendId5 = dynamicBackends[4]->GetBackendId(); BackendId dynamicBackendId6 = dynamicBackends[5]->GetBackendId(); BackendId dynamicBackendId7 = dynamicBackends[6]->GetBackendId(); CHECK((dynamicBackendId1 == "TestValid2")); CHECK((dynamicBackendId2 == "TestValid3")); CHECK((dynamicBackendId3 == "TestValid2")); // From duplicate Arm_TestValid2_backend.so CHECK((dynamicBackendId4 == "TestValid2")); // From Arm_TestValid4_backend.so CHECK((dynamicBackendId5 == "TestValid5")); CHECK((dynamicBackendId6 == "")); CHECK((dynamicBackendId7 == "Unknown")); for (size_t i = 0; i < dynamicBackends.size(); i++) { BackendVersion dynamicBackendVersion = dynamicBackends[i]->GetBackendVersion(); CHECK(TestDynamicBackendUtils::IsBackendCompatible(dynamicBackendVersion)); } // Dummy registry used for testing BackendRegistry backendRegistry; CHECK(backendRegistry.Size() == 0); std::vector expectedRegisteredbackendIds { "TestValid2", "TestValid3", "TestValid5" }; BackendIdSet registeredBackendIds = TestDynamicBackendUtils::RegisterDynamicBackendsImplTest(backendRegistry, dynamicBackends); CHECK(backendRegistry.Size() == expectedRegisteredbackendIds.size()); CHECK(registeredBackendIds.size() == expectedRegisteredbackendIds.size()); BackendIdSet backendIds = backendRegistry.GetBackendIds(); CHECK(backendIds.size() == expectedRegisteredbackendIds.size()); for (const BackendId& expectedRegisteredbackendId : expectedRegisteredbackendIds) { CHECK((backendIds.find(expectedRegisteredbackendId) != backendIds.end())); CHECK((registeredBackendIds.find(expectedRegisteredbackendId) != registeredBackendIds.end())); auto dynamicBackendFactoryFunction = backendRegistry.GetFactory(expectedRegisteredbackendId); CHECK((dynamicBackendFactoryFunction != nullptr)); IBackendInternalUniquePtr dynamicBackend = dynamicBackendFactoryFunction(); CHECK((dynamicBackend != nullptr)); CHECK((dynamicBackend->GetId() == expectedRegisteredbackendId)); } } #endif void RegisterMultipleInvalidDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // Try to register many invalid dynamic backends // The test covers one directory: // /src/backends/backendsCommon/test/ // └─ backendsTestPath9/ -> exists, contains files // // The test sub-directory backendsTestPath9/ contains the following test files: // // Arm_TestInvalid10_backend.so -> not valid (invalid backend id) // Arm_TestInvalid11_backend.so -> not valid (invalid backend id) std::string testDynamicBackendsSubDir9 = GetTestSubDirectory(g_TestDynamicBackendsSubDir9); CHECK(exists(testDynamicBackendsSubDir9)); std::string testInvalidBackend10FilePath = GetTestFilePath(testDynamicBackendsSubDir9, g_TestInvalidBackend10FileName); std::string testInvalidBackend11FilePath = GetTestFilePath(testDynamicBackendsSubDir9, g_TestInvalidBackend11FileName); CHECK(exists(testInvalidBackend10FilePath)); CHECK(exists(testInvalidBackend11FilePath)); std::vector sharedObjects { testInvalidBackend10FilePath, testInvalidBackend11FilePath, "InvalidSharedObject" }; std::vector dynamicBackends = TestDynamicBackendUtils::CreateDynamicBackends(sharedObjects); CHECK(dynamicBackends.size() == 2); CHECK((dynamicBackends[0] != nullptr)); CHECK((dynamicBackends[1] != nullptr)); BackendId dynamicBackendId1 = dynamicBackends[0]->GetBackendId(); BackendId dynamicBackendId2 = dynamicBackends[1]->GetBackendId(); CHECK((dynamicBackendId1 == "")); CHECK((dynamicBackendId2 == "Unknown")); for (size_t i = 0; i < dynamicBackends.size(); i++) { BackendVersion dynamicBackendVersion = dynamicBackends[i]->GetBackendVersion(); CHECK(TestDynamicBackendUtils::IsBackendCompatible(dynamicBackendVersion)); } // Dummy registry used for testing BackendRegistry backendRegistry; CHECK(backendRegistry.Size() == 0); // Check that no dynamic backend got registered BackendIdSet registeredBackendIds = TestDynamicBackendUtils::RegisterDynamicBackendsImplTest(backendRegistry, dynamicBackends); CHECK(backendRegistry.Size() == 0); CHECK(registeredBackendIds.empty()); } #if !defined(ARMNN_DYNAMIC_BACKEND_ENABLED) void RuntimeEmptyTestImpl() { using namespace armnn; // Swapping the backend registry storage for testing TestBackendRegistry testBackendRegistry; const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() == 0); IRuntime::CreationOptions creationOptions; IRuntimePtr runtime = IRuntime::Create(creationOptions); const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.empty()); CHECK(backendRegistry.Size() == 0); } #endif void RuntimeDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // Swapping the backend registry storage for testing TestBackendRegistry testBackendRegistry; // This directory contains valid and invalid backends std::string testDynamicBackendsSubDir5 = GetTestSubDirectory(g_TestDynamicBackendsSubDir5); CHECK(exists(testDynamicBackendsSubDir5)); // Using the path override in CreationOptions to load some test dynamic backends IRuntime::CreationOptions creationOptions; creationOptions.m_DynamicBackendsPath = testDynamicBackendsSubDir5; IRuntimePtr runtime = IRuntime::Create(creationOptions); std::vector expectedRegisteredbackendIds { "TestValid2", "TestValid3" }; const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() == expectedRegisteredbackendIds.size()); BackendIdSet backendIds = backendRegistry.GetBackendIds(); for (const BackendId& expectedRegisteredbackendId : expectedRegisteredbackendIds) { CHECK((backendIds.find(expectedRegisteredbackendId) != backendIds.end())); } const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.size() == expectedRegisteredbackendIds.size()); for (const BackendId& expectedRegisteredbackendId : expectedRegisteredbackendIds) { CHECK((supportedBackendIds.find(expectedRegisteredbackendId) != supportedBackendIds.end())); } } void RuntimeDuplicateDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // Swapping the backend registry storage for testing TestBackendRegistry testBackendRegistry; // This directory contains valid, invalid and duplicate backends std::string testDynamicBackendsSubDir6 = GetTestSubDirectory(g_TestDynamicBackendsSubDir6); CHECK(exists(testDynamicBackendsSubDir6)); // Using the path override in CreationOptions to load some test dynamic backends IRuntime::CreationOptions creationOptions; creationOptions.m_DynamicBackendsPath = testDynamicBackendsSubDir6; IRuntimePtr runtime = IRuntime::Create(creationOptions); std::vector expectedRegisteredbackendIds { "TestValid2", "TestValid5" }; const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() == expectedRegisteredbackendIds.size()); BackendIdSet backendIds = backendRegistry.GetBackendIds(); for (const BackendId& expectedRegisteredbackendId : expectedRegisteredbackendIds) { CHECK((backendIds.find(expectedRegisteredbackendId) != backendIds.end())); } const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.size() == expectedRegisteredbackendIds.size()); for (const BackendId& expectedRegisteredbackendId : expectedRegisteredbackendIds) { CHECK((supportedBackendIds.find(expectedRegisteredbackendId) != supportedBackendIds.end())); } } void RuntimeInvalidDynamicBackendsTestImpl() { using namespace armnn; using namespace fs; // Swapping the backend registry storage for testing TestBackendRegistry testBackendRegistry; // This directory contains only invalid backends std::string testDynamicBackendsSubDir9 = GetTestSubDirectory(g_TestDynamicBackendsSubDir9); CHECK(exists(testDynamicBackendsSubDir9)); // Using the path override in CreationOptions to load some test dynamic backends IRuntime::CreationOptions creationOptions; creationOptions.m_DynamicBackendsPath = testDynamicBackendsSubDir9; IRuntimePtr runtime = IRuntime::Create(creationOptions); const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() == 0); const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.empty()); } void RuntimeInvalidOverridePathTestImpl() { using namespace armnn; // Swapping the backend registry storage for testing TestBackendRegistry testBackendRegistry; // Using the path override in CreationOptions to load some test dynamic backends IRuntime::CreationOptions creationOptions; creationOptions.m_DynamicBackendsPath = "InvalidPath"; IRuntimePtr runtime = IRuntime::Create(creationOptions); const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() == 0); const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.empty()); } #if defined(ARMNNREF_ENABLED) // This test unit needs the reference backend, it's not available if the reference backend is not built void CreateReferenceDynamicBackendTestImpl() { using namespace armnn; using namespace fs; // Swapping the backend registry storage for testing TestBackendRegistry testBackendRegistry; // This directory contains the reference dynamic backend std::string dynamicBackendsBaseDir = GetDynamicBackendsBasePath(); std::string referenceDynamicBackendSubDir = GetTestSubDirectory(dynamicBackendsBaseDir, g_ReferenceDynamicBackendSubDir); CHECK(exists(referenceDynamicBackendSubDir)); // Check that the reference dynamic backend file exists std::string referenceBackendFilePath = GetTestFilePath(referenceDynamicBackendSubDir, g_ReferenceBackendFileName); CHECK(exists(referenceBackendFilePath)); // Using the path override in CreationOptions to load the reference dynamic backend IRuntime::CreationOptions creationOptions; creationOptions.m_DynamicBackendsPath = referenceDynamicBackendSubDir; IRuntimePtr runtime = IRuntime::Create(creationOptions); const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() == 1); BackendIdSet backendIds = backendRegistry.GetBackendIds(); CHECK((backendIds.find("CpuRef") != backendIds.end())); const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.size() == 1); CHECK((supportedBackendIds.find("CpuRef") != supportedBackendIds.end())); // Get the factory function auto referenceDynamicBackendFactoryFunction = backendRegistry.GetFactory("CpuRef"); CHECK((referenceDynamicBackendFactoryFunction != nullptr)); // Use the factory function to create an instance of the reference backend IBackendInternalUniquePtr referenceDynamicBackend = referenceDynamicBackendFactoryFunction(); CHECK((referenceDynamicBackend != nullptr)); CHECK((referenceDynamicBackend->GetId() == "CpuRef")); // Test the backend instance by querying the layer support IBackendInternal::ILayerSupportSharedPtr referenceLayerSupport = referenceDynamicBackend->GetLayerSupport(); CHECK((referenceLayerSupport != nullptr)); TensorShape inputShape { 1, 16, 16, 16 }; TensorShape outputShape{ 1, 16, 16, 16 }; TensorShape weightShape{ 16, 1, 1, 16 }; TensorInfo inputInfo (inputShape, DataType::Float32); TensorInfo outputInfo(outputShape, DataType::Float32); TensorInfo weightInfo(weightShape, DataType::Float32); Convolution2dDescriptor convolution2dDescriptor; std::vector infos = {inputInfo, outputInfo, weightInfo, TensorInfo()}; bool referenceConvolution2dSupported = referenceLayerSupport->IsLayerSupported(LayerType::Convolution2d, infos, convolution2dDescriptor); CHECK(referenceConvolution2dSupported); // Test the backend instance by creating a workload IBackendInternal::IWorkloadFactoryPtr referenceWorkloadFactory = referenceDynamicBackend->CreateWorkloadFactory(); CHECK((referenceWorkloadFactory != nullptr)); // Create dummy settings for the workload Convolution2dQueueDescriptor convolution2dQueueDescriptor; WorkloadInfo workloadInfo { { inputInfo, weightInfo }, { outputInfo } }; convolution2dQueueDescriptor.m_Inputs.push_back(nullptr); auto weights = std::make_unique(weightInfo); convolution2dQueueDescriptor.m_Weight = weights.get(); // Create a convolution workload with the dummy settings auto workload = referenceWorkloadFactory->CreateWorkload(LayerType::Convolution2d, convolution2dQueueDescriptor, workloadInfo); CHECK((workload != nullptr)); CHECK(workload.get() == PolymorphicDowncast(workload.get())); } #endif #if defined(SAMPLE_DYNAMIC_BACKEND_ENABLED) void CheckSampleDynamicBackendLoaded() { using namespace armnn; // At this point we expect DYNAMIC_BACKEND_PATHS to include a path to where libArm_SampleDynamic_backend.so is. // If it hasn't been loaded there's no point continuing with the rest of the tests. BackendIdSet backendIds = BackendRegistryInstance().GetBackendIds(); if (backendIds.find("SampleDynamic") == backendIds.end()) { std::string message = "The SampleDynamic backend has not been loaded. This may be a build configuration error. " "Ensure a DYNAMIC_BACKEND_PATHS was set at compile time to the location of " "libArm_SampleDynamic_backend.so. " "To disable this test recompile with: -DSAMPLE_DYNAMIC_BACKEND_ENABLED=0"; FAIL(message); } } void CreateSampleDynamicBackendTestImpl() { using namespace armnn; // Using the path override in CreationOptions to load the reference dynamic backend IRuntime::CreationOptions creationOptions; IRuntimePtr runtime = IRuntime::Create(creationOptions); const BackendRegistry& backendRegistry = BackendRegistryInstance(); CHECK(backendRegistry.Size() >= 1); CheckSampleDynamicBackendLoaded(); const DeviceSpec& deviceSpec = *PolymorphicDowncast(&runtime->GetDeviceSpec()); BackendIdSet supportedBackendIds = deviceSpec.GetSupportedBackends(); CHECK(supportedBackendIds.size()>= 1); CHECK((supportedBackendIds.find("SampleDynamic") != supportedBackendIds.end())); // Get the factory function auto sampleDynamicBackendFactoryFunction = backendRegistry.GetFactory("SampleDynamic"); CHECK((sampleDynamicBackendFactoryFunction != nullptr)); // Use the factory function to create an instance of the dynamic backend IBackendInternalUniquePtr sampleDynamicBackend = sampleDynamicBackendFactoryFunction(); CHECK((sampleDynamicBackend != nullptr)); CHECK((sampleDynamicBackend->GetId() == "SampleDynamic")); // Test the backend instance by querying the layer support IBackendInternal::ILayerSupportSharedPtr sampleLayerSupport = sampleDynamicBackend->GetLayerSupport(); CHECK((sampleLayerSupport != nullptr)); TensorShape inputShape { 1, 16, 16, 16 }; TensorShape outputShape{ 1, 16, 16, 16 }; TensorShape weightShape{ 16, 1, 1, 16 }; TensorInfo inputInfo (inputShape, DataType::Float32); TensorInfo outputInfo(outputShape, DataType::Float32); TensorInfo weightInfo(weightShape, DataType::Float32); Convolution2dDescriptor convolution2dDescriptor; std::vector infos = {inputInfo, outputInfo, weightInfo, TensorInfo()}; bool sampleConvolution2dSupported = sampleLayerSupport->IsLayerSupported(LayerType::Convolution2d, infos, convolution2dDescriptor); CHECK(!sampleConvolution2dSupported); // Test the backend instance by creating a workload IBackendInternal::IWorkloadFactoryPtr sampleWorkloadFactory = sampleDynamicBackend->CreateWorkloadFactory(); CHECK((sampleWorkloadFactory != nullptr)); // Create dummy settings for the workload AdditionQueueDescriptor additionQueueDescriptor; WorkloadInfo workloadInfo { { inputInfo, inputInfo }, { outputInfo } }; // Create a addition workload auto workload = sampleWorkloadFactory->CreateWorkload(LayerType::Addition, additionQueueDescriptor, workloadInfo); CHECK((workload != nullptr)); } void SampleDynamicBackendEndToEndTestImpl() { using namespace armnn; // Create runtime in which test will run IRuntime::CreationOptions options; IRuntimePtr runtime(IRuntime::Create(options)); CheckSampleDynamicBackendLoaded(); // Builds up the structure of the network. INetworkPtr net(INetwork::Create()); IConnectableLayer* input0 = net->AddInputLayer(0); IConnectableLayer* input1 = net->AddInputLayer(1); IConnectableLayer* add = net->AddAdditionLayer(); IConnectableLayer* output = net->AddOutputLayer(0); input0->GetOutputSlot(0).Connect(add->GetInputSlot(0)); input1->GetOutputSlot(0).Connect(add->GetInputSlot(1)); add->GetOutputSlot(0).Connect(output->GetInputSlot(0)); TensorInfo tensorInfo(TensorShape({2, 1}), DataType::Float32); input0->GetOutputSlot(0).SetTensorInfo(tensorInfo); input1->GetOutputSlot(0).SetTensorInfo(tensorInfo); add->GetOutputSlot(0).SetTensorInfo(tensorInfo); // optimize the network IOptimizedNetworkPtr optNet = Optimize(*net, {"SampleDynamic"}, runtime->GetDeviceSpec()); // Loads it into the runtime. NetworkId netId; runtime->LoadNetwork(netId, std::move(optNet)); std::vector input0Data{ 5.0f, 3.0f }; std::vector input1Data{ 10.0f, 8.0f }; std::vector expectedOutputData{ 15.0f, 11.0f }; std::vector outputData(2); TensorInfo inputTensorInfo = runtime->GetInputTensorInfo(netId, 0); inputTensorInfo.SetConstant(true); InputTensors inputTensors { {0,armnn::ConstTensor(inputTensorInfo, input0Data.data())}, {1,armnn::ConstTensor(inputTensorInfo, input1Data.data())} }; OutputTensors outputTensors { {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())} }; // Does the inference. runtime->EnqueueWorkload(netId, inputTensors, outputTensors); // Checks the results. CHECK(outputData == expectedOutputData); } #endif