For the visual testing tool Test Monkey we need a special module on each platform that is used as a communication channel between the Test Monkey and the application actually under testing. For Windows this module must be written in Delphi as our front-ends all are written in that language.
The snoop module is written as a Delphi unit, which can be included by any Delphi project to provide the necessary snoop interface for Test Monkey. Communication is done via sockets (TCP/IP), which requires the use of the Indy package in the snoop module. The snoop module runs in the same process as the tested application and must accept connections from the TM, which will send queries to get info like listings of widgets in windows and get info about their state. They should not interfere with the way the application executes. In other words, the TM should not be able to modify the application state with it, only query it.
The Delphi unit will contain a class that listens to a specific TCP/IP port and handles all the requests comming in from the Test Monkey. Since these requests are entirely read commands it should be fairly easy to get the info out that is required. In conjunction with Delphi's VCL hierarchy and runtime type info (RTTI), plus the application's runtime structure it will be relatively easy to get the required info. The TCP/IP port used for communication must be given in one of two ways, otherwise the snoop module stays inactive. Either use the MONKEY_SNOOP_PORT environment variable or give it as a command line parameter to the application to test. The parameter is: "-snoop_port=XXXX" in exact this form (four digit decimal port number), no spaces around the parts of the parameter. If both forms are given the command line gets higher priority. The protocol used for communication follows these rules: - Commands are sent as text lines ending with newline (0x0A) - Replies are sent with a prefix of "TM:", errors with "ERROR:". Lines marked with "ERROR:" are completed by a plain text error description after the colon. - Replies must end with a "TM:" (nothing else following it except for the new line character) - On connection, it must send something like: "TM:DelphiSnoop Ready" "TM:" - Commands and replies are case-sensitiv - Commands are encoded as UTF-8 allowing so Unicode identifiers and captions in replies - Coordinates are always given in screen space not relative to a control's parent window - Bounds are always given as left,top,width,height - There are no spaces around delimiters, be it colons, commas or dots - Reply parts which contain a colon (:) must mask this character by a backslash because the colon is used as separator between those parts. A single backslash must be written as double backslash (\\) to be recognised. In fact any character can be masked by a backslash to take it literally if it has a special meaning otherwise, e.g. new lines/carriage returns in clipboard text. - Widget/Controls may have no name in an application (e.g. because they are created dynamically). In such cases the returned name is their class name accompanied by their memory address as a hex string (without 0x prefix), e.g.: ...:TRadioButton_001A3B5C:... Important here are two attributes, which mark an entry as being in this format: the leading capitel letter T (by convention all types and classes in Delphi start with this letter) and the underbar. So ensure, no class type in an application contains an underbar in its identifier (convention in Delphi is camel case without underbar). - Empty captions are indicated by the NULL keyword. Following command syntax has to be recognized: ============================================================================= General commands, which apply to all widgets/controls ============================================================================= * quit - Causes the application to terminate itself, no parameter is required --> quit TM: * toplevels - returns a list of all currently existing top-level windows: --> list TM:0x95242d0:connect_dialog:GtkWindow:SHOWN:MySQL Administrator TM: Top-level windows are those that have no parent window. Usually an application only has one top level window, unless there are dialog boxes or floating windows. The first field in the response is a pointer value of the object, the secon is the path/name of the window, the third is the type of widget, then follows the visibility state (SHOWN/HIDDEN) and finally the current title of the window. * tree <window-spec> - returns a list of widgets (window controls) in the tree. The window specification can be either the pointer value or the path of the window. --> tree connect_dialog TM:0x95242d0:connect_dialog:GtkWindow:SHOWN TM:0x9528cf8:connect_dialog.vbox2:GtkVBox:SHOWN ... TM: If a path is returned for a specifc control then its members are separated by a dot. * props <window-spec> - returns a list of properties for the widget. --> props connect_dialog TM:_type:GtkWindow TM:user-data:NULL TM:name:connect_dialog TM:parent:NULL TM:width-request:-1 TM:height-request:-1 TM:visible:TRUE TM:sensitive:TRUE TM:app-paintable:FALSE .. TM: Since Delphi controls have a concept of "published properties" these are used for the props command. Subobjects are also returned by using path consisting of the property name and the property's own properties, separated by a dot. * info <window-spec> - returns a list of basic info for the widget: --> info connect_dialog TM:id:0x95242d0 TM:path:connect_dialog TM:name:connect_dialog TM:caption:MySQL Administrator TM:class:GtkWindow TM:handle:0x3800004 TM:visible:TRUE TM:bounds:300,640,418,320 TM: This is a fixed list of core properties that TM always needs to know and hence asks separately for. TM:handle is the window handle of the control and might be NULL if the widget/control is not a window control at all. * bounds <window-spec> - returns the out bounds for the widget --> bounds connect_dialog TM:300,640,418,320 TM: Currently only the bounds of the control are specified as response. Others might follow later. * query <x> <y> - returns the control at position x,y, in screen coordinates --> query 500 600 TM:0x9528cf8:connect_dialog.vbox2:GtkVBox TM: First response parameter is a pointer to the control, second one is its path/name and the third one its type. * check <window-spec> - checks whether the widget exists and returns TRUE or FALSE --> check MainForm.OKButton TM:TRUE TM: * clickable <window-spec> - checks wether a widget exists, is visible, and active. Returns TRUE or FALSE --> clickable MainForm.HiddenButotn TM:FALSE TM: A widget/control is only visible if also all its parents are visible. * waitidle - blocks until the application reaches the event loop --> waitidle TM: Although the snoop module runs in the same thread as the application's main thread, communication is done asynchronously via a socket and a separate communication thread. This command blocks this thread (and blocks so the calling Test Monkey) until the application has entered its idle loop (that is, no pending Windows messages are to be processed). This state indicates that any previously triggered action in the application has been finished and it is safe for the test monkey to continue execution. * flash <window-spec> - quickly flashes the specified control --> flash LoginDialog.CancelButton TM: Flashing is done by highlighting the control. If an invalid window-spec was given an error is returned. * childcount <window-spec> - returns number of child controls/widgets in a given widget --> childcount Painter.Canvas TM:3 TM: * clipboard - returns the content of the clipboard if it is just text. --> clipboard TM:Some longer text\: which might contain\n\rmultiple lines. TM: If anything else but text is on the clipboard then nothing is returned. ============================================================================= List commands (this includes list boxes, treeviews and listviews) ============================================================================= * list.textpos <list-spec> <column> <text> - returns the screen position of the item with the given text, in the given column. --> list.textpos LoginDialog.ConnectionList 0 "Local MySQL server" TM:506,781 TM: list-spec is a control/widget that comprises a list of items, possibly displayed in a hierarchical manner (i.e. indented like in a treeview) and with one or more columns. The result is the coordinate of an item in that control having the given text. This item is usually not a control on its own, but just an entry in the list widget. The returned position (given in screen coordinates) is the upper left corner of the item, where the vertical position is relative to the client space of the list control (taking so the current scroll state into account) and the horizontal distance is the left border of the column in which the item is (so there is no consideration of additional elements like indentation, images, checkboxes and the like). If the element is currently not visible in the client area of the list widget then it is as would it not exist and nothing is returned. Note: the text can be given in single or double quotes (which are stripped before using the string). Alternativly, special characters like spaces can be masked using a backslash. * list.indexpos <list-spec> <index> - returns the screen position of the item at the given index --> list.textpos LoginDialog.ConnectionList 12345 TM:506,781 TM: This command is basically the same the previous one with the exception that instead of a text an index is given. This index indicates which item to consider, which must exist (the index is >= 0 and < the item count) and must be visible in the client area (that is, the control must be scrolled so that the item is currently visible on screen). * list.selected <list-spec> - returns a list of all selected items in the list --> list.selected MainForm.DirectoryListing TM:1 TM:15 TM:100 .. TM: * list.checkstate <list-spec> - returns a list of all checked items in the list, if the list supports check states --> list.checkstate MainForm.BackupList TM:10 TM:11 .. TM: Note: this list must be ordered by increasing index ============================================================================= Special commands, which are specific to certain controls only ============================================================================= * checkbox.checkstate <window-spec> - returns the current check state of a single check box Applies only to standalone checkboxes --> checkstate LoginDialog.UseSSL TM:TRUE TM: * radiogroup.selected <window-spec> - returns the currently selected entry in a radio group (a group of radio buttons handled automatically as one single widget/control) --> radiogroup.selected Options.PasswortStorage TM:3 TM: This command also handles individiual radio buttons if they are within a common container. In this case give the container as the window-spec. The returned index is then the index of the selected radio button in a list of all radio buttons in this common container, collected in creation order (zero based). If there is no radio button in such a common container (or the radio group) or if no button is currently selected an empty TM: line is returned. * range.info <window-spec> - returns the current position, minimum and maximum values of controls that display values within a range, like progress bars, trackbars, up-down buttons, spinner, gauges etc. --> range.position Work.Progress TM:min:0 TM:max:100 TM:pos:90 TM:selstart:4 TM:selend:16 TM: Note: :selstart: and :selend: are optional, as they are not supported by all range controls. * datetime.info <windo-spec> - returns date info from a date/time picker control --> datetime.info Options.BackupStart TM:date:2006-10-29 TM:time:17\:30 TM: alternatively: .. TM:time:5\:30pm TM: Note: both :date: and :time: entries are optional but usually at least one of both is given, depending on the control/widget which is queried. * menus - returns all defined menus in the application --> menus TM:0x95242d0:MainMenu:TMainMenu TM:0x71142aa:PaintContextMenu:TPopupMenu ... TM: * menu.items <menu-spec> - returns a list of menu items in the given menu (or sub menu) Syntax: TM:item-id:path:class:flags:submenu-id:bounds:caption --> menu.items TM:0x95242d0:MainMenu.SaveAllItem:TMenuItem:CHECKED,DEFAULT,ENABLED:0x12345678:100,200,40,10:Save all... TM: Following flags can be returned (comma separated list), order is not important: AUTOCHECK CHECKED DEFAULT ENABLED RADIOITEM VISIBLE If none of the flags are active currently then NULL is returned instead.