From 0cd088174c94c56ee86506dab9a6a33f6e8fdaa4 Mon Sep 17 00:00:00 2001
From: liuxu <liuxu156@huawei.com>
Date: Thu, 25 Apr 2024 10:52:20 +0800
Subject: [PATCH 76/78] cdi:add UT

Signed-off-by: liuxu <liuxu156@huawei.com>
---
 CI/make-and-install.sh                        |   4 +-
 test/cutils/CMakeLists.txt                    |   1 +
 test/cutils/utils_array/utils_array_ut.cc     | 128 ++++++++++
 test/cutils/utils_utils/utils_utils_ut.cc     |  12 +
 test/cutils/utils_version/CMakeLists.txt      |  17 ++
 test/cutils/utils_version/utils_version_ut.cc |  71 ++++++
 .../image/oci/oci_config_merge/CMakeLists.txt |   1 +
 test/mocks/cdi_operate_api_mock.cc            |  58 +++++
 test/mocks/cdi_operate_api_mock.h             |  35 +++
 test/sandbox/controller/shim/CMakeLists.txt   |   1 +
 test/specs/specs/CMakeLists.txt               |   1 +
 test/specs/specs/specs_ut.cc                  | 241 ++++++++++++++++++
 test/specs/specs_extend/CMakeLists.txt        |   1 +
 13 files changed, 569 insertions(+), 2 deletions(-)
 create mode 100644 test/cutils/utils_version/CMakeLists.txt
 create mode 100644 test/cutils/utils_version/utils_version_ut.cc
 create mode 100644 test/mocks/cdi_operate_api_mock.cc
 create mode 100644 test/mocks/cdi_operate_api_mock.h

diff --git a/CI/make-and-install.sh b/CI/make-and-install.sh
index 9bb984cd..9d4c5533 100755
--- a/CI/make-and-install.sh
+++ b/CI/make-and-install.sh
@@ -72,7 +72,7 @@ cd $ISULAD_COPY_PATH
 sed -i 's/fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO/fd == 0 || fd == 1 || fd == 2 || fd >= 1000/g' ./src/utils/cutils/utils.c
 rm -rf build
 mkdir build && cd build
-cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UT=ON -DENABLE_CRI_API_V1=ON -DENABLE_SHIM_V2=ON -DENABLE_METRICS=ON ..
+cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UT=ON -DENABLE_CRI_API_V1=ON -DENABLE_CDI=ON -DENABLE_SHIM_V2=ON -DENABLE_METRICS=ON ..
 make -j $(nproc)
 make install
 ctest -E "driver_devmapper_ut" -T memcheck --output-on-failure
@@ -103,7 +103,7 @@ rm -rf build
 mkdir build
 cd build
 if [[ ${enable_gcov} -ne 0 ]]; then
-  cmake -DLIB_INSTALL_DIR=${builddir}/lib -DCMAKE_INSTALL_PREFIX=${builddir} -DCMAKE_INSTALL_SYSCONFDIR=${builddir}/etc -DCMAKE_BUILD_TYPE=Debug -DGCOV=ON -DENABLE_EMBEDDED=ON -DENABLE_COVERAGE=ON -DENABLE_CRI_API_V1=ON -DENABLE_UT=ON -DENABLE_METRICS=ON -DENABLE_REMOTE_LAYER_STORE=ON -DENABLE_GRPC_REMOTE_CONNECT=ON ..
+  cmake -DLIB_INSTALL_DIR=${builddir}/lib -DCMAKE_INSTALL_PREFIX=${builddir} -DCMAKE_INSTALL_SYSCONFDIR=${builddir}/etc -DCMAKE_BUILD_TYPE=Debug -DGCOV=ON -DENABLE_EMBEDDED=ON -DENABLE_COVERAGE=ON -DENABLE_CRI_API_V1=ON -DENABLE_CDI=ON -DENABLE_UT=ON -DENABLE_METRICS=ON -DENABLE_REMOTE_LAYER_STORE=ON -DENABLE_GRPC_REMOTE_CONNECT=ON ..
 else
   cmake -DLIB_INSTALL_DIR=${builddir}/lib -DCMAKE_INSTALL_PREFIX=${builddir} -DCMAKE_INSTALL_SYSCONFDIR=${builddir}/etc -DENABLE_EMBEDDED=ON -DENABLE_METRICS=ON -DENABLE_REMOTE_LAYER_STORE=ON -DENABLE_CRI_API_V1=ON -DENABLE_GRPC_REMOTE_CONNECT=ON ..
 fi
