Extending product function
1. Extending RPT
1.1. Extending test execution with custom code
1.1.1. Create custom Java code
1.1.2. Test execution services interfaces and classes
1.1.3. Reducing the performance impact of custom code
1.1.4. Custom code examples
1.1.4.1. Controlling loops
1.1.4.2. Retrieve the IP address of a virtual user
1.1.4.3. Printing input arguments to a file
1.1.4.4. Counting the number of times that code is executed
1.1.4.5. Set and clearing cookies for a virtual user
1.1.4.6. Determine where a test is running
1.1.4.7. Storing and retrieving a string
1.1.4.8. Extracting a string or token from its input argument
1.1.4.9. Retrieve the maximum JVM heap size
1.1.4.10. Running an external program from a test
1.1.4.11. Add custom counters to reports
1.1.4.12. Use transactions and statistics
1.1.4.13. Reporting custom verification point failures
1.1.4.14. Debugging custom code
1.1.5. Migrate custom code from previous versions
1.2. Extending RPT to support other protocols
1.2.1. Protocol extension structure
1.2.2. Extending the test recorder
1.2.2.1. Defining a new type of captured data
1.2.2.2. Defining a new recorder
1.2.2.3. Defining a new client
1.2.2.4. Defining how a recorder can record a client
1.2.2.5. Running recorders and clients without a UI
1.2.2.6. Defining wizards for recorders and clients
1.2.2.7. Migrate recorder implementations from previous versions
1.2.3. Extending the test generation framework
1.2.3.1. Defining a new test generator
1.2.3.2. Defining a new packet converter
1.2.3.3. Generating tests without a UI
1.2.3.4. Defining a test-generation wizard
1.2.3.5. Migrate test generator implementations from previous versions
1.2.4. Contributing annotations
1.2.4.1. Defining a new annotation type
1.2.4.2. Contributing new actions to the annotation toolbar
1.2.4.3. Generating a test-model element from a new annotation type
1.2.4.4. Advanced annotation concepts
1.2.5. Extending the load test behavior model
1.2.5.1. Updates to the load test behavior model
1.2.5.2. Extension points for LTBM
1.2.5.2.1. Registering a model element
1.2.5.2.2. Required attributes in a model class
1.2.5.2.3. Registration examples
1.2.5.3. Create protocol constructs
1.2.5.3.1. Modeling the behavior of a protocol extension
1.2.5.4. Extending the classes in LTBM
1.2.5.5. Public APIs for LTBM
1.2.5.5.1. com.ibm.rational.test.lt.models.behavior.lttest package
1.2.5.5.2. com.ibm.rational.test.lt.models.behavior.common package
1.2.5.5.3. com.ibm.rational.test.lt.models.behavior.data package
1.2.5.5.4. com.ibm.rational.test.lt.models.behavior.vps package
1.2.6. Extending data correlation
1.2.6.1. Implementing data correlation for test generation
1.2.6.2. Implementing data correlation for execution
1.2.7. Extending the test editor
1.2.7.1. Migrate test editor extensibility
1.2.7.2. Test editor structure
1.2.7.3. Common editor framework
1.2.7.3.1. The modelObjectDescriptor extension point
1.2.7.3.2. The testOptions extension point
1.2.7.4. Contributing actions to the menu
1.2.7.4.1. Test editor menus
1.2.7.4.2. Create actions
1.2.7.5. Editor layer extension points
1.2.7.6. API classes
1.2.8. Contributing error handlers
1.2.8.1. Defining the user interface for an error handler
1.2.8.2. Controlling how available error handlers are displayed
1.2.8.3. Controlling how error handlers for specific elements are displayed
1.2.8.4. Create an error type
1.2.8.5. Implementing code generation for error handling
1.2.8.6. Implementing execution for error handling
1.2.9. Extending the schedule component
1.2.9.1. Extension points for extending schedules
1.2.9.2. Public APIs for extending schedules
1.2.10. Extending code generation
1.2.10.1. Code generation
1.2.10.2. Extension points for code generation
1.2.10.3. Generating test code
1.2.10.4. Create the script class
1.2.10.5. Code generation templates
1.2.10.6. New protocol extensions
1.2.10.7. Public APIs of codegen.core
1.2.11. Extending the run-time environment
1.2.11.1. Blocked Action detection
1.2.11.2. Extending subsystem management during a test run
1.2.11.2.1. Informing the test engine that the subsystem exists
1.2.11.2.2. Create a simple test and schedule
1.2.11.2.3. Add SampleAction and SampleSubsystem to the project
1.2.11.2.4. Running the schedule with SampleAction using SampleSubsystem
1.2.11.2.5. SampleAction.java code sample
1.2.11.2.6. SampleSubsystem.java code sample
1.2.11.3. Extending initialization and finalization during a test run
1.2.11.4. Public APIs for run time
1.2.12. Extending the test log viewer
1.2.13. Extending evaluation results
1.2.13.1. Aggregation of statistical data
1.2.13.2. Extending report counters
1.2.13.2.1. The ReportAction extension point
1.2.13.3. Extending default reports
1.2.13.3.1. The RPTReport extension point
1.2.13.4. Public APIs for evaluate results
1.1. Extending test execution with custom code
You can extend how you run your tests by writing custom Java. code and calling the code from the test. You can also specify that results from the tests that are affected by your custom code be included in reports.
1.1.1. Creating custom Java code
Custom code uses references in the test as input and returns modified values to the test. Use the ICustomCode2 interface to create custom code and the ITestExecutionServices interface to extend test execution. These interfaces are contained in the com.ibm.rational.test.lt.kernel.services package.
Note: When you use the ITestExecutionServices interface in your custom code to report test results, the results for the custom code are displayed in the test log. If you log custom verification point verdicts, these are reflected in the overall schedule verdict.
Custom code input values can be located in references or field references. You can also pass a text string as an argument to custom code. References that are used as input to custom code must be included in the same test as the custom code. In the test, the reference must precede the code that it affects. Verify that the test contains the references required for customized inputs to your code.
If your custom code uses external JAR files, you might need to change the Java. build path. In some cases, you can avoid changing the build path manually by running the test before adding your custom code to it. The first time a test runs, classes and libraries required for compilation are added to the build path. For example, you can import Test and Performance Tools Platform (TPTP) classes required to create custom events in the test log if the test, to which you have added your custom code, has run previously. However, if the test has never been run, import errors occur because the classes are not named in the build path for the project until the test has run.
If your code uses external resources, for example, an SQL database or a product that manages customer relationships, you must configure the custom code to work on every computer on which your test runs.
Custom code is saved in the src folder of the project containing the test that calls the code. By default, custom code is located in a package named test in the src folder.
You can reuse a custom code package for tests that are located in multiple projects. The projects must be in one workspace. To reuse custom code across projects, use the project name before the custom code package. For example,
.
The following example shows the standard Navigator view of two custom code classes. (The Test Navigator does not display Java source files.)
![]()
When you add the ReplaceCC.java and VerifyYUserID.java custom code classes to the test and return a value to the test, Substitute lists these two classes.
The test package also contains the generated Java code for tests in the project.
You can put custom code in a different package (for example, custom). Separate custom code from generated code, especially if you use a source-control system.
To add custom code:
- Open the test, and select a test element.
- Click Add or Insert, and select Custom Code. Add appends the custom code to the bottom of the selected test element. Insert adds the custom code above the selected test element.
Note: After you add or insert custom code, the Problems view displays an error stating that the new custom code element has no Java file. This error message remains until you click View Code or Generate Code, to remind you that the custom code test element is not yet associated with any Java code.
- Inspect the Class name field, and complete one of these steps:
- If the code to call already exists, change the class name to match its name. Click View Code to open the code in the Java editor.
- If the code does not exist, change the class name to describe the purpose of the code. Click Generate Code to generate a template class for logging results and to open it in the Java editor. If a class with this name exists, you are warned that it will be overwritten.
- In the Arguments field, click Add.
- In the Custom Code window, select all inputs that your code requires. The Custom Code window lists all values in the test that can be used as inputs to your code (references or field references in the test that precede the code).
- Click OK. The window closes, the selected references are added to the Arguments field.
- Optional: To add text strings as inputs to your custom code, click Text, and then type the text string to use.
- In the test, after your custom code, locate a value that your code returns to the test.
- Highlight the value.
- Right-click the highlighted value, click Substitute, and select the class name of your custom code. The custom code classes that you have added are listed. After you have made your selection, the value to be returned to the test is highlighted in orange, and the Used by table is updated with this information.
What to do next
Custom code is not displayed in the Test Navigator view. To view custom code, open the Package Explorer view and use the Java tools to identify the custom code that you added.
1.1.2. Test execution services interfaces and classes
Use the test execution services interfaces and classes to customize how you run tests. These interfaces and classes are located in the com.ibm.rational.test.lt.kernel package. Each interface and class is described briefly in this topic and in detail in the Javadoc information.
The custom code does not run on the mobile device, but from the generated Java code that is available in the test workbench. So, if you initiate the test run from the mobile device and the test script includes custom code, the custom code is not executed. To execute the custom code that is available in a mobile test scrip, you must initiate the run from test workbench. To integrate custom code between two mobile instructions, you must split the test script. See Splitting a test .
The Javadoc for the test execution services interfaces and classes are in this reference topic .
Test execution services interfaces
Interface Description ICustomCode2 Defines customized Java. code for test execution services. Use this interface to create all custom code. ITestExecutionServices Provides information for adding custom test execution features to tests. Replaces the IKLog interface. All the methods that were available in IKLog are contained in ITestExecutionServices, along with several newly exposed objects and interfaces. This interface is the primary interface for execution services. ITestExecutionServices contains the following interfaces: IDataArea, IARM, ILoopControl, IPDLogManager, IStatisticsManager, ITestLogManager, ITime, and ITransaction. IDataArea Defines methods for storing and accessing objects in data areas. A data area is a container that holds objects. The elements of a data area are similar to program variables and are scoped to the owning container. To use objects specific to a protocol, you should use objects provided by that protocol that are stored in the protocol-specific data area. IARM Provides information about defining ARM (Application Response Measurement) specifications. Use this interface if your virtual users are being sampled for ARM processing. ILoopControl Provides control over loops in a test or schedule. For example, you can use this interface to break loops at specific points in a test. The loop that is affected is the nearest containing loop found in either the test or the schedule. IPDLogManager Provides logging information such as problem severity, location levels, and error messages. IStatisticsManager Provides access to performance counters in the ICustomCode2 interface (used for defining custom code). Performance counters are stored in a hierarchy of counters. Periodically, all the counter values in the hierarchy are reported to the testing workbench and collected into test run results, where they are available for use in reports and graphs. Each counter in the hierarchy has a type (defined in class StatType). The operations that are available on a counter depend on the counter's type. ITestLogManager Logs messages and verification points to the test log. Use this interface for handling error conditions, anomalies in expected data or other abstract conditions that need to be reported to users, or for comparisons or verifications whose outcome is reported to the test log. ITestLogManager can also convey informational or status messages after the completion of a test. ITime Defines basic time services, such as the current system time in milliseconds (adjusted so that all systems are synchronized with the schedule controller), the time the test begins, and the elapsed time from the beginning of the test. ITransaction Provides support for transactions. A collection of named transactions is maintained for each virtual user. Transactions created in custom code can be started and stopped wherever custom code can be used. These transactions can span several tests. Performance counters are kept for custom code transactions and appear in reports. An example of how you could use ITransaction is to create transactions for one virtual user but not another, to help verify responses from tests. IEngineInfo Provides information about the testing execution engine; for example, the number of virtual users running in this engine, the number of virtual users that have completed, the local directory in which test assets are deployed, and the host name of the computer on which the engine runs. ITestInfo Provides information about the test that is running; for example, the test name and information about the current problem determination log level for this test. IVirtualUserInfo Provides information about virtual users; for example, the virtual user's name, problem determination log level, TestLog level, globally unique ID, and user group name. IScalar Provides methods for simple integer performance counters. It is used for counters of SCALAR and STATIC types. Use this interface to decrement and increment counters. IStat Defines observational performance counters. It defines the method for submitting a data point to performance counters of type RATE, AVERAGE, and RANGE. IStatistics Retrieves the performance counter tree associated with the current statistics processor. Stops the delivery of performance counters. Changes the priority of the statistics delivery thread. IStatTree Provides methods that can retrieve child counters, create the XML fragments that define counters, and set the description field of counters. IText Contains text-based performance counters. Performance counters that do not fit any of the other counter types can be created as type TEXT. TEXT counters are not assigned definitions, but they are collected in the test results.
Test execution services classes
Class Description DataAreaLockException Throws an exception whenever an attempt is made to modify a locked DataArea key. OutOfScopeException Indicates that an object created by ITestExecutionServices has been referenced outside of its intended scope. TransactionException Throws an exception when a transaction is misused. The following conditions lead to a TransactionException exception: attempting to start a transaction that has already been started, attempting to stop a transaction that has not been started, and getting the start time or the elapsed time of a transaction that has not been started. Any operation (except abort()) on a transaction that has been aborted will throw a TransactionException exception. StatType Provides a list of valid performance counter types. The performance counter types are: AVERAGE, iAVERAGE, iRANGE, iRATE, iSCALAR, iSTATIC, iSTRUCTURE, iTEXT, RANGE, RATE, SCALAR, STATIC, STRUCTURE, and TEXT.
1.1.3. Reducing the performance impact of custom code
If custom code runs inside a page, it can affect that page's response time.
HTTP pages are containers of HTTP requests. On a given HTTP page, requests run in parallel across all of the connections between the agent computer and the system under test.
Page response time is the interval between page start and page end, which are defined as follows: Page start is the first timestamp associated with the client-server interaction. This interaction is either the first byte sent or the first connect of the first HTTP request. Page end is the last timestamp associated with the client-server interaction. This interaction is the last byte received of the last HTTP request to complete. Because of parallelism, the last HTTP request to complete might not be the last one listed for the page.
Typically, you should not insert custom code inside a page. While custom code that runs for only a few milliseconds should have little effect on page response time, the best practice is to place custom code outside a page. Custom code placed outside a page has no effect on page response time, and its execution time can overlap with think time delays.
Do not use custom code for data correlation if you can instead use the data correlation features built into the product. The built-in data correlation code takes advantage of requests running in parallel, whereas custom code actions do not begin until all earlier actions are completed.
You might need to place custom code inside a page to correlate a string from the response of a request inside that page to another request inside the same page. Even in this case, if you split the page into two pages, you can use the built-in data correlation features instead of custom code.
If you still want to run tests with custom code inside HTTP pages, use the Page Element report to evaluate performance. The Page Element report shows the response time and throughput for individual HTTP requests. Custom code does not affect the response time measurement of individual HTTP requests.
1.1.4. Custom code examples
Custom code enables you to perform such tasks as managing loops, retrieving virtual user information, running external programs from tests, and customizing data correlation.
1.1.4.1. Controlling loops
This example demonstrates extending test execution by using custom code to control loops. It provides sample code that shows how you can manipulate the behavior of loops within a test to better analyze and verify test results.
This example uses a recording of a stock purchase transaction using IBM's Trade application. The concepts shown here can be used in tests of other applications.
The test begins with a recording of a stock purchase transaction, using datapool substitution for the login IDs.
The pages are wrapped in a five-iteration loop, as shown in the following figure:
![]()
Notice that among the various pages of the test, three items of custom code exist (indicated by the green circles with "C"s in them). This example explores these items of custom code.
The first piece of custom code, InitializeBuyTest, is mentioned here:
package customcode; import java.util.Random; import com.ibm.rational.test.lt.kernel.IDataArea; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.kernel.services.IVirtualUserInfo; /** * @author unknown */ public class InitializeBuyTest implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public InitializeBuyTest() { } /** * For description of ICustomCode2 and ITestExecutionServices interfaces, * see the Javadoc information . */ public String exec(ITestExecutionServices tes, String[] args) { // Get the test's data area and set a flag indicating that nothing // has failed yet. This flag will be used later to break out // of the schedule loop as soon as a failure is encountered. IDataArea dataArea = tes.findDataArea(IDataArea.TEST); dataArea.put("failedYet", "false"); // Get the virtual users's data area IDataArea vda = tes.findDataArea(IDataArea.VIRTUALUSER); // Randomly select a stock to purchase from the set of s:0 to s:499. IVirtualUserInfo vuInfo = (IVirtualUserInfo) vda.get(IVirtualUserInfo.KEY); Random rand = vuInfo.getRandom(); String stock = "s:" + Integer.toString(rand.nextInt(499)); // Persist the name of the stock in the virtual user's data area. vda.put("myStock", stock); return stock; }This custom code is located in the method exec().
First, the data area for the test is acquired to store a flag value, in this case a string of text, to be used later to stop the test loop when an error is discovered. Data stored in this way can be persisted across tests.
Then a randomly generated stock string is created. The value is stored as the variable stock, and is passed back as the return value for the method. This return value is used as a substitute in a request later, as shown in the following figure:
![]()
The highlighted item uses a substitution (s%3A716), which is the value returned by the InitializeBuyTest custom code item. We are using custom code to drive the direction of our test.
The next lines of code in InitializeBuyTest use the Virtual User data area to store the name of the stock for later reference. Again, data stored in this way can persist across tests.
The second piece of custom code is called CheckStock. Its contents are as follows (listing only the exec() method this time):
public String exec(ITestExecutionServices tes, String[] args) { // Get the actual and requested stock purchased. String actualStock = args[0].replaceAll("<B>", ""); actualStock = actualStock.substring(0, actualStock.indexOf("<")); String requestedStock = args[1]; // Set the log level to ALL. IDataArea dataArea = tes.findDataArea(IDataArea.TEST); ITestInfo testInfo = (ITestInfo)dataArea.get(ITestInfo.KEY); testInfo.setTestLogLevel(ITestLogManager.ALL); // If the log level is set to ALL, report the actual and requested stock // purchased. ITestLogManager testLogManager = tes.getTestLogManager(); if (testLogManager.wouldReport(ITestLogManager.ALL)) { testLogManager.reportMessage("Actual stock purchased: " + actualStock + ". Requested stock: " + requestedStock + "."); } // If the actual and requested stock don't match, submit a FAIL verdict. if (testLogManager.wouldReport(ITestLogManager.ALL)) { if (!actualStock.equalsIgnoreCase(requestedStock)) { testLogManager.reportVerdict( "Actual and requested purchase stock do not match.", VerdictEvent.VERDICT_FAIL); // Use the test's data area to record the fact that an error has // occurred. dataArea.put("failedYet", "true"); } } return null; }This code begins by extracting two arguments that have been passed to the code. A part of the response in the original recording is highlighted and used as a reference, as shown in the following figure.
![]()
Some string manipulation is needed to acquire the text of interest; in this case, the name of the stock that was actually purchased. This newly created reference is then passed into CheckStock as an argument, as shown in the following figure:
![]()
Note that the return value of InitializeBuyTest is passed in as an argument as well.
The CheckStock custom code item uses these values to verify that the randomly chosen stock generated by InitializeBuyTest is actually purchased during the execution of the test.
CheckStock then sets the test log level, reports the actual and requested stock purchase, and raises a FAIL verdict if they do not match. CheckStock also stores a true value associated with the tag failedYet in the test's data area.
The third piece of custom code (exec() method only) is mentioned here:
public String exec(ITestExecutionServices tes, String[] args) { // Get the test log manager. ITestLogManager testLogManager = tes.getTestLogManager(); // Get the test's data area and get a flag indicating to // see if anything has failed yet. If so, stop the loop. IDataArea dataArea = tes.findDataArea(IDataArea.TEST); String failedYet = (String) dataArea.get("failedYet"); // Break out of the loop if an error has been encountered. if (failedYet.equalsIgnoreCase("true")) { tes.getLoopControl().breakLoop(); if (testLogManager.wouldReport(ITestLogManager.ALL)) { testLogManager.reportMessage("Loop stopped."); } } return null; }This code uses the test's data area to determine the user-defined value associated with the tag failedYet. If failedYet is true, StopLoopCheck breaks out of the test loop.
1.1.4.2. Retrieve the IP address of a virtual user
This example shows how to retrieve the local IP address of a virtual user. Retrieving IP addresses is particularly useful when virtual users are using IP aliases.
The following custom code retrieves the IP address that was assigned to a virtual user:
import java.net.InetAddress; import com.ibm.rational.test.lt.kernel.IDataArea; import com.ibm.rational.test.lt.kernel.services.ITestLogManager; import com.ibm.rational.test.lt.kernel.services.IVirtualUserInfo; public String exec(ITestExecutionServices tes, String[] args) { IVirtualUserInfo vui = (IVirtualUserInfo) tes.findDataArea(IDataArea.VIRTUALUSER).get(IVirtualUserInfo.KEY); ITestLogManager tlm = tes.getTestLogManager(); if (vui != null) { String localAddr = null; InetAddress ipAddr = vui.getIPAddress(); if (ipAddr != null) localAddr = ipAddr.toString(); tlm.reportMessage("IPAlias address is " + (localAddr != null ? localAddr : "not set")); return localAddr; } else return ("Virtual User Info not found"); }
1.1.4.3. Printing input arguments to a file
The PrintArgs class prints its input arguments to the file C:\arguments.out. This class could be used, for example, to print a response returned by the server.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import java.io.*; /** * The PrintArgs class prints its input arguments to the file * C:\arguments.out. This example could be used to print a response * returned by the server. */ /** * @author IBM Custom Code Samples */ public class PrintArgs implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public PrintArgs() { } public String exec(ITestExecutionServices tes, String[] args) { try { FileWriter outFile = new FileWriter("C:\\arguments.out"); for (int i = 0; i < args.length; i++) outFile.write("Argument " + i + " is: " + args[i] + "\n"); outFile.close(); } catch (IOException e) { tes.getTestLogManager().reportMessage( "Unable to write to C:\\arguments.out"); } return null; } }
1.1.4.4. Counting the number of times that code is executed
The CountAllIterations class counts the number of times code is executed by all virtual users. The CountUserIterations class counts the number of times code is executed by an individual virtual user.
The CountAllIterations class counts the number of times it is executed by all virtual users running in a particular JVM and returns this count as a string.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; /** * The CountAllIterations class counts the number of times it is executed * by all virtual users running in a particular JVM and returns this count * as a string. If all virtual users on an agent are running in the same * JVM (as would typically be the case), this class will count the number of * times it is run on the agent. */ /** * @author IBM Custom Code Samples */ public class CountAllIterations implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { private static int numJVMLoops = 0; /** * Instances of this will be created using the no-arg constructor. */ public CountAllIterations() { } public String exec(ITestExecutionServices tes, String[] args) { return Integer.toString(++numJVMLoops); } }
The CountUserIterations class counts the number of times code is executed by an individual virtual user.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.kernel.IDataArea; /** * The CountUserIterations class counts the number of times it is executed * by an individual virtual user and returns this count as a string. */ /** * @author IBM Custom Code Samples */ public class CountUserIterations implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public CountUserIterations() { } public String exec(ITestExecutionServices tes, String[] args) { IDataArea userDataArea = tes.findDataArea(IDataArea.VIRTUALUSER); final String KEY = "NumberIterationsPerUser"; Number numPerUser = (Number)userDataArea.get(KEY); if (numPerUser == null) { numPerUser = new Number(); userDataArea.put(KEY, numPerUser); } numPerUser.value++; return Integer.toString(numPerUser.value); } private class Number { public int value = 0; } }
1.1.4.5. Set and clearing cookies for a virtual user
The SetCookieFixedValue class sets a Cookie for a virtual user, and the ClearCookies class clears all cookies for a virtual user.
The SetCookieFixedValue class sets a Cookie, defined in the newCookie variable, for a virtual user just as if the server had returned a Set-Cookie.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.execution.http.cookie.IHTTPVirtualUserInfo; import com.ibm.rational.test.lt.kernel.IDataArea; import java.text.ParseException; /** * The SetCookieFixedValue class sets a Cookie, defined in the newCookie * variable, for a virtual user just as if the server had returned a Set-Cookie. */ /** * @author IBM Custom Code Samples */ public class SetCookieFixedValue implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public SetCookieFixedValue() { } public String exec(ITestExecutionServices tes, String[] args) { String newCookie = "MyCookie=CookieValue;path=/;domain=.ibm.com"; IDataArea dataArea = tes.findDataArea(IDataArea.VIRTUALUSER); IHTTPVirtualUserInfo httpInfo = (IHTTPVirtualUserInfo)dataArea.get(IHTTPVirtualUserInfo.KEY); try { httpInfo.getCookieCache().setCookie(newCookie); } catch (ParseException e) { tes.getTestLogManager().reportMessage("Unable to parse Cookie " + newCookie); } return null; } }The ClearCookies class clears all Cookies for a virtual user. For information on how cookies are treated in tests and schedules,package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.execution.http.util.CookieCacheUtil; /** * The ClearCookies class clears all Cookies for a virtual user. */ /** * @author IBM Custom Code Samples */ public class ClearCookies implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public ClearCookies() { } public String exec(ITestExecutionServices tes, String[] args) { CookieCacheUtil.clearCookieCache(tes); return null; } }
1.1.4.6. Determine where a test is running
The ComputerSpecific class determines where a test is running
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import java.net.InetAddress; import java.net.UnknownHostException; /** * The ComputerSpecific class determined the hostname on which the test is * running, prints the hostname and IP address as a message in the test log, * and returns different strings based on the hostname. */ /** * @author IBM Custom Code Samples */ public class ComputerSpecific implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public ComputerSpecific() { } public String exec(ITestExecutionServices tes, String[] args) { String hostName = "Unknown"; String hostAddress = "Unknown"; try { hostName = InetAddress.getLocalHost().getHostName(); hostAddress = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { tes.getTestLogManager().reportMessage( "Not able to obtain host information"); return null; } tes.getTestLogManager().reportMessage("The hostname is " + hostName + "; IP address is " + hostAddress); if (hostName.equals("host-1234")) return "Special"; else return "Normal"; } }
1.1.4.7. Storing and retrieving variable values
You can use the getValue() and setValue() methods to store and retrieve values in variables. Depending on the storage location that you specify, variables can be shared among tests, or stored locally in the current test.
You can use the getValue() and setValue() methods to store multiple values in variables in one custom code call. You can then create substitutions from variables instead of from multiple custom code elements.
For example, assume that a response contains three values: id, book title, and price. You can read all three values from the response, and then use custom code to set the variables id, title, and price. You can then substitute the values from the three variables as needed in the test, instead of having to write custom code for each variable.
Note: The storage location passed to the method must match the storage location used when declaring the variable.
package customcode; import com.ibm.rational.test.lt.kernel.IDataArea; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; /** * For Javadoc information on the ICustomCode2 and ITestExecutionServices interfaces, * see the 'Extending test execution with custom code' help topic. */ /** * @author IBM Custom Code Samples */ public String exec(ITestExecutionServices tes, String[] args) { tes.getValue("myVar", tes.STORAGE_USER); // This retrieves a value from a test for the variable called myVar. The storage area is shared between tests. tes.getValue("myLocalVar", tes.STORAGE_LOCAL); // This variable is stored locally, per test. tes.setValue("myVar", tes.STORAGE_USER, "myNewValue"); // Change the value of the variable myVar, which is shared between tests, to myNewValue. tes.setValue("myLocalVar", tes.STORAGE_LOCAL, "myLocalNewVar"); // Change the value of the local variable to myLocalNewVar. return null; }
1.1.4.8. Extracting a string or token from its input argument
The ParseResponse class extracts a string from its input argument. The ExtractToken class extracts a particular token (string) from its input argument. Both classes can be useful for handling certain types of dynamic data correlation.
The ParseResponse class extracts a string from its input argument, using a regular expression for pattern matching.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import java.util.regex.*; /** * The ParseResponse class demonstrates using Custom Code to extract a * string from its input argument using a regular expression for pattern * matching. * * In this sample, the args[0] input string is assumed to be the full response from a previous request. This response contains the day's headlines in a format such as: * * <a class=f href=r/d2>In the News</a><small class=m> <span id=nw> * </span></small></h2> * <div class=ct> * • <a href=s/213231>Cooler weather moving into eastern U.S.</a> * <br>• <a href=s/262502>Digital camera shipments up</a><br> * * Given the above response, the extracted string would be: * Cooler weather moving into eastern U.S. */ /** * @author IBM Custom Code Samples */ public class ParseResponse implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public ParseResponse() {} public String exec(ITestExecutionServices tes, String[] args) { String HeadlineStr = "No Headline Available"; String RegExpStr = ".*In the News[^;]*;[^;]*;[^;]*;<a href=([^>]*)>([^<]*)<"; Pattern pattern = Pattern.compile(RegExpStr, Pattern.DOTALL); Matcher matcher = pattern.matcher(args[0]); if (matcher.lookingAt()) HeadlineStr = matcher.group(2); else tes.getTestLogManager().reportMessage("Input does not match pattern."); return HeadlineStr; }The ExtractToken class extracts a particular string from its input argument.package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; /** * The ExtractToken class demonstrates using Custom Code to extract a particular * token (string) from its input argument. This can be useful for handling * certain types of dynamic data correlation. * * In this sample, the args[0] input string is assumed to be comma-delimited * and the token of interest is the next-to-last token. For example, if * args[0] is: * javascript:parent.selectItem('1010','[Negative]1010','1010','','IBM', * '30181','Rational','1','null','1','1','6fd8e261','RPT') * the class will return the string 6fd8e261. */ /** * @author IBM Custom Code Samples */ public class ExtractToken implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { public ExtractToken() { } public String exec(ITestExecutionServices tes, String[] args) { String ArgStr; String NextToLastStr; String[] Tokens = args[0].split(","); if (Tokens.length > 2) { ArgStr = Tokens[Tokens.length - 2]; // Extract next-to-last token // Remove enclosing '' NextToLastStr = ArgStr.substring(1, ArgStr.length() - 1); } else { tes.getTestLogManager().reportMessage("Could not extract value"); NextToLastStr = null; } return NextToLastStr; } }
1.1.4.9. Retrieve the maximum JVM heap size
The JVM_Info class retrieves the maximum heap size of the JVM.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import java.net.*; /** * The JVM_Info class retrieves the maximum heap size of the JVM. * It writes a message in the test log with the hostname where the * JVM is running and the JVM's maximum heap size in megabytes. */ /** * @author IBM Custom Code Samples */ public class JVM_Info implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { public JVM_Info() { } public String exec(ITestExecutionServices tes, String[] args) { Runtime rt = Runtime.getRuntime(); long maxMB = rt.maxMemory()/(1024*1024); // maxMemory() size is in bytes String hostName = "Unknown"; try { hostName = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e1) { tes.getTestLogManager().reportMessage("Can't get hostname"); return null; } tes.getTestLogManager().reportMessage("JVM maximum heap size for host " + hostName + " is " + maxMB + " MB"); return null; } }
1.1.4.10. Running an external program from a test
The ExecTest class runs a program, defined in the execName variable, on the system where the test is running.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.kernel.services.ITestLogManager; import org.eclipse.hyades.test.common.event.VerdictEvent; import java.io.IOException; /** * The ExecTest class runs a program, defined in the execName variable, * on the system where the test is running. * The test verdict is set to PASS if the program return code is 0. * The test verdict is set to FAIL if the program doesn't execute or * if the program return code is non-zero * In this sample, the program is perl.exe. */ /** * @author IBM Custom Code Samples */ public class ExecTest implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public ExecTest() { } public String exec(ITestExecutionServices tes, String[] args) { ITestLogManager logger = tes.getTestLogManager(); int rtnval = 1; Process p = null; String execName = "C:/Windows/System32/perl.exe C:/Perl/true.pl"; Runtime rt = Runtime.getRuntime(); // Execute test try { p = rt.exec(execName); } catch (IOException e) { logger.reportMessage("Unable to run = " + execName); logger.reportVerdict("Execution of " + execName + " failed", VerdictEvent.VERDICT_FAIL); return null; } // Wait for the test to complete try { rtnval = p.waitFor(); logger.reportMessage("Process return value is " + String.valueOf(rtnval)); } catch (InterruptedException e1) { logger.reportMessage("Unable to wait for " + execName); logger.reportVerdict("WaitFor on " + execName + " failed", VerdictEvent.VERDICT_FAIL); return null; } // Check the test return code and set the test verdict appropriately if (rtnval != 0) { logger.reportVerdict("Execution failed", VerdictEvent.VERDICT_FAIL); } else { logger.reportVerdict("Execution passed", VerdictEvent.VERDICT_PASS); } return null; } }
1.1.4.11. Add custom counters to reports
You can add custom counters to performance reports by using custom code. After running tests, the results from the custom counters are automatically aggregated in the same way that the default performance testing counters are (for example, byte and page counters). The aggregate for the custom counters is combined from all agent computers.
Note: Unless you place the custom counters under Run, Pages, or another root element, the Add/Remove Run Statistics Counters window will not contain information for the custom counters.
With the following code, you can add a custom counter. After running tests, you can display the custom counter on the report by dragging the custom counter from the results onto the report or by using the Add/Remove wizard.
package CustomCounter; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; /** * @author unknown */ public class Class implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public Class() { } /** * For javadoc of ICustomCode2 and ITestExecutionServices interfaces, select 'Help Contents' in the * Help menu and select 'Extending RPT functionality' -> 'Extending test execution with custom code' */ public String exec(ITestExecutionServices tes, String[] args) {tes.getStatisticsManager().getStatTree().getDistribution(new String[]{"Custom Counter","MyTest","MyValue"}) .submitDataPoint(Double.valueOf(Math.random()*100.).longValue()); return null; } }
1.1.4.12. Use transactions and statistics
You can use custom code to start transactions, gather additional statistics during a transaction, and stop a transaction.
The following code shows how to start a transaction. Transactions that are generated by test execution services automatically create and manage statistics.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.kernel.services.ITransaction; /** * @author IBM Custom Code Samples */ public class BeginTransaction implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public BeginTransaction() { } /** * For Javadoc information on the ICustomCode2 and ITestExecutionServices interfaces, * see the 'Test execution services interfaces and classes' help topic. */ public String exec(ITestExecutionServices tes, String[] args) { // the name of the transaction could have been passed in via data correlation mechanism. ITransaction foo = tes.getTransaction("foo"); foo.start(); return null; } }The following code shows how to gather additional statistics during a transaction.
package customcode; import com.ibm.rational.test.lt.kernel.ITime; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.kernel.statistics.IScalar; import com.ibm.rational.test.lt.kernel.statistics.IStat; import com.ibm.rational.test.lt.kernel.statistics.IStatTree; import com.ibm.rational.test.lt.kernel.statistics.impl.StatType; /** * @author IBM Custom Code Samples */ public class BodyTransaction implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public BodyTransaction() { } /** * For Javadoc information on the ICustomCode2 and ITestExecutionServices interfaces, * see the 'Test execution services interfaces and classes' help topic. */ public String exec(ITestExecutionServices tes, String[] args) { IStatTree tranStat; IStatTree timeStat; IStatTree countStat; IStat timeDataStat = null; // counter for the time RANGE IScalar countDataStat = null; // counter for the count SCALAR ITime timer = tes.getTime(); IStatTree rootStat = tes.getStatisticsManager().getStatTree(); if (rootStat != null) { // these counters set up the hierarchy tranStat = rootStat.getStat("Transactions", StatType.STRUCTURE); timeStat = tranStat.getStat("Body Time", StatType.STRUCTURE); countStat = tranStat.getStat("Bocy Count", StatType.STRUCTURE); // the name of the counters could have been passed in via data correlation mechanism timeDataStat = (IStat) timeStat.getStat("foo", StatType.RANGE); countDataStat = (IScalar) countStat.getStat("foo", StatType.SCALAR); } // get the start time long startTime = timer.timeInTest(); // do the work // whatever that work might be // get the end time long endTime = timer.timeInTest(); // update timeDataStat with the elapsed time if (timeDataStat != null) timeDataStat.submitDataPoint(endTime - startTime); // update the countDataStat if (countDataStat != null) countDataStat.increment(); return null; } }The following code shows how to stop a transaction.
package customcode; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; import com.ibm.rational.test.lt.kernel.services.ITransaction; /** * @author IBM Custom Code Samples */ public class EndTransaction implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public EndTransaction() { } /** * For Javadoc information on the ICustomCode2 and ITestExecutionServices interfaces, * see the 'Test execution services interfaces and classes' help topic. */ public String exec(ITestExecutionServices tes, String[] args) { // the name of the transaction could have been passed in via data correlation mechanism. ITransaction foo = tes.getTransaction("foo"); foo.stop(); return null; } }
1.1.4.13. Reporting custom verification point failures
You can use custom code to report a custom verification point failure.
The following code shows how to report a custom verification point failure.
package customcode; import org.eclipse.hyades.test.common.event.VerdictEvent; import org.eclipse.hyades.test.common.runner.model.util.Verdict; import com.ibm.rational.test.lt.execution.core.IVerificationPoint; import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices; /** * @author IBM Custom Code Samples */ public class Class implements com.ibm.rational.test.lt.kernel.custom.ICustomCode2 { /** * Instances of this will be created using the no-arg constructor. */ public Class() { } /** * For javadoc of ICustomCode2 and ITestExecutionServices interfaces, select 'Help Contents' in the * Help menu and select 'Extending RPT functionality' -> 'Extending test execution with custom code' */ public String exec(ITestExecutionServices tes, String[] args) { tes.getTestLogManager().reportVerificationPoint("CustomVP", VerdictEvent.VERDICT_FAIL); return null; } }
1.1.4.14. Debugging custom code
This example demonstrates debugging custom code by adding a breakpoint. It provides sample code to add a breakpoint. This way of debugging custom code is applicable only for a schedule.
- Start IBM Rational Performance Tester and create a performance test project MyProject.
- Create an HTTP test, MyTest, by recording a visit to http://<hostname>:7080/.
Note: Before accessing the URL, ensure that RPT is running. The URL returns an HTTP 404 error, which is expected.
![]()
- Expand the first request and click the response element.
- In the Test Element Details section, right-click in the Content field and click Create Field Reference.
- Type the reference name and click OK.
- Click the first page, and then click Add > Custom Code.
- In the Arguments section of Test Element Details, click Add.
- Expand the data source for the search results page, select the reference name that you created in step 5, and click Select.
- Click Generate Code. A new tab with the generated code is displayed.
- Insert the following the code into the exec() method:
ITestLogManager history = tes.getTestLogManager(); if (args.length > 0) { if (args[0].indexOf("Invester Relations") != -1) { history.reportMessage("First page failed. Bail loop!"); tes.getLoopControl().continueLoop(); } }Important:
- Fix the double quotation marks, if any, so they are straight and the compiler no longer gives warning.
- To resolve complier warnings related to importing a class, press Ctrl + Shift + O.
The code will look like this:
![]()
- To set a breakpoint, click anywhere on the args[0].indexOf line. Move the pointer to the left-most portion of the text editor window and double-click with the pointer horizontally on the same line. A blue button is displayed in this left-most portion of the window indicating the breakpoint is set.
![]()
- Save the custom code and then the test.
- Create a new schedule, Schtest.
- In Schtest, set the number of users to run to 1.
- Click User Group 1 and click Add > Test. Select the MyTest test and click OK.
- Click User Group 1 and click the Run this group on the following locations button.
- Click Add > Add New.
- In the New Location window, type the following information:
- In Host name, type localhost.
- In Name, type debuglocation.
- In Deployment directory, type C:\mydeploy.
- Click Finish.
- Save the schedule.
- In the Test Navigator, right-click debuglocation and click Open.
- Click the General Properties tab and click Add.
- In the Property name field, type RPT_VMARGS and in the Property value field, add the following values each separated by a space.
-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
![]()
- Save the location.
- Attach the debugger to the schedule execution process.
- Run the schedule. Because the schedule is using debuglocation, it will pause at the beginning to let you attach the debugger to the execute process.
- Click Window > Open Perspective > Other > Debug.
- Click Run > Debug Configurations.
- In the Debug Configurations window, right-click Remote Java Application and click New.
- Click Debug. A list of running threads are displayed in the Debug window and the schedule execution pauses at the debug breakpoint.
- If you are doing it for the first time, you might need to provide the source location to see the custom Java code. You do this by taking the following steps:
- Click Edit Source Lookup Path and click Add.
- Click Workspace Folder > OK.
- Now, expand MyProject, select the src folder, and click OK. The schedule run stops at the specified breakpoint.
![]()
1.1.5. Migrate custom code from previous versions
You can run scripts that contain custom code from previous releases and edit tests to make new calls to old or new custom code classes.
You can perform the following tasks without any additional steps:
- Run a script containing custom code that was created in a previous release.
- Edit a test to make a new call to an old custom code class.
- Add new custom code to a test containing old custom code.
To edit a class in existing custom code so that it can call new TestExecutionServices methods, type cast the IKlog argument in the old custom code to the ITestExecutionServices interface.
1.2. Extending RPT to support other protocols
The Rational Performance Tester Extensibility Software Development Kit (SDK) enables you to create extensions to support testing new protocols without modifying the core product code. You can create an extension that provides functionality for testing other protocols in addition to the HTTP protocol support that is standard in RPT.
RPT is a performance testing tool and part of the Rational Software Delivery Platform. RPT provides the capability to emulate multiple users applying load on a system under test. To test performance, you record a test and subsequently replicate it, and then play it back against the system being tested. Refer to the RPT product information center for more details.
RPT supports the HTTP protocol. The goal of the Extensibility SDK is to provide a framework that supports the development of various protocols which can be plugged into RPT.
To develop support for testing protocols in RPT, you must be familiar with the following areas:
- The protocol that you intend to support
- The Eclipse platform (The Eclipse documentation provides relevant information in the "JDT Programmer's Guide" topic.)
- The Test and Performance Tools Platform (TPTP).
For examples of RPT extensions, see the following plug-ins:
- com.ibm.rational.test.lt.sdksamples.core.socket
- com.ibm.rational.test.lt.sdksamples.recorder.socket
- com.ibm.rational.test.lt.sdksamples.models.behavior.socket
- com.ibm.rational.test.lt.sdksamples.codegen.socket
- com.ibm.rational.test.lt.sdksamples.protocol.socket
- com.ibm.rational.test.lt.sdksamples.testgen.socket
- com.ibm.rational.test.lt.sdksamples.editor.socket
- com.ibm.rational.test.lt.sdksamples.results.socket
- com.ibm.rational.test.lt.sdksamples.datacorrelation.testgen.socket
- com.ibm.rational.test.lt.sdksamples.datacorrelation.execution.socket
1.2.1. Protocol extension structure
Before organizing your extension, note the restrictions and guidelines provided in this topic.
Restrictions
The following restrictions apply to plug-ins:
- The execution portion of the plug-in must be deployed to an agent. You cannot assume that the Software Development Workbench is available.
- The execution portion of the plug-in code cannot depend on any workbench code.
- The execution portion of the code cannot depend on the workspace because none is available on the agent.
For best results, minimize the amount of code that is deployed to the agent for execution.
Guidelines for naming plug-ins
Most base Rational Performance Tester plug-ins follow this naming convention:
<prefix>.<component>[.subcomponent].<protocol>
where:
- prefix: The prefix com.ibm.rational.test.lt is used for all load-test-specific RPT plug-ins.
- component: One of the extension components:testgen, datacorrelation, testeditor, codegen, execution, or models.
- subcomponent: Some plug-ins contain subcomponents (for example, execution.ui contains the UI portion of execution), or they might have separate code based on their use in a different component (for example, datacorrelation.testgen and datacorrelation.execution).
- protocol: For example, http, or sap. (Some plug-ins use core for the base plug-in.)
Use these conventions, the extension can have the following plug-ins:
- com.ibm.rational.test.lt.recorder.protocol
- com.ibm.rational.test.lt.testgen.protocol
- com.ibm.rational.test.lt.models.protocol
- com.ibm.rational.test.lt.testeditor.protocol
- com.ibm.rational.test.lt.sdksamples.datacorrelation.testgen.protocol
- com.ibm.rational.test.lt.sdksamples.datacorrelation.execution.protocol
- com.ibm.rational.test.lt.codegen.protocol
- com.ibm.rational.test.lt.execution.protocol
- com.ibm.rational.test.lt.execution.results.protocol
Alternatively, you can divide the code into two plug-ins, one for the workbench and one for execution. This has the advantage of deploying fewer plug-ins. For an example, refer to the Siebel extension to Performance Tester.
Use this method, you would have the following two plug-ins:
- com.ibm.rational.test.lt.protocol
- com.ibm.rational.test.lt.protocol.execution
Within these plug-ins, you can either arrange the components in one of two ways:
- As separate source folders
- As separate packages within a single source folder
1.2.2. Extending the test recorder
To generate a test, the data exchanged by an application and another entity such as a server must be recorded. The recorded data is then processed to generate a test.
Data is recorded in a recording session. In a recording session, clients are launched in parallel with recorders that capture the data that the clients and the servers (or other external entities) exchange. All data that is captured during a recording session is stored in a file.
You can contribute to the recording framework in several ways:
- By defining a new kind of captured data. Captured data is implemented as recorder packets. You can define a new type of recorder packet in which to store relevant information for a specific kind of captured data. The product comes with a set of recorder packet types. The packet types include proxy packets, socket packets, HTTP packets, SAP packets, and Citrix packets.
- By developing a new recorder to capture a new kind of data or to use a different recording technique. The product comes with a set of recorders. The bundled recorders include the HTTP proxy recorder, the SOCKS proxy recorder, the socket recorder, the SAP Recorder, and the Citrix recorder.
- By developing a new client whose data will be recorded. A client is not necessarily an application, but rather the ability to start an application. The product comes with a set of clients. The clients include: Application, Manual, Microsoft Internet Explorer, Mozilla Firefox, Apple Safari, Opera, Google Chrome, Generic Service Client, SAP client, Citrix client, and TN3270 client.
- By defining a new recorder-client binding. A recorder-client binding declares that a recorder is able to capture the data that a client sends or receives. A recorder-client binding can also define a client decorator, which configures a client so that the client can be recorded by the recorder. For example, the product comes with a decorator that can modify Internet Explorer settings so that the browser sends its traffic through a proxy recorder.
After the recorder framework has been defined, the user interface (UI) can be extended so that the new recording components can be used. The following elements are extensible:
- Packet types, recorders, and clients. You can assign specific icons to these elements.
- Wizards. You can define wizards for configuring a specific client, a specific recorder, several recorders together, or a client and several recorders together.
The recording framework has three main UI contributions:
- The New Recording Session wizard. With this wizard, you can select the client to use, and then select the recording technique (in other words, the recorder), and finally set the configuration details for the client and the recorder. Extensions can contribute to this wizard.
- The Recording Control view. This view shows the active recording session and the recorders and clients that are involved. This view shows a summary of captured data, and messages that the recorders and clients have issued. Extensions can contribute to labels and icons displayed in this view. Any user message that a recorder or a client sends is also displayed in this view.
- The Recording Session Editor, which opens when you double-click a recording session (.recsession) file. The editor shows the same information as the recording control view, but includes more details about the captured data. Extensions can contribute actions, labels, and icons to this editor.
1.2.2.1. Defining a new type of captured data
The data captured by a recorder must be stored in a file and consumed by a test generator to produce a test. Captured data must be defined so that it can be stored by the recorder and consumed by the test generator.
To define a new type of recorded data, complete these tasks:
- Declare a new type of recorder packet in the plugin.xml file, using the com.ibm.rational.test.lt.recorder.core.recorderPacket extension point.
- Define one or more implementation classes, in the same plug-in that implement the interface: com.ibm.rational.test.lt.recorder.core.packet.IRecorderPacket.
These factors apply to IRecorderPacket class implementations:
- You can define as many fields as required to store your data.
- Because these classes are instantiated by a recorder, they must expose a way to construct themselves.
- Because a test generator uses these classes, the classes must expose ways to retrieve information for the test generator. In other words, they must expose getter methods.
- You must make the classes serializable. Take this into account when designing the classes. The classes must not have any references to data that you do not want to include in the stored information. Fields that include references to data that you do not want stored must be declared using the transient keyword. If you plan to have evolutions to these classes, make sure that these evolutions are compatible with earlier versions.
Recorder packets have these common features:
- They have a type. This must be a type ID that is declared in an extension to the com.ibm.rational.test.lt.recorder.core.recorderPacket extension point. The implementation class must be in the same plug-in where the type ID is declared.
- They have a start time and end time. This is important for sorting captured packets, as recorders typically do not send the packets at the exact same time as they are captured. Many packets have the same time for start and end events, because they are atomically captured. Packets do not have to have different start and end times. Packet end times must, however, be higher or equal to the start time. All timestamps must be expressed in units that the framework provides. The units must be of the highest accuracy that the system permits.
For best results, follow these practices for implementing recorder packets:
- If you have more than one type of data, define an interface that all implementation classes implement.
- Define an interface for each concrete implementation class, which exposes only read-only features of the class. The test generator requires access only to the interfaces, while the recorder requires access to the classes.
- Do not define redundant or computable fields. Because the classes are serialized, a single additional field might make the recording file much larger if many packets are stored. Declare a redundant or computable field using the transient keyword.
1.2.2.2. Defining a new recorder
To capture a new kind of data or an existing kind of data with a new recording technique, define a new recorder.
The only required task for a recorder is to capture data. The recorder does not start or configure a client.
To define a new recorder:
- Declare a new recorder type in the plugin.xml file. Use the com.ibm.rational.test.lt.recorder.core.recorder extension point.
- Determine whether the recorder can be run locally, within the workbench, or if it must run remotely in a separate Java. virtual machine (JVM).
- Define a delegate, which is the implementation class of the recorder. The delegate starts and stops the recorder, and captures data.
- If the recorder is remote, define a remote launcher implementation that provides details about the JVM that runs the delegate.
- Optionally, define a prerequisite validator that performs basic verification that the recorder can be run on the current computer.
- Declare which types of recorder packets are emitted by the recorder.
Follow these requirements for implementing the IRecorderDelegate interface:
- This interface starts and stops the recorder and provides notification about events such as "recorder started," "recorder stopped," and "packet captured." You must provide an implementation of this interface. Optionally, a recorder can support being paused and resumed. If the recorder does not support pause and resume operations, ensure that the implementations of the methods are empty.
- Most methods are asynchronous. In other words, the framework does not require that the operation be completed when the method returns. For this reason, a recorder delegate must notify the framework when an operation is complete.
- A recorder can have a configuration. The configuration is built either by using a XML file with the .recconfig extension or by using a wizard. For an example of an XML file, open an existing recording session file, and then click File > Save recording configuration as. The configuration is stored in a RecorderConfiguration object, which is a map of strings to various types of objects. A recorder delegate reads its configuration in its initialize() method.
- A recorder delegate is given a context in its initialize() method. This context enables the recorder to send notifications of events, record log messages, and send captured packets.
- Typically, you extend the BaseRecorderDelegate class rather than directly implement IRecorderDelegate interface. The base abstract class provides a basic behavior for most methods, so that you can override only those needed.
- A recorder must use the getContext().packetCaptured() method to notify the framework when it has captured data.
- When a recorder constructs a packet, the packet must be filled with a recorder ID. The recorder ID is attributed by the framework and can be retrieved using the getContext().getComponentUniqueId() method.
- When a recorder constructs a packet, the packet must be filled with time information. The time information must be expressed in a unit that the framework defines. The current time, expressed in the framework units, can be retrieved using the getContext().currentTime() method.
- A recorder delegate can send messages to the user by invoking the sendUserMessage() method or the getContext().sendMessage(new UserMessage(...)) method if the delegate does not extend the BaseRecorderDelegate class.
- A recorder delegate can be enabled to communicate with outside entities by setting or retrieving dynamic properties or by receiving messages. A recorder delegate is useful only if you develop a specific UI for the recorder or if you define a client decorator for the recorder.
1.2.2.3. Defining a new client
To produce data, a recording session must start at least one client. Several clients come with the product. To automate starting a specific application, you can define a new client.
The only task for the client is to start an application and to notify the recording framework about client life cycle events.
Sometimes there is no client to launch because the client already exists or because the client is a system that has its own life cycle. In this case, you can use the Manual client that comes with the product. The ID of the Manual client is com.ibm.rational.test.lt.recorder.core.manualClient.
To define a new type of client, complete these tasks:
- Declare a new client type in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.core.client extension point.
- Determine whether the client can be run locally within the workbench, remotely in a separate JVM, or in the same JVM as the recorder to which it is bound.
- Define a delegate, which is the implementation class of the client. The delegate starts and stops the client and notifies the framework when the client is started or closed.
- If the client is remote, define a remote launcher implementation that provides details about the JVM that runs the delegate.
- Optionally, define a prerequisite validator that performs basic verification that the client can be run on the current computer.
To implement the IClientDelegate interface:
- You must provide an implementation of this interface. This interface starts and stops the client and sends notifications of events such as "client started" and "client stopped."
- The start() and stop() methods are asynchronous. In other words, the operation does not need to be complete when these methods return. For this reason, a client delegate must notify the framework when an operation is complete.
- The stop() operation is invoked only when the user clicks Stop in the user interface. In many cases, the user gestures in the application itself to close the application. The delegate must monitor the application and notify the framework when the application is closed.
- A client can have a configuration. The configuration is built either by using an XML file, which is a file with the .recconfig extension or by using a wizard. For an example of an XML file, open an existing recording session file, and then click File > Save recording configuration as. The configuration is stored in a ClientConfiguration object, which is a map of strings to various types of objects. A client delegate reads this configuration in its initialize() method.
- A client delegate is given a context in its initialize() method. This context enables the recorder to send notifications of events and record log messages.
- Typically, you extend the BaseClientDelegate class rather than directly implement the IClientDelegate class. The base abstract class provides a basic behavior for most methods. You can override specifically those that you must override.
- A client delegate can send messages to the user by calling the sendUserMessage() method, or by calling the getContext().sendMessage(new UserMessage(...)) method if the delegate does not extend the BaseClientDelegate class.
- A client delegate can be enabled to communicate with outside entities by setting or retrieving dynamic properties, or by receiving messages. The client delegate is useful only if you develop a specific UI for the client or if you define a client decorator for the client.
If your client launches a specific process, you can extend the com.ibm.rational.test.lt.recorder.core.clients.ProcessBuilderClientDelegate class. In this case, you need to extend only the initialize() method, and then invoke setter methods to set up the command line, arguments, environment variables, and working directory.
1.2.2.4. Defining how a recorder can record a client
All recorders cannot record all clients. The framework must be notified that a recorder can record a client. In many cases, the recorder must configure a client before recording, and then undo these configuration actions when the client is closed. In these cases, you can declare a client decorator. A client decorator is a class that configures a client so that it can be recorded by a recorder.
To declare that a recorder can record a client:
- Declare a new recorder-client binding in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.core.recorderClientBinding extension point.
- Specify the IDs of the recorder and the client that are compatible.
- Optionally, declare a client decorator. A client decorator is a class that modifies a client so that it can be recorded by the recorder.
To implement a client decorator, define an implementation of the com.ibm.rational.test.lt.recorder.core.extensibility.IClientDecorator class.
The implementation has two main methods: decorate() and undecorate(). The decorate() method is called when the recorder starts running, but before the client is launched. The undecorate() method is called after the client is closed, but before the recorder is stopped.
The decorator can interact with the recorder delegate and the client delegate by setting or getting properties from them. The methods for doing so are available in the decorator context. The client and the recorder must support the properties.
Typically, you extend the BaseClientDecorator class rather than directly implementing the IClientDecorator class. The base abstract class provides a basic behavior for most methods. With that class, you can override specifically those methods that you must override.
1.2.2.5. Running recorders and clients without a UI
To test recorders, clients, and decorators during the development process, you can run those elements before any UI components are ready to start the recorders, clients, and decorators.
You can start a recording session that includes the recorders or clients that you have developed by using a recording session configuration file. This file specifies which recorders and clients to start and the options for the recorders and clients to use.
The following file is an example of such a configuration file:
<?xml version="1.0" encoding="UTF-8"?> <rpt:session xmlns:rpt="rpt"> <rec:myRecorder <-- recorder configuration --> xmlns:rec="recorder:org.xyz.myplugin" option1="value1" option2="value2" id="myRecorder1"/> <cli:myClient <-- client configuration --> xmlns:cli="client:org.xyz.myplugin" optionA="valueA" id="myClient1"/> <rpt:binding client="myClient1" recorder="myRecorder1"/> </rpt:session>In the preceding example, replace org.xyz.myplugin with the name of the plug-in that defines the recorder and the client. Replace myRecorder with the recorder ID and myClient with the client ID. The option1 and option2 attributes can be replaced by attributes that the recorder supports. Replace the optionA attribute with an attribute that the client supports. You must save the file with the .recconfig extension.
In the configuration file, a session node can contain as many recorder configurations and client configurations as required. All the recorders and clients that are referred to are launched together and the options from the configuration file are passed to them. All recorders and clients must have id attributes so the recorder and clients can be referred to in a binding node. Examples of recording session configuration files can be generated by opening an existing recording session and clicking File > Save Recording Configuration As.
To start a recording session from a recording configuration file, right-click the file, and then select Start Recording Session.
1.2.2.6. Defining wizards for recorders and clients
You can define wizards for recorders and clients.
The New Recording Session wizard runs in the following sequence:
- The recording session file is selected.
- The client is selected.
- The recording method is selected.
- The client wizard pages are displayed.
- The recorder wizard pages are displayed.
Steps 4 and 5 can be combined in into a single step. Steps 4 and 5 are extension contributions.
Declare a wizard for each client and each recorder that you define. A client or a recorder that has no declared wizard is not available in the user interface and can be started only with a recording configuration file or programmatically.
When the New Recording Session wizard is complete, the wizard produces a recording session configuration containing a client configuration and one or more recorder configurations. The recording session configuration is used to start a recording session and to instantiate the corresponding recorders and clients.
Several types of wizards can be defined, depending on how you have defined recorders and clients:
- If you have defined only a recorder and you plan to use a client that comes with the product, define a recorder wizard.
- If you have defined one or more types of clients and one or more types of recorders, and you want to combine clients and recorders in several ways, define a wizard for each client and a wizard for each recorder.
- If you have defined one recorder type and one client type to be used together, consider defining a unique wizard that configures both of them.
Additional information about the New Recording Session wizard:
- The recording method selection step is displayed only if there is more than one recording method available for the client that is selected in step 2.
- Wizards that configure both recorders and clients take precedence over separate wizards for clients and recorders.
To define a client wizard:
- Declare a new client wizard in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.ui.clientWizard extension point.
- Specify the client ID that the client wizard configures.
- Optionally, provide an implementation class. If you do not provide a class, the wizard has no configuration page. If you provide a class, it must extend the com.ibm.rational.test.lt.recorder.ui.wizards.NewClientWizard class.
About the NewClientWizard class implementations:
- This class extends the JFace class wizard, so the class must extend typical methods such as addPages().
- The class is passed an empty client configuration, typed with the client ID selected by the user in step 2.
- The class sets the client configuration options, which are available using the getClientConfiguration() method. This configuration is typically done in the doPerformFinish() method.
To define a recorder wizard:
- Declare a new recorder wizard in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.ui.recorderClientWizard extension point and the recordersWizard element.
- Specify one or more recorder IDs that the recorder wizard configures.
If the recorder wizard declares configurations for more than one recorder, examine the performsRecorderSelection attribute. This attribute specifies whether the wizard enables the user to choose which recorders to use or if the framework chooses which recorder to use. Depending on the conditions, the attribute then calls the wizard with the recorder ID that the user selected. In the first case, the recording method selection step is always displayed, whereas in the latter case, the recording method selection step can be skipped. In the first case, the wizard can enable several recorders, whereas in the latter case, only one recorder is enabled. Choose the first option if you need to enable more than one recorder in one recording session.
- Provide an implementation class. This is optional only if you have associated the wizard with exactly one recorder or if the performsRecorderSelection attribute is false. If you do not provide a class, the wizard has no configuration page. If you provide a class, it must extend the com.ibm.rational.test.lt.recorder.ui.wizards.NewRecordersWizard class.
About NewRecordersWizard implementations:
- This class extends the JFace class Wizard, so it must extend typical methods such as addPages().
- If the performsRecorderSelection value is true, the class is passed null to its initialize() method and it must invoke the setRecorderConfigurations() method from its doPerformFinish() method, with the recorder configurations for each recorder that must be included in the recording session.
- If the performsRecorderSelection value is false, the class is passed an empty recorder configuration, typed with the recorder ID selected in step 3, to its initialize() method and it must fill the empty recorder configuration with recorder options in its doPerformFinish() method.
To define a wizard that configures a client and one or more recorders altogether:
- Declare a new recorder and client wizard in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.ui.recorderClientWizard extension point and the combinedWizard element.
- Specify the client ID that this wizard configures.
- Specify one or more recorder IDs that this wizard configures.
- If this wizard declares configurations for more than one recorder, examine the performsRecorderSelection attribute. This attribute specifies whether the wizard lets the user choose which recorders to use or if the framework chooses which recorder to use, and then invokes the wizard with the recorder ID selected by the user. In the first case, the recording method selection step is always displayed, whereas in the latter case, the recording method selection step can be skipped. In the first case, the wizard can enable several recorders, whereas in the latter case, only one recorder is enabled. Choose the first option if you need to enable more than one recorder in one recording session.
- Provide an implementation class. This is optional only if you have associated the wizard with exactly one recorder or if the performsRecorderSelection attribute is false. If you do not provide a class, the wizard has no configuration page. If you provide a class, it must extend the com.ibm.rational.test.lt.recorder.ui.wizards.NewRecorderClientWizard class.
About NewRecorderClientWizard class implementations:
- This class extends the JFace class Wizard, so it must extend typical methods such as addPages().
- If performsRecorderSelection is true, the class is passed a null recorder configuration to its initialize() method and it must invoke the setRecorderConfigurations() method from its doPerformFinish() method, with the recorder configurations for each recorder that must be included in the recording session.
- If performsRecorderSelection is false, the class is passed an empty recorder configuration, typed with the recorder ID selected in step 3, to its initialize() method and it must fill the empty recorder configuration with recorder options in its doPerformFinish() method.
- The class is passed a client configuration, typed with the client ID selected in step 2, to its initialize() method. It must fill the client configuration with client options in its doPerformFinish() method.
1.2.2.7. Migrate recorder implementations from previous versions
The new recording framework includes significant improvements over the generic recorder framework (GRF) that was delivered in previous versions. The new framework introduces more flexibility for combining clients and recorders. With the framework, you can start multiple recorders and clients in one session, consolidating the recorded data in one file. In addition, the framework does not use the TPTP Agent Controller, and thus does not require recorders and clients to be run in a separate Java. Virtual Machine. Finally, the new framework provides improved performance and better scalability in terms of the amount of recordable data and the impact on memory usage.
The API has been completely redefined to produce these improvements. Migrating existing recorder implementations to the new framework requires significant effort. The following tables summarize the changes to classes and methods in the new recording framework.
Previous class New recording framework, version 8.2 and later Comments com.ibm.rational.test.lt.trace.PayloadMsg com.ibm.rational.test.lt.recorder.core.packet.IRecorderPacket com.ibm.rational.test.lt.recorder.core.packet.connection.IConnectionPacket Use Java serialization for serialization in this product version. If packets support connections, extend the IConnectionPacket interface and its subinterfaces. This extension enables filtering capabilities at test-generation time. org.eclipse.hyades.execution.recorder.remote.RecorderAgent com.ibm.rational.test.lt.recorder.core.extensibility.BaseRecorderDelegate The run() method has been replaced with the start() method. The threads that this method can start are no longer monitored by the framework. The setIsReady(true) method has been replaced with the sendStarted(enabled) method. The handleCommand(STOP) method has been replaced with the stop() method. The class calls the sendStopped() method when the recorder has stopped. (This event was previously implicitly notified by the termination of the thread returned by the run() method.) This class is no longer required to run in a separate JVM. org.eclipse.hyades.execution.recorder.remote.RecorderEnvironmentAdapter org.eclipse.tptp.test.provisional.recorder.framework.AbstractRecorderExecOptionsProvider org.eclipse.hyades.execution.recorder.remote.RecorderExecutableObjectAdapter com.ibm.rational.test.lt.recorder.core.deploy.IRemoteLauncher This class is required only if the recorder delegate must run in a separate JVM. With this version, you can add classpath entries and system properties to the launched JVM. There is no requirement for file deployment because the JVM is always run on the local computer. Recording on remote computers was never enabled in previous releases. The AbstractRecorderExecOptionsProvider.getAgentClassPath() and RecorderExecutableObjectAdapter.getAgentClassPath() classes are replaced by the delegate class declaration in the extension point. org.eclipse.tptp.test.provisional.recorder.messages.AbstractRecorderMessageProvider None The IRecorderPacket auto-serialization class and the framework handle message serialization in this version. Implementing this class is no longer required. org.eclipse.tptp.test.provisional.recorder.ui.wizards.DefaultRecWizardProvider com.ibm.rational.test.lt.recorder.ui.wizards.NewRecorderWizard com.ibm.rational.test.lt.recorder.ui.wizards.NewRecorderClientWizard org.eclipse.tptp.test.provisional.recorder.framework.RecorderClientHelperAdapter None The framework automatically handles the step that this class completed in earlier versions.
Previous extension point New recording framework, version 8.2 and later Comments org.eclipse.hyades.test.core.Recorder#Recorder.protocol com.ibm.rational.test.lt.recorder.core.recorder#recorder.outputPacket org.eclipse.hyades.test.core.Recorder#Recorder.id com.ibm.rational.test.lt.recorder.core.recorder#id org.eclipse.hyades.test.core.Recorder#Recorder.name com.ibm.rational.test.lt.recorder.core.recorder#name org.eclipse.hyades.test.core.Recorder#Recorder.recorderAgent com.ibm.rational.test.lt.recorder.core.recorder#recorder.delegate org.eclipse.hyades.test.core.Recorder#Recorder.recorderClientHelper None The framework automatically handles the step that this class handled in earlier versions. org.eclipse.hyades.test.core.Recorder#Recorder.recorderMessageHandlers None The IRecorderPacket auto-serialization class and the framework handle message serialization in this version. Implementing this class is no longer required. org.eclipse.hyades.test.core.Recorder#Recorder.wizardPageProvider com.ibm.rational.test.lt.recorder.ui.recorderClientWizard#recordersWizard.class org.eclipse.hyades.test.core.Recorder#Recorder.execOptionsProvider com.ibm.rational.test.lt.recorder.core.recorder#recorder.remoteLauncher See the previous comment about the AbstractRecorderExecOptionsProvider class. org.eclipse.hyades.test.core.Recorder#Recorder.requiresIntermediateFile None This option is no longer supported. Recording sessions are always kept after recording. org.eclipse.hyades.test.core.Recorder#Recorder.fileExtension None This option is no longer supported. The recording format and extension are now always controlled by the framework in a .recsession file. org.eclipse.hyades.test.core.Recorder#Recorder.icon com.ibm.rational.test.lt.recorder.ui.recordingUiImage#recorderImage.icon org.eclipse.hyades.test.core.Recorder#Recorder.description com.ibm.rational.test.lt.recorder.ui.recorderClientWizard#recordersWizard.description
1.2.3. Extending the test generation framework
Test generation consists of processing the recorded data and producing a test.
The recording-session file that is produced during recording provides the input for the test generation operation. Before writing a test generator, you must identify the type of data that the generator uses.
Recording and test generation are typically chained when you use the New Test From Recording wizard, but this relationship is not always established. The user can choose to produce only a recording, using the New Recording Session wizard, and can also choose to generate a test from an existing recording.
Test generation happens in the following stages and phases:
- The conversion stage is a preliminary step, during which the original packets can be filtered, sorted, aggregated, or converted to a different, typically higher-level, protocol. Extensions can contribute additional converters.
- The test generation stage consists of using the recorder packets that are sent from the converter stage, and then distributing the recorder packets to the appropriate test generator. The test generator then produces the corresponding model elements in the test model. Extensions can contribute new test generators for processing a new type of recorded data or producing a new type of test element.
- The data processing phase happens after a raw test has been completely generated. During this phase data correlation and data transformation are performed.
- The test splitting phase is an optional step that runs if split points were inserted during the recording. The complete test is split into several tests.
The test generation framework also defines two wizards:
1.2.3.1. Defining a new test generator
A test generator uses recorder packets and produces test-model elements that the test generator adds to the test model.
Before defining a test generator, you must identify the type of data that the generator can use and the type of model elements that the generator produces. The input data for a test generator can either be the raw data that the recorders produced, or data that has been altered during the conversion stage. The output elements of a test generator are typed by feature, which usually corresponds to a specific protocol.
To define a new test generator:
- Declare a new test generator type in the plugin.xml file, using the com.ibm.rational.test.lt.testgen.core3.testGenerator extension point.
- Assign the test generator a unique ID and a name.
- Declare which packet types the test generator can use.
- Declare the required properties of the packet stream that is sent to the test generator. The framework includes the necessary converters in the conversion stage so these properties are verified when the packets reach the test generator.
- Define an implementation class that implements the com.ibm.rational.test.lt.testgen.core.testgen.ITestGenerator interface.
The product includes a built-in converter that produces an ordered stream of packets that is based on start time stamps of the packets. Typically, recorders produce packets that are sorted by their end time stamps. Most test generators require that input packets be ordered by their start time stamps, so the packet stream must include the sorted property in the required properties.
Consider these facts about ITestGenerator implementations:
- Typically, you extend the com.ibm.rational.test.lt.testgen.core.testgen.BaseTestGenerator class, which provides a basic implementation and only requires overriding the necessary methods.
- The initialize() method is where any options from the test generator configuration are read using the getContext().getConfiguration() method.
- The process() method is the most important. The framework calls this method for each input packet. This method creates model elements and adds the elements to the output test. Model elements must be added to the test using methods from the ITestStack object that is returned by the getContext().getStack() method.
- The framework calls the complete() method after all packets have been sent to the test generator. The complete() method can be used for performing any post-processing operations.
- Use the getContext().logMessage() method to report any messages from the test generator to the user, including error messages. If the message pertains to an unrecoverable error, the framework stops the test generation process.
Note: A test generator must not delay in adding elements to a test. Test generators must add elements to the test as soon as they are created. A test generator can still add data to an element after the element has been added to a test. If there are delays in adding elements to a test, generated elements might not be correctly ordered. For example, if another test generator also generates elements in a mixed protocol environment, or if the recording contains annotations, then the generated elements might not be in the correct order. If you still need to perform processing that would delay the insertion of an element into to the test (for example, accumulating data in order to build a higher-level object), write a converter that does the processing and insert this converter before the test generator starts.
1.2.3.2. Defining a new packet converter
A packet converter transforms a stream of recorder packets. Use packet converters for adapting the raw data that recorders capture into a suitable format for the test generators to use.
A converter typically follows one of these patterns:
- A filtering converter removes input packets that do not meet a specific criterion. This converter does not modify packets, nor does the converter introduce new packets in the output stream. The product comes with a generic converter, with the com.ibm.rational.test.lt.testgen.core3.filter ID. You can add parameters to this converter with conditions.
- An annotator converter does not remove or modify packets in the input stream, but rather introduces additional packets in the output stream. These packets are annotation packets that convey additional information that are inferred from the other packets. For instance, a converter might look for session, connection, or page boundaries in a packet stream, and then add a boundary packet whenever a boundary is detected in the input stream. This assists the test generator in identifying boundaries without the need to look ahead in the packet stream.
- A reordering converter does not add, change, or remove packets from the input stream, but it outputs them in a different order. A typical example is the packet sorter that comes with the product, which outputs packets sorted by their start time stamp. The sorter ID is com.ibm.rational.test.lt.testgen.core3.packetSorter.
- An aggregator converter has different input and output packet types. It aggregates multiple input packets into one output packet. The converter usually translates a lower-level protocol into a higher-level protocol. For example, the product comes with a converter that transforms raw data in a byte stream that is exchanged between a client and an HTTP server into aggregated HTTP packets (request/response pairs).
To define a new converter, you must complete these procedures:
- Declare a new packet converter type in the plugin.xml file, using the com.ibm.rational.test.lt.testgen.core3. packetConverter extension point.
- Assign the packet converter a unique ID and a name.
- Declare the required properties of the packet input stream that the converter receives. The framework includes the required converters in the conversion stage, so these properties are verified when the packets reach the converter. For instance, if the converter requires the input packets to be ordered according to their start time stamps, specify the sorted parameter.
- Declare which properties this converter adds to the output stream or removes from the output stream, as compared to the properties of the input stream. For example, a converter might disrupt the ordered property of the input stream; in this case, the sorted parameter must be included in removedProperties class.
- If the converter has different input and output packet types and can be considered as a packet type converter, declare that it contributes to packet type conversions and specify the types of input and output packets that the converter produces.
- Define an implementation class that implements the com.ibm.rational.test.lt.testgen.core.conversion.IPacketConverter interface.
Consider these facts about IPacketConverter implementations:
- Typically, you extend the com.ibm.rational.test.lt.testgen.core.conversion.BasePacketConverter class, which provides a basic implementation and only requires overriding the specific methods.
- A packet converter is an IPacketReferenceOutputStream interface that writes to another IPacketReferenceOutputStream interface. In other words, a packet converter has a writePacket() method, which is invoked by the framework for each input packet it processes. The packet converter is responsible for invoking the getContext().getOutputStream().writePacket() method whenever it needs to send a packet to its output.
- A packet converter can have options. The options are available by using the getContext().getConfiguration() method, which is typically called in the initialize() method.
- A packet converter can send additional packets to its output in the complete() method. This method is called when there are no more input packets to use.
- Use the getContext().logMessage() method to report messages from the test generator to the user, including error messages. If the message pertains to an unrecoverable error, the framework stops the test generation process.
For scalability reasons, converters manipulate IRecorderPacketReference objects instead of IRecorderPacket objects. Follow these procedures to get the best results when you write packet converter code:
- An IRecorderPacket interfacecan be obtained from a reference that uses the IRecorderPacketReference.getRecorderPacket() method.
- A converter that must echo the same packet to its output as the one received must write the same reference instance that the converter has received.
- When a converter instantiates a new recorder packet, the converter can wrap the packet into a new reference by using the getContext().createPacketReference() method so that the packet can be sent as output.
- Just as recorders can produce packet attachments, converters can do so as well. To create a new attachment, use the getContext().createPacketAttachment() method.
- If the converter must hold a packet a long time before the packet is sent as output, the converter can unload the packet and keep only a reference to the packet. To do so, call the unload() method on the packet reference.
- Converters are provided with a facility for efficiently accumulating a large number of packets and atomically discarding them, or flushing them, to the output. See the com.ibm.rational.test.lt.testgen.core.store.IPacketReferenceStore class for more information. A packet store can be created using the getContext().createPacketStore() method.
1.2.3.3. Generating tests without a UI
To test converters and test generators during the development process, you can run them before any UI component is ready for launching them.
You can start test generation that includes the converters and test generators that you have developed by writing a test generation configuration file. This file specifies which converters and test generators to use and options for each.
This file is an example of such a configuration file:
<rpt:testGeneration xmlns:rpt="rpt" recsession="/Project/MyRecording.recsession" autoDataCorrelation="true" autoDataCorrelationNames="true" output="/Project/MyTest.testsuite"> <cnv:packetSorter xmlns:cnv="converter:com.ibm.rational.test.lt.testgen.core3"/> <cnv:myConverter xmlns:cnv="converter:org.xyz.myplugin"/> <gen:myTestGenerator xmlns:gen="generator:org.xyz.myplugin"/> </rpt:testGeneration>In the preceding example, replace org.xyz.myplugin with the name of the plug-in that defines the converter and the test generator. Replace myConverter with the converter ID, and myTestGenerator with the test generator ID. Save the file with the .testgenconfig extension.
In the configuration file, a testGeneration node can contain as many converter configurations and test generator configurations as required. All the referenced converters and test generators are included in the test generation process, with the options from the configuration file passed to them.
To launch test generation from a test generation configuration file, right-click the file, and then select Generate Test.
1.2.3.4. Defining a test-generation wizard
The Generate Test wizard contains four sections to guide you in defining a test generator.
The Generate Test wizard contains these sections:
- Test generator selection.
- Test files selection.
- Data correlation options.
- Test generator wizard.
The first page is displayed only if more than one test generator is applicable for the input recording session. The second page is displayed only if the user has not already chosen the test file before the recording.
You must declare a wizard for each test generator that you have defined. A test generator that has no declared wizard is not available in the user interface and can be launched only using a test generation configuration file, which is a file with the .testgenconfig extension, or programmatically.
When completed, this wizard produces a test-generation configuration containing a set of converter and test generator configurations. This configuration is applied by the test generation framework to instantiate the appropriate converters and test generators and to send the packet stream from the recording session to the converters and test generators.
Test-generator wizards can add pages to the Generate Test wizard by specifying an implementation class in the test generator wizard declaration in the plugin.xml file.
To define a test generator wizard:
- Declare a new test generator wizard in the plugin.xml file by using the com.ibm.rational.test.lt.testgen.ui.testgenWizard extension point.
- Specify the test generator ID that the wizard configures.
- Provide a label and icon that represents the type of test to be generated by the test generator.
- Optionally, provide an implementation class. If you do not provide a class, the wizard has no configuration page. If you provide a class, it must extend the com.ibm.rational.test.lt.testgen.ui.wizards.NewTestGeneratorWizard class.
Consider these facts about NewTestGeneratorWizard implementations:
- This class extends the JFace class wizard, so it must extend typical methods such as the addPages() method.
- The class is passed an empty test-generator configuration, typed with the test generator ID selected by the user in step 1.
- The class is responsible for setting the test generator configuration options, which are available by using the getTestGeneratorConfiguration() method. This method is typically used in the doPerformFinish() method.
- The wizard can get contextual information by using getContext() method.
1.2.3.5. Migrate test generator implementations from previous versions
The new test generation framework builds on the improvements that were made in the recording framework. These improvements include the ability to record several protocols at the same time, which in turn supports generating a single test with mixed protocols. The test generation framework also includes improvements in efficiency and scalability.
The test generation API has been completely redefined. You must refactor existing extension code to use the new framework. The following tables summarize the changes to classes and methods in the new test generation framework.
Previous class Current class Comments com.ibm.rational.test.lt.testgen.core2. IC2ProtocolHandler com.ibm.rational.test.lt.testgen.core.testgen. BaseTestGenerator The class no longer has to determine whether it supports a packet type. The framework sends to the test generator only packets that are declared to be supported by the test generator in the plugin.xml file. Previously, protocol handlers loaded messages, then processed them in the process() method. In the new framework, packets are passed one-by-one to the process() method. The process() method generates model elements without delay. The complete() method is available for any post processing.
Previous extension point Current extension point Comments com.ibm.rational.test.lt.testgen.core2.protocolHandler com.ibm.rational.test.lt.testgen.core3. testGenerator
1.2.4. Contributing annotations
An annotation is an action that a user performs during a recording session. Annotations are used to document or structure the test generated from the recording.
These annotation types come with the product:
- Insert comment
- Insert screen capture
- Start transaction
- Stop transaction
- Insert Synchronization Point
- Insert Split Point
- Set page name
Extensions can define additional annotation types. To contribute an annotation type:
- Define the new annotation type and its properties.
- Contribute an annotation toolbar with an action that enables inserting the annotation.
- Process the annotation in a test generator.
1.2.4.1. Defining a new annotation type
Each annotation type has its own semantics and supports a set of properties.
To define a new annotation type, complete these procedures:
- Declare a new annotation type in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.core.recorderAnnotation extension point and the annotationType element.
- Assign the type a unique ID. Typically the ID is in this form: pluginName.type.
- Assign a user-readable name to the type. The name is visible in the recording session editor.
For best results, complete these optional steps:
- Define an interface containing a string constant with the annotation type ID defined earlier.
- In this interface, include a string constant for each property name that the annotation type supports. Specify the meaning and the type of the property in the constant Javadoc information. Property types can be those that are supported by setters and getters of the com.ibm.rational.test.lt.recorder.core.property.AbstractConfiguration class.
Also, define a label provider for the annotation type. A label provider returns a dynamic label that is based on the annotation properties and an image. The label and the icon are visible in the recording session editor. To define a label provider, complete these procedures:
- Declare a new annotation label provider in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.ui.annotationContribution extension point and the annotationLabelProvider element.
- Specify the annotation type that the label provider supports.
- Define an implementation class of the label provider. The implementation class must implement the JFace ILabelProvider interface. Any object passed to this interface is always an instance of the com.ibm.rational.test.lt.recorder.core.annotations.RecorderAnnotation class.
1.2.4.2. Contributing new actions to the annotation toolbar
Typically, you produce an annotation by adding an action to the annotation toolbar. This action is represented as a toolbar button.
To contribute a new action to the annotation toolbar, complete these procedures:
- Declare a new annotation action in the plugin.xml file by using the com.ibm.rational.test.lt.recorder.ui.annotationContribution extension point and the annotationAction element.
- Assign a unique ID to the annotation action.
- Decide whether this action is visible by default or not. Actions that are visible by default are available in all recording sessions unless made unavailable by a client or recorder. Actions that are not visible by default are available only if a recorder or client requires the action.
- Define an implementation class for this action. The implementation class must extend the com.ibm.rational.test.lt.recorder.ui.actions.AbstractAnnotationAction abstract class.
Consider these facts about AbstractAnnotationAction implementations:
- This class extends the JFace Action class.
- In the constructor, set the name, tooltip text, and image descriptor of the action.
- Implement the run() method. This method can interact with the user. For example, this method can prompt the user for a text field. If there is any interaction with the user, the time in milliseconds spent interacting with the user must be measured.
- To create an annotation, create an instance of the com.ibm.rational.test.lt.recorder.core.annotations.RecorderAnnotation class, and then pass the annotation type as an argument. Set the annotation properties using the setter methods provided in this class.
- After the annotation has been built, forward the annotation by sending an AnnotationMessage message to the annotation recorder. This is typically a call of this form: annotationRecorder.sendMessage(new AnnotationMessage(annotation, interactionTime));
1.2.4.3. Generating a test-model element from a new annotation type
After annotations have been inserted into a recording session, the annotations must be processed by a test generator to translate them into a test-model element. To process annotations with a test generator, define a dedicated test generator or modify an existing test generator that you have developed.
To enable a test generator to process a new annotation type:
- Add the com.ibm.rational.test.lt.recorder.core.recorderAnnotation packet type to the list of supported packets of the test generator in the plugin.xml file. Set the isRequired attribute to false.
- In the process() method of the test generator, add code based on this template:
if (packet instanceof IRecorderAnnotationPacket) { IRecorderAnnotationPacket p = (IRecorderAnnotationPacket) packet; RecorderAnnotation annotation = getContext().resolveAnnotation(p); if (annotation == null) return true; if (MY_ANNOTATION_TYPE.equals(annotation.getType())) { // Add code to process the annotation return true; } }
1.2.4.4. Advanced annotation concepts
In some rare cases, annotations can be produced by recorders or clients.
Sending annotations from a recorder or a client
Annotations can be created by recorders and clients. To create annotations, the recorder delegate or the client delegate uses the AnnotationMessage class. The call typically is in this form:
getContext().dispatchMessage(new AnnotationMessage(annotation, interactionTime));
Modify the available annotation actions for a specific recorder or client
If an annotation action is available by default, you can choose to hide it when a specific recorder or client is active during the recording session. If an annotation is hidden by default, you can make it available when a specific recorder or client is active during the session.
To modify the annotation actions for specific recorders or clients, use the com.ibm.rational.test.lt.recorder.ui.annotationContribution extension point and the annotationActionFilter element.
Manage a state for a annotation types
By default, annotations can be inserted at any time during recording. There might be cases where permitting users to insert an annotation is inappropriate. For example, in a test with .Start Transaction. and .End Transaction. annotations, .End Transaction. might be available only if a transaction has already been started.
To manage the state of annotation types, define an annotation state handler. An annotation state handler manages one or more annotation types and can individually enable or disable annotation insertions. For more information about annotation state handlers, refer to the com.ibm.rational.test.lt.recorder.core.recorderAnnotation extension point, the annotationStateHandler element, and the com.ibm.rational.test.lt.recorder.core.extensibility.AnnotationStateHandler class.
The state handler for a specific annotation type can be retrieved from an annotation action class by using this code:
stateHandler = (IMyAnnotationStateHandler) getAnnotationStateHandler(myAnnotationType);
1.2.5. Extending the load test behavior model
The load test behavior model (LTBM) is a model of the behavior of a performance test in Rational Performance Tester.
The load test behavior model (LTBM) was developed using Rational Software Architect and Eclipse Modeling Framework (EMF) to model the behavior of a performance test. The LTBM also consists of code generated from this model using EMF. The LTBM is extended from the common behavior model (CBM). The schedule behavior model (SBM) that is the underlying model to the Schedule Editor in RPT is the other model that extends the CBM. The CBM is itself built on top of the Test and Performance Tools Platform (TPTP) common behavior model (TCBM). The LTBM plug-in ID is com.ibm.rational.test.lt.models.behavior.
For a sample of an extension to the LTBM, see the plug-in com.ibm.rational.test.lt.sdksamples.models.behavior.socket.
1.2.5.1. Updates to the load test behavior model
There are new features in the model and there are changes to the following two plug-ins, which the protocol behavior model plug-in depends on.
There are changes to the two plug-ins that the protocol behavior model plug-in depends on:
- com.ibm.rational.test.common.models.behavior
- com.ibm.rational.test.lt.models.behavior
com.ibm.rational.test.common.models.behavior.CBAssetMigration
To open a performance test in version 7.0 that was created in version 6.1.2, the test suite loader must identify model elements that have been modified in 7.0 and adapt to the changes. The suite loader accomplishes identification and adaptation by recognizing elements that implement the CBAssetMigration interface. If a model element has been modified in a given release, starting with that release, that particular model element must implement this interface. The two methods from this interface that need to be implemented are as follows:
- Public Boolean needMigration(CBVersion version): determines whether migration is needed
- Public void migrate(CBVersion version): performs any required migration
The version that is passed into this method is the version of the test. LTTestUtil.getCurrentVersion() always returns the current version of the test suite that can be created with the installed version of the product. The methods in the BehaviorUtil class enable you to compare any two version objects.
com.ibm.rational.test.common.models.behavior.CBElementHost
A model element can implement this interface provided that it is a container and that it has children in the model. Some examples of core model elements that implement this interface are CBLoop, LTTransaction, and LTTest.
com.ibm.rational.test.lt.models.behavior.common.LTAnnotation com.ibm.rational.test.lt.models.behavior.common.impl.LTAnnotationImpl
If a protocol model element is required to hold data that is not text, or that is large, the data being held could affect performance if it persists within the test suite model. To prevent performance issues, these types of data are stored in a file called annotation, which is in the test suite, but outside of the model. To use this feature, the attribute that holds the data must be created with type LTAnnotation. Use the APIs provided in this interface to access the data.
com.ibm.rational.test.lt.models.behavior.common.LTArmEnabled
A model element can implement this interface if this interface is required to log Application Resource Monitoring (ARM) data. ARM must be supported by the protocol being tested for this feature to work end-to-end.
Extensibility using RSA/EMF modeling
The model can now be extended by using IBM Rational Software Architect Eclipse Modeling Framework (EMF). A base starter model is provided along with the Load Test Behavior Model (LTBM) plug-in. You can start with this model and add the protocol extension model elements to the base model. When generating EMF code, choose the model element that is unique to the protocol model.
1.2.5.2. Extension points for LTBM
You must register your protocol model elements with the load test behavior model (LTBM) core through extension points regardless of the way that you chose to model your protocol.
1.2.5.2.1. Registering a model element
The extension point for registering a model element enables the protocol extension to specify a factory class, a class providing test options, and the type of the model element handled by the protocol extension.
The extension point for registering a model element allows the protocol extension to specify:
- A factory class containing instructions to create a model element of a given type. The factory class must implement the ElementFactory interface from the load test behavior model (LTBM).
- A class that provides test level options to the test for the protocol. This class should extend the OptionImpl class from the LTBM.
- A type of model element that is handled by this protocol extension. The element of this type should extend the CBBlockImpl class in the LTBM. The type defaults to the fully qualified name of the model element class. If there are duplicate element types, the element loaded later is ignored and a message is logged in the error log.
Sample
<extension point="com.ibm.rational.test.lt.models.behavior.protocol"> <protocol id="com.ibm.rational.test.lt.example.protocol" factory="com.ibm.rational.test.lt.example.protocol.ProtocolElementFactory" option=" com.ibm.rational.test.lt.example.protocol.ProtocolOptions"> <element type="com.ibm.rational.test.lt.example.protocol.XModelElement"/> <element type="com.ibm.rational.test.lt.example.protocol.YModelElement"/> </protocol> </extension>
1.2.5.2.2. Required attributes in a model class
A model element can contain attributes of different types, both primitive and complex.
A model element typically consists of one or more of the following types:
Attribute type Description Primitive, except byte or byte arrays
- Call the appropriate setProperty() method to set the property of the element. Various overrides exist for all primitive types except byte.
Byte array or large strings
- To store binary data, the attribute must be of LTAnnotation type or one that extends it. This class has setBytes() and getBytes() methods to store and retrieve binary information. After the getBytes() or setBytes() is called, the data is maintained in memory until the test is saved.
Note: If this action is not wanted.and it is not in cases where the data set can be large, you can flush the data to a file by individually calling the flush() method on the LTAnnotation. Make sure that you call this method immediately after a set or get is called.
Complex
- Call setProperty(CBActionElement,CBActionElement) to which you can pass the old and the new value of the attribute. All model elements extend from the CBActionElement class. The old value is passed so that it can be removed from the model and the new value is passed, so it can be set. If you do not have a value, then pass null. For example, if the value is changing from val1 to val2, then call the setProperty(val1,val2) method. If you are setting the value for the first time, call the setProperty(null,val1) method. To clear the value, call setProperty(val1,null).
- This is similar to the other setProperty() methods for the primitive type, with the added restriction that this needs the old value too.
- When the model gets loaded, the addReference() method will be called and this method needs to be overwritten to interpret and properly assign the attribute to the object. Call the appropriate setXXX() method to set that attribute with the value passed in.
List
- The attribute should be of type EList. To access the list, if the element is a container, then it can implement the LTElementHost interface and will need to implement the getElements() method. This method must return the EList. To load the list when the children of this model element are being loaded, the addReference() method is called for each child and the element must put the child in the right list.
Reference to another model element
- Define an attribute of the type of the element you reference. This will provide the simple getters and setters to this attribute. These getters or setters will not call getProperty or setProperty, but they will simply get or set the attribute value.
- Define another attribute that will act as a proxy for this element. This element is your own class that extends ProxyElement from the LTBM.
- When creating the reference, call the setHref() method of the ProxyElement with the ID (getId()) of the element that is being referenced.
- To get the element that is being referenced, use the href from the ProxyElement (getHref()) to locate the element in the test. You can also use the BehaviorUtil.findElement() method (the test containing this element, the ID of the element being searched) to get the element.
1.2.5.2.3. Registration examples
The examples in this topic show you how to register strings for content verification and built-in data sources.
Registering strings for content verification
The following example shows how to register strings for content verification:
<extension point="com.ibm.rational.test.lt.models.behavior.contentVPData"> <category label="Example protocol strings" id=" examples.protocol.strings "> <property type="boolean" name="Does value exist" value="true" id="com.ibm.rational.test.lt.exmaple.protocol.valueExist"/> <content label="Joe" id="example.protocol.strings.1"/> <content label="Jane" id="example.protocol.strings.2"/> </category> </extension>
Registering built-in data sources
A protocol extension can provide its own list of built-in data sources. A data source is used to mine data and hold it for later consumption. This is done using the following extension point.
The following example shows how to register built-in data sources:
<extension point="com.ibm.rational.test.lt.models.behavior.builtInDataSource"> <dataSource typeId="protocol.buitindatasource" className="com.ibm.rational.test.lt.example.protocol.BIDataSource"> <property name="database"/> <property name="table"/> <property name="column"/> </dataSource> </extension>In the above example, a protocol extension can extract a value from a particular column in a table in a database.
1.2.5.3. Create protocol constructs
The first step in creating protocol constructs is to identify the behavior of the protocol, how the ends, client and server, communicate through this protocol, or if there is an order in which things happen.
For example, in HTTP, the browser sends a request to the web server by using a connection to send the request and receives a response from the web server. This process suggests that at least the following model elements need to be present:
- An HTTP Request model element
- A Server Connection model element
- An HTTP Response model element
After you have the basic behavior defined, you can add additional features to the model elements.
1.2.5.3.1. Modeling the behavior of a protocol extension
To create a model element for your protocol, you must extend the com.ibm.rational.test.lt.models.behavior.impl.CBBlockImpl class. Extending this class enables you to use all the basic functionality that is provided by this model element, including persistence into the test files and APIs to set or get properties of a model element.
There are other model elements in the load test behavior model (LTBM) that are extended from the CBBlockImpl class and that provide various functionality. You could also extend those elements for your protocol. The LTBM provides common constructs (com.ibm.rational.test.lt.models.behavior.common) that can be reused by protocol extensions. These constructs are included in various packages in the LTBM. See the Javadoc information and the API topic for more details.
To create a protocol model element that is not related to any of the common constructs provided by the LTBM, extend the CBBlockImpl class. If the model element for your protocol is a specific type of an LTBM model element, extend the implementation class for that element. For example, if a protocol extension provides a special type of a CBLoop construct, it would extend the CBLoopImpl class and add additional attributes to that element.
- Optionally, create an interface that defines the methods for the class, including the setters and getters for the attributes added by this element.
- Create a new class that extends the CBBlockImpl interface, and if you created an interface in step one, implement the interface.
- In the constructor of the model element, call the method setType(type), where type is a unique type string denoting the model element as registered using the com.ibm.rational.test.lt.models.behavior.protocol extension point.
- Implement the getters and setters of attributes for this model element class.
Note: The setter must set the value attributes of the primitive data types into the underlying model using one of the overloaded setProperty() methods based on the type of attribute.
- To set attributes of complex types, override the addReference() method.
Results
While the test is loading, the getter (at least initially) gets the value of the attribute from the underlying model by using one of the overloaded getProperty() methods, based on the type of the property for primitive attributes.
You can define the model element to store the value of the attributes in local attributes. However, the guideline is that the getter should get it first from the underlying model and the setter should store it in the underlying model.
1.2.5.4. Extending the classes in LTBM
You can create a new Load Test Behavior Model (LTBM) element.
Follow these steps to create a new model element:
- Optionally create an interface that defines the methods for the class, including the setters and getters.
- Create a new class that extends CBBlockImpl interface and that optionally implements the interface defined in the above step.
- Implement the getters and setters of attributes for this model element class.
- The setter must set the value attributes of the primitive data types into the underlying model using one of the overloaded setProperty() methods.
- Override the addReference() method to set attributes of complex types.
- The getter, at least initially, gets the value of the attribute from the underlying model using one of the overloaded getProperty() methods based on the type of the property for primitive attributes.
- The model element can additionally decide to store the value of the attributes in local attributes. However, the underlying principle to be followed is that the get should get it first from the underlying model and the set should store it into the underlying model.
1.2.5.5. Public APIs for LTBM
The load test behavior model (LTBM) contains multiple packages. Each package contains the interfaces that define the LTBM. In addition, each package has a corresponding implementation package containing the implementation classes for these interfaces.
The extensions will either use or extend the implemented classes from LTBM while having access only to the methods defined in the corresponding interfaces. The following packages are part of LTBM:
- com.ibm.rational.test.lt.models.behavior.lttest
- com.ibm.rational.test.lt.models.behavior.common
- com.ibm.rational.test.lt.models.behavior.data
- com.ibm.rational.test.lt.models.behavior.vps.impl
The implementation classes are contained in the corresponding implementation packages, such as com.ibm.rational.test.lt.models.behavior.lttest.impl, com.ibm.rational.test.lt.models.behavior.common.impl, com.ibm.rational.test.lt.models.behavior.data.impl and com.ibm.rational.test.lt.models.behavior.vps.impl.
Each of these packages also contains factory classes that enable you to create a model element from that package. For best results, use the factory classes to create model elements. For example, to create a loop, use this method:LTTestFactory.eINSTANCE.createCBLoop()
The LTBM provides common constructs that can be reused by protocol extensions. These constructs are included in various packages in the LTBM. The Javadoc information explains each available interface in more detail.
1.2.5.5.1. com.ibm.rational.test.lt.models.behavior.lttest package
This package holds the interfaces related to the performance test object and its attributes.
The LTTest interface is the root container for all model elements. The LTTest interface also consists of model elements that are used to refer to outside assets, such as datapools, features, and external resources. An extension is not expected to implement or extend an LTTest interface.
An LTTest interface consists of the following items:
- An instance of LTResources containing features and other globally scoped items.
- A list of common and protocol based options.
- A list of datapools being used in this test, if any.
- A list of properties specific to this test.
- A list of model elements that might contain other model elements.
The following diagram shows the com.ibm.rational.test.lt.models.behavior.lttest package structure:
![]()
1.2.5.5.2. com.ibm.rational.test.lt.models.behavior.common package
This package contains common interfaces that the extending model elements can implement to add these features to them.
The com.ibm.rational.test.lt.models.behavior.common package also contains common constructs such as the IF construct. The package also contains other commonly used constructs such as LTNameValuePair, in which you can store a list of name or value pairs.
The following diagram shows the relationship between the model elements in the com.ibm.rational.test.lt.models.behavior.common package:
![]()
The following diagram shows the inheritance of model elements in this package:
![]()
1.2.5.5.3. com.ibm.rational.test.lt.models.behavior.data package
This package contains classes related to data correlation and custom code.
Data correlation consists of two parts. One part is the source for the data and the other is the consumer of the data. The base class for all data sources is called DataSource and the one for consumer is Substituter.
A model element in the protocol extension that can contain data sources must be marked as a data source host by implementing the DataSourceHost interface. A model element that can contain consumers of data sources must be marked as a consumer host by implementing the DataSourceConsumer interface.
The following diagram shows the relationship between the data source and the consumer:
![]()
Data sources
The data sources are model elements that designate data extraction and storage for consumption by other model elements. The following types of data sources are provided by the load test behavior model (LTBM):
For details about these classes, see the Javadoc information.
- DatapoolHarvester - to denote columns of data extracted from a datapool.
- CorrelationHarvester - to denote a string of data to be used later on in a test.
- BuiltInDataSources - built-in functions that provide derived information (eg. Current time).
- Arbitrary - enables users to write custom code to be inserted into a test.
A protocol extension can provide its own set of built-in data sources using the extension point described in the "Extension points" topic.
The following diagram shows the relationship between these classes:
![]()
Substituters
The basic consumer type provided is a Substituter. The following diagram shows the Substituter structure:
![]()
1.2.5.5.4. com.ibm.rational.test.lt.models.behavior.vps package
This package provides classes related to verification points.
The com.ibm.rational.test.lt.models.behavior.vps package contains the VerificationPoint interface and its corresponding implementation. This package provides the generic verification point VPContent. It is used to detect the presence (or indicate the absence) of one or more defined test strings that correlate with strings, or parts of strings, that the application generates while under test. You can define the strings that the verification point should look for.
1.2.6. Extending data correlation
Data correlation is the process of extracting data that is returned from a server, and then sending it back to the server in a subsequent request.
There are two main parts to data correlation. One part occurs during test generation and script editing, and the other part occurs during script execution.
The test generation part of data correlation is the most significant part. There is automatic data correlation that is performed during test generation, and there is manual correlation that can be performed during script editing. There are several different things that can be done as part of manual correlation: you can substitute values into a site from a data pool, a built-in data source, or a reference that is already created.
For more details about test generation extensibility, see the "Extending the test generator" topic. For details about the Script class, see the "Extending code generation" topic.
For examples of data correlation extensions, see these plug-ins: com.ibm.rational.test.lt.sdksamples.datacorrelation.testgen.socket com.ibm.rational.test.lt.sdksamples.datacorrelation.execution.socket
1.2.6.1. Implementing data correlation for test generation
To implement data correlation for test generation, become familiar with these classes: com.ibm.rational.test.lt.datacorrelation.testgen.proto.IProtoElementAdapter, com.ibm.rational.test.lt.datacorrelation.testgen.DataCorrelator, and com.ibm.rational.test.lt.datacorrelation.testgen.IDCStringLocator.
To implement data correlation for test generation and script editing, your model elements must extend com.ibm.rational.test.lt.models.behavior.data.DataSource and com.ibm.rational.test.lt.models.behavior.data.Substituter so that you can associate data sources and substituters with your model elements. Also, when you extend those classes, make sure you implement the code to make Substituters and DataSources persist in your model. For more information, refer to the model documentation.
You need to create your own data correlation plug-in to extend the extension point DCTestgenProto, which is defined in the plug-in com.ibm.rational.test.lt.datacorrelation.testgen. To extend the extension point, your code must implement the interface IProtoElementAdapter which is defined in com.ibm.rational.test.lt.datacorrelation.testgen.proto. Then, when you extend the DCTestgenProto extension point, use the following items:
.
- ID - This is the unique ID of your extension.
- Name - The name that is meaningful for you.
- Point - com.ibm.rational.test.lt.datacorrelation.testgen.DCTestgenProto extension point
The other element details are:
- class - This is the full name of the class (including plug-in name) that implements IProtoElementAdapter.
- protoType - The type of model element that this plug-in handles, for example, in HTTP, the model element that is handled is com.ibm.rational.test.lt.models.behavior.http.HTTPRequest.
- generic - This is a boolean that can be true or false. If it is true, this means that if there are other plug-ins that handle the same protoType, this plug-in will be called last.
- uniqueID - This ID is unique among all data correlators.
After you have extended the data correlation extension point, the main data correlation engine will call your code with all the elements of the model type that you defined.
There are two parts to data correlation at testgen time. The first part is automatic data correlation which happens at testgen time. This will happen through a call to DataCorrelator.CorrelateAll(). When this method is called automatically by testgen, it will in turn call two main methods in your plugin. Those methods are IProtoAdapter.findSubs() and IProtoAdapter.findReference(). The findSubs() method should return a list of substituters that are found for the current element that is passed into your method, and null if there were no substituters found. The findReference() method should try to find a reference for the substituter passed into it. You start looking for references at the element before the substituter and keep working backwards in the testsuite until you find a suitable reference. If no reference is found, return null. CorrelateAll is used to find all correlations that you can so that the user does not have to manually find them at test editing time.
The other parts of the IProtoAdapter class are called at test editing time when a user wants to create a substitution site, a reference, or make a correlation between a substitution site and a reference. If you are creating your own type of Substituters or DataSources, your makeDataSource() and makeSub() methods will be used for that. You will be passed the DCStringLocator class with all the information about offsets and text that you need. Use this DCStringLocator to determine what substituter or data source you want to create and then call back into IDataCorrelator. Correlations, substituters, and data sources are created in the model for you. Read the documentation on the IDataCorrelator class to learn which methods are right for your purposes.
1.2.6.2. Implementing data correlation for execution
To implement data correlation during execution, set up your class that extends the com.ibm.rational.test.lt.kernel.action.KAction class to trigger a call into the data correlation engine.
Before you implement data correlation, become familiar with the following classes:
See the Javadoc information for class and method descriptions.
- com.ibm.rational.test.lt.datacorrelation.execution.harvest.IDataHarvester
- com.ibm.rational.test.lt.datacorrelation.execution.sub.IDataSub
- IDataCorrelationVar
- com.ibm.rational.test.lt.datacorrelation.execution.proto.IProtoActionAdapter
The IKAction interface defines the basic functionality that all kernel actions must implement. The IKAction interface is the base interface for all kernel actions. The actions can represent loops, conditions, or other code constructs.
To implement data correlation during execution , codegen writes your harvesters and substituters into containers. To implement data correlation at execution time, codegen will need to write your harvesters and substituters into containers. This is done by calling LTTestTranslator.translateHarvesterContainer() and LTTestTranslator.translateSubstituterContainer() at codegen time when you are translating your action. Both of these methods live in the com.ibm.rational.test.lt.codegen.core plugin. These containers will need to be added to your action and stored with your action. These containers must be added to and stored with your action. When your actions are being executed, they must call these substituter and datasource containers. The substituter container is called at the beginning of your action, before you send data, and the data source container is called at the end of your action, after you have received the data.
To implement the execution portion of data correlation:
- You must extend an extension point in codegen.core to get your execution IProtoActionAdapter regisetered for playback:
- Extend the extension point DataCorrelationProtoAdapter in the com.ibm.rational.test.lt.codegen.core package. To do the extension, use your plug-in that is the extension to the com.ibm.rational.test.lt.datacorrelation.execution plug-in that implements the IProtoActionAdapter interface.
- Tell the com.ibm.rational.test.lt.datacorrelation.execution plug-in what types of IKActions to handle and the name of your plug-in.
- When this is complete, you should see something similar to the following added near the top of your generated .java code:
pa.addPA("com.ibm.rational.test.lt.sdksamples.datacorrelation.execution.socket.SocketActionAdapter", "com.ibm.rational.test.lt.sdksamples.protocol.socket.io.SocketSend"); The first string should be the name of your class that implements the IProtoActionAdapter, and the second string should be the name of the KAction that your IProtoActionAdapter class should be called for. Be very careful on the spelling, it has to be exactly right.
- To implement the datacorrelation.execution plug-in extension, your plug-in must implement the com.ibm.rational.test.lt.datacorrelation.execution.IProtoActionAdapter interface. The main com.ibm.rational.test.lt.datacorrelation.execution plug-in uses the interface to call your plug-in and to get the information for the implementation.
Note: This is also the plug-in that extends the DataCorrelationProtoAdapter class in the codegen extension point.
- To start the substitution process, in the IDataSub.substituteData() method, use the action and hash map as parameters.
- The action that is currently active will call the com.ibm.rational.test.lt.datacorrelation.execution.sub.IDataSub.substituteData() method. The substitution container executes all substitution rules and put the new string values (read from data correlation variables) into the hashMap. The first value of the hashMap is the propertyType, and the second value is the entire new string for that propertyType. So, when the substitution is done, it fixes the entire string for you.
- The substitute container returns the control to the IKAction interface.
- The IKAction interface reads the string values from the hash map and sends them to the appropriate places.
- To start data harvesting, the action that is currently active calls the IDataHarvester.harvestData() method. The action passes itself to the container.
- The data harvester calls the plug-in that has extended the data correlation execution plug-in to get the string values that the harvest rules must be applied to.
- The data harvester container places the harvested values into data correlation variables. These data correlation variables are then used in later substitute executions.
1.2.7. Extending the test editor
The performance testing software provides application programming interface (API) classes for extending the test and performance tools platform (TPTP) to write new editors and protocol extensions.
The editors in performance testing are extensions of the defined editor framework in TPTP. The editors are loaded by TPTP based on the file type, for example schedule, test, or data pool.
The performance testing editor is built of several layers: the common framework, the editor, and the protocol layers.
- The common framework layer consists mainly of abstract classes and interfaces, and some utility libraries. The common framework is initialized by TPTP editor architecture.
- The editor layer extends classes from the common framework layer to provide specific implementations for their models.
- The protocol layers can be basic and dependent. The basic layers do not depend on other protocols such as HTTP. The dependent layers are built on top of other protocols, for example Siebel on top of HTTP. The protocol layers are implemented on top of the single editor layer.
When the editor needs to be opened in a model file, TPTP determines which editor is capable of handling this particular model and loads, and initializes the specific extension. The common framework layer provides its implementation of the handler class, the com.ibm.rational.common.test.editor.framework.extensions.CommonEditorExtension class. The editor layer creates an instance of the com.ibm.rational.common.test.editor.framework.TestEditor class that must be extended by the editor layer. After the TestEditor object is created, the user interface (UI) widgets and components are created to display the model data.
The TestEditor class provides the bridge between TPTP (through the CommonEditorExtension), the model (through the CBTest member variable), Eclipse, and the concrete protocol code. When the editor is closed by the user or reloaded, the first instance of the TestEditor class is destroyed and a new one is created.
The editors support only data that comes out of the corresponding models. The editor layer of the performance testing editor is called the Load Test class. The Load Test class extends the TestEditor class by creating concrete LoadTestEditor and CommonEditorExtension classes through the LoadTestEditorExtension extension point.
For the recorder extension sample, see the plug-in com.ibm.rational.test.lt.sdksamples.editor.socket.
1.2.7.1. Migrate test editor extensibility
Changes in the performance testing editor base framework package are provided in this release to support more standardized ways of handling model element attributes.
Details area contents and layout (attribute field support)
You can now manage the layout and contents of the Details area in the test editor (schedule and test editors) that developers had to manage in earlier versions. In this version, you can manipulate the contents by using the abstraction called AttributeField. This construct hides much of the required behavior from developers, and enables a great deal of extensibility. Updates to AttributeField can coexist with the user interfaces of existing editors or protocol extensions, provided that some minor adjustments are made. Classes derived from AttributeField must be used whenever an attribute from a model element is displayed. Do not use the AttributeField for any other information presented to the user in the Details area. Instead, use regular Eclipse widgets.
Behavior
The getXXXvalue(), setXXXValue(), and getFieldName() classes and APIs provide a way to link model data with their user-interface representation, and at the same time hide related low-level maintenance work from the developer.
These classes are abstract. When a developer chooses one of them to display their model data, only a small number of methods need to be implemented to define the default behavior of the attribute field. If more customized behavior is needed, other methods are available for overriding. Refer to the Javadoc HTML documentation for more information.
The smallest set of methods that a developer must implement are as follows:
- getXXXValue(): Retrieves and returns a value from model element. XXX signifies a type of an attribute. For example, when extending IntegerAttributeField, the name of the method is getIntegerValue().
- setXXXValue(): Sends a value obtained from the UI to the model element. The meaning of XXX is the same as above.
- getFieldName(): Returns the name of the field. Names make a field addressable for navigation.
Note: Before version 7.0, field names were optional; in this version they are mandatory.
Classes
The following table is a hierarchy of the AttributeField related classes that are available to editor and protocol developers. The list includes descriptions about which classes to use in specific situations.
Note: These classes have limited functionality because they must support classes and APIs from versions earlier than 7.0.
AttributeField class Description OptionsComboField This class is used to display a set of options to a user. Options are presented in a combination box. When extending this class, you must provide an index of the option currently selected in the model element. When a user chooses a different value from the combination box, the new index is passed to the derived class, in order to update the model. The class developer needs to understand the meaning of the index in the context of the model. OptionsRadioField The same provision that applies to OptionsComboField applies to OptionsRadioField. The following exception, however, applies: options are displayed as a set of radio buttons in a group. A user must select one of the radio buttons to indicate the index of the selected option. BooleanAttributeField This class is used when the model element attribute is a Boolean value. The value is displayed as a check box. The developer must provide a Boolean value from a model element and accept a new Boolean value from the user interface to update the model element. IntegerAttributeField This class is used when the model element attribute holds an integer value. The field can represent an integer value in several ways. The following control types are available for representation:
- StyledText
- Spinner
- Slider
- Scale
Note: This class is subject to change in the future.
TextAttributeField Use this field when there is text data in the model element. FilteredTextAttibuteField This class extends the behavior of TextAttributeField by enabling condition checking and displaying alternative text (message) to the user. For example, the developer might want to filter binary data, or filter text that is too long for convenient display. DataCorrelatingTextAttrField Use this class when the text data can be either datapooled, data correlated, used as a reference, or configured in any combination of these.
Porting code from layoutProvider
The following is a short guide to porting existing code, typically found in the layoutProvider class, to the new function.
Previous implementation:
class MyLayoutProvider extends ExtlayoutProvider { layoutControls( CBActionElement element ) { super.layoutControls( element ); // call super first. createWidgets(); // create all the UI for display refreshWidgets(); // call refreshLayout to populate UI return true; // return true is success. } createWidgets() { new StyledText(); new Button(); } refreshControls( CBActionElement element ) { super.refreshControls( element ); // call super first // grab data from model element and apply it to UI widgets applyModelDataToWidgets(); return true; // return true if success. } /* because the ExtLayoutProvider is SelectionListener, this method is called when Buttons, ComboBoxes and such are modified. */ widgetSelected( SelectionEvent event ) { // find the widget, get its value and apply it to model applyUiDataToModelElement(); // call super to update the editor. super.widgetSelected(); } /* because the ExtLayoutProvider is ModifyListener, this method is called when StyledText is modified. */ modifyText( ModifyEvent event ) { // find relevant StyledText control and apply // its value to the model element. applyTextUiDataToModelElement(); super.modifyText(); } }Current implementation:
class MyLayoutProvider extends ExtlayoutProvider { // class declared as internal. class MyTextField extends TextFieldAttribute { String getTextValue(){ return ((MyModelElement)getSelection()).getTextAttr(); } setTextValue( String newVal ){ ((MyModelElement)getSelection()).setTextAttr( newVal ); } String getFieldName(){ return MY_FIELD_NAME; // defined elsewhere } }; MyTextField m_fldText; MyDataCorrelationField m_DcField; // declared outside. layoutControls( CBActionElement element ) { createWidgetsAndFields(); // create all the UI for display updateNonFieldWidgets(); // update non-model widgets // always call super at the end. return super.layoutControls( element ); } createWidgetsAndFields() { // create UI widgets for displaying non-model info . // create Fields m_fldText = new MyTextField( this ); m_fldText.createLabel( . ); m_fldText.createControl( .. ); // create more UI widgets for displaying non-model info m_DcField = new MyDataCorrelationField( this ); m_DcField.createLabel( . ); m_DcField.createControl( .. ); } refreshControls( CBActionElement element ) { // update NON-UI widgets only. applyModelDataToWidgets(); //always call super at the end. return super.refreshControls( element ); } /*You do not have to have this method unless you want to update NON-model widgets/data. */ widgetSelected( SelectionEvent event ) { // find the widget and do whatever you need, but // do not update the model. applyUiDataTo_NON_ModelElement(); // DO NOT call super to update the editor. } /* You do not need to have this method unless you want to update non-model widgets/data. */ modifyText( ModifyEvent event ) { // find the widget and do whatever you need, but do not // update the model. applyTextUiDataToModelElement(); // DO NOT call super to update the editor. } }
1.2.7.2. Test editor structure
The editor in Rational Performance Tester is built of several layers; the common framework, the editor and the protocol layer.
The common layer defines the interfaces and extension-points, provides the API classes and interfaces. It also provides the hooks into the Eclipse user interface (UI) menus, actions, markers. The common framework layer is initialized by the TPTP editor architecture. Because RPT editors are in fact extensions loaded and initialized by the TPTP platform via org.eclipse.hyades.ui.editorExtensions extension point, one of the common editor framework.s responsibilities is to provide hooks and application programming interfaces (APIs) for concrete editors implementations to communicate with TPTP. The common editor framework defines the classes that must be extended for a more specific behavior to be used by the extended editor implementations. This extension is realized by the TestEditor class that is extended by the Load Test Editor plugin and called LoadTestEditor.
The editor layer extends classes from the common framework layer to provide specific implementations for their models. The protocol layers can be basic and dependent. The basic protocols do not depend on other protocols, such as HTTP. The dependent protocols are built on top of other protocols, for example Siebel on top of HTTP. The protocol layers are implemented on top of the single editor layer. The protocol layers provide handlers for protocol-specific objects.
When the editor needs to be opened on a model file, Hyades determines which editor is capable of handling this particular model and loads and initializes specific extension. The common layer provides its implementation of the handler class, the CommonEditorExtension class. The common editor layer creates an instance of the TestEditor class that must be extended by the editor layer. After the TestEditor object is created, the user interface (UI) widgets and components are created to display the model data.
The TestEditor provides the bridge between TPTP, the model, Eclipse, and the concrete protocol code. The CommonEditorExtension class is used. When the editor is closed by the user or reloaded, the first instance of the TestEditor is destroyed and a new one is created.
The layers described above are split into separate plugins. Each plugin defines some extension points which are used by the higher-level plugins as well as by the defining plugins themselves. Additionally, there are some Java. interfaces that must be used when writing classes.
The plugins are:
- com.ibm.rational.test.common.editor.framework
- com.ibm.rational.test.lt.testeditor
- com.ibm.rational.test.lt.http.editor
- com.ibm.rational.test.lt.http.siebel
1.2.7.3. Common editor framework
Extend performance test editing in the common editor framework. It contains the classes to extend for specific behaviors that will be used by the extended editor implementations.
Because performance testing editors are extensions loaded and initialized by the TPTP platform through the org.eclipse.hyades.ui.editorExtensions extension point, one of the common editor framework functions is to provide hooks and application programming interfaces (APIs) for concrete editor implementations to communicate with TPTP. The common editor framework defines the classes to be used by higher editor implementations. One of the most widely used classes is the TestEditor class, which is extended by the com.ibm.rational.test.lt.testeditor.main.LoadTestEditor plug-in.
The layoutProvider, labelProvider, contentProvider and actionHandler extension points have been deprecated and are superseded by the modelObjectDescriptor extension point that combines and enhances them. The following table lists the active extension points that you can use to extend the common editor framework:
Extension point Description modelObjectDescriptor Type of the object, the test editor, and the general name and icon for the object. testOptions Used to contribute user interface (UI) elements for displaying the protocol options in the Details area of the root element in the tree, Performance Test or Schedule. editorAddonEnabler Used to write contributions to existing user interface (UI). It can have enabled and displayed state. Works with the AddonReader class. searchTypeProvider Contributes a search category to the performance testing Search window. The category consists of object type and, optionally, search parameters. searchTypeOptionsContibutor Contributes extra search parameters to some other search type category. preferencePageContributor Provides a way to create extensible preference pages as well as to contribute UI to other preference pages. For preference pages, use the Eclipse extension point and extend the class com.ibm.rational.common.test.editor.framework.kernel.EditorPreferenecePage. For page contributions, use this extension point and extend the com.ibm.rational.common.test.editor.framework.kernel.TestPreferenceContributor class.
1.2.7.3.1. The modelObjectDescriptor extension point
The modelObjectDescriptor extension point combines and enhances the deprecated contentProvider, labelProvider, layoutProvider, and actionHandler extension points.
The modelObjectDescriptor extension point has four attributes. The attributes specify the type of the object, the type of the test editor where the object is used, and the general name and icon for the object. The name should not have any formatting characters in it. The name with the icon is used primarily for reporting purposes.
Attribute Description type Represents the type of the model object, returned by the CBActionElement.getType() method. model_type The type of the model or test. The same value returned by your CBTest.getType() method. For performance testing, protocol extensions use com.ibm.rational.test.lt.lttest. icon The image that represents your object. label The name of your object. The modelObjectDescriptor extension point has four child definitions, as described in the following table:
Child Attributes description labelProvider. See com.ibm.rational.common.test.editor.framework.extensions. ExtLabelProvider class.
- treeLabel . The text to be displayed in the tree in the Main section of the editor. This text can be static or contain formatting. It is up to the implementing class to format and return proper textual representation of an element.
- statusLine . The text to be displayed on the status line when the object is selected in the Main section tree of the editor. If omitted, the value of the treeLabel attribute will be used for this purpose.
- tooltip . The text to be displayed in the tooltip (when appropriate). If omitted, the value of the treeLabel will be used for this purpose.
- menuText . The text to be displayed in a pop-up menu, such as Add or Insert. If omitted, the value of the modelObjectDescriptor label will be used for this purpose.
- description . The longer description of the model element. If omitted, the value of the modelObjectDescriptor label will be used for this purpose.
- icon . The image that represents the model element. May be same or different as the one specified in the modelObjectDescriptor icon.
- class . The instance of this class will be created when an object is set up to be referenced in the editor. There is a default base class to be used for this purpose, called ExtLabelProvider. It provides methods that return information, specified in the extension. All of its methods can be overridden by the extending class to provide appropriate formatting.
layoutProvider. See com.ibm.rational.common.test.editor.framework.extensions. ExtLayoutProvider class.
- class . The instance of this class will be created when the object details are displayed in the editor.s Details section. A model element must have a layoutProvider class if this object is displayed in the Main section tree. There is a default base class to be used for this purpose, called ExtLayoutProvider. It provides methods for constructing, formatting, and refreshing Details section for the model element. The methods layoutControls and refreshControls must be overridden by the extending class. The com.ibm.rational.common.test.editor.framework.extensions.ExtLayoutProvider class implements the SelectionListener and ModifyListener interfaces, so it can be used to listen to such events generated by your controls.
contentProvider. See ExtContentProvider class.
- class . The instance of this class will be created when the object hierarchy information needs to be discovered. A model element must have a contentProvider class if the object is displayed in the Main section tree. There is a default base class to be used for this purpose, called ExtContentProvider. It provides methods for discovering information about object children and parents. See the Eclipse IStructuredContentProvider interface for more details. In many cases there is no need to override any of the ExtContentProvider methods, but if some non-standard processing must be done, the first method to override is getChildrenAsList(), as it is called by other methods in this class.
actionHandler. See the ExtActionHandler class.
- class . The instance of this class will be created when a new object of this type needs to be created or when the existing object needs to be removed from model or moved up or down. Your class must extend ExtActionHandler.
1.2.7.3.2. The testOptions extension point
The testOptions extension point is used to contribute user interface elements for displaying options for a protocol in the Details area of the root element in the tree, Performance Test or Schedule. Each contribution is displayed in a separate tab in a Tab folder.
The testOptions extension point has the following attributes:
Attribute Description label The text to be displayed in the tab title. toolTip The text to be displayed in the tab tooltip. image The image to be displayed in the tab title. feature_id The feature to which these options pertain, currently not filtered. order The numeric order of the tab, used to sort tabs. If no number is given or there are duplicate numbers, the label will be used for sorting. class The class that will be instantiated to display and handle options. The default implementation base class, DefaultOptionsHandler , provides convenience methods. This class is abstract, which means that it needs to be extended to provide concrete user interface elements. The default implementation of the ExtLayoutProvider class for Test objects, DefaultTestLayoutProvider, loads and displays relevant options declared through this extension point.
1.2.7.4. Contributing actions to the menu
You can use extensions to extend the menu, change navigation, and perform data correlation.
1.2.7.4.1. Test editor menus
The test editor has three menus. You can extend the menus by adding more actions such as remove, move up, or move down
The first menu is displayed when the user right-clicks inside the left side of the tree. The other two menus are displayed when the user clicks the Add and Insert buttons. The Add or Insert menus are also displayed as submenus in the tree menu.
The IDs of the menus and named group separators are listed in the following table:
ID Group separator defined in ITestEditorActionIDs org.eclipse.hyades.test.ui.editor.TestSuiteEditorPart.tree.menu
- additions.new-start
- additions.new-end
- additions.edit-start
- additions.edit-end
- additions.find-start
- additions.find-end
- IWorkbenchActionConstants.MB_ADDITIONS
org.eclipse.hyades.test.ui.editor.TestSuiteEditorPart.action_add
- IWorkbenchActionConstants.MB_ADDITIONS
org.eclipse.hyades.test.ui.editor.TestSuiteEditorPart.action_insert
- IWorkbenchActionConstants.MB_ADDITIONS
1.2.7.4.2. Create actions
You can use the com.ibm.rational.common.test.editor.framework.extensions.ExtActionHandler class to remove, move up, and move down menu buttons
The editor or protocol extensions that are needed to manipulate the test, and the test add, remove, insert specific model elements, must use the NewModelElementAction class as described in the following conditions:
- For every model element displayed in the tree, the com.ibm.rational.common.test.editor.framework.Test Editor class expects to find an instance of the com.ibm.rational.common.test.editor.framework.extensions.ExtActionHandler class and some com.ibm.rational.common.test.editor.framework.kernel.actions.NewModelElementActions registered in com.ibm.rational.common.test.editor.framework. TestEditorplug-in.s com.ibm.rational.common.test.editor.framework.RptMenuManager.
- The ActionHandlers are declared in the plugin.xml file, while the com.ibm.rational.common.test.editor.framework.kernel.actions.NewModelElementAction class must be created and registered programmatically through the protocol plug-in class. Only one set of actions is needed for multiple editors, because this class is context sensitive.
- The ActionHandlers of the selected elements are asked whether to enable the Remove, Move up and Move down buttons . The com.ibm.rational.common.test.editor.framework.extensions.ExtActionHandler examines the selection, looks for the objects of the recognized types, and then returns a value of true or false.
- To enable or disable Add and Insert buttons and menus, the TestEditor class passes the selection to each of the registered com.ibm.rational.common.test.editor.framework.kernel.actions.NewModelElementAction instances. The action is expected to examine the selection, and enable or disable its state, based on whether this action can add its model object to the selected item.
- The ID of each com.ibm.rational.common.test.editor.framework.kernel.actions.NewModelElementAction or the derived type must match the type of the model object that this action represents. The ID must have the same value as the one used in the modelObjectDescriptor extension point. The com.ibm.rational.common.test.editor.framework.Test Editor class uses this value to locate various providers for the specific model element.
- There are two separate sets of actions maintained by the com.ibm.rational.common.test.editor.framework.RptMenuManager class, the Add and the Insert actions. While objects of the same type can be registered for both, the same instances of the same class cannot. This means that two instances of the AddObject action must be created and registered. If the object cannot be inserted, the insert action is not required. The same is true for the add action.
- If an action is selected, the action calls its correspondent ActionHandler to create a new model object. At this point, the ActionHandler class must create and initialize a new model object, including any required children. The action will add the new model object to the selected parent, but only if the selected parent ContentProvider returns a list of the children that have EList type, the native model list. For a composite list of children, for example the ArrayList assembled by provider, the ActionHandler is expected to add a new child to the parent.
- The protocol writers must use the LoadTestNewModelElementAction class as the base class for creating actions. The class supports filtering by feature.
1.2.7.5. Editor layer extension points
The editor layer enables you to write protocol extensions. During the initialization process, a number of extension points are checked for extensions that define different aspects of the test editor.
The editor layer extends classes from the common framework layer to provide specific implementations for their models. The extensions must be defined in the protocol-specific plug-ins extending the test editor.
Extension point Description dataCorrelationHandler
- Defines an entry in the Data Correlation Handler Selection window. The handlers are queried and displayed in a dialog for the user to choose when there is more than one protocol capable of creating data correlation objects out of the selected text.
- The user interface descriptor of a handler is matched to its data correlation package through the typeID attribute in the extension point.
dataCorrelationUICategory
- Defines a category for grouping built-in data sources that are represented in the user interface by the dataCorrelationUIDescriptor extension point.
dataCorrelatorUIDescriptor
- Provides visual representation for built-in data source types. These are grouped under data correlation user interface categories in the Built-in Datasource Selection Wizard. The wizard is displayed when the user wants to create data correlation with a built-in data source.
wizardPageContributor
- Used for contributing a protocol specific page or pages to the New RPT Test Wizard.
The protocol handler can make a contribution to the details page of the test to protocol-specific test-wide options. If your protocol needs to support data correlation, you can use the class DataCorrelationLabelProvider. The class contains several methods to use with data correlation. Many of the methods deal with display aspects of the data correlation objects.
In data correlation there must be an attribute name assigned to every field or property of a model object that supports data correlation. These names are used for many purposes, one of which is to provide formatted labels for data correlation objects. Because the LoadTest editor creates labels early in a process cycle, these labels may not contain detailed formatting instructions. To overcome this issue, higher level protocol extensions or plug-ins can register LabelFormatters that are called when needed.
1.2.7.6. API classes
The following classes and methods can be used to extend the common editor framework:
Class Methods com.ibm.rational.common.test.editor.framework.extensions.ExtLabelProvider
.
- getTestEditor() - Returns the TestEditor object, the LoadTestEditor in the Performance Test Editor
com.ibm.rational.common.test.editor.framework.extensions.ExtLayoutProvider
- getTestEditor() - Returns the TestEditor object.
- getFactory() - Returns the WidgetFactory for creating widgets and controls.
- getDetails() - Returns Composite which is a parent for creating controls.
1.2.8. Contributing error handlers
An error handler is associated with an error condition. Error handlers specify the action to take when a specified error condition occurs. Error handlers are provided for conditions such as verification-point failures, connection failures, server timeouts, and data-correlation problems. You can add new types of errors and error handlers.
1.2.8.1. Defining the user interface for an error handler
After you create a new type of error, you must define the user interface for the error handler. If you do not define a user interface, the error handler is not available in the test editor or schedule editor.
To define the user interface for a new error handler, use the com.ibm.rational.test.common.editor.framework.exceptionDefinition extension point.
The creator class can implement the com.ibm.rational.common.test.editor.framework.extensions.IExceptionCreator class or extend the com.ibm.rational.common.test.editor.framework.extensions.DefaultExceptionCreator class. The com.ibm.rational.common.test.editor.framework.extensions.DefaultExceptionCreator class provides a default implementation of the IExceptionCreator element, which uses the isMyType(CBErrorType exceptionType) method.
The following is an example definition using HTTP:
<extension point="com.ibm.rational.test.common.editor.framework.exceptionDefinition"> <exceptionTypeDefinition creatorClass="com.ibm.rational.test.lt.http.editor.PageTitleErrorExceptionCreator" defaultBehavior="0" defaultMessage="%PageTitle.Vp.Failed" forFeature="com.ibm.rational.test.lt.feature.http" labelProvider="com.ibm.rational.test.lt.http.editor.ui.exceptions.PageTitleVpErrorLabelProvider"> </exceptionTypeDefinition> <exceptionTypeDefinition creatorClass="com.ibm.rational.test.lt.http.editor.ResponseCodeVpErrorCreator" defaultBehavior="0" defaultMessage="%Resp.Code.Vp.Failed" forFeature="com.ibm.rational.test.lt.feature.http" labelProvider="com.ibm.rational.test.lt.http.editor.ui.exceptions.ResponseCodeVpErrorLabelProvider"> </exceptionTypeDefinition> <exceptionTypeDefinition creatorClass="com.ibm.rational.test.lt.http.editor.ResponseSizeVpErrorCreator" defaultBehavior="0" defaultMessage="%Resp.Size.Vp.Failed" forFeature="com.ibm.rational.test.lt.feature.http" labelProvider="com.ibm.rational.test.lt.http.editor.ui.exceptions.RespSizeVpErrorLabelProvider"> </exceptionTypeDefinition> </extension>You must specify the feature ID when you define the user interface for an error handler. By specifying the feature ID, the test editor can find the new types of errors and to filter out error types that are not applicable in multiprotocol tests.
1.2.8.2. Controlling how available error handlers are displayed
You can control how custom error handlers are displayed in the test and schedule editors.
For any test element, all core errors and all applicable protocol-specific errors are displayed automatically in the Details section of the Advanced page in the test editor, if the following conditions are satisfied:
These conditions are tested in the createExceptionsUi method in the ExtLayoutProvider class. Typically, you do not modify the default behavior for a test element, but it is possible to override the createExceptionsUi method. If the createExceptionsUi method returns null, no user interface for error handling is displayed on the Advanced page in the test editor.
- The element implements the CBErrorHost interface.
- The canHostErrors method returns true.
- The isErrorGenerator method returns false.
The ExceptionsUI class controls the user interface for error-handling on the Advanced page. The test-specific implementation is the TestExceptionsUI class. The schedule-specific implementation is the ScheduleExceptionsUI class. A shortcut way to create these classes is to call the createExceptionsUi method in the TestEditor class.
Note: Container objects in schedules do not display any user interface for error handling if there are no tests inside the containers.
1.2.8.3. Controlling how error handlers for specific elements are displayed
You can control how error handlers for specified test elements are displayed.
In the test editor, users can configure error handling for a specific model element that produces an error. For example, if a specific instance of a content verification point fails, the user can change the error-handling behavior for only that instance of the content verification point. To change how the error handlers for protocol-specific elements are displayed, you must know what types of errors can be generated for the element when tests run.
The test editor SDK provides classes for the display of the error-handling user interface. The com.ibm.rational.test.lt.testeditor.main.exceptions.TestExceptionProducerUI class controls the error-handling user interface in the Details section of the Advanced page. The com.ibm.rational.test.lt.testeditor.main.exceptions.DialogExceptionProducerUI class controls the error-handling user interface in dialog boxes and properties pages.
The following code is an example for the Details section of the Advanced page in the test editor. In the layoutControls method, add code similar to these lines:
m_exceptionUi = new TestExceptionProducerUI( getTestEditor(), new AuthenticationExceptionCreator()); m_exceptionUi.createErrorProducerContents( getDetails(), getNtlm(), (LoadTestWidgetFactory) getFactory());The AuthenticationExceptionCreator class is the same class that is specified in the plugin.xml file in the exceptionDefinition extension point. In the refreshControls method, add code similar to this line:
m_exceptionUi.refresh( getNtlm() );The following code is an example for dialog boxes and properties pages. In the createContents method or the createDialogArea method, add code similar to these lines:
m_errorHandling = new DialogExceptionProducerUI( m_page.getEditor(), new PageTitleErrorExceptionCreator()); m_errorHandling.createErrorProducerContents( parent, vp );In the onCancelPressed method, which is called when the user clicks Cancel in a dialog box, add code similar to this line:
m_errorHandling.cleanUp();When the user clicks OK in a dialog box or property page, the changes are applied to the CBError object immediately, so no extra code is required. When the user clicks Cancel in a dialog box or property page, the changes must be undone using the cleanUp method.
To receive notifications when changes are made to a CBError object, override the updateEditor method by using this code:
m_errorHandling = new DialogExceptionProducerUI( TestEditorPlugin.getEditorFor(m_datapool), new EndOfDatapoolExceptionCreator()){ @Override protected void updateEditor() { getButton(OK).setEnabled(true); super.updateEditor(); } }; m_errorHandling.createErrorProducerContents(composite, m_datapool, null );
1.2.8.4. Create an error type
The core performance test model includes the generic CBError object. The CBError object is a shell containing the actual error, the CBErrorType object. Extend the CBErrorType object to add new types of errors, including protocol-specific errors. Associate error-handling behavior with an error by using the CBErrorBehaviorEnum object.
The following code is an example of creating a new protocol error type from the generic error object:
public abstract class ProtocolErrorTypeImpl extends CBErrorTypeImpl implements ProtocolErrorType { /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected ProtocolErrorTypeImpl() { super(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected EClass eStaticClass() { return ErrorsPackage.Literals.PROTOCOL_ERROR_TYPE; } public boolean isErrorGenerator(){ return true; } /** * Imports needed at code generation time * so that the test runs correctly. */ public List<String> getExecImport() { ArrayList<String> imports = new ArrayList<String>(); imports.add("import com.ibm.rational.test.lt.execution.protocol.tes.*;"); imports.add("import com.ibm.rational.test.lt.kernel.action.impl.KThrow;"); imports.add("import com.ibm.rational.test.lt.kernel.services.*;"); return imports; } } //ProtocolErrorTypeImplThe following code is an example of creating a protocol error type from another protocol error type:
public class ProtocolNewErrorTypeImpl extends ProtocolErrorTypeImpl implements ProtocolNewErrorType { /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected ProtocolNewErrorTypeImpl() { super(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected EClass eStaticClass() { return ErrorsPackage.Literals.PROTOCOL_NEW_ERROR_TYPE; } public String getExecType(){ return "ProtocolNewEvent" ; } } //ProtocolNewErrorTypeImplFor model elements that generate errors, declare these elements to be error generators by using this code:
public boolean isErrorGenerator(){ return true; }
1.2.8.5. Implementing code generation for error handling
During code generation, test elements are translated into code language elements. You must implement code generation for new error-handling methods.
Complete these steps to implement code generation for objects that do not generate errors:
- For Script objects, add the <PARAM name="codegen.core.attributes"> parameter to the creation template for the method. For KAction objects, add the <PARAM name="codegen.core.attributes"> parameter to the execute() method.
- Add a call to the translateCoreAttributes() method. For example, the LTTestScriptDefinition class now includes this call:
LTTestTranslator.translateCoreAttributes(scriptTemplate, null, (CBActionElement)test);Complete the following steps to implement code generation for methods that generate errors, such as connection failures and authentication failures:
- Add the <PARAM name="errorBehavior"> parameter to the creation template for the method.
- Add a call to the translateErrorBehavior() method. For example, code generation for the BasicAuthentication object in HTTP includes these calls:
CBError err = (bAuth.getCBErrors().size()==0)?null:(CBError)bAuth.getCBErrors().get(0); translateErrorBehavior(err, bAuthElem, ILanguageElement.TEMPLATE_CREATION);
1.2.8.6. Implementing execution for error handling
Events that require error handling at run time must have an associated errorBehavior action. Extend the RPTEventGenerator class to specify event behaviors.
KAction objects handle events by implementing the IRPTEventHandler method. When an event such as a verification-point failure or connection failure occurs, make the following call to the KAction object that is the parent of the event:
KAction.registerEvent(eventType, eventBehavior);In the previous example, the eventType parameter is the type of failure. The eventBehavior parameter is the action to take when the failure occurs.
During the KAction.finish() procedure, all registered event behaviors are processed. Behaviors that are registered for a specific event are processed. Applicable behaviors that are specified at a higher level in the event hierarchy are processed.
The following code implements the RPTEventGenerator class:
public abstract class RPTEventGenerator implements IRPTEventGenerator{ RPTEvent behavior = null; boolean behaviorSet = false; IKAction act = null; RPTEvent eventType; public void setEventBehavior(IKAction act, RPTEvent eventType, RPTEvent behavior){ behaviorSet = true; this.behavior = behavior; this.act = act; this.eventType = eventType; } public RPTEvent getEventBehavior(){ return behavior; } public RPTEvent getEventType(){ return eventType; } public KAction getAction(){ return act; } }The following code examples demonstrate how to implement error handling at run time for the ServerConnection class.
public class ServerConnection extends RPTEventGenerator implements IServerConnection { public ServerConnection(String name, int port, ISSLInfo sslInfo, INtlmAuthenticationContext ntlmContext, IProxyServerInfo proxyServerInfo, boolean closeWhenTestCompletes, RPTEvent behav) { this.serverAddr = new InetAddressInfo(name, port); this.sslInfo = sslInfo; this.ntlmCxt = ntlmContext; this.proxyInfo = proxyServerInfo; this.inUse = true; this.closeWhenTestCompletes = closeWhenTestCompletes; setEventBehavior(null, new RPTConnectEvent(), behav); } }The behavior for a server connection failure now includes the following code:
registerEvent(((IRPTEventGenerator)m_Request.getServerConnection()).getEventType(), ((IRPTEventGenerator)m_Request.getServerConnection()).getEventBehavior());
1.2.9. Extending the schedule component
When you extend the schedule component in performance testing, another plug-in can contribute options to a schedule. Those options can be set up to persist in the schedule model, and a corresponding user interface can be displayed for editing the new options.
Another plug-in can contribute options to a schedule, have them persisted in the schedule model, and have a corresponding user interface displayed to edit those options. A schedule object represents a performance testing schedule. It is the main object in the schedule model. Schedules can have only one type of element added to them, a UserGroups object. You can use the com.ibm.rational.test.common.schedule.UserGroup interface to add a user group to a schedule.
The mechanism to extend scheduling is similar to the mechanism used to extend the test editor. See the "Extending the test editor" topic for more information.
1.2.9.1. Extension points for extending schedules
During the process of extending the schedule object, a number of extension points are checked to determine whether they extend different aspects of the schedule object.
The following table lists the extension points:
Extension point Description com.ibm.rational.test.common.schedule.editor.optionProvider This extension point enables a contributor to add options when a new schedule object is created through the File > New wizard command. The class attribute must be set to a class that implements the IOptionProvider interface from the com.ibm.rational.test.common.schedule.extensions package. A child node, forTypes, must be added also, with the type attribute set to com.ibm.rational.test.common.schedule.Schedule. com.ibm.rational.test.common.editor.framework.testOptions This extension point enables a contributor to specify that the user interface code display or edit the options.
If you are contributing options for both the Test and Schedule objects, you must create two instances of the extension definition in your plugin.xml file.
- Set the label attribute to the desired label string. Each set of contributed options will appear under its own tab in the editor.
- Set the order attribute, if desired. A value of zero (0) must not be used, because it is used by the User Load tab.
- Set the forTestType attribute to com.ibm.rational.test.common.schedule.Schedule.
1.2.9.2. Public APIs for extending schedules
The public APIs contain the public interfaces and classes that you can use to extend the Schedule object.
The following table lists the public interfaces and classes:
Classes and interfaces Description com.ibm.rational.test.common.schedule.editor.extensions.IOptionProvider
- The IOptionProvider interface is used in conjunction with the optionProvider extension point to allow contributors to initialize and add their options to a Schedule object when it is created by using the File > New wizard command.
- The class has the method public void setDefaultOptionValues(Schedule theSchedule), which is called after the schedule object is created. In this method, create your option object. This object must be a subclass of the com.ibm.rational.test.common.models.behavior.CBOption class.
- Once you have created the schedule object, you can add the option object to the schedule object by calling the addOptions() method on the schedule object. See the Javadoc information for more details about the schedule interface.
com.ibm.rational.test.common.models.behavior.CBOption
- This class has no functionality. It only serves as a common type for options that are added to test and schedule objects.
com.ibm.rational.test.common.schedule.Schedule
- For option contributors, the important method is the boolean addOptions(CBOption options) method. Use this method to add your option object to the Schedule object. This should be done through the optionProvider extension point. The method returns true if the option object is successfully added.
- If you need to remove your options from the schedule, use the boolean removeOptions(CBOption options) method. The parameter is the option object to remove. The method returns a value of true if the option object is successfully removed.
- Use the com.ibm.rational.test.common.models.behavior.CBOption.getOptions(String strType) method to retrieve your options from the schedule object. The parameter is the fully-qualified classname of the object type you want to retrieve. Note that the return type is CBOption, so it must correspond to the appropriate type. For example, to retrieve the general options for the schedule, the following code can be used: ScheduleOptions2 theOptions = theSchedule.getOptions(ScheduleOptions2.class.getName());
1.2.10. Extending code generation
The code generation subsystem maps Load Test Behavior Model (LTBM) elements to objects of the code generation element model (IModelElement), which in turn are mapped to objects of its ILanguageElement class. As a result, a language element tree is created containing all equivalent elements of the behavior model test element tree and at the same time determines the structure of code to be generated.
The language elements are typed, named, and have appropriate templates defined for them. Performance testing code generation takes place entirely within the Eclipse workbench. The Eclipse extension point mechanism is used for code generation extensibility to accommodate new protocols.
The com.ibm.rational.test.lt.codegen.core plug-in supports code generation for the generic Load Test script extending the com.ibm.rational.test.lt.execution.core.impl.LTTestScript class and generic test elements such as loops, containers, transactions, data pools, generic content verification points, data sources and substituters, and generic custom code. Specific protocols such as the com.ibm.rational.test.lt.codegen.http plug-in are implemented as extensions of the com.ibm.rational.test.lt.codegen.core plug-in. The com.ibm.rational.test.lt.codegen.schedule plug-in generates schedules implemented on top of com.ibm.rational.test.lt.codegen.core functionality.
For the code generation extension sample, see the plug-in com.ibm.rational.test.lt.sdksamples.codegen.socket.
1.2.10.1. Code generation
The com.ibm.rational.test.lt.codegen.core.extLibraryDependency extension point has been modified.
The com.ibm.rational.test.lt.codegen.core.extLibraryDependency extension point has been modified in the following ways:
- supportedFeature has been added with attribute featureName, which is the feature ID of this protocol extension as defined by extending the com.ibm.rational.test.lt.licensing.feature extension point. If this element is specified, and the test being generated does not contain this feature, the existence of external libraries will not be enforced. This will prevent the developer from requiring that libraries are invalid for various OS platforms. If the element is not defined, external libraries are enforced as before, so no existing code is broken. Essentially these external libraries will not be enforced for tests that do not contain this feature.
- optionalExtLibraryLocation has been added with attribute pathname (the same as the one for the existing extLibraryLocation element). Libraries defined by this element are deployed when present. If they are not present, however, no warning will be raised. An optional external library might have the supported feature defined, so if the test does not contain the feature, the optional library, even if it exists, will not be deployed.
- optionalExtLibraryLocation and extLibraryLocation have a new attribute, RelativetoExternalFiles. Set this attribute to true if you are providing a library that is in the external_files directory and to false if you are providing a library that is relative to your plug-in.
1.2.10.2. Extension points for code generation
During the initialization process a number of extension points are checked for existence of extensions that define different aspects of the code generation behavior.
These extensions must be defined in the protocol specific plug-ins extending core code generation classes.
Note: Java. has a 64KB limit on the size of a class method code. When there is a possibility of creating large methods that approach this limit, a check has to be programmed into the code generation extension to ensure that the limit is not being exceeded. The guideline is to generate multiple smaller methods instead.
Extension point Description com.ibm.rational.test.lt.codegen.core.typeDefDescription Defines language element types that specify their own translators. com.ibm.rational.test.lt.codegen.core.elementTranslatorMapping Defines what AbstractTranslator class extensions are responsible for generating code for code generation model elements. com.ibm.rational.test.lt.codegen.core.structureDefinition Defines the class that perform translation at the script level, the ScriptDefinition class. com.ibm.rational.test.lt.codegen.core.modelElementAdapter Defines the relationship between low-level behavior model elements and code generation model elements. com.ibm.rational.test.lt.codegen.core.modelReader Defines the class that reads the behavior model elements. com.ibm.rational.test.lt.codegen.core.TemplateLocation Defines the location of the directory containing the templates. com.ibm.rational.test.lt.codegen.core.elementTypeTemplateBinding Associates element types with the text templates. com.ibm.rational.test.lt.codegen.core.elementTypeImportMapping Defines the packages to be imported into the script when a particular element type is used in the test. com.ibm.rational.test.lt.codegen.core.testProjectDependency Defines the projects and the plug-ins to be added to the test project.s class path.
1.2.10.3. Generating test code
During the initialization phase of a code generation request, an object extending the LTTestExtensionPreferences class is created by calling all plug-ins implementing the com.ibm.rational.test.lt.codegen.core.codegenProtocolExtension extension point through their supportFeatures() method.
The supportFeatures()method gets the feature list of the test model object, and if it determines that it supports these features, it returns the appropriate LTTestExtensionPreferences object. The returned object implements the com.ibm.rational.test.lt.codegen.core.config.IExtensionPreferences interface, which is the public part of the LTTestExtensionPreferences class.
The code generation is controlled by the com.ibm.rational.test.lt.codegen.core.CodeGenerator class. This class uses an EclipseCodegenConfiguration object that stores the hash maps for translators, the model element adapters and the templates that are determined by language element types. The container test elements, data pools, and some other independent protocol-specific elements have their own translators defined. Elements that cannot exist on their own are translated as children of their containing elements by calling the translateChildren() method of the translator.
The code generator determines the proper translator and calls its getTranslationFor() method for the given model element.
The ElementAdapter class with its getAdapterForType() method determines the proper code generation model element for the given Behavior Model element.
The translation of test elements involves the following steps:
- The test elements are read from the behavior model, and the appropriate translator is determined for each of them.
- The language element object is created for the element and its template is determined.
- The element attributes are read from the model and their corresponding parameters are substituted in the template.
Results
The generation of the code language elements is recursive and generates the language element tree containing all levels of the execution model test elements, each with the template object instantiated and containing all the requested test substitutions. The tree is implemented by using the language element containers as its nodes. The template substitutions for the language element containers for parameters defining multiple elements are processed by the LangElemCollectionValue class.
1.2.10.4. Create the script class
After all model language elements are processed and the code language elements created, the code for the script class is generated.
The global script variables need to be declared at the top of the script, and their number and types are not known until all test elements are processed. The names of these variables and other associated information are gathered during the processing of the test element tree and are stored as temporary attributes of the test elements in the test model. The script class creation is accomplished by the ScriptDefinition class and is based on a separate script template.
The script template declares the imports and the script globals. It contains the parameters for script type-specific declarations and for methods creating and returning the test elements.
The creation of the top-level script class and test project involves the following steps:
- The language element tree and the top level script element are created.
- The generation of script text is performed and the text is stored in Eclipse storage units.
- The test project is configured, the class path is determined and updated, and the project is built.
Example
The following code represents an example of the script code structure:package customcode; import com.ibm.rational.test.lt.execution.http.IHTTPRequest; ... public class Google_Test_C240F3CB2D546DE2A9BDE160BDA411D9 extends com.ibm.rational.test.lt.execution.protocol.impl.HTTPTestScript { //GLOBAL DECLARATIONS private IBuiltInDataSource bds1 = new com.ibm.rational.test.lt.kernel.custom.impl.timestampdatasource(); { builtInDCVars[50] = new BuiltInCorrelationVar(bds1); builtInDCVars[50].setProperty(1, "16"); builtInDCVars[50].setProperty(2, "16"); } //TEST CLASS CONSTRUCTOR public Google_Test_C240F3CB2D546DE2A9BDE160BDA411D9(IContainer container, String invocationId) { super(container, "google", invocationId); setTimeoutScheme(IKTimeoutControl.CONTINUE); setArmEnabled(false); public void execute() { this.add(page_1(this)); ... super.execute(); } ...... //page_1 CREATION METHOD private HTTPPage page_1(IContainer parent) { HTTPPage page = new HTTPPage(parent, "Google", .....) { public void execute() { this.add(request_1(this); ... super.execute(); } ..... }; return page; } //request_1 CREATION METHOD private HTTPAction request_1(IContainer parent) { HTTPAction reqAction = new HTTPAction(parent, .....); ..... harvestContainer_16.addHarvestInstruction ("resp_content", dcVars[50], ..); ..... return reqAction; } }
1.2.10.5. Code generation templates
The code generation subsystem uses declaration templates and creation templates.
The declaration templates contain code for declarations of the methods and classes creating the test element. The creation template contains code that calls these methods to instantiate the test element objects.
The type of the template is reflected in the template name and is defined by the com.ibm.rational.test.lt.codegen.core.elementTypeTemplateBinding extensions.
Note: It is possible to have name conflicts between code generation templates with the same names that come from different protocol extensions. Use template names containing a protocol name (for example: HTTPScript.template) to avoid this problem.
The following example shows a creation template for a DCSubstituter object:
ISubRule sub_<PARAM name="subRuleIdx> = newSubRule(<PARAM name="targetAttr">", <PARAM name="offset">, <PARAM name="length", <PARAM name="isEncoded", <PARAM name="dataSourceVarName">, <PARAM name="parentInstancename">, addSubInstruction (sub_<PARAM name="subRuleIdx">);
1.2.10.6. New protocol extensions
This section describes the implementation of the code generation subsystem extension for the socket protocol as an example of using code generation extensibility features.
A dedicated code generation plug-in, called com.ibm.rational.test.lt.sdksamples.codegen.socket was created with subpackages of config, lang, and model. The socket code generation subsystem extends the capabilities of the codegen.core plug-in.
The config package contains the SocketExtensionPreferences class which contains the supportsFeatures() method that expresses interest in tests with feature lists containing the socket feature. Also, its getSupportedModelElements() method declares which translator-associated model element types this codegen extension will support.
The lang package contains two main classes, a script definition (SocketScriptDefinition) class and a translator (SocketTranslator) class. The classes extend the appropriate super classes from the codegen.core plug-in, and defining methods for translating script and test elements. The script definition class overrides the doScriptLevelTranslation() method which, after calling its superclass, translates all socket script template parameters specific to the socket protocol. The translator class overrides the getTranslationFor() method, handling all non-socket functionality by calling its superclass, and all socket-specific test elements and parameters locally.
The model package contains the SocketElementAdapter class, which implements a getAdapterFor() method that states what Common Behavior element type this protocol extension handles, and returns the corresponding codegen model element.
The template directory was also added to the protocol extension plug-in, and socket-specific templates were created and placed there. All necessary extensions were defined in the protocol extension plugin.xml file.
For the code generation extension sample, see the plug-in com.ibm.rational.test.lt.sdksamples.codegen.socket.
1.2.10.7. Public APIs of codegen.core
The public APIs contain the public interfaces and classes used to extend the code generation core subsystem.
The following table lists the public interfaces and classes:
Package Public classes and interfaces com.ibm.rational.test.lt.codegen.core.config
- IExtensionPreferences
- InitializationException
- ConfigurationException
com.ibm.rational.test.lt.codegen.core.template
- ITemplate
- Template
- ITemplateParameter
- LangElementCollectionValue
- LangElementParameterValue
com.ibm.rational.test.lt.codegen.core.lang
- IStructureDefinition
- ILanguageElement
- LanguageElement
- ITranslator
- ICoreTranslationConstants
- TranslationException
com.ibm.rational.test.lt.codegen.core.model
- IModelElement
- ModelElement
- IModelElementAdapter
com.ibm.rational.test.lt.codegen.lttest.config
- LTTestExtensionPreferences
com.ibm.rational.test.lt.codegen.lttest.lang
- ILTTestTranslationConstants
- LTTestScriptDefinition
- LTTestTranslator
com.ibm.rational.test.lt.codegen.lttest.model
- LTTestElementAdapter
1.2.11. Extending the run-time environment
The run-time environment defines the plug-ins on which all other plug-ins depend. The run-time environment is responsible for defining a structure for plug-ins and the implementation detail behind them.
1.2.11.1. Blocked Action detection
The performance testing execution engine provides the capability of detecting that an Action is blocked. The definition of blocked is provided by the author of the Action. The definition is provided as an amount of time an Engine Worker Thread is allowed to be unresponsive in the course of executing the Action before it is considered blocked. The performance testing execution engine logs the detection of blocked actions at level FINEST in the Problem Determination Log. In the simplest case, the run would be hung, and discovery of the blocked action would come from examining the Problem Determination log after clicking Stop to end the run.
In addition to detecting blocked actions, a protocol can be notified that the action is blocked. A reference to the blocked Eengine thread is provided with the action, making it possible for a protocol to send a message to the blocked thread. Upon notification of an action being blocked, the protocol can retry the action, finish the action and move to the next action, or finish the virtual user execution.
The IKAction interface will provide the following method as an entry point for protocols to react to a blocked action condition:
public void blocked(). The KAction object state, upon entering blocked(), is potentially corrupted. The protocol writer must assume any or all data associated with the Action is not safe. Also, any locks which may have been held during execute() have been released.
public long getBlockedTimeout()
Returns the action blocked timeout value. Default is 0, which means block indefinitely
public WorkerThread getWorkerThread()
Returns the Engine WorkerThread executing the action when the blocked state was detected.
IEngine
The performance testing engine interface has added public boolean createWorker(). The purpose of createWorker() is to allow the sentinel thread monitoring workers to add additional workers in response to having removed workers found executing blocked actions.
1.2.11.2. Extending subsystem management during a test run
A subsystem is a collection of classes in a discrete component within the performance testing engine that provides a service to many actions. For example, in test execution, the KernelWait subsystem manages think and sleep time for virtual users while a test is running.
Here are additional examples of the services that subsystems provide:
- Tracking server responses. For example, consider tests in which an HTTP request is sent to a server, and you need to know when the response returns. Instead of tying up a thread to wait for the response, a subsystem can do this while the rest of the threads perform other actions. The subsystem can provide notification or reissue an action when the server response occurs.
- Handling asynchronous communication.
- Manage the sleeping action for virtual users.
- Manage logging. For example, creating a custom execution history can take a long time. You can assign a subsystem to do this without tying up a thread with this process. You can set up a subsystem to take care of the special logging actions while the rest of the actions perform other things.
Create the performance test engine subsystem sample
A performance test engine subsystem provides services to one or more actions. An action that uses a subsystem during a test run is known as a recurrent action. Most actions contain finish() at the end of their execute(). A recurrent action requests service from a subsystem before the end of execute(). After the service is provided, the subsystem that provides the service updates state information in the action and reissues the action for execution. Based on the state information, a performance test engine worker thread takes a different course of execution during the recurring call to execute() and eventually calls finish() to end the action.
The following list gives an overview of the steps required to use this sample:
- Create the performance test engine subsystem sample
- Informing the performance test engine of the existence of the subsystem
- Create a simple test and schedule
- Add SampleAction and SampleSubsystem to the project
- Running the schedule with SampleAction using SampleSubsystem
1.2.11.2.1. Informing the test engine that the subsystem exists
Subsystems are identified to the performance test engine with extensions. Typically, a new protocol indicates in its plugin.xml file the class name of the subsystem that it is providing. This example modifies an existing performance test plug-in and specifies the class name of the sample plug-in in the plugin.xml file.
To identify the subsystem to the performance testing engine, complete the following steps.
- Open a command prompt, and change directory to the RPT plug-ins directory.
- Save a copy of the existing HTTP execution JAR. For example: C:\IBM\common\plugins>copy com.ibm.rational.test.lt.execution.http_7.0.0.v200609010404.jar com.ibm.rational.test.lt.execution.http_7.0.0.v200609010404.jar.orig
- Make a new copy of the HTTP execution JAR for modification. For example: C:\IBM\common\plugins>copy com.ibm.rational.test.lt.execution.http_7.0.0.v200609010404.jar foo.jar
- Extract the plugin.xml file. For example: C:\IBM\common\plugins>jar xvf foo.jar plugin.xml Note: You must have a Java. SDK in your PATH statement so that you have access to the JAR utility.
- Add these lines to the plugin.xml file just above the </plugin> line at the bottom:
<extension point="com.ibm.rational.test.lt.execution.Subsystem"> <Subsystem feature="com.ibm.rational.test.lt.feature.lt" class="test.SampleSubsystem"/> </extension>- Start Rational Performance Tester with the -clean option so that the plug-ins are reloaded, for example: C:\IBM\RPT>eclipse .clean.
1.2.11.2.2. Create a simple test and schedule
You must create a simple performance test and schedule. By creating and playing back a test and a schedule, code is generated for both. Later, you will modify the generated test code so that the test contains a special action that uses the sample subsystem.
- Create a performance test project (File > New > Performance Test Project).
- Type a project name, for example, testproj, and then click Finish.
- When the Create New Test From Recording prompt opens, click Cancel.
- Right-click testproj and select New > Test Element.
- Expand Test Assets, select New HTTP Test, and then click Next.
- Name the test, for example, subtest, and then click Next.
- In the Test Attributes window, click Next.
- In the HTTP Extension window, in the Number of HTTP pages to generate field, type 0 (zero), and then click Finish.
- Right-click testproj and select New >Performance Schedule.
- Type a name for the schedule, for example, Schtest, and then click Finish.
- Select User Group 1, and then click Add > Test.
- Select subtest, click OK, and then click File > Save.
- Right-click Schtest, and then select Run as > Performance Schedule.
1.2.11.2.3. Add SampleAction and SampleSubsystem to the project
Import the performance test and schedule source files into the testproj project.
- Open the Java. perspective, open src, and then right-click test.
- Select Import.
- Import SampleAction.java and SampleSubsystem.java.
- Edit the generated subtest file. The name will begin with Subtest_Test_ and end in .java.
- At the bottom of the constructor for the test, add the following line: add(new SampleAction(this, "Sample Action"));
1.2.11.2.4. Running the schedule with SampleAction using SampleSubsystem
Modify the generated test code to use SampleAction, so that the subsystem is active during the next playback.
- Right-click the schedule and select Run As > Performance Schedule.
- When the run completes, right-click the results in the Performance Test Runs view and select Display Test Log.
- Click Events.
- Open the test log hierarchy and navigate through the events of the schedule playback. Look for message events where SampleAction indicates it is requesting service from the subsystem, and where SampleAction recognizes that it has received services from SampleSubsystem.
1.2.11.2.5. SampleAction.java code sample
This is an example of SampleAction.java.
SampleAction.java
package customcode; import com.ibm.rational.test.lt.kernel.IKSubsystem; import com.ibm.rational.test.lt.kernel.action.IContainer; import com.ibm.rational.test.lt.kernel.action.impl.KAction; public class SampleAction extends KAction { final String subsystemName = "test.SampleSubsystem"; private boolean serviced = false; public SampleAction(IContainer arg0, String arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } public void execute() { if (serviced) { reportMessage("SampleAction execute(): service completed"); finish(); } else { IKSubsystem subsystem = getSubsystem(subsystemName); if (subsystem != null) { reportMessage("SampleAction execute(): requesting service"); subsystem.enqueue(this); } else { reportMessage("SampleAction execute(): Cannot find subsystem '" + subsystemName + "'"); finish(); } } } public void setServiced() { serviced = true; } }
1.2.11.2.6. SampleSubsystem.java code sample
This is an example of SampleSubsystem.java.
SampleSubsystem.java
package customcode; import com.ibm.rational.test.lt.kernel.action.IKAction; import com.ibm.rational.test.lt.kernel.engine.impl.Queue; import com.ibm.rational.test.lt.kernel.impl.KSubsystem; /** * Sample RPT Engine Subsystem */ public class SampleSubsystem extends KSubsystem { private Queue sampleSubsystemQueue; private boolean stopRequested = false; private SampleAction client; public SampleSubsystem(String name) { super(name); sampleSubsystemQueue = new Queue(); sampleSubsystemQueue.setBlocking(true); // Allows for waiting for something to appear on the queue } /* * Actions enter the subsystem for service via a call to enqueue(). * An action can get a reference to the subystem using the IKAction * getSubsystem() method. * * @see com.ibm.rational.test.lt.kernel.IKSubsystem#enqueue(com.ibm.rational.test.lt.kernel.action.IKAction) */ public void enqueue(IKAction action) { sampleSubsystemQueue.enqueue(action); } /* * Message to the subsystem to stop. * * @see com.ibm.rational.test.lt.kernel.IKSubsystem#shutdown() */ public void shutdown() { stopRequested = true; } /* * @see java.lang.Thread#run() */ public void run() { while(!stopRequested) { ringIn(); // Informs engine subsystem is healthy client = null; // If nothing to do wait for work updateJob("Idle"); client = (SampleAction)sampleSubsystemQueue.dequeue(pingTime); // This subsystem's work will be to touch an attribute of the action if (client != null) { updateJob("Servicing " + client.getName()); // Good for debugging client.setServiced(); dispatch(client); // Serviced action leaves subsystem } } } }
1.2.11.3. Extending initialization and finalization during a test run
You can specify code for your protocol to be executed by the performance tester engine threads at strategic points during startup and shutdown. For example, you can specify code to load libraries, unload libraries, or perform other initialization or cleanup as required by the protocol.
Create a class that implements IKInitializeFinalize. The interface requires the following methods:
- public void initializeEngine()
- public void finalizeEngine()
- public void initializeWorker()
- public void finalizeWorker()
- public interface IKInitializeFinalize
The IKInitializeFinalize interface provides a way for protocols to specify code that must be executed to the Rational Performance Tester engine:
Use this startup and shutdown code when it is necessary for the engine to execute initialization or shutdown code. Also use this code for each worker thread before test execution occurs or after test execution finishes.
- once by the engine at startup
- once by the engine at shutdown
- once by each engine worker thread at startup
- once by each engine worker thread at shutdown
- Specify a dependency on com.ibm.rational.test.lt.execution in the plugin.xml file of a protocol.
- Use Add under plugin Extensions to specify an extension for com.ibm.rational.test.lt.execution.InitializeFinalize.
- Create a new extension element called InitializeFinalize. This element must have the following properties:
For example:
- class: The class name that implements IKInitializeFinalize
- id: The protocol feature ID
- dependsOn: Leave blank
- class="com.ibm.rational.test.lt.execution.http.impl.HTTPInitializeFinalize"
- id="com.ibm.rational.test.lt.feature.http"
- dependsOn=
1.2.11.4. Public APIs for run time
The public APIs contain the public interfaces and classes that you can use to extend the run-time environment functionality.
The following table lists the public packages:
Package Description com.ibm.rational.test.lt.kernel Contains the factory, counter, monitoring, and constants classes. com.ibm.rational.test.lt.kernel.action Contains the classes and interfaces necessary to define conditions and the basic functionality that all kernel actions should implement com.ibm.rational.test.lt.kernel.arbitrary Contains the IArbitrary interface. com.ibm.rational.test.lt.kernel.custom Contains the interfaces that enable additions of custom code to a performance test. com.ibm.rational.test.lt.kernel.engine Contains the interfaces used to manage the process of arranging actions onto queues. com.ibm.rational.test.lt.kernel.io Contains the interfaces that get the buffer factory. com.ibm.rational.test.lt.kernel.library Contains the class that loads the library. com.ibm.rational.test.lt.kernel.logging Contains the interfaces to manage the cache. com.ibm.rational.test.lt.kernel.runner Contains the IRatlRunner interface. com.ibm.rational.test.lt.kernel.services Contains the interfaces for datapool, loop control, test information. com.ibm.rational.test.lt.kernel.statistics Contains the interfaces for statistical counters. com.ibm.rational.test.lt.kernel.util Contains the interfaces and classes for kernel utilities. For details about the packages, classes and interfaces, see the Javadoc information.
1.2.12. Extending the test log viewer
After running a test, the test results are saved in a test log. Use the Test Log viewer to check the results for specific events, such as the script start and end, loop, invocation, message, or verdict.
You can extend the test log viewer for your protocol. For information about how to do this, see the Testing Performance Tools Platform (TPTP) documentation.
1.2.13. Extending evaluation results
Reports are a specification of how performance test data should be extracted from the statistical model and presented to the user.
The persisting reports contain no statistical data. After a report is created, it can be focused on any statistical model.
The user interface components for evaluation results are the Performance Test Runs view, Results Viewer, Report Wizard, Execution History Viewer (TPTP), Protocol Data view and Properties view in Eclipse. For details about how to use the user interface components, see the "Evaluating results" topic in the product information center.
Besides the user interface components, generic counters and aggregators also enable you to extend the evaluation of results. The generic counters represent the mechanism for specifying the model path of statistical data to be displayed in the Results Viewer. The generic counters are specified with the com.ibm.rational.test.lt.execution.results.DynamicCounter extension point. The aggregators calculate the majority of the data contained in the statistical model and reduce the amount of data that must be transmitted. The aggregators are deployed through the com.ibm.rational.test.lt.execution.results.data.aggregation.Aggregator extension point. For details about generic counters, see the "Generic counters" topic in the product information center. For details about extending evaluation results with aggregators and other classes and interfaces, see the Javadoc information.
For an example of extending evaluation results, see the plug-in com.ibm.rational.test.lt.sdksamples.results.socket.
1.2.13.1. Aggregation of statistical data
To minimize network load, the guideline is to send only a minimal subset of the statistical data that can be used to aggregate the remainder of the statistical data. This aggregation is handled by the aggregation subsystem. There are two primary types of aggregators: transfer aggregators and standard aggregators. Transfer aggregators transfer data of like origin from individual nodes to the All Hosts node. Standard aggregators calculate additional data based on the data sent across the network from the execution engine. An example of a transfer aggregator task is creating the counter "Pages/Response Time/Google/Average interval" on the All Hosts node by considering the same counter from three drivers. An example of a standard aggregator task is calculating counter "Pages/Response Time/Google/Average cumulative" on the All Hosts node by tracking and weighting each "average interval" value in real time.
For more information on aggregation, refer to the Javadoc information for the following transfer aggregators:
- MaxTransferAggregator
- MinTransferAggregator
- ScalarTransferLastValueAggregator
- AverageTranferAggregator
as well as the following standard aggregators:
- AverageAggregator
- MaxAggregator
- MinAggregator
- PercentAggregator
- PercentAggregator_NonInclusive
- RateAggregator
- TotalScalarAggregator
1.2.13.2. Extending report counters
In performance test reports, generic counters are collectable queries in the user interface that dynamically gather specific information from the statistical model, such as the number of page hits, the response time, the response success, and information about verification points. Counters are dynamic. The counter wizard is used to add counters to reports. You can extend the counter wizard for specific protocols.
You extend the counter wizard in the ReportAction extension point, which is contained in the com.ibm.rational.test.lt.execution.results plug-in.
Note: Any report that provides mean average data now also provides standard deviation data. If a protocol-specific report contains a mean counter, the extension is able to add the corresponding standard deviation counter onto that report.
1.2.13.2.1. The ReportAction extension point
ReportAction enables interactions directly on the report screen as well as in the Performance Test Runs view. Use this extension point to enable report menus and menu items in the tree and to extend the counter wizard for your protocol. Implementors of generic ReportActions must extend the abstract class com.ibm.rational.test.lt.execution.results.actions.ReportAction, while implementors of AddCounterAction must specify relevant data in plugin.xml only.
The following table describes the elements and attributes of the ReportAction extension point.
Elements and attributes Description ReportActionGroup Provides menu groupings for ReportActions that define the location of report actions within menus in reports. menuPos Position of a menu group in a report menu. Valid values: "start" (top) "mid1," "mid2," and "end" (bottom). ReportAction Provides interactivity on performance test reports and in the Performance Test Runs view. Defines the behavior of actions in reports. groupID ID of the ReportActionGroup (menu grouping) containing this ReportAction menu item. actionprovider Behavior provider that extends com.ibm.rational.test.lt.execution.results.ReportAction. Menutext Text for this menu item. Icon The project-relative path to the icon for this menu item. Tooltip Tooltip for menu item. AddCounterAction Provides a wizard for adding and removing specified statistical descriptors (counters). groupID ID of ReportActionGroup containing this wizard action. Menutext Menu text for an agent. Icon The project-relative path to the icon in the wizard for this action. Tooltip Tooltip for this action. allowAllAvailable When true, a check box is displayed at the bottom of the wizard that reads "Automatically add localizedCounterCategoryName counters to graphic as they appear in result." If the user selects this check box, any counter that appears in the stat model that meets the specifications of the AddCounter action is automatically added to the graphic of focus. For an example of this feature in use, see the Add/Remove Resource Counters wizard in the performance testing product. agentID Returns the ID of the TRCAgent to which this add wizard action applies. Agent IDs can be declared as "not" to specify systems under test, for example !com.ibm.rational.test.lt.execution.results.XMLStatisticalDataProcessor. Declaring a "!" ID causes all other agents to be included. If this attribute is blank, it defaults to com.ibm.rational.test.lt.execution.dataprocessor.XMLStatisticalDataProcessor which is the performance testing Statistical agent. wizardIcon The project-relative path to the icon that is displayed on the wizard page. modelBasePath Root path from the statistical model from which AddCounterAction should pull counters for the add/remove action. cshelpID The context-sensitive help ID for this wizard. localizedCounterCategoryName The localized description of the type of counters this wizard processes. This string is substituted into the wizard to describe what the user is adding to the report. For example, in the string "Add/Remove Resource Counters Wizard", "Resource Counters" is the localizedCounterCategoryName. This name should be plural as indicated above. showScale Makes data readable within the space provided for it in the user interface. If showScale is true, the wizard enables the user to adjust the scale of counters relative to each other. It also enables a recommended scale to be calculated when data is added to a report from the wizard. For an example of this feature in use, see the Add/Remove Resource Counters wizard in the performance testing product. showScope "Scope" refers to the nodes from which data is pulled from in the statistical model. If true, the user is provided with a control to specify the scope of a counter. Valid scopes are:
For an example of this feature in use, see the Add/Remove Resource Counters wizard in the performance testing product.
- All Locations Data is pulled from any node in the model that has data meeting the specifications of an AddCounterAction.
- Systems Under Test Data is pulled from any node that does not contain an "RPT Statistical Agent." This signifies that the node is a "driver node."
- Selected location Data is pulled only from the node on which the report is focused.
showAgents If true, the wizard shows agents in the tree hierarchy. If not included or if false, descriptors are shown as the root objects. defaultScope Default scope to be used by the wizard. "Scope" refers to the nodes in the wizard from which data is pulled from in the wizard. It can be used with or without the showScope attribute. Valid entries are: "CURRENT", "SUT", and "ALL." Enablement Controls when actions are visible and selectable. type Specifies selected object types for which this action should be enabled. Any object that can be identified in a report or in the Performance Test Runs view (PTR) can be specified as a valid type, for example, a chart or table as shown in the PTR or in a report. path Controls visibility. If no path is specified, the item is visible on any selection where "type" is valid. Path also specifies the model path to the data to be referenced within the selected object for menu-item visibility. Paths can contain wildcards, and more than one path can be specified. An example path specification is as follows: <path value="Pages,Response Time,*,Average Cumulative"/>runstate Controls enablement (not visibility) by run state. If true, the menu item is enabled only if a run is in progress. Filterstate Controls enablement (not visibility) by filter state. If true, the menu item is enabled only if the selected object is filtered. The following is an example of a ReportAction extension point.
<extension point="com.ibm.rational.test.lt.execution.results.ReportAction"> <ReportAction groupId="com.ibm.rational.test.lt.execution.results.ImportGroup" menutext="%IMPORT_RESMON_COUNTERS" tooltip="%IMPORT_RESMON_COUNTERS_TOOLTIP" icon="icons/elcl16/import_resmon_data.gif" actionprovider="com.ibm.rational.test.lt.execution.rm.actions.ImportResourceCounters" <Enablement> <type objecttype="com.ibm.rational.test.lt.execution.results.view.countertree.MonitorTreeObject"/> <type objecttype="com.ibm.rational.test.lt.execution.results.view.countertree.GraphicTreeObject"/> <type objecttype="com.ibm.rational.test.lt.execution.results.view.graphics.Graphic"/> <runstate active="false"/> </Enablement> </ReportAction> <AddCounterAction agentID="!com.ibm.rational.test.lt.execution.dataprocessor.XMLStatisticalDataProcessor" allowAllAvailable="true" cshelpID="add_resource_wiz" defaultScope="ALL" groupId="com.ibm.rational.test.lt.execution.results.addcountersgroup" icon="icons/elcl16/add_res_ctr.gif" localizedCounterCategoryName="%RESOURCE_COUNTERS_DESCRIPTION" menutext="%ADD_RESOURCE_COUNTER" showAgents="true" showScale="true" showScope="true" tooltip="%ADD_RESOURCE_COUNTER_TOOLTIP" wizardIcon="icons/wizban/add_res_wiz.gif" <Enablement> <type objecttype="com.ibm.rational.test.lt.execution.results.view.graphics.Graphic"/> <type objecttype="com.ibm.rational.test.lt.execution.results.view.countertree.GraphicTreeObject"/> </Enablement> </AddCounterAction> </extension>
1.2.13.3. Extending default reports
You can specify the default performance report for your protocol. In the Preferences page in performance testing, the Determine the default report based on protocols in test check box is selected by default. You can specify, for your protocol, the report that automatically displays during a test run. You define the default performance report in the RPTReport extension point, which is contained in the com.ibm.rational.test.lt.execution.results plug-in.
1.2.13.3.1. The RPTReport extension point
Use this extension point to define performance testing reports that are installed with the product and are available for "Restore to default."
The following table describes the elements and attributes of the RPTReport extension point.
Elements and attributes Description Report A performance testing report definition. Can be a part of a ReportGroup; however, any report that applies only to a specific protocol should be assigned to that protocol report group. path The provider plug-in relative path to the .view file that defines the performance report, for example:/reports/my_report.view. menuText A localized string for the Report menu item (for example, "Display Performance Report"). Localize this string using the "%" prefaced key. When this string is retrieved from the extension, the localized value from the plug-in resource bundle is provided. Refer to the example that follows in this topic. icon Plug-in root-relative path to an icon used with the menu of this report, for example: /icons/my_icon.gif restrictToPostRun To have this report available only after the run has completed, set this Boolean variable to "true." id A unique identifier for this report. PostRunGenerator Implies restrictToPostRun. If a report requires post-run processing (for example, a Page Percentile report that calculates its data based on the entire test run), this attribute can specify an implementor of PostRunReportGenerator. Refer to the example that follows in this topic. isTemplate Note: this attribute is not currently available. ReportGroup Grouping for protocol-specific reports. Results in a drop-down menu labeled with text provided in menuText. All protocol-specific reports must have a protocol group. menuText Text that labels the protocol group drop-down menu. Should be localized using the "%" prefaced key. capability_id Note: This attribute is not currently available. defaultReportID The ID of the default report for the protocol associated with this group. protocol_id The ID of the protocol to which this ReportGroup applies. The following example shows an RPTReport extension point.
<Report menuText="%DISPLAY_TRANS_REPORT_LABEL" icon="icons/elcl16/trans_report.gif" path="reports/Transaction Report.view" id="com.ibm.rational.test.lt.execution.results.transactions"/> <ReportGroup menuText="%HTTP_REPORTS"> <Report menuText="%DISPLAY_PERF_REPORT_LABEL" icon="icons/report_default.gif" path="reports/default.view" id="com.ibm.rational.test.lt.execution.results.performance"/> <Report menuText="%PAGEEL_REPORT" icon="icons/elcl16/pageelem_report.gif" path="reports/Page Element Report.view" id="com.ibm.rational.test.lt.execution.results.pageelement"/> <Report restrictToPostRun="true" menuText="%PERCENTILE_REPORT" icon="icons/report_percent.gif" path="reports/Percentile Report.view" PostRunGenerator="com.ibm.rational.test.lt.execution.results.view.controller.PercentileReportController" id="com.ibm.rational.test.lt.execution.result.percentile"/> <Report menuText="%VP_REPORT" icon="icons/report_vp.gif" path="reports/Verification Point Report.view" id="com.ibm.rational.test.lt.execution.results.vp"/> </ReportGroup>
1.2.13.4. Public APIs for evaluate results
The public APIs contain the public interfaces and classes that you can use to extend the evaluate results functionality.
The following table lists the public packages:
Package Description com.ibm.rational.test.lt.execution.results.data Contains the IStatModelFacade interface used to access and modify the performance testing statistical model. com.ibm.rational.test.lt.execution.results.data.aggregation Contains the aggregation classes used to aggregate statistical data in real time. com.ibm.rational.test.lt.execution.results.data.aggregation.aggregators Contains the aggregator classes that aggregate statistical data in real time and place the newly calculated data on the host which contains the aggregators dependency data. com.ibm.rational.test.lt.execution.results.data.aggregation.transferaggregators Contains the aggregator classes that aggregate statistical data based on data from all hosts in the resultset. Data calculated by these aggregators is placed in the âAll Hostsâ host. com.ibm.rational.test.lt.execution.results.internal.actions Contains the PostRunReportGenerator interface that is specified in the extension point RPTReport. Post Report generators are used to calculate statistical data after a run has completed. com.ibm.rational.test.lt.execution.results.view.data.stringtranslator Contains the IRPTStatStringTranslator interface extended by the implementors of the com.ibm.rational.test.lt.execution.results.StatisticalStringTranslator class. String translators are used to localize strings contained in the statistical model.