1. Common Features
1.1 Signals (bus signals, environment and system variables)
NET中的信号类型通常是指总线信号、环境变量和系统变量。这些类型的处理方式完全相同。
1.1.1 设置信号值
//Signal values can be accessed through the Value property, e.g. <signal name>.Value.
LockState.Value = 1;
1.1.2 数组类型的环境变量
如果环境变量是数据数组类型,则可以使用索引运算符轻松读取每个字节:
if (MyDataEv.Value[2] == 2) {…}
赋值时,需要按以下步骤进行:
Byte[] tmp = MyDataEv.Value;
tmp[2] = 2;
MyDataEv.Value = tmp;
当信号对象的名称已知时,还可以动态创建它。下面是一个示例,如何创建系统变量对象(namespace = ‘Tester’ and variable name = ‘Enabled’),并修改其值:
DynamicSystemVariable var = new DynamicSystemVariable("Tester", "Enabled");
var.Value = 1;
1.2 Message
为了构造数据库(例如dbc)中未定义的用户定义的CAN消息,可以将CANFrame类用作一个父类。信号用[Signal]属性定义。
class myMessage : CANFrame
{
[Signal(LSB = 0, BitCount = 2)]
public UInt32 mySignal;
}
myMessage msg = new myMessage(0x42,8);
msg.Send();
CAN消息也可以通过创建CANFrame类的实例并在构造函数中初始化ID和DLC来声明。只能以原始格式设置消息数据,可以使用属性Bytes[]按字节设置,也可以通过大小必须为8 Bytes min的字节数组设置:
byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
CANFrame msg = new CANFrame(0x500, 4); // ID=0x500, DLC=4
msg.SetRawData(data);
msg.Send();
1.2 Timer
属性[OnTimer]可用于完成循环计时器。该方法将在定义的时间点(这里是1s)循环执行。
对于测试模块,当测试模块处于活动状态时,计时器处于活动状态;对于模拟节点,在测量过程中,计时器处于活动状态。
[OnTimer(1000))]
public void OnTimer1s() {
Output.Writeline("Timer elapsed");
}
如果计时器应该在某个点启动和停止,则可以使用计时器对象。使用Timer类及其属性/方法,例如定义计时器处理程序:
{
Timer t = new Timer(new TimeSpan(0, 0, 0, 0, 250),TimerHandler);
t.Start();
…
t.Stop();
}
public void TimerHandler(Object o, ElapsedEventArgs e)
{
Report.TestCaseComment("Timer event");
}
1.3 Event Procedures
对于测试模块,当测试模块处于活动状态时,事件处理程序处于活动状态;对于模拟节点,事件处理程序在测量期间处于活动状态。
1.3.1 OnChange
对网络信号LockState的值变化作出反应的处理程序定义如下:
[OnChange(typeof(LockState))]
public void OnSignalLockState() {
double value = LockState.Value;
}
面向信号的.NET API只支持“on change”处理程序,而CAPL同时支持“on change”和“on update”处理程序。一个原因是.NET程序的性能方面。另一个原因是基于信号的测试是基于状态的。
与此相反,消息处理程序(7.1版引入)是基于事件的。要处理消息窗口状态的消息事件,请使用[OnCANFrame]属性。CAN通道和消息ID也可以在属性构造函数中指定。
1.3.2 OnCANFrame
[OnCANFrame]
public void onMessage(NetworkDB.Frames.WindowState frame) {
double pos = frame.WindowPosition.Value;
}
[OnCANFrame(1, 500)]
public void Frame500Received(CANFrame frame) {…}
1.3.3 OnKey
使用属性[OnKey]对关键事件作出反应。定义的方法必须有一个char参数。
[OnKey('+'))]
public void OnKeyPressed(char ch) {
Output.WriteLine("+ was pressed");
}
1.3.4 OnTimer
要处理计时器事件,应该使用[OnTimer]属性。每500毫秒调用一次下面的TimeRecursed方法:
[OnTimer(500))]
public void TimerElapsed() {
msg.Send();
}
1.3.5 OnChange
该模块还可以对系统变量变化作出反应:
[OnChange(typeof(MyNameSpace.MyInt))]
public void MyIntHandler()
{
MyNameSpace.MyInt2.Value = MyNameSpace.MyInt.Value;
}
2 Test Features
2.1 Test Module Types
有两种不同类型的测试模块:
1 非结构化测试模块-Unstructured test modules
在测试模块执行期间,将显示并完成测试用例和组的顺序。不可能启用单个测试用例或组。
2 结构化测试模块-Structured test modules
与非结构化模块相比,这些模块具有很大的优势,因为它们的结构在编译之后直接显示在测试执行对话框中,并且可以在测试执行之前启用或禁用它们。
2.1.1 Unstructured test modules
非结构化.NET测试模块必须从类Vector.CANoe.TFS.TestModule继承。它主要由执行测试控制流和测试用例的主要方法组成。测试组可以用来组织属于一起的测试用例。测试用例是用自定义属性[TestCase]定义的,可以分为几个测试步骤。Vector.CANoe.TFS.Report类用于向报告文件报告。
测试模块的主要方法是将测试模块的主程序模拟成CAPL测试模块。重写TestModule类继承的Main方法是必需的。主方法将测试用例分组并按顺序执行它们:
public class MyTestModule : TestModule
{
public override void Main()
{
TestGroupBegin("Id", "Title", "Description");
TestCase1();
if (TestModule.VerdictLastTestCase == Verdict.Passed)
{
TestCase2();
}
TestCase3();
TestGroupEnd();
}
}
[TestCase("Test case title")]
public void TestCase1()
{
Report.TestStep("Description of the test step");
}
主方法应该只包含测试流控制。具体的测试应该在测试用例中进行。可以实现复杂的流逻辑,例如,让一个测试用例通过在循环中调用它来执行多次,或者在前面的测试用例返回失败时阻止执行测试用例。
一般来说,测试用例的编程应该使它们不相互依赖,例如在测试用例之间不使用全局变量。建议重新开始每个测试用例;
在测试用例开始前初始化参数,如果需要,在测试用例结束后重置参数。这确保了良好的编程实践和可能的代码重用(这是.NET API的主要动机之一)。
事件程序仅在测试模块运行期间有效,因为测试模块不必在整个测量期间始终有效。
2.1.2 Structured test modules
结构化.NET测试模块必须从类Vector.CANoe.TFS.StructuredTestModule继承。测试模块的StructuredMain方法是主程序,需要重写StructuredTestModule类继承的StructuredMain方法。需要动态测试序列(两个测试模块之间可能不同的测试序列运行)使用标记有[TestGroup]属性的方法进行封装。动态测试组将始终作为一个整体执行。
该示例显示了一个DynamicTestGroup()和一个包含两个测试用例的普通测试组。
public class MyTestModule : StructuredTestModule
{
public override void StructuredMain()
{
DynamicTestGroup();
TestGroupBegin("Id", "Title", "Description");
TestCase1();
TestCase3();
TestGroupEnd(); }
}
[TestCase("Test case title")]
public void TestCase1()
{
Report.TestStep("Description of the test step");
}
…
[TestGroup(“Dynamic Test case sequence”,” depends on preceding test case results”)]
public void DynamicTestGroup()
{
TestCase1();
if (TestModule.VerdictLastTestCase == Verdict.Passed)
{
TestCase2();
// …
}
}
StructuredMain方法只能定义测试序列。具体的测试应该在测试用例和测试组中进行。
事件过程仅在测试模块执行期间处于活动状态
2.2 Reporting Commands
report类允许详细报告测试结果和测试执行步骤。这里列出了最重要的方法。
2.3 Wait Points
通常情况下,SUT的刺激是在测试用例中生成的,然后等待反应。测试模块的顺序执行可以被所谓的等待点中断。等待点的原理是,它们将流控制返回到CANoe,直到指定的事件发生(例如,信号值更改、消息接收或超时)才恢复处理。
对于简单超时、总线或I/O信号的专用值、消息、用户定义(复杂)系统条件的实现、用户交互等,都有等待点。通过使用执行类的wait方法来完成等待点:
//Simple timeout:
Execution.Wait(50);
//Bus signal:
Execution.Wait<EngineRunning>(1);
//System variable:
Execution.Wait<SystemUnderTest.OperationMode>(2);
//Environment variable:
Execution.Wait<EnvDoorLocked>(EnvDoorLocked.Locked);
//Message:
Execution.WaitForCANFrame(ref frame, 500);
NET中面向信号的API是面向状态的。这意味着,如果使用信号元素的等待点,则立即检查等待条件,如果信号已设置(即满足条件),则等待点也会立即恢复。与.NET面向消息的方法中的面向信号的方法不同,面向消息的方法是基于事件的。
2.4 Checks
NET测试模块中的Checks用于在测试序列期间监视某些系统状况。
有三种类型的检查;约束、条件和观察。
约束用于保证环境满足某些标准和条件,并监督被测系统的行为。任何违反这些检查的行为都会导致测试用例和测试模块失败。
这与不能影响测试模块判断的观察结果形成对比,即使测试报告中总是包含违规行为。
检查可以在一个完整的测试序列、一个完整的测试用例中激活,也可以是
使用方法调用激活/停用。
TFS提供了几个预定义的检查
自定义Checks:
类ValueCheck表示对信号、环境变量或系统变量的值的简单检查。这里检查引擎信号值:
[TestCase("Title", "Description")]
public void ObserveEngineState()
{
ICheck engOn = new ValueCheck<EngineRunning>(CheckType.Observation, 1);
engOn.Activate();
Vector.CANoe.Threading.Execution.Wait(4000); // observation active
engOn.Deactivate();
}
这是检查总线信号值的示例。首先,使用值“1”的类型条件创建对信号值的检查。然后检查被激活,在等待4s之后,检查在测试用例结束之前被停用。
消息的周期时间检查是这样完成的。激活操作如前一个示例所示。
ICheck check = new AbsoluteCycleTimeCheck<NetworkDB.Frames.CyclicMsg>
(CheckType.Condition, 80, 120);
如果应检查用户定义的系统条件,则可以定义自定义类(例如,对于DLC
观察结果):
{
public MyUserCheck(string title, string description)
: base(title, description)
{}
[OnCANFrame(1, 0x64)]
public void FrameReceived1(CANFrame frame)
{
if (frame.DLC != 1)
ReportViolation("DLC check for frame 0x64 failed");
}
}
Checks的应用如上所述:
ICheck check = new MyUserCheck("MyUser check", "Check DLC of message 0x64");
check.Activate(); // or: check.Activate("a new title");
// ...
check.Deactivate();
用户检查可以组合多个运行时值作为验证条件。
2.5 Criterions
Criteria属性可用于定义要在检查点或等待点中使用的处理程序
[Criterion]
[OnChange(typeof(AntiTheftSystemActive))]
[OnChange(typeof(LockState))]
bool AntiTheftSystemCriterion()
{
if((EngineRunning.Value == 0) && (LockState.Value == 1))
return (AntiTheftSystemActive.Value == 1);
else
return (AntiTheftSystemActive.Value == 0);
}
在检查中应用上述条件–如果条件处理程序返回false,则检查失败:
Check observeAntiTheftSystem = new Check(AntiTheftSystemCriterion);
observeAntiTheftSystem.Activate();
在Wait Point条件中应用上述条件–如果条件处理程序返回true,则恢复等待点:
Execution.Wait(AntiTheftSystemCriterion);
注1:在设置等待或检查时立即调用处理程序来处理启动条件。
注2:仅当check或wait条件处于活动状态时,用criteria属性标记的处理程序才处于活动状态。
criteria类可用于将多个单一准则组合为一个复杂准则,例如,当必须并行检查多个信号值时。这类似于CAPL中的 testJoinAuxEvent() 函数。
Criterion antiTheftSystem = new Criterion();
antiTheftSystem.AddMandatory(new ValueCriterion<AntiTheftSystemActive >(0));
antiTheftSystem.AddOneOf(new ValueCriterion<LockState>(0));
antiTheftSystem.AddOneOf(new ValueCriterion<EngineRunning>(1));
同样,组合标准可用于检查-如果违反强制性条件或违反所有可选条件,则检查失败:
Check observeAntiTheftSystem = new Check(antiTheftSystem);
observeAntiTheftSystem.Activate();
2.6 User Dialogs
原始的windows对话框不能在.NET测试模块中使用,因为它们会干扰CANoe的实时行为。相反,Vector.CANoe.Threading和Vector.Scripting.UI中有几个预定义的对话框。下面是一些如何使用它们的例子。
Tester Confirmation Dialog:
int result = Execution.WaitForConfirmation("Please confirm!");
//or
int result = ConfirmationDialog.Show(("Please confirm!","Confirmation Dialog");
Value input with ranges:
List<Range<uint>> l = new List<Range<uint>>();
l.Add(new Range<uint>(1, 5));
l.Add(new Range<uint>(10, 15));
RangeCollection<uint> mRangesUint = new RangeCollection<uint>(l);
result = DataEntryDialog.Show<uint>(
"Please enter a value between 1-5 or 10-15! ",
"ValueInput with range",
"Two different ranges are defined.", ref value, mRangesUint);
2.7 Test Patterns
测试模式是基本的预定义测试过程或测试流,可以用值参数化,并在测试用例中执行。通常,模式用输入值参数化,超时后检查输出(预期)值。测试模式的使用是
支持重用概念。
在.NET中,测试模式被实现为抽象类,例如类StateChange。这些抽象类必须实例化为具有类型化输入和输出(预期)向量的具体类。StateChange模式实现了典型的流:stimulate、wait、evaluate——也可以从XML测试模块模式中得知。自定义属性[Input]和[Expected]用于确定输入(刺激)和输出(评估)参数。
首先通过从抽象类派生来创建测试模式。定义超时、输入和期望值。
public class CrashDetectionTest : StateChange
{
public CrashDetectionTest() { Wait = 200; }
[Input(typeof(NetworkDB.CrashDetected))]
public double crashDetected = 1;
[Expected(typeof(NetworkDB.LockState), Relation.Equal)]
public double lockState = 0;
}
创建的测试模式现在可以在测试用例中使用,例如通过对其应用Execute()方法:
[TestCase]
public void LockStateDependsOnCrashDetection()
{
// Test Pattern - Crash Detection function test
CrashDetectionTest crashTest = new CrashDetectionTest();
crashTest.Execute();
// Change test pattern input parameter and re-execute
crashTest.crashDetected = 0;
crashTest.Execute();
}
可以使用[TestFunction]属性定义用户定义的测试模式:
[TestFunction]
private void InitSUT()
{
// perform some actions
}
执行测试功能时,详细的报告数据会写入测试报告。
2.8 Diagnostic Tests
诊断用于实现诊断请求/响应场景。一个简短的例子说明了这一点:
[TestCase("Diagnostic Test")]
public void TC_Diagnostic()
{
// select a diagnostic target ECU:
Ecu myEcu = Application.GetEcu("ECU");
// Create a request
Request request = myEcu.CreateRequest("ECUOrgin_Read");
// Send the request and wait for a response
SendResult result = request.Send();
// Check if the message transmission was successful
if (result.Status == SendStatus.Ok)
{
// Get the response (Caution: the response might be null)
Response response = result.Response;
// Check if the ECU sent a positive response
if (response.IsPositive)
{
Report.TestStepPass("Pos response received.");
// Read and verify a parameter value:
Parameter para = response.GetParameter("SerialNumber");
if (para.Value.ToInt32 == 123)
Report.TestStepPass("Serial number is OK");
else
...
}
else
{
Report.TestStepFail("Negative Response received");
}
}
else
{
Report.TestStepFail("Sending failed." + result.Status.ToString());
}
}
服务和参数限定符可以从CANoe符号资源管理器中复制。
更多的背景信息可以在“VDS\u Library\u QuickStart.pdf”中找到,该文件位于CANoes开始菜单/Help/“Documentation Vector Diagnostic Scripting Library”中。
3 Debugging .NET Programs
所有.NET程序都在CANoe实时任务“RuntimeKernel.exe”中执行。要调试.NET程序,调试器必须附加到RuntimeKernel.exe,并带有“managed code type”选项。首次启动CANoe测量时,将启动runtimekernel.exe。
当.NET模块是用CANoe生成的时,通常没有可用的调试信息,您应该首先在IDE中用启用的调试模式重新生成程序集,或者可以在“选项”对话框(菜单:配置|选项|编程|.NET调试|.NET调试器)中的CANoe中启用.NET调试
如果您在CANoe的模拟模式下调试您的模块,那么在选项对话框(菜单:配置|选项|测量|常规|模拟|使用windows计时器)中启用“windows计时器”很有用。
有关如何附加调试器的详细信息,请参阅VisualStudio文档。
4 Troubleshooting
[TestClass]
public class TestCaseLibrary
{
[Export]
[TestCase]
[BreakOnFail(true)]
public static void TestWithOwnExceptionHandling()
{
try
{
//Do something you need exception handling
…
}
catch (MyConcreteException)
{
//Do something inside your catch-block
…
}
}
//Override the “System.Exception”-class
private class MyConcreteException : System.Exception { }
}