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
529 lines
20 KiB
C++
529 lines
20 KiB
C++
// Modification Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Unit tests for the public ncclParam* C API (handle-based and key-based
|
|
// parameter access). Grounded in the implementation at src/param/c_api.cc and
|
|
// src/include/param/param.h.
|
|
//
|
|
#include <gtest/gtest.h>
|
|
#include <rccl/rccl.h>
|
|
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
#include "common/ProcessIsolatedTestRunner.hpp"
|
|
|
|
// Internal param subsystem: DEFINE_NCCL_PARAM + parser factories. Uses the
|
|
// hipify-staged "_tmp" header names, matching src/param/*.cc.
|
|
#include "param/param_tmp.h"
|
|
#include "param/parsers.h"
|
|
|
|
namespace RcclUnitTesting {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Test-only registered parameters. Registered at static-init time and thus
|
|
// visible in every re-exec'd isolated child process.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Published integer params, one per width, used for typed-getter and boundary
|
|
// coverage. Defaults chosen so "unset" reads are unambiguous.
|
|
DEFINE_NCCL_PARAM(testParamI8, int8_t, NCCL_TEST_PARAM_I8, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only i8 param");
|
|
DEFINE_NCCL_PARAM(testParamI16, int16_t, NCCL_TEST_PARAM_I16, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only i16 param");
|
|
DEFINE_NCCL_PARAM(testParamI32, int32_t, NCCL_TEST_PARAM_I32, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only i32 param");
|
|
DEFINE_NCCL_PARAM(testParamI64, int64_t, NCCL_TEST_PARAM_I64, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only i64 param");
|
|
DEFINE_NCCL_PARAM(testParamU8, uint8_t, NCCL_TEST_PARAM_U8, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only u8 param");
|
|
DEFINE_NCCL_PARAM(testParamU16, uint16_t, NCCL_TEST_PARAM_U16, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only u16 param");
|
|
DEFINE_NCCL_PARAM(testParamU32, uint32_t, NCCL_TEST_PARAM_U32, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only u32 param");
|
|
DEFINE_NCCL_PARAM(testParamU64, uint64_t, NCCL_TEST_PARAM_U64, 7, NCCL_PARAM_FLAG_PUBLISHED,
|
|
NCCL_PARAM_DEFAULT, "test-only u64 param");
|
|
|
|
// Cached integer param for caching behavior tests.
|
|
DEFINE_NCCL_PARAM(testParamCached, int32_t, NCCL_TEST_PARAM_CACHED, 0,
|
|
NCCL_PARAM_FLAG_PUBLISHED | NCCL_PARAM_FLAG_CACHED, NCCL_PARAM_DEFAULT,
|
|
"test-only cached i32 param");
|
|
|
|
// Private (non-published) param for published-vs-all filtering tests.
|
|
DEFINE_NCCL_PARAM(testParamPrivate, int32_t, NCCL_TEST_PARAM_PRIVATE, 0, NCCL_PARAM_FLAG_NONE,
|
|
NCCL_PARAM_DEFAULT, "test-only private i32 param");
|
|
|
|
namespace {
|
|
|
|
// Keys used across tests.
|
|
constexpr const char* kI32Key = "NCCL_TEST_PARAM_I32";
|
|
constexpr const char* kI8Key = "NCCL_TEST_PARAM_I8";
|
|
constexpr const char* kU8Key = "NCCL_TEST_PARAM_U8";
|
|
constexpr const char* kCachedKey = "NCCL_TEST_PARAM_CACHED";
|
|
constexpr const char* kPrivateKey = "NCCL_TEST_PARAM_PRIVATE";
|
|
constexpr const char* kDumpAllKey = "NCCL_PARAM_DUMP_ALL";
|
|
constexpr const char* kNoCacheKey = "NCCL_NO_CACHE";
|
|
|
|
// Returns true if `table` (length `len`) contains `key`.
|
|
bool tableContains(const char** table, int len, const char* key) {
|
|
for (int i = 0; i < len; ++i) {
|
|
if (table[i] && std::strcmp(table[i], key) == 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// ===========================================================================
|
|
// ncclParamBind
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, Bind_KnownKey_Succeeds) {
|
|
RUN_ISOLATED_TEST("Bind_KnownKey_Succeeds", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
ASSERT_NE(h, nullptr);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, Bind_UnknownKey_ReturnsInvalidArgAndLeavesOutUntouched) {
|
|
RUN_ISOLATED_TEST("Bind_UnknownKey", []() {
|
|
auto* sentinel = reinterpret_cast<ncclParamHandle_t*>(0xdeadbeef);
|
|
ncclParamHandle_t* h = sentinel;
|
|
ASSERT_EQ(ncclParamBind(&h, "NCCL_DEFINITELY_NOT_A_PARAM"), ncclInvalidArgument);
|
|
ASSERT_EQ(h, sentinel) << "*out must be left untouched on unknown key";
|
|
});
|
|
}
|
|
|
|
// Legacy NCCL_PARAM(...) knobs are not registered in the new registry.
|
|
TEST(ParameterApiTests, Bind_LegacyParamNotRegistered) {
|
|
RUN_ISOLATED_TEST("Bind_LegacyParamNotRegistered", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, "NCCL_DEBUG"), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, Bind_NullArgs) {
|
|
RUN_ISOLATED_TEST("Bind_NullArgs", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(nullptr, kI32Key), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamBind(&h, nullptr), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Typed integer accessors
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, GetI32_MatchingType_ReturnsValue) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetI32_MatchingType",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 42);
|
|
},
|
|
{{"NCCL_TEST_PARAM_I32", "42"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetI32_Unset_ReturnsDefault) {
|
|
RUN_ISOLATED_TEST("GetI32_Unset_ReturnsDefault", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 7) << "default value from DEFINE_NCCL_PARAM";
|
|
});
|
|
}
|
|
|
|
// A handle bound to a bool param cannot be read as I32 (typeId guard).
|
|
TEST(ParameterApiTests, GetI32_TypeMismatch_ReturnsInvalidArg) {
|
|
RUN_ISOLATED_TEST("GetI32_TypeMismatch", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kDumpAllKey), ncclSuccess); // bool param
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
// Reading an I8 param through the I32 accessor is a typeId mismatch.
|
|
TEST(ParameterApiTests, GetI32_CrossWidth_ReturnsInvalidArg) {
|
|
RUN_ISOLATED_TEST("GetI32_CrossWidth", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI8Key), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetI32_NullArgs) {
|
|
RUN_ISOLATED_TEST("GetI32_NullArgs", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(nullptr, &v), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamGetI32(h, nullptr), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetU32_MatchingType_ReturnsValue) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetU32_MatchingType",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, "NCCL_TEST_PARAM_U32"), ncclSuccess);
|
|
uint32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetU32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 4000000000u);
|
|
},
|
|
{{"NCCL_TEST_PARAM_U32", "4000000000"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetI64_MatchingType_ReturnsValue) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetI64_MatchingType",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, "NCCL_TEST_PARAM_I64"), ncclSuccess);
|
|
int64_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI64(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, static_cast<int64_t>(9000000000LL));
|
|
},
|
|
{{"NCCL_TEST_PARAM_I64", "9000000000"}});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Boundary / overflow behavior
|
|
//
|
|
// The integer parser uses strtoll/strtoull. Values that overflow (unsigned)
|
|
// long long set errno==ERANGE and are rejected -> fall back to default. Values
|
|
// that fit long long but overflow the narrow target width are silently
|
|
// truncated by static_cast, and validate() runs on the truncated value, so
|
|
// they are accepted (wrapped), not rejected.
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, GetI64_WordOverflow_FallsBackToDefault) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetI64_WordOverflow",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, "NCCL_TEST_PARAM_I64"), ncclSuccess);
|
|
int64_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI64(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 7) << "ERANGE overflow -> default";
|
|
},
|
|
{{"NCCL_TEST_PARAM_I64", "99999999999999999999"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetI8_InRange_ReturnsValue) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetI8_InRange",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI8Key), ncclSuccess);
|
|
int8_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI8(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, static_cast<int8_t>(127));
|
|
},
|
|
{{"NCCL_TEST_PARAM_I8", "127"}});
|
|
}
|
|
|
|
// "128" fits in long long, then truncates to int8_t(-128); validate() sees the
|
|
// already-wrapped value and passes, so the wrapped result is returned.
|
|
TEST(ParameterApiTests, GetI8_SubWordOverflow_Wraps) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetI8_SubWordOverflow",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI8Key), ncclSuccess);
|
|
int8_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI8(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, static_cast<int8_t>(-128)) << "128 wraps to -128 for int8_t";
|
|
},
|
|
{{"NCCL_TEST_PARAM_I8", "128"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetU8_Negative_Wraps) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetU8_Negative",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kU8Key), ncclSuccess);
|
|
uint8_t v = 0;
|
|
// strtoull("-1") wraps to ULLONG_MAX (no ERANGE), truncates to 0xFF.
|
|
ASSERT_EQ(ncclParamGetU8(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, static_cast<uint8_t>(0xFF));
|
|
},
|
|
{{"NCCL_TEST_PARAM_U8", "-1"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetI32_NonNumeric_FallsBackToDefault) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetI32_NonNumeric",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 7) << "unparseable value -> default";
|
|
},
|
|
{{"NCCL_TEST_PARAM_I32", "not_a_number"}});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ncclParamGetStr
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, GetStr_StringParam_ReturnsValue) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetStr_StringParam",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kNoCacheKey), ncclSuccess);
|
|
const char* s = nullptr;
|
|
ASSERT_EQ(ncclParamGetStr(h, &s), ncclSuccess);
|
|
ASSERT_NE(s, nullptr);
|
|
ASSERT_STREQ(s, "NCCL_TEST_PARAM_I32");
|
|
},
|
|
{{"NCCL_NO_CACHE", "NCCL_TEST_PARAM_I32"}});
|
|
}
|
|
|
|
// A non-cstr (bool) param cannot be read via GetStr.
|
|
TEST(ParameterApiTests, GetStr_TypeMismatch_ReturnsInvalidArg) {
|
|
RUN_ISOLATED_TEST("GetStr_TypeMismatch", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kDumpAllKey), ncclSuccess); // bool param
|
|
const char* s = nullptr;
|
|
ASSERT_EQ(ncclParamGetStr(h, &s), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetStr_NullArgs) {
|
|
RUN_ISOLATED_TEST("GetStr_NullArgs", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kNoCacheKey), ncclSuccess);
|
|
const char* s = nullptr;
|
|
ASSERT_EQ(ncclParamGetStr(nullptr, &s), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamGetStr(h, nullptr), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ncclParamGet (raw)
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, GetRaw_SufficientBuffer_WritesLenBytes) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetRaw_SufficientBuffer",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int32_t buf = 0;
|
|
int len = -1;
|
|
ASSERT_EQ(ncclParamGet(h, &buf, sizeof(buf), &len), ncclSuccess);
|
|
ASSERT_EQ(len, static_cast<int>(sizeof(int32_t)));
|
|
ASSERT_EQ(buf, 55);
|
|
},
|
|
{{"NCCL_TEST_PARAM_I32", "55"}});
|
|
}
|
|
|
|
// A too-small buffer returns ncclInvalidArgument and sets *len = 0 (NOT the
|
|
// required size that the NVIDIA doc describes).
|
|
TEST(ParameterApiTests, GetRaw_MaxLenTooSmall_ReturnsInvalidArgAndZeroLen) {
|
|
RUN_ISOLATED_TEST("GetRaw_MaxLenTooSmall", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int8_t tiny = 0;
|
|
int len = -1;
|
|
ASSERT_EQ(ncclParamGet(h, &tiny, sizeof(tiny), &len), ncclInvalidArgument);
|
|
ASSERT_EQ(len, 0);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetRaw_NullArgs) {
|
|
RUN_ISOLATED_TEST("GetRaw_NullArgs", []() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kI32Key), ncclSuccess);
|
|
int32_t buf = 0;
|
|
int len = 0;
|
|
ASSERT_EQ(ncclParamGet(nullptr, &buf, sizeof(buf), &len), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamGet(h, nullptr, sizeof(buf), &len), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ncclParamGetParameter (key-based)
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, GetParameter_KnownKey_ReturnsStringAndLen) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetParameter_KnownKey",
|
|
[]() {
|
|
const char* value = nullptr;
|
|
int len = -1;
|
|
ASSERT_EQ(ncclParamGetParameter(kI32Key, &value, &len), ncclSuccess);
|
|
ASSERT_NE(value, nullptr);
|
|
ASSERT_STREQ(value, "123");
|
|
ASSERT_EQ(len, static_cast<int>(std::strlen(value)));
|
|
},
|
|
{{"NCCL_TEST_PARAM_I32", "123"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetParameter_UnknownKey_SetsNullZeroAndInvalidArg) {
|
|
RUN_ISOLATED_TEST("GetParameter_UnknownKey", []() {
|
|
const char* value = reinterpret_cast<const char*>(0x1);
|
|
int len = -1;
|
|
ASSERT_EQ(ncclParamGetParameter("NCCL_DEFINITELY_NOT_A_PARAM", &value, &len),
|
|
ncclInvalidArgument);
|
|
ASSERT_EQ(value, nullptr);
|
|
ASSERT_EQ(len, 0);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetParameter_NullArgs) {
|
|
RUN_ISOLATED_TEST("GetParameter_NullArgs", []() {
|
|
const char* value = nullptr;
|
|
int len = 0;
|
|
ASSERT_EQ(ncclParamGetParameter(nullptr, &value, &len), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamGetParameter(kI32Key, nullptr, &len), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamGetParameter(kI32Key, &value, nullptr), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ncclParamGetAllParameterKeys
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, GetAllKeys_DefaultPublishedOnly) {
|
|
RUN_ISOLATED_TEST("GetAllKeys_DefaultPublishedOnly", []() {
|
|
const char** table = nullptr;
|
|
int len = 0;
|
|
ASSERT_EQ(ncclParamGetAllParameterKeys(&table, &len), ncclSuccess);
|
|
ASSERT_GT(len, 0);
|
|
EXPECT_TRUE(tableContains(table, len, kI32Key)) << "published test key expected";
|
|
EXPECT_FALSE(tableContains(table, len, kPrivateKey))
|
|
<< "private key must be excluded by default";
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetAllKeys_DumpAllIncludesPrivate) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"GetAllKeys_DumpAllIncludesPrivate",
|
|
[]() {
|
|
const char** table = nullptr;
|
|
int len = 0;
|
|
ASSERT_EQ(ncclParamGetAllParameterKeys(&table, &len), ncclSuccess);
|
|
EXPECT_TRUE(tableContains(table, len, kPrivateKey))
|
|
<< "NCCL_PARAM_DUMP_ALL=1 must include private keys";
|
|
},
|
|
{{"NCCL_PARAM_DUMP_ALL", "1"}});
|
|
}
|
|
|
|
TEST(ParameterApiTests, GetAllKeys_NullArgs) {
|
|
RUN_ISOLATED_TEST("GetAllKeys_NullArgs", []() {
|
|
const char** table = nullptr;
|
|
int len = 0;
|
|
ASSERT_EQ(ncclParamGetAllParameterKeys(nullptr, &len), ncclInvalidArgument);
|
|
ASSERT_EQ(ncclParamGetAllParameterKeys(&table, nullptr), ncclInvalidArgument);
|
|
});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ncclParamDumpAll
|
|
// ===========================================================================
|
|
|
|
TEST(ParameterApiTests, DumpAll_WritesRegistryToStdout) {
|
|
RUN_ISOLATED_TEST("DumpAll_WritesRegistryToStdout", []() {
|
|
testing::internal::CaptureStdout();
|
|
ncclParamDumpAll();
|
|
std::string out = testing::internal::GetCapturedStdout();
|
|
EXPECT_NE(out.find("=== ncclParam Registry Dump ==="), std::string::npos);
|
|
EXPECT_NE(out.find("NCCL_TEST_PARAM_I32"), std::string::npos);
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, DumpAll_PrivateHiddenByDefault) {
|
|
RUN_ISOLATED_TEST("DumpAll_PrivateHiddenByDefault", []() {
|
|
testing::internal::CaptureStdout();
|
|
ncclParamDumpAll();
|
|
std::string out = testing::internal::GetCapturedStdout();
|
|
EXPECT_EQ(out.find("NCCL_TEST_PARAM_PRIVATE"), std::string::npos)
|
|
<< "private key must be hidden without NCCL_PARAM_DUMP_ALL";
|
|
});
|
|
}
|
|
|
|
TEST(ParameterApiTests, DumpAll_PrivateShownWhenDumpAll) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"DumpAll_PrivateShownWhenDumpAll",
|
|
[]() {
|
|
testing::internal::CaptureStdout();
|
|
ncclParamDumpAll();
|
|
std::string out = testing::internal::GetCapturedStdout();
|
|
EXPECT_NE(out.find("NCCL_TEST_PARAM_PRIVATE"), std::string::npos);
|
|
},
|
|
{{"NCCL_PARAM_DUMP_ALL", "1"}});
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Caching behavior (NCCL_NO_CACHE)
|
|
// ===========================================================================
|
|
|
|
// A CACHED param resolves once; a later env change is ignored.
|
|
TEST(ParameterApiTests, Cache_DefaultCachesValue) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"Cache_DefaultCachesValue",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kCachedKey), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 100);
|
|
|
|
setenv("NCCL_TEST_PARAM_CACHED", "200", 1);
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 100) << "cached value must not change when NCCL_NO_CACHE is unset";
|
|
},
|
|
{{"NCCL_TEST_PARAM_CACHED", "100"}});
|
|
}
|
|
|
|
// Listing the key in NCCL_NO_CACHE forces a re-read on the next access.
|
|
TEST(ParameterApiTests, Cache_PerKeyForcesReRead) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"Cache_PerKeyForcesReRead",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kCachedKey), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 100);
|
|
|
|
setenv("NCCL_TEST_PARAM_CACHED", "200", 1);
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 200) << "value should be re-read when key is listed in NCCL_NO_CACHE";
|
|
},
|
|
{{"NCCL_NO_CACHE", "NCCL_TEST_PARAM_CACHED"}, {"NCCL_TEST_PARAM_CACHED", "100"}});
|
|
}
|
|
|
|
// NCCL_NO_CACHE=ALL disables caching for every key.
|
|
TEST(ParameterApiTests, Cache_AllForcesReRead) {
|
|
RUN_ISOLATED_TEST_WITH_ENV(
|
|
"Cache_AllForcesReRead",
|
|
[]() {
|
|
ncclParamHandle_t* h = nullptr;
|
|
ASSERT_EQ(ncclParamBind(&h, kCachedKey), ncclSuccess);
|
|
int32_t v = 0;
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 100);
|
|
|
|
setenv("NCCL_TEST_PARAM_CACHED", "200", 1);
|
|
ASSERT_EQ(ncclParamGetI32(h, &v), ncclSuccess);
|
|
ASSERT_EQ(v, 200) << "NCCL_NO_CACHE=ALL should disable caching for every key";
|
|
},
|
|
{{"NCCL_NO_CACHE", "ALL"}, {"NCCL_TEST_PARAM_CACHED", "100"}});
|
|
}
|
|
|
|
} // namespace RcclUnitTesting
|