C语言-单元测试(自研)

前沿

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

在网上找了找C语言都没有类似java 的junit单元测试 ,反复测试自己写的模块非常费劲,特别是交叉模块测试的时候根本就无法弄

因为一个程序只允许一个main方法,如果其他地方存在了,那么就会报错,这就导致了测完A模块想要测试B模块就需要把A模块测试的相关内容删除,这样会出现什么问题呢? 如果后期我们对A模块的内容进行了修改,那么是不是需要在重新写一套测试Demo, 这样非常浪费时间和精力 ,没办法只能自己开发一套类似的,来协助本地开发进行测试代码

  1. 实现最小单元以函数进行测试
  2. 实现流程测试
  3. 实现模块交互模块测试

使用前提

自己必须有集合数据结构和hash结构,然后根据下面的代码进行修改即可 ,如果没有可以到我博客里学习

测试结构

在这里插入图片描述

断言


#ifndef STUDY_ASSERTMY_H
#define STUDY_ASSERTMY_H

//设置字体颜色 (只是在windward下有效)
#define  color(x)  SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x)

#define assertErrorMessageTest(_Expression,errorMessage) \
    do \
    {
      
                                        \
     if((_Expression)){
      
                      \
       color(2) ;\
      printf("==============================: method:%s --->  (%s)Assertion success\n ",__func__,#_Expression);     \
      } ;                              \
      color(7);\
     if(!(_Expression)){
      
                     \
     color(4) ;\
      printf("==============================: (%s)\n Assertion failed: (%s), file:%s \nmethod:%s , line %d \n", errorMessage, #_Expression, __FILE__,__func__ , __LINE__) ;                                                                                  \
     exit(0);                   \
     color(7);                         \
     } ;                              \
} while(0)



#define assertMessageMoreTest(_Expression,correctMessage, errorMessage) \
    do \
    {
      
                                        \
     if((_Expression)){
      
                      \
       color(2) ;\
      printf("==============================: (%s)\n Assertion success: (%s) , method:%s \n   ",correctMessage,__func__,#_Expression);     \
      } ;                              \
      color(7);\
     if(!(_Expression)){
      
                     \
     color(4) ;\
      printf("==============================: (%s)\n Assertion failed: (%s), file:%s \nmethod:%s , line %d \n", errorMessage, #_Expression, __FILE__,__func__ , __LINE__) ;                                                                                  \
     exit(0);                   \
     color(7);                         \
     } ;                              \
} while(0)


#define assertTest(_Expression) \
    do \
    {
      
                                        \
     if(!(_Expression)){
      
                     \
         color(4) ;\
          printf("Assertion failed: (%s), file:%s \nmethod:%s , line %d \n", #_Expression, __FILE__,__func__ , __LINE__) ;                                                                                  \
         exit(0);                   \
         color(7);                         \
     } ;                              \
} while(0)


#endif //STUDY_ASSERTMY_H

核心代码


#ifndef STUDY_TESTCORE_H
#define STUDY_TESTCORE_H

#include "../../structure/charlinkedhashmap.h"
#include "../../structure/char_kv_orderly_list.h"
#include "../assertmy.h"
#include <windows.h>
typedef int BOOL;//定义一个布尔类型
#define TRUE 1
#define FALSE 0

typedef void (*TestMethod)(void);

typedef void (TestGroupMethod)(void);

typedef struct testCore {
    
    
    char methodName[100]; //方法名
    BOOL methodState; //方法状态
    void *method; //方法
} TestCore;

typedef struct testGroup {
    
    
    char groupName[100]; //组名
    BOOL groupState; //组状态
} TestGroup;


void addTestGroup(char *testGroupName,BOOL state, Long order, TestGroupMethod testGroupMethod);

void test_Run_Core_MethodAll();

void test_Run_Core_Method(char *methodName);

void addTestMethodName( const char *groupId, char *methodName, BOOL state, Long order, TestMethod testMethod);

void test_Run_Core_Group(char *testGroupName);

void test_Run_Core_Group_ALL();

#endif //STUDY_TESTCORE_H



#include "testcore.h"
#include <string.h>
#include "assert.h"
static CharLinkedHashMap *testCore = NULL;
static CharKvOrderlyList *testGroup = NULL;

#define MAXSCORE 2147483647
#define MINCORE -2147483648
#define LIMIT(x) (assert(x>=MINCORE&&x<=MAXSCORE))

Long groupCount = 1; //记录第几组
#define addOrder(x) x->sort=groupCount*MAXSCORE+x->sort;


//创建测试核心
TestCore *createTestCore(char *methodName, BOOL state, TestMethod testMethod) {
    
    
    TestCore *testCore = malloc(sizeof(TestCore));
    testCore->methodState = state;
    testCore->method = testMethod;
    strcpy(testCore->methodName, methodName);
    return testCore;
}

//创建测试组
TestGroup *createTestGroup(char *groupName, BOOL state) {
    
    
    TestGroup *testGroup = malloc(sizeof(TestGroup));
    testGroup->groupState = state;
    strcpy(testGroup->groupName, groupName);
    return testGroup;
}


/**
 *
 * @param groupId     __FUNCTION__   使用宏获取当前方法名代来作为测试组名
 * @param methodName   方法名
 * @param testMethod     需要运行的方法
 * @param state     方法状态TRUE启用 FALSE禁用
 * @param order     执行的顺序 (值越大越靠后)
 */
void addTestMethodName(const char *groupId, char *methodName, BOOL state, Long order, TestMethod testMethod) {
    
    
    LIMIT(order); //判定order是否在范围内
    if (testCore == NULL) {
    
    
        testCore = createCharLinkedHashMap(200);
    }
    void *pVoid = getCharLinkedHashMap(testCore, groupId);
    if (pVoid == NULL) {
    
    
        CharKvOrderlyList *pList = createCharKvOrderlyList(200);
        TestCore *pCore = createTestCore(methodName, state, testMethod);
        addCharKvOrderlyList(pList, methodName, pCore, order);
        putCharLinkedHashMap(testCore, groupId, pList);
    } else {
    
    
        CharKvOrderlyList *pList = (CharKvOrderlyList *) pVoid;
        TestCore *pCore = createTestCore(methodName, state, testMethod);
        addCharKvOrderlyList(pList, methodName, pCore, order);
    }
}


/**
 * 运行指定的测试方法
 * @param methodName
 * @param initMethod
 */
void test_Run_Core_Method(char *methodName) {
    
    
    CharList *pCharlist = keysCharLinkedHashMap(testCore);
    //迭代测试组
    for (int i = 0; i < pCharlist->len; i++) {
    
    
        char *groupId = pCharlist->str[i];
        CharKvOrderlyList *pList = getCharLinkedHashMap(testCore, groupId);
        CharKvOrderlyListData *pData = getCharKvOrderlyListByKey(pList, methodName);
        if(pData!=NULL){
    
    
            TestCore *testCore1 = (TestCore *) pData->data;
            TestMethod pVoid = testCore1->method;
            pVoid();
            return;
        }

    }
}

/**
 * 运行所有的测试方法,如果方法状态为FALSE则跳过
 * @param initMethod
 */
void test_Run_Core_MethodAll() {
    
    
    //根据指定的顺序执行
    CharLinkedHashIterator *pHashIterator = createCharLinkedHashMapIterator(testCore);
    while (hasNextCharLinkedHashMapIterator(pHashIterator)) {
    
    
        CharKvLinkedNode *pNode =  nextCharLinkedHashMapIterator(pHashIterator);//拿到方法名
       //拿到测试组的方法并且运行
        CharKvOrderlyListIterator *pListIterator = createCharKvOrderlyListIterator(pNode->value);
        while (hasNextCharKvOrderlyList(pListIterator)) {
    
    
            CharKvOrderlyListData *pData = (CharKvOrderlyListData *) nextCharKvOrderlyList(pListIterator);//拿到方法名
            TestCore *testCore1 =   pData->data;
            if (testCore1->methodState == TRUE) {
    
    
                TestMethod pVoid = testCore1->method;
                pVoid();
            }
        }
    }
}


//添加测试组
void addTestGroup(char *testGroupName,BOOL state, Long order, TestGroupMethod testGroupMethod) {
    
    
    if (testGroup == NULL) {
    
    
        testGroup = createCharKvOrderlyList(200);
    }
    testGroupMethod();//执行测试组方法载入测试容器里面
    //添加测试组
    TestGroup *testGroup1 = createTestGroup(testGroupName, state);
    addCharKvOrderlyList(testGroup, testGroupName, testGroup1, order);
    //获取组内的所有的方法,然后从新计算排序分值
    CharKvOrderlyList *pNode = (CharKvOrderlyList *) getCharLinkedHashMap(testCore, testGroupName);
    CharKvOrderlyListIterator *pListIterator = createCharKvOrderlyListIterator(pNode);
    while (hasNextCharKvOrderlyList(pListIterator)) {
    
    
        CharKvOrderlyListData *pData = (CharKvOrderlyListData *) nextCharKvOrderlyList(pListIterator);//拿到方法名
        addOrder(pData);
    }
    groupCount++;
    quickSort(pNode); //重新排序
}


void test_Run_Core_Group(char *testGroupName) {
    
    
    CharKvOrderlyList *pNode = (CharKvOrderlyList *) getCharLinkedHashMap(testCore, testGroupName);
    if(pNode==NULL){
    
    
        return;
    }
    //拿到测试组的方法并且运行
    CharKvOrderlyListIterator *pListIterator = createCharKvOrderlyListIterator(pNode);
    while (hasNextCharKvOrderlyList(pListIterator)) {
    
    
        CharKvOrderlyListData *pData = (CharKvOrderlyListData *) nextCharKvOrderlyList(pListIterator);//拿到方法名
        TestCore *testCore1 =   pData->data;
        if (testCore1->methodState == TRUE) {
    
    
            TestMethod pVoid = testCore1->method;
            pVoid();
        }
    }
}


void test_Run_Core_Group_ALL() {
    
    
    //根据指定的顺序执行
    CharKvOrderlyListIterator *pListIterator = createCharKvOrderlyListIterator(testGroup);
    while (hasNextCharKvOrderlyList(pListIterator)) {
    
    
        CharKvOrderlyListData *pData = (CharKvOrderlyListData *) nextCharKvOrderlyList(pListIterator);//拿到方法名
        TestGroup *testGroup1 = (TestGroup *) pData->data;
        if (testGroup1->groupState == TRUE) {
    
    
            test_Run_Core_Group(testGroup1->groupName);
        }
    }
}

测试组装工厂



#ifndef STUDY_TESTFACTORY_H
#define STUDY_TESTFACTORY_H
#include "../structure/charlinkedhashmap.h"
#include "../structure/char_kv_orderly_list.h"
#include "testcore/testcore.h"

typedef  void(*TestFun)(void);
void test_Run_Method(char *methodName);
void test_Run_GroupAll_MethodAlL();
void test_Run_Group(char *groupMethodName);
void testMain(TestFun testFun);
#endif //STUDY_TESTFACTORY_H




#include "testfactory.h"
#include  <stdio.h>
static  BOOL test_init_of=FALSE;//测试初始化
void test_Run_Method(char *methodName){
    
    
    assertTest(test_init_of);
    test_Run_Core_Method(methodName);

}
void test_Run_GroupAll_MethodAlL(){
    
    
    assertTest(test_init_of);
    test_Run_Core_MethodAll();

}

void test_Run_Group(char *groupMethodName){
    
    
    assertTest(test_init_of);
    test_Run_Core_Group(groupMethodName);
}

void testMain(TestFun testFun){
    
    
    testFun();
    test_init_of=TRUE;
}




编写测试Demo(演示)


#ifndef STUDY_TEST_CHARLIST_DEMO_H
#define STUDY_TEST_CHARLIST_DEMO_H
void test_charlist_demo_init_method();
#endif //STUDY_TEST_CHARLIST_DEMO_H



#include "test_charlist_demo.h"
#include "../../structure/charlist.h"
#include  <stdio.h>
#include "../testcore/testcore.h"

static CharList *pCharlist;

void test_create_charlist_demo() {
    
    
    pCharlist = createCharList(200);
    assertErrorMessageTest(pCharlist, "createCharList创建失败");
}

void test_add_charlist_demo() {
    
    
    char *str = "hello";
    char *str2 = "world";
    char *str3 = "!";
    char *str4 = "hu";
    char *str5 = "an";
    char *str6 = "min";
    addCharList(pCharlist, str);
    addCharList(pCharlist, str2);
    addCharList(pCharlist, str3);
    addCharList(pCharlist, str4);
    addCharList(pCharlist, str5);
    addCharList(pCharlist, str6);
    assertErrorMessageTest(pCharlist->len == 6, "addCharList添加失败");
}

void test_get_charlist_demo() {
    
    
    char *str = "hello";
    int i = charListBinarySearch(pCharlist, str);

    char message[100];
    sprintf(message, "charListBinarySearch查找成功,查找的位置为%d", i);
    assertMessageMoreTest(i != -1,message, "charListBinarySearch查找失败");
}

void test_del_charlist_demo() {
    
    
    char *str = "hello";
    int value = deleteCharListByValue(pCharlist, str);
    char message[100];
    sprintf(message, "deleteCharListByValue删除成功,删除的位置为%d", value);
    assertMessageMoreTest(value,message, "delCharList删除失败");
}

void test_del_index_charlist_demo() {
    
    
    int i = deleteCharListByIndex(pCharlist, 0);
    char message[100];
    sprintf(message, "deleteCharListByIndex删除成功,删除的位置为%d", i);
    assertMessageMoreTest( i !=-1,message, "delCharListByIndex删除失败");
}

void test_print_charlist_demo() {
    
    
    printCharList(pCharlist);
    char message[100]="test_print_charlist_demo处理完毕";
    assertMessageMoreTest(pCharlist->len > 0,message, "test_print_charlist_demo处理失败");
}

//因为原有的字符串是系统创建的内存,所以不能修改,所以需要复制一份,给够空间,然后再修改
static  char * func(char * str){
    
    
    char * st1 = (char *)malloc(15);
    strcpy(st1, str);
    return strcat(st1,"1") ;
}

void test_forEachCharList_demo() {
    
    
    forEachCharList(pCharlist, func);
    char message[100]="test_forEachCharList_demo处理完毕";
    assertMessageMoreTest(pCharlist->len > 0,message, "test_forEachCharList_demo处理失败");
}
void test_charListClean_demo() {
    
    
    charListClean(pCharlist);

    char message[100];
    sprintf(message, "charListClean清空成功,长度为%d", pCharlist->len);
    assertMessageMoreTest(pCharlist->len == 0,message, "charListClean清空失败");
}
void test_charListIndexOf_demo() {
    
    
    char *str = "hu";
    int i = charListIndexOf(pCharlist, str);
    char message[100];
    sprintf(message, "charListIndexOf查找成功,查找的位置为%d", i);
    assertMessageMoreTest(i == -1,message , "charListIndexOf查找失败");
}

void test_charListLastIndexOf_demo() {
    
    
    char *str = "hu";
    int i = charListLastIndexOf(pCharlist, str);
    char message[100];
    sprintf(message, "charListLastIndexOf查找成功,查找的位置为%d", i);
    assertMessageMoreTest(i == -1, message , "charListLastIndexOf查找失败");
}


void test_charListIsSorted_demo() {
    
    
    BOOL pd = charListIsSorted(pCharlist,TRUE,TRUE);
    char message[100];
    sprintf(message, "charListIsSorted判断数组是否有序 %d", pd);
    assertMessageMoreTest(pd, message , "charListIsSorted判断数组是否有序失败");
}

void test_charListBinarySearch_demo() {
    
    
    char *str = "hu";
    int i = charListBinarySearch(pCharlist, str);
    char message[100];
    sprintf(message, "charListBinarySearch查找成功,查找的位置为%d", i);
    assertMessageMoreTest(i == -1, message , "charListBinarySearch查找失败");
}

void test_charListSet_demo() {
    
    
    char *str = "hu1111";
    charListSet(pCharlist, str,0);
    char message[100];
    sprintf(message, "charListSet设置成功,设置后的值为%s", pCharlist->str[0]);
    assertMessageMoreTest( strcmp(pCharlist->str[0],"hu1111")==0, message , "charListSet设置失败");
}


void test_quickSortCharList_demo() {
    
    
    charListSort(pCharlist,TRUE);
    char message[100];
    sprintf(message, "quickSortCharList排序成功");
    assertMessageMoreTest( pCharlist->len > 0, message , "quickSortCharList排序失败");
}

void test_charListReverse_demo() {
    
    
    charListReverse(pCharlist);
    char message[100];
    sprintf(message, "charListReverse反转成功");
    assertMessageMoreTest( pCharlist->len > 0, message , "charListReverse反转失败");
}

void test_charListCopy_demo() {
    
    
    CharList *pCharlist1 = charListCopy(pCharlist);
    char message[100];
    sprintf(message, "charListCopy复制成功");
    assertMessageMoreTest( pCharlist1->len > 0, message , "charListCopy复制失败");
}

void test_charListDistinct_demo() {
    
    
    charListDistinct(pCharlist);
    char message[100];
    sprintf(message, "charListDistinct去重成功");
    assertMessageMoreTest( pCharlist->len > 0, message , "charListDistinct去重失败");
}

void test_charListMerge_demo() {
    
    
    CharList *pCharlist1 = charListCopy(pCharlist);
    CharList *merge = charListMerge(pCharlist, pCharlist1);
    char message[100];
    sprintf(message, "charListMerge合并成功,长度为%d", merge->len);
    assertMessageMoreTest( merge->len > 0, message , "charListMerge合并失败");
}

void test_charListDifference_demo() {
    
    
    CharList *pCharlist1 = charListCopy(pCharlist);
    addCharList(pCharlist, "hu1344");
    addCharList(pCharlist, "h222u1344");
    CharList *difference = charListDifference(pCharlist, pCharlist1);
    char message[100];
    sprintf(message, "charListDifference差集成功,长度为%d", difference->len);
    assertMessageMoreTest( difference->len > 0, message , "charListDifference差集失败");
}

void test_charListComplement_demo() {
    
    
    CharList *pCharlist1 = charListCopy(pCharlist);
    addCharList(pCharlist1, "12345");
    addCharList(pCharlist1, "把231");
    CharList *complement = charListComplement(pCharlist, pCharlist1);
    char message[100];
    sprintf(message, "charListComplement补集成功,长度为%d", complement->len);
    assertMessageMoreTest( complement->len > 0, message , "charListComplement补集失败");
}

void test_charListUnion_demo() {
    
    
    CharList *pCharlist1 = charListCopy(pCharlist);
    addCharList(pCharlist1, "12345");
    addCharList(pCharlist1, "把231");
    CharList *unionList = charListUnion(pCharlist, pCharlist1);
    char message[100];
    sprintf(message, "charListUnion并集成功,长度为%d", unionList->len);
    assertMessageMoreTest( unionList->len > 0, message , "charListUnion并集失败");
}

void test_charListIntersection_demo() {
    
    
    CharList *pCharlist1 = charListCopy(pCharlist);
    addCharList(pCharlist1, "12345");
    addCharList(pCharlist1, "把231");
    CharList *intersection = charListIntersection(pCharlist, pCharlist1);
    char message[100];
    sprintf(message, "charListIntersection交集成功,长度为%d", intersection->len);
    assertMessageMoreTest( intersection->len > 0, message , "charListIntersection交集失败");
}

void test_CharListIterator_demo() {
    
    
    CharListIterator *iterator = createCharListIterator(pCharlist);
    while (hasNextCharListIterator(iterator)) {
    
    
        char *str = nextCharListIterator(iterator);
        printf("%s,", str);
    }
    char message[100];
    sprintf(message, "CharListIterator迭代器创建成功");
    assertMessageMoreTest( iterator!=NULL, message , "CharListIterator迭代器创建失败");
}

void test_charlist_demo_init_method() {
    
    
    addTestMethodName(__FUNCTION__, "test_create_charlist_demo", TRUE, 0, test_create_charlist_demo);
    addTestMethodName(__FUNCTION__, "test_add_charlist_demo", TRUE, 1, test_add_charlist_demo);
    addTestMethodName(__FUNCTION__, "test_get_charlist_demo", TRUE, 2, test_get_charlist_demo);
    addTestMethodName(__FUNCTION__, "test_del_charlist_demo", TRUE, 3, test_del_charlist_demo);
    addTestMethodName(__FUNCTION__, "test_del_index_charlist_demo", TRUE, 4, test_del_index_charlist_demo);
    addTestMethodName(__FUNCTION__, "test_forEachCharList_demo", TRUE, 5, test_forEachCharList_demo);
    addTestMethodName(__FUNCTION__, "test_charListIndexOf_demo", TRUE, 6, test_charListIndexOf_demo);
    addTestMethodName(__FUNCTION__, "test_charListLastIndexOf_demo", TRUE, 7, test_charListLastIndexOf_demo);
    addTestMethodName(__FUNCTION__, "test_quickSortCharList_demo", TRUE, 8, test_quickSortCharList_demo);
    addTestMethodName(__FUNCTION__, "test_charListIsSorted_demo", TRUE, 9, test_charListIsSorted_demo);
    addTestMethodName(__FUNCTION__, "test_charListBinarySearch_demo", TRUE, 10, test_charListBinarySearch_demo);
    addTestMethodName(__FUNCTION__, "test_charListSet_demo", TRUE, 11, test_charListSet_demo);
    addTestMethodName(__FUNCTION__, "test_charListReverse_demo", TRUE, 12, test_charListReverse_demo);
    addTestMethodName(__FUNCTION__, "test_charListCopy_demo", TRUE, 13, test_charListCopy_demo);
    addTestMethodName(__FUNCTION__, "test_charListDistinct_demo", TRUE, 14, test_charListDistinct_demo);
    addTestMethodName(__FUNCTION__, "test_charListMerge_demo", TRUE, 15, test_charListMerge_demo);
    addTestMethodName(__FUNCTION__, "test_charListDifference_demo", TRUE, 16, test_charListDifference_demo);
    addTestMethodName(__FUNCTION__, "test_charListComplement_demo", TRUE, 17, test_charListComplement_demo);
    addTestMethodName(__FUNCTION__, "test_charListUnion_demo", TRUE, 18, test_charListUnion_demo);
    addTestMethodName(__FUNCTION__, "test_charListIntersection_demo", TRUE, 19, test_charListIntersection_demo);
    addTestMethodName(__FUNCTION__, "test_CharListIterator_demo", TRUE, 20, test_CharListIterator_demo);
    addTestMethodName(__FUNCTION__, "test_print_charlist_demo", TRUE, 99, test_print_charlist_demo);
    addTestMethodName(__FUNCTION__, "test_charListClean_demo", TRUE, 100, test_charListClean_demo);

}

验证


#include  <stdio.h>
#include "unittest/testfactory.h"
#include "./unittest//testdemo/test_verification_demo.h"
#include "./unittest/testdemo/test_verification_demo1.h"
#include "./unittest/testdemo/test_charlist_demo.h"

static void  test_init(){
    
    
    //初始化测试组
    addTestGroup("verification_demo_initMethod",FALSE,0,verification_demo_initMethod);
    addTestGroup("verification_demo1_initMethod",FALSE,1,verification_demo1_initMethod);
    addTestGroup("test_charlist_demo_init_method",TRUE,2,test_charlist_demo_init_method);
}
static  void test_run(){
    
    
    testMain(test_init);//初始化测试
    //运行全部的测试方法
//    test_Run_GroupAll_MethodAlL();
    test_Run_Core_Group_ALL();//运行全部的测试组
//    test_Run_Group("test_charlist_demo_init_method"); //运行指定测试组
//    test_Run_Method("test_print_charlist_demo"); //运行指定测试方法
}

int main() {
    
    
    test_run();
    return (0);
}

在这里插入图片描述

现在我就能随心所欲的测试了,想测试那个方法就测试那个方法,方法之间还可以联动测试,模块和模块还可以交互测试,等等

在这里插入图片描述

点赞 -收藏-关注-便于以后复习和收到最新内容
有其他问题在评论区讨论-或者私信我-收到会在第一时间回复
在本博客学习的技术不得以任何方式直接或者间接的从事违反中华人民共和国法律,内容仅供学习、交流与参考
免责声明:本文部分素材来源于网络,版权归原创者所有,如存在文章/图片/音视频等使用不当的情况,请随时私信联系我、以迅速采取适当措施,避免给双方造成不必要的经济损失。
感谢,配合,希望我的努力对你有帮助^_^

猜你喜欢

转载自blog.csdn.net/weixin_45203607/article/details/126660961