new file: README.md

new file:   doxmind.py
	new file:   doxml.py
	new file:   example.png
	new file:   test.xmind
	new file:   test/test1.xml
	new file:   test/test2.xml
	new file:   xmind2case.py
This commit is contained in:
sheerfish999
2018-05-17 09:39:32 +08:00
commit 6261ddea0b
8 changed files with 1040 additions and 0 deletions

86
README.md Normal file
View File

@@ -0,0 +1,86 @@
# workflow2case
通过绘制的xmind流程可视化文件可直接分析生成所有遍历路径以及python逻辑脚本适合与各类自动化测试框架进行结合在基础功能团操作封装完毕后完成测试路径的覆盖遍历。
## 安装
python环境 3.x\<br>
'''bash
pip install HTMLParser
pip install lxml
'''
## 样例
流程图效果\<br>
![](https://github.com/sheerfish999/workflow2case/example.png)
### 调用方法参考xmind2case.py
'''python
xmindfile="test.xmind"
endtitle="Stop"
lastpath=xmind2path(xmindfile, endtitle)
'''
### 生成的逻辑脚本
case 目录下 case_code.py:\<br>
'''python
Receive_Data()
Verify_Data()
a=test()
if a=="Y":
b=Transform(a)
if b>1:
Load()
c=select1()
if c==5:
e=select2(c)
if e>=5:
d=select4(c)
if d<=8:
runit()
if c==2:
d=select3(c)
if d>8:
runit()
if d<=8:
runit()
if a=="N":
Report_Errors()
'''
### 生成的遍历路径
生成的 case 目录下各个 casen_path.py
## 基本绘图要求
1 使用xmind中workflow类型只能有一个起始入口和结束点\<br>
2 函数使用钻石型,返回判断条件使圆形,过程使用圆角矩形
### 未来计划支持
1) 处理回环\<br>
2 支持 else 逻辑条件

177
doxmind.py Normal file
View File

