Rational Functional Tester 对象文件(rftdef)合并

Rational Functional Tester (RFT) 是一款集成在Eclipse上的自动化测试工具。而自动化测试最麻烦的维护部分,一大部分也是指的是对象库文件的管理维护。

之前的项目里,经常遇到对象库文件有冲突,虽然我们有用Rational Team Concert (RTC) 去做compair & merge 的工作,但是还是不是太理想,有时候还是会遇到一些文件的冲突(主要是对象文件,“*.rftdef”文件,以及与之相关的helper文件)。下图就是一个典型的rftdef文件,很容易看出其本质就是个xml文件

而冲突的内容主要就是<TestObject>元素的冲突。其实就是两个XML文件的合并。

我用python写了个RftDef类去将不同的<TestObject>元素加在最后

# RftDef
# $Id: RftDef.py 2014-11-17 SunYu$
#
# light-wight Rational Functional Tester Object definition files 
# '*.rftdef' merge support for Python 3.3 and later.
#
# Sample of RFT test object map
# RftDef is target to merge <TestObject/> in 2 different files
#
# <?xml version="1.0" encoding="UTF-8"?>
# <ScriptDefinition L=".ScriptDefinition">
#   <ScriptName/>
#   <Language/>
#   <Map/>
#   <Datapool/>
#   <DatapoolIterator/>
#   <HelperSuper/>
#   <ScriptNameMap L=".ScriptDefinitionNameMap">
#       <TestObject L=".ScriptDefNameMapElement">
#           <Name>kw_AVProposalDetails_btn_cancelbutton</Name>
#           <ID>Nn.SFdtlx8A898:EjdFp:NhRhOwR:8WU</ID>
#           <Role>Button</Role>
#           <Deleted>false</Deleted>
#       </TestObject>
#       ......... 
#   </ScriptNameMap>
#   <Properties L=".PropSet"/>
#   <ScriptEncoding>Cp1252</ScriptEncoding>
#   <KeywordName/>
#   <VisualScriptName/>
#   <DatapoolSet L=".DatapoolSet"/>
# </ScriptDefinition>

from xml.etree import ElementTree as ET
import os,sys

class RftDef:
    def __init__(self, path=None, f1=None, f2=None):     
        if not path:
            #change python workspace to current folder
            self._path = sys.path[0]
        else:
            self._path = path
            
        os.chdir(self._path)
        
        if not f1:
            self._f1 = self.__getFileName()
        else:
            self._f1 = f1
        if not f2:
            self._f2 = self.__getFileName("second")
        else:
            self._f2 = f2

        self.__validFileName()
        #ElementTree f1,f2
        self._et1 = ET.parse(self._f1)
        self._et2 = ET.parse(self._f2)
        #list to merge
        self._lstEt1 = list()
        self._lstEt2 = list()

    def __str__(self):
        return ("Path %s\n"
                "f1 %s\n"
                "f2 %s\n"
                "et1 %s\n"
                "et2 %s\n"
                "lstEt1 %s\n"
                "lstEt2 %s"
                % (self._path,
                   self._f1,
                   self._f2,
                   self._et1,
                   self._et2,
                   self._lstEt1,
                   self._lstEt2)
               )
    
    def __getFileName(self,s="first"):
        return input("Please input %s file name\n" % s)
        
    
    def __validFileName(self):
        if not self._f1.endswith(".rftdef"):
            self._f1 = self._f1 + ".rftdef"
        if not self._f2.endswith(".rftdef"):
            self._f2 = self._f2 + ".rftdef"

        self._f1 = self._path + "/" + self._f1
        self._f2 = self._path + "/" + self._f2

        if not os.path.exists(self._f1):
            raise ValueError("%s is not a valid file path!"
                             " Please check." % self._f1)
        if not os.path.exists(self._f2):
            raise ValueError("%s is not a valid file path!"
                             " Please check." % self._f2)

    def __mergeList(self):
        """ see sample structure of rftdef above """
        lstEt1 = list()
        lstEt2 = list()
        lstDiff = list()
        for obj in self._lstEt1:
            for attr in obj:
                if(attr.tag=="Name"):
                    lstEt1.append(attr.text)
        for obj in self._lstEt2:
            for attr in obj:
                if(attr.tag=="Name"):
                    lstEt2.append(attr.text)

        for objName in lstEt1:
            if objName not in lstEt2:
                lstDiff.append(objName)
        for objName in lstEt2:
            if objName not in lstEt1:
                lstDiff.append(objName)
                
        if len(lstDiff) <= 0:
            return "These 2 files are the same."
        
        lstTemp = self._lstEt1
        for objName in lstDiff:
            for obj in self._lstEt2:
                for attr in obj:
                    if(attr.text == objName):
                        lstTemp.append(obj)
        return lstTemp

    def parse(self,xpath = "./ScriptNameMap/TestObject"):
        
        """ xpath default is './ScriptNameMap/TestObject'
            '.' stands for root
        """

        self._lstEt1 = self._et1.findall(xpath)
        self._lstEt2 = self._et2.findall(xpath)

        if self._lstEt1 == None or self._lstEt2 == None:
            raise ValueError("%s is not a valid xpath." % xpath)
        

    def write(self, out="out.rftdef"):
        lstTemp = self.__mergeList()
        #clear <ScriptNameMap>
        tar = self._et1.find("./ScriptNameMap")
        tar.clear()
        for i in lstTemp:
            tar.append(i)
        self._et1.write(out,"utf-8",True)
        
        if not os.path.exists(self._path + "/" + out):
            return "Unknow Error in Merge File!"
        else:
            return "Merge files sucessful!"


rft = RftDef("","test1","test2")
rft.parse()
rft.write()
    

上面的代码会比较两个rftdef文件 test1 和 test2,最后生成的是out.rftdef文件

附test1.rftdef文件,test2则是自己随便改了些<TestObject> Name里面的内容

<?xml version="1.0" encoding="UTF-8"?>
<ScriptDefinition L=".ScriptDefinition">
    <ScriptName>Business.CP_UILib.PtyVal</ScriptName>
    <Language>java</Language>
    <Map>ObjectMap/ObjectsRepository_CP.rftmap</Map>
    <Datapool/>
    <DatapoolIterator>com.rational.test.ft.datapool.SequentialIterator</DatapoolIterator>
    <HelperSuper>Business.Helpers.CP.BusinessHelper</HelperSuper>
    <ScriptNameMap L=".ScriptDefinitionNameMap">
        <TestObject L=".ScriptDefNameMapElement">
            <Name>kw_ViewAndCreateTrigger_lbl_html_TrigReasonVal</Name>
            <ID>OP.SFdtlx8A898:tfcq3:Ni6hV0h:8WT</ID>
            <Role>Html</Role>
            <Deleted>false</Deleted>
        </TestObject>
        <TestObject L=".ScriptDefNameMapElement">
            <Name>kw_ViewAndCreateTrigger_txt_dtpPropDateDateBox</Name>
            <ID>ON.SFdtlx8A898:tfcq3:Ni6hV0h:8WT</ID>
            <Role>Text</Role>
            <Deleted>false</Deleted>
        </TestObject>
    </ScriptNameMap>
    <Properties L=".PropSet"/>
    <ScriptEncoding>Cp1252</ScriptEncoding>
    <KeywordName/>
    <VisualScriptName/>
    <DatapoolSet L=".DatapoolSet"/>
</ScriptDefinition>

 

posted @ 2014-11-18 16:23  平静缓和用胸音说爱  阅读(331)  评论(0编辑  收藏  举报