################################################################################ # TEST: EMOS_FRM_driver_lib ################################################################################ # Copyright (C) 2000 EMOS Computer Consulting GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # For further information please contact: # # Dean Rajovic # EMOS Computer Consulting GmbH # Oskar-Messter-Straße 25 # 85737 Ismaning # Germany # tel.: +49 89 608 765-0 # mailto:drajovic@emos.de # http://www.emos.de ################################################################################ # $Revision: 1.8 $ # $Author: drajovic $ # $Date: 2005/01/27 08:53:54 $ # $Source: C:/Archive/FRAMEWORK/EMOS_GPL/FRM/emos_frm_driver_lib/script,v $ # $NoKeywords: $ ################################################################################ #**# #* The library routines for the EMOS_FRM_driver test. #*/ static const PREFIX = "IDX:"; static const GER_COLUMNS = "Bearbeiten?,Testscript,Testtabelle,Testset"; static const GER_ERROR_MODES = "1 - Weiter,2 - Test anhalten,3 - Testset anhalten,4 - Testsuite anhalten,5 - Block wiederholen"; #* Properties (parameters that can be set/get) static script_home; static data_home; static default_suite_table; static ask = FALSE; static columns = GER_COLUMNS; static new_test_driver = FALSE; static fail_on_unknown_test = FALSE; static def_error_mode = E_FRM_CONTINUE; static error_modes = GER_ERROR_MODES; #/** #* Home directory for test scripts #*/ public function FRM_DRV_set_script_home ( in dir ) { script_home = dir; } #/** #* Home directory for test data #*/ public function FRM_DRV_set_data_home ( in dir ) { data_home = dir; } #/** #* Name of the test suite table #*/ public function FRM_DRV_set_default_suite_table ( in name ) { default_suite_table = name; } #/** #* Set routine for activating the new test driver logic #*/ public function FRM_DRV_set_new_test_driver ( in test_driver ) { new_test_driver = test_driver; } #/** #* Get routine for activating the new test driver logic #*/ public function FRM_DRV_is_new_test_driver ( ) { return new_test_driver; } #/** #* Turns failure on unknown test on/off [default: FALSE] #* @param mode (in) TRUE raises an error by FRM_DRV_test_set_driver() #* if an unknown test (i.e. nonexisting column) is called, FALSE doesn't #*/ public function FRM_DRV_set_fail_on_unknown_test ( in mode ) { fail_on_unknown_test = mode; } #/** #* Get routine for activating activating failure on unknown test #*/ public function FRM_DRV_is_fail_on_unknown_test ( ) { return fail_on_unknown_test; } #/** #* Sets the default error mode. This value is evaluated by the emos driver in #* in order to determine how to proceed after an error has been detected in #* some test block. In interactive mode a dialog is displayed where you can #* interactively choose the desired behaviour. In batchmode or if you choose #* "Cancel" in interactive mode the default mode which is set here is applied. #* By default the <code>E_FRM_CONTINUE</code> is used (which means that #* no test block will be skipped due to errors). #* @param mode (in) possible values <code>E_FRM_TEST_STOP, E_FRM_SET_STOP, #* E_FRM_SUITE_STOP, E_FRM_CONTINUE</code> if eny other value was passed #* (including <code>E_FRM_RETRY</code>) the default mode will be set to #* <code>E_FRM_CONTINUE</code>. #* @param eror_modes (in) String list defining mode names #* <pre>[default:"1 - Weiter,2 - Test anhalten,3 - Testset anhalten,4 - Testsuite anhalten,5 - Block wiederholen"]</pre> #* Make sure to preserve the numbers, the meaning of modes and no blanks after comma! #* @return the mode actually set (note it may be different from the wanted one!) #*/ public function FRM_DRV_set_default_error_mode ( in mode, in mode_list ) { if ( mode_list != "" ) error_modes = mode_list; switch( mode ) { case E_FRM_TEST_STOP: case E_FRM_SET_STOP: case E_FRM_SUITE_STOP: def_error_mode = mode; break; default: def_error_mode = E_FRM_CONTINUE; } return def_error_mode; } #/** #* TRUE: gives you the option to choose the alternative suite table; #* FALSE: opens the defined table only [default] #*/ public function FRM_DRV_set_ask ( in par ) { ask = par; } #/** #* Comma-separated string defining the #* titles of the four important columns #* [default: <pre>"Bearbeiten?,Testscript,Testtabelle,Testset[,Kommentar]"</pre>] #*/ public function FRM_DRV_set_columns ( in cols ) { columns = cols; } #** #* The main loop processes all rows in the suite table. #* @param arg1 home directory for test scripts #* @param arg2 home directory for test data #* @param arg3 name of the test suite table #* @param arg4 TRUE: gives you the option to choose #* alternative suite table; FALSE: opens the defined table only [default] #* @param arg5 comma-separated string defining the #* titles of the four important columns #* [default: <pre>"Bearbeiten?,Testscript,Testtabelle,Testset[,Kommentar]"</pre>] #* @return #* E_OK: success #* !E_OK: failure #*/ #public function FRM_DRV_main( in script_home, in data_home, in default_suite_table, in ask, in columns ) public function FRM_DRV_main( in arg1, in arg2, in arg3, in arg4, in arg5 ) { auto suite_table, stid, cols[], rc, rows; # these ifs are just for backward compatibility (when function arguments were not optional) if ( arg1 != "" ) FRM_DRV_set_script_home( arg1 ); if ( arg2 != "" ) FRM_DRV_set_data_home( arg2 ); if ( arg3 != "" ) FRM_DRV_set_default_suite_table( arg3 ); if ( arg4 != "" ) FRM_DRV_set_ask( arg4 ); if ( arg5 != "" ) FRM_DRV_set_columns( arg5 ); # column titles can be dynamically defined via columns # by default they are in german (backwards compatibility) if ( columns == "" ) columns = GER_COLUMNS; # sanity check that we have at lest four cols if ( split( columns, cols, "," ) < 4 ) { tl_step( "columns", FAIL, "invalid parameter" ); return E_ILLEGAL_PARAMETER; } # find out which table is to be used if( fail( FRM_DRV_choose_table( data_home, default_suite_table, ask, suite_table ) ) ) return getLastRc(); # ask whether retry of the run is wanted FRM_DRV_ask_retry(); # forget the garbage from the previous run FRM_close_all(); # open table and init iterator if( fail( FRM_open( suite_table, columns, stid ) ) ) return getLastRc(); if( fail( FRM_get_row_count( stid, rows ) ) ) return getLastRc(); # <suite_table> must stay opened # see below FRM_close_all_except rc = FRM_DRV_test_suite_driver(script_home, data_home, stid, cols, rows ); FRM_close_all( ); return rc; } #** #* Picks the appropriate file. If <ask> = TRUE [defult], then a #* file browse dialog is shown to interactively pick the file. In batch #* mode, the dialog is not shown. #*@param dir (in) directory #*@param name (in) file name #*@param ask (in) (optional) TRUE/FALSE [default: TRUE] #*@param path (out) full path name to be used #*@return #* E_OK: success #* !E_OK: failure #*/ static function FRM_DRV_choose_table ( in dir, in name, in ask, out path ) { auto rc = E_OK; if ( ask || ask == "" ) { if ( fail( DDT_choose_table( join_path( dir, name ), dir, name ) ) ) return getLastRc(); } path = join_path( dir, name, "\\" ); return E_OK; } #** #* Waits for the user input (an edit field and OK/Cancel buttons) if a table #* cell contains a specielly formatted data. The data is "specially formatted" #* if it either begins with the question mark or with some other string as #* defined by <code>flag</code> parameter. #* @param tid (in) table ID #* @param param (in) column name #* @param msg (in) message to be displayed #* @param val (out) value choosen (empty string indicates Cancel) #* @param flag (in) (optional) prefix which indicates the "askable" data [default: ?] #* @return #* E_OK: success; <code>val</code> contains valid data #* !E_OK: failure; do not rely on <code>val</code> #*/ static function FRM_DRV_ask ( in tid, in param, in msg, out val, in flag ) { auto rc, len; if ( flag == "" ) flag = "?"; len = length( flag ); if ( fail( FRM_get( tid, param, val ) ) ) return getLastRc(); if ( length( val ) < len ) return E_OK; if ( substr( val, 1, len ) == flag ) { val = substr( val, len+1 ); rc = create_input_dialog( msg & ": " & val ); if ( rc != "" ) val = rc; } return E_OK; } #** #* execute a test suite excel table. #* @param script_home home directory for test scripts #* @param data_home home directory for test data #* @param stid suite_table ID ( as returned by FRM_open() ) #* @param cols #* @param rows #*/ static function FRM_DRV_test_suite_driver( in script_home, in data_home, in stid, inout cols[], in rows ) { auto data_table, xtabs[]; auto table, script, testset, comment; auto doit; auto row; auto i; auto rc; auto suite_rc; rc = E_OK; suite_rc = E_OK; xtabs[stid] = FRM_get_name( stid ); wrlog_suite_start( xtabs[stid] ); # iterate through all rows DDT_ACCESS_init( stid, "1-"& rows ); while( DDT_ACCESS_has_more( stid ) ) { # fetch test-set data from excel table row = DDT_ACCESS_get_next( stid ); rc = FRM_set_row( stid, row ); if ( rc != E_OK ) { suite_rc += rc; break; } rc = FRM_get( stid, cols[1], doit ); rc+= FRM_get( stid, cols[2], script ); rc+= FRM_get( stid, cols[3], table ); rc+= FRM_get( stid, cols[4], testset ); FRM_get( stid, cols[5], comment ); # comment column is not mandatory if ( rc != E_OK ) { report_msg( "Row " & row & " ignored." ); continue; } rc+= FRM_DRV_ask( stid, cols[1], "("&table&": "&testset&")? ", doit ); if ( rc != E_OK ) { suite_rc += rc; break; } if ( !Ja( doit ) ) { report_msg( "Row " & row & " skipped." ); continue; } table = join_path( data_home, table, "\\" ); #script = join_path( script_home, script ); rc = FRM_DRV_ask( stid, cols[4], cols[4], testset ); if ( rc != E_OK ) { suite_rc += rc; break; } report_msg( cols[2] & ": " & script ); report_msg( cols[3] & ": " & table ); report_msg( cols[4] & ": " & testset ); if ( comment != "" ) { report_msg( cols[5] & ": " & comment ); } # process test set rc = FRM_DRV_test_set_driver(script, table, testset); suite_rc += rc; FRM_close_all_except( xtabs ); } DDT_ACCESS_clean( stid ); wrlog_suite_stop( xtabs[stid], suite_rc ); return suite_rc; } #** #* Executes a set of tests from a specified data table by the specified script. #* @param script (in) test driver (full path or accessable vie search path) #* @param table (in) data table (full path name!) #* @param testset (in) names of the tests to be executed (individual entries #* are comma-separated; numeric ranges can be shortend by hyphen, e.g. 1-5 which #* stands for 1,2,3,4,5; ranges can also be defined within name spaces and are #* similarly shortened, e.g. a1-3 stands for a1,a2,a3) #*/ public function FRM_DRV_test_set_driver(in script, in table, in testset, in comment ) { auto test, tid; auto via_IDX; auto expr, msg; auto rc, set_rc; extern RLENGTH; rc = E_OK; set_rc = E_OK; wrlog_set_start( testset ); if ( comment != "" ) { wrlog_set_data( "DESCRIPTION", comment ); } # NOTE! # If testset begins with "IDX:", it indicates that test # are defined accross rows instead of column-wise. # For this to work correctly ALL collumns have to be # loaded at once. Normally, only the columns specified # by the testset are loaded. via_IDX = ( substr( testset, 1, length( PREFIX ) ) == PREFIX); if ( via_IDX ) { testset = substr( testset, length(PREFIX)+1 ); rc = FRM_open( table, "<<ALL>>", tid ); } else { rc = FRM_open( table, testset, tid ); } if ( rc != E_OK ) { wrlog_set_stop( testset, rc ); return rc; } DDT_ACCESS_init( table, testset ); while ( DDT_ACCESS_has_more( table ) ) { rc = E_OK; test = DDT_ACCESS_get_next( table ); if ( via_IDX || FRM_is_parameter( tid, test ) == E_OK ) { wrlog_test_start( test, table, script ); if ( FRM_DRV_is_retry() && FRM_DRV_retry_lookup_log( table, test ) == 0 ) { report_msg( "Test: " & test & "\t--> won't be retried" ); rc = 0; } else { FRM_DRV_retry_log_test1( table, test ); report_msg( "Test: " & test & "\t--> is being processed" ); if ( FRM_DRV_is_new_test_driver() ) { if ( isCompiledModule( script & "_lib" ) || isCompiledModule( script ) ) { expr = sprintf( "treturn call_close \"DRV/emos_test_driver\" ( \"%s\", \"%s\", \"%s\" );", script, tid, test ); } else { expr = sprintf( "treturn call_close \"%s\" ( \"%s\", \"%s\", TRUE );", script, tid, test ); } } else { expr = sprintf( "treturn call_close \"%s\" ( \"%s\", \"%s\", TRUE );", script, tid, test ); } rc = eval( expr ); tl_step( test, rc, script & " returned rc=[" & rc & "]" ); FRM_DRV_retry_log_test2( rc ); set_rc += rc; if ( rc == E_OK ) FRM_RES_add_test_statistics("# of successful tests" ); else FRM_RES_add_test_statistics("# of failed tests" ); } wrlog_test_stop( test, rc ); } else { wrlog_test_start( test, table, script ); if ( fail_on_unknown_test ) { rc = E_NOT_FOUND; msg = sprintf( "Test \"%s\" not found", test ); tl_step( test, rc, msg ); } else { rc = ""; # empty string is shown as ? in ttracx msg = sprintf( "Test \"%s\" --> UNKNOWN, ignored", test ); report_msg( msg ); } wrlog_test_data( "MSG", msg ); wrlog_test_stop( test, rc ); set_rc += rc; FRM_RES_add_test_statistics("# of ignored/unknown tests" ); } if ( rc == E_FRM_SET_STOP || rc == E_FRM_SUITE_STOP ) { DDT_ACCESS_clean( table ); wrlog_set_stop( testset, rc ); return rc; } } DDT_ACCESS_clean( table ); wrlog_set_stop( testset, set_rc ); return set_rc; } #/** #* The main lop for driving the test navigation. #* @param lib (in) library that contains application-specific test navigation functions #* @param tid (in) table ID of the Excel table that contains the sepcified <code>test</code> #* @param test (in) name of the test (i.e. column) that is to be processed #* @return E_OK if successfull, else failure #*/ public function FRM_DRV_test_driver ( in lib, in tid, in test ) { auto step; auto mode; auto step_rc; auto rc; auto i; rc = load_drv_lib( lib, 0, 1 ); if ( rc != E_OK ) return rc; rc+= AUT_DRV_report ( tid, test ); rc+= AUT_DRV_load ( tid, test ); rc+= AUT_DRV_init_steps( tid, test ); if ( rc != E_OK ) { return rc; } step_rc = E_OK; while( FRM_STP_has_more_steps( tid, test ) ) { rc = FRM_STP_get_next_step( tid, test, step, mode ); if ( rc == E_FILE_EOF ) { break; } if ( rc == E_FRM_TEST_STOP || rc == E_FRM_SET_STOP || rc == E_FRM_SUITE_STOP ) { return rc; } if ( rc != E_OK ) { step_rc++; continue; } do { rc = AUT_DRV_call_block( tid, test, step, mode ); if ( rc == E_FRM_NOT_IMPLEMENTED ) { step_rc++; tl_step( step, rc, "Test block [" & step & "] not implemented yet" ); wrlog_block_stop ( step, "unimplemented" ); } else if ( rc == E_FRM_UNKNOWN ) { step_rc++; tl_step( step, rc, "Test block [" & step & "] unknown" ); wrlog_block_stop ( step, "unknown" ); } else { rc = FRM_DRV_handle_processed_block( step, test, rc, step_rc ); switch( rc ) { case E_FRM_TEST_STOP: case E_FRM_SET_STOP: case E_FRM_SUITE_STOP: return rc; } } } while ( rc == E_FRM_RETRY ); } return step_rc; } #/** #* Formats the report for unimplemented test blocks. Note that this function #* increases the inout variable <code>frm_rc</code> by 1. #* @param block (in) the name of the test block #* @param frm_rc (inout) the status variable (increased by 1 upon the exit) #*/ public function FRM_DRV_handle_unimplemented_block( in block, inout frm_rc ) { frm_rc++; tl_step( block, frm_rc, "Test block [" & block & "] not implemnted" ); wrlog_block_stop ( block, "unimplemented" ); return frm_rc; } #/** #* Formats the report for unknown test blocks. Note that this function #* increases the inout variable <code>frm_rc</code> by 1. #* @param block (in) the name of the test block #* @param frm_rc (inout) the status variable (increased by 1 upon the exit) #*/ public function FRM_DRV_handle_unknown_block( in block, inout frm_rc ) { frm_rc++; tl_step( block, frm_rc, "Test block [" & block & "] unknown" ); wrlog_block_stop ( block, "unknown" ); } #/** #* Formats the report for procesed test blocks. Note that this function #* increases the inout variable <code>frm_rc</code> by 1 in case of <code>rc!=0</code>. #* @param block (in) the name of the test block #* @param test (in) the name of the test case #* @param rc (in) status returned by the processed test block #* @param frm_rc (in) the current value of the status variable #* @return 0 to continue with the test case, 1 to stop the execution of the test case #*/ public function FRM_DRV_handle_processed_block( in block, in test, in rc, inout frm_rc ) { auto rc2; wrlog_block_stop ( block, rc ); if ( rc != E_OK ) { tl_step( block, rc, "Test block=[" & block & "], rc=[" & rc & "]" ); rc2 = create_list_dialog( "Test block failed", block, error_modes ); # "1 - Continue,2 - Stop test,3 - Stop test set,4 - Stop test suite,5 - Retry block" switch( substr( rc2, 1, 1 ) ) { case 1: frm_rc = rc; break; case 2: frm_rc = E_FRM_TEST_STOP; break; case 3: frm_rc = E_FRM_SET_STOP; break; case 4: frm_rc = E_FRM_SUITE_STOP; break; case 5: frm_rc = E_FRM_RETRY; break; default: # either batch mode or Cancel frm_rc = (def_error_mode == E_FRM_CONTINUE) ? rc : def_error_mode; } return frm_rc; } return E_OK; } #/** #* Loads driver libs. #* <p>NOTE1<br> #* Each invocation of LINK|LINA|LINX instruction will cause a new "reload" #* of the specified driver library. This is necessary in order to ensure #* proper loading when multiple divers are used (big projects tend to use #* this strategy). If you want to skeep such unecessary reloading, you may #* remove the name of the driver from the LINK commands wherever you are #* sure that the proper driver has already been loaded, for example #* <pre> #* LINK~drv/xxx~~1_1 #* LINK~~~1_2 #* LINK~~~1_3 #* </pre> #* <p>NOTE2<br> #* For compatibility reasons with older projects you should name your new #* driver libraries using the "_lib" suffix. If you preserve the rest of #* the driver name (e.g. if "DRV/xxx" is changed to "DRV/xxx_lib", then you #* need not modify anything in your Excel tables because this routine will #* automatically append "_lib" to all names that do not use this suffix. #*/ static function load_drv_lib ( in lib, in p1, in p2 ) { auto rc; if ( lib == "" ) return E_OK; if ( isCompiledModule( lib ) ) return reload( lib, p1, p2 ); else return reload( lib & "_lib", p1, p2 ); } #/** #* Executes several test sets as defined by the parameter <code>callArr[]</code>. #* <p>NOTE1<br> #* Input array must be a two-dimensional array. First dimension must be sequentially #* indexed from 0 onwards. The second dimension must be sequentially numbered from #* 0 onwards where 0 holds "test driver name", 1 hodls "test table name", and 2 holds #* "test set name". The easiest way to initialise such array in the callong script #* is via the following construct: #* <pre> #* static callArr[] = { #* { "drv/main", "table1.xls", "1-5" } #* ,{ "drv/main", "table2.xls", "1-3" } #* ,{ "drv/xxxx", "table2.xls", "3-9" } #* }; #* </pre> #* @param callArr (inout) array contianing test set instructions (see above) #* @return E_OK if success else error #*/ public function FRM_DRV_execute_set_array( inout callArr[] ) { extern DATA_HOME; auto rc, rc2; auto driver, table, set; auto i, count=0; for (i in callArr ) count++; if ( count%3 != 0 ) { # malformed input array (# of elements not the multipe of 3) return E_ILLEGAL_PARAMETER; } count/=3; rc2 = 0; FRM_close_all(); for (i=0; i<count; i++) { driver = callArr[i,0]; table = callArr[i,1]; set = callArr[i,2]; wrlog_set_start( set ); if ( driver == "" || callArr[i,1] == "" || set == "" ) rc = E_ILLEGAL_PARAMETER; else rc = FRM_DRV_test_set_driver( driver, table, set ); wrlog_set_stop( set, rc ); if( rc == E_FRM_SET_STOP || rc == E_FRM_SUITE_STOP ) { rc2 = rc; break; } rc2 += rc; } FRM_close_all(); return rc2; }