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:

  1. Open the test, and select a test element.
  2. 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.

  3. 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.
  4. In the Arguments field, click Add.
  5. 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).
  6. Click OK. The window closes, the selected references are added to the Arguments field.
  7. Optional: To add text strings as inputs to your custom code, click Text, and then type the text string to use.
  8. In the test, after your custom code, locate a value that your code returns to the test.
  9. Highlight the value.
  10. 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>&nbsp;<span id=nw> 
 *   </span></small></h2> 
 *   <div class=ct> 
 *   &#149;&nbsp;<a href=s/213231>Cooler weather moving into eastern 
U.S.</a>  *   <br>&#149;&nbsp;<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.

  1. Start IBM Rational Performance Tester and create a performance test project MyProject.
  2. 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.

  3. Expand the first request and click the response element.
  4. In the Test Element Details section, right-click in the Content field and click Create Field Reference.
  5. Type the reference name and click OK.
  6. Click the first page, and then click Add > Custom Code.
  7. In the Arguments section of Test Element Details, click Add.
  8. Expand the data source for the search results page, select the reference name that you created in step 5, and click Select.
  9. Click Generate Code. A new tab with the generated code is displayed.
  10. 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:

  11. 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.

  12. Save the custom code and then the test.
  13. Create a new schedule, Schtest.

    1. In Schtest, set the number of users to run to 1.
    2. Click User Group 1 and click Add > Test. Select the MyTest test and click OK.
    3. Click User Group 1 and click the Run this group on the following locations button.
    4. Click Add > Add New.
    5. In the New Location window, type the following information:

      1. In Host name, type localhost.
      2. In Name, type debuglocation.
      3. In Deployment directory, type C:\mydeploy.
      4. Click Finish.
    6. Save the schedule.
  14. In the Test Navigator, right-click debuglocation and click Open.
  15. Click the General Properties tab and click Add.
  16. 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
    

  17. Save the location.
  18. Attach the debugger to the schedule execution process.

    1. 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.
    2. Click Window > Open Perspective > Other > Debug.
    3. Click Run > Debug Configurations.
    4. In the Debug Configurations window, right-click Remote Java Application and click New.
    5. Click Debug. A list of running threads are displayed in the Debug window and the schedule execution pauses at the debug breakpoint.
    6. 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:

      1. Click Edit Source Lookup Path and click Add.
      2. Click Workspace Folder > OK.
      3. 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:

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:

For examples of RPT extensions, see the following plug-ins:


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:

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:

Use these conventions, the extension can have the following plug-ins:

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:

Within these plug-ins, you can either arrange the components in one of two ways:


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:

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:

The recording framework has three main UI contributions:


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:

These factors apply to IRecorderPacket class implementations:

Recorder packets have these common features:

For best results, follow these practices for implementing recorder packets:


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:

Follow these requirements for implementing the IRecorderDelegate interface:


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:

To implement the IClientDelegate interface:

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:

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:

  1. The recording session file is selected.
  2. The client is selected.
  3. The recording method is selected.
  4. The client wizard pages are displayed.
  5. 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:

Additional information about the New Recording Session wizard:

To define a client wizard:

About the NewClientWizard class implementations:

To define a recorder wizard:

About NewRecordersWizard implementations:

To define a wizard that configures a client and one or more recorders altogether:

About NewRecorderClientWizard class implementations:


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 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:

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:

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:

To define a new converter, you must complete these procedures:

Consider these facts about IPacketConverter implementations:

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:


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:

  1. Test generator selection.
  2. Test files selection.
  3. Data correlation options.
  4. 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:

Consider these facts about NewTestGeneratorWizard implementations:


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:

Extensions can define additional annotation types. To contribute an annotation type:


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:

For best results, complete these optional steps:

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:


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:

