QA Partner Extension Kit

Lucent Technologies

Background

This paper describes my efforts getting QA Partner (QAP) automated test scripts to recognize Rogue Wave zApp custom objects using the QA Partner Extension Kit. The applications under test were developed using the client-server model and written in C++ for Windows 95.


Introduction

QA Partner: A Brief History

QA Partner, a graphical user interface (GUI) test tool, was developed by Laurence Kepple, founder, president and chief executive of Segue Software, Inc., Newton Centre, Massachusetts. Keeple was also the developer of the Softbridge, Inc. ATF automated test tool. QA Partner (versions 1.0 thru 4.0) was later renamed SilkTest® in 1996 and included Web testing support. Segue Software was acquired by Borland in 2006. Then, in 2009, Borland was acquired by Micro Focus where SilkTest sales and support now reside. SilkTest 2010 introduced Microsoft Visual Basic as the scripting language although SilkTest Classic 4Test Outline Editor Mode is still supported.

Extension Kit

The Extension Kit is an optional component of the QualityWorks family of automated test tools. The Extension Kit allows system testers to code new 4Test Agent functions in C. This paper describes the new Agent functions that were developed to allow QA Partner test scripts access to custom objects implemented in Actiview applications using the Rogue Wave zApp Interface Pack Class Library. In particular, the Actiview extension functions provide access to the zTable and zStatusLine objects. The zTable object appears on the screen as a grid or spreadsheet. The zStatusLine is typically displayed as a partitioned display-only text area at the bottom of a window, which is used to display messages to the user.

Solution Approach

Simply stated, the zTable and zStatusLine objects are basically invisible to QA Partner. Therefore, test scripts are not able to invoke any methods on these objects (with the only possible exception of Click using bitmap coordinates). On the other hand, all member functions can be freely invoked on these objects internally within the application under test. My approach was to code an implant or extension, which then becomes a part of the production application code. As a result, the implant now has equal access to these custom objects on par with the application code. This implant or extension has been implemented as standard C functions. The Segue documentation refers to this type of extension as an internal extension.

Implementation Process

Since the QA Partner Extension Kit functions would be built along with the application under test, it was necessary to develop a way to test these extension functions prior to handing them over to development for inclusion in the general application builds. So, I modified a Rogue Wave tutorial application that contained the custom objects necessary to test the extension. By using this test application, I was able to drive the extension on an application built without having to install the entire Actiview build environment. In addition to the code for the extension functions themselves, extension code must also be included in the application’s main window source (.cpp) file. This code registers the extension functions with the QA Partner 4Test Agent. The Assist.dll file enables this communication between the Agent and the extension functions. During application startup, the extension calls the QAP_Initialize function, which verifies the availability of the Agent and returns a Boolean value. If the Agent is not running, then the extension code that registers the extension functions will not execute.

The production version of the QAP Extension required a modification from the test version. The test version utilized two global variables for pointers to the zTable and zStatusLine objects. For the production version, development provided a method call that the QAPFUNCs could invoke to acquire the pointer values.

QAP C Functions

The following QA Partner C functions were developed so that the 4Test scripts could access the zTable and zStatusLine objects:

  • zTab_ReturnColLabels
  • zTab_SelectTableRow
  • zTab_ReturnTableRow
  • zTab_ReturnStatusLine

 Test Application Source Code

QA Partner Extension code is highlighted in blocks within the following application code.

// testapp.cpp -
// Version - 1.0
// 3/30/98 - 12:59:25pm
//
// zpb_begin AppRevisions
// zpb_end

#include "zapp.hpp"
#include "testapp.hpp"  // Main App Class Definitions & Includes
#include <stdlib.h>
#include <string.h>
#include "qapwinek.h"

ZAPP_IMPL_ASSERTS

// zpb_begin GlobalVars
zTable* Tbl_ptr;
zStatusLine* Status_ptr;

void QAPFUNC zTab_ReturnColLabels(PARGS pArgs);
void QAPFUNC zTab_SelectTableRow(PARGS pArgs);
void QAPFUNC zTab_ReturnTableRow(PARGS pArgs);
void QAPFUNC zTab_ReturnStatusLine(PARGS pArgs);
// zpb_end

// Table Member Functions - MainTable1

