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 (); }