TDengine/source/dnode/mnode/impl/test/xnode/xnodeMemoryTest.cpp

363 lines
8.3 KiB
C++
Raw Normal View History

feat(taosx): support distributed taosx (#34126) * feat: add xnode syntax * refactor(xnode): reduce function complexity * chore: add lost xnode.h file * feat(xnode): create xnode task * chore: fix double free error * add xnoded * start xnoded as subprocess * complete xnode task feature * complete show xnode jobs feature * complete with option feature * complete alter xnode job feature * complete alter xnode task feature * complete user pass feature * clean code * modify status type as char * fix leader ep null * fix start task req null * fix pass id for status * support timeout msg * drop xnode task relative jobs * clean code * wip * chore: add test cases for xnode * chore: fix 3.0 merge changes * fix drain core dump and create task core dump * add password check * retrieve xnode status from xnoded * pass integer as double to cjson * add some debug log * add some job log * fix start task lock * do not handle http response * fix coredump drop xnode task by name * support start/stop/drop task by name * remove mock xnoded * support unix socket * kill pre-xnoded before start * support dnode close xnoded * test(xnode): add unit test cases for xnode * rebalance support where clause * fix some test issue * unformat http post content json string * add xnode zh doc * modify drain description * remove job create/stop/drop operation * support rebalance all without where condition * support alter task by name * add NULL param for mndCheckOperPrivilege * add xnode txnode module for libmnode.a * code clean * change parser len to 4096 * clean code * chore: try to fix gtest/gtest.h not found * chore: fix markdown files * chore: fix markdown in zh * chore: fix enum issue and add ci * chore: fix test case problem * chore: fix pKeyVal overflow * chore: rename to 排空节点 * chore: external cmake remove parallel * chore: add DEP_ext_gtest for xnode test * chore: fix gtest errors * chore: remove gtest pthread lib * chore: fix data type not match * chore: fix some lint errors * chore: fix void unlink * chore: fix return with null pointer check * chore: fix pointer double free and xnodeMemoryTest strncpy null * chore: fix xnode encode action invalid datelen * chore: remove TD_LINUX condition * chore: use PRIu64 denote long long * chore: fix task parser NULL and allow no with clause * fix(xnode): fix windows build error * chore: fix windows curl error * chore: fix test case ins_tables relative error * chore: fix memory leak * docs: update taosx docs * chore: update taosx docs * chore: add role priviledge table * chore: fix error code doc * chore: fix test_xnode.py * chore: fix doc typo * fix: ci error while run test_user_privilege_sysinfo.py --------- Co-authored-by: Linhe Huo <linhehuo@gmail.com> Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Simon Guan <guanshengliang@qq.com>
2026-01-01 06:51:03 +00:00
/**
* @file xnodeMemoryTest.cpp
* @brief XNode memory management unit tests
* @version 1.0
* @date 2025-12-25
*/
#include <gtest/gtest.h>
#include <cstdlib>
#include <cstring>
#include <vector>
// Test structure
// typedef struct SXnodeObj {
// int32_t id;
// int32_t urlLen;
// char* url;
// int32_t statusLen;
// char* status;
// int64_t createTime;
// int64_t updateTime;
// } SXnodeObj;
// typedef struct SXnodeTaskObj {
// int32_t tid;
// char name[64];
// int32_t xnodeId;
// int32_t agentId;
// int32_t optionsLen;
// char* options;
// int64_t createTime;
// int64_t updateTime;
// } SXnodeTaskObj;
extern "C" {
#include "mndDef.h"
}
class XnodeMemoryTest : public ::testing::Test {
protected:
void SetUp() override {
allocCount = 0;
freeCount = 0;
}
void TearDown() override {
// Verify no memory leaks
EXPECT_EQ(allocCount, freeCount) << "Memory leak detected!";
}
int allocCount;
int freeCount;
SXnodeObj* createXnode(int id, const char* url, const char* status) {
SXnodeObj* obj = (SXnodeObj*)taosMemMalloc(sizeof(SXnodeObj));
allocCount++;
memset(obj, 0, sizeof(SXnodeObj));
obj->id = id;
if (url) {
obj->urlLen = strlen(url) + 1;
obj->url = (char*)taosMemMalloc(obj->urlLen);
allocCount++;
strcpy(obj->url, url);
}
if (status) {
obj->statusLen = strlen(status) + 1;
obj->status = (char*)taosMemMalloc(obj->statusLen);
allocCount++;
strcpy(obj->status, status);
}
obj->createTime = 1000000;
obj->updateTime = 2000000;
return obj;
}
void freeXnode(SXnodeObj* obj) {
if (!obj) return;
if (obj->url) {
taosMemFree(obj->url);
}
if (obj->status) {
taosMemFree(obj->status);
}
taosMemFree(obj);
freeCount++;
}
SXnodeTaskObj* createTask(int tid, const char* name, int xnodeId) {
SXnodeTaskObj* task = (SXnodeTaskObj*)taosMemMalloc(sizeof(SXnodeTaskObj));
allocCount++;
memset(task, 0, sizeof(SXnodeTaskObj));
task->id = tid;
task->xnodeId = xnodeId;
task->via = -1;
task->name = (char*)taosMemCalloc(1, strlen(name) + 1);
allocCount++;
if (name) {
strncpy(task->name, name, sizeof(task->name) - 1);
}
return task;
}
void freeTask(SXnodeTaskObj* task) {
if (!task) return;
if (task->name) {
taosMemFree(task->name);
freeCount++;
}
if (task->sourceDsn) {
taosMemFree(task->sourceDsn);
freeCount++;
}
if (task->sinkDsn) {
taosMemFree(task->sinkDsn);
freeCount++;
}
if (task->parser) {
taosMemFree(task->parser);
freeCount++;
}
if (task->status) {
taosMemFree(task->status);
freeCount++;
}
if (task->reason) {
taosMemFree(task->reason);
freeCount++;
}
taosMemFree(task);
freeCount++;
}
};
TEST_F(XnodeMemoryTest, SingleObjectAllocFree) {
SXnodeObj* obj = createXnode(1, "node1:6050", "online");
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->id, 1);
EXPECT_STREQ(obj->url, "node1:6050");
EXPECT_STREQ(obj->status, "online");
freeXnode(obj);
// Leak check in TearDown
}
TEST_F(XnodeMemoryTest, MultipleObjectsAllocFree) {
std::vector<SXnodeObj*> objects;
// Create 100 objects
for (int i = 0; i < 100; i++) {
char url[32];
snprintf(url, sizeof(url), "node%d:6050", i);
SXnodeObj* obj = createXnode(i, url, i % 2 ? "online" : "offline");
objects.push_back(obj);
}
EXPECT_EQ(objects.size(), 100);
// Free all objects
for (auto obj : objects) {
freeXnode(obj);
}
objects.clear();
}
TEST_F(XnodeMemoryTest, NullFieldsHandling) {
// Create object with null fields
SXnodeObj* obj = createXnode(1, nullptr, nullptr);
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->urlLen, 0);
EXPECT_EQ(obj->statusLen, 0);
EXPECT_EQ(obj->url, nullptr);
EXPECT_EQ(obj->status, nullptr);
freeXnode(obj);
}
TEST_F(XnodeMemoryTest, PartialFieldsHandling) {
// Only URL, no status
SXnodeObj* obj1 = createXnode(1, "node1:6050", nullptr);
ASSERT_NE(obj1, nullptr);
EXPECT_NE(obj1->url, nullptr);
EXPECT_EQ(obj1->status, nullptr);
freeXnode(obj1);
// Only status, no URL
SXnodeObj* obj2 = createXnode(2, nullptr, "online");
ASSERT_NE(obj2, nullptr);
EXPECT_EQ(obj2->url, nullptr);
EXPECT_NE(obj2->status, nullptr);
freeXnode(obj2);
}
TEST_F(XnodeMemoryTest, TaskObjectMemory) {
SXnodeTaskObj* task = createTask(100, "test_task", 1);
ASSERT_NE(task, nullptr);
EXPECT_EQ(task->id, 100);
EXPECT_STREQ(task->name, "test_task");
EXPECT_EQ(task->xnodeId, 1);
// Add options
const char* opts = "key1=value1,key2=value2";
// task->optionsLen = strlen(opts) + 1;
// task->options = (char*)taosMemMalloc(task->optionsLen);
allocCount++;
// strcpy(task->options, opts);
// EXPECT_STREQ(task->options, opts);
freeTask(task);
}
TEST_F(XnodeMemoryTest, MultipleTasksMemory) {
std::vector<SXnodeTaskObj*> tasks;
for (int i = 0; i < 50; i++) {
char name[64];
snprintf(name, sizeof(name), "task_%d", i);
SXnodeTaskObj* task = createTask(i, name, i % 5);
tasks.push_back(task);
}
EXPECT_EQ(tasks.size(), 50);
// Free all tasks
for (auto task : tasks) {
freeTask(task);
}
tasks.clear();
}
TEST_F(XnodeMemoryTest, LargeStringFields) {
// Create object with large strings
char largeUrl[1024];
char largeStatus[512];
memset(largeUrl, 'A', sizeof(largeUrl) - 1);
largeUrl[sizeof(largeUrl) - 1] = '\0';
memset(largeStatus, 'S', sizeof(largeStatus) - 1);
largeStatus[sizeof(largeStatus) - 1] = '\0';
SXnodeObj* obj = createXnode(1, largeUrl, largeStatus);
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->urlLen, sizeof(largeUrl));
EXPECT_EQ(obj->statusLen, sizeof(largeStatus));
EXPECT_STREQ(obj->url, largeUrl);
EXPECT_STREQ(obj->status, largeStatus);
freeXnode(obj);
}
TEST_F(XnodeMemoryTest, ZeroLengthStrings) {
// Empty strings (not null)
SXnodeObj* obj = createXnode(1, "", "");
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->urlLen, 1); // '\0' only
EXPECT_EQ(obj->statusLen, 1);
EXPECT_STREQ(obj->url, "");
EXPECT_STREQ(obj->status, "");
freeXnode(obj);
}
TEST_F(XnodeMemoryTest, RapidAllocFree) {
// Test rapid allocation and deallocation
for (int i = 0; i < 1000; i++) {
SXnodeObj* obj = createXnode(i, "node:6050", "online");
ASSERT_NE(obj, nullptr);
freeXnode(obj);
}
// All should be properly freed (checked in TearDown)
}
TEST_F(XnodeMemoryTest, MixedObjectTypes) {
// Mix XNode and Task objects
SXnodeObj* xnode = createXnode(1, "node1:6050", "online");
SXnodeTaskObj* task1 = createTask(100, "task1", 1);
SXnodeTaskObj* task2 = createTask(101, "task2", 1);
SXnodeObj* xnode2 = createXnode(2, "node2:6051", "offline");
ASSERT_NE(xnode, nullptr);
ASSERT_NE(task1, nullptr);
ASSERT_NE(task2, nullptr);
ASSERT_NE(xnode2, nullptr);
// Free in different order
freeTask(task1);
freeXnode(xnode);
freeTask(task2);
freeXnode(xnode2);
}
TEST_F(XnodeMemoryTest, DoubleFreePrevention) {
SXnodeObj* obj = createXnode(1, "node1:6050", "online");
ASSERT_NE(obj, nullptr);
freeXnode(obj);
// Second free with nullptr should be safe
freeXnode(nullptr);
// This is just to demonstrate safe null handling
// In real code, we should not double-free
}
TEST_F(XnodeMemoryTest, StressTest) {
// Stress test: create and free many objects in random order
std::vector<SXnodeObj*> objects;
// Create 500 objects
for (int i = 0; i < 500; i++) {
char url[32];
snprintf(url, sizeof(url), "node%d:%d", i, 6050 + (i % 10));
SXnodeObj* obj = createXnode(i, url, i % 3 ? "online" : "offline");
objects.push_back(obj);
}
// Free every other object
for (size_t i = 0; i < objects.size(); i += 2) {
freeXnode(objects[i]);
objects[i] = nullptr;
}
// Free remaining objects
for (auto obj : objects) {
if (obj) {
freeXnode(obj);
}
}
}
TEST_F(XnodeMemoryTest, BoundaryValues) {
// Test with boundary integer values
SXnodeObj* obj = createXnode(INT32_MAX, "max:6050", "status");
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->id, INT32_MAX);
freeXnode(obj);
obj = createXnode(INT32_MIN, "min:6050", "status");
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->id, INT32_MIN);
freeXnode(obj);
obj = createXnode(0, "zero:6050", "status");
ASSERT_NE(obj, nullptr);
EXPECT_EQ(obj->id, 0);
freeXnode(obj);
}