MainTable1::MainTable1(zWindow *w, int ctrlId, zTableModel* m) : zTable(w, ctrlId, m) {
  // zpb_begin MainTable1InitList
  // zpb_end

  setLabelType(ZTLABEL_COLS, ZTLABEL_TEXTUAL);
  zResStrBlock colLabels(zResId(IDT_MAINTABLE1COLS));
  setLabelText(ZTLABEL_COLS, colLabels);
  enableColSizing(0);
  enableRowSizing(0);

  // zpb_begin pMainTable1Constructor1
  // zpb_end
  show();
}

zStatusMessage tbMainMainToolsMsgs[]={
  {IDM_FILENEW, "New"},
  {IDM_FILEOPEN, "Open"},
  {IDM_FILESAVE, "Save"},
  {IDM_FILEPRINT, "Print"},
  {IDM_EDITCUT, "Cut"},
  {IDM_EDITCOPY, "Copy"},
  {IDM_EDITPASTE, "Paste"},
  {IDM_HELPCONTENTS, "Help"},
  {0, 0}
};

// ToolBar Member Functions - MainTools
tbMainMainTools::tbMainMainTools(zWindow *w, zSizer *sz, zBitmap *bmp): zToolbar(w, sz) {
  theBmp = bmp;
  zToolButton *tbtn;
  tooltips = new zToolTipCtrl(this);
  tooltips->setTipMessageTable(tbMainMainToolsMsgs);
  tbtn = new zToolButton(this, new zSizer(zPoint(6,3), zDimension(24,22)), 0, IDM_FILENEW, theBmp, zRect(0, 0, 16, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(29,3), zDimension(24,22)), 0, IDM_FILEOPEN, theBmp, zRect(16, 0, 32, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(52,3), zDimension(24,22)), 0, IDM_FILESAVE, theBmp, zRect(32, 0, 48, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(75,3), zDimension(24,22)), 0, IDM_FILEPRINT, theBmp, zRect(48, 0, 64, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(107,3), zDimension(24,22)), 0, IDM_EDITCUT, theBmp, zRect(64, 0, 80, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(130,3), zDimension(24,22)), 0, IDM_EDITCOPY, theBmp, zRect(80, 0, 96, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(153,3), zDimension(24,22)), 0, IDM_EDITPASTE, theBmp, zRect(96, 0, 112, 15));
  tbtn->show();
  tooltips->add(tbtn);
  tbtn = new zToolButton(this, new zSizer(zPoint(185,3), zDimension(24,22)), 0, IDM_HELPCONTENTS, theBmp, zRect(112, 0, 128, 15));
  tbtn->show();
  tooltips->add(tbtn);
  // zpb_begin tbMainMainToolsConstructor
  // zpb_end
}

tbMainMainTools::~tbMainMainTools() {
  // zpb_begin tbMainMainToolsDestructor
  // zpb_end
  if (theBmp)
    delete theBmp;
}

