/lib/functions/results.class.php
PHP | 1965 lines | 1061 code | 227 blank | 677 comment | 187 complexity | 2879c2c132554bb016a5bcd5c053bddd 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 Kevin Levy, franciscom
- * @copyright 2004-2009, TestLink community
- * @version CVS: $Id: results.class.php,v 1.162 2010/08/21 15:36:11 asimon83 Exp $
- * @link http://www.teamst.org/index.php
- * @uses config.inc.php
- * @uses common.php
- *
- * @internal Revisions:
- *
- * 20100821 - asimon - BUGID 3682
- * 20100721 - asimon - BUGID 3406, 1508: changed for user assignments per build:
- * results_overload(), tallyBuildResults(),
- * getTotalsForBuilds(), createTotalsForBuilds(),
- * 20100518 - franciscom - BUGID 3474: Link to test case in Query Metrics Report is broken if using platforms
- * 20100515 - franciscom - BUGID 3438
- * 20090804 - franciscom - added contributed code getPriority()
- * 20090618 - franciscom - BUGID 0002621
- * 20090414 - amitkhullar - BUGID: 2374-Show Assigned User in the Not Run Test Cases Report
- * 20090413 - amitkhullar - BUGID 2267 - SQL Error in linked Test Cases
- * 20090409 - amitkhullar - Created an results_overloaded function for extending the base class
- results for passing extra parameters.
- * 20090327 - amitkhullar- BUGID 2156 - added option to get latest/all results in Query metrics report.
- * 20080928 - franciscom - some minor refactoring regarding keywords
- * 20080928 - franciscom - refactoring on buildExecutionsMap()
- * 20080602 - franciscom - added logic to manage version using tcversion_number
- * 20080513 - franciscom - getTCLink() added external_id
- *
- * 20080513 - franciscom - buildExecutionsMap() added external_id in output
- * 20080413 - franciscom - refactoring
- * 20080302 - franciscom - refactored of tally* functions, to manage
- * user defined test case statuses.
- * created tallyResults() method to remove
- * duplicated code. (may be can have performance side effects).
- *
- * 20071101 - franciscom - import_file_types, export_file_types
- * 20071013 - franciscom - changes to fix MSSQL problems
- * 20070916 - franciscom - refactoring to remove global coupling
- * changes in constructot interface()
- *
- * 20070825 - franciscom - added node_order in buildExecutionsMap()
- * 20070505 - franciscom - removing timer.php
- * 20070219 - kevinlevy - nearing completion for 1.7 release
- * 20060829 - kevinlevy - development in progress
- **/
-
- /** TBD */
- require_once('treeMenu.inc.php');
- require_once('users.inc.php');
- require_once('exec.inc.php'); // used for bug string lookup
-
- // BUGID 3438
- if (config_get('interface_bugs') != 'NO')
- {
- require_once(TL_ABS_PATH. 'lib' . DIRECTORY_SEPARATOR . 'bugtracking' .
- DIRECTORY_SEPARATOR . 'int_bugtracking.php');
- }
- /**
- * This class is encapsulates most functionality necessary to query the database
- * for results to publish in reports. It returns data structures to the gui layer in a
- * manner that are easy to display in smarty templates.
- *
- * @package TestLink
- * @author kevinlevy
- */
- class results extends tlObjectWithDB
- {
- /**
- * only call get_linked_tcversions() only once, and save it to
- * $this->linked_tcversions
- */
- private $linked_tcversions = null;
- private $suitesSelected = "";
-
- /** @var resource references passed in by constructor */
- var $db = null;
-
- /** @var object class references passed in by constructor */
- private $tplanMgr = null;
- private $testPlanID = -1;
- private $tprojectID = -1;
- private $testCasePrefix='';
-
- private $priorityLevelsCfg='';
- private $resultsCfg;
- private $testCaseCfg='';
- private $map_tc_status;
- private $tc_status_for_statistics;
-
- private $mapOfLastResultByOwner = null;
- private $mapOfLastResultByKeyword = null;
- private $mapOfLastResultByBuild = null;
-
- // Platform contribution
- private $mapOfLastResultByPrio = null;
- private $mapOfLastResultByPlatform = null;
- private $tplanName = null;
-
- // construct map linking suite ids to execution rows
- private $SUITE_TYPE_ID = 2;
- private $executionsMap = null;
-
- /**
- * suiteStructure is an array with pattern : name, id, array
- * array may contain another array in the same pattern
- * this is used to describe tree structure
- */
- private $suiteStructure = null;
- const NAME_POS = 0;
- const ID_POS = 1;
- const ARRAY_POS = 2;
- const ITEM_PATTERN_POS = 3;
-
- private $flatArray = null;
-
- /**
- * associated with flatArray
- */
- private $flatArrayIndex = 0;
- private $depth = 0;
- private $previousDepth = 0;
- const DEPTH_FLAT_POS = 0;
- const NAME_FLAT_POS = 1;
- const SUITE_ID_FLAT_POS = 2;
- const ITEM_PATTERN_FLAT_POS = 3;
-
- /** mapOfLastResult is in the following format
- * array ([suiteId] => array ([tcId] => Array([buildIdLastExecuted][result])))
- */
- private $mapOfLastResult = null;
-
- /**
- * map test suite id's to array of [total, passed, failed, blocked, not_run, other user defined]
- * for cases in that suite
- */
- private $mapOfSuiteSummary = null;
-
- /**
- * map test suite id's to array of [total, passed, failed, blocked, not_run, other user defined]
- * for cases in that suite and in all child suites
- */
- private $mapOfAggregate = null;
-
- /**
- * related to $mapOfAggregate creation
- * as we navigate up and down tree, $suiteId's are addded and removed from '$aggSuiteList
- * when totals are added for a suite, we add to all suites listed in $executionsMap
- * suiteIds are are registered and de-registered from aggSuiteList using
- * functions addToAggSuiteList(), removeFromAggSuiteList()
- */
- private $aggSuiteList = array();
-
- /**
- * map test suite id to number of (total, passed, failed, blocked, not run)
- * only counts test cases in current suite
- */
- private $mapOfTotalCases = null;
-
- private $mapOfCaseResults = null;
-
- /**
- * array(total cases in plan, total pass, total fail, total blocked, total not run)
- */
- private $totalsForPlan = null;
-
- /**
- * BUGID 3406, 1508: array similar to $totalsForPlan, but with counts for each build in it
- */
- private $totalsForBuilds = null;
-
- /**
- * map test case ids to array of keywords associated with test case
- */
- private $keywordData = null;
-
- /**
- * TO-DO check this description of this object
- * map of keywordIds
- */
- private $aggregateKeyResults = null;
-
- /**
- * TO-DO check this description of this object
- * map of ownerIds
- */
- private $aggregateOwnerResults = null;
-
- /**
- * TO-DO check this description of this object
- * map of buildsIds
- */
- private $aggregateBuildResults = null;
- private $aggregatePrioResults = null;
- private $aggregatePlatformResults = null;
- protected $latest_results;
-
- var $import_file_types = array("XML" => "XML");
- var $export_file_types = array("XML" => "XML");
-
- /**
- * class constructor
- * @param resource &$db reference to database handler
- * @param object &$tplan_mgr reference to instance of testPlan class
- **/
- public function results(&$db, &$tplan_mgr,$tproject_info, $tplan_info,
- $suitesSelected = 'all',
- $builds_to_query = -1, $lastResult = 'a',
- $keywordId = 0, $owner = null,
- $startTime = null, $endTime = null,
- $executor = null, $search_notes_string = null, $linkExecutionBuild = null,
- &$suiteStructure = null, &$flatArray = null, &$linked_tcversions = null)
- {
- $this->latest_results = 1;
-
- return $this->results_overload($db, $tplan_mgr,$tproject_info, $tplan_info,
- $suitesSelected ,
- $builds_to_query , $lastResult,
- $keywordId , $owner ,
- $startTime , $endTime ,
- $executor , $search_notes_string, $linkExecutionBuild ,
- $suiteStructure, $flatArray, $linked_tcversions);
- }
-
- /**
- * $builds_to_query = 'a' will query all build, $builds_to_query = -1 will prevent
- * most logic in constructor from executing/ executions table from being queried
- * if keyword = 0, search by keyword would not be performed
- * @author kevinlevy
- *
- * @internal Revisions:
- * 20100720 - asimon - BUGID 3406, 1508: added logic to get result counts on build level
- * 20090327 - amitkhullar - added parameter $latest_results to get the latest results only.
- * 20071013 - franciscom - changes to fix MSSQL problems
- * $startTime = "0000-00-00 00:00:00" -> null
- * $endTime = "9999-01-01 00:00:00" -> null
- *
- * 20070916 - franciscom - interface changes
- */
- public function results_overload(&$db, &$tplan_mgr,$tproject_info, $tplan_info,
- $suitesSelected = 'all',
- $builds_to_query = -1, $lastResult = 'a',
- $keywordId = 0, $owner = null,
- $startTime = null, $endTime = null,
- $executor = null, $search_notes_string = null, $linkExecutionBuild = null,
- &$suiteStructure = null, &$flatArray = null, &$linked_tcversions = null)
- {
- $this->priorityLevelsCfg = config_get('priority_levels');
- $this->resultsCfg = config_get('results');
- $this->testCaseCfg = config_get('testcase_cfg');
-
- $this->db = $db;
-
- tlObjectWithDB::__construct($db);
- $this->tplanMgr = $tplan_mgr;
- $this->map_tc_status = $this->resultsCfg['status_code'];
-
-
- // TestLink standard configuration is (at least for me)
- // not_run not available at user interface level on execution feature as choice.
- //
- // if( !isset($dummy['not_run']) )
- // {
- // $dummy['not_run']=$this->map_tc_status['not_run'];
- // }
-
- // This will be used to create dynamically counters if user add new status
- foreach( $this->resultsCfg['status_label_for_exec_ui'] as $tc_status_verbose => $label)
- {
- $this->tc_status_for_statistics[$tc_status_verbose] = $this->map_tc_status[$tc_status_verbose];
- }
- if( !isset($this->resultsCfg['status_label_for_exec_ui']['not_run']) )
- {
- $this->tc_status_for_statistics['not_run'] = $this->map_tc_status['not_run'];
- }
-
- $this->suitesSelected = $suitesSelected;
- $this->tprojectID = $tproject_info['id'];
- $this->testCasePrefix = $tproject_info['prefix'];
-
- $this->testPlanID = $tplan_info['id'];
- $this->tplanName = $tplan_info['name'];
-
- $this->suiteStructure = $suiteStructure;
- $this->flatArray = $flatArray;
- $this->linked_tcversions = $linked_tcversions;
-
- // build suiteStructure and flatArray
- if (($this->suiteStructure == null) && ($this->flatArray == null) && ($this->linked_tcversions == null))
- {
- list($this->suiteStructure,$this->linked_tcversions) = $this->generateExecTree($db,$keywordId, $owner);
- }
-
-
- // KL - if no builds are specified, no need to execute the following block of code
- if ($builds_to_query != -1) {
- // retrieve results from executions table
-
- // get keyword id -> keyword name pairs used in this test plan
- $keywords_in_tplan = $tplan_mgr->get_keywords_map($this->testPlanID,'ORDER BY keyword');
-
- // KL - 20061229 - this call may not be necessary for all reports
- // only those that require info on results for keywords
- // Map of test case ids to array of associated keywords
- $this->keywordData = null;
- if(!is_null($keywords_in_tplan))
- {
- $this->keywordData = $this->getKeywordData(array_keys($keywords_in_tplan));
- }
- //$tplan_mgr->get_keywords_tcases($this->testPlanID);
- // get owner id -> owner name pairs used in this test plan
- $arrOwners = getUsersForHtmlOptions($db);
-
- // create data object which tallies last result for each test case
- // this function now also creates mapOfLastResultByKeyword ???
-
- // KL - 2/01/07
- // we should NOT build executions map with cases that are just pass/failed/or blocked.
- // we should always populate the executions map with all results
- // and then programmatically figure out the last result
- // if you just query the executions table for those rows with status = $this->map_tc_status['passed']
- // that is not the way to determine last result
- $all_results = $this->latest_results;
- $this->executionsMap = $this->buildExecutionsMap($builds_to_query, 'a', $keywordId,
- $owner, $startTime, $endTime, $executor,
- $search_notes_string, $linkExecutionBuild,
- $all_results);
-
- // new dBug($this->executionsMap);
-
- $this->createMapOfLastResult($this->suiteStructure, $this->executionsMap, $lastResult);
- $this->aggregateKeywordResults = $this->tallyKeywordResults($this->mapOfLastResultByKeyword, $keywords_in_tplan);
- $this->aggregateOwnerResults = $this->tallyOwnerResults($this->mapOfLastResultByOwner, $arrOwners);
-
- // create data object which tallies totals for individual suites
- // child suites are NOT taken into account in this step
- $this->createMapOfSuiteSummary($this->mapOfLastResult);
-
- // create data object which tallies totals for suites taking
- // child suites into account
- $this->createAggregateMap($this->suiteStructure, $this->mapOfSuiteSummary);
- $this->totalsForPlan = $this->createTotalsForPlan($this->suiteStructure);
-
- // must be done after totalsForPlan is performed because the total # of cases is needed
- // BUGID 3682
- $arrBuilds = $tplan_mgr->get_builds($this->testPlanID, testplan::GET_ACTIVE_BUILD);
-
- // 3406, 1508 - we need the totals per build here, not for the whole plan anymore
- //$this->aggregateBuildResults = $this->tallyBuildResults($this->mapOfLastResultByBuild,
- // $arrBuilds, $this->totalsForPlan);
- $this->totalsForBuilds = $this->createTotalsForBuilds($arrBuilds);
- $this->aggregateBuildResults = $this->tallyBuildResults($this->mapOfLastResultByBuild,
- $arrBuilds, $this->totalsForBuilds);
- } // end if block
- } // end results constructor
-
-
- /**
- * get_export_file_types
- * @return array map of
- * key: export file type code
- * value: export file type verbose description
- */
- function get_export_file_types()
- {
- return $this->export_file_types;
- }
-
-
- /**
- * get_import_file_types
- * @return array map of
- * key: export file type code
- * value: export file type verbose description
- */
- function get_import_file_types()
- {
- return $this->import_file_types;
- }
-
- /**
- * tallyKeywordResults($keywordResults, $keywordIdNamePairs)
- *
- * @param array $keywordResults Array ([keyword id] => Array ( [test case id] => result ))
- * example:
- * <code>
- * Array ( [128] => Array ( [14308] => p [14309] => n [14310] => n [14311] => f [14312] => n [14313] => n )
- * [11] => Array ( [14309] => n [14310] => n [14311] => f [14312] => n [14313] => n ))
- * </code>
- *
- * @return array map indexed using Keyword ID, where each element is a map with following structure:
- * <code>
- * [keyword_name] => K1
- * [total_tc] => 2
- * [percentage_completed] => 100.00
- * [details] => Array
- * (
- * [passed] => Array
- * (
- * [qty] => 1
- * [percentage] => 50.00
- * )
- * [failed] => Array
- * (
- * [qty] => 1
- * [percentage] => 50.00
- * )
- * [blocked] => Array
- * (
- * [qty] => 0
- * [percentage] => 0.00
- * )
- * [unknown] => Array
- * (
- * [qty] => 0
- * [percentage] => 0.00
- * )
- * [not_run] => Array
- * (
- * [qty] => 0
- * [percentage] => 0.00
- * )
- * )
- * )
- * </code>
- * IMPORTANT: keys on details map dependends of configuration map $tlCfg->results['status_label']
- */
- private function tallyKeywordResults($keywordResults, $keywordIdNamePairs)
- {
- if ($keywordResults == null)
- {
- return null;
- }
-
- $rValue = null;
- foreach($keywordResults as $keywordId => $results)
- {
- $item_name = 'keyword_name';
- $element = $this->tallyResults($results,sizeOf($results),$item_name);
-
- $element[$item_name] = $keywordIdNamePairs[$keywordId];
- $rValue[$keywordId] = $element;
- }
-
- return $rValue;
- }
-
-
- /**
- *
- * @param array $ownerResults format:
- * <code>Array ([owner id] => Array ( [test case id] => result ))</code>
- *
- * @return array map
- * Example of output format :
- * <code>
- * [tester_name] => johndoe
- * [total_tc] => 2
- * [percentage_completed] => 100.00
- * [details] => Array
- * (
- * [passed] => Array
- * (
- * [qty] => 1
- * [percentage] => 50.00
- * )
- * [failed] => Array
- * (
- * [qty] => 1
- * [percentage] => 50.00
- * )
- * [blocked] => Array
- * (
- * [qty] => 0
- * [percentage] => 0.00
- * )
- * [unknown] => Array
- * (
- * [qty] => 0
- * [percentage] => 0.00
- * )
- * [not_run] => Array
- * (
- * [qty] => 0
- * [percentage] => 0.00
- * )
- * )
- * )
- * </code>
- *
- * IMPORTANT: keys on details map dependends of configuration map $tlCfg->results['status_label']
- */
- private function tallyOwnerResults($ownerResults, $ownerIdNamePairs)
- {
- if ($ownerResults == null)
- {
- return;
- }
-
- $no_tester_assigned = lang_get('unassigned');
- $rValue = null;
- foreach($ownerResults as $ownerId => $results)
- {
- $item_name = 'tester_name';
- // Calculates number of tc assignes to user
- $num = 0;
- foreach($results as $tc_id => $tc_info)
- {
- foreach ($tc_info as $platform_id => $platform)
- {
- $num++;
- }
- }
- $element = $this->tallyResults($results,$num,$item_name);
- $element[$item_name] = ($ownerId == -1) ? $no_tester_assigned : $ownerIdNamePairs[$ownerId];
- $rValue[$ownerId] = $element;
- }
-
- return $rValue;
- }
-
-
- /**
- *
- * parameter1 format:
- * Array ([owner id] => Array ( [test case id] => result ))
- *
- * @return array map
- * <code>Array ( [owner id] => Array ( total, passed, failed, blocked, not run))</code>
- *
- * @internal revisions:
- * 20100721 - asimon - BUGID 3406, 1508
- */
- private function tallyBuildResults($buildResults, $arrBuilds, $finalResults)
- {
- if ($buildResults == null)
- {
- return null;
- }
-
- // OK go ahead
-
- // 3406, 1508
- //$totalCases = $finalResults['total'];
- $na_string = lang_get('not_aplicable');
-
- $rValue = null;
- foreach($arrBuilds as $buildId => $buildInfo)
- {
- $item_name='build_name';
- $results = isset($buildResults[$buildId]) ? $buildResults[$buildId] : array();
-
- // 3406, 1508 - total cases is now an array with totals for each build ID
- $totalCases = $finalResults[$buildId]['total'];
-
- $element=$this->tallyResults($results,$totalCases,$item_name);
- if (!is_null ($element))
- {
- $element[$item_name]=$buildInfo['name'];
- $rValue[$buildId] = $element;
-
- // 3406, 1508: here we need to insert the correct "not run" value now
- // and the percentages need to be re-calculated after that of course
- $not_run_count = $this->tplanMgr->assignment_mgr->get_not_run_tc_count_per_build($buildId);
- $not_run_percentage = ($totalCases != 0) ?
- number_format($not_run_count / $totalCases * 100, 2) : $na_string;
- $rValue[$buildId]['details']['not_run']['qty'] = $not_run_count;
- $rValue[$buildId]['details']['not_run']['percentage'] = $not_run_percentage;
- $rValue[$buildId]['percentage_completed'] = 100 - $not_run_percentage;
- }
- } // end foreach
-
- unset($element);
- unset($results);
-
- return $rValue;
- }
-
- /**
- * Aggregates results based on priority.
- * @see aggregateOwnerResults()
- * @param $prioResults array
- * <code> Array ([priority] => Array ( [tc_id] => Array( [platform_id] => result ))) </code>
- *
- * @return array map
- * <code>Array ( [priority] => Array ( total, passed, failed, blocked, not run))</code>
- */
- private function tallyPriorityResults($prioResults)
- {
- if ($prioResults == null)
- {
- return null;
- }
-
- $urgencyCfg = config_get('urgency');
- $rValue = null;
- foreach($prioResults as $prio => $results)
- {
- $item_name='priority';
- // Calculates number of tc in this prio
- $num = 0;
- foreach($results as $tc_id => $tc_info) {
- foreach ($tc_info as $platform_id => $platform) {
- $num++;
- }
- }
- $element=$this->tallyResults($results,$num,$item_name);
- if (!is_null ($element))
- {
- $element[$item_name]=lang_get($urgencyCfg["code_label"][$prio]);
- $rValue[$prio] = $element;
- }
- } // end foreach
- // Sort returning array from high to low priority
- krsort($rValue);
-
- unset($element);
- unset($results);
-
- return $rValue;
- }
-
- /**
- * Aggregates results based on platform.
- * @see aggregateOwnerResults()
- * @param $prioResults array
- * <code> Array ([platform] => Array ( [tc_id] => Array( [platform_id] => result ))) </code>
- *
- * @return array map
- * <code>Array ( [priority] => Array ( total, passed, failed, blocked, not run))</code>
- */
- // private function tallyPlatformResults($platformResults, $platforms)
- // {
- // echo __FUNCTION__;
- // if ($platformResults == null)
- // {
- // return null;
- // }
- //
- // $rValue = null;
- // foreach($platformResults as $platform_id => $results)
- // {
- // $item_name='platform';
- // // Calculates number of tc in this platform.
- // $num = 0;
- // foreach($results as $tc_id => $tc_info) {
- // foreach ($tc_info as $platform) {
- // $num++;
- // }
- // }
- // $element=$this->tallyResults($results,$num,$item_name);
- // if (!is_null ($element))
- // {
- // $element[$item_name]=$platforms[$platform_id];
- // $rValue[$platform_id] = $element;
- // }
- // } // end foreach
- //
- // unset($element);
- // unset($results);
- //
- // return $rValue;
- // }
-
- /**
- * @return array total / pass / fail / blocked / not run results for each keyword id
- */
- public function getAggregateKeywordResults() {
- return $this->aggregateKeywordResults;
- }
-
- /**
- * @return array total / pass / fail / blocked / not run results for each owner id
- * unassigned test cases show up under owner id = -1
- */
- public function getAggregateOwnerResults() {
- return $this->aggregateOwnerResults;
- }
-
- /**
- * @return array total / pass / fail / blocked / not run results for each build id
- */
- public function getAggregateBuildResults() {
- return $this->aggregateBuildResults;
- }
-
- /**
- * Aggregates and returns results grouped by priority
- * @return array total / pass / fail / blocked / not run results for each build id
- */
- public function getAggregatePriorityResults()
- {
- if ($this->aggregatePrioResults == null) {
- $this->aggregatePrioResults = $this->tallyPriorityResults($this->mapOfLastResultByPrio);
- }
- return $this->aggregatePrioResults;
- }
-
- /**
- * Aggregates and returns results grouped by platform
- * @return array total / pass / fail / blocked / not run results for each build id
- */
- // public function getAggregatePlatformResults()
- // {
- // if ($this->aggregatePlatformResults == null)
- // {
- // $platforms = $this->tplanMgr->getPlatforms($this->testPlanID,'map');
- // $this->aggregatePlatformResults = $this->tallyPlatformResults($this->mapOfLastResultByPlatform, $platforms);
- // }
- // return $this->aggregatePlatformResults;
- // }
-
- /**
- * @TODO rename this method to getExecutionsMap()
- * (resultsTC.php is 1 file (may not be only file) that references this method)
- * @return array
- */
- public function getSuiteList(){
- return $this->executionsMap;
- }
-
- /**
- * @return array array which describes suite hierachy
- */
- public function getSuiteStructure(){
- return $this->suiteStructure;
- }
-
- public function getLinkedTCVersions(){
- return $this->linked_tcversions;
- }
-
- /**
- * @return array
- */
- public function getMapOfSuiteSummary(){
- return $this->mapOfSuiteSummary;
- }
-
- /**
- * @return array
- */
- public function getMapOfLastResult() {
- return $this->mapOfLastResult;
- }
-
- /**
- * @return array
- */
- public function getAggregateMap(){
- return $this->mapOfAggregate;
- }
-
- /**
- * @return array
- */
- public function getTotalsForPlan()
- {
- return $this->totalsForPlan;
- }
-
- /**
- * BUGID 3406, 1508
- * @author Andreas Simon
- * @return array
- */
- public function getTotalsForBuild()
- {
- return $this->totalsForBuilds;
- }
-
- /**
- * @return array single-dimension array with pattern level, suite name, suite id
- */
- public function getFlatArray(){
- return $this->flatArray;
- }
-
- /**
- * @param array $results map: key tc_id => value status_code
- * @return mixed array or null
- */
- private function tallyResults($results,$totalCases,$item_name=null)
- {
-
- if ($results == null)
- {
- return null;
- }
-
- // OK go ahead
-
- // 3406
- $na_string = lang_get('not_aplicable');
-
- $code_verbose=array_flip($this->tc_status_for_statistics);
- $element = null;
- foreach($this->tc_status_for_statistics as $status_verbose => $status_code)
- {
- $total[$status_verbose]=0;
- $percentage[$status_verbose]=0;
- }
-
- $dummy=0;
- foreach($results as $tc_id => $tc_info)
- {
- foreach((array)$tc_info as $platform_id => $result_code) {
- $status_verbose=$code_verbose[$result_code];
-
- // Check if user has configured and add not_run.
- // Standard TestLink behavior:
- // 1. do not show not_run as a choice on execute pages
- // 2. do not save on DB not_run executions
- //
- if( $status_verbose !='' && $status_verbose != 'not_run')
- {
- $total[$status_verbose]++;
- $dummy++;
- }
- }
- }
-
- // not_run is an special status
- $total['not_run'] = abs($totalCases - $dummy);
- $percentage['not_run'] = ($totalCases != 0) ?
- number_format((($total['not_run']) / $totalCases) * 100,2) : $na_string;
-
- $percentCompleted = 0;
- if ($totalCases != 0)
- {
- $percentCompleted = (($totalCases - $total['not_run']) / $totalCases) * 100;
-
- foreach($this->tc_status_for_statistics as $status_verbose => $status_code)
- {
- $percentage[$status_verbose]=(($total[$status_verbose]) / $totalCases) * 100;
- $percentage[$status_verbose]=number_format($percentage[$status_verbose],2);
- }
- }
- $percentCompleted = number_format($percentCompleted,2);
-
- // Because I want to have this as first key on map, just for easy reading.
- // Value will be setted by caller.
- if( !is_null($item_name) )
- {
- $element[$item_name]='';
- }
- $element['total_tc']=$totalCases;
- $element['percentage_completed']=$percentCompleted;
-
- $element['details']=array();
- foreach($total as $status_verbose => $counter)
- {
- $element['details'][$status_verbose]['qty']=$counter;
- $element['details'][$status_verbose]['percentage']=$percentage[$status_verbose];
- }
-
- return $element;
- } // end function
-
- function tallyResults2($results,$totalCases)
- {
- if ($results == null)
- {
- return null;
- }
-
- // not_run is an special status
- $total['not_run'] = abs($totalCases - $dummy);
- $percentage['not_run']=number_format((($total['not_run']) / $totalCases) * 100,2);
-
- $keySet = array_keys($results);
- foreach($keySet as $keyID)
- {
- $results[$keyID]['percentage_completed'] = 0;
- $totalCases = $results[$keyID]['total_tc'];
- $target = &$results[$keyID]['details'];
- if ($totalCases != 0)
- {
- $results[$keyID]['percentage_completed'] =
- number_format((($totalCases - $target['not_run']['qty']) / $totalCases) * 100,2);
-
- }
- foreach($target as $status_verbose => $qty)
- {
- $target[$status_verbose]['percentage']=(($target[$status_verbose]['qty']) / $totalCases) * 100;
- $target[$status_verbose]['percentage']=number_format($target[$status_verbose]['percentage'],2);
- }
- }
- return $results;
- } // end function
-
-
-
- /**
- * Gets the user for a specific feature_id
- * @author amitkhullar
- *
- * @return integer User identifier
- */
- function getUserForFeature($feature_id)
- {
- $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
- $sql = "/* $debugMsg */ ";
- $sql .= " SELECT user_id FROM {$this->tables['user_assignments']} " .
- " user_assignments WHERE feature_id = " . $feature_id ;
- $owner_row = $this->db->fetchFirstRow($sql,'testcase_id', 1);
- $owner_id = $owner_row['user_id'] == '' ? -1 : $owner_row['user_id'];
- return $owner_id;
- } // end function
-
-
- /**
- * Fill $this->mapOfLastResult - which provides information on the last result
- * for each test case.
- *
- * $this->mapOfLastResult is an array of suite ids
- * each suiteId -> arrayOfTCresults, arrayOfSummaryResults
- * arrayOfTCresults -> array of rows containing (buildIdLastExecuted, result) where row id = testcaseId
- *
- * currently it does not account for user expliting marking a case "not run".
- *
- * @author kevinlevy
- *
- * @return void
- *
- * @internal Revisions:
- *
- * 20070919 - franciscom - interface changes
- * 20070825 - franciscom - added node_order
- */
- private function addLastResultToMap($suiteId, $suiteName, $exec,$lastResultToTrack)
- {
- $testcase_id = $exec['testcaseID'];
- $platform_id = $exec['platform_id'];
- $external_id = $exec['external_id'];
- $buildNumber = $exec['build_id'];
- $result = $exec['status'];
- $tcversion_id = $exec['tcversion_id'];
- // echo __FUNCTION__ . 'tcversion id ' . $tcversion_id . '<br>';
-
- // 20080602 - franciscom
- // Maybe this check is not more needed, because $exec has been changed using same logic
- if( isset($exec['tcversion_number']) && !is_null($exec['tcversion_number']) )
- {
- $version=$exec['tcversion_number'];
- }
- else
- {
- $version=$exec['version'];
- }
-
- $execution_ts=$exec['execution_ts'];
- $notes=$exec['notes'];
- $executions_id=$exec['executions_id'];
- $name=$exec['name'];
- $tester_id=$exec['tester_id'];
- $feature_id=$exec['feature_id'];
- $assigner_id=$exec['assigner_id'];
- if ($buildNumber)
- {
- $this->mapOfLastResultByBuild[$buildNumber][$testcase_id][$platform_id] = $result;
- }
- $owner_id = $this->getUserForFeature($feature_id);
- $associatedKeywords = null;
- if ($this->keywordData != null && array_key_exists($testcase_id, $this->keywordData))
- {
- $associatedKeywords = $this->keywordData[$testcase_id];
- }
-
- $doInsert = false;
- // handle case where suite has already been added to mapOfLastResult
- if ($this->mapOfLastResult && array_key_exists($suiteId, $this->mapOfLastResult))
- {
- // handle case where both suite and test case have been added to elmapOfLastResult
- if (array_key_exists($testcase_id, $this->mapOfLastResult[$suiteId]))
- {
- // handle case where all of suite, test case and platform have been added to mapOfLastResult
- if (array_key_exists($platform_id, $this->mapOfLastResult[$suiteId][$testcase_id]))
- {
- $buildInMap = $this->mapOfCaseResults[$testcase_id]['buildNumber'];
- $execIDInMap = $this->mapOfCaseResults[$testcase_id]['execID'];
- if (($buildInMap < $buildNumber) || ($buildInMap == $buildNumber && $execIDInMap < $executions_id))
- {
- $doInsert = true;
- }
- }
- else
- {
- $doInsert = true;
- }
- }
- else
- {
- $doInsert = true;
- }
- }
- else
- {
- $doInsert = true;
- }
-
- if ($doInsert)
- {
- $this->mapOfLastResultByOwner[$owner_id][$testcase_id][$platform_id] = $result;
-
- // priorities
- // echo __FUNCTION__ . ' LINE::' . __LINE__ .'<br> - tcversion_id=' . $tcversion_id . '<br>';
-
- $prio = $this->getPriority($this->testPlanID, $tcversion_id);
-
- $this->mapOfLastResultByPrio[$prio][$testcase_id][$platform_id] = $result;
-
- // 20090804 - Eloff
- // This structure might look weird, but is needed to reuse aggregation code
- // $this->mapOfLastResultByPlatform[$platform_id][$testcase_id][$platform_id] = $result;
-
- $qta_loops=sizeof($associatedKeywords);
- for ($i = 0;$i <$qta_loops ; $i++)
- {
- $this->mapOfLastResultByKeyword[$associatedKeywords[$i]][$testcase_id] = $result;
- }
-
- $this->mapOfCaseResults[$testcase_id]['buildNumber'] = $buildNumber;
- $this->mapOfCaseResults[$testcase_id]['execID'] = $executions_id;
-
- $this->mapOfLastResult[$suiteId][$testcase_id][$platform_id] = array("buildIdLastExecuted" => $buildNumber,
- "result" => $result,
- "tcversion_id" => $tcversion_id,
- "platform_id" => $platform_id,
- "external_id" => $external_id,
- "version" => $version,
- "execution_ts" => $execution_ts,
- "notes" => $notes,
- "suiteName" => $suiteName,
- "executions_id" => $executions_id,
- "name" => $name,
- "tester_id" => $tester_id);
- }
- } // end function
-
- /**
- * Creates statistics on each suite
- * @return void
- */
- private function createMapOfSuiteSummary(&$mapOfLastResult)
- {
- if ($mapOfLastResult)
- {
- $code_verbose=array_flip($this->tc_status_for_statistics);
- $code_verbose[$this->map_tc_status['not_run']]='not_run';
-
- foreach($mapOfLastResult as $suiteId => $tcaseResults)
- {
- foreach($this->tc_status_for_statistics as $status_verbose => $status_code)
- {
- $total[$status_verbose]=0;
- }
- $total['not_run']=0;
- $total['total'] = 0;
- foreach($tcaseResults as $testcase_id => $platformResults) {
-
- foreach($platformResults as $platform_id => $value)
- {
- $currentResult = $value['result'];
- $status_verbose=$code_verbose[$currentResult];
- $total[$status_verbose]++;
- $total['total']++;
- }
-
- $this->mapOfSuiteSummary[$suiteId] = $total;
- }
- }
- } // end if
- } // end function
-
- /**
- * tallies total cases, total pass/fail/blocked/not run for each suite
- * it takes into account sub-suite tallies within that suite
- * ex : if suite A contains suites A.1 and A.2, this function
- * adds up A.1, A.2, and test cases within A
- */
- private function createAggregateMap(&$suiteStructure, &$mapOfSuiteSummary)
- {
- $loop_qty=count($suiteStructure);
- for ($idx = 0; $idx < $loop_qty; $idx++ )
- {
- $suiteId = 0;
- $tpos=$idx % self::ITEM_PATTERN_POS;
- if ($tpos == self::ID_POS)
- {
- // register a suite that we will use to increment aggregate results for
- $suiteId = $suiteStructure[$idx];
- array_push($this->aggSuiteList, $suiteId);
-
- if ($mapOfSuiteSummary && array_key_exists($suiteId, $mapOfSuiteSummary))
- {
- $summaryArray = $mapOfSuiteSummary[$suiteId];
- $this->addResultsToAggregate($summaryArray);
- }
- }
- elseif ($tpos == self::ARRAY_POS)
- {
- if (is_array($suiteStructure[$idx]))
- {
- // go get child totals
- $newSuiteStructure = $suiteStructure[$idx];
- $this->createAggregateMap($newSuiteStructure, $mapOfSuiteSummary);
- }
-
- // it's very important to pop a suite off the list at this point
- // and only this point
- array_pop($this->aggSuiteList);
- }
-
- } // end for
- } // end function
-
- /**
- * iterates over top level suites and adds up totals using data from mapOfAggregate
- */
- private function createTotalsForPlan($suiteStructure)
- {
- $counters['total']=0;
- $counters['not_run']=0;
- foreach($this->tc_status_for_statistics as $status_verbose => $status_code)
- {
- $counters[$status_verbose]=0;
- }
-
- $loop_qty=count($suiteStructure);
- for ($idx = 0 ; $idx < $loop_qty ; $idx++)
- {
- if (($idx % self::ITEM_PATTERN_POS) == self::ID_POS)
- {
-
- $suiteId = $suiteStructure[$idx];
- $resultsForSuite = isset($this->mapOfAggregate[$suiteId]) ? $this->mapOfAggregate[$suiteId] : null;
- if( !is_null($resultsForSuite) )
- {
- foreach($counters as $code_verbose => $value)
- {
- $counters[$code_verbose]+=$resultsForSuite[$code_verbose];
- }
- }
- } // end if
- }
-
- return $counters;
- } // end function
-
- /**
- * For BUGID 3406, 1508: New function to get counts on build level instead of testplan level
- *
- * @author Andreas Simon
- * @param array $arrBuilds Array with information about the builds for this testplan.
- * @return array $counters Array similar to $this->totalsForPlan, but with correct numbers per build
- */
- private function createTotalsForBuilds($arrBuilds) {
-
- $counters = array();
-
- // first get totals from plan, then replace "wrong" values
- foreach ($arrBuilds as $build_id => $build_info) {
- $counters[$build_id] = $this->totalsForPlan;
-
- // replace "total" value
- $total = $this->tplanMgr->assignment_mgr->get_count_of_assignments_for_build_id($build_id);
- $counters[$build_id]['total'] = $total;
-
- // replace "not run" value
- $not_run = $this->tplanMgr->assignment_mgr->get_not_run_tc_count_per_build($build_id);
- $counters[$build_id]['not_run'] = $not_run;
- }
-
- return $counters;
- } // end of method
-
- /**
- * @return array map with following keys:
- * 'total','not_run', other keys depends of status configured by user
- * If users leave untouched TestLink Factory configuration:
- * 'total','not_run','passed','failed','blocked'
- */
- private function addResultsToAggregate($results)
- {
- $loop_qty=count($this->aggSuiteList);
-
-
- for ($idx = 0 ; $idx < $loop_qty ; $idx++)
- {
- $suiteId = $this->aggSuiteList[$idx];
- $currentSuite = null;
- if ($this->mapOfAggregate && array_key_exists($suiteId, $this->mapOfAggregate))
- {
- $currentSuite = $this->mapOfAggregate[$suiteId];
- }
- else
- {
- $currentSuite['total'] = 0;
- foreach($this->tc_status_for_statistics as $status_verbose => $status_code)
- {
- $currentSuite[$status_verbose]=0;
- }
- $currentSuite['not_run'] = 0;
- }
-
- foreach($currentSuite as $key => $value)
- {
- $currentSuite[$key] += $results[$key];
- }
-
- $this->mapOfAggregate[$suiteId] = $currentSuite;
- }
- } // end function
-
-
- /**
- * @return array map testcase_id, keyword_id
- */
- private function getKeywordData($keywordsInPlan)
- {
- $CUMULATIVE=1;
-
- // limit the sql query to just those keys in this test plan
- if ($keywordsInPlan == null) {
- return null;
- }
-
- $keys = implode(',',$keywordsInPlan);
- $sql = " SELECT testcase_id, keyword_id" .
- " FROM {$this->tables['testcase_keywords']}" .
- " WHERE keyword_id IN ($keys)";
-
- $returnMap = $this->db->fetchColumnsIntoMap($sql,'testcase_id', 'keyword_id',$CUMULATIVE);
-
- return $returnMap;
- } // end function
-
-
- /**
- *
- */
- private function createMapOfLastResult(&$suiteStructure, &$executionsMap, $lastResult)
- {
- $suiteName = null;
- $totalSuites = count($suiteStructure);
- for ($i = 0; $i < $totalSuites; $i++)
- {
- if (($i % self::ITEM_PATTERN_POS) == self::NAME_POS)
- {
- $suiteName = $suiteStructure[$i];
- }
- elseif (($i % self::ITEM_PATTERN_POS) == self::ID_POS)
- {
- $suiteId = $suiteStructure[$i];
- if( isset($executionsMap[$suiteId]) )
- {
- $totalCases = count($executionsMap[$suiteId]);
- for ($j = 0 ; $j < $totalCases; $j++)
- {
- $cexec = $executionsMap[$suiteId][$j];
- $this->addLastResultToMap($suiteId, $suiteName,$cexec,$lastResult);
- }
- }
- }
- elseif (($i % self::ITEM_PATTERN_POS) == self::ARRAY_POS)
- {
- if (is_array($suiteStructure[$i]))
- {
- $childSuite = $suiteStructure[$i];
- $summaryTreeForChild = $this->createMapOfLastResult($childSuite, $executionsMap, $lastResult);
- }
- } // end elseif
- } // end for loop
- } // end function
-
- /**
- * Builds $executionsMap map.
- * $executionsMap contains all execution information for suites and test cases.
- *
- *
- * $executionsMap = [testsuite_id_1_array, test_suite_id_2_array, ...]
- *
- * testsuite_id_1_array = []
- * all test cases are included, even cases that have not been executed yet
- *
- * @internal Revisions:
- *
- * 20100518 - franciscom - BUGID 3474: Link to test case in Query Metrics Report is broken if using platforms
- * 20090302 - amitkhullar - added a parameter $all_results to get latest results (0) only otherwise
- * all results are displayed in reports (1).
- * 20080928 - franciscom - seems that adding a control to avoid call to buildBugString()
- * reduces dramatically memory usage();
- * IMPORTANT:
- * we need to refactor this method, because if we had to call buildBugsString()
- * probably will crash again.
- * We need to think about function that got bug for all test cases, with one
- * call, may be is the solution.
- *
- * 20070916 - franciscom - removed session coupling
- * added node_order
- */
- private function buildExecutionsMap($builds_to_query, $lastResult = 'a', $keyword = 0,
- $owner = null, $startTime, $endTime, $executor,
- $search_notes_string, $executeLinkBuild, $all_results = 1)
- {
- $searchBugs= config_get('bugInterfaceOn');
-
- // first make sure we initialize the executionsMap
- // otherwise duplicate executions will be added to suites
- $executionsMap = null;
-
- // map for adding SuiteName directly to every TC
- $allSuites = $this->getAllSuites();
-
- // for execution link
- $canExecute = has_rights($this->db,"tp_execute");
-
- // Build Sql additional filters
- $sqlFilters='';
- if( !is_null($startTime) )
- {
- $sqlFilters .= " AND execution_ts > '{$startTime}'";
- }
-
- if( !is_null($endTime) )
- {
- $sqlFilters .= " AND execution_ts < '{$endTime}' ";
- }
-
- if (($lastResult == $this->map_tc_status['passed']) || ($lastResult == $this->map_tc_status['failed']) ||
- ($lastResult == $this->map_tc_status['blocked'])){
- $sqlFilters .= " AND status = '" . $lastResult . "' ";
- }
- if (($builds_to_query != -1) && ($builds_to_query != 'a')) {
- $sqlFilters .= " AND build_id IN ($builds_to_query) ";
- }
- // BUGID 2023
- if (!is_null($executor) && $executor != '' && $executor != TL_USER_ANYBODY)
- {
- $sqlFilters .= " AND tester_id = $executor ";
- }
- if ($search_notes_string != null) {
- $sqlFilters .= " AND notes LIKE '%" . $search_notes_string ."%' ";
- }
-
- $suffixLink = htmlspecialchars($this->testCasePrefix . $this->testCaseCfg->glue_character);
-
- foreach($this->linked_tcversions as $tcase_info)
- {
- foreach($tcase_info as $index => $info)
- {
- $testcaseID = $info['tc_id'];
- $executionExists = true;
- $currentSuite = null;
- if (!$executionsMap || !(array_key_exists($info['testsuite_id'], $executionsMap)))
- {
- $currentSuite = array();
- }
- else
- {
- $currentSuite = $executionsMap[$info['testsuite_id']];
- }
-
- $version = $info['version'];
- if(isset($info['tcversion_number']) && !is_null($info['tcversion_number']))
- {
- $version = $info['tcversion_number'];
- }
- ;
- $owner_id = $this->getUserForFeature($info['feature_id']);
-
- // BUGID - 2374: Show Assigned User in the Not Run Test Cases Report
- $infoToSave = array('testcaseID' => $testcaseID,
- 'platform_id' => $info['platform_id'],
- 'testcasePrefix' => $this->testCasePrefix . $this->testCaseCfg->glue_character,
- 'external_id' => $info['external_id'],
- 'tcversion_id' => $info['tcversion_id'],
- 'version' => $version,
- 'build_id' => '',
- 'tester_id' => $owner_id,
- 'execution_ts' => '',
- 'status' => $this->map_tc_status['not_run'],
- 'executions_id' => '',
- 'notes' => '',
- 'bugString' => '',
- 'name' => $info['name'],
- 'suiteName' => $allSuites[$info['testsuite_id']],
- 'assigner_id' => $info['assigner_id'],
- 'feature_id' => $info['feature_id'],
- 'execute_link' => '');
-
- if ($info['tcversion_id'] != $info['executed'])
- {
- $executionExists = false;
- if (($lastResult == 'a') || ($lastResult == $this->map_tc_status['not_run']))
- {
- // Initialize information on testcaseID to be "not run"
- // echo 'NOTRUN - DEBUG- Added Not Run info <br>';
- array_push($currentSuite, $infoToSave);
- }
- }
-
- if ($executionExists)
- {
- // TO-DO - this is where we can include the searching of results
- // over multiple test plans - by modifying this select statement slightly
- // to include multiple test plan ids
- $sql = "SELECT * FROM {$this->tables['executions']} executions " .
- "WHERE tcversion_id = " . $info['executed'] . " AND testplan_id = $this->testPlanID " .
- "AND platform_id = " . $info['platform_id'];
-
- $sql .= $sqlFilters;
-
- // mht: fix 966
- // mike_h - 20070806 - when ordering executions by the timestamp,
- // the results are represented correctly in the report "Test Report".
- // amitkhullar - BUGID 2156 - added option to get latest/all results in Query metrics report.
-
- if ($all_results == 0)
- {
- $sql .= " ORDER BY execution_ts DESC limit 1";
- }
- else
- {
- $sql .= " ORDER BY execution_ts ASC ";
-
- }
- //echo "<br>debug - <b><i>" . __FUNCTION__ . "</i></b><br><b>" . $sql . "</b><br>";
-
- $execQuery = $this->db->fetchArrayRowsIntoMap($sql,'id');
- if ($execQuery)
- {
- if ($lastResult != $this->map_tc_status['not_run'])
- {
- foreach($execQuery as $executions_id => $execInfo)
- {
- $exec_row = $execInfo[0];
-
- $infoToSave['version'] = $exec_row['tcversion_number'];
- $infoToSave['build_id'] = $exec_row['build_id'];
- $infoToSave['platform_id'] = $exec_row['platform_id'];
- $infoToSave['tester_id'] = $exec_row['tester_id'];
- $infoToSave['status'] = $exec_row['status'];
- $infoToSave['notes'] = $exec_row['notes'];
- $infoToSave['executions_id'] = $executions_id;
- //@todo: Refactor for this code - BUGID 2242
- $infoToSave['bugString'] = $searchBugs ? $this->buildBugString($this->db, $executions_id) : '';
-
- $dummy = null;
- $infoToSave['execution_ts'] = localize_dateOrTimeStamp(null, $dummy,'timestamp_format',
- $exec_row['execution_ts']);
- //-amitkhullar - BugID:2267
- $prefixLink = '<a href="lib/execute/execSetResults.php?level=testcase&build_id=' . $infoToSave['build_id'];
-
- // 20100518 - franciscom - BUGID 3474: Link to test case in Query Metrics Report is broken if using platforms
- $prefixLink .= '&platform_id=' . $exec_row['platform_id'];
- $infoToSave['execute_link'] = $prefixLink . "&id={$testcaseID}&version_id=" . $info['tcversion_id'] .
- "&tplan_id=" . $this->testPlanID . '">' .
- $suffixLink . $info['external_id'] . ": <b>" .
- htmlspecialchars($info['name']). "</b></a>";
-
- array_push($currentSuite, $infoToSave);
- } // end foreach
- }
- } // end if($execQuery)
-
- elseif (($lastResult == 'a') || ($lastResult == $this->map_tc_status['not_run']))
- {
- // HANDLE scenario where execution does not exist
- array_push($currentSuite, $infoToSave);
- }
- } // end if($executionExists)
-
- $executionsMap[$info['testsuite_id']] = $currentSuite;
- } // foreach platform
- } // end foreach
-
- unset($infoToSave);
-
- return $executionsMap;
- } // end function
-
- /**
- * @TODO - figure out what file to include so i don't have
- * to redefine this
- * builds bug information for execution id
- * written by Andreas, being implemented again by KL
- */
- function buildBugString(&$db,$execID) …
Large files files are truncated, but you can click here to view the full file