diff --git a/test/cutils/CMakeLists.txt b/test/cutils/CMakeLists.txt
index 9e681cc9..bd9def02 100644
--- a/test/cutils/CMakeLists.txt
+++ b/test/cutils/CMakeLists.txt
@@ -34,4 +34,5 @@ add_subdirectory(utils_utils)
 add_subdirectory(utils_verify)
 add_subdirectory(utils_network)
 add_subdirectory(utils_transform)
+add_subdirectory(utils_version)
 add_subdirectory(map)
diff --git a/test/cutils/utils_array/utils_array_ut.cc b/test/cutils/utils_array/utils_array_ut.cc
index 7bd13c25..6c6e76fa 100644
--- a/test/cutils/utils_array/utils_array_ut.cc
+++ b/test/cutils/utils_array/utils_array_ut.cc
@@ -50,6 +50,33 @@ TEST(utils_array, test_util_free_array)
     util_free_array(array);
 }
 
+TEST(utils_array, test_util_copy_array_by_len)
+{
+    char **array = nullptr;
+    char **array_copy = nullptr;
+    size_t len = 3;
+
+    array = (char **)util_common_calloc_s(4 * sizeof(char *));
+    ASSERT_NE(array, nullptr);
+    array[0] = util_strdup_s("test1");
+    array[1] = util_strdup_s("test2");
+    array[2] = util_strdup_s("test3");
+
+    array_copy = util_copy_array_by_len(array, len);
+    ASSERT_NE(array_copy, nullptr);
+    for (size_t i = 0; i < len; i++) {
+        ASSERT_EQ(strcmp(array_copy[i], array[i]), 0);
+        free(array[i]);
+        free(array_copy[i]);
+    }
+
+    ASSERT_EQ(util_copy_array_by_len(array, 0), nullptr);
+    ASSERT_EQ(util_copy_array_by_len(nullptr, len), nullptr);
+
+    free(array);
+    free(array_copy);
+}
+
 TEST(utils_array, test_util_grow_array)
 {
     char **array = nullptr;
@@ -229,6 +256,34 @@ TEST(utils_array, test_util_append_string_array)
     sarray = nullptr;
 }
 
+TEST(utils_array, test_util_copy_string_array)
+{
+    __isula_auto_string_array_t string_array *sarray_copy = nullptr;
+    __isula_auto_string_array_t string_array *sarray = (string_array *)util_common_calloc_s(sizeof(string_array));
+    ASSERT_NE(sarray, nullptr);
+    int ret;
+
+    ret = util_append_string_array(sarray, "1234567890");
+    ASSERT_EQ(ret, 0);
+    ret = util_append_string_array(sarray, "abc");
+    ASSERT_EQ(ret, 0);
+    ret = util_append_string_array(sarray, "bcd");
+    ASSERT_EQ(ret, 0);
+    ASSERT_EQ(sarray->len, 3);
+
+    sarray_copy = util_copy_string_array(sarray);
+    ASSERT_NE(sarray_copy, nullptr);
+    ASSERT_EQ(sarray_copy->len, sarray->len);
+    for (size_t i = 0; i < sarray_copy->len; i++) {
+        ASSERT_EQ(strcmp(sarray_copy->items[i], sarray->items[i]), 0);
+    }
+
+    ASSERT_EQ(util_copy_string_array(nullptr), nullptr);
+    sarray->cap = 0;
+    ASSERT_EQ(util_copy_string_array(sarray), nullptr);
+    sarray->cap = sarray->len;
+}
+
 TEST(utils_array, test_util_string_array_contain)
 {
     string_array *sarray = (string_array *)util_common_calloc_s(sizeof(string_array));
@@ -299,3 +354,76 @@ TEST(utils_array, test_util_common_array_append_pointer)
     delete element1;
     delete element2;
 }
