* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
* K-NET is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <rte_lcore.h>
#include "securec.h"
#include "common.h"
#include "mock.h"
#include "knet_config.h"
#include "knet_stk_mp.h"
#define KNET_STK_MP_MAX_WORKER_NUM_TEST 32
#define MALLOC_SUCCESS_MAX_NUM_FAILED 2
#define MALLOC_SUCCESS_MAX_NUM_SUCCESS 100
extern bool g_knetStkMpIsInit;
extern unsigned g_knetStkMpNum;
struct KnetStkMpTest {
void** bufs;
uint32_t count;
uint32_t off;
uint32_t mallocCount;
};
extern struct KnetStkMpTest g_knetStkMps[KNET_STK_MP_MAX_WORKER_NUM_TEST];
static int g_mallocCnt = 0;
static int g_mallocCntMax = 0;
static int g_workerNum = 0;
static const union KNET_CfgValue *KNET_GetCfgMock(enum KNET_ConfKey key)
{
static union KNET_CfgValue cfgVal = {0};
cfgVal.intValue = g_workerNum;
return &cfgVal;
}
static void *StkMpMallocMock(uint32_t size)
{
++g_mallocCnt;
if (g_mallocCnt > g_mallocCntMax) {
return NULL;
}
return calloc(1, size);
}
* @brief KNET_StkMpInit 函数,malloc 失败场景测试
* 测试步骤:
* 1. 置零全局变量 g_knetStkMpIsInit, g_knetStkMpNum 和 g_knetStkMps;
* 2. 打桩 KNET_GetCfg,使其返回的 workerNum 为 2,有预期结果 1;
* 3. 打桩 malloc,使其在第一次调用时失败,有预期结果 1;
* 4. 入参 size 为 1,count 为 10,调用 KNET_StkMpInit,有预期结果 2;
* 5. 打桩 malloc,使其在第三次调用时(创建第二个 mp 时)失败,有预期结果 1;
* 6. 入参 size 为 1,count 为 10,调用 KNET_StkMpInit,有预期结果 2;
* 预期结果:
* 1. 打桩成功;
* 2. 返回失败,全局变量仍为 0,无内存泄漏;
*/
DTEST_CASE_F(STK_MP, TEST_STK_MP_INIT_MALLOC_FAILED, NULL, NULL)
{
g_knetStkMpIsInit = false;
g_knetStkMpNum = 0;
(void)memset_s(g_knetStkMps, sizeof(g_knetStkMps), 0, sizeof(g_knetStkMps));
uint32_t ret;
uint32_t size = 1;
uint32_t count = 10;
KTestMock *Mock = CreateMock();
DT_ASSERT_NOT_EQUAL(Mock, NULL);
g_workerNum = 2;
Mock->Create(KNET_GetCfg, KNET_GetCfgMock);
g_mallocCntMax = 0;
g_mallocCnt = 0;
Mock->Create(malloc, StkMpMallocMock);
ret = KNET_StkMpInit(size, count);
DT_ASSERT_EQUAL(ret, KNET_ERROR);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
for (uint32_t i = 0; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
g_mallocCntMax = MALLOC_SUCCESS_MAX_NUM_FAILED;
g_mallocCnt = 0;
ret = KNET_StkMpInit(size, count);
DT_ASSERT_EQUAL(ret, KNET_ERROR);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
for (uint32_t i = 0; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
Mock->Delete(KNET_GetCfg);
Mock->Delete(malloc);
DeleteMock(Mock);
}
* @brief KNET_StkMpInit 函数,成功场景,然后调用 KNET_StkMpDeInit 释放内存池
* 测试步骤:
* 1. 置零全局变量 g_knetStkMpIsInit, g_knetStkMpNum 和 g_knetStkMps;
* 2. 未初始化内存池前,调用 KNET_StkMpDeInit 函数,有预期结果 1;
* 2. 打桩 KNET_GetCfg,使其返回的 workerNum 为 2,有预期结果 2;
* 3. 打桩 malloc,使其在调用过程中不失败,有预期结果 2;
* 4. 入参 size 为 1,count 为 10,调用 KNET_StkMpInit,有预期结果 3;
* 5. 调用 KNET_StkMpDeInit,有预期结果 1;
* 预期结果:
* 1. 返回成功,全局变量为 0,无内存泄漏;
* 2. 打桩成功;
* 3. 返回成功,全局变量不再为 0;
*/
DTEST_CASE_F(STK_MP, TEST_STK_MP_INIT_NORMAL, NULL, NULL)
{
g_knetStkMpIsInit = false;
g_knetStkMpNum = 0;
(void)memset_s(g_knetStkMps, sizeof(g_knetStkMps), 0, sizeof(g_knetStkMps));
uint32_t ret;
uint32_t size = 1;
uint32_t count = 10;
ret = KNET_StkMpDeInit();
DT_ASSERT_EQUAL(ret, KNET_OK);
KTestMock *Mock = CreateMock();
DT_ASSERT_NOT_EQUAL(Mock, NULL);
g_workerNum = 2;
Mock->Create(KNET_GetCfg, KNET_GetCfgMock);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
g_mallocCntMax = MALLOC_SUCCESS_MAX_NUM_SUCCESS;
g_mallocCnt = 0;
Mock->Create(malloc, StkMpMallocMock);
ret = KNET_StkMpInit(size, count);
DT_ASSERT_EQUAL(ret, KNET_OK);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, true);
DT_ASSERT_EQUAL(g_knetStkMpNum, g_workerNum);
for (uint32_t i = 0; i < g_workerNum; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, count);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, count);
DT_ASSERT_NOT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
for (uint32_t i = g_workerNum; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
ret = KNET_StkMpDeInit();
DT_ASSERT_EQUAL(ret, KNET_OK);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
for (uint32_t i = 0; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
Mock->Delete(KNET_GetCfg);
Mock->Delete(malloc);
DeleteMock(Mock);
}
extern __thread unsigned int per_lcore__lcore_id;
* @brief KNET_StkMpAlloc 函数测试
* 测试步骤:
* 1. 置零全局变量 g_knetStkMpIsInit, g_knetStkMpNum 和 g_knetStkMps;
* 2. 未初始化内存池前,调用 KNET_StkMpAlloc 函数,有预期结果 1;
* 3. 打桩 KNET_GetCfg,使其返回的 workerNum 为 2,有预期结果 2;
* 4. 打桩 malloc,使其在调用过程中不失败,有预期结果 2;
* 5. 入参 size 为 1,count 为 1,调用 KNET_StkMpInit,有预期结果 3;
* 6. 调用 KNET_StkMpAlloc,有预期结果 4;
* 7. 再一次调用 KNET_StkMpAlloc,有预期结果 1;
* 8. 调用 KNET_StkMpDeInit,有预期结果 5;
* 9. 调用 KNET_StkMpFree 释放 6 申请的内存,有预期结果 6;
* 10. 调用 KNET_StkMpDeInit,有预期结果 7;
* 预期结果:
* 1. 内存申请失败,返回空指针;
* 2. 打桩成功;
* 3. 返回成功,全局变量不再为 0;
* 4. 内存申请成功;
* 5. 返回失败,全局变量不为 0;
* 6. 释放成功;
* 7. 返回成功,全局变量为 0,无内存泄漏;
*/
DTEST_CASE_F(STK_MP, TEST_STK_MP_ALLOC_NORMAL, NULL, NULL)
{
g_knetStkMpIsInit = false;
g_knetStkMpNum = 0;
(void)memset_s(g_knetStkMps, sizeof(g_knetStkMps), 0, sizeof(g_knetStkMps));
uint32_t ret;
uint32_t size = 1;
uint32_t count = 1;
void *bufs[2] = {0};
KTestMock *Mock = CreateMock();
DT_ASSERT_NOT_EQUAL(Mock, NULL);
per_lcore__lcore_id = 0;
bufs[0] = KNET_StkMpAlloc();
DT_ASSERT_EQUAL(bufs[0], NULL);
g_workerNum = 2;
Mock->Create(KNET_GetCfg, KNET_GetCfgMock);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
g_mallocCntMax = MALLOC_SUCCESS_MAX_NUM_SUCCESS;
g_mallocCnt = 0;
Mock->Create(malloc, StkMpMallocMock);
ret = KNET_StkMpInit(size, count);
DT_ASSERT_EQUAL(ret, KNET_OK);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, true);
DT_ASSERT_EQUAL(g_knetStkMpNum, g_workerNum);
for (uint32_t i = 0; i < g_workerNum; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, count);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, count);
DT_ASSERT_NOT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
for (uint32_t i = g_workerNum; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
bufs[0] = KNET_StkMpAlloc();
DT_ASSERT_NOT_EQUAL(bufs[0], NULL);
bufs[1] = KNET_StkMpAlloc();
DT_ASSERT_EQUAL(bufs[1], NULL);
ret = KNET_StkMpDeInit();
DT_ASSERT_EQUAL(ret, KNET_ERROR);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, true);
DT_ASSERT_EQUAL(g_knetStkMpNum, g_workerNum);
KNET_StkMpFree(bufs[0]);
ret = KNET_StkMpDeInit();
DT_ASSERT_EQUAL(ret, KNET_OK);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
for (uint32_t i = 0; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
Mock->Delete(KNET_GetCfg);
Mock->Delete(malloc);
DeleteMock(Mock);
}
* @brief KNET_StkMpFree 函数测试,释放失败场景
* 测试步骤:
* 1. 置零全局变量 g_knetStkMpIsInit, g_knetStkMpNum 和 g_knetStkMps;
* 2. 未初始化内存池前,调用 KNET_StkMpFree 函数,有预期结果 1;
* 3. 打桩 KNET_GetCfg,使其返回的 workerNum 为 2,有预期结果 2;
* 4. 打桩 malloc,使其在调用过程中不失败,有预期结果 2;
* 5. 入参 size 为 1,count 为 1,调用 KNET_StkMpInit,有预期结果 3;
* 6. 调用 KNET_StkMpFree,有预期结果 1;
* 7. 调用 KNET_StkMpDeInit,有预期结果 4;
* 预期结果:
* 1. 释放失败,内存池无变化;
* 2. 打桩成功;
* 3. 返回成功,全局变量不再为 0;
* 4. 返回成功,全局变量为 0,无内存泄漏;
*/
DTEST_CASE_F(STK_MP, TEST_STK_MP_FREE_FAILED, NULL, NULL)
{
g_knetStkMpIsInit = false;
g_knetStkMpNum = 0;
(void)memset_s(g_knetStkMps, sizeof(g_knetStkMps), 0, sizeof(g_knetStkMps));
uint32_t ret;
uint32_t size = 1;
uint32_t count = 1;
void *buf = malloc(1);
DT_ASSERT_NOT_EQUAL(buf, NULL);
KTestMock *Mock = CreateMock();
DT_ASSERT_NOT_EQUAL(Mock, NULL);
per_lcore__lcore_id = 0;
uint32_t preOff = g_knetStkMps[0].off;
KNET_StkMpFree(buf);
DT_ASSERT_EQUAL(g_knetStkMps[0].off, g_knetStkMps[0].off);
g_workerNum = 2;
Mock->Create(KNET_GetCfg, KNET_GetCfgMock);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
g_mallocCntMax = MALLOC_SUCCESS_MAX_NUM_SUCCESS;
g_mallocCnt = 0;
Mock->Create(malloc, StkMpMallocMock);
ret = KNET_StkMpInit(size, count);
DT_ASSERT_EQUAL(ret, KNET_OK);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, true);
DT_ASSERT_EQUAL(g_knetStkMpNum, g_workerNum);
for (uint32_t i = 0; i < g_workerNum; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, count);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, count);
DT_ASSERT_NOT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
for (uint32_t i = g_workerNum; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
preOff = g_knetStkMps[0].off;
KNET_StkMpFree(buf);
DT_ASSERT_EQUAL(g_knetStkMps[0].off, g_knetStkMps[0].off);
ret = KNET_StkMpDeInit();
DT_ASSERT_EQUAL(ret, KNET_OK);
DT_ASSERT_EQUAL(g_knetStkMpIsInit, false);
DT_ASSERT_EQUAL(g_knetStkMpNum, 0);
for (uint32_t i = 0; i < KNET_STK_MP_MAX_WORKER_NUM_TEST; ++i) {
DT_ASSERT_EQUAL(g_knetStkMps[i].count, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].off, 0);
DT_ASSERT_EQUAL(g_knetStkMps[i].bufs, NULL);
}
free(buf);
Mock->Delete(KNET_GetCfg);
Mock->Delete(malloc);
DeleteMock(Mock);
}