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

306 lines
12 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <algorithm>
#include <gtest/gtest.h>
#include <hipdnn_data_sdk/utilities/EngineNames.hpp>
#include <string>
#include <unordered_map>
#include <unordered_set>
using namespace hipdnn_data_sdk::utilities;
// Test engines defined with the macro to verify expansion
// These use unique names to avoid collision with production engines
namespace test_macro_expansion
{
// Single-argument form: identifier and display name are the same
HIPDNN_REGISTER_ENGINE(TEST_MACRO_SINGLE_ARG_ENGINE)
// Two-argument form: identifier differs from display name
HIPDNN_REGISTER_ENGINE(TEST_MACRO_DUAL_ARG_ENGINE, "CustomDisplayName")
// Two-argument form where identifier and display name happen to match (like MIOPEN_ENGINE)
HIPDNN_REGISTER_ENGINE(TEST_MACRO_MATCHING_ARGS_ENGINE, "TEST_MACRO_MATCHING_ARGS_ENGINE")
} // namespace test_macro_expansion
class TestEngineNames : public ::testing::Test
{
};
TEST_F(TestEngineNames, MacroGeneratesCorrectConstants)
{
// Check that the string constants are defined
EXPECT_STREQ(MIOPEN_ENGINE_NAME, "MIOPEN_ENGINE");
EXPECT_STREQ(HIPBLASLT_ENGINE_NAME, "HIPBLASLT_ENGINE");
// Check that the ID constants are defined and match the hash function
EXPECT_EQ(MIOPEN_ENGINE_ID, engineNameToId("MIOPEN_ENGINE"));
EXPECT_EQ(HIPBLASLT_ENGINE_ID, engineNameToId("HIPBLASLT_ENGINE"));
}
TEST_F(TestEngineNames, EngineIdToNameMappingConsistent)
{
// Get the ID to name map
const auto& idToName = getEngineIdToNameMap();
// Verify each mapping is consistent
for(const auto& [id, name] : idToName)
{
auto calculatedId = engineNameToId(name);
EXPECT_EQ(id, calculatedId)
<< "ID mismatch for engine: " << name << " (stored: 0x" << std::hex << id
<< ", calculated: 0x" << calculatedId << std::dec << ")";
}
}
TEST_F(TestEngineNames, IsEngineNameRegistered)
{
// Test with known registered names
EXPECT_TRUE(isEngineNameRegistered(MIOPEN_ENGINE_NAME));
EXPECT_TRUE(isEngineNameRegistered(HIPBLASLT_ENGINE_NAME));
// Test with unregistered names
EXPECT_FALSE(isEngineNameRegistered("UNKNOWN_ENGINE"));
EXPECT_FALSE(isEngineNameRegistered("NOT_REGISTERED"));
EXPECT_FALSE(isEngineNameRegistered(""));
}
TEST_F(TestEngineNames, GetEngineNameFromId)
{
// Test with registered engines
EXPECT_EQ(getEngineNameFromId(MIOPEN_ENGINE_ID), "MIOPEN_ENGINE");
EXPECT_EQ(getEngineNameFromId(HIPBLASLT_ENGINE_ID), "HIPBLASLT_ENGINE");
// Test with non-existent ID - should throw
const int64_t nonExistentId = 0xDEADBEEF;
EXPECT_THROW(getEngineNameFromId(nonExistentId), std::out_of_range);
}
TEST_F(TestEngineNames, RegisteredRocKEEngineHasStableNameAndId)
{
EXPECT_STREQ(ROCKE_ENGINE_NAME, "ROCKE_ENGINE");
EXPECT_EQ(ROCKE_ENGINE_ID, engineNameToId("ROCKE_ENGINE"));
EXPECT_TRUE(isEngineNameRegistered(ROCKE_ENGINE_NAME));
EXPECT_EQ(getEngineNameFromId(ROCKE_ENGINE_ID), "ROCKE_ENGINE");
}
TEST_F(TestEngineNames, EngineCountMatches)
{
// Verify that the number of engines in getAllEngineNames matches getEngineIdToNameMap
const auto& allEngines = getAllEngineNames();
const auto& idToName = getEngineIdToNameMap();
EXPECT_EQ(allEngines.size(), idToName.size())
<< "Mismatch between getAllEngineNames and getEngineIdToNameMap sizes";
// Also verify all names in one are in the other
for(const auto& name : allEngines)
{
auto id = engineNameToId(name);
EXPECT_NE(idToName.find(id), idToName.end())
<< "Engine '" << name << "' is in getAllEngineNames but not in getEngineIdToNameMap";
}
}
TEST_F(TestEngineNames, RegistrarSucceedsForNewUniqueName)
{
// Registering a brand-new unique engine name should not throw
EXPECT_NO_THROW(EngineRegistrar{"TEST_UNIQUE_ENGINE_FOR_REGISTRAR"});
// Verify it was registered
EXPECT_TRUE(isEngineNameRegistered("TEST_UNIQUE_ENGINE_FOR_REGISTRAR"));
auto id = engineNameToId("TEST_UNIQUE_ENGINE_FOR_REGISTRAR");
EXPECT_EQ(getEngineNameFromId(id), "TEST_UNIQUE_ENGINE_FOR_REGISTRAR");
}
TEST_F(TestEngineNames, RegistrarThrowsOnDuplicateName)
{
// These names are already in the map from HIPDNN_REGISTER_ENGINE static initialization.
// Re-registering should throw to catch accidental duplicate engine definitions.
const std::string_view miopenName{MIOPEN_ENGINE_NAME};
EXPECT_THROW(EngineRegistrar{miopenName}, std::runtime_error);
}
TEST_F(TestEngineNames, RegistrarDetectsCollision)
{
// Simulate a collision by inserting a fake entry into the ID map
// with the same ID that "COLLISION_TEST_ENGINE" would generate,
// but mapped to a different name
auto collisionId = engineNameToId("COLLISION_TEST_ENGINE");
detail::getMutableEngineIdToNameMap()[collisionId] = "SOME_OTHER_ENGINE";
// Now registering "COLLISION_TEST_ENGINE" should throw because the ID
// is already taken by "SOME_OTHER_ENGINE"
EXPECT_THROW(EngineRegistrar{"COLLISION_TEST_ENGINE"}, std::runtime_error);
// Clean up: remove the fake entry so other tests aren't affected
detail::getMutableEngineIdToNameMap().erase(collisionId);
}
TEST_F(TestEngineNames, EnsureAllEngineNameToIdsBehaveTheSame)
{
{
auto engineIdCString = engineNameToId(MIOPEN_ENGINE_NAME);
auto engineIdString = engineNameToId(std::string(MIOPEN_ENGINE_NAME));
auto engineIdStringView = engineNameToId(std::string_view(MIOPEN_ENGINE_NAME));
EXPECT_EQ(engineIdCString, engineIdString);
EXPECT_EQ(engineIdCString, engineIdStringView);
}
{
auto engineIdCString = engineNameToId(HIPBLASLT_ENGINE_NAME);
auto engineIdString = engineNameToId(std::string(HIPBLASLT_ENGINE_NAME));
auto engineIdStringView = engineNameToId(std::string_view(HIPBLASLT_ENGINE_NAME));
EXPECT_EQ(engineIdCString, engineIdString);
EXPECT_EQ(engineIdCString, engineIdStringView);
}
}
TEST_F(TestEngineNames, EngineNameOrIdParsesRegisteredName)
{
EXPECT_EQ(engineNameOrIdToId(MIOPEN_ENGINE_NAME), MIOPEN_ENGINE_ID);
}
TEST_F(TestEngineNames, EngineNameOrIdPrefersRegisteredNumericName)
{
[[maybe_unused]] static const EngineRegistrar s_numericEngine("8675309");
EXPECT_TRUE(isEngineNameRegistered("8675309"));
EXPECT_EQ(engineNameOrIdToId("8675309"), engineNameToId("8675309"));
EXPECT_NE(engineNameOrIdToId("8675309"), 8675309);
}
TEST_F(TestEngineNames, EngineNameOrIdParsesDecimalId)
{
EXPECT_EQ(engineNameOrIdToId("12345"), 12345);
EXPECT_EQ(engineNameOrIdToId("-18"), -18);
}
TEST_F(TestEngineNames, EngineNameOrIdParsesPlusSignedDecimalId)
{
EXPECT_EQ(engineNameOrIdToId("+12345"), 12345);
}
TEST_F(TestEngineNames, EngineNameOrIdTreatsBarePlusAsName)
{
EXPECT_EQ(engineNameOrIdToId("+"), engineNameToId("+"));
}
TEST_F(TestEngineNames, EngineNameOrIdParsesHexId)
{
EXPECT_EQ(engineNameOrIdToId("0x0000000000003039"), 12345);
EXPECT_EQ(engineNameOrIdToId("0xffffffffffffffee"), -18);
}
TEST_F(TestEngineNames, EngineNameOrIdTreatsLeadingWhitespaceAsName)
{
EXPECT_EQ(engineNameOrIdToId(" 12345"), engineNameToId(" 12345"));
EXPECT_NE(engineNameOrIdToId(" 12345"), 12345);
}
TEST_F(TestEngineNames, MacroSingleArgGeneratesCorrectName)
{
// Single-argument form: _NAME should be the stringified identifier
EXPECT_STREQ(test_macro_expansion::TEST_MACRO_SINGLE_ARG_ENGINE_NAME,
"TEST_MACRO_SINGLE_ARG_ENGINE");
}
TEST_F(TestEngineNames, MacroSingleArgGeneratesCorrectId)
{
// Single-argument form: _ID should be computed from the stringified identifier
EXPECT_EQ(test_macro_expansion::TEST_MACRO_SINGLE_ARG_ENGINE_ID,
engineNameToId("TEST_MACRO_SINGLE_ARG_ENGINE"));
}
TEST_F(TestEngineNames, MacroSingleArgRegistersEngine)
{
// Single-argument form: engine should be registered with the stringified identifier
EXPECT_TRUE(isEngineNameRegistered("TEST_MACRO_SINGLE_ARG_ENGINE"));
EXPECT_EQ(getEngineNameFromId(test_macro_expansion::TEST_MACRO_SINGLE_ARG_ENGINE_ID),
"TEST_MACRO_SINGLE_ARG_ENGINE");
}
TEST_F(TestEngineNames, MacroDualArgGeneratesCorrectName)
{
// Two-argument form: _NAME should be the custom display name, not the identifier
EXPECT_STREQ(test_macro_expansion::TEST_MACRO_DUAL_ARG_ENGINE_NAME, "CustomDisplayName");
}
TEST_F(TestEngineNames, MacroDualArgGeneratesCorrectId)
{
// Two-argument form: _ID should be computed from the custom display name
EXPECT_EQ(test_macro_expansion::TEST_MACRO_DUAL_ARG_ENGINE_ID,
engineNameToId("CustomDisplayName"));
// Verify it's NOT computed from the identifier
EXPECT_NE(test_macro_expansion::TEST_MACRO_DUAL_ARG_ENGINE_ID,
engineNameToId("TEST_MACRO_DUAL_ARG_ENGINE"));
}
TEST_F(TestEngineNames, MacroDualArgRegistersWithDisplayName)
{
// Two-argument form: engine should be registered with the display name, not the identifier
EXPECT_TRUE(isEngineNameRegistered("CustomDisplayName"));
EXPECT_FALSE(isEngineNameRegistered("TEST_MACRO_DUAL_ARG_ENGINE"));
EXPECT_EQ(getEngineNameFromId(test_macro_expansion::TEST_MACRO_DUAL_ARG_ENGINE_ID),
"CustomDisplayName");
}
TEST_F(TestEngineNames, EngineNameOrIdToIdParsesPositiveDecimal)
{
// A plain decimal serialized ID round-trips exactly.
EXPECT_EQ(engineNameOrIdToId("12345"), 12345);
}
TEST_F(TestEngineNames, EngineNameOrIdToIdRecoversNegativeIdFromSignedHex)
{
// The frontend serializes an unregistered negative ID (e.g. test plugin -18)
// as two's-complement unsigned hex. strtoull + reinterpret must recover -18.
EXPECT_EQ(engineNameOrIdToId("0xffffffffffffffee"), static_cast<int64_t>(-18));
}
TEST_F(TestEngineNames, EngineNameOrIdToIdParsesPositiveHex)
{
EXPECT_EQ(engineNameOrIdToId("0x10"), 16);
EXPECT_EQ(engineNameOrIdToId("0X1A2B"), 0x1A2B);
}
TEST_F(TestEngineNames, EngineNameOrIdToIdParsesPlainDecimalNegative)
{
EXPECT_EQ(engineNameOrIdToId("-18"), static_cast<int64_t>(-18));
}
TEST_F(TestEngineNames, EngineNameOrIdToIdHashesOrdinaryName)
{
// An ordinary, non-numeric engine name falls through to FNV-1a, matching
// engineNameToId so registered engines still resolve.
EXPECT_EQ(engineNameOrIdToId("MIOPEN_ENGINE"), engineNameToId("MIOPEN_ENGINE"));
}
TEST_F(TestEngineNames, EngineNameOrIdToIdTreatsWhitespaceAsNameCharacter)
{
// Whitespace is a valid name character: a string is only treated as numeric
// when it parses fully with no whitespace anywhere. Leading, trailing, and
// internal whitespace all force the FNV-1a name path (matching engineNameToId
// on the untrimmed string), and an all-whitespace or empty string is a name.
EXPECT_EQ(engineNameOrIdToId(" 123"), engineNameToId(" 123"));
EXPECT_EQ(engineNameOrIdToId("\t123"), engineNameToId("\t123"));
EXPECT_EQ(engineNameOrIdToId("\n123"), engineNameToId("\n123"));
EXPECT_EQ(engineNameOrIdToId("123 "), engineNameToId("123 "));
EXPECT_EQ(engineNameOrIdToId("1 2 3"), engineNameToId("1 2 3"));
EXPECT_EQ(engineNameOrIdToId(" "), engineNameToId(" "));
EXPECT_EQ(engineNameOrIdToId(""), engineNameToId(""));
}
TEST_F(TestEngineNames, MacroMatchingArgsEquivalentToSingleArg)
{
// Two-argument form with matching args should behave the same as single-argument form
EXPECT_STREQ(test_macro_expansion::TEST_MACRO_MATCHING_ARGS_ENGINE_NAME,
"TEST_MACRO_MATCHING_ARGS_ENGINE");
EXPECT_EQ(test_macro_expansion::TEST_MACRO_MATCHING_ARGS_ENGINE_ID,
engineNameToId("TEST_MACRO_MATCHING_ARGS_ENGINE"));
EXPECT_TRUE(isEngineNameRegistered("TEST_MACRO_MATCHING_ARGS_ENGINE"));
}