发明内容
有鉴于此,本发明的目的在于提供一种简单、方便的远程调用通信组件的系统和方法。
根据上述目的,本发明提供了一种实现远程调用通信组件的系统,该系统包括测试端和被测端,所述被测端包括被测对象通信组件,所述测试端包括客户端测试模块,用于通过脚本语言和编解码规则对所要调用通信组件的API接口名和参数进行描述形成测试脚本,根据编解码规则将测试脚本生成测试消息发送给被测端,以及接收从被测端的服务器端测试模块返回的调用结果;所述被测端进一步包括服务器端测试模块,用于解析所述测试消息得到API接口名和参数,并通过根据所述API接口名和参数调用与所述API接口名对应的API接口函数实现对通信组件的调用,以及向测试端的客户端测试模块发送调用结果。
所述脚本语言为树表结合标示语言TTCN、工具命令语言TCL或脚本描述语言Python。
所述测试端的客户端测试模块和被测端的服务器端测试模块之间通过TCP/IP协议建立通信连接。
所述脚本语言为TTCN,所述客户端测试模块进一步包括TTCN模块和TTCN适配器;其中,所述TTCN模块用于通过TTCN和编解码规则对所要调用通信组件的API接口名和参数进行描述形成测试脚本,以及将所述测试脚本传输给TTCN适配器;所述TTCN适配器用于根据编解码规则将所述测试脚本生成测试消息发送给被测端,以及接收从被测端返回的调用结果。
所述编解码规则为抽象符号描述语言ASN.1编解码规则。
所述服务器端测试模块进一步包括消息解释器和API映射模块;其中,所述消息解释器用于从被测端接收的数据中解析出测试消息并传递给API映射模块,以及向测试端的客户端测试模块发送调用结果;所述API映射模块用于解析所述测试消息得到API接口名和参数,以及通过根据所述API接口名和参数调用与所述API接口名对应的API接口函数实现对通信组件的调用。
所述API映射模块进一步包括函数注册表,用于保存API接口名及其对应的API接口函数地址;所述API映射模块进一步用于根据所述函数注册表获取与所述API接口名对应的API接口函数地址,以及通过将所述参数映射到所述API接口函数地址指示的内存空间调用所述API接口函数。
所述API映射模块进一步包括数据缓冲区,用于存放所述参数。
本发明还提供了一种远程调用通信组件的方法,该方法包括以下步骤:
A.测试端通过脚本语言和编解码规则描述所要调用通信组件的API接口名和参数形成测试脚本,并根据编解码规则将测试脚本生成测试消息发送给被测端;
B.被测端解析所述测试消息,得到所述API接口名和参数;
C.被测端通过根据所述API接口名和参数调用与所述API接口名对应的API接口函数实现对通信组件的调用,并向测试端发送调用结果。
步骤A中所述脚本语言为TTCN、TCL或Python;和/或所述编解码规则为ASN.1编解码规则。
预先在被测端保存API接口名与API接口函数地址的对应关系;步骤C中,所述被测端根据所述API接口名和参数调用与所述API接口名对应的API接口函数的步骤包括:根据所保存的对应关系获取与所述API接口名对应的API接口函数地址;通过将所述参数映射到所述API接口函数地址指示的内存空间中调用所述API接口函数。
步骤C中所述向测试端发送调用结果的步骤包括:向测试端发送调用成功或失败的消息;和/或在调用成功且完成后,向测试端发送通信组件的返回值,并在存在输出参数的情况下向测试端发送所述输出参数。
从上述方案中可以看出,由于本发明使用了ASN.1规则之类的编解码规则和TTCN、TCL、Python等脚本语言对待调用通信组件的API接口名和参数进行描述,形成测试脚本,并利用ASN.1编解码规则将测试脚本生成测试消息,然后发送给被测端,在被测端解析测试消息得到API接口名和参数,调用对应的API接口函数,从而实现对通信组件的调用,本发明不需要对API接口设计桩函数,因此本发明所提供的方法和系统具有简单易实现的优点,能够降低组建通信组件测试系统的开发成本,以及降低修改测试数据时的工作量,大大提高了测试数据的自动化程度和复杂测试数据构造的便利性。并且本发明在修改测试数据的时候不需要重新编译,具有良好的可重用性。
具体实施方式
为使本发明的目的、技术方案和优点更加清楚,以下举实施例对本发明进一步详细说明。
与Microsoft的RPC方案不同,本发明在测试端通过脚本语言和编解码规则直接根据API接口名和参数构造测试消息,在被测端解析出测试消息中的API接口名和参数,通过将参数映射到对应的API接口函数运行的内存空间中,直接调用对应的API接口函数,从而实现对通信组件的调用,并且不需要对API接口进行封装,降低了测试结构设计的工作量。
本发明采用的编解码规则为抽象符号描述语言(ASN.1,Abstract SyntaxNotation One)编解码规则,所采用的脚本语言如:树表结合的标示语言(TTCN,Tree and Tabular Combined Notation)、工具命令语言(TCL,ToolCommand language)或者脚本描述语言(Python)等,下面以TTCN为例说明本发明的具体实施方法。
如图2所示的是本发明测试系统的原理结构图。图中测试端即客户端包括客户端测试模块,被测端即服务器端包括服务器端测试模块和被测实现(IUT),所述IUT即为被测通信组件。
其中客户端测试模块利用TTCN和ASN.1规则对所要调用通信组件的API接口名和参数进行描述,形成测试脚本,然后根据ASN.1编解码规则将测试脚本生成二进制数据流格式的测试消息,并发送给服务器端的CComp模块;客户端测试模块还将被测端CComp模块发送过来的返回值和输出参数转换字节序,然后将转换后的返回值传递给应用层、将转换后的输出参数写入测试端的数据缓冲区。
客户端测试模块进一步可分为TTCN模块和TTCN适配器,其中TTCN模块利用TTCN和ASN.1规则对所要调用通信组件的API接口名和参数进行描述,形成测试脚本,并将测试脚本传送给TTCN适配器;TTCN适配器根据ASN.1编解码规则将测试脚本生成测试消息,并通过TCP/IP协议发送给服务器端的CComp模块,TTCN适配器还能将被测端CComp模块发送过来的返回值和输出参数转换字节序,然后将转换后的返回值传递给应用层、将转换后的输出参数写入测试端的数据缓冲区。
图2中被测端的服务器端测试模块与客户端TTCN适配器建立TCP/IP协议的底层通信连接,并解析从底层通信连接接收到的网络数据,从中解析得到测试消息,然后将测试消息进行消息解码,解析出API接口名和具体参数信息,并通过将参数映射到与API接口名对应的API接口函数运行的内存空间中,调用该API接口函数,从而实现对通信组件的调用。
服务器端测试模块进一步可分为消息解释器(CComponent,简称CComp模块)和API映射模块。其中CComp模块与客户端TTCN适配器建立TCP/IP协议的底层通信连接,并解析从底层通信连接接收到的网络数据,从中解析得到测试消息,并将测试消息传送给API映射模块;API映射模块将接收到的测试消息进行消息解码,解析出API接口名和具体参数信息,并通过将参数映射到与API接口名对应的API接口函数运行的内存空间中,调用对应的API接口函数,从而实现对通信组件的调用。
下面分别说明图2中各模块的实现方法。
图2中的客户端TTCN模块利用ASN.1以及TTCN规则对API接口名和参数进行描述,形成测试脚本。以下给出进行描述所用的各种数据类型的示例,其中主要涉及到ASN.1和TTCN的定义,由于ASN.1和TTCN的定义为现有公知技术,这里只对其作简单说明。
类型名称 | 编码类型 |
约束条件 | 扩展的编解码函数 |
B_1 |
二进制字符串(BITSTRING) |
[1] | |
O_1 |
16进制字符串(OCTETSTRING) |
[1] | |
O_4 |
16进制字符串(OCTETSTRING) |
[4] | |
O_N |
16进制字符串(OCTETSTRING) | | |
I_B1 |
整型(INTEGER) | |
BITS(1) |
I_1 |
整型(INTEGER) | |
BITS(8) |
I_4 |
整型(INTEGER) | |
BITS(32) |
I_N |
整型(INTEGER) | | |
IA5Name |
可见字符串(IA5String) | |
TLV(0,0,8,0) |
O_Data |
八进制字符串(OCTETSTRING) | |
TLV(0,0,8,0) |
Load |
可见字符串(IA5String) | |
TLV(1,8,8,0) |
parFunc |
自定义类型(SEQUENCE OF OnePar) | | |
ia5Array |
可见字符串类型(SEQUENCE OF IA5String) | | |
octArray |
16进制字符串类型(SEQUENCE OF OCTET STRING) | | |
表1简单数据结构定义
表1给出了TTCN模块中用到的数据类型定义的示例,表1中第一列为各个数据类型的名称;第二列是对应的编码类型;第三列为约束条件,用于限制变量占用的字节数,该项的数字表示变量占用的字节数;第四列是扩展的ASN.1编解码函数,其中BITS(n)用于限制变量占用的长度为n个bit,TLV表示如何来编码或解码该结构,包含4个参数Tag、TagLen、LenLength、offset,分别为该结构的标识符、该标识符占用的长度、该结构长度值所占用的长度、偏移量,例如TLV(0,0,8,0)表示在编解码该结构时没有Tag标识符,且该结构的长度值占用8位,即1个字节。
在确定数据类型之后即可根据表1的各种数据类型构造参数结构,具体的参数结构如表2、表3和表4所示。
表2给出的是自定义OnePar类型参数的说明,该类型包括:用于声明参数长度的ParaSize,为一个字节长度;用于声明参数类型的ParaPtrL,为一个字节长度,所述参数类型是指参数是指针类型还是非指针类型;用于存放实际参数值的ParData,为可变长度。
类型名称 |
控制观察点(PCO) |
扩展的编解码函数 | |
OnePar | | | |
Field Name(If has) |
域(Field) | |
属性 |
SEQUENCE{ |
parSize |
0_1 | |
DEFAULT’04’0 |
parPtrL |
0_1 | |
DEFAULT’00’0 |
parData |
PDU |
TLV(0,0,16,0) | |
} |
表2参数OnePar结构说明
类型名称 |
控制观察点 |
扩展的编解码函数 |
PtrType | | | |
Field Name(If has) |
域 | |
属性 |
SEQUENCE{ | |
ptrLvl |
0_1 | |
DEFAULT’01’0 |
ptrTag |
0_N | |
DEFAULTGetPtrTag(’01’0) |
ptrDat |
PDU | | |
} | |
表3指针类型参数说明
表3给出的是指针类型参数的说明,该类型包括:用于声明指针级别的ptrLvl,为一个字节长度,所述指针级别是指该指针是单级指针还是多重指针;用于声明指针标示的ptrTag,为一个字节长度;用于存放实际指针数据的ptrData,为可变长度。
表4给出的是调用API接口的结构定义,该结构包括用于声明底层通信通道标识符的h_Comp,它为4个字节长度,h_Comp取0时表示通过函数注册表调用内置函数;所要调用的API接口名apiName,apiName是可见字符串类型,为可变长度,apiName被用来从函数注册表中搜寻实际调用的API接口函数地址;用于声明返回值类型的retPtrL,它为1个字节长度;用于存放实际函数返回值的retData,它的长度可变;用于存放参数数据的parData,为可变长度。
类型名称 |
控制观察点 |
扩展的编解码函数 |
Call | |
TLV(3,8,0,0) | |
Field Name(If has) |
域 | |
属性 |
SEQUENCE{ | |
h_Comp |
0_4 | | |
apiName |
IA5Name | | |
retPtrL |
O_1 | | |
retData |
O_Data | | |
parData |
parFunc | | |
} | |
表4远程调用结构的说明
图2中客户端的TTCN适配器和服务器端的CComp模块之间的交互信息和交互流程如图3所示,用Start、StartAck、Done、Notify等四个原语描述如下:
Start原语:当启动测试过程时,TTCN适配器向CComp模块发送Start原语,携带被调用的API接口名以及参数等信息,启动测试过程;
StartAck原语:当服务器端根据收到的信息对通信组件进行调用后,CComp模块向TTCN适配器发送StarAck原语,告知TTCN适配器调用成功或失败;
Done原语:在通信组件调用结束后,CComp模块向TTCN适配器发送Done原语,携带通信组件的返回值给客户端;
Notify原语:当被测通信组件存在输出参数时,CComp模块向TTCN适配器发送Notify原语,将输出参数传递给测试端。
图2中API映射模块的作用是从消息解释器传送来的测试消息中解析得到所要调用的API接口名及参数,将参数保存到数据缓冲区,根据预先建立的函数注册表寻找对应的API接口函数地址,并通过将数据缓冲区中数据映射到对应的API接口函数地址指示的内存空间中,调用API接口函数,从而实现对通信组件的调用。所述API接口函数指示的内存空间为该API接口函数运行的内存空间,
API映射部分的实现和工作流程如图4所示:
步骤401,构造一个函数注册表,在函数注册表中保存API接口名,以及与API接口名对应的API接口函数地址。构造一个数据缓冲区,用于顺序存放解析测试消息中参数数据后得到的参数。
函数注册表的结构如下:
struct tagProcMap mapProc[]={
{“Sample_Func1”,Sample_Func1},
{“Sample_Func2”,Sample_Func2},
{“Sample_Func3”,Sample_Func3},
……
}
该函数注册表结构中每个单元前面的元素是API接口名,后面的元素是对应的API接口函数地址。
在上述函数注册表中API接口名和API接口函数地址的数据结构为:
Struct TagProcMap{
VOS_CHAR nameProc[256];
VOS_UINT32 h_Proc;
}
在测试过程中循环执行步骤402至步骤406。
步骤402,接收消息解释器传来的测试消息,该测试消息为二进制数据流。
步骤403,解析上述二进制数据流得到API接口名以及参数,并将参数保存到数据缓冲区。如果解析成功,则执行步骤404;如果解析失败,则执行步骤402,即继续接收测试消息。本步骤中参数解析和保存的过程将在后面图5所示的流程描述。
步骤404至步骤406,根据解析得到的API接口名在函数注册表中检索到对应的API接口名并获得与该API接口名对应的API接口函数地址,然后通过将数据缓冲区中的数据映射到API接口函数地址指示的内存空间中,调用对应的API接口函数,从而实现调用通信组件。无论在调用成功或失败后,皆通过CComp模块的StartAck原语向测试端发送相应的调用成功或失败的消息。在调用成功并且完成后,利用CComp模块的Done原语向客户端发送返回值,如果存在输出参数,则利用CComp模块的Notify原语将输出参数发送给测试端。
在上述步骤403中参数的解析和保存流程如图5所示,包括以下步骤:
步骤501,获取参数个数,不妨设为N个,下面从n=1开始解析各个参数。
步骤502至步骤503,解码第n个参数的长度与类型,并判断该参数的类型是否为指针类型,如果该参数的类型为指针类型,则执行步骤506及其后续步骤,否则执行步骤504及其后续步骤。
步骤504,将该参数存储到数据缓冲区。
步骤505,判断解码过程是否结束,即n是否等于N,如果n等于N,表明解码结束,则结束解码流程。否则表明解码未结束,将n加1后执行步骤502。
步骤506,获取当前参数的指针级别。
步骤507至步骤508,将指针替换为一级伪指针,并将数据拷贝到实际指针区。该步骤的目的是将指针值替换为数据实际存放在服务器端的内存位置的指针值,以便于调用API接口函数。这是因为来自客户端的参数中包含的指针信息指向的是客户端的内存地址,在服务器端是不可用的,服务器端需要将指针指向该数据在服务器端存储的内存地址后才可以使用。
步骤509,判断当前指针是否结束,如果结束则执行步骤505,否则执行步骤507。
另外,在上述步骤404中根据测试消息中的API接口名检索获得API接口函数地址的过程如图6所示,包括如下步骤:
步骤601,获取解析出来的API接口名。假设函数注册表中一共有N个API接口函数名,下面从n=1开始在函数注册表中检索。
步骤602至步骤603,获取函数注册表中第n个API接口名,将所解析出来的API接口名与函数注册表中第n个API接口名进行字符串比较,判断两者是否符合,如果两者符合,则执行步骤604,否则执行步骤605。
步骤604,获取函数注册表中第n个API接口名对应的API接口函数地址,结束检索流程。
步骤605,判断注册表是否结束,即n是否等于N,如果n等于N,则结束API检索流程。否则将n加1执行步骤602。
下面以一个调用实例来说明本发明的方法。假设参数类型为:
struct te{
int i1;
int*i2
}
该参数包括两个数据,其中一个i1为整型数据,另一个i2为指针。假设被测通信组件的定义如下:int AddStructParLvl(struct te,int*i){int Sum;
Sum=te.i1+*te.i2;
Return Sum;
}
该通信组件的API接口名为AddStructParLvl,通信组件的作用是将输入的参数Te中的整数i1和指针i2对应的取值相加得到两者之和Sum,并返回Sum。
下面以参数te={10,11}、I=20为例,并通过注册表调用内置函数,即h_Comp=0。参考图5,本实施例的流程如下:
步骤701,TTCN模块利用ASN.1以及TTCN规则对API接口名和参数进行描述,形成测试脚本。
根据前面相关部分的描述,该测试脚本的结构如表5所示。
SEQUENCE{ |
其中各段的数据以及含义说明 |
h_Comp |
0_4 |
00000000表示通过“函数注册表”调用内置函数 |
apiName | IA5Name |
10AddStructPar2Lvl表示被调用的API接口名,其中第一个字节10表示函数名称的长度为16个字节;AddStructPar2Lvl本应是十六进制字节,这里为了方便理解,直接采用了明文。 |
retPtrL |
O_1 |
00表示该函数的返回值的指针级别,此处为非指针,即返回值是函数定义的int。 |
retData | O_Data |
0400000000表示返回的数据长度及空间值。这里表示返回值占用4字节空间,初始化空间值为全0 |
parData |
parFunc |
函数参数的结构见表6和表7 |
} | |
表5测试脚本的结构
步骤702,TTCN适配器利用ASN.1编解码规则将测试脚本生成二进制数据流,并通过Start原语发给被测端的消息解释器。
根据表5、表6和表7的解释,构成的数据流如下:
00000000 10 AddStructPar2Lvl 00 04 00000000 04 01 0A00000019800724 0B000000 04 01 14000000
SEQUENCE{ |
OnePar由参数类型大小、指针级别和数据组成。根据假设,第一个参数取值为te={10,11},其码流为04 01 0A000000 19800724 0B000000,含义如下 |
parSize |
0_1 |
04表示参数类型的大小,此处为一结构,结构名体现为一指针,在本实施例的编译中,指针占用4个字节 |
parPtrL |
0_1 |
01表示函数参数te在访问时是一个一级伪指针 |
parData |
PDU |
0A000000 19800724 0B000000是参数te的编码结果,其中0A000000表示取值10,19800724表示一级伪指针(如果是多级指针,则编码应当生成多个相连的19800724),0B000000表示取值11}; |
} |
|
表6第一个参数Te的解释
SEQUENCE{ |
OnePar由参数类型大小、指针级别和数据组成。根据假设,第二个参数取值为I=20,04 01 14000000,含义如下 |
parSize |
0_1_ |
04表示参数类型的大小,此处为一整数,占用4个字节 |
parPtrL |
0_1_ |
01表示函数参数I在访问时是一个一级指针 |
parData |
PDU |
14000000是参数I的编码结果,表示取值20 |
} | |
表7第二个参数I的解释
步骤703,消息解释器接收到从客户端发送过来的数据后,将其解析,得到测试消息,即步骤702中所示的数据流,然后传送给API映射模块。
步骤704,API映射模块接收到CComp模块的二进制数据流后,进行消息解码,获取所要调用通信组件的API接口名AddStructPar2Lvl和参数,根据API接口名在函数注册表中寻找对应的API接口函数地址。将参数中的指针Te替换后和I存入数据缓冲区,并通过将数据缓冲区中的数据映射到API接口函数地址指示的内存空间中,调用API接口函数,从而实现调用通信组件。调用成功后,通过CComp模块用StartAck原语发送调用成功的消息。
步骤705,调用结束后,由CComp模块用Done原语将返回值Sum发送给客户端,以及用Notify原语携带输出参数给客户端。
步骤706,客户端TTCN适配器将返回值和输出参数转换字节序后,将返回值传递给应用层,以及将输出参数写入客户端的数据缓冲区。
以上所述仅为本发明的较佳实施例而已,并不用以限制本发明,凡在本发明的精神和原则之内,所作的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。