白盒测试之基本路径测试法
2012-11-08 21:00
288 查看
有更多的实现方法,这里只是分享思路,拒绝直接使用源码从事一切学习作业相关的提交!!!
白盒测试的一种常用方法是基本路径法,根据源代码构造程序流程图,转换为控制流程图,得到基本路径,进而为每条基本路径设计测试用例。基本路径法的一个关键步骤是识别出所有的基本路径。
下面是测试用例:
下面的代码已经不是最新的,每次完善(修复BUG,增加功能)后的代码均已托管:whiteBoxTestExtendArc.cpp!!!
左侧给出的是控制流图,相应的输入数据如右侧所示。
(支持大于2位的结点信息,测试样例请参考:https://github.com/luofei2011/White-box-testing --->test.txt)
输出格式如下:
其中CC为圈的复杂度,而且每条路径按照长度大小的原则排序,长度相同的比较各结点进行排序。
*2012-11-10更新:对任何一个或者多个判定结点可以附加OR or AND,以此来生成若干新行。
*2012-11-12更新:优化完善遍历算法,缩短遍历时间。优化改善排序算法,修复已知BUG。
输入数据中除了边信息之外,后续增加若干新行(与原有各行之间通过一行“EXT”分隔),每一行表示控制流图中的判断节点信息,例如:
1, AND
3, OR
分别表示节点1是 if (A && B) 的形式、节点3是if (A || B)的形式。
只考虑and/or两种逻辑运算符,且只考虑包含两个单一条件表达式(A, B)的复合的情况。
为应对上述情况,你的程序应对原有控制流图进行修改,使之变为标准的控制流图的形式,再生成圈复杂度和基本路径作为输出。修改控制流图后若需要引入新的节点,其编号可采用11、12、31、32(为消除BUG,只支持分离后的结点不会与原始结点重名:比如不会出现原来有一个叫42的结点,现在分离出来一个名为42的结点)来表示。
以下给出了一个例子(AND),前一个是输入,后一个为输出。
再给出一个OR的例子:
实现方法:
1.数据结构采用有向图的十字链表存储。
十字链表(Orthogonal List)是有向图的另一种链式存储结构,可以看成是将有向图的邻接表和逆邻接表结合起来得到的一种链表。------>摘自《数据结构(C语言版)--清华大学出版》
我们可以理解为将结点和弧连接起来的“网”,并且其中的每个结点都是“非零”的元素。
可以这样想:
用链表模拟矩阵的行(或者列,这可以根据个人喜好来定),然后,再构造代表列的链表,将每一行中的元素节点插入到对应的列中去。十字链表的逻辑结构就像是一个围棋盘(没见过,你就想一下苍蝇拍,这个总见过吧),而非零元就好像是在棋盘上放的棋子,总共占的空间就是,确定那些线的表头节点和那些棋子代表的非零元节点。最后,我们用一个指针指向这个棋盘,这个指针就代表了这个稀疏矩阵。
在十字链表中,对应于有向图中每一条弧有一个结点,对应于每个顶点也有一个结点,这些结构如下:
弧结点中:
尾域(tailvex):弧尾所在位置;
头域(headvex):弧头所在位置;
hlink,tlink:分别指向弧头/尾相同的下一条弧(在本次遍历过程中很有用);
info:该弧信息(本次项目中用来存储T/F/N).
头结点:
data:顶点相关的信息;
firstin,firstout:一该结点为弧头/尾的“第一个”结点。
以下是一个例子:
2.寻找基本路径采用非递归的深度优先搜索实现(DFSTraverse)。
基本思路是:
从头结点开始(需要找到头结点),采用深度优先搜索策略,若遇到判定结点,需“先F,后T”;若“N”则直接正常处理,这里的“先F,后T”就需要把T弧“压栈”(我只用数组实现),当循环到没有以当前结点为弧尾时则停止深度搜索->弹栈。或者当遇到“圈”时,代表路径重复,需要停止深度搜索--->弹栈。如此便能遍历该有向图。(这是最终版本,基本发现的BUG,错误均已修复!)
最新代码:https://github.com/luofei2011/White-box-testing
About Me:luofeihit2010@gmail.com
白盒测试的一种常用方法是基本路径法,根据源代码构造程序流程图,转换为控制流程图,得到基本路径,进而为每条基本路径设计测试用例。基本路径法的一个关键步骤是识别出所有的基本路径。
下面是测试用例:
下面的代码已经不是最新的,每次完善(修复BUG,增加功能)后的代码均已托管:whiteBoxTestExtendArc.cpp!!!
左侧给出的是控制流图,相应的输入数据如右侧所示。
(支持大于2位的结点信息,测试样例请参考:https://github.com/luofei2011/White-box-testing --->test.txt)
输出格式如下:
其中CC为圈的复杂度,而且每条路径按照长度大小的原则排序,长度相同的比较各结点进行排序。
*2012-11-10更新:对任何一个或者多个判定结点可以附加OR or AND,以此来生成若干新行。
*2012-11-12更新:优化完善遍历算法,缩短遍历时间。优化改善排序算法,修复已知BUG。
输入数据中除了边信息之外,后续增加若干新行(与原有各行之间通过一行“EXT”分隔),每一行表示控制流图中的判断节点信息,例如:
1, AND
3, OR
分别表示节点1是 if (A && B) 的形式、节点3是if (A || B)的形式。
只考虑and/or两种逻辑运算符,且只考虑包含两个单一条件表达式(A, B)的复合的情况。
为应对上述情况,你的程序应对原有控制流图进行修改,使之变为标准的控制流图的形式,再生成圈复杂度和基本路径作为输出。修改控制流图后若需要引入新的节点,其编号可采用11、12、31、32(为消除BUG,只支持分离后的结点不会与原始结点重名:比如不会出现原来有一个叫42的结点,现在分离出来一个名为42的结点)来表示。
以下给出了一个例子(AND),前一个是输入,后一个为输出。
再给出一个OR的例子:
实现方法:
1.数据结构采用有向图的十字链表存储。
十字链表(Orthogonal List)是有向图的另一种链式存储结构,可以看成是将有向图的邻接表和逆邻接表结合起来得到的一种链表。------>摘自《数据结构(C语言版)--清华大学出版》
我们可以理解为将结点和弧连接起来的“网”,并且其中的每个结点都是“非零”的元素。
可以这样想:
用链表模拟矩阵的行(或者列,这可以根据个人喜好来定),然后,再构造代表列的链表,将每一行中的元素节点插入到对应的列中去。十字链表的逻辑结构就像是一个围棋盘(没见过,你就想一下苍蝇拍,这个总见过吧),而非零元就好像是在棋盘上放的棋子,总共占的空间就是,确定那些线的表头节点和那些棋子代表的非零元节点。最后,我们用一个指针指向这个棋盘,这个指针就代表了这个稀疏矩阵。
在十字链表中,对应于有向图中每一条弧有一个结点,对应于每个顶点也有一个结点,这些结构如下:
弧结点中:
尾域(tailvex):弧尾所在位置;
头域(headvex):弧头所在位置;
hlink,tlink:分别指向弧头/尾相同的下一条弧(在本次遍历过程中很有用);
info:该弧信息(本次项目中用来存储T/F/N).
头结点:
data:顶点相关的信息;
firstin,firstout:一该结点为弧头/尾的“第一个”结点。
以下是一个例子:
2.寻找基本路径采用非递归的深度优先搜索实现(DFSTraverse)。
基本思路是:
从头结点开始(需要找到头结点),采用深度优先搜索策略,若遇到判定结点,需“先F,后T”;若“N”则直接正常处理,这里的“先F,后T”就需要把T弧“压栈”(我只用数组实现),当循环到没有以当前结点为弧尾时则停止深度搜索->弹栈。或者当遇到“圈”时,代表路径重复,需要停止深度搜索--->弹栈。如此便能遍历该有向图。(这是最终版本,基本发现的BUG,错误均已修复!)
#include<iostream> #include<stdlib.h> #include<malloc.h> #include<string> #include<cstring> #define MAX 512 //允许输入的最大结点数 using namespace std; //char arcArr[MAX][MAX]={'\0'}; int arcTail[MAX]={0}; int arcHead[MAX]={0}; char Judge[MAX]={'\0'}; int vexArr[MAX]={0}; int basic_path[MAX];//存储基本路径,并实时更新 int CC = 0; //圈复杂度:(1)判定结点数+1;(2)e(边)-n(结点)+2 char is_EXT = 'N'; string order_out = ""; //string ordered = ""; typedef struct arcNode{ int tail,head; //弧的尾结点和头结点的位置 struct arcNode *hlink, *tlink; //弧头或弧尾相同的弧链表 char judge; //T/F/N }arcNode; typedef struct vexNode{ int data; bool visited; arcNode *fin, *fout; //分别指向该顶点的第一条入弧和出弧 }vexNode; typedef struct{ vexNode list[MAX]; //表头向量顶点列表 int vnum, anum; //有向图当前的顶点和弧数 }Graph; bool is_end(string str){ // transform(str.begin(),str.end(), str.begin(), ::toupper); if(str == "END" || str == "end" || str == "EXT" || str == "ext") return true; return false; } int length(int num[]){ int sum=0; for(int i=0; i<MAX; i++){ if(num[i] != 0) sum++; else break; } return sum; } bool is_inArr(int ch[],int in){ for(int i=0; i<length(ch); i++) if(in == ch[i]) return true; return false; } void insert(string str){ int pos = length(vexArr); int i = length(arcTail); string _tail = str.substr(0,str.find('-')); string _head = str.substr(str.find('>')+1,str.find(',')-str.find('>')-1); char _judge = str[str.size()-1]; if(!is_inArr(vexArr, atoi(_tail.c_str()))) vexArr[pos++] = atoi(_tail.c_str()); if(!is_inArr(vexArr, atoi(_head.c_str()))) vexArr[pos++] = atoi(_head.c_str()); arcTail[i] = atoi(_tail.c_str()); arcHead[i] = atoi(_head.c_str()); Judge[i] = _judge; } int LocateVex(Graph *G, int vertex){ int j=0,k; for(k=0; k<length(vexArr); k++) if(G->list[k].data == vertex){ j = k; break; } return j; } void CreateGraph(Graph *G){ //初始化顶点列表 int _tail, _head; arcNode *arcTemp; G->vnum = G->anum =0; for(int i=0; i<length(vexArr); i++){ (*G).list[i].data = vexArr[i]; G->list[i].fin = NULL; G->list[i].fout = NULL; G->vnum++; G->list[i].visited = false; } //初始化弧并构建十字链表 for(int i=0; i<length(arcTail); i++){ if(Judge[i] == 'T') CC ++; _tail = LocateVex(G,arcTail[i]); _head = LocateVex(G,arcHead[i]); arcTemp = (arcNode*)malloc(sizeof(arcNode)); arcTemp->tail = _tail; arcTemp->head = _head; arcTemp->tlink = (*G).list[_tail].fout; (*G).list[_tail].fout = arcTemp; arcTemp->hlink = (*G).list[_head].fin; (*G).list[_head].fin = arcTemp; arcTemp->judge = Judge[i]; G->anum++; } } void display(Graph *G,int arr[]){ char ch[10]; int temp =0; char is_no = 'N'; for(int i=0; i<length(arr)-1; i++){ string Arc = ""; temp = arr[i]; snprintf(ch,sizeof(ch),"%d",temp); Arc += ch; Arc += ","; temp = arr[i+1]; snprintf(ch,sizeof(ch),"%d",temp); Arc += ch; if(order_out.find(Arc) != order_out.npos) continue; else{ is_no = 'Y'; break; } i += 1; } if(is_no == 'Y'){ memset(ch,0,10); for(int i=0; i<length(arr)-1; i++){ temp = arr[i]; snprintf(ch,sizeof(ch),"%d",temp); order_out += ch; order_out += ","; } memset(ch,0,10); snprintf(ch,sizeof(ch),"%d",arr[length(arr)-1]); order_out += ch; order_out += "|"; } } int my_memset(int arr[],int _data){ int pos = 0; int _length = length(arr); for(int i=0; i<_length; i++) if(_data == arr[i]){ pos = i; break; } //不能使用i<length(arr)清空arr的时候length(arr)就会改变 for(int i=pos+1; i<_length; i++) arr[i] = 0; //display(arr); return pos; } void changeVisited(Graph *G,int arr[], int num){ char is_find_ = 'N'; int pos =0; for(int i=0; i<length(arr); i++){ if(arr[i] == num){ is_find_ = 'Y'; i++; } if(arr[i] != 0) if(is_find_ == 'Y'){ pos = LocateVex(G,arr[i]); // cout << "pos:" << pos << endl; G->list[pos].visited = false; // cout << "data:" << G->list[pos].data << endl; } } } //有向图十字链表结构的深度优先非递归遍历 void DFSTraverse(Graph G, int pos){ arcNode *p; // int _pos = 0; //记录不同路径的分离点 arcNode *Queue[MAX*MAX]; int Vex[MAX]={0}; int _len_=0; int queue = -1; int pointer = -1; p = G.list[pos].fout; basic_path[++pointer] = G.list[pos].data; if(!p){ display(&G,basic_path); return; } while(p){ if(!is_inArr(basic_path,G.list[p->tail].data)) basic_path[++pointer] = G.list[p->tail].data; if(p->judge != 'N'){ //若遇到先前遍历过的结点则直接走F就行,T不予考虑 if(is_inArr(Vex,G.list[p->tail].data) && (!G.list[p->tail].visited)){ if(p->judge == 'T') p = p->tlink; } else{ if((p->judge == 'T') && (!G.list[p->tail].visited)){ // if(p->judge == 'T'){ Queue[++queue] = p; p = p->tlink; } else if(p->judge == 'F') Queue[++queue] = p->tlink; G.list[p->tail].visited = true; if(!is_inArr(Vex,G.list[p->tail].data)) Vex[_len_++] = G.list[p->tail].data; } } if(is_inArr(basic_path,G.list[p->head].data)){ basic_path[++pointer] = G.list[p->head].data; if(queue >= 0){ display(&G,basic_path); p = Queue[queue]; queue --; pointer = my_memset(basic_path,G.list[p->tail].data); changeVisited(&G,Vex,G.list[p->tail].data); } //不加else会导致死循环,因为到最后一层的时候数组为空。直接把头结点加入到循环队列中 else{ display(&G,basic_path); break; } continue; } basic_path[++pointer] = G.list[p->head].data; pos = LocateVex(&G,G.list[p->head].data); p = G.list[pos].fout; if(!p){ if(queue >= 0){ display(&G,basic_path); p = Queue[queue]; queue --; pointer = my_memset(basic_path,G.list[p->tail].data); changeVisited(&G,Vex,G.list[p->tail].data);//每次出栈需要改变后续结点的访问visited值 } else{ display(&G,basic_path); break; } } }/* for(int i=0; i<length(Vex);i++) cout << Vex[i]; cout << endl;*/ } int returnNum(string str,char ch){ int sum =0; while(str.size()){ int pos = str.find(ch); if(pos != str.npos){ str = str.substr(pos+1); sum++; } else break; } return sum+1; } void remove_(string str){ int pos = order_out.find(str); order_out.replace(pos,str.size()+1,""); } //基本路径结点大小排序(路径长度相同的情况下) bool my_compare(string str1,string str2){ //cout << str1 << endl; //cout << str2 << endl; str1 += ","; str2 += ","; string temp1 = ""; string temp2 = ""; int pos1 = str1.find(','); int pos2 = str2.find(','); while(str1.size()){ temp1 = str1.substr(0,pos1); temp2 = str2.substr(0,pos2); if(atoi(temp1.c_str()) > atoi(temp2.c_str())) return true; else if(atoi(temp1.c_str()) < atoi(temp2.c_str())) return false; str1 = str1.substr(pos1+1); pos1 = str1.find(','); str2 = str2.substr(pos2+1); pos2 = str2.find(','); } } void orderDisplay(string str){ //cout << "CC=" << returnNum(str,'|')-1 << endl; string ordered = ""; string _str = str; string small = str; int small_num = 100; string temp = ""; char is_end = 'N'; int pos = str.find('|'); while(str.size()){ for(int i=0; i<_str.size(); i++){ temp = _str.substr(0,pos); if(returnNum(temp,',') < small_num){ small = temp; small_num = returnNum(temp,','); } else if(returnNum(temp,',') == small_num) if(my_compare(small,temp)){ small = temp; small_num = returnNum(temp,','); } if(is_end == 'N'){ _str = _str.substr(pos+1); pos = _str.find('|'); if(pos != _str.npos) continue; else{ pos = _str.size(); is_end = 'Y'; } } else break; } //cout << small << endl; ordered += small; ordered += "|"; int _pos = str.find(small); str.replace(_pos,small.size()+1,""); _str = small = str; small_num =100; pos = str.find('|'); is_end = 'N'; } order_out = ordered; } void coutResult(string str){ int pos = str.find('|'); string temp; while(str.size()){ temp = str.substr(0,pos); cout << temp << endl; str = str.substr(pos+1); pos = str.find('|'); } } void changeVexArr(int num){ for(int i=0; i<length(vexArr); i++) if(vexArr[i] == num){ vexArr[i] = num*10 + 1; vexArr[length(vexArr)] = num*10 + 2; break; } } void CreatePath(string str){ //当输入数据中含有AND,OR时需在原有弧的基础上增加 string _vertex = str.substr(0,str.find(',')); string _condition = str.substr(str.find(',')+1); int _num = atoi(_vertex.c_str()); int _T = 0,_F = 0,_in[MAX]={0}; int l = length(arcTail); int l_l =0; changeVexArr(_num); for(int i=0; i<l; i++) if(arcHead[i] == _num) _in[l_l++] = i; for(int j=0; j<l; j++){ if((Judge[j] == 'F') && (arcTail[j] == _num)) _F = j; else if((Judge[j] == 'T') && (arcTail[j] == _num)) _T = j; } for(int i=0; i<l_l; i++) arcHead[_in[i]] = _num*10+1; if(_condition == "AND"){ int _into = length(arcTail); arcTail[_F] = _num*10+1; arcTail[_into] = _num*10+1; arcHead[_into] = _num*10+2; Judge[_into] = 'T'; _into ++; arcTail[_T] = _num*10+2; arcTail[_into] = _num*10+2; arcHead[_into] = arcHead[_F]; Judge[_into] = 'F'; } else if(_condition == "OR"){ int _into = length(arcTail); arcTail[_T] = _num*10+1; arcTail[_into] = _num*10+1; arcHead[_into] = _num*10+2; Judge[_into] = 'F'; _into++; arcTail[_F] = _num*10+2; arcTail[_into] = _num*10+2; arcHead[_into] = arcHead[_T]; Judge[_into] = 'T'; } } void xianshi(){ for(int i=0; i<length(vexArr); i++) cout << vexArr[i] << endl; for(int i=0; i<length(arcTail); i++) cout << arcTail[i] << "->" << arcHead[i] << "," << Judge[i] << endl; } int main(){ Graph G; int i=0; string temp =""; int _header = 0; getline(cin,temp); if(temp == "END") return 0; vexArr[i++] = atoi(temp.c_str()); while(1){ getline(cin,temp); if(temp == "EXT" || temp == "ext") is_EXT = 'Y'; if(is_end(temp)) break; insert(temp); } if(is_EXT == 'Y') while(1){ getline(cin,temp); if(is_end(temp)) break; CreatePath(temp); // xianshi(); } //xianshi(); CreateGraph(&G); DFSTraverse(G,LocateVex(&G,vexArr[0])); cout << "CC=" << returnNum(order_out,'|')-1 << endl; for(int i=0;i<10;i++) //很奇葩的地方,需要进行多次重复排序 orderDisplay(order_out); coutResult(order_out); //orderDisplay(ordered); //system("pause"); return 0; }
最新代码:https://github.com/luofei2011/White-box-testing
About Me:luofeihit2010@gmail.com
相关文章推荐
- 白盒测试之基本路径测试法
- 白盒测试用例设计方法-路径覆盖法及基本路径覆盖法
- 白盒测试之基本路径测试法[1]
- 白盒测试之基本路径测试法
- 白盒测试之基本路径测试法[2]
- (转)白盒测试之基本路径测试方法
- 白盒测试之基本路径测试法
- 白盒测试之基本路径测试法
- 白盒测试之基本路径测试法
- 白盒测试之基本路径测试法[3]
- 白盒测试之基本路径测试法[6]
- 白盒测试--基本路径测试法详细说明和举例
- 软件测试白盒测试之基本路径测试方法
- 白盒测试之基本路径测试法[5]
- 白盒测试之基本路径测试法
- 软件测试 白盒测试--逻辑覆盖法&基本路径测试法
- 白盒测试之基本路径测试法
- [转]白盒测试之基本路径测试法
- 白盒测试之基本路径测试法
- 关于白盒测试之基本路径测试法的一点思考