vTESTstudio的可移植性主要表现在它的参数可以提取出来,作为参数文件的形式存在,这样,不同的项目想要实现同样的功能,只需要改变参数值,就可以在不重写或修改脚本的前提下,实现测试用例的执行。利用这一点,试着设计车载以太网的基于vTESTstudio的测试用例的框架
思路
以“发送一条ARP request报文”为例
我们需要知道的协议字段:
- senderMacAddress
- senderIpv4Address
- targetIpv4Address
- targetVlanID
其中前三个是必须的,而targetVlanID由于目的网卡配置vlanID或没有配置vlanID这两种情况,还需要分情况考虑
常规思路:用capl实现发ARP request
void InternalSendArpRequest(char senderMacAddress[],
char senderIpv4Address[],
char targetIpv4Address[],
int targetVlanID)
{
ethernetPacket pkt;
setBusContext(getBusNameContext("Ethernet")); //指定bus
pkt.arp.init();
pkt.arp.hwType = 0x0001;
pkt.arp.protType = 0x0800;
pkt.arp.hwSize = 0x06;
pkt.arp.protSize = 0x04;
pkt.arp.operation = 0x0001; //arp request
pkt.arp.hwSourceAddr.ParseAddress(senderMacAddress);
pkt.arp.protSourceAddr.ParseAddress(senderIpv4Address);
pkt.arp.hwDestinationAddr.ParseAddress(syspar::CommPara::MacAddressZero);
pkt.arp.protDestinationAddr.ParseAddress(targetIpv4Address);
pkt.source.ParseAddress(senderMacAddress);
pkt.destination.ParseAddress(syspar::CommPara::MacAddressBroadcast);
if (targetVlanID > -1)
{
pkt.SetVlanId(targetVlanID);
}
pkt.CompletePacket();
output(pkt);
我传入一个vlanID参数,在内部做一个判断,当传入的vlanID大于-1时,我就把这条报文设置vlanID,如果是-1,就不设置vlanID
这样虽然能实现有vlan和没有vlan这两种情况,但是这个函数是想作为底层接口使用的,我并不想在它内部有这种不确定的逻辑,那怎么办?
capl语言是类C语言,它是有重写功能的,所以我可以写两个具有相同函数名的方法发arp request,一个有vlan,一个没有vlan
- 不带vlan的
void InternalSendArpRequest(char senderMacAddress[],
char senderIpv4Address[],
char targetIpv4Address[])
{
ethernetPacket pkt;
setBusContext(getBusNameContext("Ethernet")); //指定bus
pkt.arp.init();
pkt.arp.hwType = 0x0001;
pkt.arp.protType = 0x0800;
pkt.arp.hwSize = 0x06;
pkt.arp.protSize = 0x04;
pkt.arp.operation = 0x0001; //arp request
pkt.arp.hwSourceAddr.ParseAddress(senderMacAddress);
pkt.arp.protSourceAddr.ParseAddress(senderIpv4Address);
pkt.arp.hwDestinationAddr.ParseAddress(syspar::CommPara::MacAddressZero);
pkt.arp.protDestinationAddr.ParseAddress(targetIpv4Address);
pkt.source.ParseAddress(senderMacAddress);
pkt.destination.ParseAddress(syspar::CommPara::MacAddressBroadcast);
//pkt.SetVlanId(vlanID);
pkt.CompletePacket();
output(pkt);
write("---");
write("sendArpRequest: senderMacAddress: %s", senderMacAddress);
write("sendArpRequest: senderIpv4Address: %s", senderIpv4Address);
write("sendArpRequest: targetIpv4Address: %s", targetIpv4Address);
write("---");
}
- 带vlan的
void InternalSendArpRequest(char senderMacAddress[],
char senderIpv4Address[],
char targetIpv4Address[],
int targetVlanID)
{
ethernetPacket pkt;
setBusContext(getBusNameContext("Ethernet")); //指定bus
pkt.arp.init();
pkt.arp.hwType = 0x0001;
pkt.arp.protType = 0x0800;
pkt.arp.hwSize = 0x06;
pkt.arp.protSize = 0x04;
pkt.arp.operation = 0x0001; //arp request
pkt.arp.hwSourceAddr.ParseAddress(senderMacAddress);
pkt.arp.protSourceAddr.ParseAddress(senderIpv4Address);
pkt.arp.hwDestinationAddr.ParseAddress(syspar::CommPara::MacAddressZero);
pkt.arp.protDestinationAddr.ParseAddress(targetIpv4Address);
pkt.source.ParseAddress(senderMacAddress);
pkt.destination.ParseAddress(syspar::CommPara::MacAddressBroadcast);
pkt.SetVlanId(targetVlanID);
pkt.CompletePacket();
output(pkt);
write("---");
write("sendArpRequest: senderMacAddress: %s", senderMacAddress);
write("sendArpRequest: senderIpv4Address: %s", senderIpv4Address);
write("sendArpRequest: targetIpv4Address: %s", targetIpv4Address);
write("sendArpRequest: targetVlanID: %d", targetVlanID);
write("---");
}
然后我再写一个方法,用这个方法根据传入的vlanID判断,如果vlanID大于-1,我就调用带vlan的那个方法,否则就调用不带vlan的那个方法
void SendArpRequest(int IsAvailable,
char senderMacAddress[],
char senderIpv4Address[],
char targetIpv4Address[],
int targetVlanID)
{
if (IsAvailable != -1)
{
if (targetVlanID > -1)
{
InternalSendArpRequest(senderMacAddress, senderIpv4Address, targetIpv4Address, targetVlanID);
}
else
{
InternalSendArpRequest(senderMacAddress, senderIpv4Address, targetIpv4Address);
}
//write("send broadcaset arp request message");
}
else
{
write("not send broadcast arp request message");
}
}
这里提一句,overwrite的两个函数具有相同的函数名,那调用它时,是如何知道要调用哪个呢?
虽然函数名相同,但是里面的参数必须不同(要么是参数数量,要么是参数类型),调用时根据传入的参数找到正确的函数
上面的这个函数是对发ARP request带不带vlan的两种不同情况的一个封装,而Test Table里如果想调用capl函数,最好是testfunction函数,而且必须有关键字export
export testfunction TC_SendArpRequest(int IsAvailable,
char senderMacAddress[],
char senderIpv4Address[],
char targetIpv4Address[],
int targetVlanID)
{
if (CheckMacAndIpParameters(senderMacAddress, senderIpv4Address, targetIpv4Address))
{
testStep("TC_SendArpRequest", targetIpv4Address);
SendArpRequest(IsAvailable,senderMacAddress, senderIpv4Address, targetIpv4Address, targetVlanID);
if (TestWaitForTextEvent(testEventArpResponse, 5000))
{
testStepPass("TC_SendArpRequest", targetIpv4Address);
}
else
{
testStepFail("TC_SendArpRequest", targetIpv4Address);
}
}
else
{
testStepFail("TC_SendArpRequest", "mac or ip address parameters ERROR");
}
}
这样就可以在Test Table中调用这个testfunction函数,把参数文件中定义的参数值传进去,实现根据vlanID的值发送一条ARP request
框架
以下是完整的框架图
设计部分
capl编写部分
InternalTestLib.can作为底层接口,只实现基本的功能,不包括逻辑判断,原则是不管参数值或条件发生了任何改变,都不会影响到这里面的代码实现,可以利用重写功能,在这里实现多种情况
TestLib.can作为对InternalTestLib.can里的接口的二次封装,在这里实现逻辑判断,同时为上层TestFunctionLib.can提供接口
TestFunctionLib.can作为对TestLib.can里的接口的二次封装,在这里实现结果判断和输出报文内容,同时为Test Table提供接口
VariablesLib.can作为定义全局变量的文件,负责提供全局变量给它们三个文件使用
这里有一个注意点:
底层的函数文件如果是被其他函数库调用的,就不能再被放入Project View下,注意要remove,而不是delete,remove只是从项目中移除,文件还在,delete是会连文件一起删除的
Test Case编写部分
通过TestFunctionLib.can提供的函数接口,Test Table调用这些接口,同时把接口参数配置成正确的参数文件里的参数,组合成想要的test case
编译后生成.vtuexe文件,被CANoe工程里的test unit调用,形成test case面板
执行部分
Test Case部分
CANoe执行test case,会通过.vtuexe文件,执行Test Table里的test case
也就是调用test case里的接口函数,即TestFunctionLib.can里的函数,在调用时需要传入参数值的,会在参数文件里寻找
capl部分
这里就很简单了,一层一层的调用,没什么特别的
一些其他需要注意的地方:
- 比如说可以在底层接口InternalTestLib.can里write()一些内容,方便调试
- 还需要考虑代码的健壮性,比如说用户配置的参数值不符合格式(ip地址写成了192.168.1.266),也需要在代码中考虑到
以上就是大概的思路