Files
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

2015 lines
89 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <array>
#include <cstring>
#include <string>
#include <hipdnn_frontend/detail/DescriptorUnpackHelpers.hpp>
#include "fake_backend/MockHipdnnBackend.hpp"
using namespace hipdnn_frontend;
using namespace hipdnn_frontend::graph;
using namespace hipdnn_frontend::detail;
using namespace ::testing;
// ---------------------------------------------------------------------------
// Shared mock helpers
// ---------------------------------------------------------------------------
namespace
{
/// Registers all backendGetAttribute expectations needed for unpackTensorAttributes to
/// fully unpack a standard FLOAT tensor with 4-D dims/strides.
/// uid is returned for HIPDNN_ATTR_TENSOR_UNIQUE_ID (WillRepeatedly to cover map-lookup
/// calls in addition to the unpack call).
void expectFullTensorMocksForDesc(Mock_hipdnn_backend& mock,
hipdnnBackendDescriptor_t fakeDesc,
int64_t uid,
const std::array<int64_t, 4>& dims,
const std::array<int64_t, 4>& strides)
{
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillRepeatedly(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([uid](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock, backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_NAME_EXT, HIPDNN_TYPE_CHAR, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto dt = HIPDNN_DATA_FLOAT;
std::memcpy(arrayOfElements, &dt, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([dims](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, dims.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock, backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_STRIDES, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([strides](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, strides.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_IS_VIRTUAL, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_IS_BY_VALUE, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(mock,
backendGetAttribute(fakeDesc,
HIPDNN_ATTR_TENSOR_IS_RUNTIME_PASS_BY_VALUE_EXT,
HIPDNN_TYPE_BOOLEAN,
1,
_,
_))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
}
/// Registers all backendGetAttribute expectations needed for unpackTensorAttributes to
/// unpack a scalar (dims={1}, strides={1}) pass-by-value tensor.
/// Parameterised on data type and scalar value so both FLOAT and INT64 variants
/// can share the same setup.
template <typename T>
void expectScalarByValueTensorMocks(Mock_hipdnn_backend& mock,
hipdnnBackendDescriptor_t fakeDesc,
int64_t uid,
hipdnnDataType_t dataType,
T scalarValue)
{
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([uid](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock, backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_NAME_EXT, HIPDNN_TYPE_CHAR, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([dataType](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, &dataType, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
int64_t dim = 1;
std::memcpy(arrayOfElements, &dim, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock, backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_STRIDES, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
int64_t stride = 1;
std::memcpy(arrayOfElements, &stride, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_IS_VIRTUAL, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_IS_BY_VALUE, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = true;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(
mock,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_VALUE_EXT, HIPDNN_TYPE_CHAR, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(sizeof(T))),
Invoke([scalarValue](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, &scalarValue, sizeof(T));
}),
Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(mock,
backendGetAttribute(fakeDesc,
HIPDNN_ATTR_TENSOR_IS_RUNTIME_PASS_BY_VALUE_EXT,
HIPDNN_TYPE_BOOLEAN,
1,
_,
_))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
}
} // namespace
class TestDescriptorUnpackHelpers : public ::testing::Test
{
protected:
std::shared_ptr<Mock_hipdnn_backend> _mockBackend;
void SetUp() override
{
_mockBackend = std::make_shared<Mock_hipdnn_backend>();
IHipdnnBackend::setInstance(_mockBackend);
}
void TearDown() override
{
IHipdnnBackend::resetInstance();
_mockBackend.reset();
}
};
// ---------------------------------------------------------------------------
// getDescriptorAttrVec tests
// ---------------------------------------------------------------------------
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecSuccess)
{
// Count query returns 3
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{3}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns {1, 2, 3}
constexpr std::array<int64_t, 3> K_DATA = {1, 2, 3};
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 3, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{3}),
Invoke([K_DATA](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_DATA.data(), 3 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int64_t> values;
auto err = getDescriptorAttrVec(
desc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, values, "test dims");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(values.size(), 3u);
EXPECT_EQ(values[0], 1);
EXPECT_EQ(values[1], 2);
EXPECT_EQ(values[2], 3);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecZeroCount)
{
// Count query returns 0
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int64_t> values;
auto err = getDescriptorAttrVec(
desc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, values, "test dims");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(values.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecNegativeCount)
{
// Count query returns -1 (treated as <= 0 by the guard in production code)
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{-1}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int64_t> values;
auto err = getDescriptorAttrVec(
desc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, values, "test dims");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(values.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecCountFails)
{
// Count query fails
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int64_t> values;
auto err = getDescriptorAttrVec(
desc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, values, "test dims");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecCountMismatch)
{
// Count query returns 3
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{3}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns actualCount=5 (mismatches count=3)
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 3, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{5}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int64_t> values;
auto err = getDescriptorAttrVec(
desc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, values, "test dims");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// getDescriptorAttrVec (typed overload) tests
// ---------------------------------------------------------------------------
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecInt32Success)
{
// Count query returns 2
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT32, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns {16, 32}
constexpr std::array<int32_t, 2> K_DATA = {16, 32};
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT32, 2, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}),
Invoke([K_DATA](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_DATA.data(), 2 * sizeof(int32_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int32_t> values;
auto err = getDescriptorAttrVec(desc,
HIPDNN_ATTR_OPERATION_BLOCK_SCALE_DEQUANTIZE_BLOCK_SIZE,
HIPDNN_TYPE_INT32,
values,
"test block_size");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(values.size(), 2u);
EXPECT_EQ(values[0], 16);
EXPECT_EQ(values[1], 32);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecInt32ZeroCount)
{
// Count query returns 0
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT32, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int32_t> values;
auto err = getDescriptorAttrVec(desc,
HIPDNN_ATTR_OPERATION_BLOCK_SCALE_DEQUANTIZE_BLOCK_SIZE,
HIPDNN_TYPE_INT32,
values,
"test block_size");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(values.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrVecInt32CountMismatch)
{
// Count query returns 2
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT32, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns actualCount=3 (mismatches count=2)
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT32, 2, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{3}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<int32_t> values;
auto err = getDescriptorAttrVec(desc,
HIPDNN_ATTR_OPERATION_BLOCK_SCALE_DEQUANTIZE_BLOCK_SIZE,
HIPDNN_TYPE_INT32,
values,
"test block_size");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// getDescriptorAttrScalar tests
// ---------------------------------------------------------------------------
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrScalarSuccess)
{
constexpr int64_t K_VALUE = 42;
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([K_VALUE](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, &K_VALUE, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
int64_t value = 0;
auto err = getDescriptorAttrScalar(
desc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, value, "test uid");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_EQ(value, 42);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrScalarFails)
{
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
int64_t value = 0;
auto err = getDescriptorAttrScalar(
desc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, value, "test uid");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// unpackTensorAttributes tests
// ---------------------------------------------------------------------------
class TestUnpackTensorAttributes : public TestDescriptorUnpackHelpers
{
protected:
// Fake descriptor pointer for the tensor
int _descPlaceholder = 0;
hipdnnBackendDescriptor_t _fakeDesc
= reinterpret_cast<hipdnnBackendDescriptor_t>(&_descPlaceholder);
static constexpr int64_t K_UID = 42;
static constexpr std::array<int64_t, 4> K_DIMS = {1, 3, 32, 32};
static constexpr std::array<int64_t, 4> K_STRIDES = {3072, 1024, 32, 1};
void expectFullTensorMocksForDesc(hipdnnBackendDescriptor_t fakeDesc,
int64_t uid,
const std::array<int64_t, 4>& dims = K_DIMS,
const std::array<int64_t, 4>& strides = K_STRIDES)
{
::expectFullTensorMocksForDesc(*_mockBackend, fakeDesc, uid, dims, strides);
}
/// Convenience overload using the fixture's _fakeDesc and K_UID.
void expectFullTensorMocks()
{
expectFullTensorMocksForDesc(_fakeDesc, K_UID);
}
};
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesSuccess)
{
expectFullTensorMocks();
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(tensor, nullptr);
EXPECT_EQ(tensor->get_uid(), K_UID);
EXPECT_EQ(tensor->get_data_type(), DataType::FLOAT);
EXPECT_EQ(tensor->get_dim(), (std::vector<int64_t>{K_DIMS.begin(), K_DIMS.end()}));
EXPECT_EQ(tensor->get_stride(), (std::vector<int64_t>{K_STRIDES.begin(), K_STRIDES.end()}));
EXPECT_FALSE(tensor->get_is_virtual());
EXPECT_FALSE(tensor->get_is_pass_by_value());
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesRestoresPassByValue)
{
static constexpr float K_SCALAR_VALUE = 1e-5f;
expectScalarByValueTensorMocks(
*_mockBackend, _fakeDesc, K_UID, HIPDNN_DATA_FLOAT, K_SCALAR_VALUE);
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(tensor, nullptr);
EXPECT_EQ(tensor->get_uid(), K_UID);
EXPECT_EQ(tensor->get_data_type(), DataType::FLOAT);
EXPECT_EQ(tensor->get_dim(), (std::vector<int64_t>{1}));
EXPECT_EQ(tensor->get_stride(), (std::vector<int64_t>{1}));
// IS_RUNTIME_PASS_BY_VALUE is never set by these mocks (flag == false), so a
// value + cleared flag is a compile-time constant per RFC-0016 §4.2.
EXPECT_TRUE(tensor->get_is_pass_by_value());
EXPECT_FALSE(tensor->get_is_runtime_pass_by_value());
ASSERT_TRUE(tensor->get_compile_time_constant<float>().has_value());
EXPECT_FLOAT_EQ(tensor->get_compile_time_constant<float>().value(), K_SCALAR_VALUE);
EXPECT_FALSE(tensor->get_pass_by_value<float>().has_value());
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesRestoresPassByValueInt64)
{
static constexpr int64_t K_SCALAR_VALUE = 123456789012345LL;
expectScalarByValueTensorMocks(
*_mockBackend, _fakeDesc, K_UID, HIPDNN_DATA_INT64, K_SCALAR_VALUE);
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(tensor, nullptr);
EXPECT_EQ(tensor->get_uid(), K_UID);
EXPECT_EQ(tensor->get_data_type(), DataType::INT64);
EXPECT_EQ(tensor->get_dim(), (std::vector<int64_t>{1}));
EXPECT_EQ(tensor->get_stride(), (std::vector<int64_t>{1}));
EXPECT_TRUE(tensor->get_is_pass_by_value());
EXPECT_FALSE(tensor->get_is_runtime_pass_by_value());
ASSERT_TRUE(tensor->get_compile_time_constant<int64_t>().has_value());
EXPECT_EQ(tensor->get_compile_time_constant<int64_t>().value(), K_SCALAR_VALUE);
EXPECT_FALSE(tensor->get_pass_by_value<int64_t>().has_value());
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesRestoresPassByValueBoolean)
{
static constexpr bool K_SCALAR_VALUE = true;
expectScalarByValueTensorMocks(
*_mockBackend, _fakeDesc, K_UID, HIPDNN_DATA_BOOLEAN, K_SCALAR_VALUE);
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(tensor, nullptr);
EXPECT_EQ(tensor->get_uid(), K_UID);
EXPECT_EQ(tensor->get_data_type(), DataType::BOOLEAN);
EXPECT_EQ(tensor->get_dim(), (std::vector<int64_t>{1}));
EXPECT_EQ(tensor->get_stride(), (std::vector<int64_t>{1}));
EXPECT_TRUE(tensor->get_is_pass_by_value());
EXPECT_FALSE(tensor->get_is_runtime_pass_by_value());
ASSERT_TRUE(tensor->get_compile_time_constant<bool>().has_value());
EXPECT_EQ(tensor->get_compile_time_constant<bool>().value(), K_SCALAR_VALUE);
EXPECT_FALSE(tensor->get_pass_by_value<bool>().has_value());
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesPassByValuePreserves4dDims)
{
static constexpr float K_SCALAR_VALUE = 1e-5f;
static constexpr std::array<int64_t, 4> K_SCALAR_DIMS = {1, 1, 1, 1};
static constexpr std::array<int64_t, 4> K_SCALAR_STRIDES = {1, 1, 1, 1};
// UID
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Name (empty)
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_NAME_EXT, HIPDNN_TYPE_CHAR, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
// Data type (FLOAT)
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto dt = HIPDNN_DATA_FLOAT;
std::memcpy(arrayOfElements, &dt, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Dims: 4D {1, 1, 1, 1}
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_SCALAR_DIMS.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Strides: 4D {1, 1, 1, 1}
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_STRIDES, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(
arrayOfElements, K_SCALAR_STRIDES.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// is_virtual: false
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_IS_VIRTUAL, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// IS_BY_VALUE: true
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_IS_BY_VALUE, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = true;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// TENSOR_VALUE: return the raw float bytes
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_VALUE_EXT, HIPDNN_TYPE_CHAR, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(sizeof(float))),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = K_SCALAR_VALUE;
std::memcpy(arrayOfElements, &val, sizeof(float));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// IS_RUNTIME_PASS_BY_VALUE: false (compile-time constant)
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeDesc,
HIPDNN_ATTR_TENSOR_IS_RUNTIME_PASS_BY_VALUE_EXT,
HIPDNN_TYPE_BOOLEAN,
1,
_,
_))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(tensor, nullptr);
EXPECT_EQ(tensor->get_uid(), K_UID);
EXPECT_EQ(tensor->get_data_type(), DataType::FLOAT);
// Flag never set on the mock → compile-time constant; dims/strides must survive.
EXPECT_TRUE(tensor->get_is_pass_by_value());
EXPECT_FALSE(tensor->get_is_runtime_pass_by_value());
ASSERT_TRUE(tensor->get_compile_time_constant<float>().has_value());
EXPECT_FLOAT_EQ(tensor->get_compile_time_constant<float>().value(), K_SCALAR_VALUE);
EXPECT_FALSE(tensor->get_pass_by_value<float>().has_value());
// Verify dims and strides are preserved (not reset to {1} by set_value)
EXPECT_EQ(tensor->get_dim(),
(std::vector<int64_t>{K_SCALAR_DIMS.begin(), K_SCALAR_DIMS.end()}));
EXPECT_EQ(tensor->get_stride(),
(std::vector<int64_t>{K_SCALAR_STRIDES.begin(), K_SCALAR_STRIDES.end()}));
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesMissingDimsFails)
{
// UID scalar succeeds
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Data type scalar succeeds
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto dt = HIPDNN_DATA_FLOAT;
std::memcpy(arrayOfElements, &dt, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Dims count query fails
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// unpackAndRegisterTensor tests
// ---------------------------------------------------------------------------
class TestUnpackAndRegisterTensor : public TestUnpackTensorAttributes
{
protected:
void expectDescriptorGet(hipdnnBackendAttributeName_t tensorAttrName)
{
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_, tensorAttrName, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([this](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto descPtr
= static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
*descPtr = _fakeDesc;
}),
Return(HIPDNN_STATUS_SUCCESS)));
}
};
TEST_F(TestUnpackAndRegisterTensor, UnpackAndRegisterTensorNewTensor)
{
expectDescriptorGet(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X);
// UID is called twice: once by unpackAndRegisterTensor (map lookup) and once by
// unpackTensorAttributes — expectFullTensorMocks uses WillRepeatedly for the UID mock.
expectFullTensorMocks();
// Destroy for the RAII wrapper
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_))
.WillRepeatedly(Return(HIPDNN_STATUS_SUCCESS));
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::shared_ptr<TensorAttributes> outTensor;
hipdnnBackendDescriptor_t opDesc = nullptr;
auto err = unpackAndRegisterTensor(
opDesc, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensor, "conv X");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(outTensor, nullptr);
EXPECT_EQ(outTensor->get_uid(), K_UID);
EXPECT_EQ(tensorMap.size(), 1u);
EXPECT_EQ(tensorMap[K_UID], outTensor);
}
TEST_F(TestUnpackAndRegisterTensor, UnpackAndRegisterTensorExistingUid)
{
// Pre-populate the tensor map
auto existingTensor = std::make_shared<TensorAttributes>();
existingTensor->set_uid(K_UID)
.set_data_type(DataType::FLOAT)
.set_dim({1, 3, 32, 32})
.set_stride({3072, 1024, 32, 1});
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
tensorMap[K_UID] = existingTensor;
// Mock getting the tensor descriptor from the operation
expectDescriptorGet(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X);
// Mock reading UID from the tensor descriptor
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Destroy for the RAII wrapper
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_))
.WillRepeatedly(Return(HIPDNN_STATUS_SUCCESS));
std::shared_ptr<TensorAttributes> outTensor;
hipdnnBackendDescriptor_t opDesc = nullptr;
auto err = unpackAndRegisterTensor(
opDesc, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensor, "conv X");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_NE(outTensor, nullptr);
EXPECT_EQ(outTensor, existingTensor);
EXPECT_EQ(tensorMap.size(), 1u);
}
TEST_F(TestUnpackAndRegisterTensor, UnpackAndRegisterTensorNullDescFails)
{
// Mock getting the tensor descriptor from the operation, returning null
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_,
HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
_,
_))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto descPtr = static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
*descPtr = nullptr;
}),
Return(HIPDNN_STATUS_SUCCESS)));
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::shared_ptr<TensorAttributes> outTensor;
hipdnnBackendDescriptor_t opDesc = nullptr;
auto err = unpackAndRegisterTensor(
opDesc, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensor, "conv X");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
EXPECT_TRUE(err.get_message().find("Null") != std::string::npos
|| err.get_message().find("null") != std::string::npos);
}
TEST_F(TestUnpackAndRegisterTensor, UnpackAndRegisterTensorUIDFails)
{
// Descriptor fetch succeeds
expectDescriptorGet(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X);
// UID scalar query fails
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
// Destroy for the RAII wrapper
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_))
.WillRepeatedly(Return(HIPDNN_STATUS_SUCCESS));
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::shared_ptr<TensorAttributes> outTensor;
hipdnnBackendDescriptor_t opDesc = nullptr;
auto err = unpackAndRegisterTensor(
opDesc, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensor, "conv X");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestUnpackAndRegisterTensor, UnpackAndRegisterTensorUnpackFails)
{
// Descriptor fetch succeeds
expectDescriptorGet(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X);
// UID scalar succeeds (new UID not in map)
constexpr int64_t K_NEW_UID = 99;
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillRepeatedly(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_NEW_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Data type query fails during unpackTensorAttributes
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
// Destroy for the RAII wrapper
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_))
.WillRepeatedly(Return(HIPDNN_STATUS_SUCCESS));
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::shared_ptr<TensorAttributes> outTensor;
hipdnnBackendDescriptor_t opDesc = nullptr;
auto err = unpackAndRegisterTensor(
opDesc, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensor, "conv X");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// getDescriptorAttrString tests
// ---------------------------------------------------------------------------
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrStringSuccess)
{
const std::string kExpected = "test_tensor";
// Count query returns string length + 1 (for null terminator)
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(kExpected.size() + 1)),
Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns the string
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, Ne(0), _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(kExpected.size() + 1)),
Invoke([kExpected](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, kExpected.c_str(), kExpected.size() + 1);
}),
Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::string value = "initial";
auto err = getDescriptorAttrString(desc, HIPDNN_ATTR_TENSOR_NAME_EXT, value, "tensor name");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_EQ(value, kExpected);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrStringNotSupported)
{
// Count query returns NOT_SUPPORTED
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_NOT_SUPPORTED));
hipdnnBackendDescriptor_t desc = nullptr;
std::string value = "initial";
auto err = getDescriptorAttrString(desc, HIPDNN_ATTR_TENSOR_NAME_EXT, value, "tensor name");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(value.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrStringEmptyCount)
{
// Count query succeeds but count=0
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::string value = "initial";
auto err = getDescriptorAttrString(desc, HIPDNN_ATTR_TENSOR_NAME_EXT, value, "tensor name");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(value.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrStringCountFails)
{
// Count query returns a non-NOT_SUPPORTED error
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
std::string value = "initial";
auto err = getDescriptorAttrString(desc, HIPDNN_ATTR_TENSOR_NAME_EXT, value, "tensor name");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrStringDataFails)
{
// Count query succeeds with count > 0
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{10}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query fails
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 10, _, Ne(nullptr)))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
std::string value = "initial";
auto err = getDescriptorAttrString(desc, HIPDNN_ATTR_TENSOR_NAME_EXT, value, "tensor name");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// getDescriptorAttrDescArray tests
// ---------------------------------------------------------------------------
class TestGetDescriptorAttrDescArray : public TestDescriptorUnpackHelpers
{
protected:
// Fake descriptor pointers for the source and returned descriptors
int _sourcePlaceholder = 0;
hipdnnBackendDescriptor_t _fakeSource
= reinterpret_cast<hipdnnBackendDescriptor_t>(&_sourcePlaceholder);
int _desc1Placeholder = 1;
hipdnnBackendDescriptor_t _fakeDesc1
= reinterpret_cast<hipdnnBackendDescriptor_t>(&_desc1Placeholder);
int _desc2Placeholder = 2;
hipdnnBackendDescriptor_t _fakeDesc2
= reinterpret_cast<hipdnnBackendDescriptor_t>(&_desc2Placeholder);
};
TEST_F(TestGetDescriptorAttrDescArray, GetDescriptorAttrDescArraySuccess)
{
// Count query returns 2
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns 2 descriptors
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 2, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}),
Invoke([this](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto descs = static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
descs[0] = _fakeDesc1;
descs[1] = _fakeDesc2;
}),
Return(HIPDNN_STATUS_SUCCESS)));
// RAII destructors call backendDestroyDescriptor for each returned descriptor
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc2))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
auto [result, err]
= getDescriptorAttrDescArray(_fakeSource, HIPDNN_ATTR_TENSOR_DIMENSIONS, "test array");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(result.size(), 2u);
EXPECT_EQ(result[0].get(), _fakeDesc1);
EXPECT_EQ(result[1].get(), _fakeDesc2);
}
TEST_F(TestGetDescriptorAttrDescArray, GetDescriptorAttrDescArrayCountFails)
{
// Count query fails
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
auto [result, err]
= getDescriptorAttrDescArray(_fakeSource, HIPDNN_ATTR_TENSOR_DIMENSIONS, "test array");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
EXPECT_TRUE(result.empty());
}
TEST_F(TestGetDescriptorAttrDescArray, GetDescriptorAttrDescArrayZeroCount)
{
// Count query returns 0
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
auto [result, err]
= getDescriptorAttrDescArray(_fakeSource, HIPDNN_ATTR_TENSOR_DIMENSIONS, "test array");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(result.empty());
}
TEST_F(TestGetDescriptorAttrDescArray, GetDescriptorAttrDescArrayDataFails)
{
// Count query returns 2
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query fails, but returns partially-filled descriptors
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 2, _, Ne(nullptr)))
.WillOnce(DoAll(Invoke([this](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto descs = static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
descs[0] = _fakeDesc1;
descs[1] = _fakeDesc2;
}),
Return(HIPDNN_STATUS_INTERNAL_ERROR)));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
// The cleanup path destroys both descriptors
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc2))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
auto [result, err]
= getDescriptorAttrDescArray(_fakeSource, HIPDNN_ATTR_TENSOR_DIMENSIONS, "test array");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
EXPECT_TRUE(result.empty());
}
TEST_F(TestGetDescriptorAttrDescArray, GetDescriptorAttrDescArrayCountMismatch)
{
// Count query returns 2
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{2}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query succeeds but actualCount > count (mismatch)
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeSource, _, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 2, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{5}),
Invoke([this](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto descs = static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
descs[0] = _fakeDesc1;
descs[1] = _fakeDesc2;
}),
Return(HIPDNN_STATUS_SUCCESS)));
// The cleanup path destroys both descriptors
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc2))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
auto [result, err]
= getDescriptorAttrDescArray(_fakeSource, HIPDNN_ATTR_TENSOR_DIMENSIONS, "test array");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
EXPECT_TRUE(result.empty());
}
// ---------------------------------------------------------------------------
// unpackAndRegisterTensorArray tests
// ---------------------------------------------------------------------------
class TestUnpackAndRegisterTensorArray : public TestGetDescriptorAttrDescArray
{
protected:
static constexpr int64_t K_UID1 = 100;
static constexpr int64_t K_UID2 = 200;
static constexpr std::array<int64_t, 4> K_DIMS = {1, 3, 32, 32};
static constexpr std::array<int64_t, 4> K_STRIDES = {3072, 1024, 32, 1};
/// Delegates to the shared free function using this fixture's mock and K_DIMS/K_STRIDES.
void expectFullTensorMocksForDesc(hipdnnBackendDescriptor_t fakeDesc, int64_t uid)
{
::expectFullTensorMocksForDesc(*_mockBackend, fakeDesc, uid, K_DIMS, K_STRIDES);
}
/// Sets up only the UID mock for a descriptor that will be found in the map
void expectUidOnlyForDesc(hipdnnBackendDescriptor_t fakeDesc, int64_t uid)
{
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([uid](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
}
/// Mocks the getDescriptorAttrDescArray count + data queries to return an array
/// of descriptors from _fakeSource.
void expectDescArrayQuery(hipdnnBackendAttributeName_t attrName,
const std::vector<hipdnnBackendDescriptor_t>& descs)
{
auto count = static_cast<int64_t>(descs.size());
// Count query
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeSource, attrName, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(count), Return(HIPDNN_STATUS_SUCCESS)));
if(count > 0)
{
// Data query
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(
_fakeSource, attrName, HIPDNN_TYPE_BACKEND_DESCRIPTOR, count, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(count),
Invoke([descs](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto out
= static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
for(size_t i = 0; i < descs.size(); ++i)
{
out[i] = descs[i];
}
}),
Return(HIPDNN_STATUS_SUCCESS)));
}
}
};
TEST_F(TestUnpackAndRegisterTensorArray, SuccessNewTensors)
{
// Set up getDescriptorAttrDescArray to return 2 descriptors
expectDescArrayQuery(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, {_fakeDesc1, _fakeDesc2});
// Both descriptors need full tensor mocks since neither UID is in the map
expectFullTensorMocksForDesc(_fakeDesc1, K_UID1);
expectFullTensorMocksForDesc(_fakeDesc2, K_UID2);
// RAII destructors
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc2))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::vector<std::shared_ptr<TensorAttributes>> outTensors;
auto err = unpackAndRegisterTensorArray(
_fakeSource, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensors, "test");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(outTensors.size(), 2u);
EXPECT_EQ(outTensors[0]->get_uid(), K_UID1);
EXPECT_EQ(outTensors[1]->get_uid(), K_UID2);
EXPECT_EQ(tensorMap.size(), 2u);
EXPECT_EQ(tensorMap[K_UID1], outTensors[0]);
EXPECT_EQ(tensorMap[K_UID2], outTensors[1]);
}
TEST_F(TestUnpackAndRegisterTensorArray, SuccessExistingTensorReuse)
{
// Pre-populate the tensor map with UID1
auto existingTensor = std::make_shared<TensorAttributes>();
existingTensor->set_uid(K_UID1)
.set_data_type(DataType::FLOAT)
.set_dim({1, 3, 32, 32})
.set_stride({3072, 1024, 32, 1});
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
tensorMap[K_UID1] = existingTensor;
// Return 2 descriptors: _fakeDesc1 has UID1 (in map), _fakeDesc2 has UID2 (new)
expectDescArrayQuery(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, {_fakeDesc1, _fakeDesc2});
// _fakeDesc1: only UID is read (already in map, no full unpack)
expectUidOnlyForDesc(_fakeDesc1, K_UID1);
// _fakeDesc2: full unpack needed (not in map)
expectFullTensorMocksForDesc(_fakeDesc2, K_UID2);
// RAII destructors
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc2))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
std::vector<std::shared_ptr<TensorAttributes>> outTensors;
auto err = unpackAndRegisterTensorArray(
_fakeSource, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensors, "test");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(outTensors.size(), 2u);
// First tensor is the existing one — same shared_ptr identity
EXPECT_EQ(outTensors[0], existingTensor);
EXPECT_EQ(outTensors[0]->get_uid(), K_UID1);
// Second tensor is newly unpacked
EXPECT_EQ(outTensors[1]->get_uid(), K_UID2);
EXPECT_EQ(tensorMap.size(), 2u);
EXPECT_EQ(tensorMap[K_UID2], outTensors[1]);
}
TEST_F(TestUnpackAndRegisterTensorArray, SuccessEmptyArray)
{
// Count query returns 0
expectDescArrayQuery(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, {});
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::vector<std::shared_ptr<TensorAttributes>> outTensors;
auto err = unpackAndRegisterTensorArray(
_fakeSource, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensors, "test");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(outTensors.empty());
EXPECT_TRUE(tensorMap.empty());
}
TEST_F(TestUnpackAndRegisterTensorArray, CountFails)
{
// Count query fails
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource,
HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
0,
_,
nullptr))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::vector<std::shared_ptr<TensorAttributes>> outTensors;
auto err = unpackAndRegisterTensorArray(
_fakeSource, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensors, "test");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestUnpackAndRegisterTensorArray, NullDescriptorInArray)
{
// Return 2 descriptors, second is nullptr
auto count = int64_t{2};
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource,
HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
0,
_,
nullptr))
.WillOnce(DoAll(SetArgPointee<4>(count), Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(*_mockBackend,
backendGetAttribute(_fakeSource,
HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
2,
_,
Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(count),
Invoke([this](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto descs = static_cast<hipdnnBackendDescriptor_t*>(arrayOfElements);
descs[0] = _fakeDesc1;
descs[1] = nullptr;
}),
Return(HIPDNN_STATUS_SUCCESS)));
// _fakeDesc1 has a valid UID and is processed before hitting the nullptr
expectUidOnlyForDesc(_fakeDesc1, K_UID1);
// Pre-populate the map so that _fakeDesc1 is found (avoids full unpack)
auto existingTensor = std::make_shared<TensorAttributes>();
existingTensor->set_uid(K_UID1);
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
tensorMap[K_UID1] = existingTensor;
std::vector<std::shared_ptr<TensorAttributes>> outTensors;
// RAII destructors for _fakeDesc1 (nullptr needs no destroy)
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
auto err = unpackAndRegisterTensorArray(
_fakeSource, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensors, "test");
// Null descriptors are silently skipped; only the valid descriptor is returned.
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(outTensors.size(), 1u);
EXPECT_EQ(outTensors[0], existingTensor);
}
TEST_F(TestUnpackAndRegisterTensorArray, UidQueryFails)
{
// Return 1 descriptor
expectDescArrayQuery(HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, {_fakeDesc1});
// UID query fails on _fakeDesc1
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc1, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
// RAII destructor
EXPECT_CALL(*_mockBackend, backendDestroyDescriptor(_fakeDesc1))
.WillOnce(Return(HIPDNN_STATUS_SUCCESS));
std::unordered_map<int64_t, std::shared_ptr<TensorAttributes>> tensorMap;
std::vector<std::shared_ptr<TensorAttributes>> outTensors;
auto err = unpackAndRegisterTensorArray(
_fakeSource, HIPDNN_ATTR_OPERATION_CONVOLUTION_FORWARD_X, tensorMap, outTensors, "test");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// Additional unpackTensorAttributes error-path tests
// ---------------------------------------------------------------------------
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesUIDFails)
{
// UID scalar query fails
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesDataTypeFails)
{
// UID scalar succeeds
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Data type query fails
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesStridesFails)
{
// UID scalar succeeds
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Data type scalar succeeds
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto dt = HIPDNN_DATA_FLOAT;
std::memcpy(arrayOfElements, &dt, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Dims: count query then data query succeed
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_DIMS.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Strides count query fails
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_STRIDES, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestUnpackTensorAttributes, UnpackTensorAttributesInvalidDataType)
{
// UID scalar succeeds
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto uid = K_UID;
std::memcpy(arrayOfElements, &uid, sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Data type scalar returns an unrecognized value
EXPECT_CALL(*_mockBackend,
backendGetAttribute(
_fakeDesc, HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto dt = static_cast<hipdnnDataType_t>(9999);
std::memcpy(arrayOfElements, &dt, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Dims: count query then data query succeed
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_DIMS.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Strides: count query then data query succeed
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_STRIDES, HIPDNN_TYPE_INT64, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}), Return(HIPDNN_STATUS_SUCCESS)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{4}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_STRIDES.data(), 4 * sizeof(int64_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// is_virtual scalar succeeds
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_IS_VIRTUAL, HIPDNN_TYPE_BOOLEAN, 1, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = false;
std::memcpy(arrayOfElements, &val, sizeof(bool));
}),
Return(HIPDNN_STATUS_SUCCESS)));
// Name count query (empty name)
EXPECT_CALL(
*_mockBackend,
backendGetAttribute(_fakeDesc, HIPDNN_ATTR_TENSOR_NAME_EXT, HIPDNN_TYPE_CHAR, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
std::shared_ptr<TensorAttributes> tensor;
auto err = unpackTensorAttributes(_fakeDesc, tensor);
// fromHipdnnDataType returns an error for unknown data types
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
// ---------------------------------------------------------------------------
// unpackGraphDataType tests
// ---------------------------------------------------------------------------
TEST_F(TestDescriptorUnpackHelpers, UnpackGraphDataTypeAbsentReturnsNotSet)
{
// Count query reports zero (backend storage is at the UNSET sentinel) -- the
// helper must surface this as DataType::NOT_SET with no error.
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
auto [dt, err] = unpackGraphDataType(desc, HIPDNN_ATTR_CONVOLUTION_COMP_TYPE, "compute type");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_EQ(dt, DataType::NOT_SET);
}
TEST_F(TestDescriptorUnpackHelpers, UnpackGraphDataTypeNotSupportedReturnsNotSet)
{
// Count query returns NOT_SUPPORTED (older backends or genuinely missing
// attributes) -- treat the same as count=0.
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_NOT_SUPPORTED));
hipdnnBackendDescriptor_t desc = nullptr;
auto [dt, err] = unpackGraphDataType(desc, HIPDNN_ATTR_CONVOLUTION_COMP_TYPE, "compute type");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_EQ(dt, DataType::NOT_SET);
}
TEST_F(TestDescriptorUnpackHelpers, UnpackGraphDataTypeCountQueryFails)
{
// Count query reports a backend error -- propagate as HIPDNN_BACKEND_ERROR.
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
auto [dt, err] = unpackGraphDataType(desc, HIPDNN_ATTR_CONVOLUTION_COMP_TYPE, "compute type");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
EXPECT_EQ(dt, DataType::NOT_SET);
}
TEST_F(TestDescriptorUnpackHelpers, UnpackGraphDataTypeSuccess)
{
// Count query reports 1, value-fetch returns HIPDNN_DATA_FLOAT -- the
// helper must surface DataType::FLOAT with no error.
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}), Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 1, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}),
Invoke([](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
auto val = HIPDNN_DATA_FLOAT;
std::memcpy(arrayOfElements, &val, sizeof(hipdnnDataType_t));
}),
Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
auto [dt, err] = unpackGraphDataType(desc, HIPDNN_ATTR_CONVOLUTION_COMP_TYPE, "compute type");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_EQ(dt, DataType::FLOAT);
}
TEST_F(TestDescriptorUnpackHelpers, UnpackGraphDataTypeValueFetchFails)
{
// Count query succeeds with 1, then the value-fetch call fails.
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{1}), Return(HIPDNN_STATUS_SUCCESS)));
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_DATA_TYPE, 1, _, _))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
auto [dt, err] = unpackGraphDataType(desc, HIPDNN_ATTR_CONVOLUTION_COMP_TYPE, "compute type");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
EXPECT_EQ(dt, DataType::NOT_SET);
}
// ---------------------------------------------------------------------------
// getDescriptorAttrByteArray tests
// ---------------------------------------------------------------------------
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrByteArraySuccess)
{
constexpr std::array<uint8_t, 5> K_DATA = {0x10, 0x20, 0x30, 0x40, 0x50};
// Count query returns 5
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{5}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query returns 5 bytes
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 5, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{5}),
Invoke([K_DATA](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t*,
void* arrayOfElements) {
std::memcpy(arrayOfElements, K_DATA.data(), K_DATA.size());
}),
Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<uint8_t> value;
auto err = getDescriptorAttrByteArray(desc, HIPDNN_ATTR_TENSOR_VALUE_EXT, value, "test bytes");
EXPECT_TRUE(err.is_good()) << err.get_message();
ASSERT_EQ(value.size(), 5u);
EXPECT_EQ(value[0], 0x10);
EXPECT_EQ(value[1], 0x20);
EXPECT_EQ(value[2], 0x30);
EXPECT_EQ(value[3], 0x40);
EXPECT_EQ(value[4], 0x50);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrByteArrayNotSupported)
{
// Count query returns NOT_SUPPORTED
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_NOT_SUPPORTED));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<uint8_t> value;
auto err = getDescriptorAttrByteArray(desc, HIPDNN_ATTR_TENSOR_VALUE_EXT, value, "test bytes");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(value.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrByteArrayZeroCount)
{
// Count query succeeds but count=0
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{0}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<uint8_t> value;
auto err = getDescriptorAttrByteArray(desc, HIPDNN_ATTR_TENSOR_VALUE_EXT, value, "test bytes");
EXPECT_TRUE(err.is_good()) << err.get_message();
EXPECT_TRUE(value.empty());
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrByteArrayCountFails)
{
// Count query returns a non-NOT_SUPPORTED error
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<uint8_t> value;
auto err = getDescriptorAttrByteArray(desc, HIPDNN_ATTR_TENSOR_VALUE_EXT, value, "test bytes");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrByteArrayDataFails)
{
// Count query succeeds with count > 0
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{3}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query fails
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 3, _, Ne(nullptr)))
.WillOnce(Return(HIPDNN_STATUS_INTERNAL_ERROR));
EXPECT_CALL(*_mockBackend, getLastErrorString(_, _)).Times(AnyNumber());
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<uint8_t> value;
auto err = getDescriptorAttrByteArray(desc, HIPDNN_ATTR_TENSOR_VALUE_EXT, value, "test bytes");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}
TEST_F(TestDescriptorUnpackHelpers, GetDescriptorAttrByteArrayCountMismatch)
{
// Count query returns 5
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 0, _, nullptr))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{5}), Return(HIPDNN_STATUS_SUCCESS)));
// Data query succeeds but reports actualCount=3 (mismatches count=5)
EXPECT_CALL(*_mockBackend, backendGetAttribute(_, _, HIPDNN_TYPE_CHAR, 5, _, Ne(nullptr)))
.WillOnce(DoAll(SetArgPointee<4>(int64_t{3}), Return(HIPDNN_STATUS_SUCCESS)));
hipdnnBackendDescriptor_t desc = nullptr;
std::vector<uint8_t> value;
auto err = getDescriptorAttrByteArray(desc, HIPDNN_ATTR_TENSOR_VALUE_EXT, value, "test bytes");
EXPECT_TRUE(err.is_bad());
EXPECT_EQ(err.code, ErrorCode::HIPDNN_BACKEND_ERROR);
}