This material is expected to provide a good review of the overall framework design and implementation for the Rational Robot SAFS Engine--also known as the "RRAFS Engine", or "DDE". The material is designed to help automators understand the engine infrastructure, and provides instruction for enhancing the engine with new functionality, keywords, or customizations.
| ELEMENT | FILES | DESCRIPTION |
|---|---|---|
| Cycle Driver | CycleDriver.SBL | The domain of the Test Designer. Executes a test that is, generally, a collection of test suites. CycleDriver invokes SuiteDriver for each suite in the test. At the Cycle level, suites are specified via a Test "T" record type. |
| Suite Driver | SuiteDriver.SBL | The domain of the Test Designer. Executes a suite that is, generally, a collection of action commands ordered to accomplish larger tasks. SuiteDriver invokes StepDriver for each action, or step, in the test. At the Suite level, these actions or tests are specified via a Test "T" record type. |
| Step Driver | StepDriver.SBL | The domain of the Test Automator. Executes the steps necessary to complete the specified action. This is typically an assortment of Driver Commands "C" and Component Function Test "T" actions. |
| Driver Commands | DDDriverCommands.SBL | Commands provided by the DDE to accomplish general purpose tasks generally not considered "tests". Things like setting global variables or execution parameters, launching applications, or waiting for other things to happen. At all test levels, Driver Commands are specified via the Command "C" record type.
There are actually several Driver Command libraries chained together at runtime. Among these are:
There are also other engines like SAFS/DriverCommands that are independent of the RRAFS implementation. These can be launched and invoked by RRAFS to process additional Driver Commands that are not otherwise supported. |
| Component Functions | ??????????Functions.SBL | Libraries that implement the low-level actions for individual GUI components. Each library generally provides a set of actions for one specific type of component. CheckboxFunctions.SBL implements actions for Checkbox components. EditboxFunctions.SBL implements actions for Editbox components. And so on.
Low-level actions are things like "Click" the Button, "Set" the text value of an Editbox, or "Select" a RadioButton. Valid at the step level ONLY, component functions are called via the Test "T" record type. There are also other engines like SAFS/RobotJ that are independent of the RRAFS implementation. These can be launched and invoked by RRAFS to process additional Component Functions that are not otherwise supported. |
| Action Map | XSLComponentActions.MAP | This is, essentially, a shorthand dictionary of all the component function actions implemented by the core RRAFS engine. The component function libraries use this dictionary to determine if they support an action that has been fed to them, or if they need to forward this action down the execution chain.
The engine will not act on any core component action command that does not exist in this Action Map. |
| Framework API\Utilities | ??????????Utilities.SBL | Support libraries implementing general purpose routines for facilitating test automation with Rational Robot. Utilities for working with files, strings, menus, or any other utility function we might have need for. |
Robot's SQABasic does not support conditional compilation or inclusion based on something like #ifdef. Thus, it has an inherent problem with circular references or believing it has found duplicate definitions for the same item. The below 3-file library system minimizes the occurrence of duplicate definitions and\or circular references.
| SomeUtilities.SBL | library sourcecode\logic; private stuff, etc. |
| SomeUtilities_X.SBH | public Asset Header (constants, globals, types, etc.) |
| SomeUtilities.SBH | public API Header for API declarations |
Most support libraries and drivers use the 3-file system defined above. The named library will $Include its own Assets Header but will NOT include its API Header because that will cause duplicate definition compile errors. Other libraries wishing to take advantage of the named library will $Include both the Asset Header and the API Header--in that order.
Some libraries do not require the 3-file system. These libraries do not have public Assets or API to $Include or reference in other libraries. That may change over time. When a library exposes no public Assets, then it is completely self-contained in the SBL library file. The appropriate header file(s) get added later as needed to expose public assets or API declarations.
If you attempt to call a routine in a library that has not made that routine publicly available via an API Header, then you are asking for trouble. A routine that is not exposed in such a public header is allowed to change its signature. If it does, your private declarations for those routines will each have to be "fixed". So stick with the public API declarations provided by the public API headers. You have been warned.
Most all of the frameworks public Asset and API Headers can be included in your project with a single $Include statement:
The DDE_RUNTIME location is where the ".\Rational\Rational Test\sqabas32" subdirectory resides. This is a subdirectory of the folder where the Rational Software products have been installed. By default, this is in "c:\Program Files\". This is not a Rational project repository location. It is where Rational hides things like SQAUtils so they are available to ALL projects. This is where we must install our framework source, headers, and executables for production use across all projects.
Any enhancements to the core framework done in any other location must be pushed to this DDE_RUNTIME location for it to be available across all projects.
A true Rational project repository dedicated for ongoing development and testing of the core framework. This project allows for development of new features and support without impacting the production version of the framework deployed in the DDE_RUNTIME location. Libraries and headers in development reside in the project's TMS_Scripts\sqabas32 subdirectory.
This project should have all of the framework regression tests and scripts available to it for comprehensive testing during your development effort. We don't want to co-mingle our framework regression tests and libraries with tests and libraries for some other application. This project only needs the source and headers copied into it that you intend to modify. The rest can remain in the DDE_RUNTIME location.
Any enhancements to the core framework done in the project space must be pushed to the DDE_RUNTIME location to be made available across all projects. Any changes made to the core framework in the project space will override those in the DDE_RUNTIME location but only as long as you are running Robot from this project location. When you move to a different project, those changes not pushed to DDE_RUNTIME will NOT be available in the new project.
The Rational project for whatever application you are testing. In this project you generally won't have any of the framework source, headers, or executables. They are already available via the DDE_RUNTIME location. However, if you find you need to add a new feature or Component Function to support your application, you may determine it is easier to do it here. This is also a great location for project-specific Custom Extensions.
This project will NOT have all of the framework regression tests and scripts available to it. So, you will need to transfer any final changes to the FRAMEWORK DEVELOPMENT project so they can be fully tested with the framework regression tests. For example, you want to make sure your changes in support of a VB application did not break support for Web or Java applications.
The APP TEST project only needs to have the source and headers copied into it that you intend to modify. The rest can remain in the DDE_RUNTIME location.
Any enhancements to the core framework done in this location must be pushed to the DDE_RUNTIME location to be made available across all projects. Any changes made to the core framework here will override those in the DDE_RUNTIME location but only as long as you are running Robot from this project location. When you move to a different project, those changes not pushed to DDE_RUNTIME will NOT be available in the new project.
Review a Driver Command routed through CycleDriver into the DDDriverCommands library. This process is the same for CycleDriver, SuiteDriver, and StepDriver. They all call DDDriverCommands.
Example: CycleDriver executing a Driver Command ("C")
Review a test record routed through SuiteDriver to invoke StepDriver. The process is the same when CycleDriver is calling SuiteDriver. Effectively, the caller invokes the next lower driver passing in the test record--a filename--to be processed by that driver. Remember, CycleDriver calls SuiteDriver. SuiteDriver calls StepDriver.
(It should be noted that to promote maximum reuse, you cannot bypass test levels in your calls. CycleDriver cannot make direct calls to StepDriver or attempt to execute Component Functions. SuiteDriver cannot call Component Functions, either. However, a driver can call a higher driver or another test at the same level via the appropriate Driver Commands. As example, StepDriver can invoke SuiteDriver via the CallSuite Driver Command.)
Example: SuiteDriver executing a Test Record ("T")
Review a Component Function test record (Pushbutton Click) routed through StepDriver into the PushbuttonFunctions library.
Example: StepDriver executing a PushButton Click
Review a Component Function test record (Pushbutton VerifyProperty) routed through StepDriver into the PushButtonFunctions library. We will find that PushButtonFunctions does NOT implement this command because it is generic enough to be implemented for all components in the GenericMasterFunctions library.
There is a default chain of execution that applies for nearly all Component Functions which is outlined below for PushButtonFunctions:
Example: StepDriver executing a Generic Component Function
Truly custom driver commands or component functions are not intended to ever be part of the shared core framework because they have no global applicability. These should be pretty rare when dealing with near-standard components or common test activities. Some actions that appear custom may simply need to be broken down into more generic and globally useful core actions or engine enhancements.
Example: An app-specific customization that need not be custom
This CRM (app-specific) custom command can be broken down into smaller steps, at least one of which (shown below) will be globally useful:
Example: Make the customization globally useful
Core driver commands and component functions are generic and globally useful across many testable applications. Most actions with near-standard components or common test activities can be implemented as core driver commands or component functions. Some actions that appear custom because they are not implememted are really globally useful and should be implemented as core functionality.
Example: Commands or actions that might initially appear custom
As a reminder, and you will see this in the existing sourcecode, action commands and parameters are position dependent. The implementation expects to find parameters in specific fields. While many people may use DDVariable references to identify a parameter, the DDVariable itself is NOT used by the DDE in locating parameters. Parameters MUST reside in the specific field location identified for them.
Working example:
The two previous records are equivalent and will function. However, the following record is NOT equivalent and will probably generate an AppMap reference error:
Incorrect example:
The reason for the error is because SetContext is specifically looking for a Window reference in field #3, and a Child component reference in field #4. You cannot switch them around or assume they will be correctly interpreted by giving them some useful DDVariable reference.
It is very easy to add a new Driver Command. Especially since there are already at least 60 different Driver Commands to use as reference for implementing your own.
As of this writing, there are at least 6 libraries of Driver Commands:
You need to make sure you pick the library that is most appropriate for what you are trying to implement. DDDriverCommands.SBL is the one to use if your command doesn't seem to fit in any of the other categories.
In general, to add your own Driver Command you must:
You will notice that Driver Commands are NOT generally implemented in separate functions. Instead, they are usually just a few lines of code within the bounds of a large SELECT CASE statement--a CASE for each command. If your Driver Command contains a significant amount of code, you should consider implementing it in an appropriate core library as a separate function and calling the function from the CASE statement.
Adding a new action to a Driver Commands library generally does not change anything about the core engine other than the files actually touched. Thus, implementing a new action usually means that only the modified library must be recompiled. Of course, any modified header files must also be distributed.
It is very easy to add a new Action to an existing Component Functions library. Especially since there are already at least 25 different libraries implementing scores of action commands to use as reference for implementing your own.
As shown earlier, the framework routes a StepDriver Test (T) record through the SDProcessTestRecord function in StepDriver.SBL according to the component type encountered at runtime. If the component is determined to be a Pushbutton, then SDProcessTestRecord routes the record to be processed by PushbuttonFunctions.SBL. If the component is determined to be a Checkbox, then the record is forwarded to CheckboxFunctions.SBL.
So, it is of utmost importance that you implement your commands in the correct library for the component you wish to act upon. Unless, of course, the action is applicable to all components and the implementation works for all components. In that case, the action should be implemented in GenericMasterFunctions.SBL. (Note, GenericObjectFunctions.SBL is really reserved for components that are specifically recognized as component type "Generic".)
For the sake of clarity, I will list the details for adding a new action command to CheckboxFunctions.SBL. You would perform the same steps for adding a new action to any of the other Component Functions libraries.
Unlike Driver Commands, each Component Function is generally implemented in its own separate subroutine. This routine is called via the SELECT CASE routing mechanism found in Sub "main" in each Component Functions library as seen in item #4.
Adding a new action to an existing Component Functions library generally does not change anything about the core engine other than the files actually modified. Thus, implementing a new action usually means that only the modified library must be recompiled. Of course, any modified header files must also be distributed.
Adding support for an entirely new component type is a little more involved than simply adding an action to an existing library. This is primarily because we have to develop a new library and then tell StepDriver how to find it.
Below are the general steps necessary to implement support for an entirely new Component Functions library. The example is for a hypothetical component type "CoolWidget".
The record types like "C" and "T" not only differentiate record format, routing, and intent; they also separate different namespaces. Driver Commands have a separate namespace from Component Functions.
The above is implemented and executed within the Driver Command libraries which have their own namespace. The call below happens to use the same action keyword, "SomeAction", but it is implemented in the libraries and namespace for Component Functions. It is a different action.
There are some not-so-obvious implications to this as we consider adding new Driver Commands and Component Functions.
Command processing will very often go through a long chain of attempts to locate a command implementation. Processing will continue until the first implementation of the command is found in the namespace, or until the entire chain has been searched and no implementation found.
For example, there are at least six libraries implementing Driver Commands (review). At runtime command processing will go through each of these libraries to locate the first implementation of a driver command and it will then execute that command. This obviously doesn't seem too problematic since all core driver commands must be unique. However, this is not so obvious when we are dealing with component function libraries and component actions.
Component Function actions like "Click" often get duplicated. There is a "Click" for a Pushbutton and there is a "Click" for a Window. (But not all components explicitly implement a "Click" action.) Each supported component type has its own Component Functions library where those actions are implemented. However, there is also a chain of libraries searched in the event the requested action is not implemented in the called library. This was demonstrated with the VerifyProperty action when we reviewed Generic Test Records.
So, while each Component Functions library loosely has its own namespace, this namespace is shared with a select few libraries in the chain of Generic command handling. This Generic handling chain is shown below:
This has the consequences shown in the following 2 lists:
Seeking a command in the chain that may be unimplemented:
Implemented commands override implementations further down the chain:
More information on avoiding namespace conflicts with truly custom actions is provided in the following section on Adding Custom Extensions.
Custom extensions are those that are application or site-specific and cannot be distributed as part of the core framework. The DDE framework provides a mechanism by which you can implement custom features without modifying the core framework source. This enables you to continue to use and upgrade to future releases of the framework without losing your customizations and without going through a painful source merging process.
It is extremely critical to note that the namespace for each type of custom extension is shared with the core framework. Because we default to custom extensions only after we exhaust core framework implementations, custom extensions cannot override or replace core framework implementations. For example, you cannot attempt to implement a custom "T" record type. The DDE will always find the core implemention of the "T" record type first and process it there.
IMPORTANT: You must realize that future core implementations of items will automatically override any identical customizations you have previously implemented.
For example, you might decide you want to implement a new "D" record type for some truly novel processing that is very specific to data formats output from your application. You note the "D" record type is not currently implemented by the DDE, so it seems safe to use it for your custom implementation.
However, 6 months later a new release of the core DDE framework suddenly contains its own "D" record type! All of your tests using the custom "D" record type will now fail because the core framework will automatically try to process these records using the core implementation instead of your custom one. And, of course, your tests will fail miserably.
You must make every effort to ensure your keyword customizations are sufficiently unique to your site such that they don't conflict with current and future core framework implementations.
One of the best ways to ensure this is to use a site-specific, dept-specific, or app-specific prefix or suffix--ANYTHING--that is unlikely to ever be used in the core implementation.
| D, MyCustomRecordInfo, ... | EVIL Custom Record Type |
| APT_D, MyCustomRecordInfo, ... | GOOD Custom Record Type |
| C, NewDriverCommand, ... | EVIL Custom Driver Command |
| C, NewDriverCommand_SAS , ... | GOOD Custom Driver Command |
| T, Window, Component, NewTestAction, ... | EVIL Custom Test Command |
| T, Window, Component, SRM_NewTestAction, ... | GOOD Custom Test Command |
A custom record allows you to define a complete new structure for your record. Of course, it means you must also provide all the code for handling that new structure and functionality. You do this via the CustomRecordTypes library and any other supporting libraries you wish to implement.
CycleDriver, SuiteDriver, and StepDriver all parse the first field as the Record Type. If the Record Type is not recognized, they then attempt to see if you are trying to call a tool-specific script. This is called an Implied CallScript command. If no matching script is found, then we call the "CustomDDERecord" routine in the CustomRecordTypes library. By default, this routine is nothing but a hook that will return the equivalent of "unknown record type" to the DDE. It is here, in the CustomRecordTypes library that a user can insert their code for processing custom record types.
A custom driver command allows you to define a site, dept, or app-specific command. Of course, it means you must also provide all the code for handling that new command. You do this via the CustomDriverCommands library and any support libraries you wish to implement.
The DDDriverCommands parser attempts to locate the command within the libraries it already provides. If the command is not recognized, we call the "CustomDDEDriverCommand" routine in the CustomDriverCommands library. By default, this routine is nothing but a hook that will return the equivalent of "unknown driver command" to the DDE. It is here, in the CustomDriverCommands library that a user can insert their code for processing custom driver commands.
A custom test command allows you to define a site, dept, or app-specific Component Function action. Of course, it means you must also provide all the code for handling that new action. You do this via the CustomTestCommands library and any other supporting libraries you wish to implement.
After the action has passed through unimplemented in the appropriate Component Function library, GenericObjectFunctions, and GenericMasterFunctions--GenericMasterFunctions will call the "CustomDDETestCommand" routine in the CustomTestCommands library. By default, this routine is nothing but a hook that will return the equivalent of "unimplemented action command" to the DDE. It is here, in the CustomTestCommands library, that a user can insert their code for processing custom component actions.
There are two types of documentation that must be considered when reviewing or enhancing the framework libraries.
Originally, there was no separate XML Documentation Format. All API and Keyword documentation was embedded in the library API format. That did not allow for the automated publication of comprehensive reference material like the SAFS Reference. We have since separated these two different types of documentation content. Some older API-format documentation still contains what is now migrating into the XML format. Over time, this duplication should be eliminated.
(TO BE DEVELOPED)
Use API documentation in existing libraries as reference. API documentation is that documentation provided in ALL libraries to document the actual library API--which is different than documenting Keywords and their syntax.
Some older libraries contain API-style documentation that includes information on how to use the Keywords they support. This style of documentation was in place before the introduction of XML. We do not intend to carry that tradition forward as it represents a duplicate documentation workload.
(TO BE DEVELOPED)
Use XML documentation existing Driver Command and Component Function XML files as reference. The XML is generally stored in the .\SAFS\keywords directory where the SAFS Framework was installed. A purely RRAFS-only installation may not have the XML installed.
The framework primarily relies on structured documentation embedded within the sourcecode and in separate XML files. Tools exist to extract that documentation and publish it in HTML format for the web. Other tools process the separate XML keywords documentation to merge content into single-point references or data for use by other tools.
The framework developer needs to use these tools to (re)publish changes that occur to existing libraries or new documentation from new libraries.
XML processing uses the MSXML Parser and MSXSL.EXE from Microsoft. As of Oct, 2002 these have been bundled with the install of the RRAFS\DDE framework. They can also be downloaded and installed from Microsoft's XML Support Pages.
These tools are currently coded in SQABasic libraries. They extract the API format documentation from the RRAFS library source and create HTML doc like CheckBoxFunctions.
(These tool docs may need some updating...)
We have created an XSL Stylesheet and a Windows Batch program that enable us to automatically publish the XSLComponentActions.MAP required by the engine. This uses the same XML files used to generate the SAFS Reference.
The XML and XSL stylesheets should be installed in the .\SAFS\keywords directory for the SAFS Framework. If you have a RRAFS-only install, these files may not be available.
The Batch programs are installed in the .\SAFS\bin directory. Again, a RRAFS-only install may not have these files available.
| XSLComponentFunctions.XML | List of XML files containing Component Functions |
| XSLComponentActionsMap.XSL | Templates for building the XSLComponentActions.MAP needed by the engine. |
| XSLComponentActionsMap.BAT | Batch program to automatically build the Actions Map. |
With a successfull SAFS Framework install, the Batch file XSLComponentActionsMap.BAT will not need any modification. If all expected XML files exist and contain properly formatted XML then all you have to do is run the XSLComponentActionsMap.BAT batch program.
The resulting XSLComponentActions.MAP file should appear in the .\SAFS\data directory. The file must then be placed in either your project's Datapool\Runtime directory for project-specific enhancements, or in your DDE_RUNTIME directory for enhancements that are ultimately intended to be global in nature. The engine will not recognize any new action command if it does not find it in the available XSLComponentActions.MAP file.
We have created some supporting XML files, XSL Stylesheets, and Windows Batch programs that enable us to automatically generate the SAFS Reference. The SAFS Reference is the single-point HTML reference for all the Driver Commands and Component Functions made available via the framework.
The XML and XSL stylesheets should be installed in the .\SAFS\keywords directory for the SAFS Framework. If you have a RRAFS-only install, these files may not be available.
The Batch programs are installed in the .\SAFS\bin directory. Again, a RRAFS-only install may not have these files available.
| XSLDriverCommands.XML | List of XML files containing Driver Commands |
| XSLComponentFunctions.XML | List of XML files containing Component Functions |
| XSLCommonDDE.XSL | Some common and reusable XSL templates for our reference material |
| XSLDDEngineReference.XSL | HTML and templates for building the SAFS Reference and DDEngine Reference |
| XSLBuildDDEngineReference.BAT | Batch program to automatically build the reference |
After a successful SAFS Framework install, the Batch file XSLBuildDDEngineReference.BAT will not need any modification to execute properly. If all expected XML files exist and contain properly formatted XML, then all you have to do is run the XSLBuildDDEngineReference.BAT batch program. The results will appear in the .\SAFS\doc directory.
Right now this is really just a sample\experiment. In essence, all we do here is combine all the XML information extracted from all the Driver Command libraries and form a single TAB delimited flat file datatable.
The table contains records providing the following fields (among others):
| XSLDriverCommands.XML | List of extracted XML files containing Driver Commands |
| XSLCombineDriverCommands.XSL | Templates for building the TAB delimited table |
| XSLCombineDriverCommands.BAT | Batch program to automatically build the datatable |
With a successful SAFS Framework install, the Batch file XSLCombineDriverCommands.BAT will not need any modification. If all expected XML files exist and contain properly formatted XML then all you have to do is run the XSLCombineDriverCommands.BAT batch program. The results should appear in the .\SAFS\data directory.
A common failure results when one or more XML files are improperly formatted, contain missing XML tags, XML tags out of sequence, or mixed-case start and end tags that don't match up. XML is case-sensitive.
The developer may have to perform an iterative process until all XML failures are resolved:
(Oh yeah, it pays to get your XML right early on :)