Files
TheRock/rocm-systems/projects/rccl/test/ParameterApiTests.cpp
T
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

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