//
// Frame Member Functions - Main
//
// Window Constructor
WMain::WMain(zWindow *w, const char *title): zAppFrame(w,new zSizer(),zSTDFRAME, title)
  // zpb_begin WMainInitList
  // zpb_end
{
  setFont(new zFont("Helv", zPrPoint(0, 8*10), FW_NORMAL));
  populateFromRes(zResId(IDW_Main));
  // zpb_begin WMainConstructor2
  // zpb_end
  menu(new zMenu(this, zResId(IDM_MainMenu1)));
  menu()->setCommand(this, (CommandProc)&WMain::cmdFileNew, IDM_FILENEW);
  menu()->setCommand(this, (CommandProc)&WMain::cmdFileOpen, IDM_FILEOPEN);
  menu()->setCommand(this, (CommandProc)&WMain::cmdFileSave, IDM_FILESAVE);
  menu()->setCommand(this, (CommandProc)&WMain::cmdFileSaveas, IDM_FILESAVEAS);
  menu()->setCommand(this, (CommandProc)&WMain::cmdFilePrint, IDM_FILEPRINT);
  menu()->setCommand(this, (CommandProc)&WMain::cmdFileExit, IDM_FILEEXIT);
  menu()->setCommand(this, (CommandProc)&WMain::cmdEditCut, IDM_EDITCUT);
  menu()->setCommand(this, (CommandProc)&WMain::cmdEditCopy, IDM_EDITCOPY);
  menu()->setCommand(this, (CommandProc)&WMain::cmdEditPaste, IDM_EDITPASTE);
  menu()->setCommand(this, (CommandProc)&WMain::cmdHelpContents, IDM_HELPCONTENTS);
  menu()->setCommand(this, (CommandProc)&WMain::cmdHelpAbout, IDM_HELPABOUT);
  // Create ToolBar
  pTB = new tbMainMainTools(this, new zGravSizer(ZGRAV_TOP, 30, sizer()), new zBitmap(zResId(IDB_MainMainTools)));
  pTB->show();
  pSB = new zStatusLineEZ(this, new zGravSizer(ZGRAV_BOTTOM,0, sizer()), ZSL_STANDARDITEM);
  pSB->show();
  zTableModel *modTable1 = new zTableSmallModel(5, 10);
  tblTable1 = new MainTable1(this, ID_TABLE1, modTable1);
  tblTable1->sizer()->replace(new zGravSizer(ZGRAV_MIDDLE, 0, sizer()));
  telTable1 = new zTableInplaceEditLine(tblTable1);
  sizer()->update();
  // zpb_begin WMainConstructor
  tblTable1->setLabelType(ZTLABEL_COLS,ZTLABEL_TEXTUAL);
  tblTable1->setLabelText(ZTLABEL_COLS,1,"Name");
  tblTable1->setLabelText(ZTLABEL_COLS,2,"EMail");
  new zTableTextCell(tblTable1->model(),"Bonsma",zTableLoc(1,1));
  new zTableTextCell(tblTable1->model(),"jrb1@bob",zTableLoc(2,1));
  new zTableTextCell(tblTable1->model(),"Briant",zTableLoc(1,2));
  new zTableTextCell(tblTable1->model(),"tazman@bob",zTableLoc(2,2));
  new zTableTextCell(tblTable1->model(),"McPherson",zTableLoc(1,3));
  new zTableTextCell(tblTable1->model(),"kwlm@bob",zTableLoc(2,3));

  Tbl_ptr = tblTable1;
  Status_ptr = pSB;
  // zpb_end
  show()
}

WMain::~WMain() {
  // zpb_begin WMainDestructor1
  // zpb_end
}

//
// Menu Item Selection Handlers
//

int WMain::cmdFileNew(zCommandEvt* ev) {
  // zpb_begin WMaincmdFileNew
  // zpb_end
  return 0;
}

int WMain::cmdFileOpen(zCommandEvt* ev) {
  // zpb_begin WMainFileOpenOpenDlg
  char *types[6];
  types[0] = "All Files (*.*)", types[1] = "*.*";
  types[2] = "Text Files (*.txt)", types[3] = "*.txt";
  types[4] = types[5] = 0;

  zFileOpenForm *p = new zFileOpenForm(this,"Open File", 0, types);
  if (p->completed()) {
    // Use p->name() to retrieve filename
  }
  delete p;
  //zpb_end
  // zpb_begin WMainFileOpen
  // zpb_end
  return 0;
}

int WMain::cmdFileSave(zCommandEvt* ev) {
  // zpb_begin WMaincmdFileSave
  // zpb_end
  return 0;
}

int WMain::cmdFileSaveas(zCommandEvt* ev) {
  // zpb_begin WMainFileSaveasSaveDlg
  char *types[6];
  types[0] = "All Files (*.*)", types[1] = "*.*";
  types[2] = "Text Files (*.txt)", types[3] = "*.txt";
  types[4] = types[5] = 0;

  zFileSaveAsForm *p = new zFileSaveAsForm(this,"Save File As", 0, types);
  if (p->completed()) {
    // Use p->name() to retrieve filename
  }
  delete p;
  //zpb_end

  // zpb_begin WMainFileSaveas
  // zpb_end
  return 0;
}

int WMain::cmdFilePrint(zCommandEvt* ev) {
  // zpb_begin WMainFilePrintPrintDlg
  zPrinterDisplay *pr=new zPrinterDisplay;
  if (!pr->isValid()) {
    zMessage msg(this,"No Printer drivers installed","Unable to print");
    return 1;
  }
  if (pr->printerSetup()) {
  }
  delete pr;
  //zpb_end

  // zpb_begin WMainFilePrint
  // zpb_end
  return 0;
}

