From 6a53f96e06fbf7a23d7eb40c35a711e9de103eaf Mon Sep 17 00:00:00 2001 From: Robby Findler Date: Sun, 27 Apr 2008 02:55:21 +0000 Subject: [PATCH] ported the documentation for the framework's test library -- also extended srcdoc.ss a little bit svn: r9499 --- collects/framework/test.ss | 361 +++++++++--------- collects/scribble/srcdoc.ss | 86 ++++- .../framework/framework-test.scrbl | 112 +++++- .../scribblings/framework/framework.scrbl | 115 ------ 4 files changed, 357 insertions(+), 317 deletions(-) diff --git a/collects/framework/test.ss b/collects/framework/test.ss index d3e8b84fc0..4a89b45751 100644 --- a/collects/framework/test.ss +++ b/collects/framework/test.ss @@ -821,187 +821,121 @@ (define test:mouse-click mouse-click) (define test:new-window new-window) -#; (provide/doc - (proc-doc - test:number-pending-actions - (-> number?) - @{Returns the number of pending events (those that haven't completed yet)})) - -(provide/contract/docs - (test:number-pending-actions - (-> number?) - () - "Returns the number of pending events (those that haven't completed yet)") - - (test:run-interval - (case-> - (number? . -> . void?) - (-> number?)) - ((msec) ()) - "See also" - "\\hyperref{Actions and completeness}{Actions and completeness, section~}{}{fw:actions-completeness}." - "The first case in the case-lambda sets" - "the run interval to \\var{msec} milliseconds and the second" - "returns the current setting.") - - (test:reraise-error - (-> void?) - () - "See also" - "\\hyperref{Errors}{Errors, section~}{}{fw:test:errors}.") - - (test:run-one - ((-> void?) . -> . void?) - (f) - "Runs the function \\var{f} as if it was a simulated event. See also" - "\\hyperref{the test section}{section ~}{}{fw:test}.") - - (test:current-get-eventspaces - (case-> - ((-> (listof eventspace?)) . -> . void?) - (-> (-> (listof eventspace?)))) - ((func) ()) - - "This parameter that specifies which " - "\\hyperref{eventspaces}{eventspace (see section~}{)}{eventspaceinfo}" - "are considered when finding the frontmost frame." - - "The first case" - "sets the parameter to \\var{func}. The procedure \\var{func} will be" - "invoked with no arguments to determine the eventspaces to consider" - "when finding the frontmost frame for simulated user events." - - "The second case" - "returns the current value of the parameter. This will be a procedure" - "which, when invoked, returns a list of eventspaces.") - (test:close-top-level-window - ((is-a?/c top-level-window<%>) . -> . void?) - (tlw) - "Use this function to simulate clicking on the close box of a frame." - "Closes \\var{tlw} with this expression:" - "" - "\\begin{schemedisplay}" - "(when (send tlw can-close?)" - " (send tlw on-close)" - " (send tlw show #f))" - "\\end{schemedisplay}") - - (test:top-level-focus-window-has? - (((is-a?/c area<%>) . -> . boolean?) . -> . boolean?) - (test) - "Calls \\var{test} for each child of the top-level-focus-frame" - "and returns \\scheme|#t| if \\var{test} ever does, otherwise" - "returns \\scheme|#f|. If there" - "is no top-level-focus-window, returns \\scheme|#f|.") - - ;; ((frame-has? p) f) = - ;; f is a frame and it has a child (in it or a subpanel) that responds #t to p - (test:button-push - ((or/c (λ (str) - (and (string? str) - (test:top-level-focus-window-has? - (λ (c) - (and (is-a? c button%) - (string=? (send c get-label) str) - (send c is-enabled?) - (send c is-shown?)))))) - - (and/c (is-a?/c button%) - (λ (btn) - (and (send btn is-enabled?) - (send btn is-shown?))) - (λ (btn) - (test:top-level-focus-window-has? - (λ (c) (eq? c btn)))))) - . -> . - void?) + (proc-doc/names + test:button-push + (-> (or/c (λ (str) + (and (string? str) + (test:top-level-focus-window-has? + (λ (c) + (and (is-a? c button%) + (string=? (send c get-label) str) + (send c is-enabled?) + (send c is-shown?)))))) + + (and/c (is-a?/c button%) + (λ (btn) + (and (send btn is-enabled?) + (send btn is-shown?))) + (λ (btn) + (test:top-level-focus-window-has? + (λ (c) (eq? c btn)))))) + void?) (button) - "Simulates pushing \\var{button}. If a string is supplied, the" - "primitive searches for a button labelled with that string in the" - "active frame. Otherwise, it pushes the button argument.") + @{Simulates pushing @scheme[button]. If a string is supplied, the + primitive searches for a button labelled with that string in the + active frame. Otherwise, it pushes the button argument.}) - (test:set-radio-box! - ((or/c string? (is-a?/c radio-box%)) (or/c string? number?) . -> . void?) + (proc-doc/names + test:set-radio-box! + (-> (or/c string? (is-a?/c radio-box%)) (or/c string? number?) void?) (radio-box state) - "Sets the radio-box to \\var{state}. If \\var{state} is a" - "string, this function finds the choice with that label and" - "if it is a number, it uses the number as an index into the" - "state. If the number is out of range or if the label isn't" - "in the radio box, an exception is raised." - "" - "If \\var{radio-box} is a string, this function searches for a" - "\\iscmclass{radio-box} with a label matching that string," - "otherwise it uses \\var{radio-box} itself.") + @{Sets the radio-box to @scheme[state]. If @scheme[state] is a + string, this function finds the choice with that label and + if it is a number, it uses the number as an index into the + state. If the number is out of range or if the label isn't + in the radio box, an exception is raised. + + If @scheme[radio-box] is a string, this function searches for a + @scheme[radio-box%] object with a label matching that string, + otherwise it uses @scheme[radio-box] itself.}) - (test:set-radio-box-item! - (string? . -> . void?) + (proc-doc/names + test:set-radio-box-item! + (-> string? void?) (entry) - "Finds a \\iscmclass{radio-box} that has a label \\var{entry}" - "and sets the radio-box to \\var{entry}.") - (test:set-check-box! - ((or/c string? (is-a?/c check-box%)) boolean? . -> . void?) + @{Finds a @scheme[radio-box%] that has a label @scheme[entry] + and sets the radio-box to @scheme[entry].}) + + (proc-doc/names + test:set-check-box! + (-> (or/c string? (is-a?/c check-box%)) boolean? void?) (check-box state) - "Clears the \\iscmclass{check-box} item if \\var{state} is \\rawscm{\\#f}, and sets it" - "otherwise." - "" - "If \\var{check-box} is a string," - "this function searches for a \\iscmclass{check-box} with a label matching that string," - "otherwise it uses \\var{check-box} itself.") + @{Clears the @scheme[check-box%] item if @scheme[state] is @scheme[#f], and sets it + otherwise. + + If @scheme[check-box] is a string, + this function searches for a @scheme[check-box%] with a label matching that string, + otherwise it uses @scheme[check-box] itself.}) - (test:set-choice! - ((or/c string? (is-a?/c choice%)) (or/c string? (and/c number? exact? integer? positive?)) . -> . void?) + (proc-doc/names + test:set-choice! + (-> (or/c string? (is-a?/c choice%)) (or/c string? (and/c number? exact? integer? positive?)) + void?) (choice str) - "Selects \\var{choice}'s item \\var{str}. If \\var{choice} is a string," - "this function searches for a \\iscmclass{choice} with a label matching" - "that string, otherwise it uses \\var{choice} itself.") + @{Selects @scheme[choice]'s item @scheme[str]. If @scheme[choice] is a string, + this function searches for a @scheme[choice%] with a label matching + that string, otherwise it uses @scheme[choice] itself.}) - (test:set-list-box! - ((or/c string? (is-a?/c list-box%)) (or/c string? (and/c number? exact? integer? positive?)) . -> . void?) + (proc-doc/names + test:set-list-box! + (-> (or/c string? (is-a?/c list-box%)) (or/c string? (and/c number? exact? integer? positive?)) + void?) (choice str) - "Selects \\var{list-box}'s item \\var{str}. If \\var{list-box} is a string," - "this function searches for a \\iscmclass{list-box} with a label matching" - "that string, otherwise it uses \\var{list-box} itself.") + @{Selects @scheme[list-box]'s item @scheme[str]. If @scheme[list-box] is a string, + this function searches for a @scheme[list-box%] with a label matching + that string, otherwise it uses @scheme[list-box] itself.}) - (test:keystroke + (proc-doc/names + test:keystroke (->* ((or/c char? symbol?)) ((listof (symbols 'alt 'control 'meta 'shift 'noalt 'nocontrol 'nometea 'noshift))) void?) ((key) ((modifier-list null))) - "This function simulates a user pressing a key. The argument, \\var{key}," - "is just like the argument to the" - "@link key-event get-key-code" - "method of the" - "@link key-event" - "class. " - "" - "{\\it Note:}" - "To send the ``Enter'' key, use \\verb|#\return|," - "not \\verb|#\newline|." - "" - "The \\rawscm{'shift} or \\rawscm{'noshift} modifier is implicitly set from \\var{key}," - "but is overridden by the argument list. The \\rawscm{'shift} modifier is" - "set for any capitol alpha-numeric letters and any of the following characters:" - "\\begin{schemedisplay}" - "#\\? #\\: #\\~ #\\\\ #\\|" - "#\\< #\\> #\\{ #\\} #\\[ #\\] #\\( #\\)" - "#\\! #\\@ #\\# #\\$ #\\% #\\^ #\\& #\\* #\\_ #\\+" - "\\end{schemedisplay}" - "" - "If conflicting modifiers are provided, the ones later in the list are used.") + @{This function simulates a user pressing a key. The argument, @scheme[key], + is just like the argument to the + @method[key-event% get-key-code] + method of the @scheme[key-event%] class. + + @italic{Note:} + To send the ``Enter'' key, use @scheme[#\return], + not @scheme[#\newline]. + + The @scheme['shift] or @scheme['noshift] modifier is implicitly set from @scheme[key], + but is overridden by the argument list. The @scheme['shift] modifier is + set for any capitol alpha-numeric letters and any of the following characters: + @schemeblock[ + #\? #\: #\~ #\\ #\| + #\< #\> #\{ #\} #\[ #\] #\( #\) + #\! #\@ #\# #\$ #\% #\^ #\& #\* #\_ #\+ + ] + + If conflicting modifiers are provided, the ones later in the list are used.}) - (test:menu-select + (proc-doc/names + test:menu-select (string? string? . -> . void?) (menu item) - "Selects the menu-item named \\var{item} in the menu named \\var{menu}." - "" - "{\\it Note:}" - "The string for the menu item does not include its keyboard equivalent." - "For example, to select ``New'' from the ``File'' menu, " - "use ``New'', not ``New Ctrl+m n''.") + @{Selects the menu-item named @scheme[item] in the menu named @scheme[menu]. + + @italic{Note:} + The string for the menu item does not include its keyboard equivalent. + For example, to select ``New'' from the ``File'' menu, + use ``New'', not ``New Ctrl+m n''.}) - (test:mouse-click + (proc-doc/names + test:mouse-click (->* ((symbols 'left 'middle 'right) (and/c exact? integer?) @@ -1010,27 +944,90 @@ void?) ((button x y) ((modifiers null))) - "Simulates a mouse click at the coordinate: $(x,y)$ in the currently" - "focused \\iscmintf{window}, assuming that it supports the " - "@ilink canvas on-event" - "method." - "Use" - "@flink test:button-push" - "to click on a button." - "" - "On the Macintosh, \\rawscm{'right} corresponds to holding down the command" - "modifier key while clicking and \\rawscm{'middle} cannot be generated." - "" - "Under Windows, \\rawscm{'middle} can only be generated if the user has a" - "three button mouse." - "" - "The modifiers later in the list \\var{modifiers} take precedence over" - "ones that appear earlier.") + @{Simulates a mouse click at the coordinate (x,y) in the currently + focused @scheme[window], assuming that it supports the + @method[canvas<%> on-event] method. + Use @scheme[test:button-push] to click on a button. + + On the Macintosh, @scheme['right] corresponds to holding down the command + modifier key while clicking and @scheme['middle] cannot be generated. + + Under Windows, @scheme['middle] can only be generated if the user has a + three button mouse. + + The modifiers later in the list @scheme[modifiers] take precedence over + ones that appear earlier.}) + + (proc-doc/names + test:run-interval + (case-> + (number? . -> . void?) + (-> number?)) + ((msec) ()) + @{See also @secref{test:actions-completeness}. + The first case in the case-lambda sets + the run interval to @scheme[msec] milliseconds and the second + returns the current setting.}) - (test:new-window - ((is-a?/c window<%>) . -> . void?) + (parameter-doc + test:current-get-eventspaces + (parameter/c (-> (listof eventspace?))) + func + + @{This parameter that specifies which + \\hyperref{eventspaces}{eventspace see section~}{}{eventspaceinfo} + are considered when finding the frontmost frame. + The first case + sets the parameter to @scheme[func]. The procedure @scheme[func] will be + invoked with no arguments to determine the eventspaces to consider + when finding the frontmost frame for simulated user events. + The second case + returns the current value of the parameter. This will be a procedure + which, when invoked, returns a list of eventspaces.}) + + (proc-doc/names + test:new-window + (-> (is-a?/c window<%>) void?) (window) - "Moves the keyboard focus to a new window within the currently active" - "frame. Unfortunately, neither this function nor any other function in" - "the test engine can cause the focus to move from the top-most (active)" - "frame. ")) + @{Moves the keyboard focus to a new window within the currently active + frame. Unfortunately, neither this function nor any other function in + the test engine can cause the focus to move from the top-most (active) frame.}) + + (proc-doc/names + test:close-top-level-window + (-> (is-a?/c top-level-window<%>) void?) + (tlw) + @{Use this function to simulate clicking on the close box of a frame. + Closes @scheme[tlw] with this expression: + @schemeblock[ + (when (send tlw can-close?) + (send tlw on-close) + (send tlw show #f))]}) + + (proc-doc/names + test:top-level-focus-window-has? + (-> (-> (is-a?/c area<%>) boolean?) boolean?) + (test) + @{Calls @scheme[test] for each child of the top-level-focus-frame + and returns @scheme[#t] if @scheme[test] ever does, otherwise + returns @scheme[#f]. If there + is no top-level-focus-window, returns @scheme[#f].}) + + + (proc-doc + test:number-pending-actions + (-> number?) + @{Returns the number of pending events (those that haven't completed yet)}) + + (proc-doc + test:reraise-error + (-> void?) + @{See also @secref{test:errors}.}) + + (proc-doc/names + test:run-one + (-> (-> void?) void?) + (f) + @{Runs the function @scheme[f] as if it was a simulated event.})) + + diff --git a/collects/scribble/srcdoc.ss b/collects/scribble/srcdoc.ss index 01a812d493..9817d3a189 100644 --- a/collects/scribble/srcdoc.ss +++ b/collects/scribble/srcdoc.ss @@ -6,7 +6,9 @@ (provide require/doc provide/doc - proc-doc) + parameter-doc + proc-doc + proc-doc/names) (define-syntax-rule (require/doc spec ...) (void (quote-syntax (require/doc spec ...)))) @@ -53,26 +55,18 @@ (lambda (stx) (syntax-case stx () [(_ id contract desc) - (with-syntax ([(arg ...) - (syntax-case #'contract (->d) - [(->d (req ...) () result) - #'(req ...)] + (with-syntax ([((arg ...) result) + (syntax-case #'contract (->d -> values) + [(->d (req ...) () (values [name res] ...)) + #'((req ...) (values res ...))] + [(->d (req ...) () [name res]) + #'((req ...) res)] + [(-> result) + #'(() result)] [else (raise-syntax-error #f - "unsupported procedure contract form (arguments)" - stx - #'contract)])] - [result - (syntax-case #'contract (->d) - [(->d reqs opts (values [name res] ...)) - #'(values res ...)] - [(->d reqs opts [name res]) - #'res] - [else - (raise-syntax-error - #f - "unsupported procedure contract form (arguments)" + "unsupported procedure contract form (no argument names)" stx #'contract)])]) (values @@ -80,4 +74,58 @@ #'(defproc (id arg ...) result . desc) #'(scribble/manual)))]))) - +(define-provide/doc-transformer proc-doc/names + (lambda (stx) + (syntax-case stx () + [(_ id contract names desc) + (with-syntax ([header + (syntax-case #'(contract names) (->d -> values) + [((-> ctcs ... result) (arg-names ...)) + (begin + (unless (= (length (syntax->list #'(ctcs ...))) + (length (syntax->list #'(arg-names ...)))) + (raise-syntax-error #f "mismatched argument list and domain contract count" stx)) + #'([(id (arg-names ctcs) ...) result]))] + + [((->* (mandatory ...) (optional ...) result) + ((mandatory-names ...) + ((optional-names optional-default) ...))) + (begin + (unless (= (length (syntax->list #'(mandatory-names ...))) + (length (syntax->list #'(mandatory ...)))) + (raise-syntax-error #f "mismatched mandatory argument list and domain contract count" stx)) + (unless (= (length (syntax->list #'(optional-names ...))) + (length (syntax->list #'(optional ...)))) + (raise-syntax-error #f "mismatched mandatory argument list and domain contract count" stx)) + #'([(id (mandatory-names mandatory) ... (optional-names optional optional-default) ...) + result]))] + [((case-> (-> doms ... rng) ...) + ((args ...) ...)) + (begin + (for-each + (λ (doms args) + (unless (= (length (syntax->list doms)) + (length (syntax->list args))) + (raise-syntax-error #f "mismatched case argument list and domain contract" stx))) + (syntax->list #'((doms ...) ...)) + (syntax->list #'((args ...) ...))) + #'([(id (args doms) ...) rng] ...))] + [else + (raise-syntax-error + #f + "unsupported procedure contract form (no argument names)" + stx + #'contract)])]) + (values + #'[id contract] + #'(defproc* header . desc) + #'(scribble/manual)))]))) + +(define-provide/doc-transformer parameter-doc + (lambda (stx) + (syntax-case stx (parameter/c) + [(_ id (parameter/c contract) arg-id desc) + (values + #'[id (parameter/c contract)] + #'(defparam id arg-id contract . desc) + #'(scribble/manual))]))) diff --git a/collects/scribblings/framework/framework-test.scrbl b/collects/scribblings/framework/framework-test.scrbl index 4119fed266..4cd7eb1ab5 100644 --- a/collects/scribblings/framework/framework-test.scrbl +++ b/collects/scribblings/framework/framework-test.scrbl @@ -1,8 +1,118 @@ #lang scribble/doc -@(require scribble/manual) +@(require scribble/manual scribble/extract) @(require (for-label framework/framework)) @(require (for-label scheme/gui)) @title{Test} @(require framework/framework-docs) +@(defmodule framework/test) + +The framework provides several new primitive functions that simulate +user actions, which may be used to test applications. You use these +primitives and combine them just as regular MzScheme functions. For +example, +@schemeblock[ +(test:keystroke #\A) +(test:menu-select "File" "Save") +] +sends a keystroke event to the window with the keyboard focus and invokes +the callback function for the ``Save'' menu item from the ``File'' menu. +This has the same effect as if the user typed the key ``A'', pulled +down the ``File'' menu and selected ``Save''. + +It is possible to load this portion of the framework without loading +the rest of the framework. Use +@scheme[(require framework/test)]. + +Currently, the test engine has primitives for pushing +buttons, setting check-boxes and choices, sending keystrokes, +selecting menu items and clicking the mouse. Many functions +that are also useful in application testing, such as +traversing a tree of panels, getting the text from a canvas, +determining if a window is shown, and so on, exist in MrEd. + +@section[#:tag "test:actions-completeness"]{Actions and completeness} + +The actions associated with a testing primitive may not have finished +when the primitive returns to its caller. +Some actions may yield control before they can complete. +For example, selecting ``Save As...'' from the ``File'' menu +opens a dialog box and will not complete until the ``OK'' +or ``Cancel'' button is pushed. + +However, all testing functions wait at least a minimum interval +before returning to give the action a chance to finish. +This interval controls the speed at which the test suite runs, +and gives some slack time for events to complete. +The default interval is 100 milliseconds. The interval can be queried +or set with @scheme[test:run-interval]. + +A primitive action will not return until the run-interval has +expired and the action has finished, raised an error, or yielded. +The number of incomplete actions is given by +@scheme[test:number-pending-actions]. + +@italic{Note:} +Once a primitive action is started, it is not possible to undo it +or kill its remaining effect. +Thus, it is not possible to write a utility that flushes the +incomplete actions and resets number-pending-actions to zero. + +However, actions which do not complete right away often provide a +way to cancel themselves. +For example, many dialog boxes have a ``Cancel'' button which will +terminate the action with no further effect. +But this is accomplished by sending an additional action +(the button push), not by undoing the original action. + +@section[#:tag "test:errors"]{Errors} + +Errors in the primitive actions (which necessarily run in the +handler thread) are caught and reraised in the calling thread. + +However, the primitive actions can only guarantee that the action +has started, and they may return before the action has completed. +As a consequence, an action may raise an error long after the +function that started it has returned. +In this case, the error is saved and reraised at the first opportunity +(the next primitive action). + +The test engine keeps a buffer for one error, saving only the +first error. Any subsequent errors are discarded. +Reraising an error empties the buffer, allowing the next error +to be saved. + +The function @scheme[test:reraise-error] +reraises any pending errors. + +@section{Technical Issues} + +@subsection{Active Frame} + +The Self Test primitive actions all implicitly apply to the +top-most (active) frame. + +@subsection{Thread Issues} + +The code started by the primitive actions must run in the handler +thread of the eventspace where the event takes place. As a result, +the test suite that invokes the primitive actions must @italic{not} run +in that handler thread (or else some actions will deadlock). See +\Mrhyperref{the eventspace section}{see section~}{}{eventspaceinfo} +for more info. + +@subsection{Window Manager (Unix only)} + +In order for the Self Tester to work correctly, the window manager +must set the keyboard focus to follow the active frame. +This is the default behavior in Microsoft Windows and MacOS, +but not in X windows. + +In X windows, you must explicitly tell your window manager to set the +keyboard focus to the top-most frame, regardless of the position of the +actual mouse. + +@section{Test Functions} + @(def-fw-procs test) +@(include-extracted (lib "test.ss" "framework")) diff --git a/collects/scribblings/framework/framework.scrbl b/collects/scribblings/framework/framework.scrbl index 4dd4a82ab9..f765257103 100644 --- a/collects/scribblings/framework/framework.scrbl +++ b/collects/scribblings/framework/framework.scrbl @@ -74,121 +74,6 @@ The precise set of exported names is: @scheme[preferences:restore-defaults]. }} -@section{GUI Test Suite Utilities} - -The framework provides several new primitive functions that simulate -user actions, which may be used to test applications. You use these -primitives and combine them just as regular MzScheme functions. For -example, -@schemeblock[ -(test:keystroke #\A) -(test:menu-select "File" "Save") -] -sends a keystroke event to the window with the keyboard focus and invokes -the callback function for the ``Save'' menu item from the ``File'' menu. -This has the same effect as if the user typed the key ``A'', pulled -down the ``File'' menu and selected ``Save''. - -It is possible to load this portion of the framework without loading -the rest of the framework. Use -@scheme[(require framework/test)]. - -Currently, the test engine has primitives for pushing -buttons, setting check-boxes and choices, sending keystrokes, -selecting menu items and clicking the mouse. Many functions -that are also useful in application testing, such as -traversing a tree of panels, getting the text from a canvas, -determining if a window is shown, and so on, exist in MrEd. - -@subsection{Actions and completeness} - -The actions associated with a testing primitive may not have finished -when the primitive returns to its caller. -Some actions may yield control before they can complete. -For example, selecting ``Save As...'' from the ``File'' menu -opens a dialog box and will not complete until the ``OK'' -or ``Cancel'' button is pushed. - -However, all testing functions wait at least a minimum interval -before returning to give the action a chance to finish. -This interval controls the speed at which the test suite runs, -and gives some slack time for events to complete. -The default interval is 100 milliseconds. The interval can be queried -or set with \iscmprocedure{test:run-interval}. - -A primitive action will not return until the run-interval has -expired and the action has finished, raised an error, or yielded. -The number of incomplete actions is given by -\iscmprocedure{test:number-pending-actions}. - -{\it Note:} -Once a primitive action is started, it is not possible to undo it -or kill its remaining effect. -Thus, it is not possible to write a utility that flushes the -incomplete actions and resets number-pending-actions to zero. - -However, actions which do not complete right away often provide a -way to cancel themselves. -For example, many dialog boxes have a ``Cancel'' button which will -terminate the action with no further effect. -But this is accomplished by sending an additional action -(the button push), not by undoing the original action. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Errors} -\label{fw:test:errors} - -Errors in the primitive actions (which necessarily run in the -handler thread) are caught and reraised in the calling thread. - -However, the primitive actions can only guarantee that the action -has started, and they may return before the action has completed. -As a consequence, an action may raise an error long after the -function that started it has returned. -In this case, the error is saved and reraised at the first opportunity -(the next primitive action). - -The test engine keeps a buffer for one error, saving only the -first error. Any subsequent errors are discarded. -Reraising an error empties the buffer, allowing the next error -to be saved. - -The function -\iscmprocedure{test:reraise-error} -reraises any pending errors. - -\subsection{Technical Issues} - -{\bf Active Frame} - -The Self Test primitive actions all implicitly apply to the -top-most (active) frame. - -{\bf Thread Issues} - -The code started by the primitive actions must run in the handler -thread of the eventspace where the event takes place. As a result, -the test suite that invokes the primitive actions must {\it not} run -in that handler thread (or else some actions will deadlock). See -\Mrhyperref{the eventspace section}{see section~}{}{eventspaceinfo} -for more info. - -{\bf Window Manager (Unix only)} - -In order for the Self Tester to work correctly, the window manager -must set the keyboard focus to follow the active frame. -This is the default behavior in Microsoft Windows and MacOS, -but not in X windows. - -In X windows, you must explicitly tell your window manager to set the -keyboard focus to the top-most frame, regardless of the position of the -actual mouse. Some window managers may not implement such functionality. -You can obtain such an effect in Fvwm and Fvwm95 by using the option: -\begin{verbatim} - Style "*" ClickToFocus -\end{verbatim} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @section{Thanks}