+
+static void common_array_free_mock(void *ptr)
+{
+    (void)ptr;
+    return;
+}
+
+TEST(utils_array, test_util_append_common_array)
+{
+    __isula_auto_common_array_t common_array *carray = nullptr;
+    int ret;
+    int value1 = 1;
+    int value2 = 2;
+    int value3 = 3;
+
+    carray = util_common_array_new(1, common_array_free_mock, util_clone_ptr);
+    ASSERT_NE(carray, nullptr);
+
+    ret = util_append_common_array(carray, &value1);
+    ASSERT_EQ(ret, 0);
+    ASSERT_EQ(carray->items[0], &value1);
+    ASSERT_EQ(carray->len, 1);
+
+    ret = util_append_common_array(carray, &value2);
+    ASSERT_EQ(ret, 0);
+    ret = util_append_common_array(carray, &value3);
+    ASSERT_EQ(ret, 0);
+    ASSERT_EQ(carray->items[1], &value2);
+    ASSERT_EQ(carray->items[2], &value3);
+    ASSERT_EQ(carray->len, 3);
+
+    carray->clone_item_cb = nullptr;
+    ASSERT_EQ(util_append_common_array(carray, &value1), -1);
+    carray->clone_item_cb = util_clone_ptr;
+    ASSERT_EQ(util_append_common_array(carray, nullptr), 0);
+}
+
+TEST(utils_array, test_util_merge_common_array)
+{
+    __isula_auto_common_array_t common_array *carray1 = nullptr;
+    __isula_auto_common_array_t common_array *carray2 = nullptr;
+    int ret;
+    int value1 = 1;
+    int value2 = 2;
+
+    carray1 = util_common_array_new(1, common_array_free_mock, util_clone_ptr);
+    ASSERT_NE(carray1, nullptr);
+    carray2 = util_common_array_new(1, common_array_free_mock, util_clone_ptr);
+    ASSERT_NE(carray2, nullptr);
+
+    ret = util_append_common_array(carray1, &value1);
+    ASSERT_EQ(ret, 0);
+    ASSERT_EQ(carray1->items[0], &value1);
+    ASSERT_EQ(carray1->len, 1);
+    ret = util_append_common_array(carray2, &value2);
+    ASSERT_EQ(ret, 0);
+    ASSERT_EQ(carray2->items[0], &value2);
+    ASSERT_EQ(carray2->len, 1);
+
+    ret = util_merge_common_array(carray1, carray2);
+    ASSERT_EQ(ret, 0);
+    ASSERT_EQ(carray1->items[1], &value2);
+    ASSERT_EQ(carray1->len, 2);
+
+    ASSERT_EQ(util_merge_common_array(nullptr, carray2), -1);
+    ASSERT_EQ(util_merge_common_array(carray1, nullptr), -1);
+    carray1->clone_item_cb = nullptr;
+    ASSERT_EQ(util_merge_common_array(carray1, carray2), -1);
+    carray1->clone_item_cb = util_clone_ptr;
+    carray2->clone_item_cb = nullptr;
+    ASSERT_EQ(util_merge_common_array(carray1, carray2), -1);
+    carray2->clone_item_cb = util_clone_ptr;
+}
\ No newline at end of file
diff --git a/test/cutils/utils_utils/utils_utils_ut.cc b/test/cutils/utils_utils/utils_utils_ut.cc
index 0720d1b1..a61e5a21 100644
--- a/test/cutils/utils_utils/utils_utils_ut.cc
+++ b/test/cutils/utils_utils/utils_utils_ut.cc
@@ -54,6 +54,18 @@ static int status_to_exit_code(int status)
     return exit_code;
 }
 
