284 lines
14 KiB
Python
284 lines
14 KiB
Python
|
|
from abc import ABC
|
|||
|
|
from typing import Union, List
|
|||
|
|
from docx.table import Table
|
|||
|
|
from stuctor.patterns import ParsePatterns
|
|||
|
|
from stuctor.wdsc import parse_wdsc
|
|||
|
|
from stuctor.dmzc import parse_dmsc, parse_dmzc, parse_luoji
|
|||
|
|
from stuctor.tools import normal_parse_content
|
|||
|
|
from stuctor.cpu_static import wdsc_content_list, jtfx_content_list, dmsc_content_list
|
|||
|
|
from datas.global_data import global_data
|
|||
|
|
from nicegui import ui
|
|||
|
|
|
|||
|
|
class Item(ABC):
|
|||
|
|
def __init__(self, type: str) -> None:
|
|||
|
|
self.type: str = type
|
|||
|
|
self.capter: str = "空"
|
|||
|
|
|
|||
|
|
class TitleItem(Item):
|
|||
|
|
def __init__(self, rank: str, title: str) -> None:
|
|||
|
|
self.title = title
|
|||
|
|
super().__init__(rank)
|
|||
|
|
|
|||
|
|
class TableItemList(Item):
|
|||
|
|
"""Table项"""
|
|||
|
|
def __init__(
|
|||
|
|
self, table: Table, current_title_item: Union[TitleItem, None], logger: ui.log, parse_content_strategy: str
|
|||
|
|
) -> None:
|
|||
|
|
self.logger = logger
|
|||
|
|
# 测试项名称
|
|||
|
|
self.name = table.cell(0, 1).text
|
|||
|
|
# 2.测试项标识
|
|||
|
|
self.ident = table.cell(0, 3).text
|
|||
|
|
# 3.追踪关系
|
|||
|
|
self.zhui = self.__parse_zhui_whole(current_title_item)
|
|||
|
|
# 4.table_item列表
|
|||
|
|
self.table_item: list[TableItem] = []
|
|||
|
|
# ~~~~~
|
|||
|
|
# 5.储存分割用例子项标题
|
|||
|
|
self.case_index_list = []
|
|||
|
|
# 6.储存分割的综述
|
|||
|
|
self.case_zongsu_list = []
|
|||
|
|
if table.cell(4, 0).text == '测试方法':
|
|||
|
|
if parse_content_strategy == 'FPGA':
|
|||
|
|
self.__parse_content_fpga(table.cell(3, 2).text, table.cell(4, 2).text)
|
|||
|
|
elif parse_content_strategy == 'CPU':
|
|||
|
|
self.__parse_content_cpu(table.cell(3, 2).text, table.cell(4, 2).text)
|
|||
|
|
super().__init__("table")
|
|||
|
|
|
|||
|
|
# 解析追踪关系一栏
|
|||
|
|
def __parse_zhui_whole(self, last_title_item: Union[TitleItem, None]):
|
|||
|
|
if last_title_item:
|
|||
|
|
return ['软件测试依据:测评大纲', f'测评大纲:({last_title_item.capter}) {last_title_item.title}', f'测评大纲标识:{self.ident}']
|
|||
|
|
|
|||
|
|
# 填充用例名称和综述的index
|
|||
|
|
def __parse_regex(
|
|||
|
|
self,
|
|||
|
|
desc_paras: List[str],
|
|||
|
|
paras: List[str],
|
|||
|
|
):
|
|||
|
|
for index, val in enumerate(desc_paras):
|
|||
|
|
if len(ParsePatterns.step_pattern.findall(val)) > 0:
|
|||
|
|
self.case_zongsu_list.append(index)
|
|||
|
|
for idx, value in enumerate(paras):
|
|||
|
|
if len(ParsePatterns.step_pattern.findall(value)) > 0:
|
|||
|
|
self.case_index_list.append(idx)
|
|||
|
|
|
|||
|
|
def __obtain_index(self, description, content):
|
|||
|
|
paras = content.replace(" ", "").split('\n')
|
|||
|
|
desc_paras = description.split('\n')
|
|||
|
|
self.__parse_regex(desc_paras, paras)
|
|||
|
|
return paras, desc_paras
|
|||
|
|
|
|||
|
|
def __parse_content_cpu(self, description: str, content: str):
|
|||
|
|
paras, desc_paras = self.__obtain_index(description, content)
|
|||
|
|
if '文档审查' in paras[0]:
|
|||
|
|
content_list = wdsc_content_list
|
|||
|
|
zongsu = "\a".join(desc_paras[1:])
|
|||
|
|
csh = '文档审查单齐备,研制方已提交文档'
|
|||
|
|
self.table_item.append(TableItem('文档审查', content_list, 0, self.ident, zongsu, csh, self.logger, has_r1 = False))
|
|||
|
|
elif '静态分析' in paras[0]:
|
|||
|
|
content_list = jtfx_content_list
|
|||
|
|
zongsu = desc_paras[1]
|
|||
|
|
csh = '研发方已提供源代码'
|
|||
|
|
self.table_item.append(TableItem('静态分析', content_list, 0, self.ident, zongsu, csh, self.logger, has_r1 = False))
|
|||
|
|
elif '代码审查' in paras[0]:
|
|||
|
|
zongsu = desc_paras[1]
|
|||
|
|
csh = '研发方已提供源代码'
|
|||
|
|
self.table_item.append(TableItem('静态分析', dmsc_content_list, 0, self.ident, zongsu, csh, self.logger, has_r1 = False))
|
|||
|
|
else:
|
|||
|
|
cIdx = 0 # 用例计数
|
|||
|
|
for index in range(len(self.case_index_list)):
|
|||
|
|
title = paras[self.case_index_list[index]] # 1.全体:获取标题
|
|||
|
|
# 2.获取综述
|
|||
|
|
if 'XQ' in "".join(desc_paras):
|
|||
|
|
try:
|
|||
|
|
zongsu = desc_paras[self.case_zongsu_list[index] + 1]
|
|||
|
|
except IndexError:
|
|||
|
|
self.logger.push(f"注意用例{title}-{self.ident},测试方法没有对应的测试项描述,该用例转换失败...")
|
|||
|
|
continue
|
|||
|
|
else:
|
|||
|
|
zongsu = desc_paras[0]
|
|||
|
|
# 3.初始化,后续可由用户填写
|
|||
|
|
csh = '具备测试环境,测试工具已就绪'
|
|||
|
|
try:
|
|||
|
|
content_list = self.__parse_step_cpu(paras[self.case_index_list[index] + 1:self.case_index_list[index + 1]])
|
|||
|
|
except IndexError:
|
|||
|
|
content_list = self.__parse_step_cpu(paras[self.case_index_list[index] + 1:])
|
|||
|
|
self.table_item.append(
|
|||
|
|
TableItem(
|
|||
|
|
title.split("(")[0].strip("123456789)),.,。;;::"),
|
|||
|
|
content_list,
|
|||
|
|
cIdx,
|
|||
|
|
self.ident,
|
|||
|
|
zongsu,
|
|||
|
|
csh,
|
|||
|
|
self.logger,
|
|||
|
|
has_r1 = False
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
cIdx += 1
|
|||
|
|
|
|||
|
|
# 分解测试类型表格中一大堆东西给每个TableItem初始化 - FPGA大纲转说明
|
|||
|
|
def __parse_content_fpga(self, description: str, content: str):
|
|||
|
|
# 1.文档审查单独解析
|
|||
|
|
if '文档审查' in description:
|
|||
|
|
res = parse_wdsc(content)
|
|||
|
|
zongsu = '对开发方提交的文档进行审查'
|
|||
|
|
csh = '文档审查单齐备'
|
|||
|
|
item = TableItem('文档审查', res, 0, self.ident, zongsu, csh, self.logger)
|
|||
|
|
self.table_item.append(item)
|
|||
|
|
elif '代码审查' in description:
|
|||
|
|
res = parse_dmsc(description, content)
|
|||
|
|
zongsu = '对开发方提交的工程、代码进行审查'
|
|||
|
|
csh = '代码审查单齐备'
|
|||
|
|
item = TableItem('代码审查', res, 0, self.ident, zongsu, csh, self.logger)
|
|||
|
|
self.table_item.append(item)
|
|||
|
|
elif '代码走查' in description:
|
|||
|
|
res = parse_dmzc(description, content)
|
|||
|
|
zongsu = '对被测软件全部代码和关键部分进行代码走查'
|
|||
|
|
csh = '代码走查范围已确定,范围内代码和模块具备走查条件'
|
|||
|
|
item = TableItem('代码走查', res, 0, self.ident, zongsu, csh, self.logger)
|
|||
|
|
self.table_item.append(item)
|
|||
|
|
elif '逻辑测试' in description:
|
|||
|
|
res = parse_luoji(description, content)
|
|||
|
|
zongsu = '源码动态测试语句、分支、状态机、条件、表达式覆盖率均需达到100%。对覆盖率达不到要求的软件,应对未覆盖的部分逐一进行分析和确认,并提供分析描述'
|
|||
|
|
csh = '软件已准备就绪'
|
|||
|
|
item = TableItem('逻辑测试', res, 0, self.ident, zongsu, csh, self.logger)
|
|||
|
|
self.table_item.append(item)
|
|||
|
|
else:
|
|||
|
|
paras, desc_paras = self.__obtain_index(description, content)
|
|||
|
|
# 在判断之前取出用例的title和cIdx
|
|||
|
|
cIdx = 0
|
|||
|
|
for index in range(len(self.case_index_list)):
|
|||
|
|
title = paras[self.case_index_list[index]] # 1.全体:获取标题
|
|||
|
|
# 2.获取当前用例的综述
|
|||
|
|
try:
|
|||
|
|
zongsu = desc_paras[self.case_zongsu_list[index] + 1]
|
|||
|
|
except IndexError:
|
|||
|
|
self.logger.push(f"注意用例{title}-{self.ident},测试方法没有对应的测试项描述,该用例转换失败...")
|
|||
|
|
continue
|
|||
|
|
# 3.初始化,后续可由用户填写
|
|||
|
|
csh = '具备测试环境'
|
|||
|
|
# 4.分情况处理content_list了
|
|||
|
|
try:
|
|||
|
|
content_list = self.__parse_step_fpga(paras[self.case_index_list[index] + 1:self.case_index_list[index + 1]])
|
|||
|
|
except IndexError:
|
|||
|
|
content_list = self.__parse_step_fpga(paras[self.case_index_list[index] + 1:])
|
|||
|
|
self.table_item.append(TableItem(title.split("(")[0], content_list, cIdx, self.ident, zongsu, csh, self.logger))
|
|||
|
|
# 最后用例计数+1
|
|||
|
|
cIdx += 1
|
|||
|
|
|
|||
|
|
# 重点函数:首先分实物或非实物 - fpga大纲转说明
|
|||
|
|
def __parse_step_fpga(self, paras: List[str]):
|
|||
|
|
# 返回格式为:[{'content':'步骤内容','yuqi':'预期'},{'content':'步骤内容','yuqi':'预期'}]
|
|||
|
|
# 1.大类仿真测试环境
|
|||
|
|
content_list = []
|
|||
|
|
if '仿真测试' in paras[0] or '功能仿真' in paras[0]:
|
|||
|
|
duo_one_hang = 0
|
|||
|
|
for i, hang in enumerate(paras):
|
|||
|
|
if '通过仿真' in hang:
|
|||
|
|
duo_one_hang = i
|
|||
|
|
break
|
|||
|
|
# 变量:“通过仿真”那行str
|
|||
|
|
flag_para = paras[duo_one_hang]
|
|||
|
|
flag_para_rear = flag_para.split('通过仿真')[-1]
|
|||
|
|
# 1.2.多对一情况 - 看“通过仿真”在第几行,且后续没有行
|
|||
|
|
if duo_one_hang > 1 and duo_one_hang == len(paras) - 1:
|
|||
|
|
prefix = paras[0]
|
|||
|
|
for pa in paras[1:duo_one_hang]:
|
|||
|
|
content_list.append({
|
|||
|
|
'content': (prefix + pa).strip(";;.。,, "),
|
|||
|
|
'yuqi': '通过仿真' + flag_para_rear.strip(";;.。,, ")
|
|||
|
|
})
|
|||
|
|
# 1.3.一对多情况 - 看“通过仿真”如果在第一行且后续有多行
|
|||
|
|
elif duo_one_hang == 0 and len(paras) - 1 > duo_one_hang:
|
|||
|
|
# 1.3.1.如果有2段,第一段是仿真,第二段是实物测试 - 判断第二段是否是实物测试
|
|||
|
|
if '实物测试' in paras[1]:
|
|||
|
|
for pa in paras:
|
|||
|
|
# 每一行就是一个步骤,“查看”分割
|
|||
|
|
content_list.append({'content': pa.split('查看')[0] + '查看', 'yuqi': pa.split('查看')[1].strip(";;.。,, ")})
|
|||
|
|
# 1.3.1 特殊,工况处理
|
|||
|
|
elif '功耗分析' in paras[0]:
|
|||
|
|
ft = '仿真测试环境下,使用设计检查的方法,在开发环境中设置以下工况,通过功耗分析报告查看功耗情况'
|
|||
|
|
for pa in paras[1:]:
|
|||
|
|
content_list.append({'content': ft.replace('以下工况', pa.strip(";;.。,, ")), 'yuqi': '功耗报告中功耗情况符合要求'})
|
|||
|
|
else:
|
|||
|
|
content = paras[0].split('通过仿真')[0]
|
|||
|
|
hou_prefix = paras[0].split('通过仿真')[-1]
|
|||
|
|
for pa in paras[1:len(paras)]:
|
|||
|
|
content_list.append({'content': content.strip(";;.。,, "), 'yuqi': (hou_prefix + pa).strip(";;.。,, ")})
|
|||
|
|
# 1.4.多对多情况 - 看“通过仿真”如果在第一行且后续有多行
|
|||
|
|
elif duo_one_hang > 1 and len(paras) - 1 > duo_one_hang:
|
|||
|
|
prefix = paras[0]
|
|||
|
|
for pa in paras[1:duo_one_hang]:
|
|||
|
|
content_list.append({
|
|||
|
|
'content': (prefix + pa).strip(";;.。,, "),
|
|||
|
|
'yuqi': '通过仿真' + ("".join(paras[duo_one_hang:]).strip(";;.。,,:: "))
|
|||
|
|
})
|
|||
|
|
# 1.1.一对一情况 - 简单通过“通过仿真”字样分割
|
|||
|
|
else:
|
|||
|
|
|
|||
|
|
# 1.X.1.第一个配置项特殊处理器边界,第二个配置项正常
|
|||
|
|
if '边界测试' in paras[0]:
|
|||
|
|
self.logger.push("注意程序对边界测试的步骤拆分可能有错误,请检查!!!")
|
|||
|
|
for pa in paras[1:]:
|
|||
|
|
front = pa.split('查看')[0].strip(";;.。,,:: ") + '查看'
|
|||
|
|
rear = pa.split('查看')[1].strip(";;.。,,:: ")
|
|||
|
|
content_list.append({'content': front, 'yuqi': rear})
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
content_list = normal_parse_content(paras)
|
|||
|
|
|
|||
|
|
# 2.大类实物测试环境
|
|||
|
|
elif '实物测试' in paras[0]:
|
|||
|
|
# 2.2.如果实物测试多行,则第一行为前缀,后面每行一个步骤
|
|||
|
|
if len(paras) > 1:
|
|||
|
|
pre = paras[0].strip(";;.。,,:: ")
|
|||
|
|
for paragraph in paras[1:]:
|
|||
|
|
front = paragraph.split('查看')[0].strip(";;.。,,:: ")
|
|||
|
|
rear = paragraph.split('查看')[-1].strip(";;.。,,:: ")
|
|||
|
|
content_list.append({'content': pre + front, 'yuqi': rear})
|
|||
|
|
# 2.1.一对一情况 - 通过“通过”字样分割
|
|||
|
|
else:
|
|||
|
|
content_list = normal_parse_content(paras, '查看')
|
|||
|
|
|
|||
|
|
# 3.大类设计检查方法 - 直接遍历每行,每行一个步骤
|
|||
|
|
elif '设计检查' in paras[0] or '静态时序' in paras[0]:
|
|||
|
|
for para in paras:
|
|||
|
|
rear = para.split('查看')[-1].replace("是否", "").strip(";;.。,,:: ")
|
|||
|
|
content_list.append({'content': para.strip(";;.。,,:: "), 'yuqi': rear})
|
|||
|
|
elif '时序仿真' in paras[0]:
|
|||
|
|
content_list = normal_parse_content(paras, '查看')
|
|||
|
|
else:
|
|||
|
|
print(f"{self.ident},{self.name}###测试项写法有错,请修改后再转换...")
|
|||
|
|
return content_list
|
|||
|
|
|
|||
|
|
# 重点函数 - cpu大纲转说明
|
|||
|
|
def __parse_step_cpu(self, paras: List[str]):
|
|||
|
|
content_list = []
|
|||
|
|
for para in paras:
|
|||
|
|
content_list.append({'content': para.strip("123456789)),.,。;;::"), 'yuqi': '各测试步骤、测试用例执行结果与预期一致,功能实现正确'})
|
|||
|
|
return content_list
|
|||
|
|
|
|||
|
|
class TableItem:
|
|||
|
|
def __init__(self, title: str, content_list, cIdx, father_ident, zongsu, csh, logger: ui.log, has_r1 = True) -> None:
|
|||
|
|
# 属性 - name用例名称
|
|||
|
|
self.name = title
|
|||
|
|
global_data.progress_value += 0.01 # 耦合
|
|||
|
|
# 输出日志记录和进度条
|
|||
|
|
logger.push(f"目前解析测试项为:{self.name},其大纲标识为:{father_ident}")
|
|||
|
|
# 用例初始化
|
|||
|
|
self.csh = csh
|
|||
|
|
# 属性 - step
|
|||
|
|
self.step = []
|
|||
|
|
for i, line in enumerate(content_list):
|
|||
|
|
# content是数组,每一项是字典,包含'content'和'yuqi'
|
|||
|
|
self.step.append({'counter': i + 1, 'content': line['content'], 'yuqi': line['yuqi']})
|
|||
|
|
# case的ident需要进步
|
|||
|
|
if has_r1:
|
|||
|
|
self.ident = "R1_" + father_ident.replace("XQ", "YL") + "_" + str(cIdx + 1)
|
|||
|
|
else:
|
|||
|
|
self.ident = father_ident.replace("XQ", "YL") + "_" + str(cIdx + 1)
|
|||
|
|
# 属性 - 综述
|
|||
|
|
self.zong = zongsu
|