软件测试课程设计——tcas的测试脚本

软件测试课程设计——tcas的测试脚本

  • 背景参考
    • 收集对单个版本所有用例的测试数据
    • 使用diff判断的python3文件:
  • 1.Linux命令及shell语言脚本
    • 使用gcov工具完成单个用例的分析
  • 2.python3生成每个版本的测试脚本
  • 3.使用numpy生成矩阵
  • 4.结果保存
  • 反思
    • 1.运行效率
    • 2.编程习惯
    • 3.Remind
  • 完整代码地址:
  • Tips
    • Q1:无法将在windows内的文件复制/拖动到虚拟机的Linux系统中
    • Q2:报错 tcas.gcno:cannot open notes file
    • Q3:Linux -cp命令遇到同名文件会替换吗
    • Q4:读取line最后一个字符[-1]出现仍完整读取文本,仅除去line后空格
    • Q5:报错 notes file busy

背景参考

首先,选择tcas作为测试项目,下载地址:
SIR内的tcas文件

tcas的内部文件中version.alt包含了不同错误版本的待测文件,source为待执行代码存放的文件夹,outputs为执行后的结果文件夹,script为各类脚本所在文件,建议按照tcas本来的存放格式保存各种输出结果,不要随意存放,这既是为了美观,也为了减少错误。

具体tcas的内容参考:
https://blog.csdn.net/mangoer_ys/article/details/25974067

阅读作用很大,我最先也按照文中编写测试脚本,因为无法满足读取每个语句被某个版本的多少用例执行这一数据,所以决定自己编写脚本。建议先阅读这篇文章。

收集对单个版本所有用例的测试数据

如果只想收集所有用例累加的测试数据(这里的累加解释一下,t1对一个语句执行2次,t2对这个语句执行1次,那么采用如下方法只会记录3次,而项目要求是,因为有2个用例执行这语句,所以应记为2次。)如果你希望得到累加数据,采用以下语句:

gcc -fprofile-arcs -ftest-coverage -o tcas.exe tcas.c   #生成文件.exe .gcno
./runall										#运行脚本,生成.gcda
gcov -b tcas.c								#分支覆盖,生成.gcov
lcov -d . -c -o r1.info							#收集信息到一个文件
genhtml -o results r1.info					#获取HTML输出,看到语句覆盖 函数覆盖

在version.alt的每个版本内命令行执行第一句,将编译好的tcas.exe复制到source,执行tcas自带的script的runall.sh即第二句,再执行后三句,可以获得绝大部分信息,如语句覆盖率,分支覆盖率,函数覆盖率,每个语句的累加测试次数等,并以html文件展示。但与项目要求相比缺少三点:
1.测试数据矩阵
2.单个语句的非累加次数
3.正确,错误的用例个数(这简单用diff语句对输出文件进行循环判断即可)
因此,需要自己编写脚本。

使用diff判断的python3文件:

