1.Test Moudule架构
2.在CANoe中创建并配置Test Module组件
在Options中点开General中的Test Feature Set 如上图所示选择2输出的文件只能用浏览器打开,选择1输出的只能通过canoe打开。
选择2输出如下:
选择1输出如下:
3. Test Module的CAPL脚本代码的基本结构
- 要创建.can⽂件跟TestModule进⾏关联
- 脚本⽂件中必须要有 MainTest 函数,⽤于描述整个测试模块中的执⾏流程
- 要编写⽤例,必须定义测试⽤例函数,使⽤ testcase 关键字定义
MainTest()
{
MyTestCase1();
MyTestCase2();
}
testcase MyTestCase1()
{
}
testcase MyTestCase2()
{
}
4.常⽤函数 —— “测试架构”相关的函数
测试用例
includes
{
}
variables
{
}
MainTest()
{
testModuleTitle("我的第一个测试模块");//用于设定测试模块的标题
testModuleDescription("这个测试模块仅仅用于演示");//用于将测试用例的描述写入测试报告
testGroupBegin("我的测试组一","这是分组里的X用例");//用于测试分组的开始
MyTestCase1();
MyTestCase2();
testGroupEnd();//用于测试分组的的结尾
testGroupBegin("我的测试组二","这是分组里都是Y的用例");
MyTestCase3();
testGroupBegin("我的测试组二下的小组","这是分组里的Z的用例");
MyTestCase4();
MyTestCase5();
testGroupEnd();
testGroupEnd();
}
testcase MyTestCase1()
{
testCaseTitle("TCL","我的测试用例1");
testCaseDescription("这个用例主要是用于验证");
}
testcase MyTestCase2()
{
testCaseTitle("TCB","我的测试用例2");
testCaseDescription("这个用例主要是用于验证");
}
testcase MyTestCase3()
{
}
testcase MyTestCase4()
{
}
testcase MyTestCase5()
{
}
testStep()//用于报告测试步骤信息,而不对测试结果有任何影响.
testStepPass();//显示通过
testStepFail();//显示错误
一个组里有一个用例错误,这个组就失败,这个组失败总结果就失败。
includes
{
}
variables
{
}
MainTest()
{
testModuleTitle("我的第一个测试模块");//用于设定测试模块的标题
testModuleDescription("这个测试模块仅仅用于演示");//用于将测试用例的描述写入测试报告
testGroupBegin("我的测试组一","这是分组里的X用例");//用于测试分组的开始
MyTestCase1();
MyTestCase2();
testGroupEnd();//用于测试分组的的结尾
testGroupBegin("我的测试组二","这是分组里都是Y的用例");
MyTestCase3();
testGroupBegin("我的测试组二下的小组","这是分组里的Z的用例");
MyTestCase4();
MyTestCase5();
testGroupEnd();
testGroupEnd();
}
testcase MyTestCase1()
{
testCaseTitle("TCL","我的测试用例1");
testCaseDescription("这个用例主要是用于验证");
testStep("这是一个重要1","这是一个");
testStep("这是一个重要2","这是一个");
testStep("这是一个重要3","这是一个");
testStepPass("当前结果判定成功","jock顶梁柱是[%s]","贺工");
testStepFail("当前结果判定失败","jock顶梁柱是[%s]","小丑");
testStepPass("当前结果判定成功","1+1=%d",2);
testStepFail("当前结果判定失败","1+1=%d",1);
}
testcase MyTestCase2()
{
int fd=0;
int matchedResult;
testCaseTitle("TCB","我的测试用例2");
testCaseDescription("这个用例主要是用于验证");
testStep("别急","等一会");
testWaitForTimeout(2500);
testStep("继续","执行下去");
//让程序弹出一个对话框
// fd=testWaitForTesterConfirmation("请确认当前值是否等于2000?");//会自己弹出来让你确认可在框内输入信息
// if(fd==1)
// {
// testStepPass("用例执行通过","当前值为2000");
// }
// else
// {
// testStepFail("用例执行失败","当前值为200");
// }
matchedResult=testWaitForSignalMatch(OnOff,1,1500);//引擎状态为1等待1500ms
if(matchedResult==1)
{
testStepPass("用例执行通过","开关打开");
}
else
{
testStepFail("用例执行通过","开关打开失败开关状态为%d",$OnOff);
}
}
testcase MyTestCase3()
{
}
testcase MyTestCase4()
{
}
testcase MyTestCase5()
{
}
5.常⽤函数 —— “信号”相关的函数
matchedResult=testWaitForSignalMatch(LeftDoorState,1,1500);
if(matchedResult)
{
testStepPass("用例执行通过","车门打开");
}
else
{
testStepFail("用例执行失败","车门打开失败开关状态为%d",$LeftDoorState);
}
6.常⽤函数 —— “测试报告”相关的函数
7.常⽤函数 —— “探测检查”相关的函数
- 为了完成⼀段时间内的持续探测检查,并能够统计这段时间内的各项数据,并且获取检查的结果
- 先:调⽤⼀个具体规则的“开始检查函数”,返回⼀个检查的标识ID
- 再:等待⼀段指定的时⻓(这个过程中),系统会持续探测检查
- 最后:判定检查的探测结果(违反规则的事件的发⽣次数),是否有违反规则
- 还可以:将这段时间内的各项统计数据获取
六、案例
- 前⾬刮(Front Wiper)可能处于四种状态:关闭中、⾼速刮中、低速刮中、故障中
- BCM模块会处理总线上收到的控制前⾬刮⼯作的请求报⽂,⼀旦收到报⽂,就进⾏控制处理,并实 时反馈
- BCM模块会实时将前⾬刮的状态反馈到汽⻋总线上,持续发送ID为0x387的报⽂
- 其中有⼀个信号 BCM_FrontWiperStatus ,它的取值如下:
- 0x0=OFF; 0x1=Low;0x2=High; 0x3=Error
- ⽤户可以通过⻋机/中控来控制前⾬刮的⼯作,本质上就是发送控制前⾬刮的请求报⽂,这个报⽂如下
- ID为 0x55D 的报⽂,其中⼀个信号 HU_FronWiperRq 负责控制前⾬刮,它的取值如下:
- 0x0 =关闭; 0x1 =低速刮; 0x2 =⾼速刮 ; 0x3=aotu档
(2)分析:按照请求信号的规格描述,分别发送不同的控制前⾬刮⼯作的信号值的报⽂,等待BCM进 ⾏处理,并验证前⾬刮⼯作状态的信号(BCM持续在总线上发的)是否符合预期。
- 发送请求控制⾬刮的报⽂(0x55D),查看验证⾬刮状态的报⽂(0x387)
(3)实现:使⽤CANoe中的Test Module来实现前⾬刮控制器(BCM)的功能
- 创建⼀个前⾬刮功能验证的测试模块 —— 前⾬刮功能测试模块
- 在测试模块下创建4个⽤例函数,分别代表请求4种不同档位的操作
/*@!Encoding:936*/
variables
{
// 进⾏前⾬刮控制的请求报⽂
message HU_0x55d msgReq;
// 检查结果的标识
int checkResult;
}
MainTest()
{
testModuleTitle("前⾬刮功能测试");
testModuleDescription("详细测试前⾬刮各个档位的⼯作状态,包括:关闭、⾼速刮、低速
刮、⾃动挡");
testReportAddEngineerInfo("测试员", "007");
testReportAddEngineerInfo("⼯号", "666888");
testReportAddSUTInfo("ECU", "BCM(⻋身控制模块)");
testReportAddSUTInfo("软件版本", "V1.3.2");
FrontWiperLow();
FrontWiperHigh();
FrontWiperOff();
FrontWiperAuto();
}
testcase FrontWiperLow()
{
testCaseTitle("⽤例1", "测试前⾬刮 —— 低速刮");
testCaseDescription("发送低速刮的请求,验证前⾬刮状态信号是否变为低速刮");
testStep("⽤例开始时", "请求发送前,当前的⼯作状态信号BCM_FrontWiperStatus为:
0x%X", (int)$BCM_FrontWiperStatus);
// 设置请求的信号值为1,代表希望控制前⾬刮低速刮,并发送该报⽂
msgReq.HU_FronWiperRq = 0x1;
output(msgReq);
// 检查BCM操作了前⾬刮后,更新前⾬刮⼯作状态的信号值为0x1,代表当前前⾬刮正在低速刮
checkResult = testWaitForSignalMatch(BCM_FrontWiperStatus, 1, 500);
if(checkResult == 1) // 满⾜检查的条件(前⾬刮⼯作状态的信号值更新为1了)
{
testStepPass("⽤例执⾏成功", "请求发送后预期值[0x01],当前的前⾬刮⼯作状态信号
BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
else
{
testStepFail("⽤例执⾏失败", "请求发送后预期值[0x01],当前的前⾬刮⼯作状态信号
BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
}
testcase FrontWiperHigh()
{
testCaseTitle("⽤例2", "测试前⾬刮 —— ⾼速刮");
testCaseDescription("发送⾼速刮的请求,验证前⾬刮状态信号是否变为低⾼速刮");
testStep("⽤例开始时", "请求发送前,当前的⼯作状态信号BCM_FrontWiperStatus为:
0x%X", (int)$BCM_FrontWiperStatus);
// 设置请求的信号值为2,代表希望控制前⾬刮⾼速刮,并发送该报⽂
msgReq.HU_FronWiperRq = 0x2;
output(msgReq);
// 检查BCM操作了前⾬刮后,更新前⾬刮⼯作状态的信号值为0x2,代表当前前⾬刮正在⾼速刮
checkResult = testWaitForSignalMatch(BCM_FrontWiperStatus, 2, 500);
if(checkResult == 1) // 满⾜检查的条件(前⾬刮⼯作状态的信号值更新为2了)
{
testStepPass("⽤例执⾏成功", "请求发送后预期值[0x02],当前的前⾬刮⼯作状态信号
BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
else
{
testStepFail("⽤例执⾏失败", "请求发送后预期值[0x02],当前的前⾬刮⼯作状态信号
BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
}
testcase FrontWiperOff()
{
testCaseTitle("⽤例3", "测试前⾬刮 —— 关闭");
testCaseDescription("发送关闭的请求,验证前⾬刮状态信号是否变为低关闭");
testStep("⽤例开始时", "请求发送前,当前的⼯作状态信号BCM_FrontWiperStatus为:
0x%X", (int)$BCM_FrontWiperStatus);
// 设置请求的信号值为0,代表希望控制前⾬刮关闭,并发送该报⽂
msgReq.HU_FronWiperRq = 0x0;
output(msgReq);
// 检查BCM操作了前⾬刮后,更新前⾬刮⼯作状态的信号值为0x0,代表当前前⾬刮已关闭
checkResult = testWaitForSignalMatch(BCM_FrontWiperStatus, 0, 500);
if(checkResult == 1) // 满⾜检查的条件(前⾬刮⼯作状态的信号值更新为0了)
{
testStepPass("⽤例执⾏成功", "请求发送后预期值[0x00],当前的前⾬刮⼯作状态信号
BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
else
{
testStepFail("⽤例执⾏失败", "请求发送后预期值[0x00],当前的前⾬刮⼯作状态信号
BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
}
testcase FrontWiperAuto()
{
testCaseTitle("⽤例4", "测试前⾬刮 —— ⾃动挡");
testCaseDescription("发送⾃动挡的请求,验证前⾬刮状态信号是在1和2之间");
testStep("⽤例开始时", "请求发送前,当前的⼯作状态信号BCM_FrontWiperStatus为:
0x%X", (int)$BCM_FrontWiperStatus);
// 设置请求的信号值为3,代表希望控制前⾬刮为⾃动挡,并发送该报⽂
msgReq.HU_FronWiperRq = 0x3;
output(msgReq);
// 检查BCM操作了前⾬刮后,更新前⾬刮⼯作状态的信号值为0x1或者0x2,代表当前前⾬刮处
于⾃动挡
checkResult = testWaitForSignalInRange(BCM_FrontWiperStatus, 1, 2, 500
);
if(checkResult == 1) // 满⾜检查的条件(前⾬刮⼯作状态的信号值更新为1或2了)
{
testStepPass("⽤例执⾏成功", "请求发送后预期值[0x01~0x02],当前的前⾬刮⼯作状
态信号BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
else
{
testStepFail("⽤例执⾏失败", "请求发送后预期值[0x01~0x02],当前的前⾬刮⼯作状
态信号BCM_FrontWiperStatus为:0x%02X", (int)$BCM_FrontWiperStatus);
}
}
2. 测试ECU(⻋身控制模块BCM)发送报⽂周期的性能
(1)需求:测试⼀段时间内,BCM发送0x28B报⽂的间隔时间是否在37到43ms之间
矩阵表中标准是40ms,由于ECU的性能,或多或少都会有偏差
(2)分析:使⽤“检查探测”相关的函数
- 先:调⽤ ChkStart_MsgAbsCycleTimeViolation 开启检查
- 再:让程序等待⼀段时间(例如6000ms),调⽤ TestWaitForTimeout
- 然后:查询检查的各项结果:违规次数、报⽂探测到次数、报⽂的平均间隔时间、……
- 最后:判定“违规次数”决定⽤例是否通过( TestStepPass 、 TestStepFail ),并记录统计 数据到测试报告中( TestStep )
- 额外:可以使⽤“为检查添加条件”的⽅式( TestAddCondition ),对⼀段时间内发⽣的每⼀次 违规,都进⾏判决并加⼊测试报告。
/*@!Encoding:936*/ includes { } variables { const dword gCHECK_TIMEOUT = 6000; const word gMIN_INTERVAL = 37; const word gMAX_INTERVAL = 43; } void MainTest () { testModuleTitle("测试BCM_0x28B报⽂的发送性能"); TestMsgSendInterval(); } testcase TestMsgSendInterval() { dword checkId; long numEvents; long statNumProbes; double msgIntervalAvg; double msgIntervalMin; double msgIntervalMax; testCaseTitle("⽤例1", "测试BCM_0x28B报⽂的发送时间间隔在规定的范围之内"); // 创建并开启了⼀次针对报⽂的发送时间间隔是否在指定范围之内的检查 // 返回⼀个检查ID,以供后续查询检查结果、获取统计数据使⽤ checkId = ChkStart_MsgAbsCycleTimeViolation(BCM_0x28b, gMIN_INTERVAL, gM AX_INTERVAL); // 为检查添加条件,记录检查过程中违规的事件信息,并加⼊测试报告中 TestAddCondition(checkId); // 持续检查6秒 testWaitForTimeout(gCHECK_TIMEOUT); // 为该检查移除条件 testRemoveCondition(checkId); /************************************ 查询检查的各项结果 查看违规次数 —— ChkQuery_NumEvents 查看报⽂出现次数/信号变化次数 —— ChkQuery_StatNumProbes 查询报⽂发送间隔的平均时间 —— ChkQuery_StatProbeIntervalAvg 查询报⽂发送间隔的最⼩时间 —— ChkQuery_StatProbeIntervalMin 查询报⽂发送间隔的最⼤时间 —— ChkQuery_StatProbeIntervalMax ************************************/ // 查询违反检查规则的事件的发⽣次数,本例中就是两次报⽂发送间隔时间不在MIN~MAX毫秒之 间 numEvents = ChkQuery_NumEvents(checkId); // 查询报⽂的出现次数 statNumProbes = ChkQuery_StatNumProbes(checkId); // 查询报⽂发送间隔的平均时间 msgIntervalAvg = ChkQuery_StatProbeIntervalAvg(checkId); // 查询报⽂发送间隔的最⼩时间 msgIntervalMin = ChkQuery_StatProbeIntervalMin(checkId); // 查询报⽂发送间隔的最⼤时间 msgIntervalMax = ChkQuery_StatProbeIntervalMax(checkId); if(numEvents == 0) // 没有出现违反规则的时间(本例中所有探测的报⽂发送的间隔时间均 规定范围内) { testStepPass("⽤例通过", "报⽂发送间隔时间均在在%d~%d毫秒范围", gMIN_INTERVA L, gMAX_INTERVAL); } else { testStepFail("⽤例失败", "违反规则【报⽂发送间隔时间不在%d~%d毫秒范围】的事件发 ⽣次数:%d", gMIN_INTERVAL, gMAX_INTERVAL, numEvents); } testStep("统计信息", "报⽂出现的次数:%d", statNumProbes); testStep("统计信息", "报⽂发送间隔的平均时间:%.3f", msgIntervalAvg); testStep("统计信息", "报⽂发送间隔的最⼩时间:%.3f", msgIntervalMin); testStep("统计信息", "报⽂发送间隔的最⼤时间:%.3f", msgIntervalMax); }