Consider these facts about AbstractAnnotationAction implementations:


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:


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.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:

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:


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:

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.

  1. Optionally, create an interface that defines the methods for the class, including the setters and getters for the attributes added by this element.
  2. Create a new class that extends the CBBlockImpl interface, and if you created an interface in step one, implement the interface.
  3. 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.
  4. 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.

  5. 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:

  1. Optionally create an interface that defines the methods for the class, including the setters and getters.
  2. Create a new class that extends CBBlockImpl interface and that optionally implements the interface defined in the above step.
  3. Implement the getters and setters of attributes for this model element class.

    1. The setter must set the value attributes of the primitive data types into the underlying model using one of the overloaded setProperty() methods.
  4. Override the addReference() method to set attributes of complex types.
  5. 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.
  6. 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:

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:

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.

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:

.

The other element details are:

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.

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:

  1. You must extend an extension point in codegen.core to get your execution IProtoActionAdapter regisetered for playback:

    1. 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.
    2. Tell the com.ibm.rational.test.lt.datacorrelation.execution plug-in what types of IKActions to handle and the name of your plug-in.
    3. 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.

  2. 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.

  3. To start the substitution process, in the IDataSub.substituteData() method, use the action and hash map as parameters.

    1. 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.
    2. The substitute container returns the control to the IKAction interface.
    3. The IKAction interface reads the string values from the hash map and sends them to the appropriate places.
  4. To start data harvesting, the action that is currently active calls the IDataHarvester.harvestData() method. The action passes itself to the container.

    1. 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.
    2. 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.

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:


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:


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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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 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;
	}
} //ProtocolErrorTypeImpl

The 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" ;
	}
} //ProtocolNewErrorTypeImpl

For 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:

Complete the following steps to implement code generation for methods that generate errors, such as connection failures and authentication failures:


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.

  • 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.
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.


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:


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:

  1. The test elements are read from the behavior model, and the appropriate translator is determined for each of them.
  2. The language element object is created for the element and its template is determined.
  3. 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:

  1. The language element tree and the top level script element are created.
  2. The generation of script text is performed and the text is stored in Eclipse storage units.
  3. 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:

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:


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.

  1. Open a command prompt, and change directory to the RPT plug-ins directory.
  2. 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
  3. 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
  4. 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.
  5. 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>
    
  6. 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.

  1. Create a performance test project (File > New > Performance Test Project).
  2. Type a project name, for example, testproj, and then click Finish.
  3. When the Create New Test From Recording prompt opens, click Cancel.
  4. Right-click testproj and select New > Test Element.
  5. Expand Test Assets, select New HTTP Test, and then click Next.
  6. Name the test, for example, subtest, and then click Next.
  7. In the Test Attributes window, click Next.
  8. In the HTTP Extension window, in the Number of HTTP pages to generate field, type 0 (zero), and then click Finish.
  9. Right-click testproj and select New >Performance Schedule.
  10. Type a name for the schedule, for example, Schtest, and then click Finish.
  11. Select User Group 1, and then click Add > Test.
  12. Select subtest, click OK, and then click File > Save.
  13. 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.

  1. Open the Java. perspective, open src, and then right-click test.
  2. Select Import.
  3. Import SampleAction.java and SampleSubsystem.java.
  4. Edit the generated subtest file. The name will begin with Subtest_Test_ and end in .java.
  5. 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.

  1. Right-click the schedule and select Run As > Performance Schedule.
  2. When the run completes, right-click the results in the Performance Test Runs view and select Display Test Log.
  3. Click Events.
  4. 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:

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.

  1. Specify a dependency on com.ibm.rational.test.lt.execution in the plugin.xml file of a protocol.
  2. Use Add under plugin Extensions to specify an extension for com.ibm.rational.test.lt.execution.InitializeFinalize.
  3. Create a new extension element called InitializeFinalize. This element must have the following properties:

    • class: The class name that implements IKInitializeFinalize
    • id: The protocol feature ID
    • dependsOn: Leave blank
    For example:

    • 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:

as well as the following standard aggregators:


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:

  • 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.
For an example of this feature in use, see the Add/Remove Resource Counters wizard in the performance testing product.
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.