import os
VER_NUM = 3
CASE_NUM = 1608
TIMES_TRUE = 0
TIMES_FALSE = 0
for i in range(1, VER_NUM+1):
	for j in range(1, CASE_NUM +1):
		tmp = os.system("diff /home/qjm/tcas/outputs/v0/t" + str(j)+ /home/qjm/tcas/outputs/v" + str(i) + "/t" + str(j) + ">/dev/null")
		if tmp != 0:
			TIMES_FALSE = TIMES_FALSE + 1
		else:
			TIMES_TRUE = TIMES_TRUE + 1
	print("Version",i,"     True: ",TIMES_TRUE,"False: ",TIMES_FALSE)
	TIMES_FALSE = 0
	TIMES_TRUE = 0

1.Linux命令及shell语言脚本

sh语言我也是第一次使用,大概好处是可以直接运行linux命令。
原有脚本(runall.sh):

echo script type: R
echo ">>>>>>>>running test 1"
../source/tcas.exe  958 1 1 2597  574 4253 0  399  400 0 0 1     > ../outputs/t1
echo ">>>>>>>>running test 2"
../source/tcas.exe  627 0 0  621  216  382 1  400  641 1 1 0     > ../outputs/t2
echo ">>>>>>>>running test 3"
../source/tcas.exe  549 1 1 4398  133 1445 1  641  639 0 0 1     > ../outputs/t3
echo ">>>>>>>>running test 4"
../source/tcas.exe  576 0 1 3469  183  381 2  641  501 1 0 1     > ../outputs/t4
echo ">>>>>>>>running test 5"
../source/tcas.exe  992 1 0 3342   23 4657 1  640  741 0 0 0     > ../outputs/t5
echo ">>>>>>>>running test 6"
../source/tcas.exe  548 0 1   34  542 3514 2  499  401 1 1 1     > ../outputs/t6
echo ">>>>>>>>running test 7"
../source/tcas.exe  710 0 0  127  403 4616 3  500  400 0 0 0     > ../outputs/t7

(未完整摘录,共1608个测试用例)
runall.sh内容为将测试用例输入source文件中的tcas.exe,输出结果则存放到outputs中。
运行方式:
进入script,进入终端,输入:

./runall.sh

使用gcov工具完成单个用例的分析

背景博客已经做过介绍,不再重复。为了实现单个用例的数据收集,需要为每个用例生成gcov文件。这里由于脚本在script文件夹运行,收集数据的gcov文件会存放在script中,需要删除。

gcc -fprofile-arcs -ftest-coverage -o ../versions.alt/versions.orig/v5/tcas.exe ../versions.alt/versions.orig/v5/tcas.c
cp ../versions.alt/versions.orig/v5/tcas.exe ../source
mv ../scripts/tcas.gcno ../versions.alt/versions.orig/v5
echo ">>>>>>>>running test 1"
../source/tcas.exe  958 1 1 2597  574 4253 0  399  400 0 0 1     > ../outputs/t1
mv ../scripts/tcas.gcda ../versions.alt/versions.orig/v5
gcov -b ../versions.alt/versions.orig/v5/tcas.c > ../versions.alt/versions.orig/v5/t1
lcov -d ../versions.alt/versions.orig/v5/ -c -o ../versions.alt/versions.orig/v5/r1.info
rm tcas.c.gcov
echo ">>>>>>>>running test 2"
../source/tcas.exe  627 0 0  621  216  382 1  400  641 1 1 0     > ../outputs/t2
mv ../scripts/tcas.gcda ../versions.alt/versions.orig/v5
gcov -b ../versions.alt/versions.orig/v5/tcas.c > ../versions.alt/versions.orig/v5/t2
lcov -d ../versions.alt/versions.orig/v5/ -c -o ../versions.alt/versions.orig/v5/r2.info
rm tcas.c.gcov

其中gcc语句进行编译。-fprofile-arcs -ftest-coverage参数为了生成数据文件。gcov和lcov语句收集测试数据。

这个脚本用于收集两个用例的测试数据信息,并存放在/versions.alt/versions.orig/v5/的info文件中。查看对应的文件,发现对这两个用例分别进行gcov数据收集不会产生数据覆盖,这为我们得到单个用例的测试数据打好第一步。

2.python3生成每个版本的测试脚本

首先本文采用python3进行代码编写,未安装的需先进行安装:

#安装python3
sudo apt install python3

#安装pip3
sudo apt install python3-pip 

#安装numpy库
pip3 install numpy 

下面生成每个版本的测试脚本,以版本号命名为runv1,runv2…

import os
import numpy as np
version = 1
test = 1


f = open("./runall.sh")
script = open("runv"+str(version),'w+')
oldscript = f.readlines()


script.write("gcc -fprofile-arcs -ftest-coverage -o ../versions.alt/versions.orig/v"+str(version)+"/tcas.exe ../versions.alt/versions.orig/v"+str(version)+"/tcas.c\n")
script.write("cp ../versions.alt/versions.orig/v"+str(version)+"/tcas.exe ../source\n")
script.write("mv ../scripts/tcas.gcno ../versions.alt/versions.orig/v"+str(version)+"\n")
script.write("mkdir ../outputs/v"+str(version)+"\n")
for line in oldscript:
	if(not line.startswith("echo")):
		script.write(line.replace('../outputs', '../outputs/v'+str(version)))
		script.write("mv ../scripts/tcas.gcda ../versions.alt/versions.orig/v"+str(version)+"\n")
		script.write("gcov -b ../versions.alt/versions.orig/v"+str(version)+"/tcas.c > ../versions.alt/versions.orig/v"+str(version)+"/t"+str(test)+"\n")
		script.write("lcov -d ../versions.alt/versions.orig/v"+str(version)+"/ -c -o ../versions.alt/versions.orig/v"+str(version)+"/r"+str(test)+".info >/dev/null\n")
		test += 1
f.close()
script.close()
os.system('chmod 777 runv'+str(version))
os.system('./runv'+str(version))

mkdir用于创建存放测试用例的输出结果的文件夹,仍按照版本号命名为v1,v2…
对这个脚本不必输出running test case 1之类反馈信息(私以为很蠢2333用处可能是防止误以为宕机,即“我在运行!”的提示),所以执行如下判断,仅将用例数据写入新脚本。

if(not line.startswith("echo")):
> chmod 777 runv1			#授予权限,因为这个脚本需要读写其他文本数据
> ./runv1							#执行脚本

聪明的你一定发现了,version保存了版本号,加上类似

for version in range(1,49):

这样的循环不就可以一下写出48个版本的脚本了吗?这个想法显然正确,但笔者选择用version记录版本号,每次测试时修改version的值也有原因,因为每个版本的脚本加上后面部分需要执行约4分钟,全部执行则有48*4分钟,与其用3个小时等待运行结果,不如每次修改version,4分钟一次的运行来的安逸。这里的内容后面会提及。

3.使用numpy生成矩阵

矩阵定义:
软件测试课程设计——tcas的测试脚本_第1张图片
我并未写上failing/passing列,实际实现也很容易,采用上文的diff稍作修改即可,不详细贴出。因为笔者觉得最后一列没有必要,正确错误的用例数很好得出,不必多写一列。

matrix_gengerator.py:

test_begin = 1
test_end   = 1608
statement_num = 65
cursor = 0
matrix = np.zeros((1609,65))
for i in range(test_begin, test_end + 1):
	read_file = open("../versions.alt/versions.orig/v"+str(version)+"/r"+str(i)+".info")
	info = read_file.readlines()
	for line in info:
		if(line.startswith("DA:")):
			matrix[i-1][cursor] = int(line.strip()[-1])
			cursor = cursor + 1
	cursor = 0
	read_file.close()

#for the statements are executed by how many tests in one version
#即最后一行s1,s2....(渣渣英语)
for cursor in range(0,65):
	for test_num in range(0,1608):
		if(matrix[test_num][cursor] != 0):
			matrix[-1][cursor] = matrix[-1][cursor] + 1
save_file = open("../versions.alt/versions.orig/v"+str(version)+"/matrix","w+")
np.savetxt("../versions.alt/versions.orig/v"+str(version)+"/matrix",matrix,fmt='%d')
save_file.close()

其中,

	for line in info:
		if(line.startswith("DA:")):
			matrix[i-1][cursor] = int(line.strip()[-1])
			cursor = cursor + 1
	cursor = 0

这段用于读取info文件,写入矩阵,DA表示
DA: <代码行号>, <当前行被执行到的次数>
具体info文件参考:https://blog.csdn.net/yanxiangyfg/article/details/80989680
对gcov介绍的很详细
line.strip()[-1]可以读取DA这行除了空格之外的最后一位数字,单个用例对单个语句显然不会执行超过10次,读取最后一位即可。

np.savetxt("../versions.alt/versions.orig/v"+str(version)+"/matrix",matrix,fmt='%d')

将矩阵写入文件,实际上上下两句开关文件操作不必要。这句展现了python的优越性,numpy库使写入矩阵变得多么轻松。fmt=‘%d’使得矩阵以整数形式写入,否则会以科学计数法储存,可以尝试看看会有多么不美观。

4.结果保存

很简单的一步,通过矩阵得到覆盖率,正确及错误的用例数,以及最重要的,平均最多最少的每个版本的单个语句被执行次数。

false_num = 0
true_num = 0
LF = 65
LH = 0
max_num = 0
min_num = 1608
sum_num = 0
mean_num = 0
for i in range(1, 1609):
	tmp = os.system("diff ../outputs/v0/t"+str(i)+" ../outputs/v"+str(version)+"/t" + str(i) + ">/dev/null")
	if tmp != 0:
		false_num += 1
	else:
		true_num += 1

for i in range(0,LF):
	if(matrix[-1][i-1] != 0):
		LH += 1	
	if(matrix[-1][i-1] > max_num):
		max_num = matrix[-1][i-1]
	if(matrix[-1][i-1] < min_num):
		min_num = matrix[-1][i-1]
	sum_num += matrix[-1][i-1]
linecov = round(LH/LF,4)
mean_num = round(sum_num/LF,2)

r = open("../versions.alt/versions.orig/v"+str(version)+"/result","w+")
r.write("True test number: "+str(true_num)+"\nFalse test number: "+str(false_num))
r.write("\nThe total statements: "+str(LF)+"\nThe statements executed: "+str(LH))
r.write("\nThe line coverage: "+("%.2f%%" % (linecov * 100)))
r.write("\nMax number of executed times: "+str(max_num))
r.write("\nMin number of executed times: "+str(min_num))
r.write("\nMean number of execute times: "+str(mean_num))
r.close()

round函数的第二个参数定义商的精确度,"%.2f%%" % (linecov * 100))完成小数到百分数的转换。

