Writing Script Extensions

Script Extension Points

When the original functionality of testIDEA is not enough, we can extend it by writing Python functions and scripts. There exist three kinds of script extension points: All extensions should be implemented as methods in extension script file. The only exception are GUI script extensions, which are described in section GUI extensions.

Configuration of script environment

Configuration for execution of scripts is accessible with command File | Properties | Scripts:

Each parameter is described in its tool-tip.

Note: Path to Python interpreter is the same as used by winIDEA. If you need to change it, please see winIDEA dialog Tools | Options, tab Scripts.

Test case extensions

These extensions can be defined for each test case. This means, that each test case may specify different script methods to be executed during test case run. These methods are defined in file and class specified in configuration.

There exist six extension points in test case:

Writing the script extension

Python script file with extension functions can be written manually from scratch, or by wizard. The description of manual script creation is following, while the wizard is described in a separate section.

Script functions should be implemented as methods of class in Python module. First we create a file with extension py. File name is also the name of the module. This name should be specified in the Imported modules field in the configuration dialog above. This way testIDEA can tell Python interpreter where to find our code.

Next we write a class in the created file. It is a normal Python class with methods. Names of the methods must be the same as given in test case. The number of parameters must also match.

Example of class with one extension method:

      class SampleExtension:

          def __init__(self, mccMgr = None):
              pass

          def initTarget(self):
              print 'Executing initTarget()'
    
Since we'll usually need winIDEA SDK calls to access the target, it is a good idea to import the module and initialize access object in extension class constructor, for example:
    import isystem.connect as ic

    class SampleExtension:

        def __init__(self, mccMgr = None):
            """
            Normally we'll connect to winIDEA, which is running the test, so that
            target state can be accessed/modified by this script.
            """
            if mccMgr == None:
                # Executed when called from testIDEA.
                # Connection can't be passed between processes.
                self.mccMgr = None
                self.connectionMgr = ic.ConnectionMgr()
                self.connectionMgr.connectMRU()
                self.debug = ic.CDebugFacade(self.connectionMgr)
            else:
                # Executed when called from generated script - connection is reused.
                self.mccMgr = mccMgr
                self.connectionMgr = mccMgr.getConnectionMgr('')
                self.debug = mccMgr.getCDebugFacade('')

            self.testCtrl = None

    
        def initTarget(self):
            print 'Executing initTarget()'
            for i in  range(0, 10):
                self.debug.modify(ic.IConnectDebug.fMonitor, 'g_charArray1[' + str(i) + ']',
                                  str(i*i))
    
Note that for access to test local variables declared in testIDEA, we have to use instance of class CTestCaseController. See below for procedure of getting this object. If this class is saved to file sampleTestExtensions.py, we configure testIDEA as shown in the image above. Similar class can be found in Python SDK: examples\winIDEA\sampleTestExtensions.py.

How can extension methods provide feedback?

There are several kinds of information a method can provide, and each has its own purpose: When not set to None, type of these variables should always be string.

Return values and info variables of each function can be seen in tool-tips of decorations next to extension function name in section Scripts in testIDEA. Red decoration means error, green one means information is available, while no decoration means no error and no information:

Important: Standard output (print statements) should be used for debugging only, and script info and return values should be at most few lines long. This way reports and information in testIDEA UI will be readable. For more extensive measurements scripts should use files.

How to access target variables

One of the tasks for script functions is evaluating values of target variables and modifying them. By the term target variables this manual refers to one of the following: All target variables can be accessed with an instance of cass CTestCaseController. Before testIDEA calls script extension functions it sets Python variable _isys_testCaseHandle to the value of current test case handle. To instantiate CTestCaseController inside script extension functions use the following Python code:

    def myExtFunction(self, testSpec):
        if self.testCtrl == None:
            self.testCtrl = ic.CTestCaseController(self.connectionMgr,
                                                   self._isys_testCaseHandle)
	