@@ -0,0 +1,177 @@
# -*- coding: utf-8 -*-
########### 本脚本用于解析 xmind, 并处理 xmind 导出的 xml 及其生成的 tree
import re
from xml.etree import ElementTree as ET
from xml.etree.ElementTree import Element
from zipfile import ZipFile
content_xml = "content.xml" # 主要数据
comments_xml = "comments.xml"
styles_xml="styles.xml" # 样式
################################# xmind 提取 xml
def read_xmind(filename):
cache = {}
with ZipFile(filename) as xmind:
for f in xmind.namelist():
#print(f) # 所有的数据
for key in [content_xml, comments_xml, styles_xml]:
if f == key:
cache[key] = xmind.open(f).read().decode('utf-8')
xml=cache[content_xml]
stylexml=cache[styles_xml]
## 生成供调试
f=open(content_xml,"w")
f.writelines(xml)
f.close()
f=open(styles_xml,"w")
f.writelines(stylexml)
f.close()
return xml,stylexml
################################# xmind 的xml 转 list (非标准xml 先转 tree , 按tree处理)
def xml2list(xml,stylexml):
lists=[]
xml_content=xmind_content_to_etree(xml)
## 起始节点
start_node=xml_content.find('sheet').find('topic')
ids,title,styleids=get_ele_attr(start_node)
next_id=get_id_nextele(xml,ids)
lists.append([title,ids,next_id])
## 下级各个节点, 最后结构类似: [' Valid ', '76q1ecqiei4u494hnsu4a9cl0i', ['6690se63nn0agpf9u043icvmc4', '1je2u1g9ghg60eu0g41668akej'], type]
children_nodes=xml_content.find('sheet').find('topic').find('children').find('topics')
for children_node in children_nodes:
ids,title,styleids=get_ele_attr(children_node)
next_id=get_id_nextele(xml,ids)
types=styleid2type(styleids,stylexml) # 类型
lists.append([title,ids,next_id,types])
lists[0].append("start") ### 起始节点类型
return lists
# 转 etree
def xmind_content_to_etree(content):
xml_content = re.sub(r'\sxmlns="[^"]+"', '', content, count=1)
return ET.fromstring(xml_content.encode('utf-8'))
#### 获取节点的 id 和 title
def get_ele_attr(ele):
title=ele.find('title').text.strip()
ids=ele.attrib['id']
try:
styleids=ele.attrib['style-id']
except:
#print(u"style-id取值错误,id:",ids)
styleids=-1 # 默认的圆角矩形 没有这个key "org.xmind.topicShape.roundedRect"
return ids,title,styleids
##### 获取节点的 指向的下一个元素的 id
def get_id_nextele(xml,first_id):
xml_content=xmind_content_to_etree(xml)
relationships=xml_content.find('sheet').find('relationships')
## 遍历所有的 relationship
next_id=[]
for relationship in relationships:
the_id=relationship.attrib['end1']
if the_id==first_id:
next_id.append(relationship.attrib['end2']) # 可能有多个下一个
if next_id==[]:
next_id=[""] ## 为空则置一个空
return next_id
##### 根据 style id ,取得 style 样式,并进行转化 (非标准xml 先转 tree , 按tree处理)
def styleid2type(styleid,stylexml):
### 查找 shape-class
shape_class=""
types=""
if styleid==-1: # 默认的圆角矩形 没有这个key
shape_class="org.xmind.topicShape.roundedRect"
########################
stylexml_content=xmind_content_to_etree(stylexml)
style_nodes=stylexml_content.find('styles').findall('style')
for style_node in style_nodes:
ids=style_node.attrib['id']
if ids==styleid: ## 如果相同则查找对应的 shape-class
try:
shape_class=style_node.getchildren()[0].attrib['shape-class'] # 相当于 style_node.find['relationship-properties'][0].attrib['shape-class'],但这个方法报错
#print(shape_class)
except:
shape_class="org.xmind.topicShape.roundedRect"
#print(u"shape-class取值错误,styleid:",styleid)
### 转化
if shape_class=="org.xmind.topicShape.roundedRect":
types="sub"
if shape_class=="org.xmind.topicShape.circle":
types="select"
if shape_class=="org.xmind.topicShape.diamond":
types="func"
return types

149
doxml.py Normal file
View File

@@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
########### 本脚本用于解析标准 xml
from lxml import etree # pip install lxml
class XmlDocment():
xmldoc=""
### xmldoc tree 对象
def __init__(self,filename):
self.xmldoc = etree.parse(filename)
### xpath特征对象的 list 注意是符合该 xpath 特征的所有对象 例如第一个对象为 lists[0]
def ele_list(self,xpath):
lists=[]
for eles in self.xmldoc.xpath(xpath):
lists.append(eles)
return lists
### 对象的 子对象的list
def getchildren_list_byele(self,eles):
eles_childrens=[]
for eles_children in eles.getchildren():
eles_childrens.append(eles_children)
return eles_childrens
### 注意是符合该 xpath 特征的所有对象的子对象 例如第一个对象的第一个子对象为 lists[0][0]
def getchildren_list(self,xpath):
lists=[]
for eles in self.xmldoc.xpath(xpath):
eles_childrens=self.getchildren_list_byele(eles)
lists.append(eles_childrens)
return lists
### 获得对象的某个属性
def get_attr(self,ele,attr=""):
ret=""
if attr=="":
ret=htmldecode(ele.text)
else:
ret=ele.attrib.get(attr)
return ret
### 设置对象的某个属性
def set_attr(self,ele,value,attr=""):
if attr=="":
ele.text=value
else:
ele.set(attr, value)
### 插入子节点
def insert_node(self,father_ele,child_node_name):
child_node = etree.SubElement(father_ele, child_node_name)
return child_node
#### 完整的 xml 信息 供输出和调试
def gettext(self):
ret=etree.tostring(self.xmldoc, pretty_print=True)
return ret
## 字符转换
def htmldecode(strs):
strs=strs.replace("&amp;","&")
strs=strs.replace("&lt;","<")
strs=strs.replace("&gt;",">")
strs=strs.replace("&nbsp;"," ")
return strs
###########################################
if __name__ == '__main__':
filename="./test/test1.xml"
xmldoc=XmlDocment(filename)
### 返回成员
xpath="//*[@lang='en']"
ele_lists=xmldoc.ele_list(xpath) #一维list
print(len(ele_lists))
xpath='//book'
ele_lists=xmldoc.ele_list(xpath) #一维list
print(ele_lists)
ele_lists=xmldoc.getchildren_list(xpath) # 二维list
print(ele_lists)
### 成员属性
ele=ele_lists[0][0]
ret=xmldoc.get_attr(ele)
print(ret)
ret=xmldoc.get_attr(ele,"lang")
print(ret)
### 添加节点
child_node=xmldoc.insert_node(ele,"test1")
xmldoc.set_attr(child_node,"11111111")
xmldoc.set_attr(child_node,"22222222",attr="attr")
### 当前 xml
ret=xmldoc.gettext()
print(ret)

BIN
example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
test.xmind Normal file

Binary file not shown.

6
test/test1.xml Normal file
View File

@@ -0,0 +1,6 @@
<book>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>

30
test/test2.xml Normal file
View File

@@ -0,0 +1,30 @@
<CMCORE
xmlns="http://www.cm-inv.com/CMINV/2015/10"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.cm-inv.com/CMINV/2015/10 CMCORE1.0.xsd">
<CMAPI0001Rq>
<CommonRqHdr>
<SPName>这个</SPName>
<RqUID>b2ebd12a-136b-43wb-s3a1-d9114c481ac2</RqUID>
<NumTranCode>000823</NumTranCode>
<ClearDate>20160405</ClearDate>
<TranDate>20160405</TranDate>
<TranTime>151440</TranTime>
<ChannelId>0000</ChannelId>
<Version>1.0</Version>
</CommonRqHdr>
<idNo>410221199005169930</idNo>
<idType>I</idType>
<name>那个</name>
<cardNo>6225182222222222</cardNo>
<cardType>DEBIT</cardType>
<bankCode>0015</bankCode>
<bankMobile>1872135xxxxx</bankMobile>
<provCode>24</provCode>
<provName>上海市</provName>
<cityCode>240001</cityCode>
<cityName>上海市</cityName>
<userId>666666</userId>
<mobile>187213xxxxx</mobile>
</CMAPI0001Rq>
</CMCORE>

592
xmind2case.py Normal file
View File

@@ -0,0 +1,592 @@
# -*- coding: utf-8 -*-
import os
import shutil
import sys
if sys.version_info.major==2: #python2
import HTMLParser #pip install HTMLParser
import urllib2
if sys.version_info.major==3: #python3
from html import parser as HTMLParser
from urllib import request as urllib2
from doxml import *
from doxmind import *
##### 由处理后的 list 中 id 转 title、 type
def id2title(id,lists):
for x in range(len(lists)):
if id==lists[x][1]:
title=lists[x][0].strip()
types=lists[x][3]
break
return title,types
################################# list 生成带有结构的 xml
def list2xml(lists,endtitle):
## 创建起始节点
filename="null.xml"
xmldoc=XmlDocment(filename)
start_node_id=lists[0][1]
start_node_value=lists[0][0]
f=open(filename,"w")
#f.write(r"<start></start>")
f.write(r"<node_" + start_node_id + "></node_" + start_node_id + ">")
f.close()
#xpath='//start'
xpath='//node_' + start_node_id
ele_lists=xmldoc.ele_list(xpath)
start_node=ele_lists[0]
xmldoc.set_attr(start_node,start_node_id,attr="id")
xmldoc.set_attr(start_node,start_node_value)
xmldoc.set_attr(start_node,"start",attr="type")
## 按list成员遍历
hasadd=True ## 本次是否成员添加
while True:
if hasadd==False: ## 已经没有成员再次被添加
break
else:
hasadd=False
for i in range(len(lists)):
list_id=lists[i][1]
xpath="//*[@id='" + list_id + "']"
## xml树中有该 id 特征的所有节点
ele_lists=xmldoc.ele_list(xpath)
#print(xpath)
#print(len(ele_lists))
for node in ele_lists: ## 逐个查现有的子节点是否完整
xml_id=xmldoc.get_attr(node,"id")
## 找到对应的节点
if xml_id==list_id:
## list中该特征的子节点与xml mode列举子节点进行比较不存在则添加并进行标记直到完全没有需要标记的节点则退出
for ii in range(len(lists[i][2])):
if lists[i][2][ii]=="":
continue
childid_fromlist=lists[i][2][ii] ## list中的子节点
childtitle_fromlist,childtype_fromlist=id2title(childid_fromlist,lists) ## list中的子节点标题
childnode_list=xmldoc.getchildren_list_byele(node)
## 结束标记
if endtitle==childtitle_fromlist:
childtype_fromlist="end"
hasnode=False
for childnode_xml in childnode_list: ## xml中的子节点
childid_fromxml=xmldoc.get_attr(childnode_xml,"id")
if childid_fromxml==childid_fromlist:
hasnode=True
break
## 不存在则添加并标记
if hasnode==False:
#child_node=xmldoc.insert_node(node,"node")
child_node=xmldoc.insert_node(node,"node_" + childid_fromlist) # 名称规则首不能为数字
xmldoc.set_attr(child_node,childid_fromlist,attr="id")
xmldoc.set_attr(child_node,childtitle_fromlist)
xmldoc.set_attr(child_node,childtype_fromlist,attr="type")
hasadd=True ## 本次有成员添加
xml_str=xmldoc.gettext().decode("utf-8")
## 输出进行调试
filename="fina.xml"
f=open(filename,"w")
f.write(xml_str)
f.close
return xml_str
################################# xml 遍历所有 xpath , 返回 xpath list
path_list=[]
tagstack = []
class ShowStructure(HTMLParser.HTMLParser):
def handle_endtag(self, tag): tagstack.pop()
def handle_starttag(self, tag, attrs):
tagstack.append(tag)
this_path="/"
for tags in tagstack:
this_path=this_path+"/"+tags
path_list.append(this_path)
def xpath_all_from_tree(xml_str):
ShowStructure().feed(xml_str)
################################# xpath list 转换成 可用路径
## 格式整理
def xpathlist2_pathlist(xpathlist):
for i in range(len(xpathlist)):
path=xpathlist[i].replace("//","")
path=path.replace("node_","")
path=path.replace("/",",")
xpathlist[i]=path
return xpathlist
## 路径 id_list id 转为 title 及类型的 list
def pathlist2_titlepathlist(pathlist,lists):
for ii in range(len(pathlist)):
path_list=pathlist[ii].split(",")
for i in range(len(path_list)):
title,types=id2title(path_list[i],lists)
path_list[i]= ([title,types])
pathlist[ii]=path_list
return pathlist
## 去掉最后一个元素不为出口的 所有节点路径, end_str 为最后字符串特征; 并进行其它必要处理
def only_start2end_path(titlelist,end_str):
## 只保留最后一个元素不为出口的 所有节点路径
lastlist=[]
for i in range(len(titlelist)):
if titlelist[i][len(titlelist[i])-1][0] == end_str:
lastlist.append(titlelist[i])
## 去掉所有 list 中的头尾
lastlist_2=[]
for i in range(len(lastlist)):
templist=[]
for ii in range(1,len(lastlist[i])-1):
templist.append(lastlist[i][ii])
lastlist_2.append(templist)
return lastlist_2
################################# 转换为 python 脚本
#### 路径类脚本
def pathlist2_scipts(pathlist):
casepath="case"
if os.path.exists(casepath)==True:
shutil.rmtree(casepath)
os.mkdir(casepath)
casetitle="case"
##### 生成 按照路径拆分逻辑脚本 ------ 这段暂时意义不大
"""
for i in range(len(pathlist)):
filepath=casepath+"/" + casetitle + str(i) + ".py"
script=""
tab="" # 前置缩进记忆
for ii in range(len(pathlist[i])):
cmd_name=pathlist[i][ii][0]
cmd_type=pathlist[i][ii][1]
## 过程
if cmd_type=="sub":
if cmd_name[-1:]!=")":
cmd_name=cmd_name+"()"
script=script+ tab +cmd_name + "\n"
## 函数
if cmd_type=="func":
if cmd_name[-1:]!=")":
cmd_name=cmd_name+"()"
script=script+ tab + cmd_name + "\n"
## 判断
if cmd_type=="select":
if cmd_name=="else":
cmd_name="else:"
else:
## = 换 ==
if cmd_name.find("=")>=0 and cmd_name.find(">")<0 and cmd_name.find("<")<0 and cmd_name.find("==")<0:
cmd_name=cmd_name.replace("=","==")
cmd_name="if " + cmd_name +":"
script=script+ "\n" + tab + cmd_name + "\n"
tab=tab+"\t" # 增加一次缩进
f=open(filepath,"w")
f.writelines(script)
f.close()
"""
###### 生成 完全遍历的脚本
for i in range(len(pathlist)):
filepath=casepath+"/" + casetitle + str(i) + "_path.py"
script=""
for ii in range(len(pathlist[i])):
cmd_name=pathlist[i][ii][0]
cmd_type=pathlist[i][ii][1]
## 过程
if cmd_type=="sub":
if cmd_name[-1:]!=")":
cmd_name=cmd_name+"()"
script=script +cmd_name + "\n"
## 函数
if cmd_type=="func":
if cmd_name[-1:]!=")":
cmd_name=cmd_name+"()"
script=script + cmd_name + "\n"
## 判断
if cmd_type=="select":
pass
f=open(filepath,"w")
f.writelines(script)
f.close()
##### 全逻辑脚本, 利用 xml 文件结构和特征
def xml2_script():
casepath="case"
if os.path.exists(casepath)==False:
os.mkdir(casepath)
xmlfile="fina.xml"
filepath="case/case_code.py"
#################
f=open(xmlfile,"r")
xml=f.read()
f.close()
## 分割成行
xml=xml.replace("<node_","\n<node_")
xml=xml.replace("</node_","\n</node_")
xml=xml.replace("\">","\">\n")
########### 根据逻辑生成 tab 缩进
lastsctipt=[]
cell=0
## 先生成xml结构层次标号
xmllist=xml.split("\n")
for line in xmllist:
if line!="":
if line.find("<node_")>=0 or line.find("</node_")>=0:
if line.find("<node_")>=0:
cell=cell+1
elif line.find("</node_")>=0:
cell=cell-1
lastsctipt.append([line,cell])
else:
lastsctipt.append([line])
"""
for lists in lastsctipt:
print(lists)
"""
## 预处理
for i in range(len(lastsctipt)):
if len(lastsctipt[i])==2:
## 过程
if lastsctipt[i][0].find("type=\"sub\"")>0:
if lastsctipt[i+1][0][-1:]!=")":
lastsctipt[i+1][0]=lastsctipt[i+1][0]+"()"
lastsctipt[i+1][0]=lastsctipt[i+1][0]+"\n"
## 函数
if lastsctipt[i][0].find("type=\"func\"")>0:
if lastsctipt[i+1][0][-1:]!=")":
lastsctipt[i+1][0]=lastsctipt[i+1][0]+"()"
lastsctipt[i+1][0]=lastsctipt[i+1][0]+"\n"
## 判断
if lastsctipt[i][0].find("type=\"select\"")>0:
lastsctipt[i+1][0]=htmldecode(lastsctipt[i+1][0]) # 逻辑中的各类字符
## = 换 ==
strs=lastsctipt[i+1][0]
if strs.find("=")>=0 and strs.find(">")<0 and strs.find("<")<0 and strs.find("==")<0:
lastsctipt[i+1][0]=strs.replace("=","==")
lastsctipt[i+1][0]="if " + lastsctipt[i+1][0] + ":"
## 产生缩进 直到同级的结构层次标号
tab=""
for i in range(len(lastsctipt)):
if len(lastsctipt[i])==2:
if lastsctipt[i][0].find("type=\"select\"")>0:
cell=lastsctipt[i][1] ## 增加一个缩进 记录逻辑层次
tab=tab+"\t"
for ii in range(i+2,len(lastsctipt)):
if len(lastsctipt[ii])==2:
if lastsctipt[ii][1]==cell: ##减少一个缩进
tab=tab[:-1]
break
lastsctipt[ii][0]=tab + lastsctipt[ii][0]
"""
for lists in lastsctipt:
print(lists)
"""
## 去掉头尾 只能最后去掉 避免影响层次关系
xmllist=[]
for i in range(len(lastsctipt)):
if lastsctipt[i][0].find("type=\"start\"")>=0 or lastsctipt[i][0].find("type=\"end\"")>=0:
lastsctipt[i+1][0]="" ## python for 循环是迭代器,因此不能直接操作 不必删除 直接置空就行了
continue
else:
xmllist.append(lastsctipt[i])
"""
for lists in xmllist:
print(lists)
"""
## 去掉 xml 节点
lastsctipt=[]
for i in range(len(xmllist)):
if len(xmllist[i])==2:
continue
else:
lastsctipt.append(xmllist[i])
"""
for lists in lastsctipt:
print(lists)
"""
#################
lastsctipt_str=""
for line in lastsctipt:
if line[0]!="":
lastsctipt_str=lastsctipt_str+ line[0] +"\n"
f=open(filepath,"w")
f.writelines(lastsctipt_str)
f.close()
########################################### 完整处理过程
# xpath 文件名,终点特征
def xmind2path(xmindfile, endtitle):
# 解压出其中的 xml
xml,stylexml=read_xmind(xmindfile)
# 根据 xmind 中的 xml 资源前后关系生成类似list结构
# 下级各个节点, 最后结构类似: ['Valid', '76q1ecqiei4u494hnsu4a9cl0i', ['6690se63nn0agpf9u043icvmc4', '1je2u1g9ghg60eu0g41668akej'], type]
lists=xml2list(xml,stylexml)
"""
for ele in lists:
print(ele)
"""
# 根据list 生成 xml 结构树
xml_str=list2xml(lists,endtitle)
# 根据xml结构树 生成所有 xpath 到 list
xpath_all_from_tree(xml_str)
#print(path_list)
# 整理 xpath list 到 路径 list
pathlist=xpathlist2_pathlist(path_list)
#print(pathlist)
titlelist=pathlist2_titlepathlist(pathlist,lists)
#print(titlelist)
# 得到出口为终点的路径 并进行处理
lastpath=only_start2end_path(titlelist,endtitle)
"""
for ele in lastpath:
print(ele)
"""
# 将所有路径转换为 python 遍历脚本
pathlist2_scipts(lastpath)
# 根据xml得到逻辑伪代码
xml2_script()
return lastpath
###########################################
# 用法:
if __name__ == '__main__':
## xmind 文件名
xmindfile="test.xmind"
## 结束节点的文本标记
endtitle="Stop"
lastpath=xmind2path(xmindfile, endtitle)