结果如图:

True test number: 1473
False test number: 135
The total statements: 65
The statements executed: 64
The line coverage: 98.46%
Max number of executed times: 1608.0
Min number of executed times: 0.0
Mean number of execute times: 1026.85

反思

1.运行效率

其实之所以每个脚本运行时间很长,大约4分钟,主要是因为

lcov -d ../versions.alt/versions.orig/v"+str(version)+
"/ -c -o ../versions.alt/versions.orig/v"+str(version)
+"/r"+str(test)+".info >/dev/null\n

lcov提供了形成info信息的功能,但是实际上不必进行这一步,gcov语句形成的tcas.c.gcov文件就够了。读入文件写入矩阵时做个判断,该行以数字开头,读入该行,对该行首个数字切片,写入矩阵。这样就能大大提高效率,因此可以再对整个python添加版本循环,使得一次运行就能产生所有结果文件,省时省力,供读者自行添加。

2.编程习惯

可以将变量名统一,更优雅
可以加少,合并循环,会更优雅

3.Remind

这篇文章如果偶然被人读到,我真诚的希望你不要将它直接用作作业提交,一是重复率太高你的项目不会通过,二是这篇文章我自认写得很详细,好好阅读你一定能写出超过本文的测试脚本。所以点击下面链接前,请注意参考即可,不要复制后作为作业提交。

完整代码地址:

如果感觉有用请star一下哦!

Tips

编写过程中不值一提的小问题总结。

Q1:无法将在windows内的文件复制/拖动到虚拟机的Linux系统中

A:安装VMware Tools,VMware选项卡->虚拟机->安装VMware Tools,这个工具也能解决无法全屏显示Linux的问题。

Q2:报错 tcas.gcno:cannot open notes file

原因:gcda,gcno与gcov不在同一目录
解决:-mv …/scripts/tcas.gcno …/versions.alt/versions.orig/v5

Q3:Linux -cp命令遇到同名文件会替换吗

A:会!

Q4:读取line最后一个字符[-1]出现仍完整读取文本,仅除去line后空格

解决:write(line.strip()[-1])

Q5:报错 notes file busy

原因:一般是未关闭文件
解决:f.close

你可能感兴趣的:(linux,python,shell,numpy库,软件测试)