CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
2841 lines
130 KiB
C++
2841 lines
130 KiB
C++
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <filesystem>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <thread>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include "HipdnnException.hpp"
|
|
#include "descriptors/BackendDescriptor.hpp"
|
|
#include "descriptors/DescriptorFactory.hpp"
|
|
#include "descriptors/DescriptorTestUtils.hpp"
|
|
#include "descriptors/ExecutionPlanDescriptor.hpp"
|
|
#include "descriptors/FlatbufferTestUtils.hpp"
|
|
#include "descriptors/GraphDescriptor.hpp"
|
|
#include "descriptors/TestMacros.hpp"
|
|
#include "descriptors/VariantDescriptor.hpp"
|
|
#include "descriptors/mocks/MockDescriptor.hpp"
|
|
#include "descriptors/mocks/MockEnginePluginResourceManager.hpp"
|
|
#include "plugin/EnginePluginResourceManager.hpp"
|
|
#include "plugins/mocks/MockEnginePlugin.hpp"
|
|
#include "plugins/mocks/MockEnginePluginManager.hpp"
|
|
#include <gtest/gtest.h>
|
|
#include <hipdnn_data_sdk/utilities/VersionUtils.hpp>
|
|
#include <hipdnn_plugin_sdk/PluginVersionConstants.hpp>
|
|
#include <hipdnn_plugin_sdk/engine_api_version.h>
|
|
|
|
using namespace hipdnn_backend;
|
|
using namespace hipdnn_backend::plugin;
|
|
using namespace hipdnn_backend::test_utilities;
|
|
using namespace ::testing;
|
|
|
|
TEST(TestEnginePluginResourceManager, PluginLoading)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
}
|
|
}
|
|
|
|
TEST(TestEngineDetailsWrapper, DestroysPluginDetailsWhenFlatbufferVerificationFails)
|
|
{
|
|
auto resourceManager = std::make_shared<MockEnginePluginResourceManager>();
|
|
std::array<uint8_t, 4> malformedBytes{1, 2, 3, 4};
|
|
hipdnnPluginConstData_t returnedDetails{malformedBytes.data(), malformedBytes.size()};
|
|
|
|
EXPECT_CALL(*resourceManager, getEngineDetails(100, nullptr, _))
|
|
.WillOnce([&returnedDetails](
|
|
int64_t, const GraphDescriptor*, hipdnnPluginConstData_t* engineDetails) {
|
|
*engineDetails = returnedDetails;
|
|
});
|
|
EXPECT_CALL(*resourceManager, destroyEngineDetails(100, _))
|
|
.WillOnce([](int64_t, hipdnnPluginConstData_t* engineDetails) {
|
|
EXPECT_NE(engineDetails, nullptr);
|
|
EXPECT_NE(engineDetails->ptr, nullptr);
|
|
EXPECT_EQ(engineDetails->size, 4u);
|
|
});
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(EngineDetailsWrapper(resourceManager, 100, nullptr),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST(TestEngineDetailsWrapper, MoveAssignmentDestroysDestinationDetailsAndTransfersSourceDetails)
|
|
{
|
|
auto resourceManager = std::make_shared<MockEnginePluginResourceManager>();
|
|
auto detailsA = createValidEngineDetails(100);
|
|
auto detailsB = createValidEngineDetails(200);
|
|
hipdnnPluginConstData_t returnedDetailsA{detailsA.GetBufferPointer(), detailsA.GetSize()};
|
|
hipdnnPluginConstData_t returnedDetailsB{detailsB.GetBufferPointer(), detailsB.GetSize()};
|
|
|
|
EXPECT_CALL(*resourceManager, getEngineDetails(100, nullptr, _))
|
|
.WillOnce([&returnedDetailsA](
|
|
int64_t, const GraphDescriptor*, hipdnnPluginConstData_t* engineDetails) {
|
|
*engineDetails = returnedDetailsA;
|
|
});
|
|
EXPECT_CALL(*resourceManager, getEngineDetails(200, nullptr, _))
|
|
.WillOnce([&returnedDetailsB](
|
|
int64_t, const GraphDescriptor*, hipdnnPluginConstData_t* engineDetails) {
|
|
*engineDetails = returnedDetailsB;
|
|
});
|
|
|
|
bool destroyedA = false;
|
|
bool destroyedB = false;
|
|
|
|
{
|
|
EngineDetailsWrapper destination(resourceManager, 100, nullptr);
|
|
EngineDetailsWrapper source(resourceManager, 200, nullptr);
|
|
|
|
EXPECT_CALL(*resourceManager, destroyEngineDetails(100, _))
|
|
.WillOnce(
|
|
[&destroyedA, &returnedDetailsA](int64_t, hipdnnPluginConstData_t* engineDetails) {
|
|
destroyedA = true;
|
|
EXPECT_EQ(engineDetails->ptr, returnedDetailsA.ptr);
|
|
EXPECT_EQ(engineDetails->size, returnedDetailsA.size);
|
|
});
|
|
EXPECT_CALL(*resourceManager, destroyEngineDetails(200, _)).Times(0);
|
|
|
|
destination = std::move(source);
|
|
|
|
EXPECT_TRUE(destroyedA);
|
|
Mock::VerifyAndClearExpectations(resourceManager.get());
|
|
|
|
EXPECT_CALL(*resourceManager, destroyEngineDetails(200, _))
|
|
.WillOnce(
|
|
[&destroyedB, &returnedDetailsB](int64_t, hipdnnPluginConstData_t* engineDetails) {
|
|
destroyedB = true;
|
|
EXPECT_EQ(engineDetails->ptr, returnedDetailsB.ptr);
|
|
EXPECT_EQ(engineDetails->size, returnedDetailsB.size);
|
|
});
|
|
}
|
|
|
|
EXPECT_TRUE(destroyedB);
|
|
}
|
|
|
|
TEST(TestEngineExecutionContextWrapper,
|
|
MoveAssignmentDestroysDestinationContextAndTransfersSourceContext)
|
|
{
|
|
auto resourceManager = std::make_shared<MockEnginePluginResourceManager>();
|
|
auto contextA = hipdnnEnginePluginExecutionContext_t(0xaaaaaaaa);
|
|
auto contextB = hipdnnEnginePluginExecutionContext_t(0xbbbbbbbb);
|
|
const hipdnnPluginConstData_t fakeEngineConfig{reinterpret_cast<const void*>("fake_config"),
|
|
11};
|
|
|
|
EXPECT_CALL(*resourceManager, createExecutionContext(100, &fakeEngineConfig, nullptr))
|
|
.WillOnce(::testing::Return(contextA));
|
|
EXPECT_CALL(*resourceManager, createExecutionContext(200, &fakeEngineConfig, nullptr))
|
|
.WillOnce(::testing::Return(contextB));
|
|
|
|
bool destroyedA = false;
|
|
bool destroyedB = false;
|
|
|
|
{
|
|
EngineExecutionContextWrapper destination(resourceManager, 100, &fakeEngineConfig, nullptr);
|
|
EngineExecutionContextWrapper source(resourceManager, 200, &fakeEngineConfig, nullptr);
|
|
|
|
EXPECT_CALL(*resourceManager, destroyExecutionContext(100, contextA))
|
|
.WillOnce([&destroyedA](int64_t, hipdnnEnginePluginExecutionContext_t) {
|
|
destroyedA = true;
|
|
});
|
|
EXPECT_CALL(*resourceManager, destroyExecutionContext(200, contextB)).Times(0);
|
|
|
|
destination = std::move(source);
|
|
|
|
EXPECT_TRUE(destroyedA);
|
|
Mock::VerifyAndClearExpectations(resourceManager.get());
|
|
|
|
EXPECT_CALL(*resourceManager, destroyExecutionContext(200, contextB))
|
|
.WillOnce([&destroyedB](int64_t, hipdnnEnginePluginExecutionContext_t) {
|
|
destroyedB = true;
|
|
});
|
|
}
|
|
|
|
EXPECT_TRUE(destroyedB);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SetStream)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
setStream(hipdnnEnginePluginHandle_t(0xdeadbeef), hipStream_t(0x12345678)));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
resourceManager.setStream(hipStream_t(0x12345678));
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, StaticPluginPathManagementSetAndGetSinglePath)
|
|
{
|
|
std::vector<std::filesystem::path> pluginPaths = {"/test/plugin/path"};
|
|
|
|
EnginePluginResourceManager::setPluginPaths(pluginPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
auto retrievedPaths = EnginePluginResourceManager::getPluginPaths();
|
|
|
|
const std::set<std::filesystem::path> expectedPaths(pluginPaths.begin(), pluginPaths.end());
|
|
EXPECT_EQ(retrievedPaths, expectedPaths);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, StaticPluginPathManagementSetAndGetMultiplePaths)
|
|
{
|
|
std::vector<std::filesystem::path> pluginPaths
|
|
= {"/test/plugin/path1", "/test/plugin/path2", "/test/plugin/path3"};
|
|
|
|
EnginePluginResourceManager::setPluginPaths(pluginPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
auto retrievedPaths = EnginePluginResourceManager::getPluginPaths();
|
|
|
|
const std::set<std::filesystem::path> expectedPaths(pluginPaths.begin(), pluginPaths.end());
|
|
EXPECT_EQ(retrievedPaths, expectedPaths);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, StaticPluginPathManagementAdditiveLoadingMode)
|
|
{
|
|
const std::vector<std::filesystem::path> initialPaths = {"/test/path1"};
|
|
EnginePluginResourceManager::setPluginPaths(initialPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
|
|
const std::vector<std::filesystem::path> additionalPaths = {"/test/path2", "/test/path3"};
|
|
EnginePluginResourceManager::setPluginPaths(additionalPaths, HIPDNN_PLUGIN_LOADING_ADDITIVE);
|
|
|
|
auto retrievedPaths = EnginePluginResourceManager::getPluginPaths();
|
|
|
|
const std::set<std::filesystem::path> expectedPaths
|
|
= {"/test/path1", "/test/path2", "/test/path3"};
|
|
EXPECT_EQ(retrievedPaths, expectedPaths);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, StaticPluginPathManagementAbsoluteLoadingModeReplacesExisting)
|
|
{
|
|
const std::vector<std::filesystem::path> initialPaths = {"/test/path1", "/test/path2"};
|
|
EnginePluginResourceManager::setPluginPaths(initialPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
|
|
std::vector<std::filesystem::path> newPaths = {"/test/path3", "/test/path4"};
|
|
EnginePluginResourceManager::setPluginPaths(newPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
|
|
auto retrievedPaths = EnginePluginResourceManager::getPluginPaths();
|
|
|
|
const std::set<std::filesystem::path> expectedPaths(newPaths.begin(), newPaths.end());
|
|
EXPECT_EQ(retrievedPaths, expectedPaths);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, StaticPluginPathManagementEmptyPathsClearing)
|
|
{
|
|
const std::vector<std::filesystem::path> pluginPaths = {"/test/path1", "/test/path2"};
|
|
EnginePluginResourceManager::setPluginPaths(pluginPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
|
|
const std::vector<std::filesystem::path> emptyPaths;
|
|
EnginePluginResourceManager::setPluginPaths(emptyPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE);
|
|
|
|
auto retrievedPaths = EnginePluginResourceManager::getPluginPaths();
|
|
EXPECT_TRUE(retrievedPaths.empty());
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, MoveConstructor)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, setStream(hipdnnEnginePluginHandle_t(0xdeadbeef), nullptr));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EnginePluginResourceManager rm1(pluginManager);
|
|
|
|
const EnginePluginResourceManager rm2 = std::move(rm1);
|
|
|
|
EXPECT_NO_THROW(rm2.setStream(nullptr));
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, MoveAssignment)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin1 = std::make_shared<MockEnginePlugin>();
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin2 = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins1{mockPlugin1};
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins2{mockPlugin2};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager1
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager2
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager1, getPlugins()).WillOnce(::testing::ReturnRef(plugins1));
|
|
EXPECT_CALL(*mockPlugin1, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin1, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin1, setStream(hipdnnEnginePluginHandle_t(0xdeadbeef), nullptr));
|
|
EXPECT_CALL(*mockPlugin1, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*pluginManager2, getPlugins()).WillOnce(::testing::ReturnRef(plugins2));
|
|
EXPECT_CALL(*mockPlugin2, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xcafebabe)));
|
|
EXPECT_CALL(*mockPlugin2, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{200}));
|
|
EXPECT_CALL(*mockPlugin2, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xcafebabe))))
|
|
.Times(testing::AtMost(1));
|
|
|
|
EnginePluginResourceManager rm1(pluginManager1);
|
|
EnginePluginResourceManager rm2(pluginManager2);
|
|
|
|
rm2 = std::move(rm1);
|
|
|
|
EXPECT_NO_THROW(rm2.setStream(nullptr));
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SelfMoveAssignment)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101}));
|
|
EXPECT_CALL(*mockPlugin, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE));
|
|
|
|
EXPECT_CALL(*mockPlugin, setStream(hipdnnEnginePluginHandle_t(0xdeadbeef), nullptr)).Times(2);
|
|
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
EXPECT_CALL(*mockPlugin,
|
|
getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xdeadbeef), testing::_))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101}));
|
|
|
|
std::set<std::filesystem::path> expectedPluginFiles = {"/path/to/plugin.so"};
|
|
EXPECT_CALL(*pluginManager, getLoadedPluginFiles())
|
|
.WillOnce(::testing::ReturnRef(expectedPluginFiles));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EnginePluginResourceManager rm(pluginManager);
|
|
|
|
EXPECT_NO_THROW(rm.setStream(nullptr));
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wself-move"
|
|
rm = std::move(rm);
|
|
#pragma clang diagnostic pop
|
|
|
|
EXPECT_NO_THROW(rm.setStream(nullptr));
|
|
|
|
auto engineIds = rm.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_EQ(engineIds.size(), 2);
|
|
EXPECT_EQ(engineIds[0], 100);
|
|
EXPECT_EQ(engineIds[1], 101);
|
|
|
|
size_t numPlugins = 0;
|
|
size_t maxStringLen = 0;
|
|
EXPECT_NO_THROW(rm.getLoadedPluginFiles(&numPlugins, nullptr, &maxStringLen));
|
|
EXPECT_EQ(numPlugins, 1);
|
|
EXPECT_GT(maxStringLen, 0);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, RapidCreationDestruction)
|
|
{
|
|
const int numIterations = 100;
|
|
|
|
for(int i = 0; i < numIterations; ++i)
|
|
{
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
auto mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager rm(pluginManager);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConcurrentCreationAndPublicMethods)
|
|
{
|
|
const size_t numThreads = 4;
|
|
const size_t managersPerThread = 10;
|
|
std::vector<std::thread> threads;
|
|
std::vector<std::vector<std::shared_ptr<EnginePluginResourceManager>>> allManagers(numThreads);
|
|
std::vector<std::vector<std::shared_ptr<MockEnginePluginManager>>> allPluginManagers(
|
|
numThreads);
|
|
std::vector<std::vector<std::shared_ptr<MockEnginePlugin>>> allMockPlugins(numThreads);
|
|
std::vector<std::vector<std::vector<std::shared_ptr<EnginePlugin>>>> allPlugins(numThreads);
|
|
std::atomic<size_t> successfulCreations{0};
|
|
|
|
threads.reserve(numThreads);
|
|
|
|
for(size_t t = 0; t < numThreads; ++t)
|
|
{
|
|
allManagers[t].reserve(managersPerThread);
|
|
allPluginManagers[t].reserve(managersPerThread);
|
|
allMockPlugins[t].reserve(managersPerThread);
|
|
allPlugins[t].reserve(managersPerThread);
|
|
}
|
|
|
|
for(size_t t = 0; t < numThreads; ++t)
|
|
{
|
|
threads.emplace_back([t,
|
|
&allManagers,
|
|
&allPluginManagers,
|
|
&allMockPlugins,
|
|
&allPlugins,
|
|
&successfulCreations]() {
|
|
for(size_t i = 0; i < managersPerThread; ++i)
|
|
{
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
auto mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
const std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
|
|
allPluginManagers[t].push_back(pluginManager);
|
|
allMockPlugins[t].push_back(mockPlugin);
|
|
allPlugins[t].push_back(plugins);
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins())
|
|
.WillOnce(::testing::ReturnRef(allPlugins[t][i]));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(
|
|
std::vector<int64_t>{static_cast<int64_t>(100 + (t * 1000) + i)}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
setStream(hipdnnEnginePluginHandle_t(0xdeadbeef), nullptr));
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
allManagers[t].push_back(
|
|
std::make_shared<EnginePluginResourceManager>(pluginManager));
|
|
successfulCreations++;
|
|
|
|
EXPECT_NO_THROW(allManagers[t].back()->setStream(nullptr));
|
|
}
|
|
});
|
|
}
|
|
|
|
for(auto& thread : threads)
|
|
{
|
|
thread.join();
|
|
}
|
|
|
|
EXPECT_EQ(successfulCreations.load(), numThreads * managersPerThread);
|
|
|
|
allManagers.clear();
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetApplicableEngineIdsNullGraphDescriptor)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(resourceManager.getApplicableEngineIds(nullptr),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SetNullStream)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, setStream(hipdnnEnginePluginHandle_t(0xdeadbeef), nullptr));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
EXPECT_NO_THROW(resourceManager.setStream(nullptr));
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetApplicableEngineIdsWithLoadedPlugin)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData = {
|
|
reinterpret_cast<const void*>("fake_graph_data"),
|
|
15 // length of "fake_graph_data"
|
|
};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE));
|
|
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
getApplicableEngineIds(
|
|
hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
testing::Pointee(testing::AllOf(
|
|
testing::Field(&hipdnnPluginConstData_t::ptr, fakeSerializedData.ptr),
|
|
testing::Field(&hipdnnPluginConstData_t::size, fakeSerializedData.size)))))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
|
|
EXPECT_EQ(engineIds.size(), 3);
|
|
EXPECT_EQ(engineIds[0], 100);
|
|
EXPECT_EQ(engineIds[1], 101);
|
|
EXPECT_EQ(engineIds[2], 102);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetWorkspaceSize)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeEngineConfig = {
|
|
reinterpret_cast<const void*>("fake_config"),
|
|
11 // length of "fake_config"
|
|
};
|
|
const hipdnnPluginConstData_t fakeSerializedData = {
|
|
reinterpret_cast<const void*>("fake_graph_data"),
|
|
15 // length of "fake_graph_data"
|
|
};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
getWorkspaceSize(
|
|
hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
testing::Pointee(testing::AllOf(
|
|
testing::Field(&hipdnnPluginConstData_t::ptr, fakeEngineConfig.ptr),
|
|
testing::Field(&hipdnnPluginConstData_t::size, fakeEngineConfig.size))),
|
|
testing::Pointee(testing::AllOf(
|
|
testing::Field(&hipdnnPluginConstData_t::ptr, fakeSerializedData.ptr),
|
|
testing::Field(&hipdnnPluginConstData_t::size, fakeSerializedData.size)))))
|
|
.WillOnce(::testing::Return(size_t(8192)));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto workspaceSize
|
|
= resourceManager.getWorkspaceSize(100, &fakeEngineConfig, &mockGraphDesc);
|
|
|
|
EXPECT_EQ(workspaceSize, 8192);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetWorkspaceSizeFromExecutionContext)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(reinterpret_cast<hipdnnEnginePluginHandle_t>(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, getWorkspaceSize(_, _)).WillOnce(::testing::Return(size_t(4096)));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(_));
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto workspaceSize = resourceManager.getWorkspaceSize(
|
|
100, reinterpret_cast<hipdnnEnginePluginExecutionContext_t>(0x12345678));
|
|
EXPECT_EQ(workspaceSize, 4096);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetEngineDetails)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData = {
|
|
reinterpret_cast<const void*>("fake_graph_data"),
|
|
15 // length of "fake_graph_data"
|
|
};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
|
|
EXPECT_CALL(
|
|
*mockPlugin,
|
|
getEngineDetails(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
int64_t(100), // engineId
|
|
testing::Pointee(testing::AllOf(
|
|
testing::Field(&hipdnnPluginConstData_t::ptr, fakeSerializedData.ptr),
|
|
testing::Field(&hipdnnPluginConstData_t::size,
|
|
fakeSerializedData.size))), // opGraph
|
|
testing::_ // output engineDetails
|
|
))
|
|
.WillOnce(testing::Invoke([](hipdnnEnginePluginHandle_t,
|
|
int64_t engineId,
|
|
const hipdnnPluginConstData_t*,
|
|
hipdnnPluginConstData_t* output) {
|
|
// Create valid flatbuffer engine details
|
|
static auto s_builder = createValidEngineDetails(engineId);
|
|
output->ptr = s_builder.GetBufferPointer();
|
|
output->size = s_builder.GetSize();
|
|
}));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroyEngineDetails(hipdnnEnginePluginHandle_t(0xdeadbeef), testing::_));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Test getEngineDetails functionality with valid flatbuffer data
|
|
auto engineDetails = EnginePluginResourceManager::getEngineDetails(
|
|
std::make_shared<EnginePluginResourceManager>(std::move(resourceManager)),
|
|
100,
|
|
&mockGraphDesc);
|
|
|
|
// Verify that we got valid engine details
|
|
EXPECT_NE(engineDetails, nullptr);
|
|
EXPECT_NE(engineDetails->get(), nullptr);
|
|
EXPECT_EQ(engineDetails->get()->engine_id(), 100);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, CreateExecutionContext)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeEngineConfig = {
|
|
reinterpret_cast<const void*>("fake_config"),
|
|
11 // length of "fake_config"
|
|
};
|
|
const hipdnnPluginConstData_t fakeSerializedData = {
|
|
reinterpret_cast<const void*>("fake_graph_data"),
|
|
15 // length of "fake_graph_data"
|
|
};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
createExecutionContext(
|
|
hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
testing::Pointee(testing::AllOf(
|
|
testing::Field(&hipdnnPluginConstData_t::ptr, fakeEngineConfig.ptr),
|
|
testing::Field(&hipdnnPluginConstData_t::size, fakeEngineConfig.size))),
|
|
testing::Pointee(testing::AllOf(
|
|
testing::Field(&hipdnnPluginConstData_t::ptr, fakeSerializedData.ptr),
|
|
testing::Field(&hipdnnPluginConstData_t::size, fakeSerializedData.size)))))
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginExecutionContext_t(0xcafebabe)));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroyExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe)));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto executionContext = EnginePluginResourceManager::createExecutionContext(
|
|
std::make_shared<EnginePluginResourceManager>(std::move(resourceManager)),
|
|
100,
|
|
&fakeEngineConfig,
|
|
&mockGraphDesc);
|
|
|
|
EXPECT_NE(executionContext, nullptr);
|
|
EXPECT_NE(executionContext->get(), nullptr);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, CreateExecutionContextWithInvalidEngineId)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeEngineConfig = {
|
|
reinterpret_cast<const void*>("fake_config"),
|
|
11 // length of "fake_config"
|
|
};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Try to create execution context with an invalid engine ID (999 is not in the list)
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
EnginePluginResourceManager::createExecutionContext(
|
|
std::make_shared<EnginePluginResourceManager>(std::move(resourceManager)),
|
|
999, // Invalid engine ID
|
|
&fakeEngineConfig,
|
|
&mockGraphDesc),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphWithNullParameters)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(resourceManager.executeOpGraph(nullptr, nullptr),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SerializeExecutionContextFailsForUnsupportedPlugin)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
std::vector<uint8_t> serializedContext;
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
serializeExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
_))
|
|
.WillOnce(::testing::Throw(
|
|
HipdnnException(HIPDNN_STATUS_NOT_SUPPORTED,
|
|
"Engine plugin does not support execution context serialization")));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.serializeExecutionContext(
|
|
100, hipdnnEnginePluginExecutionContext_t(0xcafebabe), serializedContext),
|
|
HIPDNN_STATUS_NOT_SUPPORTED);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SerializeExecutionContextRejectsNullPluginPayload)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
std::vector<uint8_t> serializedContext;
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
serializeExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
_))
|
|
.WillOnce([](hipdnnEnginePluginHandle_t,
|
|
hipdnnEnginePluginExecutionContext_t,
|
|
hipdnnPluginConstData_t* serializedContext) {
|
|
*serializedContext = hipdnnPluginConstData_t{nullptr, 4};
|
|
});
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroySerializedExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef), _));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.serializeExecutionContext(
|
|
100, hipdnnEnginePluginExecutionContext_t(0xcafebabe), serializedContext),
|
|
HIPDNN_STATUS_PLUGIN_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SerializeExecutionContextRejectsEmptyPluginPayload)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
const std::array<uint8_t, 1> payloadBytes{9};
|
|
std::vector<uint8_t> serializedContext;
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
serializeExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
_))
|
|
.WillOnce([&payloadBytes](hipdnnEnginePluginHandle_t,
|
|
hipdnnEnginePluginExecutionContext_t,
|
|
hipdnnPluginConstData_t* serializedContext) {
|
|
*serializedContext = hipdnnPluginConstData_t{payloadBytes.data(), 0};
|
|
});
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroySerializedExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef), _));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.serializeExecutionContext(
|
|
100, hipdnnEnginePluginExecutionContext_t(0xcafebabe), serializedContext),
|
|
HIPDNN_STATUS_PLUGIN_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SerializeExecutionContextCopiesPluginPayload)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
const std::array<uint8_t, 4> payloadBytes{9, 8, 7, 6};
|
|
std::vector<uint8_t> serializedContext;
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
serializeExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
_))
|
|
.WillOnce([&payloadBytes](hipdnnEnginePluginHandle_t,
|
|
hipdnnEnginePluginExecutionContext_t,
|
|
hipdnnPluginConstData_t* serializedContext) {
|
|
*serializedContext = hipdnnPluginConstData_t{payloadBytes.data(), payloadBytes.size()};
|
|
});
|
|
EXPECT_CALL(*mockPlugin,
|
|
destroySerializedExecutionContext(hipdnnEnginePluginHandle_t(0xdeadbeef), _));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_NO_THROW(resourceManager.serializeExecutionContext(
|
|
100, hipdnnEnginePluginExecutionContext_t(0xcafebabe), serializedContext));
|
|
}
|
|
|
|
ASSERT_EQ(serializedContext, std::vector<uint8_t>(payloadBytes.begin(), payloadBytes.end()));
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SerializeExecutionContextRejectsInvalidInputs)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
std::vector<uint8_t> serializedContext;
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.serializeExecutionContext(100, nullptr, serializedContext),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.serializeExecutionContext(
|
|
101, hipdnnEnginePluginExecutionContext_t(0xcafebabe), serializedContext),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, CreateExecutionContextFromSerializedFailsForUnsupportedPlugin)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
const std::array<uint8_t, 3> serializedContextBytes{4, 5, 6};
|
|
const hipdnnPluginConstData_t serializedContext{serializedContextBytes.data(),
|
|
serializedContextBytes.size()};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
createExecutionContextFromSerialized(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
&serializedContext))
|
|
.WillOnce(::testing::Throw(
|
|
HipdnnException(HIPDNN_STATUS_NOT_SUPPORTED,
|
|
"Engine plugin does not support execution context serialization")));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
EnginePluginResourceManager::createExecutionContextFromSerialized(
|
|
std::make_shared<EnginePluginResourceManager>(std::move(resourceManager)),
|
|
100,
|
|
&serializedContext),
|
|
HIPDNN_STATUS_NOT_SUPPORTED);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, CreateExecutionContextFromSerializedPropagatesPluginFailure)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
const std::array<uint8_t, 3> serializedContextBytes{4, 5, 6};
|
|
const hipdnnPluginConstData_t serializedContext{serializedContextBytes.data(),
|
|
serializedContextBytes.size()};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin,
|
|
createExecutionContextFromSerialized(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
&serializedContext))
|
|
.WillOnce(::testing::Throw(HipdnnException(
|
|
HIPDNN_STATUS_PLUGIN_ERROR, "Plugin rejected serialized execution context")));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
EnginePluginResourceManager::createExecutionContextFromSerialized(
|
|
std::make_shared<EnginePluginResourceManager>(std::move(resourceManager)),
|
|
100,
|
|
&serializedContext),
|
|
HIPDNN_STATUS_PLUGIN_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphFailNonFinalizedPlan)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
auto executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
auto variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
auto mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
executionPlanWrapper.get());
|
|
auto mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(variantWrapper.get());
|
|
|
|
const std::vector<int64_t> tensorIds = {1, 2, 3};
|
|
const std::vector<const void*> dataPtrs = {reinterpret_cast<void*>(0x1000),
|
|
reinterpret_cast<void*>(0x2000),
|
|
reinterpret_cast<void*>(0x3000)};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, isFinalized()).WillOnce(::testing::Return(false));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.executeOpGraph(executionPlanWrapper.get(), variantWrapper.get()),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphFailNonFinalizedVariant)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
auto executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
auto variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
auto mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
executionPlanWrapper.get());
|
|
auto mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(variantWrapper.get());
|
|
|
|
const std::vector<int64_t> tensorIds = {1, 2, 3};
|
|
const std::vector<const void*> dataPtrs = {reinterpret_cast<void*>(0x1000),
|
|
reinterpret_cast<void*>(0x2000),
|
|
reinterpret_cast<void*>(0x3000)};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, isFinalized()).WillOnce(::testing::Return(true));
|
|
EXPECT_CALL(*mockVariantPack, isFinalized()).WillOnce(::testing::Return(false));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.executeOpGraph(executionPlanWrapper.get(), variantWrapper.get()),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphFailTensorMismatch)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
auto executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
auto variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
auto mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
executionPlanWrapper.get());
|
|
auto mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(variantWrapper.get());
|
|
|
|
// More data ptrs than tensor ids
|
|
std::vector<int64_t> tensorIds = {1};
|
|
std::vector<const void*> dataPtrs = {reinterpret_cast<void*>(0x1000),
|
|
reinterpret_cast<void*>(0x2000),
|
|
reinterpret_cast<void*>(0x3000)};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, isFinalized()).WillRepeatedly(::testing::Return(true));
|
|
EXPECT_CALL(*mockVariantPack, isFinalized()).WillOnce(::testing::Return(true));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, getEngineId()).WillOnce(::testing::Return(int64_t(100)));
|
|
EXPECT_CALL(*mockVariantPack, getWorkspace())
|
|
.WillOnce(::testing::Return(reinterpret_cast<void*>(0x4000)));
|
|
EXPECT_CALL(*mockVariantPack, getTensorIds()).WillOnce(::testing::ReturnRef(tensorIds));
|
|
EXPECT_CALL(*mockVariantPack, getDataPointers()).WillOnce(::testing::ReturnRef(dataPtrs));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.executeOpGraph(executionPlanWrapper.get(), variantWrapper.get()),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
MATCHER_P2(MatchesMemory, data, size, "")
|
|
{
|
|
return memcmp(arg, data, size) == 0;
|
|
}
|
|
} // namespace
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphSuccessWithValidDescriptors)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
auto executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
auto variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
auto mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
executionPlanWrapper.get());
|
|
auto mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(variantWrapper.get());
|
|
|
|
std::vector<int64_t> tensorIds = {1, 2, 3};
|
|
std::vector<const void*> dataPtrs = {reinterpret_cast<void*>(0x1000),
|
|
reinterpret_cast<void*>(0x2000),
|
|
reinterpret_cast<void*>(0x3000)};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, isFinalized()).WillRepeatedly(::testing::Return(true));
|
|
EXPECT_CALL(*mockVariantPack, isFinalized()).WillOnce(::testing::Return(true));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, getEngineId()).WillOnce(::testing::Return(int64_t(100)));
|
|
EXPECT_CALL(*mockVariantPack, getWorkspace())
|
|
.WillOnce(::testing::Return(reinterpret_cast<void*>(0x4000)));
|
|
EXPECT_CALL(*mockVariantPack, getTensorIds()).WillOnce(::testing::ReturnRef(tensorIds));
|
|
EXPECT_CALL(*mockVariantPack, getDataPointers()).WillOnce(::testing::ReturnRef(dataPtrs));
|
|
EXPECT_CALL(*mockExecutionPlan, getExecutionContext())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginExecutionContext_t(0xcafebabe)));
|
|
|
|
std::vector<hipdnnPluginDeviceBuffer_t> expectedDeviceBuffers;
|
|
expectedDeviceBuffers.reserve(tensorIds.size());
|
|
for(size_t i = 0; i < tensorIds.size(); ++i)
|
|
{
|
|
hipdnnPluginDeviceBuffer_t buffer;
|
|
buffer.uid = tensorIds[i];
|
|
buffer.ptr = const_cast<void*>(dataPtrs[i]);
|
|
expectedDeviceBuffers.push_back(buffer);
|
|
}
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
executeOpGraph(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
reinterpret_cast<void*>(0x4000),
|
|
MatchesMemory(expectedDeviceBuffers.data(),
|
|
expectedDeviceBuffers.size()
|
|
* sizeof(hipdnnPluginDeviceBuffer_t)),
|
|
static_cast<uint32_t>(tensorIds.size())));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
resourceManager.executeOpGraph(executionPlanWrapper.get(), variantWrapper.get());
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphThrowsOnMisalignedTensorPointer)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
auto executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
auto variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
auto mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
executionPlanWrapper.get());
|
|
auto mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(variantWrapper.get());
|
|
|
|
std::vector<int64_t> tensorIds = {1, 2};
|
|
// Second tensor's pointer (0x1001) violates its 16-byte alignment requirement.
|
|
std::vector<const void*> dataPtrs
|
|
= {reinterpret_cast<void*>(0x1000), reinterpret_cast<void*>(0x1001)};
|
|
std::vector<int64_t> planUids = {1, 2};
|
|
std::vector<int64_t> planAlignments = {16, 16};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, isFinalized()).WillRepeatedly(::testing::Return(true));
|
|
EXPECT_CALL(*mockVariantPack, isFinalized()).WillOnce(::testing::Return(true));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, getEngineId()).WillOnce(::testing::Return(int64_t(100)));
|
|
EXPECT_CALL(*mockVariantPack, getWorkspace())
|
|
.WillOnce(::testing::Return(reinterpret_cast<void*>(0x4000)));
|
|
EXPECT_CALL(*mockVariantPack, getTensorIds()).WillOnce(::testing::ReturnRef(tensorIds));
|
|
EXPECT_CALL(*mockVariantPack, getDataPointers()).WillOnce(::testing::ReturnRef(dataPtrs));
|
|
EXPECT_CALL(*mockExecutionPlan, getTensorUids()).WillRepeatedly(::testing::ReturnRef(planUids));
|
|
EXPECT_CALL(*mockExecutionPlan, getTensorAlignments())
|
|
.WillRepeatedly(::testing::ReturnRef(planAlignments));
|
|
|
|
// The op graph must never be dispatched when alignment validation fails.
|
|
EXPECT_CALL(*mockPlugin, executeOpGraph(_, _, _, _, _)).Times(0);
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.executeOpGraph(executionPlanWrapper.get(), variantWrapper.get()),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ExecuteOpGraphSucceedsWhenTensorPointersAreAligned)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
auto executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
auto variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
auto mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
executionPlanWrapper.get());
|
|
auto mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(variantWrapper.get());
|
|
|
|
std::vector<int64_t> tensorIds = {1, 2};
|
|
// 0x1000 satisfies 256-byte alignment, 0x2000 satisfies 64-byte alignment.
|
|
std::vector<const void*> dataPtrs
|
|
= {reinterpret_cast<void*>(0x1000), reinterpret_cast<void*>(0x2000)};
|
|
std::vector<int64_t> planUids = {1, 2};
|
|
std::vector<int64_t> planAlignments = {256, 64};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, isFinalized()).WillRepeatedly(::testing::Return(true));
|
|
EXPECT_CALL(*mockVariantPack, isFinalized()).WillOnce(::testing::Return(true));
|
|
|
|
EXPECT_CALL(*mockExecutionPlan, getEngineId()).WillOnce(::testing::Return(int64_t(100)));
|
|
EXPECT_CALL(*mockVariantPack, getWorkspace())
|
|
.WillOnce(::testing::Return(reinterpret_cast<void*>(0x4000)));
|
|
EXPECT_CALL(*mockVariantPack, getTensorIds()).WillOnce(::testing::ReturnRef(tensorIds));
|
|
EXPECT_CALL(*mockVariantPack, getDataPointers()).WillOnce(::testing::ReturnRef(dataPtrs));
|
|
EXPECT_CALL(*mockExecutionPlan, getTensorUids()).WillRepeatedly(::testing::ReturnRef(planUids));
|
|
EXPECT_CALL(*mockExecutionPlan, getTensorAlignments())
|
|
.WillRepeatedly(::testing::ReturnRef(planAlignments));
|
|
EXPECT_CALL(*mockExecutionPlan, getExecutionContext())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginExecutionContext_t(0xcafebabe)));
|
|
|
|
EXPECT_CALL(*mockPlugin,
|
|
executeOpGraph(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
reinterpret_cast<void*>(0x4000),
|
|
_,
|
|
static_cast<uint32_t>(tensorIds.size())));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
resourceManager.executeOpGraph(executionPlanWrapper.get(), variantWrapper.get());
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetLoadedPluginFiles)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
std::set<std::filesystem::path> expectedPluginFiles
|
|
= {"/path/to/plugin1.so", "/path/to/plugin2.so"};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*pluginManager, getLoadedPluginFiles())
|
|
.Times(2)
|
|
.WillRepeatedly(::testing::ReturnRef(expectedPluginFiles));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
size_t numPlugins = 0;
|
|
size_t maxStringLen = 0;
|
|
|
|
EXPECT_NO_THROW(resourceManager.getLoadedPluginFiles(&numPlugins, nullptr, &maxStringLen));
|
|
|
|
EXPECT_EQ(numPlugins, 2);
|
|
EXPECT_GT(maxStringLen, 0);
|
|
|
|
std::vector<std::string> pluginStrings(numPlugins);
|
|
std::vector<char*> pluginPaths(numPlugins);
|
|
for(size_t i = 0; i < numPlugins; ++i)
|
|
{
|
|
pluginStrings[i].resize(maxStringLen);
|
|
pluginPaths[i] = pluginStrings[i].data();
|
|
}
|
|
|
|
EXPECT_NO_THROW(
|
|
resourceManager.getLoadedPluginFiles(&numPlugins, pluginPaths.data(), &maxStringLen));
|
|
|
|
// Note: std::set ordering may differ, so we check that both paths are present
|
|
const std::set<std::string> returnedPaths
|
|
= {std::string(pluginPaths[0]), std::string(pluginPaths[1])};
|
|
const std::set<std::string> expectedPaths = {"/path/to/plugin1.so", "/path/to/plugin2.so"};
|
|
EXPECT_EQ(returnedPaths, expectedPaths);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetWorkspaceSizeNullEngineConfig)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(resourceManager.getWorkspaceSize(100, nullptr, &mockGraphDesc),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetWorkspaceSizeThrowsExceptionForInvalidEngineId)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeEngineConfig = {
|
|
reinterpret_cast<const void*>("fake_config"),
|
|
11 // length of "fake_config"
|
|
};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.getWorkspaceSize(200, &fakeEngineConfig, &mockGraphDesc),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetWorkspaceSizeFromExecutionContextNullExecutionContext)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(reinterpret_cast<hipdnnEnginePluginHandle_t>(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(_));
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(resourceManager.getWorkspaceSize(100, nullptr),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager,
|
|
GetWorkspaceSizeFromExecutionContextThrowsExceptionForInvalidEngineId)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(reinterpret_cast<hipdnnEnginePluginHandle_t>(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(_));
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.getWorkspaceSize(
|
|
200, reinterpret_cast<hipdnnEnginePluginExecutionContext_t>(0x12345678)),
|
|
HIPDNN_STATUS_INTERNAL_ERROR);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, SetPluginPathsWithActiveResourceManager)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100, 101, 102}));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
std::vector<std::filesystem::path> pluginPaths = {"/test/path"};
|
|
|
|
EXPECT_NO_THROW(EnginePluginResourceManager::setPluginPaths(
|
|
pluginPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE));
|
|
|
|
auto retrievedPaths = EnginePluginResourceManager::getPluginPaths();
|
|
const std::set<std::filesystem::path> expectedPaths(pluginPaths.begin(), pluginPaths.end());
|
|
EXPECT_EQ(retrievedPaths, expectedPaths);
|
|
|
|
const std::vector<std::filesystem::path> emptyPaths;
|
|
EXPECT_NO_THROW(EnginePluginResourceManager::setPluginPaths(
|
|
emptyPaths, HIPDNN_PLUGIN_LOADING_ABSOLUTE));
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorSkipsPluginWhenCreateHandleThrowsHipdnnException)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, name()).WillRepeatedly(::testing::Return("BadPlugin"));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Throw(HipdnnException(HIPDNN_STATUS_PLUGIN_ERROR, "Test error")));
|
|
|
|
// Plugin should never be queried since it failed to load
|
|
EXPECT_CALL(*mockPlugin, getApplicableEngineIds(testing::_, testing::_)).Times(0);
|
|
|
|
// No destroyHandle call expected since handle creation failed
|
|
|
|
{
|
|
// Constructor should not throw, but the plugin should be skipped
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Verify no engines were registered
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_TRUE(engineIds.empty());
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorSkipsPluginWhenCreateHandleThrowsStdException)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, name()).WillRepeatedly(::testing::Return("BadPlugin"));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Throw(std::runtime_error("Test std::exception")));
|
|
|
|
// Plugin should never be queried since it failed to load
|
|
EXPECT_CALL(*mockPlugin, getApplicableEngineIds(testing::_, testing::_)).Times(0);
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Verify no engines were registered
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_TRUE(engineIds.empty());
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorSkipsPluginWhenCreateHandleReturnsNull)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, name()).WillRepeatedly(::testing::Return("NullHandlePlugin"));
|
|
EXPECT_CALL(*mockPlugin, createHandle()).WillOnce(::testing::Return(nullptr));
|
|
|
|
// Plugin should never be queried since it returned null handle
|
|
EXPECT_CALL(*mockPlugin, getApplicableEngineIds(testing::_, testing::_)).Times(0);
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Verify no engines were registered
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_TRUE(engineIds.empty());
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorSkipsPluginOnHandleCollision)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin1 = std::make_shared<MockEnginePlugin>();
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin2 = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin1, mockPlugin2};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
// Both plugins return the same handle (simulating a collision)
|
|
auto collisionHandle = reinterpret_cast<hipdnnEnginePluginHandle_t>(0x123);
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
|
|
// First plugin succeeds
|
|
EXPECT_CALL(*mockPlugin1, createHandle()).WillOnce(::testing::Return(collisionHandle));
|
|
EXPECT_CALL(*mockPlugin1, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin1, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE));
|
|
EXPECT_CALL(*mockPlugin1, destroyHandle(collisionHandle));
|
|
|
|
// Second plugin returns same handle - should be skipped
|
|
EXPECT_CALL(*mockPlugin2, name()).WillRepeatedly(::testing::Return("CollidingPlugin"));
|
|
EXPECT_CALL(*mockPlugin2, createHandle()).WillOnce(::testing::Return(collisionHandle));
|
|
EXPECT_CALL(*mockPlugin2, destroyHandle(collisionHandle));
|
|
|
|
// Second plugin should never be queried since it had handle collision
|
|
EXPECT_CALL(*mockPlugin2, getApplicableEngineIds(testing::_, testing::_)).Times(0);
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Verify first plugin loaded successfully, second was skipped
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
EXPECT_CALL(*mockPlugin1, getApplicableEngineIds(collisionHandle, testing::_))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_EQ(engineIds.size(), 1);
|
|
EXPECT_EQ(engineIds[0], 100);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorSkipsPluginWhenGetAllEngineIdsThrows)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
auto handle = reinterpret_cast<hipdnnEnginePluginHandle_t>(0xdeadbeef);
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, name()).WillRepeatedly(::testing::Return("EngineIdFailPlugin"));
|
|
EXPECT_CALL(*mockPlugin, createHandle()).WillOnce(::testing::Return(handle));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Throw(
|
|
HipdnnException(HIPDNN_STATUS_PLUGIN_ERROR, "Failed to get engine IDs")));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(handle));
|
|
|
|
// Plugin should never be queried since getAllEngineIds failed
|
|
EXPECT_CALL(*mockPlugin, getApplicableEngineIds(testing::_, testing::_)).Times(0);
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Verify plugin was skipped and handle was cleaned up
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_TRUE(engineIds.empty());
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorContinuesAfterBadPluginWithGoodPlugin)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> badPlugin = std::make_shared<MockEnginePlugin>();
|
|
const std::shared_ptr<MockEnginePlugin> goodPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{badPlugin, goodPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
|
|
// Bad plugin throws during createHandle
|
|
EXPECT_CALL(*badPlugin, name()).WillRepeatedly(::testing::Return("BadPlugin"));
|
|
EXPECT_CALL(*badPlugin, createHandle())
|
|
.WillOnce(::testing::Throw(HipdnnException(HIPDNN_STATUS_PLUGIN_ERROR, "Test error")));
|
|
|
|
// Good plugin succeeds
|
|
auto goodHandle = reinterpret_cast<hipdnnEnginePluginHandle_t>(0xcafe);
|
|
EXPECT_CALL(*goodPlugin, createHandle()).WillOnce(::testing::Return(goodHandle));
|
|
EXPECT_CALL(*goodPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{200, 201}));
|
|
EXPECT_CALL(*goodPlugin, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE));
|
|
EXPECT_CALL(*goodPlugin, destroyHandle(goodHandle));
|
|
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
EXPECT_CALL(*goodPlugin, getApplicableEngineIds(goodHandle, testing::_))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{200, 201}));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
// Verify good plugin's engines are available
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_EQ(engineIds.size(), 2);
|
|
EXPECT_EQ(engineIds[0], 200);
|
|
EXPECT_EQ(engineIds[1], 201);
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetEngineInfosSinglePlugin)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillRepeatedly(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*mockPlugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin, getAllEngineIds())
|
|
.WillRepeatedly(::testing::Return(std::vector<int64_t>{100, 101}));
|
|
EXPECT_CALL(*mockPlugin, name()).WillRepeatedly(::testing::Return("test-plugin"));
|
|
EXPECT_CALL(*mockPlugin, version()).WillRepeatedly(::testing::Return("1.0"));
|
|
EXPECT_CALL(*mockPlugin, type()).WillRepeatedly(::testing::Return(HIPDNN_PLUGIN_TYPE_ENGINE));
|
|
EXPECT_CALL(*mockPlugin, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto infos = resourceManager.getEngineInfos();
|
|
|
|
ASSERT_EQ(infos.size(), 2);
|
|
|
|
// Results are sorted by engineName. formatEngineIdHex(100) = "0x0000000000000064",
|
|
// formatEngineIdHex(101) = "0x0000000000000065"
|
|
EXPECT_EQ(infos[0].engineId, 100);
|
|
EXPECT_EQ(infos[0].engineName, "0x0000000000000064");
|
|
EXPECT_EQ(infos[0].pluginName, "test-plugin");
|
|
EXPECT_EQ(infos[0].version, "1.0");
|
|
EXPECT_EQ(infos[0].type, "HIPDNN_PLUGIN_TYPE_ENGINE");
|
|
|
|
EXPECT_EQ(infos[1].engineId, 101);
|
|
EXPECT_EQ(infos[1].engineName, "0x0000000000000065");
|
|
EXPECT_EQ(infos[1].pluginName, "test-plugin");
|
|
EXPECT_EQ(infos[1].version, "1.0");
|
|
EXPECT_EQ(infos[1].type, "HIPDNN_PLUGIN_TYPE_ENGINE");
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetEngineInfosMultiplePlugins)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin1 = std::make_shared<MockEnginePlugin>();
|
|
const std::shared_ptr<MockEnginePlugin> mockPlugin2 = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{mockPlugin1, mockPlugin2};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillRepeatedly(::testing::ReturnRef(plugins));
|
|
|
|
EXPECT_CALL(*mockPlugin1, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*mockPlugin1, getAllEngineIds())
|
|
.WillRepeatedly(::testing::Return(std::vector<int64_t>{200}));
|
|
EXPECT_CALL(*mockPlugin1, name()).WillRepeatedly(::testing::Return("plugin-alpha"));
|
|
EXPECT_CALL(*mockPlugin1, version()).WillRepeatedly(::testing::Return("2.0"));
|
|
EXPECT_CALL(*mockPlugin1, type()).WillRepeatedly(::testing::Return(HIPDNN_PLUGIN_TYPE_ENGINE));
|
|
EXPECT_CALL(*mockPlugin1, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xdeadbeef))));
|
|
|
|
EXPECT_CALL(*mockPlugin2, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xcafebabe)));
|
|
EXPECT_CALL(*mockPlugin2, getAllEngineIds())
|
|
.WillRepeatedly(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*mockPlugin2, name()).WillRepeatedly(::testing::Return("plugin-beta"));
|
|
EXPECT_CALL(*mockPlugin2, version()).WillRepeatedly(::testing::Return("3.0"));
|
|
EXPECT_CALL(*mockPlugin2, type())
|
|
.WillRepeatedly(::testing::Return(HIPDNN_PLUGIN_TYPE_UNSPECIFIED));
|
|
EXPECT_CALL(*mockPlugin2, destroyHandle(testing::Eq(hipdnnEnginePluginHandle_t(0xcafebabe))));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto infos = resourceManager.getEngineInfos();
|
|
|
|
ASSERT_EQ(infos.size(), 2);
|
|
|
|
// Sorted by engineName: "0x0000000000000064" (100) < "0x00000000000000C8" (200)
|
|
EXPECT_EQ(infos[0].engineId, 100);
|
|
EXPECT_EQ(infos[0].engineName, "0x0000000000000064");
|
|
EXPECT_EQ(infos[0].pluginName, "plugin-beta");
|
|
EXPECT_EQ(infos[0].version, "3.0");
|
|
EXPECT_EQ(infos[0].type, "HIPDNN_PLUGIN_TYPE_UNSPECIFIED");
|
|
|
|
EXPECT_EQ(infos[1].engineId, 200);
|
|
EXPECT_EQ(infos[1].engineName, "0x00000000000000C8");
|
|
EXPECT_EQ(infos[1].pluginName, "plugin-alpha");
|
|
EXPECT_EQ(infos[1].version, "2.0");
|
|
EXPECT_EQ(infos[1].type, "HIPDNN_PLUGIN_TYPE_ENGINE");
|
|
}
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, GetEngineInfosNoPlugins)
|
|
{
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins;
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillRepeatedly(::testing::ReturnRef(plugins));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
|
|
auto infos = resourceManager.getEngineInfos();
|
|
|
|
EXPECT_TRUE(infos.empty());
|
|
}
|
|
}
|
|
|
|
// Test subclass to access the protected default constructor
|
|
class TestableEnginePluginResourceManager : public EnginePluginResourceManager
|
|
{
|
|
public:
|
|
TestableEnginePluginResourceManager() = default;
|
|
};
|
|
|
|
TEST(TestEnginePluginResourceManager, GetEngineInfosNullPluginManager)
|
|
{
|
|
const TestableEnginePluginResourceManager resourceManager;
|
|
|
|
auto infos = resourceManager.getEngineInfos();
|
|
|
|
EXPECT_TRUE(infos.empty());
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, ConstructorHandlesMultipleBadPlugins)
|
|
{
|
|
const std::shared_ptr<MockEnginePlugin> nullPlugin = std::make_shared<MockEnginePlugin>();
|
|
const std::shared_ptr<MockEnginePlugin> throwingPlugin = std::make_shared<MockEnginePlugin>();
|
|
const std::shared_ptr<MockEnginePlugin> goodPlugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{nullPlugin, throwingPlugin, goodPlugin};
|
|
const std::shared_ptr<MockEnginePluginManager> pluginManager
|
|
= std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
|
|
// First plugin returns null
|
|
EXPECT_CALL(*nullPlugin, name()).WillRepeatedly(::testing::Return("NullPlugin"));
|
|
EXPECT_CALL(*nullPlugin, createHandle()).WillOnce(::testing::Return(nullptr));
|
|
|
|
// Second plugin throws
|
|
EXPECT_CALL(*throwingPlugin, name()).WillRepeatedly(::testing::Return("ThrowingPlugin"));
|
|
EXPECT_CALL(*throwingPlugin, createHandle())
|
|
.WillOnce(::testing::Throw(std::runtime_error("Plugin initialization failed")));
|
|
|
|
// Third plugin succeeds
|
|
auto goodHandle = reinterpret_cast<hipdnnEnginePluginHandle_t>(0xbeef);
|
|
EXPECT_CALL(*goodPlugin, createHandle()).WillOnce(::testing::Return(goodHandle));
|
|
EXPECT_CALL(*goodPlugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{300}));
|
|
EXPECT_CALL(*goodPlugin, setStream(goodHandle, nullptr));
|
|
EXPECT_CALL(*goodPlugin, destroyHandle(goodHandle));
|
|
|
|
{
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
// Verify good plugin was loaded by calling setStream
|
|
EXPECT_NO_THROW(resourceManager.setStream(nullptr));
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Override execute applicability and dispatch tests.
|
|
// =============================================================================
|
|
|
|
namespace
|
|
{
|
|
|
|
/// Programs `mockGraphDesc.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT, ...)`
|
|
/// to report `flag`. Uses the shared `SetArg4ToBool` action helper from
|
|
/// `MockDescriptor.hpp`. Other attribute lookups remain unhandled (StrictMock
|
|
/// would fail; NaggyMock will warn, which is the existing test convention).
|
|
void programOverrideFlag(MockGraphDescriptor& mockGraphDesc, bool flag)
|
|
{
|
|
EXPECT_CALL(mockGraphDesc,
|
|
getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
::testing::_,
|
|
::testing::_))
|
|
.WillRepeatedly(SetArg4ToBool(flag));
|
|
}
|
|
|
|
enum class OverrideFlagReadMode
|
|
{
|
|
RETURNS_TRUE,
|
|
RETURNS_FALSE,
|
|
THROWS_NOT_SUPPORTED,
|
|
RETURNS_COUNT_ZERO,
|
|
THROWS_INTERNAL_ERROR
|
|
};
|
|
|
|
void programOverrideFlagRead(MockGraphDescriptor& mockGraphDesc, OverrideFlagReadMode mode)
|
|
{
|
|
switch(mode)
|
|
{
|
|
case OverrideFlagReadMode::RETURNS_TRUE:
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/true);
|
|
break;
|
|
case OverrideFlagReadMode::RETURNS_FALSE:
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/false);
|
|
break;
|
|
case OverrideFlagReadMode::THROWS_NOT_SUPPORTED:
|
|
EXPECT_CALL(mockGraphDesc,
|
|
getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
::testing::_,
|
|
::testing::_))
|
|
.WillOnce(::testing::Throw(
|
|
HipdnnException(HIPDNN_STATUS_NOT_SUPPORTED, "override flag not supported")));
|
|
break;
|
|
case OverrideFlagReadMode::RETURNS_COUNT_ZERO:
|
|
EXPECT_CALL(mockGraphDesc,
|
|
getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
::testing::_,
|
|
::testing::_))
|
|
.WillOnce([](hipdnnBackendAttributeName_t,
|
|
hipdnnBackendAttributeType_t,
|
|
int64_t,
|
|
int64_t* elementCount,
|
|
void*) { *elementCount = 0; });
|
|
break;
|
|
case OverrideFlagReadMode::THROWS_INTERNAL_ERROR:
|
|
EXPECT_CALL(mockGraphDesc,
|
|
getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
::testing::_,
|
|
::testing::_))
|
|
.WillOnce(::testing::Throw(
|
|
HipdnnException(HIPDNN_STATUS_INTERNAL_ERROR, "override flag read failed")));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
struct ApplicabilityFilterCase
|
|
{
|
|
const char* name;
|
|
std::string_view apiVersion;
|
|
OverrideFlagReadMode flagReadMode;
|
|
int hasOverrideExecute = -1; // -1: not queried, 0: queried false, 1: queried true.
|
|
std::vector<int64_t> expectedEngineIds;
|
|
hipdnnStatus_t expectedThrow = HIPDNN_STATUS_SUCCESS;
|
|
};
|
|
|
|
class TestEnginePluginResourceManagerApplicabilityFilter
|
|
: public ::testing::TestWithParam<ApplicabilityFilterCase>
|
|
{
|
|
};
|
|
|
|
TEST_P(TestEnginePluginResourceManagerApplicabilityFilter, SinglePluginVersionAndFlagMatrix)
|
|
{
|
|
const auto& testCase = GetParam();
|
|
auto plugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{plugin};
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*plugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*plugin, name()).WillRepeatedly(::testing::Return("MockPlugin"));
|
|
EXPECT_CALL(*plugin, apiVersion()).WillRepeatedly(::testing::Return(testCase.apiVersion));
|
|
EXPECT_CALL(*plugin, destroyHandle(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
programOverrideFlagRead(mockGraphDesc, testCase.flagReadMode);
|
|
|
|
if(testCase.hasOverrideExecute >= 0)
|
|
{
|
|
EXPECT_CALL(*plugin, hasOverrideExecute())
|
|
.WillOnce(::testing::Return(testCase.hasOverrideExecute == 1));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*plugin, hasOverrideExecute()).Times(0);
|
|
}
|
|
|
|
if(testCase.expectedThrow == HIPDNN_STATUS_SUCCESS && !testCase.expectedEngineIds.empty())
|
|
{
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xdeadbeef), _))
|
|
.WillOnce(::testing::Return(testCase.expectedEngineIds));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(_, _)).Times(0);
|
|
}
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
if(testCase.expectedThrow != HIPDNN_STATUS_SUCCESS)
|
|
{
|
|
ASSERT_THROW_HIPDNN_STATUS(resourceManager.getApplicableEngineIds(&mockGraphDesc),
|
|
testCase.expectedThrow);
|
|
return;
|
|
}
|
|
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_EQ(engineIds, testCase.expectedEngineIds);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
SinglePlugin,
|
|
TestEnginePluginResourceManagerApplicabilityFilter,
|
|
::testing::Values(
|
|
ApplicabilityFilterCase{"BaselineExcludedForOverrideGraph",
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
OverrideFlagReadMode::RETURNS_TRUE,
|
|
-1,
|
|
{}},
|
|
ApplicabilityFilterCase{"BaselineIncludedForRegularGraph",
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
OverrideFlagReadMode::RETURNS_FALSE,
|
|
-1,
|
|
{100}},
|
|
ApplicabilityFilterCase{"BaselineIncludedWhenOverrideFlagUnsupported",
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
OverrideFlagReadMode::THROWS_NOT_SUPPORTED,
|
|
-1,
|
|
{100}},
|
|
ApplicabilityFilterCase{"BaselineIncludedWhenOverrideFlagCountZero",
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
OverrideFlagReadMode::RETURNS_COUNT_ZERO,
|
|
-1,
|
|
{100}},
|
|
ApplicabilityFilterCase{"OverrideFlagReadFailurePropagates",
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
OverrideFlagReadMode::THROWS_INTERNAL_ERROR,
|
|
-1,
|
|
{},
|
|
HIPDNN_STATUS_INTERNAL_ERROR},
|
|
ApplicabilityFilterCase{"OverrideCapableIncludedForOverrideGraph",
|
|
hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION,
|
|
OverrideFlagReadMode::RETURNS_TRUE,
|
|
1,
|
|
{100}},
|
|
ApplicabilityFilterCase{"OverrideVersionWithoutSymbolExcludedForOverrideGraph",
|
|
hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION,
|
|
OverrideFlagReadMode::RETURNS_TRUE,
|
|
0,
|
|
{}},
|
|
ApplicabilityFilterCase{"OverrideCapableIncludedForRegularGraph",
|
|
hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION,
|
|
OverrideFlagReadMode::RETURNS_FALSE,
|
|
-1,
|
|
{100}}),
|
|
[](const auto& info) { return std::string(info.param.name); });
|
|
|
|
// Two plugins at K_OVERRIDE_EXECUTE_MIN_API_VERSION both contribute engines for
|
|
// an override-flag graph; the filter does not short-circuit.
|
|
TEST(TestEnginePluginResourceManager,
|
|
ApplicabilityFilterIncludesAllOverrideCapablePluginsForOverrideGraph)
|
|
{
|
|
auto pluginA = std::make_shared<MockEnginePlugin>();
|
|
auto pluginB = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{pluginA, pluginB};
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*pluginA, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xaaaaaaaa)));
|
|
EXPECT_CALL(*pluginA, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*pluginA, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION));
|
|
EXPECT_CALL(*pluginA, destroyHandle(hipdnnEnginePluginHandle_t(0xaaaaaaaa)));
|
|
|
|
EXPECT_CALL(*pluginB, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xbbbbbbbb)));
|
|
EXPECT_CALL(*pluginB, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{200}));
|
|
EXPECT_CALL(*pluginB, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION));
|
|
EXPECT_CALL(*pluginB, destroyHandle(hipdnnEnginePluginHandle_t(0xbbbbbbbb)));
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/true);
|
|
|
|
EXPECT_CALL(*pluginA, getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xaaaaaaaa), _))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*pluginB, getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xbbbbbbbb), _))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{200}));
|
|
EXPECT_CALL(*pluginA, hasOverrideExecute()).WillOnce(::testing::Return(true));
|
|
EXPECT_CALL(*pluginB, hasOverrideExecute()).WillOnce(::testing::Return(true));
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
ASSERT_EQ(engineIds.size(), 2);
|
|
// Order of plugins iteration is unordered_map-defined; assert as set.
|
|
const std::set<int64_t> idSet(engineIds.begin(), engineIds.end());
|
|
EXPECT_EQ(idSet, (std::set<int64_t>{100, 200}));
|
|
}
|
|
|
|
// With no plugins loaded, an override-flag graph yields an empty applicable
|
|
// engine list; the caller surfaces NOT_SUPPORTED.
|
|
TEST(TestEnginePluginResourceManager, ApplicabilityFilterEmptyWhenNoPluginsLoaded)
|
|
{
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins;
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/true);
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_TRUE(engineIds.empty());
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
// Common mock setup for executeOpGraph dispatch tests.
|
|
struct DispatchHarness
|
|
{
|
|
std::shared_ptr<MockEnginePlugin> plugin;
|
|
std::shared_ptr<MockEnginePluginManager> pluginManager;
|
|
std::vector<std::shared_ptr<EnginePlugin>> pluginsList;
|
|
std::unique_ptr<HipdnnBackendDescriptor> executionPlanWrapper;
|
|
std::unique_ptr<HipdnnBackendDescriptor> variantWrapper;
|
|
std::shared_ptr<MockExecutionPlanDescriptor> mockExecutionPlan;
|
|
std::shared_ptr<MockVariantDescriptor> mockVariantPack;
|
|
};
|
|
|
|
DispatchHarness makeDispatchHarness(int64_t engineId,
|
|
hipdnnEnginePluginHandle_t handle,
|
|
hipdnnEnginePluginExecutionContext_t execCtx)
|
|
{
|
|
DispatchHarness h;
|
|
h.plugin = std::make_shared<MockEnginePlugin>();
|
|
h.pluginsList = {h.plugin};
|
|
h.pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
h.executionPlanWrapper = createDescriptor<MockExecutionPlanDescriptor>();
|
|
h.variantWrapper = createDescriptor<MockVariantDescriptor>();
|
|
|
|
h.mockExecutionPlan = MockDescriptorUtility::asDescriptorUnsafe<MockExecutionPlanDescriptor>(
|
|
h.executionPlanWrapper.get());
|
|
h.mockVariantPack
|
|
= MockDescriptorUtility::asDescriptorUnsafe<MockVariantDescriptor>(h.variantWrapper.get());
|
|
|
|
EXPECT_CALL(*h.pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(h.pluginsList));
|
|
EXPECT_CALL(*h.plugin, createHandle()).WillOnce(::testing::Return(handle));
|
|
EXPECT_CALL(*h.plugin, getAllEngineIds())
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{engineId}));
|
|
EXPECT_CALL(*h.plugin, destroyHandle(handle));
|
|
|
|
EXPECT_CALL(*h.mockExecutionPlan, isFinalized()).WillOnce(::testing::Return(true));
|
|
EXPECT_CALL(*h.mockVariantPack, isFinalized()).WillOnce(::testing::Return(true));
|
|
|
|
EXPECT_CALL(*h.mockExecutionPlan, getEngineId()).WillOnce(::testing::Return(engineId));
|
|
// getExecutionContext() is called only on the successful dispatch path (not when dispatch
|
|
// throws before reaching it, e.g. safety-net or bad-param tests).
|
|
EXPECT_CALL(*h.mockExecutionPlan, getExecutionContext())
|
|
.WillRepeatedly(::testing::Return(execCtx));
|
|
return h;
|
|
}
|
|
|
|
enum class DispatchExpectedPath
|
|
{
|
|
LEGACY_EXECUTE,
|
|
THROW_BEFORE_EXECUTE
|
|
};
|
|
|
|
struct DispatchCase
|
|
{
|
|
const char* name;
|
|
int hasOverrideExecute; // -1: not queried, 0: queried false, 1: queried true.
|
|
bool planOverrideShapeEnabled = true;
|
|
std::vector<int64_t> overrideUniqueIds;
|
|
std::vector<int64_t> overrideShapes;
|
|
std::vector<int64_t> overrideStrides;
|
|
std::vector<int64_t> overrideLengths;
|
|
DispatchExpectedPath expectedPath;
|
|
hipdnnStatus_t expectedThrow = HIPDNN_STATUS_SUCCESS;
|
|
std::string_view apiVersion = hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class TestEnginePluginResourceManagerDispatchMatrix : public ::testing::TestWithParam<DispatchCase>
|
|
{
|
|
};
|
|
|
|
TEST_P(TestEnginePluginResourceManagerDispatchMatrix, RoutesOrRejectsOverrideDispatch)
|
|
{
|
|
const auto& testCase = GetParam();
|
|
auto h = makeDispatchHarness(/*engineId=*/100,
|
|
hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe));
|
|
|
|
std::vector<int64_t> tensorIds{1, 2, 3};
|
|
std::vector<const void*> dataPtrs{reinterpret_cast<void*>(0x1000),
|
|
reinterpret_cast<void*>(0x2000),
|
|
reinterpret_cast<void*>(0x3000)};
|
|
std::vector<int64_t> overrideUniqueIds = testCase.overrideUniqueIds;
|
|
std::vector<int64_t> overrideShapes = testCase.overrideShapes;
|
|
std::vector<int64_t> overrideStrides = testCase.overrideStrides;
|
|
std::vector<int64_t> overrideLengths = testCase.overrideLengths;
|
|
|
|
EXPECT_CALL(*h.mockVariantPack, getWorkspace())
|
|
.WillOnce(::testing::Return(reinterpret_cast<void*>(0x4000)));
|
|
EXPECT_CALL(*h.mockVariantPack, getTensorIds()).WillOnce(::testing::ReturnRef(tensorIds));
|
|
EXPECT_CALL(*h.mockVariantPack, getDataPointers()).WillOnce(::testing::ReturnRef(dataPtrs));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideUniqueIds())
|
|
.WillOnce(::testing::ReturnRef(overrideUniqueIds));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideShapes())
|
|
.WillOnce(::testing::ReturnRef(overrideShapes));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideStrides())
|
|
.WillOnce(::testing::ReturnRef(overrideStrides));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideLengths())
|
|
.WillOnce(::testing::ReturnRef(overrideLengths));
|
|
|
|
const bool hasOverrides = !overrideUniqueIds.empty() || !overrideShapes.empty()
|
|
|| !overrideStrides.empty() || !overrideLengths.empty();
|
|
if(hasOverrides)
|
|
{
|
|
EXPECT_CALL(*h.mockExecutionPlan, isOverrideShapeEnabled())
|
|
.WillOnce(::testing::Return(testCase.planOverrideShapeEnabled));
|
|
}
|
|
|
|
if(hasOverrides && testCase.planOverrideShapeEnabled && testCase.hasOverrideExecute >= 0)
|
|
{
|
|
EXPECT_CALL(*h.plugin, hasOverrideExecute())
|
|
.WillOnce(::testing::Return(testCase.hasOverrideExecute == 1));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*h.plugin, hasOverrideExecute()).Times(0);
|
|
}
|
|
|
|
if(hasOverrides && testCase.planOverrideShapeEnabled && testCase.hasOverrideExecute == 1)
|
|
{
|
|
EXPECT_CALL(*h.plugin, apiVersion()).WillOnce(::testing::Return(testCase.apiVersion));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*h.plugin, apiVersion()).Times(0);
|
|
}
|
|
|
|
EXPECT_CALL(*h.plugin, executeOpGraphWithOverrides(_, _, _, _, _, _, _, _, _, _)).Times(0);
|
|
|
|
if(testCase.expectedPath == DispatchExpectedPath::LEGACY_EXECUTE)
|
|
{
|
|
EXPECT_CALL(*h.plugin,
|
|
executeOpGraph(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
reinterpret_cast<void*>(0x4000),
|
|
::testing::NotNull(),
|
|
static_cast<uint32_t>(3)));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*h.plugin, executeOpGraph(_, _, _, _, _)).Times(0);
|
|
}
|
|
|
|
const EnginePluginResourceManager resourceManager(h.pluginManager);
|
|
if(testCase.expectedThrow != HIPDNN_STATUS_SUCCESS)
|
|
{
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
resourceManager.executeOpGraph(h.executionPlanWrapper.get(), h.variantWrapper.get()),
|
|
testCase.expectedThrow);
|
|
return;
|
|
}
|
|
|
|
resourceManager.executeOpGraph(h.executionPlanWrapper.get(), h.variantWrapper.get());
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
OverrideDispatch,
|
|
TestEnginePluginResourceManagerDispatchMatrix,
|
|
::testing::Values(DispatchCase{"VersionLiarPluginRejected",
|
|
/*hasOverrideExecute=*/0,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1},
|
|
{2, 3, 4},
|
|
{12, 4, 1},
|
|
{3},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_NOT_SUPPORTED},
|
|
DispatchCase{"NoOverridesRoutesToLegacyEntry",
|
|
/*hasOverrideExecute=*/-1,
|
|
/*planOverrideShapeEnabled=*/false,
|
|
{},
|
|
{},
|
|
{},
|
|
{},
|
|
DispatchExpectedPath::LEGACY_EXECUTE},
|
|
DispatchCase{"NoOverridesRoutesToLegacyEntryWhenPlanOverrideEnabled",
|
|
/*hasOverrideExecute=*/-1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{},
|
|
{},
|
|
{},
|
|
{},
|
|
DispatchExpectedPath::LEGACY_EXECUTE},
|
|
DispatchCase{"MismatchedOverrideLengthsRejected",
|
|
/*hasOverrideExecute=*/-1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1, 2},
|
|
{2, 3, 4, 5},
|
|
{12, 4, 1, 1},
|
|
{3},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_BAD_PARAM},
|
|
DispatchCase{"OverrideShapesFlatCountMismatchRejected",
|
|
/*hasOverrideExecute=*/1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1},
|
|
{2, 3},
|
|
{12, 4, 1},
|
|
{3},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_BAD_PARAM},
|
|
DispatchCase{"OverrideStridesFlatCountMismatchRejected",
|
|
/*hasOverrideExecute=*/1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1},
|
|
{2, 3, 4},
|
|
{12, 4, 1, 1},
|
|
{3},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_BAD_PARAM},
|
|
DispatchCase{"ZeroOverrideLengthRejected",
|
|
/*hasOverrideExecute=*/1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1},
|
|
{},
|
|
{},
|
|
{0},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_BAD_PARAM_OUT_OF_BOUND},
|
|
DispatchCase{"OverrideLengthExceedsUint32Rejected",
|
|
/*hasOverrideExecute=*/1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1},
|
|
{},
|
|
{},
|
|
{static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_BAD_PARAM_OUT_OF_BOUND},
|
|
DispatchCase{"OverrideMetadataRejectedWhenPlanNotOverrideEnabled",
|
|
/*hasOverrideExecute=*/1,
|
|
/*planOverrideShapeEnabled=*/false,
|
|
{1},
|
|
{2, 3, 4},
|
|
{12, 4, 1},
|
|
{3},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_NOT_SUPPORTED},
|
|
DispatchCase{"OldApiVersionWithOverrideSymbolRejected",
|
|
/*hasOverrideExecute=*/1,
|
|
/*planOverrideShapeEnabled=*/true,
|
|
{1},
|
|
{2, 3, 4},
|
|
{12, 4, 1},
|
|
{3},
|
|
DispatchExpectedPath::THROW_BEFORE_EXECUTE,
|
|
HIPDNN_STATUS_NOT_SUPPORTED,
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE}),
|
|
[](const auto& info) { return std::string(info.param.name); });
|
|
|
|
// When the variant pack carries override tensors and the plugin exports the
|
|
// override symbol, dispatch must:
|
|
// - call `executeOpGraphWithOverrides` (NOT `executeOpGraph`),
|
|
// - reconstruct per-UID shape/stride pointer arrays from the flat buffers
|
|
// (3 tensors of ranks 3, 4, 4 over an 11-element buffer),
|
|
// - narrow lengths from int64 to uint32,
|
|
// - read each variant-pack accessor exactly once (lifetime / mutation safety).
|
|
TEST(TestEnginePluginResourceManager, DispatchRoutesToOverrideEntryWithReconstructedPtrArrays)
|
|
{
|
|
auto h = makeDispatchHarness(/*engineId=*/100,
|
|
hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe));
|
|
|
|
std::vector<int64_t> tensorIds{1, 2, 3};
|
|
std::vector<const void*> dataPtrs{reinterpret_cast<void*>(0x1000),
|
|
reinterpret_cast<void*>(0x2000),
|
|
reinterpret_cast<void*>(0x3000)};
|
|
|
|
// Three tensors of ranks {3, 4, 4}: flat-buffer offsets are {0, 3, 7}.
|
|
std::vector<int64_t> overrideUniqueIds{1, 2, 3};
|
|
std::vector<int64_t> overrideLengths{3, 4, 4};
|
|
std::vector<int64_t> overrideShapes{// tensor 1 (rank 3)
|
|
2,
|
|
3,
|
|
4,
|
|
// tensor 2 (rank 4)
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
// tensor 3 (rank 4)
|
|
5,
|
|
6,
|
|
7,
|
|
8};
|
|
std::vector<int64_t> overrideStrides{// tensor 1
|
|
12,
|
|
4,
|
|
1,
|
|
// tensor 2
|
|
24,
|
|
12,
|
|
4,
|
|
1,
|
|
// tensor 3
|
|
336,
|
|
56,
|
|
8,
|
|
1};
|
|
|
|
EXPECT_CALL(*h.mockVariantPack, getWorkspace())
|
|
.WillOnce(::testing::Return(reinterpret_cast<void*>(0x4000)));
|
|
EXPECT_CALL(*h.mockVariantPack, getTensorIds()).WillOnce(::testing::ReturnRef(tensorIds));
|
|
EXPECT_CALL(*h.mockVariantPack, getDataPointers()).WillOnce(::testing::ReturnRef(dataPtrs));
|
|
|
|
// Each accessor is read exactly once by the dispatch wrapper. WillOnce
|
|
// here doubles as a strict-arity check.
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideUniqueIds())
|
|
.WillOnce(::testing::ReturnRef(overrideUniqueIds));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideShapes())
|
|
.WillOnce(::testing::ReturnRef(overrideShapes));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideStrides())
|
|
.WillOnce(::testing::ReturnRef(overrideStrides));
|
|
EXPECT_CALL(*h.mockVariantPack, getOverrideLengths())
|
|
.WillOnce(::testing::ReturnRef(overrideLengths));
|
|
|
|
EXPECT_CALL(*h.plugin, hasOverrideExecute()).WillOnce(::testing::Return(true));
|
|
EXPECT_CALL(*h.plugin, apiVersion())
|
|
.WillOnce(::testing::Return(hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION));
|
|
EXPECT_CALL(*h.mockExecutionPlan, isOverrideShapeEnabled()).WillOnce(::testing::Return(true));
|
|
|
|
// Capture the call to verify length narrowing and pointer reconstruction.
|
|
EXPECT_CALL(*h.plugin,
|
|
executeOpGraphWithOverrides(hipdnnEnginePluginHandle_t(0xdeadbeef),
|
|
hipdnnEnginePluginExecutionContext_t(0xcafebabe),
|
|
reinterpret_cast<void*>(0x4000),
|
|
::testing::NotNull(), // deviceBuffers
|
|
static_cast<uint32_t>(3), // numDeviceBuffers
|
|
static_cast<uint32_t>(3), // numOverrides
|
|
::testing::_, // overrideUniqueIds
|
|
::testing::_, // overrideLengths (uint32)
|
|
::testing::_, // overrideShapes (ptr-array)
|
|
::testing::_)) // overrideStrides (ptr-array)
|
|
.WillOnce(::testing::Invoke([&overrideUniqueIds, &overrideShapes, &overrideStrides](
|
|
hipdnnEnginePluginHandle_t,
|
|
hipdnnEnginePluginExecutionContext_t,
|
|
void*,
|
|
const hipdnnPluginDeviceBuffer_t*,
|
|
uint32_t,
|
|
uint32_t numOverrides,
|
|
const int64_t* uniqueIds,
|
|
const uint32_t* lengths,
|
|
const int64_t* const* shapesPerUid,
|
|
const int64_t* const* stridesPerUid) {
|
|
ASSERT_EQ(numOverrides, 3u);
|
|
// Lengths are narrowed to uint32 with values matching the int64 source.
|
|
EXPECT_EQ(lengths[0], 3u);
|
|
EXPECT_EQ(lengths[1], 4u);
|
|
EXPECT_EQ(lengths[2], 4u);
|
|
// unique ids forwarded by-pointer; values match.
|
|
EXPECT_EQ(uniqueIds[0], overrideUniqueIds[0]);
|
|
EXPECT_EQ(uniqueIds[1], overrideUniqueIds[1]);
|
|
EXPECT_EQ(uniqueIds[2], overrideUniqueIds[2]);
|
|
// Per-UID shape pointers reference offsets {0, 3, 7} into the flat
|
|
// shape buffer owned by the variant pack.
|
|
EXPECT_EQ(shapesPerUid[0], overrideShapes.data() + 0);
|
|
EXPECT_EQ(shapesPerUid[1], overrideShapes.data() + 3);
|
|
EXPECT_EQ(shapesPerUid[2], overrideShapes.data() + 7);
|
|
// Stride pointers use the same offsets.
|
|
EXPECT_EQ(stridesPerUid[0], overrideStrides.data() + 0);
|
|
EXPECT_EQ(stridesPerUid[1], overrideStrides.data() + 3);
|
|
EXPECT_EQ(stridesPerUid[2], overrideStrides.data() + 7);
|
|
// Spot-check a value through each pointer to confirm the slice
|
|
// really sees the per-tensor data (lifetime safety: the data
|
|
// is still live at dispatch time).
|
|
EXPECT_EQ(shapesPerUid[1][0], 1);
|
|
EXPECT_EQ(shapesPerUid[2][3], 8);
|
|
EXPECT_EQ(stridesPerUid[1][3], 1);
|
|
}));
|
|
|
|
// Legacy entry must NOT be reached.
|
|
EXPECT_CALL(*h.plugin, executeOpGraph(_, _, _, _, _)).Times(0);
|
|
|
|
const EnginePluginResourceManager resourceManager(h.pluginManager);
|
|
resourceManager.executeOpGraph(h.executionPlanWrapper.get(), h.variantWrapper.get());
|
|
}
|
|
|
|
// =============================================================================
|
|
// `PluginBase` metadata.
|
|
// =============================================================================
|
|
TEST(TestPluginBase, MockPluginCachedNameUsesDeterministicFallback)
|
|
{
|
|
const MockEnginePlugin plugin;
|
|
|
|
EXPECT_EQ(plugin.cachedName(), "mock_plugin");
|
|
}
|
|
|
|
TEST(TestPluginBase, ParsedApiVersionReturnsNulloptForMalformedString)
|
|
{
|
|
const MockEnginePlugin plugin;
|
|
|
|
EXPECT_CALL(plugin, apiVersion())
|
|
.Times(1)
|
|
.WillOnce(::testing::Return(std::string_view{"not.a.version"}));
|
|
|
|
const auto parsed = plugin.parsedApiVersion();
|
|
EXPECT_FALSE(parsed.has_value()) << "Malformed version string must yield nullopt.";
|
|
}
|
|
|
|
TEST(TestPluginBase, ParsedApiVersionReturnsParsedValueForWellFormedString)
|
|
{
|
|
const MockEnginePlugin plugin;
|
|
|
|
EXPECT_CALL(plugin, apiVersion())
|
|
.Times(1)
|
|
.WillOnce(::testing::Return(hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION));
|
|
|
|
const auto parsed = plugin.parsedApiVersion();
|
|
ASSERT_TRUE(parsed.has_value()) << "Well-formed version string must parse successfully.";
|
|
EXPECT_EQ(parsed->major, 1);
|
|
EXPECT_EQ(parsed->minor, 1);
|
|
EXPECT_EQ(parsed->patch, 0);
|
|
}
|
|
|
|
// Ragged tensor version gating: a plugin at the baseline version is excluded
|
|
// when the graph reports ragged tensors, and a plugin at
|
|
// K_RAGGED_TENSOR_MIN_API_VERSION is included.
|
|
TEST(TestEnginePluginResourceManager, RaggedTensorGraphExcludesBaselinePlugin)
|
|
{
|
|
auto plugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{plugin};
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*plugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*plugin, name()).WillRepeatedly(::testing::Return("BaselinePlugin"));
|
|
EXPECT_CALL(*plugin, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE));
|
|
EXPECT_CALL(*plugin, destroyHandle(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(_, _)).Times(0);
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/false);
|
|
EXPECT_CALL(mockGraphDesc, hasRaggedTensors()).WillRepeatedly(::testing::Return(true));
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_TRUE(engineIds.empty());
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, RaggedTensorGraphIncludesRaggedCapablePlugin)
|
|
{
|
|
auto plugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{plugin};
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*plugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*plugin, name()).WillRepeatedly(::testing::Return("RaggedPlugin"));
|
|
EXPECT_CALL(*plugin, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_RAGGED_TENSOR_MIN_API_VERSION));
|
|
EXPECT_CALL(*plugin, destroyHandle(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xdeadbeef), _))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/false);
|
|
EXPECT_CALL(mockGraphDesc, hasRaggedTensors()).WillRepeatedly(::testing::Return(true));
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
ASSERT_EQ(engineIds.size(), 1u);
|
|
EXPECT_EQ(engineIds[0], 100);
|
|
}
|
|
|
|
TEST(TestEnginePluginResourceManager, NonRaggedGraphUnaffectedByRaggedVersionConstant)
|
|
{
|
|
auto plugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{plugin};
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*plugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*plugin, name()).WillRepeatedly(::testing::Return("BaselinePlugin"));
|
|
EXPECT_CALL(*plugin, apiVersion())
|
|
.WillRepeatedly(::testing::Return(hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE));
|
|
EXPECT_CALL(*plugin, destroyHandle(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xdeadbeef), _))
|
|
.WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
|
|
// Non-ragged graph: hasRaggedTensors() defaults to the real base-class
|
|
// accessor, which reports false for a descriptor with no ragged tensors.
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
programOverrideFlag(mockGraphDesc, /*flag=*/false);
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
ASSERT_EQ(engineIds.size(), 1u);
|
|
EXPECT_EQ(engineIds[0], 100);
|
|
}
|
|
|
|
// =============================================================================
|
|
// Ragged-tensor applicability filter (mock-based matrix).
|
|
//
|
|
// Mirrors the override-dispatch matrix (RoutesOrRejectsOverrideDispatch): drives
|
|
// the version-gating logic in getApplicableEngineIds() in isolation by mocking
|
|
// GraphDescriptor::hasRaggedTensors() directly, rather than building a descriptor
|
|
// containing an actual ragged tensor (covered by the standalone tests above and
|
|
// by TestGraphDescriptor). A graph reporting ragged tensors requires a plugin
|
|
// advertising at least K_RAGGED_TENSOR_MIN_API_VERSION; otherwise the baseline
|
|
// applies.
|
|
// =============================================================================
|
|
namespace
|
|
{
|
|
|
|
// Whether the plugin's hasOverrideExecute() capability is expected to be queried,
|
|
// and with what result. It is only queried when the graph enables override-shape
|
|
// and the plugin has already cleared the version gate.
|
|
enum class OverrideExecuteQuery
|
|
{
|
|
NOT_QUERIED,
|
|
RETURNS_FALSE,
|
|
RETURNS_TRUE,
|
|
};
|
|
|
|
struct RaggedApplicabilityCase
|
|
{
|
|
const char* name;
|
|
bool raggedEnabled;
|
|
std::string_view apiVersion;
|
|
std::vector<int64_t> expectedEngineIds; // empty => plugin excluded
|
|
bool overrideEnabled = false;
|
|
OverrideExecuteQuery overrideExecuteQuery = OverrideExecuteQuery::NOT_QUERIED;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class TestEnginePluginResourceManagerRaggedApplicabilityMatrix
|
|
: public ::testing::TestWithParam<RaggedApplicabilityCase>
|
|
{
|
|
};
|
|
|
|
TEST_P(TestEnginePluginResourceManagerRaggedApplicabilityMatrix, RoutesOrRejectsRaggedApplicability)
|
|
{
|
|
const auto& testCase = GetParam();
|
|
auto plugin = std::make_shared<MockEnginePlugin>();
|
|
std::vector<std::shared_ptr<EnginePlugin>> plugins{plugin};
|
|
auto pluginManager = std::make_shared<MockEnginePluginManager>();
|
|
|
|
EXPECT_CALL(*pluginManager, getPlugins()).WillOnce(::testing::ReturnRef(plugins));
|
|
EXPECT_CALL(*plugin, createHandle())
|
|
.WillOnce(::testing::Return(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
EXPECT_CALL(*plugin, getAllEngineIds()).WillOnce(::testing::Return(std::vector<int64_t>{100}));
|
|
EXPECT_CALL(*plugin, name()).WillRepeatedly(::testing::Return("MockPlugin"));
|
|
EXPECT_CALL(*plugin, apiVersion()).WillRepeatedly(::testing::Return(testCase.apiVersion));
|
|
EXPECT_CALL(*plugin, destroyHandle(hipdnnEnginePluginHandle_t(0xdeadbeef)));
|
|
|
|
MockGraphDescriptor mockGraphDesc;
|
|
const hipdnnPluginConstData_t fakeSerializedData
|
|
= {reinterpret_cast<const void*>("fake_graph_data"), 15};
|
|
EXPECT_CALL(mockGraphDesc, getSerializedGraph())
|
|
.WillOnce(::testing::Return(fakeSerializedData));
|
|
// The override-shape flag is read first (via getAttribute). Most cases keep it
|
|
// off so only the ragged-tensor gate decides applicability; the combined
|
|
// override+ragged cases turn it on to exercise the version-max composition.
|
|
programOverrideFlag(mockGraphDesc, testCase.overrideEnabled);
|
|
EXPECT_CALL(mockGraphDesc, hasRaggedTensors())
|
|
.WillRepeatedly(::testing::Return(testCase.raggedEnabled));
|
|
|
|
switch(testCase.overrideExecuteQuery)
|
|
{
|
|
case OverrideExecuteQuery::NOT_QUERIED:
|
|
EXPECT_CALL(*plugin, hasOverrideExecute()).Times(0);
|
|
break;
|
|
case OverrideExecuteQuery::RETURNS_FALSE:
|
|
EXPECT_CALL(*plugin, hasOverrideExecute()).WillOnce(::testing::Return(false));
|
|
break;
|
|
case OverrideExecuteQuery::RETURNS_TRUE:
|
|
EXPECT_CALL(*plugin, hasOverrideExecute()).WillOnce(::testing::Return(true));
|
|
break;
|
|
default:
|
|
FAIL();
|
|
}
|
|
|
|
if(!testCase.expectedEngineIds.empty())
|
|
{
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(hipdnnEnginePluginHandle_t(0xdeadbeef), _))
|
|
.WillOnce(::testing::Return(testCase.expectedEngineIds));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*plugin, getApplicableEngineIds(_, _)).Times(0);
|
|
}
|
|
|
|
const EnginePluginResourceManager resourceManager(pluginManager);
|
|
auto engineIds = resourceManager.getApplicableEngineIds(&mockGraphDesc);
|
|
EXPECT_EQ(engineIds, testCase.expectedEngineIds);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
RaggedApplicability,
|
|
TestEnginePluginResourceManagerRaggedApplicabilityMatrix,
|
|
::testing::Values(
|
|
RaggedApplicabilityCase{"BaselineExcludedForRaggedGraph",
|
|
/*raggedEnabled=*/true,
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
{}},
|
|
RaggedApplicabilityCase{"OverrideMinExcludedForRaggedGraph",
|
|
/*raggedEnabled=*/true,
|
|
hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION,
|
|
{}},
|
|
RaggedApplicabilityCase{"RaggedMinIncludedForRaggedGraph",
|
|
/*raggedEnabled=*/true,
|
|
hipdnn_plugin_sdk::K_RAGGED_TENSOR_MIN_API_VERSION,
|
|
{100}},
|
|
RaggedApplicabilityCase{"AboveRaggedMinIncludedForRaggedGraph",
|
|
/*raggedEnabled=*/true,
|
|
std::string_view{"1.3.0"},
|
|
{100}},
|
|
RaggedApplicabilityCase{"BaselineIncludedForNonRaggedGraph",
|
|
/*raggedEnabled=*/false,
|
|
hipdnn_plugin_sdk::K_ENGINE_PLUGIN_API_VERSION_BASELINE,
|
|
{100}},
|
|
// Combined override + ragged: the required version is the MAX of both
|
|
// features' minimums (ragged, 1.2.0), not whichever an if-chain checks
|
|
// first. An override-min (1.1.0) plugin is rejected by the version gate
|
|
// before its override capability is even queried.
|
|
RaggedApplicabilityCase{"OverrideMinExcludedForOverrideAndRaggedGraph",
|
|
/*raggedEnabled=*/true,
|
|
hipdnn_plugin_sdk::K_OVERRIDE_EXECUTE_MIN_API_VERSION,
|
|
{},
|
|
/*overrideEnabled=*/true,
|
|
OverrideExecuteQuery::NOT_QUERIED},
|
|
// A ragged-min (1.2.0) plugin clears the version gate; with override also
|
|
// enabled its override capability is then queried and, being present,
|
|
// the plugin is included.
|
|
RaggedApplicabilityCase{"RaggedMinIncludedForOverrideAndRaggedGraph",
|
|
/*raggedEnabled=*/true,
|
|
hipdnn_plugin_sdk::K_RAGGED_TENSOR_MIN_API_VERSION,
|
|
{100},
|
|
/*overrideEnabled=*/true,
|
|
OverrideExecuteQuery::RETURNS_TRUE}),
|
|
[](const auto& info) { return std::string(info.param.name); });
|