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
1016 lines
39 KiB
C++
1016 lines
39 KiB
C++
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "FlatbufferTestUtils.hpp"
|
|
#include "FlatbufferUtilities.hpp"
|
|
#include "HipdnnException.hpp"
|
|
#include "ScopedBackendDescriptor.hpp"
|
|
#include "TestMacros.hpp"
|
|
#include "descriptors/GraphDescriptor.hpp"
|
|
#include "hipdnn_backend.h"
|
|
|
|
#include <array>
|
|
#include <cstring>
|
|
#include <flatbuffers/flatbuffers.h>
|
|
#include <gtest/gtest.h>
|
|
#include <hipdnn_flatbuffers_sdk/data_objects/graph_generated.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <vector>
|
|
|
|
using namespace hipdnn_backend;
|
|
using hipdnn_backend::test_utilities::ScopedBackendDescriptor;
|
|
|
|
class TestGraphDescriptor : public ::testing::Test
|
|
{
|
|
public:
|
|
static flatbuffers::FlatBufferBuilder createValidGraph()
|
|
{
|
|
return test_utilities::createValidGraph();
|
|
}
|
|
|
|
static void verifyGraph(const hipdnn_flatbuffers_sdk::data_objects::GraphT& graph)
|
|
{
|
|
EXPECT_EQ(graph.name, "test");
|
|
EXPECT_EQ(graph.compute_data_type, hipdnn_flatbuffers_sdk::data_objects::DataType::FLOAT);
|
|
EXPECT_EQ(graph.intermediate_data_type,
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::HALF);
|
|
EXPECT_EQ(graph.io_data_type, hipdnn_flatbuffers_sdk::data_objects::DataType::BFLOAT16);
|
|
EXPECT_EQ(graph.tensors.size(), 3);
|
|
EXPECT_EQ(graph.nodes.size(), 1);
|
|
}
|
|
|
|
static void verifyGraphsEquivalent(const hipdnn_flatbuffers_sdk::data_objects::GraphT& graph1,
|
|
const hipdnn_flatbuffers_sdk::data_objects::GraphT& graph2)
|
|
{
|
|
EXPECT_EQ(graph1.name, graph2.name);
|
|
EXPECT_EQ(graph1.compute_data_type, graph2.compute_data_type);
|
|
EXPECT_EQ(graph1.intermediate_data_type, graph2.intermediate_data_type);
|
|
EXPECT_EQ(graph1.io_data_type, graph2.io_data_type);
|
|
EXPECT_EQ(graph1.preferred_engine_id, graph2.preferred_engine_id);
|
|
|
|
ASSERT_EQ(graph1.tensors.size(), graph2.tensors.size());
|
|
for(size_t i = 0; i < graph1.tensors.size(); ++i)
|
|
{
|
|
SCOPED_TRACE("tensor[" + std::to_string(i) + "]");
|
|
ASSERT_NE(graph1.tensors[i], nullptr);
|
|
ASSERT_NE(graph2.tensors[i], nullptr);
|
|
EXPECT_EQ(*graph1.tensors[i], *graph2.tensors[i]);
|
|
}
|
|
|
|
ASSERT_EQ(graph1.nodes.size(), graph2.nodes.size());
|
|
for(size_t i = 0; i < graph1.nodes.size(); ++i)
|
|
{
|
|
SCOPED_TRACE("node[" + std::to_string(i) + "]");
|
|
ASSERT_NE(graph1.nodes[i], nullptr);
|
|
ASSERT_NE(graph2.nodes[i], nullptr);
|
|
EXPECT_EQ(*graph1.nodes[i], *graph2.nodes[i]);
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(TestGraphDescriptor, SerializeDeserializeGraph)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
auto handle = reinterpret_cast<hipdnnHandle_t>(0x12345678);
|
|
descriptor.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle));
|
|
descriptor.finalize();
|
|
|
|
auto output = descriptor.getSerializedGraph();
|
|
flatbuffers::Verifier verifier(static_cast<const uint8_t*>(output.ptr), output.size);
|
|
ASSERT_TRUE(verifier.VerifyBuffer<hipdnn_flatbuffers_sdk::data_objects::Graph>());
|
|
|
|
auto graph = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(output.ptr));
|
|
ASSERT_NE(graph, nullptr);
|
|
verifyGraph(*graph);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, DeserializeGraphExtractsAttributes)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
// Verify graph-level attributes were correctly extracted from the FlatBuffer
|
|
hipdnnDataType_t computeType{};
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_COMPUTE_DATA_TYPE_EXT,
|
|
HIPDNN_TYPE_DATA_TYPE,
|
|
1,
|
|
&count,
|
|
&computeType));
|
|
EXPECT_EQ(computeType, HIPDNN_DATA_FLOAT);
|
|
|
|
hipdnnDataType_t intermediateType{};
|
|
count = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_INTERMEDIATE_DATA_TYPE_EXT,
|
|
HIPDNN_TYPE_DATA_TYPE,
|
|
1,
|
|
&count,
|
|
&intermediateType));
|
|
EXPECT_EQ(intermediateType, HIPDNN_DATA_HALF);
|
|
|
|
hipdnnDataType_t ioType{};
|
|
count = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(
|
|
HIPDNN_ATTR_OPERATIONGRAPH_IO_DATA_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &count, &ioType));
|
|
EXPECT_EQ(ioType, HIPDNN_DATA_BFLOAT16);
|
|
|
|
std::array<char, 64> name{};
|
|
count = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_NAME_EXT,
|
|
HIPDNN_TYPE_CHAR,
|
|
static_cast<int64_t>(name.size()),
|
|
&count,
|
|
name.data()));
|
|
EXPECT_STREQ(name.data(), "test");
|
|
|
|
int64_t opsCount = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(
|
|
HIPDNN_ATTR_OPERATIONGRAPH_OPS, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &opsCount, nullptr));
|
|
EXPECT_EQ(opsCount, 1);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, WillCorrectlySetGraph)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
ASSERT_NO_THROW(descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size()));
|
|
|
|
// Finalize requires a handle
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.finalize(), HIPDNN_STATUS_BAD_PARAM);
|
|
|
|
auto handle = reinterpret_cast<hipdnnHandle_t>(0x12345678);
|
|
ASSERT_NO_THROW(descriptor.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)));
|
|
|
|
ASSERT_NO_THROW(descriptor.finalize());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, WillCorrectlySetGraphReverseOrder)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
auto handle = reinterpret_cast<hipdnnHandle_t>(0x12345678);
|
|
ASSERT_NO_THROW(descriptor.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)));
|
|
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.finalize(), HIPDNN_STATUS_BAD_PARAM);
|
|
|
|
ASSERT_NO_THROW(descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size()));
|
|
ASSERT_NO_THROW(descriptor.finalize());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, WillFailToSetInvalidGraph)
|
|
{
|
|
GraphDescriptor descriptor;
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.deserializeGraph(nullptr, 0),
|
|
HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, FinalizeFailInvalidGraph)
|
|
{
|
|
GraphDescriptor descriptor;
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.finalize(), HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, GetAttributeWorksOnDeserializedUnfinalizedGraph)
|
|
{
|
|
auto builder = createValidGraph();
|
|
const auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
// Querying OPS on a deserialized graph returns the number of unpacked nodes
|
|
int64_t elementCount = -1;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(
|
|
HIPDNN_ATTR_OPERATIONGRAPH_OPS, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, nullptr));
|
|
EXPECT_EQ(elementCount, 1);
|
|
|
|
// Query compute data type without finalization - should succeed
|
|
int64_t computeCount = 0;
|
|
hipdnnDataType_t computeDt = HIPDNN_DATA_HALF;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_COMPUTE_DATA_TYPE_EXT,
|
|
HIPDNN_TYPE_DATA_TYPE,
|
|
1,
|
|
&computeCount,
|
|
&computeDt));
|
|
EXPECT_EQ(computeDt, HIPDNN_DATA_FLOAT);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, GetAttributeUnsupportedReturnsNotSupported)
|
|
{
|
|
auto builder = createValidGraph();
|
|
const auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
// getAttribute with an unsupported attribute name does not require finalization
|
|
int64_t elementCount = 0;
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
descriptor.getAttribute(
|
|
HIPDNN_ATTR_ENGINEHEUR_MODE, HIPDNN_TYPE_DATA_TYPE, 0, &elementCount, nullptr),
|
|
HIPDNN_STATUS_NOT_SUPPORTED);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, SetAttributeReturnsNotSupported)
|
|
{
|
|
GraphDescriptor descriptor;
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
descriptor.setAttribute(HIPDNN_ATTR_ENGINEHEUR_MODE, HIPDNN_TYPE_DATA_TYPE, 0, nullptr),
|
|
HIPDNN_STATUS_NOT_SUPPORTED);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, EmptyGraphDeserializesButFailsToFinalize)
|
|
{
|
|
auto builder = test_utilities::createEmptyGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
ASSERT_NO_THROW(descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size()));
|
|
|
|
auto handle = reinterpret_cast<hipdnnHandle_t>(0x12345678);
|
|
ASSERT_NO_THROW(descriptor.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)));
|
|
|
|
// Finalize fails because the empty graph has no operations
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.finalize(), HIPDNN_STATUS_BAD_PARAM);
|
|
|
|
// Serialization still works (not gated by finalize)
|
|
descriptor.buildSerializedGraph();
|
|
auto data = descriptor.getSerializedGraph();
|
|
flatbuffers::Verifier verifier(static_cast<const uint8_t*>(data.ptr), data.size);
|
|
ASSERT_TRUE(verifier.VerifyBuffer<hipdnn_flatbuffers_sdk::data_objects::Graph>());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, GetSerializedGraphWithoutPopulationThrows)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
// getSerializedGraph requires the buffer to be populated by finalize() or
|
|
// buildSerializedGraph(). Without either call, the getter throws.
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.getSerializedGraph(), HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, GetSerializedGraphEmptyOperationsThrows)
|
|
{
|
|
const GraphDescriptor descriptor;
|
|
ASSERT_THROW_HIPDNN_STATUS(descriptor.getSerializedGraph(), HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonRoundTripViaDescriptorApi)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
// Deserialize from binary into first descriptor
|
|
GraphDescriptor descriptor1;
|
|
descriptor1.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
// Build the serialized buffer and get JSON
|
|
descriptor1.buildSerializedGraph();
|
|
auto jsonStr = descriptor1.getSerializedJsonGraph();
|
|
ASSERT_FALSE(jsonStr.empty());
|
|
|
|
// Verify JSON is valid
|
|
auto parsed = nlohmann::json::parse(jsonStr);
|
|
EXPECT_TRUE(parsed.contains("name"));
|
|
EXPECT_TRUE(parsed.contains("compute_data_type"));
|
|
EXPECT_TRUE(parsed.contains("nodes"));
|
|
EXPECT_TRUE(parsed.contains("tensors"));
|
|
|
|
// Round-trip: create a new descriptor from the JSON
|
|
GraphDescriptor descriptor2;
|
|
ASSERT_NO_THROW(
|
|
GraphDescriptor::createFromJsonGraph(descriptor2, jsonStr.c_str(), jsonStr.size()));
|
|
|
|
// Both descriptors should produce equivalent serialized graphs
|
|
descriptor2.buildSerializedGraph();
|
|
auto binary1 = descriptor1.getSerializedGraph();
|
|
auto binary2 = descriptor2.getSerializedGraph();
|
|
|
|
auto graph1 = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(binary1.ptr));
|
|
auto graph2 = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(binary2.ptr));
|
|
|
|
verifyGraphsEquivalent(*graph1, *graph2);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonSerializationEmitsOverrideShapeTrue)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
bool overrideShapeEnabled = true;
|
|
ASSERT_NO_THROW(
|
|
descriptor.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&overrideShapeEnabled));
|
|
|
|
descriptor.buildSerializedGraph();
|
|
const auto jsonStr = descriptor.getSerializedJsonGraph();
|
|
const auto parsed = nlohmann::json::parse(jsonStr);
|
|
|
|
ASSERT_TRUE(parsed.contains("is_override_shape_enabled"));
|
|
EXPECT_TRUE(parsed.at("is_override_shape_enabled").get<bool>());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonRoundTripPreservesOverrideShapeTrue)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor original;
|
|
original.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
bool overrideShapeEnabled = true;
|
|
ASSERT_NO_THROW(original.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&overrideShapeEnabled));
|
|
|
|
original.buildSerializedGraph();
|
|
const auto jsonStr = original.getSerializedJsonGraph();
|
|
|
|
GraphDescriptor roundTripped;
|
|
ASSERT_NO_THROW(
|
|
GraphDescriptor::createFromJsonGraph(roundTripped, jsonStr.c_str(), jsonStr.size()));
|
|
|
|
bool output = false;
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(
|
|
roundTripped.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&count,
|
|
&output));
|
|
EXPECT_EQ(count, 1);
|
|
EXPECT_TRUE(output);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonMissingOverrideShapeFieldDefaultsFalse)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
descriptor.buildSerializedGraph();
|
|
|
|
auto parsed = nlohmann::json::parse(descriptor.getSerializedJsonGraph());
|
|
parsed.erase("is_override_shape_enabled");
|
|
const auto jsonStr = parsed.dump();
|
|
|
|
GraphDescriptor fromJson;
|
|
ASSERT_NO_THROW(
|
|
GraphDescriptor::createFromJsonGraph(fromJson, jsonStr.c_str(), jsonStr.size()));
|
|
|
|
bool output = true;
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(fromJson.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&count,
|
|
&output));
|
|
EXPECT_EQ(count, 1);
|
|
EXPECT_FALSE(output);
|
|
}
|
|
|
|
// ============================================================================
|
|
// JSON C API error-path tests
|
|
// ============================================================================
|
|
|
|
TEST_F(TestGraphDescriptor, JsonSerializeNullDescriptor)
|
|
{
|
|
size_t size = 0;
|
|
auto status = hipdnnBackendGetSerializedJsonGraph_ext(nullptr, 0, &size, nullptr);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonSerializeNullSize)
|
|
{
|
|
// Create a valid descriptor through the binary C API
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(desc.get(), 0, nullptr, nullptr);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonSerializeInsufficientBuffer)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
// Query the required size
|
|
size_t requiredSize = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(desc.get(), 0, &requiredSize, nullptr);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_GT(requiredSize, 0u);
|
|
|
|
// Provide a buffer that is 1 byte too small
|
|
std::vector<char> buffer(requiredSize - 1);
|
|
size_t returnedSize = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(
|
|
desc.get(), buffer.size(), &returnedSize, buffer.data());
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_SIZE_INSUFFICIENT);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonSerializeOversizedBuffer)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
// Query the required size
|
|
size_t requiredSize = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(desc.get(), 0, &requiredSize, nullptr);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_GT(requiredSize, 0u);
|
|
|
|
// Provide an oversized buffer (larger than required)
|
|
std::vector<char> buffer(requiredSize + 64);
|
|
size_t returnedSize = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(
|
|
desc.get(), buffer.size(), &returnedSize, buffer.data());
|
|
EXPECT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
EXPECT_EQ(returnedSize, requiredSize);
|
|
|
|
// Verify the JSON content is valid
|
|
auto parsed = nlohmann::json::parse(buffer.data());
|
|
EXPECT_TRUE(parsed.contains("name"));
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonDeserializeNullInput)
|
|
{
|
|
hipdnnBackendDescriptor_t desc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeJsonGraph_ext(&desc, nullptr, 1);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonDeserializeNullDescriptor)
|
|
{
|
|
auto status = hipdnnBackendCreateAndDeserializeJsonGraph_ext(nullptr, "{}", 2);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonDeserializeEmptySize)
|
|
{
|
|
hipdnnBackendDescriptor_t desc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeJsonGraph_ext(&desc, "{}", 0);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonDeserializeNonGraphJson)
|
|
{
|
|
hipdnnBackendDescriptor_t desc = nullptr;
|
|
const std::string json = R"({"foo": "bar", "baz": 42})";
|
|
auto status
|
|
= hipdnnBackendCreateAndDeserializeJsonGraph_ext(&desc, json.c_str(), json.size() + 1);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM);
|
|
EXPECT_EQ(desc, nullptr);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonRoundTripViaApi)
|
|
{
|
|
// Create a valid graph descriptor via the binary C API
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc1 = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc1, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc1, nullptr);
|
|
const ScopedBackendDescriptor scopedDesc1(rawDesc1);
|
|
|
|
// Serialize to JSON via C API: first query the size
|
|
size_t jsonSize = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(scopedDesc1.get(), 0, &jsonSize, nullptr);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_GT(jsonSize, 0u);
|
|
|
|
// Then retrieve the JSON data
|
|
std::vector<char> jsonBuffer(jsonSize);
|
|
size_t returnedSize = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(
|
|
scopedDesc1.get(), jsonBuffer.size(), &returnedSize, jsonBuffer.data());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
|
|
// Deserialize from JSON via C API
|
|
hipdnnBackendDescriptor_t rawDesc2 = nullptr;
|
|
status = hipdnnBackendCreateAndDeserializeJsonGraph_ext(
|
|
&rawDesc2, jsonBuffer.data(), std::strlen(jsonBuffer.data()));
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc2, nullptr);
|
|
const ScopedBackendDescriptor scopedDesc2(rawDesc2);
|
|
|
|
// Verify the round-tripped graph matches by extracting binary from both
|
|
auto graphDesc1 = scopedDesc1.get()->asDescriptor<GraphDescriptor>();
|
|
auto graphDesc2 = scopedDesc2.get()->asDescriptor<GraphDescriptor>();
|
|
|
|
// desc1 buffer was populated by the GetSerializedJsonGraph call above.
|
|
// desc2 needs explicit buildSerializedGraph() since it was only deserialized.
|
|
graphDesc2->buildSerializedGraph();
|
|
|
|
auto binary1 = graphDesc1->getSerializedGraph();
|
|
auto binary2 = graphDesc2->getSerializedGraph();
|
|
|
|
auto graph1 = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(binary1.ptr));
|
|
auto graph2 = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(binary2.ptr));
|
|
|
|
verifyGraphsEquivalent(*graph1, *graph2);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, DeserializeInvalidatesSerializedBuffer)
|
|
{
|
|
// Build graph A with name "graphA"
|
|
flatbuffers::FlatBufferBuilder builderA;
|
|
const std::vector<flatbuffers::Offset<hipdnn_flatbuffers_sdk::data_objects::TensorAttributes>>
|
|
emptyTensorsA;
|
|
const std::vector<flatbuffers::Offset<hipdnn_flatbuffers_sdk::data_objects::Node>> emptyNodesA;
|
|
auto graphA = hipdnn_flatbuffers_sdk::data_objects::CreateGraphDirect(
|
|
builderA,
|
|
"graphA",
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::FLOAT,
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::HALF,
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::BFLOAT16,
|
|
&emptyTensorsA,
|
|
&emptyNodesA);
|
|
builderA.Finish(graphA);
|
|
auto serializedA = builderA.Release();
|
|
|
|
// Deserialize graph A
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedA.data(), serializedA.size());
|
|
|
|
// Verify graph A name via getAttribute
|
|
std::array<char, 64> nameBuffer{};
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_NAME_EXT,
|
|
HIPDNN_TYPE_CHAR,
|
|
static_cast<int64_t>(nameBuffer.size()),
|
|
&count,
|
|
nameBuffer.data()));
|
|
EXPECT_EQ(std::string(nameBuffer.data()), "graphA");
|
|
|
|
// Build graph B with name "graphB"
|
|
flatbuffers::FlatBufferBuilder builderB;
|
|
const std::vector<flatbuffers::Offset<hipdnn_flatbuffers_sdk::data_objects::TensorAttributes>>
|
|
emptyTensorsB;
|
|
const std::vector<flatbuffers::Offset<hipdnn_flatbuffers_sdk::data_objects::Node>> emptyNodesB;
|
|
auto graphB = hipdnn_flatbuffers_sdk::data_objects::CreateGraphDirect(
|
|
builderB,
|
|
"graphB",
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::FLOAT,
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::HALF,
|
|
hipdnn_flatbuffers_sdk::data_objects::DataType::BFLOAT16,
|
|
&emptyTensorsB,
|
|
&emptyNodesB);
|
|
builderB.Finish(graphB);
|
|
auto serializedB = builderB.Release();
|
|
|
|
// Deserialize graph B (should replace graph A's data)
|
|
descriptor.deserializeGraph(serializedB.data(), serializedB.size());
|
|
|
|
// Verify the name reflects graph B, not stale graph A
|
|
nameBuffer.fill(0);
|
|
count = 0;
|
|
ASSERT_NO_THROW(descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_NAME_EXT,
|
|
HIPDNN_TYPE_CHAR,
|
|
static_cast<int64_t>(nameBuffer.size()),
|
|
&count,
|
|
nameBuffer.data()));
|
|
EXPECT_EQ(std::string(nameBuffer.data()), "graphB");
|
|
}
|
|
|
|
// ============================================================================
|
|
// Binary/JSON serialization error-path tests
|
|
// ============================================================================
|
|
|
|
TEST_F(TestGraphDescriptor, BinarySerializeNullDescriptor)
|
|
{
|
|
size_t size = 0;
|
|
auto status = hipdnnBackendGetSerializedBinaryGraph_ext(nullptr, 0, &size, nullptr);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, BinarySerializeNullSize)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(desc.get(), 0, nullptr, nullptr);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, BinarySerializeEmptyGraph)
|
|
{
|
|
// Create a graph descriptor with no operations via the C API
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_OPERATIONGRAPH_DESCRIPTOR, &rawDesc);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
// Serializing a graph with no operations produces a valid (empty) FlatBuffer.
|
|
// finalize() is the gate that enforces non-empty operations for execution.
|
|
size_t size = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(desc.get(), 0, &size, nullptr);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
EXPECT_GT(size, 0u);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, JsonSerializeEmptyGraph)
|
|
{
|
|
// Create a graph descriptor with no operations via the C API
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_OPERATIONGRAPH_DESCRIPTOR, &rawDesc);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
// Serializing a graph with no operations produces valid JSON
|
|
size_t size = 0;
|
|
status = hipdnnBackendGetSerializedJsonGraph_ext(desc.get(), 0, &size, nullptr);
|
|
EXPECT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
EXPECT_GT(size, 0u);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Malformed JSON tests
|
|
// ============================================================================
|
|
|
|
TEST_F(TestGraphDescriptor, MalformedJsonReturnsBadParam)
|
|
{
|
|
GraphDescriptor descriptor;
|
|
const std::string badJson = "{ not valid json !!!";
|
|
ASSERT_THROW_HIPDNN_STATUS(
|
|
GraphDescriptor::createFromJsonGraph(descriptor, badJson.c_str(), badJson.size()),
|
|
HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, MalformedJsonViaCApiReturnsBadParam)
|
|
{
|
|
hipdnnBackendDescriptor_t desc = nullptr;
|
|
const std::string badJson = "{ not valid json !!!";
|
|
auto status
|
|
= hipdnnBackendCreateAndDeserializeJsonGraph_ext(&desc, badJson.c_str(), badJson.size());
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, BinaryDeserializeCorruptedData)
|
|
{
|
|
const std::vector<uint8_t> garbageData = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04};
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, garbageData.data(), garbageData.size());
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM);
|
|
EXPECT_EQ(rawDesc, nullptr);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Binary C API error-path tests
|
|
// ============================================================================
|
|
|
|
TEST_F(TestGraphDescriptor, BinarySerializeInsufficientBuffer)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, serializedGraph.data(), serializedGraph.size());
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
|
|
// Query the required size
|
|
size_t requiredSize = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(desc.get(), 0, &requiredSize, nullptr);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_GT(requiredSize, 0u);
|
|
|
|
// Provide a buffer that is 1 byte too small
|
|
std::vector<uint8_t> buffer(requiredSize - 1);
|
|
size_t returnedSize = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(
|
|
desc.get(), buffer.size(), &returnedSize, buffer.data());
|
|
EXPECT_EQ(status, HIPDNN_STATUS_BAD_PARAM_SIZE_INSUFFICIENT);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, BinarySerializeOversizedBuffer)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc, nullptr);
|
|
const ScopedBackendDescriptor desc(rawDesc);
|
|
|
|
// Query the required size
|
|
size_t requiredSize = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(desc.get(), 0, &requiredSize, nullptr);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_GT(requiredSize, 0u);
|
|
|
|
// Provide an oversized buffer
|
|
std::vector<uint8_t> buffer(requiredSize + 64);
|
|
size_t returnedSize = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(
|
|
desc.get(), buffer.size(), &returnedSize, buffer.data());
|
|
EXPECT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
EXPECT_EQ(returnedSize, requiredSize);
|
|
|
|
// Verify the binary content is valid
|
|
flatbuffers::Verifier verifier(buffer.data(), returnedSize);
|
|
ASSERT_TRUE(verifier.VerifyBuffer<hipdnn_flatbuffers_sdk::data_objects::Graph>());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, BinaryRoundTripViaApi)
|
|
{
|
|
// Create a valid graph descriptor via the binary C API
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
hipdnnBackendDescriptor_t rawDesc1 = nullptr;
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
&rawDesc1, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc1, nullptr);
|
|
const ScopedBackendDescriptor scopedDesc1(rawDesc1);
|
|
|
|
// Serialize to binary via C API: first query the size
|
|
size_t binarySize = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(scopedDesc1.get(), 0, &binarySize, nullptr);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_GT(binarySize, 0u);
|
|
|
|
// Then retrieve the binary data
|
|
std::vector<uint8_t> binaryBuffer(binarySize);
|
|
size_t returnedSize = 0;
|
|
status = hipdnnBackendGetSerializedBinaryGraph_ext(
|
|
scopedDesc1.get(), binaryBuffer.size(), &returnedSize, binaryBuffer.data());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
|
|
// Deserialize from binary via C API
|
|
hipdnnBackendDescriptor_t rawDesc2 = nullptr;
|
|
status
|
|
= hipdnnBackendCreateAndDeserializeGraph_ext(&rawDesc2, binaryBuffer.data(), returnedSize);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_NE(rawDesc2, nullptr);
|
|
const ScopedBackendDescriptor scopedDesc2(rawDesc2);
|
|
|
|
// Verify the round-tripped graph matches
|
|
auto graphDesc1 = scopedDesc1.get()->asDescriptor<GraphDescriptor>();
|
|
auto graphDesc2 = scopedDesc2.get()->asDescriptor<GraphDescriptor>();
|
|
|
|
// desc1 buffer was populated by the GetSerializedBinaryGraph call above.
|
|
// desc2 needs explicit buildSerializedGraph() since it was only deserialized.
|
|
graphDesc2->buildSerializedGraph();
|
|
|
|
auto binary1 = graphDesc1->getSerializedGraph();
|
|
auto binary2 = graphDesc2->getSerializedGraph();
|
|
|
|
auto graph1 = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(binary1.ptr));
|
|
auto graph2 = hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(
|
|
static_cast<const uint8_t*>(binary2.ptr));
|
|
|
|
verifyGraphsEquivalent(*graph1, *graph2);
|
|
}
|
|
|
|
// ============================================================================
|
|
// HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT (RFC 0008)
|
|
// ============================================================================
|
|
|
|
TEST_F(TestGraphDescriptor, IsOverrideShapeEnabledDefaultsToFalseWhenUnset)
|
|
{
|
|
// A freshly-created descriptor that never had IS_OVERRIDE_SHAPE_ENABLED set
|
|
// should report false (the wire default for an absent optional bool).
|
|
const GraphDescriptor descriptor;
|
|
|
|
bool value = true;
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(
|
|
descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&count,
|
|
&value));
|
|
EXPECT_FALSE(value);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, IsOverrideShapeEnabledSetGetTrueRoundTrip)
|
|
{
|
|
GraphDescriptor descriptor;
|
|
|
|
bool input = true;
|
|
ASSERT_NO_THROW(descriptor.setAttribute(
|
|
HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT, HIPDNN_TYPE_BOOLEAN, 1, &input));
|
|
|
|
bool output = false;
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(
|
|
descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&count,
|
|
&output));
|
|
EXPECT_TRUE(output);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, IsOverrideShapeEnabledTrueSurvivesSerializationRoundTrip)
|
|
{
|
|
// Build a valid graph, set the opt-in flag, serialize, deserialize, verify
|
|
// the flag is preserved as true through the flatbuffer round-trip.
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor original;
|
|
original.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
bool input = true;
|
|
ASSERT_NO_THROW(original.setAttribute(
|
|
HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT, HIPDNN_TYPE_BOOLEAN, 1, &input));
|
|
|
|
auto handle = reinterpret_cast<hipdnnHandle_t>(0x12345678);
|
|
ASSERT_NO_THROW(original.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)));
|
|
ASSERT_NO_THROW(original.finalize());
|
|
|
|
auto serialized = original.getSerializedGraph();
|
|
|
|
GraphDescriptor revived;
|
|
revived.deserializeGraph(static_cast<const uint8_t*>(serialized.ptr), serialized.size);
|
|
|
|
bool output = false;
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(revived.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&count,
|
|
&output));
|
|
EXPECT_TRUE(output);
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, LegacyGraphWithoutOverrideShapeFieldRoundTripsToFalse)
|
|
{
|
|
// createValidGraph() does NOT set is_override_shape_enabled — it produces a
|
|
// wire image equivalent to a legacy graph that predates this field. Verify
|
|
// deserialize+get reports the wire default (false) without throwing.
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
bool value = true;
|
|
int64_t count = 0;
|
|
ASSERT_NO_THROW(
|
|
descriptor.getAttribute(HIPDNN_ATTR_OPERATIONGRAPH_IS_OVERRIDE_SHAPE_ENABLED_EXT,
|
|
HIPDNN_TYPE_BOOLEAN,
|
|
1,
|
|
&count,
|
|
&value));
|
|
EXPECT_FALSE(value);
|
|
}
|
|
|
|
// ============================================================================
|
|
// GraphDescriptor::hasRaggedTensors() (RFC 0014)
|
|
// ============================================================================
|
|
|
|
TEST_F(TestGraphDescriptor, HasRaggedTensorsFalseForNonRaggedGraph)
|
|
{
|
|
auto builder = createValidGraph();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
EXPECT_FALSE(descriptor.hasRaggedTensors());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, HasRaggedTensorsFalseForEmptyDescriptor)
|
|
{
|
|
// No operations => no tensors => not ragged.
|
|
const GraphDescriptor descriptor;
|
|
EXPECT_FALSE(descriptor.hasRaggedTensors());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, HasRaggedTensorsTrueWhenTensorCarriesRaggedOffset)
|
|
{
|
|
auto builder = test_utilities::createValidGraphWithRaggedTensor();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor descriptor;
|
|
descriptor.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
EXPECT_TRUE(descriptor.hasRaggedTensors());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, HasRaggedTensorsSurvivesSerializationRoundTrip)
|
|
{
|
|
auto builder = test_utilities::createValidGraphWithRaggedTensor();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor original;
|
|
original.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
|
|
auto handle = reinterpret_cast<hipdnnHandle_t>(0x12345678);
|
|
ASSERT_NO_THROW(original.setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)));
|
|
ASSERT_NO_THROW(original.finalize());
|
|
|
|
auto serialized = original.getSerializedGraph();
|
|
|
|
GraphDescriptor revived;
|
|
revived.deserializeGraph(static_cast<const uint8_t*>(serialized.ptr), serialized.size);
|
|
|
|
EXPECT_TRUE(revived.hasRaggedTensors());
|
|
}
|
|
|
|
TEST_F(TestGraphDescriptor, HasRaggedTensorsSurvivesJsonRoundTrip)
|
|
{
|
|
auto builder = test_utilities::createValidGraphWithRaggedTensor();
|
|
auto serializedGraph = builder.Release();
|
|
|
|
GraphDescriptor original;
|
|
original.deserializeGraph(serializedGraph.data(), serializedGraph.size());
|
|
original.buildSerializedGraph();
|
|
const auto jsonStr = original.getSerializedJsonGraph();
|
|
|
|
GraphDescriptor fromJson;
|
|
ASSERT_NO_THROW(
|
|
GraphDescriptor::createFromJsonGraph(fromJson, jsonStr.c_str(), jsonStr.size()));
|
|
|
|
EXPECT_TRUE(fromJson.hasRaggedTensors());
|
|
}
|