/lib/functions/testcase.class.php
PHP | 4679 lines | 2534 code | 517 blank | 1628 comment | 280 complexity | 85cec307c4494274c6a143574811ca1f MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * TestLink Open Source Project - http://testlink.sourceforge.net/
- * This script is distributed under the GNU General Public License 2 or later.
- *
- * @package TestLink
- * @author Francisco Mancardi (francisco.mancardi@gmail.com)
- * @copyright 2005-2009, TestLink community
- * @version CVS: $Id: testcase.class.php,v 1.295 2010/08/31 20:07:11 franciscom Exp $
- * @link http://www.teamst.org/index.php
- *
- * @internal Revisions:
- *
- * 20100831 - franciscom - BUGID 3729 - get_by_name()
- * 20100825 - franciscom - BUGID 3702 - _blind_delete() issue
- * 20100821 - franciscom - BUGID 3695 - Test Case Steps - Export/Import - missing attribute execution type
- * create_step() - fixed issue when execution_type was NULL.
- * new method - update_tcversion_steps() needed for BUGID 3634
- *
- * 20100814 - franciscom - getInternalID() - removed unused code and minor code rearrangement
- * changes in returned value when internal ID can not be found.
- * 20100813 - asimon - deactivated last slash on full path in get_assigned_to_user()
- * to remove it from test suite name in "tc assigned to user" tables
- * 20100802 - asimon - BUGID 3647 - filtering by build id in get_assigned_to_user()
- * 20100731 - asimon - more modifications to get_assigned_to_user()
- * 20100722 - asimon - BUGID 3406 - modified statement to get build name in get_assigned_to_user()
- * 20100714 - Julian - BUGID 3575 - get_assigned_to_user() added priority in output set
- * 20100712 - asimon - inserted missing semicolon after break in get_assigned_to_user()
- * 20100711 - franciscom - BUGID 3575 - get_assigned_to_user() added $filters as optional arg
- * 20100708 - franciscom - BUGID 3575 - get_assigned_to_user() add plaftorm in output set
- * 20100706 - franciscom - BUGID 3573 - _blind_delete() with alias has problems with MySQL
- * 20100521 - franciscom - BUGID 3481 - copy_tcversion() - preconditions are not copied
- * 20100516 - franciscom - BUGID 3465: Delete Test Project - User Execution Assignment is not deleted
- * 20100514 - franciscom - get_by_id() interface changes and improvements
- * 20100503 - franciscom - create_tcase_only() - BUGID 3374
- * 20100502 - franciscom - show() fixed error due to non existent variable $info
- * 20100417 - franciscom - new method - filter_tcversions()
- * get_last_active_version() - changes on output data
- * 20100411 - franciscom - BUGID 3387 - changes in show()
- * 20100411 - franciscom - new methods: get_last_active_version(),filter_tcversions_by_exec_type()
- * 20100409 - franciscom - BUGID 3367: Error after trying to copy a test case that the name is in the size limit.
- * 20100330 - eloff - BUGID 3329 - fixes test plan usage with platforms
- * 20100323 - asimon - fixed BUGID 3316 in show()
- * 20100317 - franciscom - new method get_by_external()
- * 20100315 - amitkhullar - Added options for Requirements and CFields for Export.
- * 20100309 - franciscom - get_by_id() - improvements on control to apply when LATEST_VERSION is requested.
- * 20100309 - franciscom - get_exec_status() - interface changes
- * get_linked_versions() - interface changes
- * BUGID 0003253
- *
- * 20100301 - franciscom - changes on show() to solve
- * BUGID 3181: From test case specification, after adding the test case
- * to test a plan with platforms, platforms are not displayed
- *
- * 20100210 - franciscom - keywords XML export refactored
- * 20100204 - franciscom - copyKeywordsTo(),copyReqAssignmentTo() - interface changes
- * 20100201 - franciscom - getExternalID(), refactored to improve performance when used on loops
- * 20100124 - franciscom - BUGID 3090 - problems when trying to delete a test case that has 0 steps.
- * 20100111 - franciscom - get_version_exec_assignment() - refactoring due to platforms feature.
- * get_linked_versions() - refactoring due to platforms feature.
- * 20100107 - franciscom - Multiple Test Case Steps Feature
- * Affected methods: delete(), _blind_delete()
- *
- * 20100106 - franciscom - Multiple Test Case Steps Feature
- * Affected methods: get_by_id(), create(), update()
- * get_last_version_info(), get_linked_versions(), copy_to()
- * copy_tcversion(),exportTestCaseDataToXML()
- *
- * 20100105 - franciscom - fixed missing copy of preconditions on copy_tcversion()
- * exportTestCaseDataToXML() - added execution_type, importance
- *
- * 20100104 - franciscom - create_new_version() - interface changes
- * new method get_basic_info()
- * fixed bug in show() regarding $gui->can_do->add2tplan
- * get_last_version_info() - interface changes
- * 20100103 - franciscom - getPrefix() - interface changes & refactoring
- * new methods - buildDirectWebLink(), getExternalID()
- * 20091229 - eloff - BUGID 3021 - getInternalID() - fixed error when tc prefix contains glue character
- * 20091220 - franciscom - copy_attachments() refactoring
- * 20091217 - franciscom - getDuplicatesByName() - new argument added
- * 20091215 - franciscom - getPrefix() - changed in return type, to avoid in some situations
- * a double call.
- * 20091207 - franciscom - get_last_execution() - internal bug
- * 20091127 - franciscom - getByPathName() new method
- * 20091118 - franciscom - get_last_execution() - still working ond fixing bug when using self::ALL_VERSIONS
- * 20091113 - franciscom - get_last_execution() - fixed bug when using self::ALL_VERSIONS
- * 20091003 - franciscom - show() changes in template get logic
- * 20090927 - franciscom - new methods: getPathLayered(),getPathTopSuite()
- * 20090922 - franciscom - get_last_execution() - used COALESCE() to return code
- * also code for NOT RUN status.
- * 20090831 - franciscom - added management of new field: preconditions
- * create(),update(),exportTestCaseDataToXML()
- *
- * 20090815 - franciscom - get_executions() - added platform related info
- * interface changes.
- * get_last_execution() - added platform related info
- *
- * 20090720 - franciscom - found bug in get_linked_cfields_at_execution()
- * when calling cfield_mgr class method
- *
- * 20090718 - franciscom - new method buildCFLocationMap();
- * 20090716 - franciscom - get_last_execution() - BUGID 2692 - interface changes.
- * 20090713 - franciscom - solved bug on get_executions() (bad SQL statement).
- * 20090530 - franciscom - html_table_of_custom_field_inputs() changes in interface
- * 20090526 - franciscom - html_table_of_custom_field_values() - added scope 'testplan_design'
- * 20090521 - franciscom - get_by_id() added version_number argument
- * 20090419 - franciscom - BUGID 2364 - show() changes on edit enabled logic
- * 20090414 - franciscom - BUGID 2378
- * 20090401 - franciscom - BUGID 2316 - changes to copy_to()
- * 20090308 - franciscom - BUGID 2204 - create() fixed return of new version number
- * 20090220 - franciscom - BUGID 2129
- * 20090106 - franciscom - BUGID - exportTestCaseDataToXML() - added export of custom fields values
- * 20081103 - franciscom - new method setKeywords() - added by schlundus
- * removed useless code from getTestProjectFromTestCase()
- * 20081015 - franciscom - delete() - improve controls to avoid bug if no children
- * 20080812 - franciscom - BUGID 1650 (REQ)
- * html_table_of_custom_field_inputs() interface changes
- * to manage custom fields with scope='testplan_design'
- *
- * 20080602 - franciscom - get_linked_versions() - internal changes due to BUG1504
- * get_exec_status() - interface and internal changes due to BUG1504
- *
- * 20080126 - franciscom - BUGID 1313
- */
-
- /** related functionality */
- require_once( dirname(__FILE__) . '/requirement_mgr.class.php' );
- require_once( dirname(__FILE__) . '/assignment_mgr.class.php' );
- require_once( dirname(__FILE__) . '/attachments.inc.php' );
- require_once( dirname(__FILE__) . '/users.inc.php' );
-
- /** list of supported format for Test case import/export */
- $g_tcFormatStrings = array ("XML" => lang_get('the_format_tc_xml_import'));
-
- /**
- * class for Test case CRUD
- * @package TestLink
- */
- class testcase extends tlObjectWithAttachments
- {
- const AUTOMATIC_ID=0;
- const DEFAULT_ORDER=0;
- const ALL_VERSIONS=0;
- const LATEST_VERSION=-1;
- const AUDIT_OFF=0;
- const AUDIT_ON=1;
- const CHECK_DUPLICATE_NAME=1;
- const DONT_CHECK_DUPLICATE_NAME=0;
- const ENABLED=1;
- const ALL_TESTPLANS=null;
- const ANY_BUILD=null;
- const GET_NO_EXEC=1;
- const ANY_PLATFORM=null;
-
-
-
- /** @var database handler */
- var $db;
- var $tree_manager;
- var $tproject_mgr;
-
- var $node_types_descr_id;
- var $node_types_id_descr;
- var $my_node_type;
-
- var $assignment_mgr;
- var $assignment_types;
- var $assignment_status;
-
- var $cfield_mgr;
-
- var $import_file_types = array("XML" => "XML", "XLS" => "XLS" );
- var $export_file_types = array("XML" => "XML");
- var $execution_types = array();
-
- /**
- * testplan class constructor
- *
- * @param resource &$db reference to database handler
- */
- function __construct(&$db)
- {
- $this->db = &$db;
- $this->tproject_mgr = new testproject($this->db);
- $this->tree_manager = &$this->tproject_mgr->tree_manager;
-
- $this->node_types_descr_id=$this->tree_manager->get_available_node_types();
- $this->node_types_id_descr=array_flip($this->node_types_descr_id);
- $this->my_node_type=$this->node_types_descr_id['testcase'];
-
- $this->assignment_mgr=New assignment_mgr($this->db);
- $this->assignment_types=$this->assignment_mgr->get_available_types();
- $this->assignment_status=$this->assignment_mgr->get_available_status();
-
- $this->cfield_mgr = new cfield_mgr($this->db);
-
- $this->execution_types = array(TESTCASE_EXECUTION_TYPE_MANUAL => lang_get('manual'),
- TESTCASE_EXECUTION_TYPE_AUTO => lang_get('automated'));
-
- // ATTENTION:
- // second argument is used to set $this->attachmentTableName,property that this calls
- // get from his parent
- parent::__construct($this->db,"nodes_hierarchy");
- }
-
-
- /*
- function: get_export_file_types
- getter
-
- args: -
-
- returns: map
- key: export file type code
- value: export file type verbose description
-
- */
- function get_export_file_types()
- {
- return $this->export_file_types;
- }
-
- /*
- function: get_impor_file_types
- getter
-
- args: -
-
- returns: map
- key: import file type code
- value: import file type verbose description
-
- */
- function get_import_file_types()
- {
- return $this->import_file_types;
- }
-
- /*
- function: get_execution_types
- getter
-
- args: -
-
- returns: map
- key: execution type code
- value: execution type verbose description
-
- */
- function get_execution_types()
- {
- return $this->execution_types;
- }
-
-
- /**
- * create a test case
- */
- function create($parent_id,$name,$summary,$preconditions,$steps,$author_id,
- $keywords_id='',$tc_order=self::DEFAULT_ORDER,$id=self::AUTOMATIC_ID,
- $execution_type=TESTCASE_EXECUTION_TYPE_MANUAL,
- $importance=2,$options=null)
- {
- $status_ok = 1;
-
- $my['options'] = array( 'check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
- 'action_on_duplicate_name' => 'generate_new');
- $my['options'] = array_merge($my['options'], (array)$options);
-
- $ret = $this->create_tcase_only($parent_id,$name,$tc_order,$id,$my['options']);
- if($ret["status_ok"])
- {
- if(trim($keywords_id) != "")
- {
- $a_keywords = explode(",",$keywords_id);
- $this->addKeywords($ret['id'],$a_keywords);
- }
-
- $version_number = 1;
- if(isset($ret['version_number']) && $ret['version_number'] < 0)
- {
- // We are in the special situation we are only creating a new version,
- // useful when importing test cases. Need to get last version number.
- // I do not use create_new_version() because it does a copy ot last version
- // and do not allow to set new values in different fields while doing this operation.
- $last_version_info = $this->get_last_version_info($ret['id'],array('output' => 'minimun'));
- $version_number = $last_version_info['version']+1;
- $ret['msg'] = sprintf($ret['msg'],$version_number);
-
- // BUGID 2204
- $ret['version_number']=$version_number;
- }
- // Multiple Test Case Steps Feature
- $op = $this->create_tcversion($ret['id'],$ret['external_id'],$version_number,$summary,
- $preconditions,$steps,$author_id,$execution_type,$importance);
-
-
- $ret['msg'] = $op['status_ok'] ? $ret['msg'] : $op['msg'];
- }
- return $ret;
- }
-
- /*
- 20061008 - franciscom
- added [$check_duplicate_name]
- [$action_on_duplicate_name]
-
- 20060725 - franciscom - interface changes
- [$order]
-
- [$id]
- 0 -> the id will be assigned by dbms
- x -> this will be the id
- Warning: no check is done before insert => can got error.
-
- return:
- $ret['id']
- $ret['external_id']
- $ret['status_ok']
- $ret['msg'] = 'ok';
- $ret['new_name']
-
- rev:
- 20100503 - franciscom - BUGID 3374
- 20100409 - franciscom - improved check on name len.
- BUGID 3367: Error after trying to copy a test case that
- the name is in the size limit.
- 20090120 - franciscom - added new action_on_duplicate_name
- */
- function create_tcase_only($parent_id,$name,$order=self::DEFAULT_ORDER,$id=self::AUTOMATIC_ID,
- $options=null)
- {
- $dummy = config_get('field_size');
- $name_max_len = $dummy->testcase_name;
- $name = trim($name);
- $originalNameLen = tlStringLen($name);
-
- $getOptions = array();
- $ret = array('id' => -1,'external_id' => 0, 'status_ok' => 1,'msg' => 'ok',
- 'new_name' => '', 'version_number' => 1, 'has_duplicate' => false);
-
- $my['options'] = array( 'check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
- 'action_on_duplicate_name' => 'generate_new');
-
- $my['options'] = array_merge($my['options'], (array)$options);
-
- $doCreate=true;
- if ($my['options']['check_duplicate_name'])
- {
- $algo_cfg = config_get('testcase_cfg')->duplicated_name_algorithm;
- $getOptions['check_criteria'] = ($algo_cfg->type == 'counterSuffix') ? 'like' : '=';
- $getOptions['access_key'] = ($algo_cfg->type == 'counterSuffix') ? 'name' : 'id';
- $itemSet = $this->getDuplicatesByName($name,$parent_id,$getOptions);
-
- if( !is_null($itemSet) && ($siblingQty=count($itemSet)) > 0 )
- {
- $ret['has_duplicate'] = true;
- switch($my['options']['action_on_duplicate_name'])
- {
- case 'block':
- $doCreate=false;
- $ret['status_ok'] = 0;
- $ret['msg'] = sprintf(lang_get('testcase_name_already_exists'),$name);
- break;
-
- case 'generate_new':
- $doCreate=true;
-
- switch($algo_cfg->type)
- {
- case 'stringPrefix':
- $name = $algo_cfg->text . " " . $name ;
- $final_len = strlen($name);
- if( $final_len > $name_max_len)
- {
- $name = substr($name,0,$name_max_len);
- }
- break;
-
- case 'counterSuffix':
- $mask = !is_null($algo_cfg->text) ? $algo_cfg->text : '#%s';
- $nameSet = array_flip(array_keys($itemSet));
- $target = $name . ($suffix = sprintf($mask,++$siblingQty));
- // BUGID 3367
- $final_len = strlen($target);
- if( $final_len > $name_max_len)
- {
- $target = substr($target,strlen($suffix),$name_max_len);
- }
-
- // Need to recheck if new generated name does not crash with existent name
- // why? Suppose you have created:
- // TC [1]
- // TC [2]
- // TC [3]
- // Then you delete TC [2].
- // When I got siblings il will got 2 siblings, if I create new progressive using next,
- // it will be 3 => I will get duplicated name.
- while( isset($nameSet[$target]) )
- {
- $target = $name . ($suffix = sprintf($mask,++$siblingQty));
- // BUGID 3367
- $final_len = strlen($target);
- if( $final_len > $name_max_len)
- {
- $target = substr($target,strlen($suffix),$name_max_len);
- }
- }
- $name = $target;
- break;
- }
-
- $ret['status_ok'] = 1;
- $ret['new_name'] = $name;
- $ret['msg'] = sprintf(lang_get('created_with_title'),$name);
- break;
-
- case 'create_new_version':
- $doCreate=false;
-
- // If we found more that one with same name and same parent,
- // will take the first one.
- // BUGID 3374
- $xx = current($itemSet);
- $ret['id'] = $xx['id'];
- $ret['external_id']=$xx['tc_external_id'];
- $ret['status_ok'] = 1;
- $ret['new_name'] = $name;
- $ret['version_number'] = -1;
- $ret['msg'] = lang_get('create_new_version');
- break;
-
- default:
- break;
- }
- }
- }
-
- if( $ret['status_ok'] && $doCreate)
- {
-
- $safeLenName = tlSubStr($name, 0, $name_max_len);
-
- // Get tproject id
- $path2root=$this->tree_manager->get_path($parent_id);
- $tproject_id=$path2root[0]['parent_id'];
- $tcaseNumber=$this->tproject_mgr->generateTestCaseNumber($tproject_id);
- $tcase_id = $this->tree_manager->new_node($parent_id,$this->my_node_type,$safeLenName,$order,$id);
- $ret['id'] = $tcase_id;
- $ret['external_id'] = $tcaseNumber;
- if( !$ret['has_duplicate'] )
- {
- $ret['new_name'] = $safeLenName;
- $ret['msg'] = sprintf(lang_get('testcase_name_length_exceeded'),$originalNameLen,$name_max_len);
- }
-
-
- }
-
- return $ret;
- }
-
- /*
- function: create_tcversion
-
- args:
-
- returns:
-
- rev:
- 20100821 - franciscom - BUGID 3696 - test case step execution type ignored
- 20100106 - franciscom - Multiple Test Case Steps Feature
- 20080113 - franciscom - interface changes added tc_ext_id
-
- */
- function create_tcversion($id,$tc_ext_id,$version,$summary,$preconditions,$steps,
- $author_id,$execution_type=TESTCASE_EXECUTION_TYPE_MANUAL,$importance=2)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
- $tcase_version_id = $this->tree_manager->new_node($id,$this->node_types_descr_id['testcase_version']);
- $sql = "/* $debugMsg */ INSERT INTO {$this->tables['tcversions']} " .
- " (id,tc_external_id,version,summary,preconditions," .
- "author_id,creation_ts,execution_type,importance) " .
- " VALUES({$tcase_version_id},{$tc_ext_id},{$version},'" .
- $this->db->prepare_string($summary) . "','" . $this->db->prepare_string($preconditions) . "'," .
- $this->db->prepare_int($author_id) . "," . $this->db->db_now() .
- ", {$execution_type},{$importance} )";
-
- $result = $this->db->exec_query($sql);
- $ret['msg']='ok';
- $ret['id']=$tcase_version_id;
- $ret['status_ok']=1;
-
- if ($result && ( !is_null($steps) && is_array($steps) ) )
- {
- $steps2create = count($steps);
- $op['status_ok'] = 1;
- for($jdx=0 ; ($jdx < $steps2create && $op['status_ok']); $jdx++)
- {
- $op = $this->create_step($tcase_version_id,$steps[$jdx]['step_number'],$steps[$jdx]['actions'],
- $steps[$jdx]['expected_results'],$steps[$jdx]['execution_type']);
- }
- }
-
- if (!$result)
- {
- $ret['msg'] = $this->db->error_msg();
- $ret['status_ok']=0;
- $ret['id']=-1;
- }
-
- return $ret;
- }
-
-
- /*
- function: getDuplicatesByname
-
- args: $name
- $parent_id
-
- returns: hash
- */
- function getDuplicatesByName($name, $parent_id, $options=null)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
-
- $my['options'] = array( 'check_criteria' => '=', 'access_key' => 'id');
- $my['options'] = array_merge($my['options'], (array)$options);
-
- $target = $this->db->prepare_string($name);
- switch($my['options']['check_criteria'])
- {
- case '=':
- default:
- $check_criteria = " AND NHA.name = '{$target}' ";
- break;
-
- case 'like':
- $check_criteria = " AND NHA.name LIKE '{$target}%' ";
- break;
-
- }
-
- $sql = " SELECT DISTINCT NHA.id,NHA.name,TCV.tc_external_id" .
- " FROM {$this->tables['nodes_hierarchy']} NHA, " .
- " {$this->tables['nodes_hierarchy']} NHB, {$this->tables['tcversions']} TCV " .
- " WHERE NHA.node_type_id = {$this->my_node_type} " .
- " AND NHB.parent_id=NHA.id " .
- " AND TCV.id=NHB.id " .
- " AND NHB.node_type_id = {$this->node_types_descr_id['testcase_version']} " .
- " AND NHA.parent_id={$parent_id} {$check_criteria}";
-
- $rs = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']);
- if( is_null($rs) || count($rs) == 0 )
- {
- $rs=null;
- }
- return $rs;
- }
-
-
-
-
- /*
- function: get_by_name
-
- args: $name
- [$tsuite_name]: name of parent test suite
- [$tproject_name]
-
- returns: hash
-
- @internal revisions
- 20100831 - franciscom - BUGID 3729
-
- */
- function get_by_name($name, $tsuite_name = '', $tproject_name = '')
- {
-
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
-
- $recordset = null;
- $filters_on = array('tsuite_name' => false, 'tproject_name' => false);
-
- // BUGID 3729 - limit all names
- $field_size = config_get('field_size');
- $tsuite_name = tlSubStr(trim($tsuite_name),0, $field_size->testsuite_name);
- $tproject_name = tlSubStr(trim($tproject_name),0,$field_size->testproject_name);
- $name = tlSubStr(trim($name), 0, $field_size->testcase_name);
-
- $sql = "/* $debugMsg */ " .
- " SELECT DISTINCT NH_TCASE.id,NH_TCASE.name,NH_TCASE_PARENT.id AS parent_id," .
- " NH_TCASE_PARENT.name AS tsuite_name, TCV.tc_external_id " .
- " FROM {$this->tables['nodes_hierarchy']} NH_TCASE, " .
- " {$this->tables['nodes_hierarchy']} NH_TCASE_PARENT, " .
- " {$this->tables['nodes_hierarchy']} NH_TCVERSIONS," .
- " {$this->tables['tcversions']} TCV " .
- " WHERE NH_TCASE.node_type_id = {$this->my_node_type} " .
- " AND NH_TCASE.name = '{$this->db->prepare_string($name)}' " .
- " AND TCV.id=NH_TCVERSIONS.id " .
- " AND NH_TCVERSIONS.parent_id=NH_TCASE.id " .
- " AND NH_TCASE_PARENT.id=NH_TCASE.parent_id ";
-
- if($tsuite_name != "")
- {
- $sql .= " AND NH_TCASE_PARENT.name = '{$this->db->prepare_string($tsuite_name)}' " .
- " AND NH_TCASE_PARENT.node_type_id = {$this->node_types_descr_id['testsuite']} ";
- }
- $recordset = $this->db->get_recordset($sql);
- if(count($recordset) && $tproject_name != "")
- {
- list($tproject_info)=$this->tproject_mgr->get_by_name($tproject_name);
- foreach($recordset as $idx => $tcase_info)
- {
- if( $this->get_testproject($tcase_info['id']) != $tproject_info['id'] )
- {
- unset($recordset[$idx]);
- }
- }
- }
-
- return $recordset;
- }
-
-
- /*
- get array of info for every test case
- without any kind of filter.
- Every array element contains an assoc array with testcase info
-
- */
- function get_all()
- {
- $sql = " SELECT nodes_hierarchy.name, nodes_hierarchy.id
- FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy
- WHERE nodes_hierarchy.node_type_id={$my_node_type}";
- $recordset = $this->db->get_recordset($sql);
-
- return $recordset;
- }
-
-
- /**
- * Show Test Case logic
- *
- * @param object $smarty reference to smarty object (controls viewer).
- * @param integer $id Test case unique identifier
- * @param integer $version_id (optional) you can work on ONE test case version,
- * or on ALL; default: ALL
- *
- * @internal
-
- [viewer_args]: map with keys
- action
- msg_result
- refresh_tree: controls if tree view is refreshed after every operation.
- default: yes
- user_feedback
- disable_edit: used to overwrite user rights
- default: 0 -> no
-
- returns:
-
- rev :
- 20090215 - franciscom - added info about links to test plans
-
- 20081114 - franciscom -
- added arguments and options that are useful when this method is
- used to display test case search results.
- path_info: map: key: testcase id
- value: array with path to test case, where:
- element 0 -> test project name
- other elements test suites name
-
- new options on viewer_args: hilite_testcase_name,show_match_count
-
- 20070930 - franciscom - REQ - BUGID 1078
- added disable_edit argument
-
- */
- function show(&$smarty,$guiObj,$template_dir,$id,$version_id = self::ALL_VERSIONS,
- $viewer_args = null,$path_info=null,$mode=null)
- {
- $status_ok = 1;
-
- $gui = is_null($guiObj) ? new stdClass() : $guiObj;
- $gui->parentTestSuiteName='';
- $gui->path_info=$path_info;
- $gui->tprojectName='';
- $gui->linked_versions=null;
- $gui->tc_current_version = array();
- $gui->bodyOnLoad="";
- $gui->bodyOnUnload="";
- $gui->submitCode="";
- $gui->dialogName = '';
- $gui->platforms = null;
- $gui->tableColspan = 5; // sorry magic related to table to display steps
- $gui->opt_requirements = false;
-
- $gui_cfg = config_get('gui');
- $the_tpl = config_get('tpl');
- $my_template = isset($the_tpl['tcView']) ? $the_tpl['tcView'] : 'tcView.tpl';
-
- $tcase_cfg = config_get('testcase_cfg');
-
- $req_mgr = new requirement_mgr($this->db);
-
- $tc_other_versions = array();
- $status_quo_map = array();
- $keywords_map = array();
- $arrReqs = array();
- $userid_array = array();
-
-
- // 20090718 - franciscom
- $cf_smarty = null;
- $formatOptions=null;
- $cfx=0;
- $filters=$this->buildCFLocationMap();
-
- if( !is_null($mode) && $mode=='editOnExec' )
- {
- // refers to two javascript functions present in testlink_library.js
- // and logic used to refresh both frames when user call this
- // method to edit a test case while executing it.
- $gui->dialogName='tcview_dialog';
- $gui->bodyOnLoad="dialog_onLoad($gui->dialogName)";
- $gui->bodyOnUnload="dialog_onUnload($gui->dialogName)";
- $gui->submitCode="return dialog_onSubmit($gui->dialogName)";
- }
-
- $viewer_defaults=array('title' => lang_get('title_test_case'),'show_title' => 'no',
- 'action' => '', 'msg_result' => '','user_feedback' => '',
- 'refreshTree' => 1, 'disable_edit' => 0,
- 'display_testproject' => 0,'display_parent_testsuite' => 0,
- 'hilite_testcase_name' => 0,'show_match_count' => 0);
-
- if( !is_null($viewer_args) && is_array($viewer_args) )
- {
- foreach($viewer_defaults as $key => $value)
- {
- if(isset($viewer_args[$key]) )
- {
- $viewer_defaults[$key]=$viewer_args[$key];
- }
- }
- }
-
- $gui->show_title=$viewer_defaults['show_title'];
- $gui->display_testcase_path=!is_null($path_info);
- $gui->hilite_testcase_name=$viewer_defaults['hilite_testcase_name'];
- $gui->pageTitle=$viewer_defaults['title'];
- $gui->show_match_count=$viewer_defaults['show_match_count'];
- if($gui->show_match_count && $gui->display_testcase_path )
- {
- $gui->match_count=count($path_info);
- }
-
- // fine grain control of operations
- // if( $viewer_defaults['disable_edit'] == 1 || has_rights($this->db,"mgt_modify_tc") == 'no' )
- // BUGID 3387
- if( $viewer_defaults['disable_edit'] == 1 || has_rights($this->db,"mgt_modify_tc") == false)
- {
- $mode = 'editDisabled';
- }
- $gui->show_mode = $mode;
- $gui->can_do = $this->getShowViewerActions($mode);
-
- if(is_array($id))
- {
- $a_id = $id;
- }
- else
- {
- $status_ok = $id > 0 ? 1 : 0;
- $a_id = array($id);
- }
- if($status_ok)
- {
- $path2root = $this->tree_manager->get_path($a_id[0]);
- $tproject_id = $path2root[0]['parent_id'];
- $info = $this->tproject_mgr->get_by_id($tproject_id);
- $gui->opt_requirements = $info['opt']->requirementsEnabled;
-
- $platformMgr = new tlPlatform($this->db,$tproject_id);
- $gui->platforms = $platformMgr->getAllAsMap();
-
- // BUGID 2378
- $testplans = $this->tproject_mgr->get_all_testplans($tproject_id,array('plan_status' =>1) );
- $gui->has_testplans = !is_null($testplans) && count($testplans) > 0 ? 1 : 0;
-
- if( $viewer_defaults['display_testproject'] )
- {
- $gui->tprojectName=$info['name'];
- }
-
- if( $viewer_defaults['display_parent_testsuite'] )
- {
- $parent_idx = count($path2root)-2;
- $gui->parentTestSuiteName = $path2root[$parent_idx]['name'];
- }
-
- $tcasePrefix = $this->tproject_mgr->getTestCasePrefix($tproject_id);
- if(trim($tcasePrefix) != "")
- {
- // Add To Testplan button will be disabled if the testcase doesn't belong to the current selected testproject
- // $gui->can_do->add2tplan = 'no';
- if ($_SESSION['testprojectPrefix'] == $tcasePrefix)
- {
- $gui->can_do->add2tplan = $gui->can_do->add2tplan == 'yes' ? has_rights($this->db,"testplan_planning") : 'no';
- }
- else
- {
- $gui->can_do->add2tplan = 'no';
- }
-
- $tcasePrefix .= $tcase_cfg->glue_character;
- }
- }
-
- if($status_ok && sizeof($a_id))
- {
- $allTCKeywords = $this->getKeywords($a_id,null,'testcase_id',' ORDER BY keyword ASC ');
- $allReqs = $req_mgr->get_all_for_tcase($a_id);
- foreach($a_id as $key => $tc_id)
- {
- $tc_array = $this->get_by_id($tc_id,$version_id);
- if (!$tc_array)
- {
- continue;
- }
-
- $tc_array[0]['tc_external_id'] = $tcasePrefix . $tc_array[0]['tc_external_id'];
-
- // get the status quo of execution and links of tc versions
- $status_quo_map[] = $this->get_versions_status_quo($tc_id);
-
- $gui->linked_versions[] = $this->get_linked_versions($tc_id);
- $keywords_map[] = isset($allTCKeywords[$tc_id]) ? $allTCKeywords[$tc_id] : null;
- $tc_current = $tc_array[0];
- $gui->tc_current_version[] = array($tc_current);
-
- //Get UserID and Updater ID for current Version
- $userid_array[$tc_current['author_id']] = null;
- $userid_array[$tc_current['updater_id']] = null;
-
- if(count($tc_array) > 1)
- {
- $tc_other_versions[] = array_slice($tc_array,1);
- }
- else
- {
- $tc_other_versions[] = null;
- }
-
- //Get author and updater id for each version
- if ($tc_other_versions[0])
- {
- foreach($tc_other_versions[0] as $key => $version)
- {
- $userid_array[$version['author_id']] = null;
- $userid_array[$version['updater_id']] = null;
- }
- }
- $tcReqs = isset($allReqs[$tc_id]) ? $allReqs[$tc_id] : null;
- $arrReqs[] = $tcReqs;
-
- foreach($filters as $locationKey => $locationFilter)
- {
- $cf_smarty[$cfx][$locationKey] =
- $this->html_table_of_custom_field_values($tc_id,'design',$locationFilter,
- null,null,$tproject_id);
- }
- $cfx++;
-
- } // foreach($a_id as $key => $tc_id)
- } // if (sizeof($a_id))
-
- // Removing duplicate and NULL id's
- unset($userid_array['']);
- $passeduserarray = array_keys($userid_array);
-
- $gui->cf = $cf_smarty;
- $gui->refreshTree = $viewer_defaults['refreshTree'];
- $gui->sqlResult = $viewer_defaults['msg_result'];
- $gui->action = $viewer_defaults['action'];
- $gui->user_feedback = $viewer_defaults['user_feedback'];
- $gui->execution_types = $this->execution_types;
- $gui->tcase_cfg = $tcase_cfg;
- $gui->users = tlUser::getByIDs($this->db,$passeduserarray,'id');
- $gui->status_quo = $status_quo_map;
- $gui->testcase_other_versions = $tc_other_versions;
- $gui->arrReqs = $arrReqs;
- $gui->view_req_rights = has_rights($this->db,"mgt_view_req");
- $gui->keywords_map = $keywords_map;
- $smarty->assign('gui',$gui);
- $smarty->display($template_dir . $my_template);
- }
-
-
-
- /**
- * update test case specification
- *
- * @param integer $id Test case unique identifier (node_hierarchy table)
- * @param integer $tcversion_id Test Case Version unique ID (node_hierarchy table)
- * @param string $name name/title
- * @param string $summary
- * @param string $preconditions
- * @param array $steps steps + expected results
- * @param integer $user_id who is doing the update
- * @param string $keywords_id optional list of keyword id to be linked to test case
- * this list will override previous keyword links (delete + insert).
- *
- * @param integer $tc_order optional order inside parent test suite
- * @param integer $execution_type optional
- * @param integer $importance optional
- *
- *
- *
- */
- function update($id,$tcversion_id,$name,$summary,$preconditions,$steps,
- $user_id,$keywords_id='',$tc_order=self::DEFAULT_ORDER,
- $execution_type=TESTCASE_EXECUTION_TYPE_MANUAL,$importance=2)
- {
- $ret['status_ok'] = 1;
- $ret['msg'] = '';
-
-
- tLog("TC UPDATE ID=($id): exec_type=$execution_type importance=$importance");
-
- // Check if new name will be create a duplicate testcase under same parent
- $checkDuplicates = config_get('check_names_for_duplicates');
- if ($checkDuplicates)
- {
- $check = $this->tree_manager->nodeNameExists($name,$this->my_node_type,$id);
- $ret['status_ok'] = !$check['status'];
- $ret['msg'] = $check['msg'];
- }
-
- if($ret['status_ok'])
- {
- $sql=array();
- $sql[] = " UPDATE {$this->tables['nodes_hierarchy']} SET name='" .
- $this->db->prepare_string($name) . "' WHERE id= {$id}";
-
- // test case version
- $sql[] = " UPDATE {$this->tables['tcversions']} tcversions " .
- " SET summary='" . $this->db->prepare_string($summary) . "'," .
- " updater_id=" . $this->db->prepare_int($user_id) . ", " .
- " modification_ts = " . $this->db->db_now() . "," .
- " execution_type=" . $this->db->prepare_int($execution_type) . ", " .
- " importance=" . $this->db->prepare_int($importance) . "," .
- " preconditions='" . $this->db->prepare_string($preconditions) . "' " .
- " WHERE tcversions.id = " . $this->db->prepare_int($tcversion_id);
-
- foreach($sql as $stm)
- {
- $result = $this->db->exec_query($stm);
- if( !$result )
- {
- $ret['status_ok'] = 0;
- $ret['msg'] = $this->db->error_msg;
- break;
- }
- }
-
- // BUGID 3634 - missing update.
- if( $ret['status_ok'] && !is_null($steps) )
- {
- $this->update_tcversion_steps($tcversion_id,$steps);
- }
-
- if( $ret['status_ok'] )
- {
- $this->updateKeywordAssignment($id,$keywords_id);
- }
- }
-
- return $ret;
- }
-
-
- /*
- function: updateKeywordAssignment
-
- args:
-
- returns:
-
- */
- private function updateKeywordAssignment($id,$keywords_id)
- {
-
- // To avoid false loggings, check is delete is needed
- $items = array();
- $items['stored'] = $this->get_keywords_map($id);
- if (is_null($items['stored']))
- $items['stored'] = array();
- $items['requested'] = array();
-
- if(trim($keywords_id) != "")
- {
- $a_keywords = explode(",",trim($keywords_id));
- $sql = " SELECT id,keyword " .
- " FROM {$this->tables['keywords']} " .
- " WHERE id IN (" . implode(',',$a_keywords) . ")";
-
- $items['requested'] = $this->db->fetchColumnsIntoMap($sql,'id','keyword');
- }
-
- $items['common'] = array_intersect_assoc($items['stored'],$items['requested']);
- $items['new'] = array_diff_assoc($items['requested'],$items['common']);
- $items['todelete'] = array_diff_assoc($items['stored'],$items['common']);
-
- if(!is_null($items['todelete']) && count($items['todelete']))
- {
- $this->deleteKeywords($id,array_keys($items['todelete']),self::AUDIT_ON);
- }
-
- if(!is_null($items['new']) && count($items['new']))
- {
- $this->addKeywords($id,array_keys($items['new']),self::AUDIT_ON);
- }
- }
-
- /*
- function: logKeywordChanges
-
- args:
-
- returns:
-
- */
- function logKeywordChanges($old,$new)
- {
-
- // try to understand the really new
-
- }
-
-
-
-
-
-
-
- /*
- function: check_link_and_exec_status
- Fore every version of testcase (id), do following checks:
-
- 1. testcase is linked to one of more test plans ?
- 2. if anwser is yes then,check if has been executed => has records on executions table
-
- args : id: testcase id
-
- returns: string with following values:
- no_links: testcase is not linked to any testplan
- linked_but_not_executed: testcase is linked at least to a testplan
- but has not been executed.
-
- linked_and_executed: testcase is linked at least to a testplan and
- has been executed => has records on executions table.
-
-
- */
- function check_link_and_exec_status($id)
- {
- $status = 'no_links';
-
- // get linked versions
- // ATTENTION TO PLATFORMS
- $linked_tcversions = $this->get_linked_versions($id);
- $has_links_to_testplans = is_null($linked_tcversions) ? 0 : 1;
-
- if($has_links_to_testplans)
- {
- // check if executed
- $linked_not_exec = $this->get_linked_versions($id,"NOT_EXECUTED");
-
- $status='linked_and_executed';
- if(count($linked_tcversions) == count($linked_not_exec))
- {
- $status = 'linked_but_not_executed';
- }
- }
- return $status;
- }
-
-
- /*
-
- rev:
- 20100107 - franciscom - Multiple Test Case Step Feature
- 20081015 - franciscom - added check to avoid bug due to no children
-
- */
- function delete($id,$version_id = self::ALL_VERSIONS)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
- $children=null;
- $do_it=true;
-
- // I'm trying to speedup the next deletes
- $sql="/* $debugMsg */ " .
- " SELECT NH_TCV.id AS tcversion_id, NH_TCSTEPS.id AS step_id " .
- " FROM {$this->tables['nodes_hierarchy']} NH_TCV " .
- " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NH_TCSTEPS " .
- " ON NH_TCSTEPS.parent_id = NH_TCV.id ";
-
- if($version_id == self::ALL_VERSIONS)
- {
- if( is_array($id) )
- {
- $sql .= " WHERE NH_TCV.parent_id IN (" .implode(',',$id) . ") ";
- }
- else
- {
- $sql .= " WHERE NH_TCV.parent_id={$id} ";
- }
- }
- else
- {
- $sql .= " WHERE NH_TCV.parent_id={$id} AND NH_TCV.id = {$version_id}";
- }
-
- $children_rs=$this->db->get_recordset($sql);
- $do_it = !is_null($children_rs);
- if($do_it)
- {
- foreach($children_rs as $value)
- {
- $children['tcversion'][]=$value['tcversion_id'];
- $children['step'][]=$value['step_id'];
- }
- $this->_execution_delete($id,$version_id,$children);
- $this->_blind_delete($id,$version_id,$children);
- }
-
-
- return 1;
- }
-
- /*
- function: get_linked_versions
- For a test case get information about versions linked to testplans.
- Filters can be applied on:
- execution status
- active status
-
- args : id: testcase id
- [exec_status]: default: ALL, range: ALL,EXECUTED,NOT_EXECUTED
- [active_status]: default: ALL, range: ALL,ACTIVE,INACTIVE
- [tplan_id]
-
- returns: map.
- key: version id
- value: map with following structure:
- key: testplan id
- value: map with following structure:
-
- testcase_id
- tcversion_id
- id -> tcversion_id (node id)
- version
- summary
- importance
- author_id
- creation_ts
- updater_id
- modification_ts
- active
- is_open
- testplan_id
- tplan_name
- */
- function get_linked_versions($id,$exec_status="ALL",$active_status='ALL',$tplan_id=null,$platform_id=null)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
- $active_filter='';
- $active_status=strtoupper($active_status);
- if($active_status !='ALL')
- {
- $active_filter=' AND tcversions.active=' . $active_status=='ACTIVE' ? 1 : 0;
- }
-
- switch ($exec_status)
- {
- case "ALL":
- $sql = "/* $debugMsg */ " .
- " SELECT NH.parent_id AS testcase_id, NH.id AS tcversion_id, " .
- " tcversions.*, TTC.testplan_id, TTC.tcversion_id, TTC.platform_id," .
- " NHB.name AS tplan_name " .
- " FROM {$this->tables['nodes_hierarchy']} NH," .
- " {$this->tables['tcversions']} tcversions," .
- " {$this->tables['testplan_tcversions']} TTC, " .
- " {$this->tables['nodes_hierarchy']} NHB " .
- " WHERE TTC.tcversion_id = tcversions.id {$active_filter} " .
- " AND tcversions.id = NH.id " .
- " AND NHB.id = TTC.testplan_id " .
- " AND NH.parent_id = {$id}";
-
- if(!is_null($tplan_id))
- {
- $sql .= " AND TTC.testplan_id = {$tplan_id} ";
- }
-
- // 20100308 - franciscom
- if(!is_null($platform_id))
- {
- $sql .= " AND TTC.platform_id = {$platform_id} ";
- }
-
- $recordset = $this->db->fetchMapRowsIntoMap($sql,'tcversion_id','testplan_id',database::CUMULATIVE);
- // 20100330 - eloff - BUGID 3329
- if( !is_null($recordset) )
- {
- // changes third access key from sequential index to platform_id
- foreach ($recordset as $accessKey => $testplan)
- {
- foreach ($testplan as $tplanKey => $testcases)
- {
- // Use a temporary array to avoid key collisions
- $newArray = array();
- foreach ($testcases as $elemKey => $element)
- {
- $platform_id = $element['platform_id'];
- $newArray[$platform_id] = $element;
- }
- $recordset[$accessKey][$tplanKey] = $newArray;
- }
- }
- }
- break;
-
- case "EXECUTED":
- $recordset=$this->get_exec_status($id,$exec_status,$active_status,$tplan_id,$platform_id);
- break;
-
- case "NOT_EXECUTED":
- $recordset=$this->get_exec_status($id,$exec_status,$active_status,$tplan_id,$platform_id);
- break;
- }
-
- // Multiple Test Case Steps
- if( !is_null($recordset) )
- {
- $version2loop = array_keys($recordset);
- foreach( $version2loop as $accessKey)
- {
- $step_set = $this->get_steps($accessKey);
- $tplan2loop = array_keys($recordset[$accessKey]);
- foreach( $tplan2loop as $tplanKey)
- {
- $elem2loop = array_keys($recordset[$accessKey][$tplanKey]);
- foreach( $elem2loop as $elemKey)
- {
- $recordset[$accessKey][$tplanKey][$elemKey]['steps'] = $step_set;
- }
- }
-
- }
- }
-
- return $recordset;
- }
-
- /*
- Delete the following info:
- req_coverage
- risk_assignment
- custom fields
- keywords
- links to test plans
- tcversions
- nodes from hierarchy
-
- rev:
- 20100825 - BUGID 3702
- 20070602 - franciscom - delete attachments
- */
- function _blind_delete($id,$version_id=self::ALL_VERSIONS,$children=null)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
- $sql = array();
-
- $destroyTC = false;
- $item_id = $version_id;
- $tcversion_list = $version_id;
- if( $version_id == self::ALL_VERSIONS)
- {
- $destroyTC = true;
- $item_id = $id;
- $tcversion_list=implode(',',$children['tcversion']);
- }
-
- // BUGID 3465: Delete Test Project - User Execution Assignment is not deleted
- // BUGID 3573: MySQL does not like ALIAS
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['user_assignments']} " .
- " WHERE feature_id in (" .
- " SELECT id FROM {$this->tables['testplan_tcversions']} " .
- " WHERE tcversion_id IN ({$tcversion_list}))";
-
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['testplan_tcversions']} " .
- " WHERE tcversion_id IN ({$tcversion_list})";
-
- // Multiple Test Case Steps Feature
-
- // BUGID 3702
- if( !is_null($children['step']) )
- {
- // remove null elements
- foreach($children['step'] as $key => $value)
- {
- if(is_null($value))
- {
- unset($children['step'][$key]);
- }
- }
-
- if( count($children['step']) > 0)
- {
- $step_list=trim(implode(',',$children['step']));
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['tcsteps']} " .
- " WHERE id IN ({$step_list})";
- }
- }
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['tcversions']} " .
- " WHERE id IN ({$tcversion_list})";
-
- foreach ($sql as $the_stm)
- {
- $result = $this->db->exec_query($the_stm);
- }
-
- if($destroyTC)
- {
- // Remove data that is related to Test Case => must be deleted when there is no more trace
- // of test case => when all version are deleted
- $sql = null;
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['testcase_keywords']} WHERE testcase_id = {$id}";
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['req_coverage']} WHERE testcase_id = {$id}";
-
- foreach ($sql as $the_stm)
- {
- $result = $this->db->exec_query($the_stm);
- }
-
- $this->deleteAttachments($id);
- $this->cfield_mgr->remove_all_design_values_from_node($id);
-
- }
-
- // Attention:
- // After addition of test case steps feature, a test case version can be root of
- // a subtree that contains the steps.
- $this->tree_manager->delete_subtree($item_id);
- }
-
-
- /*
- Delete the following info:
- bugs
- executions
- cfield_execution_values
- */
- function _execution_delete($id,$version_id=self::ALL_VERSIONS,$children=null)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
- $sql = array();
-
- if( $version_id == self::ALL_VERSIONS )
- {
- $tcversion_list=implode(',',$children['tcversion']);
-
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['execution_bugs']} " .
- " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
- " WHERE tcversion_id IN ({$tcversion_list}))";
-
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['cfield_execution_values']} " .
- " WHERE tcversion_id IN ({$tcversion_list})";
-
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['executions']} " .
- " WHERE tcversion_id IN ({$tcversion_list})";
-
- }
- else
- {
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['execution_bugs']} " .
- " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
- " WHERE tcversion_id = {$version_id})";
-
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['cfield_execution_values']} " .
- " WHERE tcversion_id = {$version_id}";
-
- $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['executions']} " .
- " WHERE tcversion_id = {$version_id}";
- }
-
- …
Large files files are truncated, but you can click here to view the full file