+TEST(utils_utils, test_util_swap_ptr)
+{
+    int val1 = 1;
+    int val2 = 2;
+    int *ptr1 = &val1;
+    int *ptr2 = &val2;
+
+    util_swap_ptr((void **)&ptr1, (void **)&ptr2);
+    ASSERT_EQ(*ptr1, val2);
+    ASSERT_EQ(*ptr2, val1);
+}
+
 TEST(utils_utils, test_util_mem_realloc)
 {
     char *old = nullptr;
diff --git a/test/cutils/utils_version/CMakeLists.txt b/test/cutils/utils_version/CMakeLists.txt
new file mode 100644
index 00000000..1ada8e93
--- /dev/null
+++ b/test/cutils/utils_version/CMakeLists.txt
@@ -0,0 +1,17 @@
+project(iSulad_UT)
+
+SET(EXE utils_version_ut)
+
+add_executable(${EXE}
+    utils_version_ut.cc)
+
+target_include_directories(${EXE} PUBLIC
+    ${GTEST_INCLUDE_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../include
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/common
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils
+    )
+
+target_link_libraries(${EXE} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${ISULA_LIBUTILS_LIBRARY} libutils_ut -lcrypto -lyajl -lz)
+add_test(NAME ${EXE} COMMAND ${EXE} --gtest_output=xml:${EXE}-Results.xml)
+set_tests_properties(${EXE} PROPERTIES TIMEOUT 120)
diff --git a/test/cutils/utils_version/utils_version_ut.cc b/test/cutils/utils_version/utils_version_ut.cc
new file mode 100644
index 00000000..d1fc0932
--- /dev/null
+++ b/test/cutils/utils_version/utils_version_ut.cc
@@ -0,0 +1,71 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad 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.
+ * Author: liuxu
+ * Create: 2024-04-25
+ * Description: utils version unit test
+ *******************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <climits>
+#include <gtest/gtest.h>
+#include "mock.h"
+#include "utils_version.h"
+#include "utils.h"
+
+TEST(utils_version, test_util_version_compare)
+{
+    const char *version1 = "1.1.1";
+    const char *version2 = "1.1.2";
+    int diff_value = 0;
+
+    ASSERT_EQ(util_version_compare(version1, version2, &diff_value), 0);
+    ASSERT_TRUE(diff_value < 0);
+    ASSERT_EQ(util_version_compare(version1, version1, &diff_value), 0);
+    ASSERT_TRUE(diff_value == 0);
+    ASSERT_EQ(util_version_compare(version2, version1, &diff_value), 0);
+    ASSERT_TRUE(diff_value > 0);
+
+    ASSERT_EQ(util_version_compare(version1, nullptr, &diff_value), -1);
+    ASSERT_EQ(util_version_compare(nullptr, version2, &diff_value), -1);
+    ASSERT_EQ(util_version_compare(version1, version2, nullptr), -1);
+    ASSERT_EQ(util_version_compare("1.1.1.1", version2, nullptr), -1);
+    ASSERT_EQ(util_version_compare(version1, "a.b.1.1", nullptr), -1);
+}
+
+TEST(utils_version, test_util_version_greater_than)
+{
+    const char *version1 = "0.6.0";
+    const char *version2 = "1.0.0";
+    bool result = true;
+
+    ASSERT_EQ(util_version_greater_than(version1, version2, &result), 0);
+    ASSERT_FALSE(result);
+    ASSERT_EQ(util_version_greater_than(version1, version1, &result), 0);
+    ASSERT_FALSE(result);
+    ASSERT_EQ(util_version_greater_than(version2, version1, &result), 0);
+    ASSERT_TRUE(result);
+}
+
+TEST(utils_version, test_util_version_greater_than_or_equal_to)
+{
+    const char *version1 = "0.6.0";
+    const char *version2 = "1.0.0";
+    bool result = true;
+
+    ASSERT_EQ(util_version_greater_than_or_equal_to(version1, version2, &result), 0);
+    ASSERT_FALSE(result);
+    ASSERT_EQ(util_version_greater_than_or_equal_to(version1, version1, &result), 0);
+    ASSERT_TRUE(result);
+    ASSERT_EQ(util_version_greater_than_or_equal_to(version2, version1, &result), 0);
+    ASSERT_TRUE(result);
+}
+
diff --git a/test/image/oci/oci_config_merge/CMakeLists.txt b/test/image/oci/oci_config_merge/CMakeLists.txt
index ffd3999d..38ade4ec 100644
--- a/test/image/oci/oci_config_merge/CMakeLists.txt
+++ b/test/image/oci/oci_config_merge/CMakeLists.txt
@@ -47,6 +47,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/selinux_label_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/storage_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/image_mock.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/cdi_operate_api_mock.cc
     oci_config_merge_ut.cc)
 
 target_include_directories(${EXE} PUBLIC
diff --git a/test/mocks/cdi_operate_api_mock.cc b/test/mocks/cdi_operate_api_mock.cc
new file mode 100644
index 00000000..d8f9f9d4
--- /dev/null
+++ b/test/mocks/cdi_operate_api_mock.cc
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
+ * iSulad 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.
+ * Author: jikai
+ * Create: 2023-10-20
+ * Description: provide image api mock
+ ******************************************************************************/
+
+#include "cdi_operate_api_mock.h"
+
+namespace {
+std::shared_ptr<MockCdiOperateApi> g_cdi_operate_api_mock = nullptr;
+}
+
+void MockCdiOperateApi_SetMock(std::shared_ptr<MockCdiOperateApi> mock)
+{
+    g_cdi_operate_api_mock = mock;
+}
+
+int cdi_operate_registry_init(char **specs_dirs, size_t specs_dirs_len)
+{
+    if (g_cdi_operate_api_mock != nullptr) {
+        return g_cdi_operate_api_mock->CdiOperateRegistryInit(specs_dirs, specs_dirs_len);
+    }
+    return 0;
+}
+
+int cdi_operate_refresh(void)
+{
+    if (g_cdi_operate_api_mock != nullptr) {
+        return g_cdi_operate_api_mock->CdiOperateRefresh();
+    }
+    return 0;
+}
+
+int cdi_operate_inject_devices(oci_runtime_spec *spec, string_array *devices)
+{
+    if (g_cdi_operate_api_mock != nullptr) {
+        return g_cdi_operate_api_mock->CdiOperateInjectDevices(spec, devices);
+    }
+    return 0;
+}
+
+int cdi_operate_parse_annotations(json_map_string_string *annotations, string_array **keys,
+                                  string_array **devices, char **error)
+{
+    if (g_cdi_operate_api_mock != nullptr) {
+        return g_cdi_operate_api_mock->CdiOperateParseAnnotations(annotations, keys, devices, error);
+    }
+    return 0;
+}
\ No newline at end of file
diff --git a/test/mocks/cdi_operate_api_mock.h b/test/mocks/cdi_operate_api_mock.h
new file mode 100644
index 00000000..c118ee7a
--- /dev/null
+++ b/test/mocks/cdi_operate_api_mock.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
+ * iSulad 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.
+ * Author: liuxu
+ * Create: 2024-04-24
+ * Description: provide cdi api mock
+ ******************************************************************************/
+
+#ifndef ISULAD_TEST_MOCKS_CDI_OPERATE_API_MOCK_H
+#define ISULAD_TEST_MOCKS_CDI_OPERATE_API_MOCK_H
+
+#include <gmock/gmock.h>
+#include <memory>
+
+#include "cdi_operate_api.h"
+
+class MockCdiOperateApi {
+public:
+    MOCK_METHOD2(CdiOperateRegistryInit, int(char **specs_dirs, size_t specs_dirs_len));
+    MOCK_METHOD0(CdiOperateRefresh, int(void));
+    MOCK_METHOD2(CdiOperateInjectDevices, int(oci_runtime_spec *spec, string_array *devices));
+    MOCK_METHOD4(CdiOperateParseAnnotations, int(json_map_string_string *annotations, string_array **keys,
+                                                 string_array **devices, char **error));
+};
+
+void MockCdiOperateApi_SetMock(std::shared_ptr<MockCdiOperateApi> mock);
+
+#endif
diff --git a/test/sandbox/controller/shim/CMakeLists.txt b/test/sandbox/controller/shim/CMakeLists.txt
index 26a66e51..d18d1861 100644
--- a/test/sandbox/controller/shim/CMakeLists.txt
+++ b/test/sandbox/controller/shim/CMakeLists.txt
@@ -21,6 +21,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/callback_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/image_api_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/service_container_api_mock.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/mocks/cdi_operate_api_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/sandbox/controller/controller_common.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/sandbox/controller/shim/shim_controller_ut.cc
 )
diff --git a/test/specs/specs/CMakeLists.txt b/test/specs/specs/CMakeLists.txt
index 12c11f51..892d44d7 100644
--- a/test/specs/specs/CMakeLists.txt
+++ b/test/specs/specs/CMakeLists.txt
@@ -44,6 +44,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/storage_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/image_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/sender_mock.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/cdi_operate_api_mock.cc
     specs_ut.cc)
 
 target_include_directories(${EXE} PUBLIC
diff --git a/test/specs/specs/specs_ut.cc b/test/specs/specs/specs_ut.cc
index 6c42216d..47836e5b 100644
--- a/test/specs/specs/specs_ut.cc
+++ b/test/specs/specs/specs_ut.cc
@@ -564,6 +564,247 @@ TEST_F(SpecsUnitTest, test_update_devcies_for_oci_spec)
     free(err);
 }
 
+#ifdef ENABLE_CDI
+TEST_F(SpecsUnitTest, test_defs_process_add_multiple_env)
+{
+    size_t env_len = 2;
+    char **envs = (char **)util_common_calloc_s(sizeof(char *) * env_len);
+    ASSERT_NE(envs, nullptr);
+    defs_process *dp = (defs_process *)util_common_calloc_s(sizeof(defs_process));
+    ASSERT_NE(dp, nullptr);
+    dp->env_len = 1;
+    dp->env = (char **)util_common_calloc_s(sizeof(char *) * dp->env_len);
+    ASSERT_NE(dp->env, nullptr);
+    
+    envs[0] = util_strdup_s("key0=value0");
+    envs[1] = util_strdup_s("key1=value1");
+    dp->env[0] = util_strdup_s("key0=value0_old");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), 0);
+    ASSERT_EQ(dp->env_len, 2);
+    ASSERT_EQ(strcmp(dp->env[0], envs[0]), 0);
+    ASSERT_EQ(strcmp(dp->env[1], envs[1]), 0);
+
+    ASSERT_EQ(defs_process_add_multiple_env(dp, nullptr, env_len), 0);
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, 0), 0);
+    ASSERT_EQ(defs_process_add_multiple_env(nullptr, (const char **)envs, env_len), -1);
+
+    free(envs[0]);
+    envs[0] = util_strdup_s("=value0");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), -1);
+    free(envs[0]);
+    envs[0] = util_strdup_s("key0=");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), -1);
+    free(envs[0]);
+    envs[0] = util_strdup_s("key0xxxx");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), -1);
+
+    free(dp->env[0]);
+    dp->env[0] = util_strdup_s("=value0");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), -1);
+    free(dp->env[0]);
+    dp->env[0] = util_strdup_s("key0=");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), -1);
+    free(dp->env[0]);
+    dp->env[0] = util_strdup_s("key0xxxx");
+    ASSERT_EQ(defs_process_add_multiple_env(dp, (const char **)envs, env_len), -1);
+
+    free_defs_process(dp);
+    free(envs[0]);
+    free(envs[1]);
+    free(envs);
+}
+
+TEST_F(SpecsUnitTest, test_spec_add_multiple_process_env)
+{
+    size_t env_len = 2;
+    char **envs = (char **)util_common_calloc_s(sizeof(char *) * env_len);
+    ASSERT_NE(envs, nullptr);
+    oci_runtime_spec *oci_spec = (oci_runtime_spec *)util_common_calloc_s(sizeof(oci_runtime_spec));
+    ASSERT_NE(oci_spec, nullptr);
+    oci_spec->process = (defs_process *)util_common_calloc_s(sizeof(defs_process));
+    ASSERT_NE(oci_spec->process, nullptr);
+    oci_spec->process->env_len = 1;
+    oci_spec->process->env = (char **)util_common_calloc_s(sizeof(char *) * oci_spec->process->env_len);
+    ASSERT_NE(oci_spec->process->env, nullptr);
+    
+    envs[0] = util_strdup_s("key0=value0");
+    envs[1] = util_strdup_s("key1=value1");
+    oci_spec->process->env[0] = util_strdup_s("key0=value0_old");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), 0);
+    ASSERT_EQ(oci_spec->process->env_len, 2);
+    ASSERT_EQ(strcmp(oci_spec->process->env[0], envs[0]), 0);
+    ASSERT_EQ(strcmp(oci_spec->process->env[1], envs[1]), 0);
+
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, nullptr, env_len), 0);
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, 0), 0);
+    ASSERT_EQ(spec_add_multiple_process_env(nullptr, (const char **)envs, env_len), -1);
+
+    free(envs[0]);
+    envs[0] = util_strdup_s("=value0");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), -1);
+    free(envs[0]);
+    envs[0] = util_strdup_s("key0=");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), -1);
+    free(envs[0]);
+    envs[0] = util_strdup_s("key0xxxx");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), -1);
+
+    free(oci_spec->process->env[0]);
+    oci_spec->process->env[0] = util_strdup_s("=value0");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), -1);
+    free(oci_spec->process->env[0]);
+    oci_spec->process->env[0] = util_strdup_s("key0=");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), -1);
+    free(oci_spec->process->env[0]);
+    oci_spec->process->env[0] = util_strdup_s("key0xxxx");
+    ASSERT_EQ(spec_add_multiple_process_env(oci_spec, (const char **)envs, env_len), -1);
+
+    free_oci_runtime_spec(oci_spec);
+    free(envs[0]);
+    free(envs[1]);
+    free(envs);
+}
+
+TEST_F(SpecsUnitTest, test_spec_add_device)
+{
+    defs_device *device = (defs_device *)util_common_calloc_s(sizeof(defs_device));
+    ASSERT_NE(device, nullptr);
+    oci_runtime_spec *oci_spec = (oci_runtime_spec *)util_common_calloc_s(sizeof(oci_runtime_spec));
+    ASSERT_NE(oci_spec, nullptr);
+    oci_spec->linux = (oci_runtime_config_linux *)util_common_calloc_s(sizeof(oci_runtime_config_linux));
+    ASSERT_NE(oci_spec->linux, nullptr);
+    oci_spec->linux->devices_len = 1;
+    oci_spec->linux->devices = (defs_device **)util_common_calloc_s(sizeof(defs_device *) * oci_spec->linux->devices_len);
+    ASSERT_NE(oci_spec->linux->devices, nullptr);
+
+    device->path = util_strdup_s("/device/path");
+    oci_spec->linux->devices[0] = (defs_device *)util_common_calloc_s(sizeof(defs_device));
+    ASSERT_NE(oci_spec->linux->devices[0], nullptr);
+    oci_spec->linux->devices[0]->path = util_strdup_s("/device/path");
+    ASSERT_EQ(spec_add_device(oci_spec, device), 0);
+    ASSERT_EQ(oci_spec->linux->devices[0], device);
+
+    oci_spec->linux->devices[0] = nullptr;
+    oci_spec->linux->devices_len = 0;
+    ASSERT_EQ(spec_add_device(oci_spec, device), 0);
+    ASSERT_EQ(oci_spec->linux->devices_len, 1);
+    ASSERT_EQ(oci_spec->linux->devices[0], device);
+
+    ASSERT_EQ(spec_add_device(oci_spec, nullptr), -1);
+    ASSERT_EQ(spec_add_device(nullptr, device), -1);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST_F(SpecsUnitTest, test_spec_add_linux_resources_device)
+{
+    oci_runtime_spec *oci_spec = (oci_runtime_spec *)util_common_calloc_s(sizeof(oci_runtime_spec));
+    ASSERT_NE(oci_spec, nullptr);
+    oci_spec->linux = (oci_runtime_config_linux *)util_common_calloc_s(sizeof(oci_runtime_config_linux));
+    ASSERT_NE(oci_spec->linux, nullptr);
+    oci_spec->linux->resources = (defs_resources *)util_common_calloc_s(sizeof(defs_resources));
+    ASSERT_NE(oci_spec->linux->resources, nullptr);
+    oci_spec->linux->resources->devices_len = 1;
+    oci_spec->linux->resources->devices = (defs_device_cgroup **)util_common_calloc_s(sizeof(defs_device_cgroup *) * oci_spec->linux->resources->devices_len);
+    ASSERT_NE(oci_spec->linux->resources->devices, nullptr);
+
+    oci_spec->linux->resources->devices[0] = (defs_device_cgroup *)util_common_calloc_s(sizeof(defs_device_cgroup));
+    ASSERT_NE(oci_spec->linux->resources->devices[0], nullptr);
+    ASSERT_EQ(spec_add_linux_resources_device(oci_spec, true, "bind", 10, 9, "rwm"), 0);
+    ASSERT_EQ(oci_spec->linux->resources->devices_len, 2);
+    ASSERT_EQ(oci_spec->linux->resources->devices[1]->allow, true);
+    ASSERT_EQ(strcmp(oci_spec->linux->resources->devices[1]->type, "bind"), 0);
+    ASSERT_EQ(oci_spec->linux->resources->devices[1]->major, 10);
+    ASSERT_EQ(oci_spec->linux->resources->devices[1]->minor, 9);
+    ASSERT_EQ(strcmp(oci_spec->linux->resources->devices[1]->access, "rwm"), 0);
+
+    ASSERT_EQ(spec_add_linux_resources_device(nullptr, true, "bind", 10, 9, "rwm"), -1);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST_F(SpecsUnitTest, test_spec_remove_mount)
+{
+    oci_runtime_spec *oci_spec = (oci_runtime_spec *)util_common_calloc_s(sizeof(oci_runtime_spec));
+    ASSERT_NE(oci_spec, nullptr);
+    oci_spec->mounts_len = 2;
+    oci_spec->mounts = (defs_mount **)util_common_calloc_s(sizeof(defs_mount *) * oci_spec->mounts_len);
+    ASSERT_NE(oci_spec->mounts, nullptr);
+
+    oci_spec->mounts[0] = (defs_mount *)util_common_calloc_s(sizeof(defs_mount));
+    ASSERT_NE(oci_spec->mounts[0], nullptr);
+    oci_spec->mounts[1] = (defs_mount *)util_common_calloc_s(sizeof(defs_mount));
+    ASSERT_NE(oci_spec->mounts[1], nullptr);
+    oci_spec->mounts[0]->destination = util_strdup_s("/mount/path/0");
+    oci_spec->mounts[1]->destination = util_strdup_s("/mount/path/1");
+    spec_remove_mount(oci_spec, oci_spec->mounts[0]->destination);
+    ASSERT_EQ(oci_spec->mounts_len, 1);
+    ASSERT_EQ(strcmp(oci_spec->mounts[0]->destination, "/mount/path/1"), 0);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST_F(SpecsUnitTest, test_spec_add_mount)
+{
+    defs_mount *mnt = (defs_mount *)util_common_calloc_s(sizeof(defs_mount));
+    ASSERT_NE(mnt, nullptr);
+    oci_runtime_spec *oci_spec = (oci_runtime_spec *)util_common_calloc_s(sizeof(oci_runtime_spec));
+    ASSERT_NE(oci_spec, nullptr);
+    oci_spec->mounts_len = 1;
+    oci_spec->mounts = (defs_mount **)util_common_calloc_s(sizeof(defs_mount *) * oci_spec->mounts_len );
+    ASSERT_NE(oci_spec->mounts, nullptr);
+
+    oci_spec->mounts[0] = (defs_mount *)util_common_calloc_s(sizeof(defs_mount));
+    ASSERT_NE(oci_spec->mounts[0], nullptr);
+    oci_spec->mounts[0]->destination = util_strdup_s("/mount/path/0");
+    ASSERT_EQ(spec_add_mount(oci_spec, mnt), 0);
+    ASSERT_EQ(oci_spec->mounts_len, 2);
+    ASSERT_EQ(oci_spec->mounts[1], mnt);
+
+    ASSERT_EQ(spec_add_mount(nullptr, mnt), -1);
+    ASSERT_EQ(spec_add_mount(oci_spec, nullptr), -1);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+#define TEST_SPEC_ADD_HOOKS_ITEM_DEF(hooktype)                                                                                  \
+    void test_spec_add_##hooktype##_hook(void)                                                                                  \
+    {                                                                                                                           \
+        defs_hook *hook = (defs_hook *)util_common_calloc_s(sizeof(defs_hook));                                                 \
+        ASSERT_NE(hook, nullptr);                                                                                               \
+        oci_runtime_spec *oci_spec = (oci_runtime_spec *)util_common_calloc_s(sizeof(oci_runtime_spec));                        \
+        ASSERT_NE(oci_spec, nullptr);                                                                                           \
+        oci_spec->hooks = (oci_runtime_spec_hooks *)util_common_calloc_s(sizeof(oci_runtime_spec_hooks));                        \
+        ASSERT_NE(oci_spec->hooks, nullptr);                                                                                           \
+        oci_spec->hooks->hooktype##_len = 1;                                                                                    \
+        oci_spec->hooks->hooktype = (defs_hook **)util_common_calloc_s(sizeof(defs_hook *) * oci_spec->hooks->hooktype##_len);  \
+        ASSERT_NE(oci_spec->hooks->hooktype, nullptr);                                                                          \
+                                                                                                                                \
+        oci_spec->hooks->hooktype[0] = (defs_hook *)util_common_calloc_s(sizeof(defs_hook));                                    \
+        ASSERT_NE(oci_spec->hooks->hooktype[0], nullptr);                                                                       \
+        ASSERT_EQ(spec_add_##hooktype##_hook(oci_spec, hook), 0);                                                               \
+        ASSERT_EQ(oci_spec->hooks->hooktype##_len, 2);                                                                          \
+        ASSERT_EQ(oci_spec->hooks->hooktype[1], hook);                                                                          \
+                                                                                                                                \
+        ASSERT_EQ(spec_add_##hooktype##_hook(nullptr, hook), -1);                                                               \
+        ASSERT_EQ(spec_add_##hooktype##_hook(oci_spec, nullptr), -1);                                                           \
+                                                                                                                                \
+        free_oci_runtime_spec(oci_spec);                                                                                        \
+    }
+
+TEST_SPEC_ADD_HOOKS_ITEM_DEF(prestart)
+TEST_SPEC_ADD_HOOKS_ITEM_DEF(poststart)
+TEST_SPEC_ADD_HOOKS_ITEM_DEF(poststop)
+
+TEST_F(SpecsUnitTest, test_spec_add_hook)
+{
+    test_spec_add_prestart_hook();
+    test_spec_add_poststart_hook();
+    test_spec_add_poststop_hook();
+}
+
+#endif /* ENABLE_CDI */
+
 /********************************* UT for merge caps *******************************************/
 struct capabilities_lens {
     size_t bounding_len;
diff --git a/test/specs/specs_extend/CMakeLists.txt b/test/specs/specs_extend/CMakeLists.txt
index 2fd37e1c..bd4d2dd6 100644
--- a/test/specs/specs_extend/CMakeLists.txt
+++ b/test/specs/specs_extend/CMakeLists.txt
@@ -44,6 +44,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/storage_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/image_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/sender_mock.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/cdi_operate_api_mock.cc
     specs_extend_ut.cc)
 
 target_include_directories(${EXE} PUBLIC
-- 
2.25.1

