Loading...
墨滴

jasonj333

2021/04/07  阅读:16  主题:红绯

车载以太网vTESTstudio

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),也需要在代码中考虑到

以上就是大概的思路


jasonj333

2021/04/07  阅读:16  主题:红绯

作者介绍

jasonj333