To access target variables use the following example:
        # 'nItems' is the name of parameter specified in 'Parameters' field of the
        # 'Stubs' section in testIDEA. 'srv' is the name of return value as
        # specified in the same section.
        # Format specifier 'd' in 'nItems,d' ensures that the returned string 
        # always contains decimal value.
          
        nItems_t = int(self.testCtrl.evaluate('nItems,d'))
        print 'nItems = ', nItems_t

        self.testCtrl.modify('srv', str(nItems_t * 2))
        
Tip: To avoid confusion, it is highly recommended to use a prefix or postfix for all Python variables, which are related to target variables. For example, above we have used postfix _t to mark that Python variable nItems_t is related to target variable nItems.

How to access test specification

If test specification data in the script function is needed, then the first parameter in testIDEA should be specified as variable _isys_testSpec. This parameter is initialized to the instance of CTestSpecification, which contains all information about the test case executed.

Reserved identifiers

All identifier names (variable, function, module, ... names) starting with string _isys_ are reserved. It is strongly advised to not use such names for custom identifiers, to avoid problems with the future versions of testIDEA.

Script extensions wizard

The wizard for script extensions is accessible with menu command iTools | Script Extensions Wizard. It creates an initial script file, which can be customized according to our requirements, and sets names of script functions in the selected test case. If no test case is selected, then only script is generated. It is recommended to create the script in the winIDEA project directory (where xjrf file is located), because then Python paths for module loading do not have to be modified.

Test execution extensions

These extensions are defined as methods in file and class specified in configuration. They are called at the following occasions: Parameter reportConfig in script functions called during report saving contains report configuration as string in iyaml format, for example:
    testIDEAVersion: 9.12.279
    winIDEAVersion: 9.12.279
    reportContents: full
    outFormat: xml
    fileName: 'report\reportFull.xml'
    iyamlFileName: d:\bb\trunk\sdk\targetProjects\testIDEADemo.iyaml
    xsltFull: ' isystemTestReport.xslt'
    xmlLogoImage: file://D:\bb\trunk\Eclipse\testidea\si.isystem.itest.plugin.core\i
    cons\itest_window_128.gif
    xmlReportHeader: |-
      Test Report
    cssFile:  blue.css
    csvSeparator: ','
    isCSVHeaderLine: true
    isXLSVerticalHeader: false
    isIncludeTestSpec: true
    isAbsPathForLinks: true
    htmlViewMode: all
    testInfo:
      Date: 2016-05-16
      Time: 16:11:04
      Subversion rev.: 58135
      description: release build
      hardware: cortex
      wiWorkspacePath:
      bootloader: '1.05g'
      _appRev: '12.35'
  

GUI extensions

These script extensions appear in menu iTools, and must be selected explicitly by user to be executed. To refresh the list in the menu, execute command iTools | Refresh. Two kinds of GUI extensions exist:

Methods in extension script file

These methods should be defined in the script extension file and class, which are set in File | Properties | Scripts. All methods, which start with prefix _isys_cmd_ are listed in menu iTools. When called, all methods receive name of the iyaml file in the currently active editor as parameter.

Example:

    def isys_cmd_printHi(self, iyamlFile):
        print("HI! Script method executed, parameter = ", iyamlFile)
    
For function output the same rules as for other extension functions defined in this file apply. Method result can be printed to stdout or stored to variable self._isys_customScriptMethodInfo. Any text printed to stderr or return value other than None mean error. See file basicTutorialExtensions.py, which comes with SDK for examples.

Standalone scripts

Python scripts (extension *.py), which are located in the same directory as iyaml file, are listed in the menu iTools. To get script directory, iyaml file which was opened in an active editor when command iTools | Refresh was executed, is used.

To avoid inconveniently long lists in the menu, scripts are sorted alphabetically, and only the first ten are used. One possible naming convention for scripts, which we want to run from testIDEA, would be to start their names with an underscore ('_') character.

Scripts can be run in three modes: