自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

軟件開發(fā)中的自測及C代碼示例

開發(fā) 開發(fā)工具
很多人所理解的軟件開發(fā)人員的工作就是寫代碼,而不包括測試,這樣的理解是片面的。實際的經(jīng)驗表明,很多時候,我們花在測試上的時間比寫代碼的時間還要多。為了保證產(chǎn)品質(zhì)量,很多項目組也對自測提出了較高的要求。

[[179305]]

在軟件開發(fā)中,程序自測是一個永遠(yuǎn)都繞不開的話題。很多開發(fā)人員以寫出有難度的代碼為榮,但卻不重視對自己編寫的代碼進(jìn)行測試,這導(dǎo)致了最終到達(dá)客戶手中的產(chǎn)品質(zhì)量不高,bug頻發(fā),損害了公司的形象。對于一個開發(fā)人員來說,我們應(yīng)該將開發(fā)和自測置于同等重要的地位,我們花在自測上的時間要不比開發(fā)少。能否對自己編寫的代碼進(jìn)行充分的自測也是檢驗一個開發(fā)人員水平高低的標(biāo)準(zhǔn)之一。

自測方法

根據(jù)所編寫的程序的特點,自測方法大致有如下幾種:

***種,利用模擬工具進(jìn)行自測。這種方法適用于需要其他模塊(尚不具備)發(fā)過來的消息才能觸發(fā)程序流程的情況。模擬工具要嚴(yán)格按照協(xié)議的要求發(fā)消息,并處理相應(yīng)的應(yīng)答消息。這種方法的優(yōu)點是可模擬真實的系統(tǒng)來測試代碼,功能覆蓋比較完全;其缺點是模擬工具的編寫比較復(fù)雜(相當(dāng)于要實現(xiàn)一個完整的功能模塊)、花費(fèi)的時間較多。

第二種,利用對端模塊進(jìn)行自測。這種方法適用于兩個軟件模塊功能的耦合性比較強(qiáng)且在同時開發(fā)的情況。例如,正在開發(fā)的模塊1的功能A需要正在開發(fā)的模塊2的功能B才能觸發(fā),而模塊1和模塊2的開發(fā)進(jìn)度差不多,此時,就可以通過模塊2向模塊1發(fā)消息的方法來對功能A和功能B進(jìn)行自測。

第三種,手動插入數(shù)據(jù)或執(zhí)行命令進(jìn)行自測。這種方法適用于某個軟件模塊的功能比較獨立的情況。此時,沒有其他模塊與該模塊進(jìn)行消息的交互,也沒有編寫單獨的測試模塊。利用手工的方法的優(yōu)點是可以根據(jù)程序的特點設(shè)置測試用例,代碼覆蓋率比較高;其缺點是難以對大數(shù)據(jù)量的消息進(jìn)行測試,無法保證程序性能。

第四種,在程序中添加測試代碼進(jìn)行自測。也就是說,在編寫軟件功能代碼的同時,將測試代碼一并加入其中。這種方法的優(yōu)點比較明顯,那就是無需與其他程序模塊進(jìn)行消息交互、無需手動插入數(shù)據(jù)或發(fā)消息,當(dāng)整個軟件模塊運(yùn)行起來之后,測試代碼即可發(fā)揮其功效。當(dāng)然,該方法也有缺點,那就是編寫這些測試代碼需要花費(fèi)一定的時間,可能導(dǎo)致代碼過于臃腫。

本文接下來的部分,將用實際的C代碼來示例第四種自測方法的使用。

示例程序

本文用到的示例程序比較簡單,其功能是每個一段時間(1分鐘)將源目錄中滿足前綴要求的文件備份(移動)到備份目錄中。

程序(FilesBackup.c)如下:

  1. /********************************************************************** 
  2. * 版權(quán)所有(C)2016, Zhou Zhaoxiong。 
  3. * 文件名稱:FilesBackup.c 
  4. * 文件標(biāo)識:無 
  5. * 內(nèi)容摘要:將某個目錄中的文件進(jìn)行備份 
  6. * 其它說明:無 
  7. * 當(dāng)前版本:V1.0 
  8. * 作    者:Zhou Zhaoxiong 
  9. * 完成日期:20160701 
  10. **********************************************************************/ 
  11. #include <stdio.h> 
  12. #include <string.h> 
  13. #include <dirent.h> 
  14. #include <ftw.h> 
  15. #include <time.h> 
  16.  
  17. // 重定義數(shù)據(jù)類型 
  18. typedef signed   int       INT32; 
  19. typedef unsigned int       UINT32; 
  20. typedef unsigned char      UINT8; 
  21.  
  22. // 全局變量定義 
  23. UINT8 g_szSourceDir[256] = {0};     // 源文件的目錄 
  24. UINT8 g_szBackupDir[256] = {0};     // 備份文件的目錄 
  25.  
  26. // 函數(shù)聲明 
  27. INT32 SelectFlies(struct dirent *pDir); 
  28. void ScanDirAndBackup(void); 
  29. void Sleep(UINT32 iCountMs); 
  30.  
  31.  
  32. /**************************************************************** 
  33. * 功能描述: 主函數(shù) 
  34. * 輸入?yún)?shù): 無 
  35. * 輸出參數(shù): 無 
  36. * 返回值: 0-執(zhí)行完成 
  37. * 其他說明: 無 
  38. * 修改日期       版本號        修改人        修改內(nèi)容 
  39. *------------------------------------------------------------- 
  40. * 20160701        V1.0    Zhou Zhaoxiong     創(chuàng)建 
  41. ****************************************************************/ 
  42. INT32 main(void) 
  43.     INT32 iRetValue = 0; 
  44.  
  45.    // 獲取源文件的目錄 
  46.    snprintf(g_szSourceDir, sizeof(g_szSourceDir)-1,"%s/zhouzx/TestDir/SourceDir", getenv("HOME")); 
  47.  
  48.     // 獲取備份文件的目錄 
  49.    snprintf(g_szBackupDir, sizeof(g_szBackupDir)-1,"%s/zhouzx/TestDir/BackupDir", getenv("HOME")); 
  50.  
  51.    // 調(diào)用函數(shù)執(zhí)行文件的備份 
  52.    while (1) 
  53.    { 
  54.        ScanDirAndBackup(); 
  55.  
  56.        Sleep(60 * 1000);    // 每一分鐘執(zhí)行一次文件的備份 
  57.    } 
  58.  
  59.    return 0; 
  60.  
  61.  
  62. /********************************************************************** 
  63. * 功能描述:根據(jù)前綴和后綴選擇文件 
  64. * 輸入?yún)?shù):dir-目錄 
  65. * 輸出參數(shù):無 
  66. * 返回值:0-失敗   1-成功 
  67. * 其它說明:無 
  68. * 修改日期         版本號      修改人          修改內(nèi)容 
  69. *-------------------------------------------------------------------- 
  70. * 20160701         V1.0   ZhouZhaoxiong        創(chuàng)建 
  71. ***********************************************************************/ 
  72. INT32 SelectFlies(struct dirent *pDir) 
  73.    INT32 iSelectResult = 0; 
  74.  
  75.    UINT8 szFilePrefix[10] = {0};     // 源文件的前綴 
  76.  
  77.    if (pDir == NULL
  78.    { 
  79.        printf("SelectFlies:input parameter is NULL!\n"); 
  80.        return 0; 
  81.    } 
  82.  
  83.    // 匹配文件前綴和后綴 
  84.    strncpy(szFilePrefix, "File_", strlen("File_")); 
  85.    iSelectResult = (strncmp(pDir->d_name, szFilePrefix,strlen(szFilePrefix)) == 0); 
  86.  
  87.    if (iSelectResult == 1)           // 找到了匹配前綴的文件 
  88.    { 
  89.        return 1; 
  90.    } 
  91.    else 
  92.    { 
  93.        return 0; 
  94.    } 
  95.  
  96.  
  97. /********************************************************************** 
  98. * 功能描述:掃描目錄并備份文件 
  99. * 輸入?yún)?shù):無 
  100. * 輸出參數(shù):無 
  101. * 返回值:無 
  102. * 其它說明:無 
  103. * 修改日期         版本號      修改人          修改內(nèi)容 
  104. *-------------------------------------------------------------------- 
  105. * 20160701         V1.0     ZhouZhaoxiong        創(chuàng)建 
  106. ***********************************************************************/ 
  107. void ScanDirAndBackup(void) 
  108.    INT32  iScanDirRet       = 0; 
  109.    UINT32 iFileIdx          = 0; 
  110.    UINT8  szScanedFile[512] = {0}; 
  111.    UINT8  szCmdBuf[256]     = {0}; 
  112.    struct dirent **ppDirEnt = NULL
  113.  
  114.    iScanDirRet = scandir(g_szSourceDir, &ppDirEnt, SelectFlies,alphasort); 
  115.    if (iScanDirRet < 0)   // 掃描目錄出錯 
  116.    { 
  117.        printf("ScanDirAndBackup:exec scandir failed, path=%s\n",g_szSourceDir); 
  118.        return
  119.    } 
  120.    else if (iScanDirRet == 0)   // 目錄下無文件 
  121.    { 
  122.        printf("ScanDirAndBackup:no satisfied file in directory %s\n",g_szSourceDir); 
  123.        return
  124.    } 
  125.    else          // 將滿足條件的文件移動到備份目錄中 
  126.    { 
  127.        for (iFileIdx = 0; iFileIdx < iScanDirRet; iFileIdx ++) 
  128.        { 
  129.            memset(szScanedFile, 0x00, sizeof(szScanedFile)); 
  130.            snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s",g_szSourceDir, ppDirEnt[iFileIdx]->d_name); 
  131.  
  132.            memset(szCmdBuf, 0x00, sizeof(szCmdBuf)); 
  133.            snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "mv %s %s",szScanedFile, g_szBackupDir); 
  134.            system(szCmdBuf); 
  135.  
  136.            printf("ScanDirAndBackup:now, %s\n", szCmdBuf); 
  137.        } 
  138.    } 
  139.  
  140.    printf("ScanDirAndBackup:this time,totally moved %d file(s) to%s\n", iScanDirRet, g_szBackupDir); 
  141.  
  142.    return
  143.  
  144.  
  145. /********************************************************************** 
  146. * 功能描述:程序休眠 
  147. * 輸入?yún)?shù):iCountMs-休眠時間(單位:ms) 
  148. * 輸出參數(shù):無 
  149. * 返回值:無 
  150. * 其它說明:無 
  151. * 修改日期      版本號       修改人        修改內(nèi)容 
  152. *------------------------------------------------------------------ 
  153. * 20160701       V1.0    Zhou Zhaoxiong     創(chuàng)建 
  154. ********************************************************************/ 
  155. void Sleep(UINT32 iCountMs) 
  156.    struct timeval t_timeout = {0}; 
  157.  
  158.    if (iCountMs < 1000) 
  159.    { 
  160.        t_timeout.tv_sec  = 0; 
  161.        t_timeout.tv_usec = iCountMs * 1000; 
  162.    } 
  163.    else 
  164.    { 
  165.        t_timeout.tv_sec  = iCountMs /1000; 
  166.        t_timeout.tv_usec = (iCountMs % 1000) * 1000; 
  167.    } 
  168.    select(0, NULLNULLNULL, &t_timeout);    // 調(diào)用select函數(shù)阻塞程序 

添加測試代碼之后的程序

我們添加測試代碼的基本思路是在掃描源文件目錄之前,先在該目錄下生成文件,這樣就相當(dāng)于手動將文件放到源目錄中了。

添加之后的程序代碼如下:

  1. /********************************************************************** 
  2. * 版權(quán)所有(C)2016, Zhou Zhaoxiong。 
  3. * 文件名稱:FilesBackup.c 
  4. * 文件標(biāo)識:無 
  5. * 內(nèi)容摘要:將某個目錄中的文件進(jìn)行備份 
  6. * 其它說明:無 
  7. * 當(dāng)前版本:V1.0 
  8. * 作    者:Zhou Zhaoxiong 
  9. * 完成日期:20160701 
  10. **********************************************************************/ 
  11. #include <stdio.h> 
  12. #include <string.h> 
  13. #include <dirent.h> 
  14. #include <ftw.h> 
  15. #include <time.h> 
  16.  
  17. // 重定義數(shù)據(jù)類型 
  18. typedef signed   int       INT32; 
  19. typedef unsigned int       UINT32; 
  20. typedef unsigned char      UINT8; 
  21.  
  22. // 全局變量定義 
  23. UINT8 g_szSourceDir[256] = {0};     // 源文件的目錄 
  24. UINT8 g_szBackupDir[256] = {0};     // 備份文件的目錄 
  25.  
  26. // 函數(shù)聲明 
  27. INT32 SelectFlies(struct dirent *pDir); 
  28. void ScanDirAndBackup(void); 
  29. void Sleep(UINT32 iCountMs); 
  30. void CreateTestFile(void); 
  31.  
  32.  
  33. /**************************************************************** 
  34. * 功能描述: 主函數(shù) 
  35. * 輸入?yún)?shù): 無 
  36. * 輸出參數(shù): 無 
  37. * 返回值: 0-執(zhí)行完成 
  38. * 其他說明: 無 
  39. * 修改日期       版本號        修改人        修改內(nèi)容 
  40. *------------------------------------------------------------- 
  41. * 20160701        V1.0    Zhou Zhaoxiong     創(chuàng)建 
  42. ****************************************************************/ 
  43. INT32 main(void) 
  44.     INT32 iRetValue = 0; 
  45.  
  46.     // 獲取源文件的目錄 
  47.     snprintf(g_szSourceDir, sizeof(g_szSourceDir)-1,"%s/zhouzx/TestDir/SourceDir", getenv("HOME")); 
  48.  
  49.    // 獲取備份文件的目錄    snprintf(g_szBackupDir,sizeof(g_szBackupDir)-1, "%s/zhouzx/TestDir/BackupDir",getenv("HOME")); 
  50.  
  51.    // 調(diào)用函數(shù)執(zhí)行文件的備份 
  52.    while (1) 
  53.    { 
  54.        // ------------- 
  55.        // 先在源目錄中創(chuàng)建測試文件 
  56.        CreateTestFile(); 
  57.        // ------------- 
  58.  
  59.         ScanDirAndBackup(); 
  60.  
  61.        Sleep(60 * 1000);    // 每一分鐘執(zhí)行一次文件的備份 
  62.    } 
  63.  
  64.    return 0; 
  65.  
  66.  
  67. /********************************************************************** 
  68. * 功能描述:根據(jù)前綴和后綴選擇文件 
  69. * 輸入?yún)?shù):dir-目錄 
  70. * 輸出參數(shù):無 
  71. * 返回值:0-失敗   1-成功 
  72. * 其它說明:無 
  73. * 修改日期         版本號      修改人          修改內(nèi)容 
  74. *-------------------------------------------------------------------- 
  75. * 20160701         V1.0   ZhouZhaoxiong        創(chuàng)建 
  76. ***********************************************************************/ 
  77. INT32 SelectFlies(struct dirent *pDir) 
  78.    INT32 iSelectResult = 0; 
  79.  
  80.    UINT8 szFilePrefix[10] = {0};     // 源文件的前綴 
  81.  
  82.    if (pDir == NULL
  83.    { 
  84.        printf("SelectFlies:input parameter is NULL!\n"); 
  85.        return 0; 
  86.    } 
  87.  
  88.    // 匹配文件前綴和后綴 
  89.    strncpy(szFilePrefix, "File_", strlen("File_")); 
  90.    iSelectResult = (strncmp(pDir->d_name, szFilePrefix,strlen(szFilePrefix)) == 0); 
  91.  
  92.    if (iSelectResult == 1)           // 找到了匹配前綴的文件 
  93.    { 
  94.        return 1; 
  95.    } 
  96.    else 
  97.    { 
  98.        return 0; 
  99.    } 
  100.  
  101.  
  102. /********************************************************************** 
  103. * 功能描述:掃描目錄并備份文件 
  104. * 輸入?yún)?shù):無 
  105. * 輸出參數(shù):無 
  106. * 返回值:無 
  107. * 其它說明:無 
  108. * 修改日期         版本號      修改人          修改內(nèi)容 
  109. *-------------------------------------------------------------------- 
  110. * 20160701         V1.0    ZhouZhaoxiong        創(chuàng)建 
  111. ***********************************************************************/ 
  112. void ScanDirAndBackup(void) 
  113.    INT32  iScanDirRet       = 0; 
  114.    UINT32 iFileIdx          = 0; 
  115.    UINT8  szScanedFile[512] = {0}; 
  116.    UINT8  szCmdBuf[256]     = {0}; 
  117.    struct dirent **ppDirEnt = NULL
  118.  
  119.    iScanDirRet = scandir(g_szSourceDir, &ppDirEnt, SelectFlies,alphasort); 
  120.    if (iScanDirRet < 0)   // 掃描目錄出錯 
  121.    { 
  122.        printf("ScanDirAndBackup:exec scandir failed, path=%s\n",g_szSourceDir); 
  123.        return
  124.    } 
  125.    else if (iScanDirRet == 0)   // 目錄下無文件 
  126.    { 
  127.        printf("ScanDirAndBackup:no satisfied file in directory %s\n",g_szSourceDir); 
  128.        return
  129.    } 
  130.    else          // 將滿足條件的文件移動到備份目錄中 
  131.    { 
  132.        for (iFileIdx = 0; iFileIdx < iScanDirRet; iFileIdx ++) 
  133.        { 
  134.            memset(szScanedFile, 0x00, sizeof(szScanedFile)); 
  135.            snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s", g_szSourceDir,ppDirEnt[iFileIdx]->d_name); 
  136.  
  137.            memset(szCmdBuf, 0x00, sizeof(szCmdBuf)); 
  138.            snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "mv %s %s",szScanedFile, g_szBackupDir); 
  139.            system(szCmdBuf); 
  140.  
  141.            printf("ScanDirAndBackup:now, %s\n", szCmdBuf); 
  142.        } 
  143.    } 
  144.  
  145.    printf("ScanDirAndBackup:this time,totally moved %d file(s) to%s\n", iScanDirRet, g_szBackupDir); 
  146.  
  147.    return
  148.  
  149.  
  150. /********************************************************************** 
  151. * 功能描述:程序休眠 
  152. * 輸入?yún)?shù):iCountMs-休眠時間(單位:ms) 
  153. * 輸出參數(shù):無 
  154. * 返回值:無 
  155. * 其它說明:無 
  156. * 修改日期      版本號       修改人        修改內(nèi)容 
  157. *------------------------------------------------------------------ 
  158. * 20160701       V1.0    Zhou Zhaoxiong     創(chuàng)建 
  159. ********************************************************************/ 
  160. void Sleep(UINT32 iCountMs) 
  161.    struct timeval t_timeout = {0}; 
  162.  
  163.    if (iCountMs < 1000) 
  164.    { 
  165.        t_timeout.tv_sec  = 0; 
  166.        t_timeout.tv_usec = iCountMs * 1000; 
  167.    } 
  168.    else 
  169.    { 
  170.        t_timeout.tv_sec  = iCountMs /1000; 
  171.        t_timeout.tv_usec = (iCountMs % 1000) * 1000; 
  172.    } 
  173.    select(0, NULLNULLNULL, &t_timeout);    // 調(diào)用select函數(shù)阻塞程序 
  174.  
  175.  
  176. /********************************************************************** 
  177.  * 功能描述:創(chuàng)建本地測試文件 
  178.  * 輸入?yún)?shù):無 
  179.  * 輸出參數(shù):無 
  180.  * 返回值:無 
  181.  * 其它說明:每一輪創(chuàng)建的測試文件數(shù)目加1,到達(dá)1000個之后又從1開始 
  182.  * 修改日期            版本號            修改人           修改內(nèi)容 
  183.  *-------------------------------------------------------------------- 
  184.  *20160701             V1.0          Zhou Zhaoxiong        創(chuàng)建 
  185.  **********************************************************************/ 
  186. void CreateTestFile(void) 
  187.    UINT32  iFileIdx        = 0; 
  188.    UINT8   szFileName[500] = {0}; 
  189.    FILE   *fp              = NULL
  190.  
  191.    static UINT32 s_iFileNo = 0; 
  192.  
  193.    s_iFileNo ++; 
  194.    if (s_iFileNo > 1000) 
  195.    { 
  196.        s_iFileNo = 0; 
  197.    } 
  198.  
  199.    for (iFileIdx = 0; iFileIdx < s_iFileNo; iFileIdx ++) 
  200.    { 
  201.        // 獲取帶路徑的文件名 
  202.        memset(szFileName, 0x00, sizeof(szFileName)); 
  203.        snprintf(szFileName, sizeof(szFileName)-1, "%s/File_%d.txt",g_szSourceDir, iFileIdx); 
  204.  
  205.        fp = fopen(szFileName, "a+"); 
  206.        if (fp == NULL
  207.        { 
  208.             printf("CreateTestFile: open file %s failed!\n", szFileName); 
  209.             return
  210.        } 
  211.  
  212.        fputs("Hello,world!", fp); 
  213.        fflush(fp); 
  214.  
  215.        fclose(fp); 
  216.        fp = NULL
  217.    } 
  218.  
  219.    if (s_iFileNo % 10 == 0)   // 每生成10批滿足前綴要求的文件之后, 生成1個不滿足前綴要求的文件 
  220.    { 
  221.        memset(szFileName, 0x00, sizeof(szFileName)); 
  222.        snprintf(szFileName, sizeof(szFileName)-1, "%s/Test_%d.txt",g_szSourceDir, s_iFileNo); 
  223.  
  224.        fp = fopen(szFileName, "a+"); 
  225.        if (fp == NULL
  226.        { 
  227.             printf("CreateTestFile: open file %s failed!\n", szFileName); 
  228.             return
  229.        } 
  230.  
  231.        fputs("Hello,world!", fp); 
  232.        fflush(fp); 
  233.  
  234.        fclose(fp); 
  235.        fp = NULL
  236.    } 

程序說明如下:

***,本程序中添加的測試函數(shù)是CreateTestFile,其作用是在源目錄中創(chuàng)建測試文件??紤]到程序性能,我們設(shè)定最多生成1000(可根據(jù)實際情況修改)個滿足前綴要求的文件,并且每一輪生成的文件數(shù)比上一輪多一個。同時,每生成10輪的滿足前綴要求的文件之后,要生成一個不滿足前綴要求的文件,用以測試異常情況。如此,正常和異常情況都考慮到了。

第二,示例代碼中寫入文件的內(nèi)容是固定的“Hello,world!”,大家可以根據(jù)需要修改該內(nèi)容以滿足自身測試的要求。

第三,在提交正式版本的時候,大家要將測試代碼注釋掉或刪掉,以免影響正常的程序流程。

總結(jié)

很多人所理解的軟件開發(fā)人員的工作就是寫代碼,而不包括測試,這樣的理解是片面的。實際的經(jīng)驗表明,很多時候,我們花在測試上的時間比寫代碼的時間還要多。為了保證產(chǎn)品質(zhì)量,很多項目組也對自測提出了較高的要求。

作為一位合格的軟件開發(fā)人員,自測是一個檢驗和提升自身能力的好方法,大家一定要對自己編寫的代碼進(jìn)行充分的測試。通過不斷地實踐,大家也可以總結(jié)出更多和更好的自測方法。

【本文是51CTO專欄作者周兆熊的原創(chuàng)文章,作者微信公眾號:周氏邏輯(logiczhou)】

責(zé)任編輯:武曉燕 來源: csdn博客
相關(guān)推薦

2016-12-05 16:55:16

開發(fā)實踐C代碼

2023-02-09 16:48:12

軟件開發(fā)測試結(jié)對測試

2020-04-17 18:00:01

軟件人工智能Python

2009-08-27 15:53:30

C#中using wo

2011-08-11 09:56:50

模式

2023-09-13 15:09:35

軟件開發(fā)數(shù)字化進(jìn)程

2014-01-16 14:06:18

軟件開發(fā)團(tuán)隊管理

2011-05-12 11:28:40

軟件開發(fā)

2023-01-09 16:08:19

2013-02-18 09:54:05

軟件開發(fā)程序員

2024-09-23 15:02:40

2009-02-10 17:11:53

SaaSSaaS開發(fā)PaaS

2015-03-02 09:35:07

軟件開發(fā)

2024-07-17 08:15:09

2011-07-04 17:09:54

2023-10-16 08:00:00

2009-06-12 11:35:28

模式框架軟件設(shè)計

2024-04-02 06:03:00

GenAI軟件開發(fā)人工智能

2013-05-09 09:26:59

軟件開發(fā)開發(fā)方法

2014-10-08 09:34:23

git并行管理并行工程
點贊
收藏

51CTO技術(shù)棧公眾號