使用Python把Gtest XML测试结果转换为HTML格式
在最近的测试中,使用gtest测试框架对c语言代码进行测试,结果以XML文件来保存,但是测试结果的查阅和分析非常不方便。便想着把xml的结果直接转为HTML文件,方便和Jenkins系统对接显示。因现在的测试方法是使用Python脚本来控制gtest的测试文件运行的,故选用Python脚本来实现xml转html的功能。
- 先看结果:
个人对于html不是很熟悉,只是简单的了解各个元素。要求只有一个,生成的结果清晰明了,便于查阅即可。
- 环境准备:
安装libxml2 libxstl模块
Python 2.7环境
Ubuntu 14.04 验证下通过。
- 输出:
执行结果生成同名的html文件。
Python代码如下此段代码是在网上搜索参考的:
原文地址如下:http://blog.csdn.net/zhaoweikid/article/details/74837
我这里进行了简单的修改,增加了命令行参数。
#!/usr/bin/python #coding=utf8 import sys import libxml2 import libxslt class compoundXML: def __init__(self): self._result=None self._xsl=None self._xml=None def do(self,xml_file_name,xsl_file_name='gtest.xsl'): self._xml = libxml2.parseFile(xml_file_name) if self._xml ==None: return 0 styledoc = libxml2.parseFile(xsl_file_name) if styledoc == None: return 0 self._xsl = libxslt.parseStylesheetDoc(styledoc) if self._xsl == None: return 0 self._result = self._xsl.applyStylesheet(self._xml, None) def get_xml_doc(self): return self._result def get_translated(self): return self._result.serialize('UTF-8') def save_translated(self, file_name): self._xsl.saveResultToFilename(file_name, self._result, 0) def release(self): ''' this function must be called in the end. ''' self._xsl.freeStylesheet() self._xml.freeDoc() self._result.freeDoc() self._xsl = None self._xml = None self._result = None def xml2html(xml_file): test=compoundXML() test.do(xml_file) test.save_translated(xml_file+'.html') test.release() if __name__ =='__main__': filename=sys.argv[1]; test=compoundXML() test.do(filename) #print test.get_translated() test.save_translated(filename+'.html') test.release()
但是这个原文链接讲的非常不详细,只是把Python脚本写了,没有放xsl模板,在尝试的时候尝试了好久,才明白过来需要一个xsl模板,在使用xsl模板来解析gtest测试结果的过程中,调试了好久,终于有一个让自己满意的显示结果。
Xslt解析xml生成html文件,是按照xsl文件的模板来解析,这些都是教训哈,摸索了好久。
关键是根据gtest的xml文件格式,编写一个合适的xsl模板。
解析gtest xml的Xsl模板如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:output method="html" indent="yes"/> <xsl:template match="/"> <table cellpadding="2" cellspacing="5" border="1px"> <tr> <th bgcolor="#808080"><font color="#FFFFFF">Testcase Num</font></th> <th bgcolor="#808080"><font color="#FFFFFF">Failure Num</font></th> </tr> <tr> <td style="font-family: Verdana; font-size: 15px; font-weight: bold;"><xsl:value-of select="testsuites/@tests"/> </td> <td style="font-family: Verdana; font-size: 15px; font-weight: bold;"><xsl:value-of select="testsuites/@failures"/> </td> </tr> </table> <table cellpadding="2" cellspacing="5"> <tr><td style="font-family: Verdana; font-size: 10px;"> <table align="left" cellpadding="2" cellspacing="0" style="font-family: Verdana; font-size: 10px;"> <tr> <th bgcolor="#808080"><font color="#FFFFFF"><b>TestSuites</b></font></th> <th bgcolor="#808080"> <table width="1000px" align="left" cellpadding="1" cellspacing="0" style="font-family: Verdana; font-size: 10px;"> <tr style="font-family: Verdana; font-size: 10px;"> <td width="15%"><font color="#FFFFFF"><b>Testcase</b></font></td> <td width="25%"><font color="#FFFFFF"><b>Result</b></font></td> <td width="75%"><font color="#FFFFFF"><b>ErrorInfo</b></font></td> </tr> </table> </th> </tr> <xsl:for-each select="testsuites/testsuite"> <tr> <td style="border: 1px solid #808080"><xsl:value-of select="@name"/></td> <td style="border: 1px solid #808080"> <table width="1000px" align="left" cellpadding="1" cellspacing="0" style="font-family: Verdana; font-size: 10px;"> <xsl:for-each select="testcase"> <tr> <td style="border: 1px solid #808080" width="15%" rowspan="@tests"><xsl:value-of select="@name"/></td> <xsl:choose> <xsl:when test="failure"> <td style="border: 1px solid #808080" bgcolor="#ff00ff" width="25%">Failure</td> <td style="border: 1px solid #808080" bgcolor="#ff00ff" width="70%"><xsl:value-of select="failure/@message"/></td> </xsl:when> <xsl:otherwise> <td style="border: 1px solid #808080" width="25%">Success</td> <td style="border: 1px solid #808080" width="70%"><xsl:value-of select="failure/@message"/></td> </xsl:otherwise> </xsl:choose> </tr> </xsl:for-each> </table> </td> </tr> </xsl:for-each> </table> </td> </tr> </table> </xsl:template> </xsl:stylesheet>
Xml文件如下:
<?xml version="1.0" encoding="UTF-8"?> <testsuites tests="22" failures="3" disabled="0" errors="0" time="73.802" name="AllTests"> <testsuite name="TEST_CStart" tests="14" failures="0" disabled="0" errors="0" time="35.012"> <testcase name="Normal_ORAY" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_ORAY" status="run" time="0" classname="TEST_CStart" /> <testcase name="Normal_NOIP" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_NOIP" status="run" time="0.001" classname="TEST_CStart" /> <testcase name="Normal_HIVIEW" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_HIVIEW" status="run" time="0" classname="TEST_CStart" /> <testcase name="Normal_CHANGEIP" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_CHANGEIP" status="run" time="0.001" classname="TEST_CStart" /> <testcase name="Normal_3322" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_3322" status="run" time="0" classname="TEST_CStart" /> <testcase name="Normal_Dyndns" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_Dyndns" status="run" time="0" classname="TEST_CStart" /> <testcase name="Normal_easy" status="run" time="5.001" classname="TEST_CStart" /> <testcase name="WithInited_easy" status="run" time="0.001" classname="TEST_CStart" /> </testsuite> <testsuite name="TEST_CStop" tests="2" failures="0" disabled="0" errors="0" time="0"> <testcase name="Normal" status="run" time="0" classname="TEST_CStop" /> <testcase name="WithoutInited" status="run" time="0" classname="TEST_CStop" /> </testsuite> <testsuite name="TEST_DynipCInit" tests="1" failures="0" disabled="0" errors="0" time="0.001"> <testcase name="Normal_DYNIP" status="run" time="0" classname="TEST_DynipCInit" /> </testsuite> <testsuite name="TEST_DynipCLogSet" tests="1" failures="0" disabled="0" errors="0" time="0"> <testcase name="Normal_DYNIP" status="run" time="0" classname="TEST_DynipCLogSet" /> </testsuite> <testsuite name="TEST_DynipCRegist" tests="1" failures="1" disabled="0" errors="0" time="19.998"> <testcase name="Normal_DYNIP" status="run" time="19.998" classname="TEST_DynipCRegist"> <failure message="Value of: DynipCRegist(&ptDynipCRInfo,dwDnsAddr)
 Actual: 4105
