Symbian 测试框架调研笔记

symbian没有合适的自动测试框架。不是java的东西就是麻烦。

摘自Nokia wiki, RWindow::Construct

Construct ( const RWindowTreeNode &, TUint32 )

IMPORT_C TInt Construct ( const RWindowTreeNode & parent,
TUint32 aHandle
)

Completes the construction of the window handle.

This method should be called after the RWindow() constructor, before any other functions are performed on the window. It creates a window in the window server corresponding to the RWindow object. The window is initialised to inherit the size and extent of its parent window, given by the first parameter. If its parent is a group window then it will be full screen.

This function always causes a flush of the window server buffer.

 

Parameter Description
parent The window’s parent.
aHandle Client handle for the window. This is an integer value chosen by the client that must be unique within the current server session. The usual way of doing this is to cast the address of the object that owns the window to a TUint32; this allows event handlers which are given a window handle to obtain a reference to the window an event is intended for. For example, CCoeControl uses this technique when it constructs a window. Note that in GUI applications, every window is created and owned by a control. Therefore it is rare for 3rd party code to ever need to call a window’s Construct() function directly.

Returns: KErrNone if successful, otherwise one of the system-wide error codes.

这里的解释很重要,The usual way of doing this is to cast the address of the object that owns the window to a TUint32; this allows event handlers which are given a window handle to obtain a reference to the window an event is intended for. For example, CCoeControl uses this technique when it constructs a window. Note that in GUI applications, every window is created and owned by a control. 也就是说,RWindow的handle可以立即强转为一个CCoeControl*,并且就是构造他的控件对象。

摘自Nokia wiki, RWindowTreeNode::ClientHandle()

ClientHandle ( )

IMPORT_C TUint32 ClientHandle ( ) const

Gets the window’s client handle

The return value is the client’s integer handle that was passed as an argument to the window’s Construct() function: see RWindow::Construct() for a description of the client handle.

This function always causes a flush of the window server buffer.

 

See also: RWindow::Construct()

Returns: Handle ID for the window.

也就是说,通过这个函数可以获取到对应的控件指针。

摘自Nokia wiki, RWindowTreeNode::Child()

Child ( )

IMPORT_C TUint32 Child ( ) const

Gets the first child of the node.

This function always causes a flush of the window server buffer.

 

Returns: The client handle of the child node that currently has ordinal position 0. This is 0 if there isn’t a child.

获取TreeNode的孩子节点。使用NextSibling继续向下遍历。

注意到RWindowGroup也是继承自RWindowTreeNode的。一个测试思路就是,获取到当前WindowGroup,然后遍历整棵树,获取每个节点对应的CCoeControl。暂时不知道Symbian系统上能不能做Dynamic Cast。理论上来说配合RTTI可以了解到每个节点的类型,并对特定的类型调用特定的方法进一步处理。不过跨进程做这种事情恐怕还是有问题。不知能不能把测试程序作为一个dll植入被测程序去运行。这就可以实现Android上Hierarchy Viewer的功能了。

简单记录以作备忘。哪位兄弟知道成熟的symbian测试框架也请不吝赐教。

Symbian 系统学习-活动对象和其他

最近做了一个android monkey test到symbian的移植。算是草草学习了一下symbian系统吧。写一些东西做个记录,方便以后自己查阅。

symbian系统最有意思的设计应该是这个Active Object的设计模型了。他使用非抢占的多任务模型,固定优先级。这个非抢占我一开始理解得不够好。后来做monkey的时候,用的console程序。想在里面用CActive做进程监视,发现他的RunL函数永远都无法进入。跟公司的symbian大牛也讨论了好久都没有解决问题。最后还是自己悟了。这个所谓的非抢占,就是说你console的主函数不退出的话,这个Active scheduler是不会调度的。之所以那位大牛写的demo可以运行起来,是因为他用的是窗口工程,在一个窗口回调函数里面调用的SetActive。窗口回调函数退出之后自然而然Active scheduler就会调度了。而且估计这个窗口消息的处理,symbian也是采用Active Object来实现的。

我本来以为,只要我在主函数里面调用一个引发调度的函数就好了。我想当然地调用了sleep函数,然完全没有效果。还有后来改用User::After函数,也完全没有用。我后来想,论坛上经常有人提醒说,这个User::After函数是同步的。究竟有多同步,这我才知道了。就是说他也是不会引发AO调度的。这样一想也是可以理解的。因为他说的是非抢占嘛,就是说如果你睡眠了把CPU让给别人,等你到了该睡醒的时间,别人正在运行,那你就没有机会再把这个CPU抢占回来了。假如说做成双方都可以互相协作让CPU的,那基本上就算是协程了。这里的AO,如同网上一些讨论一样,应该算是“纤程”吧,可惜还没腾出时间去仔细研究他。本来想仔细去研究一下AO调度,究竟哪些函数(确切的说应该是系统调用)是可以引发AO调度的,但毕竟还是公司的任务优先吧,这个没有去细查,用了另外一个进程,乖乖写成窗口程序来做了。有机会的话以后再来改吧。

Symbian另外一个有意思的设定就是process崩溃之后不会立即退出。而是会继续保留一段时间,之后系统会把这些死进程统一回收。一开始还不是很理解他为什么要这么做,可是后来做进程监视的时候就发现,这个设定非常好用。你可以在进程崩溃之后,再去遍历系统的进程表,然后找到有问题的进程(他的退出码会是负数),然后找到具体的错误原因。其实这个工作就叫做“验尸”,而且symbian也提供了一些验尸工具去帮助调试。这样就给了系统调试很大的自由空间。否则的话,必须在一开始就确定监视哪些进程。假如有些进程,例如我现在在调试的这个(哔—),他是随着用户操作过程中而时而启动时而退出的,你很难去一直捕获着他,去监视他的运行情况。

还有一个就是本来对于symbian程序员来说是司空见惯的东西吧,就是这个清除栈CleanupStack,还有对象的所谓二次构造,以及symbian自有的一套类似于try catch的机制,是用的Leave和trap来实现。这些东西应该就是symbian的系统特点了吧,其他系统上是绝对见不到这些东西的。从异常安全的角度来说,这个二次构造真的是合理的设计吗?我觉得也不见得。不过他配合清除栈的使用,确实能够保证内存泄漏。这在内存紧缺同时开机时间甚至可能长达数年的嵌入式产品来说,的的确确是至关重要的。否则即使是非常微小的内存泄漏,假如连续开机数年(这里肯定不是手机啦,比如说是电冰箱咖啡壶什么的),还是会引起系统崩溃的。

最后就是监视到进程崩溃如何获取调用栈信息的问题。本来的想法甚至是去获取CPU寄存器的值,然后根据可以得到的线程列表中获取到的堆栈地址的信息,配合编译过程中生成的.exe.map,进行函数地址对比,从而得出调用信息。后来发现这种事情不用自己去做(而且在用户这个层次应该是很难获取到CPU寄存器的值的吧)。symbian提供了一个很好的内核调试工具,d_exc.exe,可以在系统中有任何进程崩溃时记录调用栈信息。需要的只是去把这个程序在测试开始之前运行起来就好了。至于symbian的内核端调试,nokia wiki上有一篇很好的文章,我还没找时间好好研究,这里也先记在这里kernel side debug

好啦今天的笔记就做到这里。并非是API介绍,假如说有人有兴趣想知道具体的内容的话,这里特别推荐的是nokia官方论坛的wiki,内容非常充实,对于我这样的初学者来说,非常有帮助。
例如这篇process monitor,还有这篇process and threads, how to find them等等。