int WMain::cmdFileExit(zCommandEvt* ev) {
  // zpb_begin WMainFileExit
  // zpb_end
  zAppGetAppVar(app)->quit();
  return 0;
}

int WMain::cmdEditCut(zCommandEvt* ev) {
  // zpb_begin WMaincmdEditCut
  // zpb_end
  return 0;
}

int WMain::cmdEditCopy(zCommandEvt* ev) {
  // zpb_begin WMaincmdEditCopy
  // zpb_end
  return 0;
}

int WMain::cmdEditPaste(zCommandEvt* ev) {
  // zpb_begin WMaincmdEditPaste
  // zpb_end
  return 0;
}

int WMain::cmdHelpContents(zCommandEvt* ev) {
  // zpb_begin WMaincmdHelpContents
  // zpb_end
  return 0;
}

int WMain::cmdHelpAbout(zCommandEvt* ev) {
  DAbout* p=new DAbout(this, zResId(IDD_About));
  p->modal();
  if (p->completed()) {   
    // zpb_begin WMainHelpAbout
    // zpb_end
  }
  else {
           // zpb_begin WMainHelpAboutCancel
           // zpb_end
  }
  delete p;
  return 0;
}

// zpb_begin WMainMemberFunctions
// zpb_end

//
// Dialog Member Functions - About
//
DAbout::DAbout(zWindow *w,const zResId& rid) : zFormDialog(w,rid)
  // zpb_begin DAboutInitList
  // zpb_end
{
  // zpb_begin DAboutConstructor2
  // zpb_end
  pOK = new zPushButton(this, IDOK);
  // zpb_begin DAboutConstructor
  // zpb_end
  centerWindow();
  show();
}

DAbout::~DAbout() {
  // zpb_begin AboutDestructor
  // zpb_end
}

// zpb_begin DAboutMemberFunctions
// zpb_end


// zpb_begin AppUserCode
// zpb_end

//
// Application Entry Point
//
void zApp::main() {
  initIntPack();
  // zpb_begin AppMain
  static BOOL fAgentRunning;
  // zpb_end
  WMain* p=new WMain(0, zResId(IDS_MAIN).loadStr());
  // zpb_begin AppMain2
  fAgentRunning = QAP_Initialize ();

  if (fAgentRunning) {
    QAP_RegisterClassFun ("ZappWin", "ReturnColLabels", zTab_ReturnColLabels, T_LIST_STRING, 2, P_IN | T_INTEGER, P_IN | T_INTEGER);
    QAP_RegisterClassFun ("ZappWin", "SelectTableRow", zTab_SelectTableRow, T_BOOLEAN, 3, P_IN | T_INTEGER, P_IN | T_STRING, P_IN | T_INTEGER);
    QAP_RegisterClassFun ("ZappWin", "ReturnTableRow", zTab_ReturnTableRow, T_LIST_STRING, 2, P_IN | T_INTEGER, P_IN | T_INTEGER);
    QAP_RegisterClassFun ("ZappWin", "ReturnStatusLine", zTab_ReturnStatusLine, T_STRING, 1, P_IN | T_INTEGER);
  }
  // zpb_end
  go();
  delete p;

  // zpb_begin AppMain3
  if (fAgentRunning) {
    QAP_Terminate ();
  }
  // zpb_end
}

void QAPFUNC zTab_ReturnStatusLine(PARGS pArgs) {
  const char* sRet;
  zStatusItem* SI_ptr;

  SI_ptr = Status_ptr->getItem(GetArg(0, lValue));
  sRet = SI_ptr->curMessage();
  QAP_ReturnString (RETVAL, (char *)sRet);
}

void QAPFUNC zTab_ReturnColLabels(PARGS pArgs) {
  const char* sRet;
  int i;

  QAP_ReturnListOpen(RETVAL);
        
  for(i = 1; i < GetArg(1, lValue)+1; i++) {
    sRet = Tbl_ptr->getLabelText(ZTLABEL_COLS, i);
    QAP_ReturnString (RETVAL, (char *)sRet);
  }

  QAP_ReturnListClose (RETVAL);
}