Expected: 0x1004
Which is: 4100" type=""><![CDATA[../_gtest.c:400 Value of: DynipCRegist(&ptDynipCRInfo,dwDnsAddr) Actual: 4105 Expected: 0x1004 Which is: 4100]]></failure> </testcase> </testsuite> <testsuite name="TEST_DynipCRefresh" tests="1" failures="1" disabled="0" errors="0" time="3.795"> <testcase name="Normal_DYNIP" status="run" time="3.795" classname="TEST_DynipCRefresh"> <failure message="Value of: DynipCRefresh(&ptDynipCRInfo,dwDnsAddr)
 Actual: 4117
Expected: 0x1014
Which is: 4116" type=""><![CDATA[../_gtest.c:413 Value of: DynipCRefresh(&ptDynipCRInfo,dwDnsAddr) Actual: 4117 Expected: 0x1014 Which is: 4116]]></failure> </testcase> </testsuite> <testsuite name="TEST_DynipCDeleteRegist" tests="1" failures="1" disabled="0" errors="0" time="14.994"> <testcase name="Normal_DYNIP" status="run" time="14.994" classname="TEST_DynipCDeleteRegist"> <failure message="Value of: DynipCDeleteRegist(htonl(638699), htonl(3372250290UL),inet_addr("8.8.8.8"))
 Actual: 4105
Expected: 0x1000
Which is: 4096" type=""><![CDATA[../_gtest.c:425 Value of: DynipCDeleteRegist(htonl(638699), htonl(3372250290UL),inet_addr("8.8.8.8")) Actual: 4105 Expected: 0x1000 Which is: 4096]]></failure> </testcase> </testsuite> <testsuite name="TEST_CGetVersion" tests="1" failures="0" disabled="0" errors="0" time="0"> <testcase name="Normal" status="run" time="0" classname="TEST_CGetVersion" /> </testsuite> </testsuites>
使用方法:
把Python脚本和gtest.xsl模板放在同一个目录下。
一个是使用命令行:
./xml2html.py gtest_reult.xml
二是作为函数调用:
使用xml2html(xml_file)函数进行转换。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?