void QAPFUNC zTab_SelectTableRow(PARGS pArgs) {
  const char* sRet;
  zTableCell *cell;
  zTableLoc pos;
  zTableRegion dim;
  zTable* ptr;
  zTableModel* ptm;
  unsigned long i, tlen, col;
  unsigned int iPass;

  switch (GetArg(0, lValue)) {
    case 1: ptr = Tbl_ptr;
            break;
    default:
      QAP_ReturnBoolean (RETVAL, 0);
      return;
  }

  ptm = ptr->model();
  col = GetArg(2, lValue);
  dim = ptm->getDimension();
  tlen = dim.height();
  tlen += 2;
  iPass = 0;
  for (i = 1; i < tlen; i++) {
    pos = zTableLoc(col,i);
    cell = ptm->getCell(pos);
    if (cell) {
      sRet = cell->getText();
      if (!strcmp(sRet, GetArg(1, pszValue))) {
        ptr->setSelection(ZTLABEL_ROWS, i);
        iPass = 1;
        break;
      }
    }
    else {
      break;
    }
  }

  QAP_ReturnBoolean (RETVAL, iPass);
}

void QAPFUNC zTab_ReturnTableRow(PARGS pArgs) {
  const char* sRet;
  zTableCell *cell;
  zTableLoc pos;
  zTableRegion dim;
  zTable* ptr;
  zTableModel* ptm;
  unsigned long i, row, cols;

  switch (GetArg(0, lValue)) {
    case 1: ptr = Tbl_ptr;
            break;
    default:
      return;
  }

  ptm = ptr->model();
  cols = GetArg(1, lValue);
  dim = ptm->getDimension();

  ptr->getSelection(dim);
  row = dim.top();
        
  QAP_ReturnListOpen(RETVAL);
        
  if (row > 0) {
    for (i = 1; i < cols+1; i++) {
      pos = zTableLoc(i, row);
      cell = ptm->getCell(pos);
      if (cell) {
        sRet = cell->getText();
        QAP_ReturnString(RETVAL, (char *)sRet);
      }
      Else {
        QAP_ReturnString(RETVAL, "");
      }
    }
  }
  else {
    QAP_ReturnNull(RETVAL);
  }
        
  QAP_ReturnListClose (RETVAL);
}

4Test Source Code

zapp.inc

winclass ZappWin : MoveableWin
{
  extern LIST OF ANYTYPE ReturnColLabels (INTEGER iWin, INTEGER iCols);
  extern BOOLEAN SelectTableRow (INTEGER iWin, STRING sVal, INTEGER iCol);
  extern LIST OF ANYTYPE ReturnTableRow (INTEGER iWin, INTEGER iCols);
  extern STRING ReturnStatusLine (INTEGER iStatusItemText);
}

window ZappWin ExtensionApplication
{
  tag "[MainWin]Extension Application";

  CustomWin PaneWindowCL1
  {
    msw tag "[PaneWindowCL]#1";
    CustomWin VisualCtrl1
    {
      msw tag "[VisualCtrl]#1";
    }
  }
}

zapp.t

use "zapp.inc";

main ()
{
   INTEGER iSIHelp = 0;
   INTEGER iSIClock = -1;
   INTEGER iSelCol = 1;
   INTEGER iNumCols = 2;
   INTEGER iWin = 1;

   STRING sSearchValue = "Briant";

   LIST OF STRING lsRowContents;

   ExtensionApplication.SetActive ();
   print ("Status Line Clock = ", ExtensionApplication.ReturnStatusLine (iSIClock));
   ExtensionApplication.PaneWindowCL1.VisualCtrl1.MoveMouse (0, 0);
   print ("Status Line Help = ", ExtensionApplication.ReturnStatusLine (iSIHelp));
   print ("Table Column Labels = ", ExtensionApplication.ReturnColLabels (iWin, iNumCols));
   ExtensionApplication.SelectTableRow (iWin, sSearchValue, iSelCol);
   lsRowContents = ExtensionApplication.ReturnTableRow (iWin, iNumCols);
   print ("Name = {lsRowContents[1]}, EMail = {lsRowContents[2]}");
   ExtensionApplication.SetActive ();
}

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *