/** * * @internal revisions * 20121010 - asimon - TICKET 4353: added active/inactive filter */ function generateTestSpecTreeNew(&$db, $tproject_id, $tproject_name, $linkto, $filters = null, $options = null) { $chronos[] = microtime(true); $tables = tlObjectWithDB::getDBTables(array('tcversions', 'nodes_hierarchy')); $my = array(); $my['options'] = array('forPrinting' => 0, 'hideTestCases' => 0, 'tc_action_enabled' => 1, 'viewType' => 'testSpecTree'); $my['filters'] = array('keywords' => null, 'testplan' => null); $my['options'] = array_merge($my['options'], (array) $options); $my['options']['showTestCaseID'] = config_get('treemenu_show_testcase_id'); $my['filters'] = array_merge($my['filters'], (array) $filters); $treeMenu = new stdClass(); $treeMenu->rootnode = null; $treeMenu->menustring = ''; $resultsCfg = config_get('results'); $glueChar = config_get('testcase_cfg')->glue_character; $menustring = null; $tproject_mgr = new testproject($db); $tree_manager =& $tproject_mgr->tree_manager; $hash_descr_id = $tree_manager->get_available_node_types(); $hash_id_descr = array_flip($hash_descr_id); $status_descr_code = $resultsCfg['status_code']; $status_code_descr = $resultsCfg['code_status']; $decoding_hash = array('node_id_descr' => $hash_id_descr, 'status_descr_code' => $status_descr_code, 'status_code_descr' => $status_code_descr); $tcase_prefix = $tproject_mgr->getTestCasePrefix($tproject_id) . $glueChar; $test_spec = getTestSpecTree($tproject_id, $tproject_mgr, $filters); // Added root node for test specification -> testproject $test_spec['name'] = $tproject_name; $test_spec['id'] = $tproject_id; $test_spec['node_type_id'] = $hash_descr_id['testproject']; $map_node_tccount = array(); $tc2show = null; if ($test_spec) { if (isset($my['filters']['filter_custom_fields']) && isset($test_spec['childNodes'])) { $test_spec['childNodes'] = filter_by_cf_values($db, $test_spec['childNodes'], $my['filters']['filter_custom_fields'], $hash_descr_id); } $pnFilters = array('keywords' => $my['filters']['filter_keywords'], 'keywords_filter_type' => $my['filters']['filter_keywords_filter_type']); $pnOptions = array('hideTestCases' => $my['options']['hideTestCases'], 'ignoreInactiveTestCases' => $my['options']['ignore_inactive_testcases'], 'ignoreActiveTestCases' => $my['options']['ignore_active_testcases']); // Important/CRITIC: // prepareTestSpecNode() will make changes to $test_spec like filtering by test case keywords. $testcase_counters = prepareTestSpecNode($db, $tproject_mgr, $tproject_id, $test_spec, $map_node_tccount, $pnFilters, $pnOptions); if (is_null($test_spec)) { $test_spec['name'] = $tproject_name; $test_spec['id'] = $tproject_id; $test_spec['node_type_id'] = $hash_descr_id['testproject']; } foreach ($testcase_counters as $key => $value) { $test_spec[$key] = $testcase_counters[$key]; } $tc2show = renderTreeNode(1, $test_spec, $hash_id_descr, $linkto, $tcase_prefix, $my['options']); } $menustring = ''; $treeMenu->rootnode = new stdClass(); $treeMenu->rootnode->name = $test_spec['text']; $treeMenu->rootnode->id = $test_spec['id']; $treeMenu->rootnode->leaf = isset($test_spec['leaf']) ? $test_spec['leaf'] : false; $treeMenu->rootnode->text = $test_spec['text']; $treeMenu->rootnode->position = $test_spec['position']; $treeMenu->rootnode->href = $test_spec['href']; // 20090328 - franciscom - BUGID 2299 // More details about problem found on 20090308 and fixed IN WRONG WAY // TPROJECT // |______ TSA // |__ TC1 // |__ TC2 // | // |______ TSB // |______ TSC // // Define Keyword K1,K2 // // NO TEST CASE HAS KEYWORD ASSIGNED // Filter by K1 // Tree will show root that spins Forever // menustring before str_ireplace : [null,null] // menustring AFTER [null] // // Now fixed. // // Some minor fix to do // Il would be important exclude Top Level Test suites. // // // 20090308 - franciscom // Changed because found problem on: // Test Specification tree when applying Keyword filter using a keyword NOT PRESENT // in test cases => Tree root shows loading icon and spin never stops. // // Attention: do not know if in other situation this will generate a different bug // // // Change key ('childNodes') to the one required by Ext JS tree. if (isset($test_spec['childNodes'])) { $menustring = str_ireplace('childNodes', 'children', json_encode($test_spec['childNodes'])); } if (!is_null($menustring)) { // Remove null elements (Ext JS tree do not like it ). // :null happens on -> "children":null,"text" that must become "children":[],"text" // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); $menustring = str_ireplace(array(':null', ',null', 'null,', 'null'), array(':[]', '', '', ''), $menustring); } $treeMenu->menustring = $menustring; $tc2show = !is_null($tc2show) ? explode(",", trim($tc2show, ",")) : null; return array('menu' => $treeMenu, 'leaves' => $tc2show, 'tree' => $test_spec); }
/** * Filter out the testcases that don't have the given value * in their custom field(s) from the tree. * Recursive function. * * @author Andreas Simon * @since 1.9 * * @param array &$tcase_tree reference to test case set/tree to filter * @param array &$cf_hash reference to selected custom field information * @param resource &$db reference to DB handler object * @param int $node_type_testsuite ID of node type for testsuites * @param int $node_type_testcase ID of node type for testcase * * @return array $tcase_tree filtered tree structure * * @internal revisions: * * 20100702 - did some changes to logic in here and added a fix for array indexes */ function filter_by_cf_values(&$tcase_tree, &$cf_hash, &$db, $node_type_testsuite, $node_type_testcase) { static $tables = null; static $debugMsg = null; if (!$debugMsg) { $tables = tlObject::getDBTables(array('cfield_design_values', 'nodes_hierarchy')); $debugMsg = 'Function: ' . __FUNCTION__; } $node_deleted = false; // This code is in parts based on (NOT simply copy/pasted) // some filter code used in testplan class. // Implemented because we have a tree here, // not simple one-dimensional array of testcases like in tplan class. foreach ($tcase_tree as $key => $node) { if ($node['node_type_id'] == $node_type_testsuite) { $delete_suite = false; if (isset($node['childNodes']) && is_array($node['childNodes'])) { // node is a suite and has children, so recurse one level deeper $tcase_tree[$key]['childNodes'] = filter_by_cf_values($tcase_tree[$key]['childNodes'], $cf_hash, $db, $node_type_testsuite, $node_type_testcase); // now remove testsuite node if it is empty after coming back from recursion if (!count($tcase_tree[$key]['childNodes'])) { $delete_suite = true; } } else { // nothing in here, suite was already empty $delete_suite = true; } if ($delete_suite) { unset($tcase_tree[$key]); $node_deleted = true; } } else { if ($node['node_type_id'] == $node_type_testcase) { // node is testcase, check if we need to delete it $passed = false; //BUGID 2877 - Custom Fields linked to TC versions $sql = " /* {$debugMsg} */ SELECT CFD.value FROM {$tables['cfield_design_values']} CFD," . " {$tables['nodes_hierarchy']} NH" . " WHERE CFD.node_id = NH.id" . " AND NH.parent_id = {$node['id']} AND value in ('" . implode("' , '", $cf_hash) . "')"; $rows = $db->fetchRowsIntoMap($sql, 'value'); //if there exist as many rows as custom fields to be filtered by //the tc does meet the criteria $passed = count($rows) == count($cf_hash) ? true : false; // now delete node if no match was found if (!$passed) { unset($tcase_tree[$key]); $node_deleted = true; } } } } // 20100702 - asimon // if we deleted a note, the numeric indexes of this array do have missing numbers, // which causes problems in later loop constructs in other functions that assume numeric keys // in these arrays without missing numbers in between - crashes JS tree! // -> so I have to fix the array indexes here starting from 0 without missing a key if ($node_deleted) { $tcase_tree = array_values($tcase_tree); } return $tcase_tree; }
function testPlanTree(&$dbHandler, &$menuUrl, $tproject_id, $tproject_name, $tplan_id, $tplan_name, $objFilters, $objOptions) { $debugMsg = ' - Method: ' . __FUNCTION__; $chronos[] = $tstart = microtime(true); $treeMenu = new stdClass(); $treeMenu->rootnode = null; $treeMenu->menustring = ''; $resultsCfg = config_get('results'); $glueChar = config_get('testcase_cfg')->glue_character; $menustring = null; $tplan_tcases = null; $renderTreeNodeOpt = null; $renderTreeNodeOpt['showTestCaseID'] = config_get('treemenu_show_testcase_id'); list($filters, $options, $renderTreeNodeOpt['showTestSuiteContents'], $renderTreeNodeOpt['useCounters'], $renderTreeNodeOpt['useColors'], $colorBySelectedBuild) = initExecTree($objFilters, $objOptions); $tplan_mgr = new testplan($dbHandler); $tproject_mgr = new testproject($dbHandler); $tree_manager = $tplan_mgr->tree_manager; $tcase_node_type = $tree_manager->node_descr_id['testcase']; $hash_descr_id = $tree_manager->get_available_node_types(); $hash_id_descr = array_flip($hash_descr_id); $tcase_prefix = $tproject_mgr->getTestCasePrefix($tproject_id) . $glueChar; $nt2exclude = array('testplan' => 'exclude_me', 'requirement_spec' => 'exclude_me', 'requirement' => 'exclude_me'); $nt2exclude_children = array('testcase' => 'exclude_my_children', 'requirement_spec' => 'exclude_my_children'); // remove test spec, test suites (or branches) that have ZERO test cases linked to test plan // // IMPORTANT: // using 'order_cfg' => array("type" =>'exec_order',"tplan_id" => $tplan_id)) // makes the magic of ignoring test cases not linked to test plan. // This unexpected bonus can be useful on export test plan as XML. // $my['options'] = array('recursive' => true, 'remove_empty_nodes_of_type' => $tree_manager->node_descr_id['testsuite'], 'order_cfg' => array("type" => 'exec_order', "tplan_id" => $tplan_id), 'hideTestCases' => $options['hideTestCases'], 'tc_action_enabled' => $options['tc_action_enabled'], 'showTestCaseExecStatus' => $options['showTestCaseExecStatus']); $my['filters'] = array('exclude_node_types' => $nt2exclude, 'exclude_children_of' => $nt2exclude_children); if (isset($objFilters->filter_toplevel_testsuite) && is_array($objFilters->filter_toplevel_testsuite)) { $my['filters']['exclude_branches'] = $objFilters->filter_toplevel_testsuite; } if (isset($objFilters->filter_custom_fields) && is_array($objFilters->filter_custom_fields)) { $my['filters']['filter_custom_fields'] = $objFilters->filter_custom_fields; } if (property_exists($objOptions, 'actionJS')) { foreach (array('testproject', 'testsuite', 'testcase') as $nk) { if (isset($objOptions->actionJS[$nk])) { $renderTreeNodeOpt['actionJS'][$nk] = $objOptions->actionJS[$nk]; } } } if (property_exists($objOptions, 'nodeHelpText')) { foreach (array('testproject', 'testsuite', 'testcase') as $nk) { if (isset($objOptions->nodeHelpText[$nk])) { $renderTreeNodeOpt['nodeHelpText'][$nk] = $objOptions->nodeHelpText[$nk]; } } } $test_spec = $tplan_mgr->getSkeleton($tplan_id, $tproject_id, $my['filters'], $my['options']); $test_spec['name'] = $tproject_name . " / " . $tplan_name; // To be discussed $test_spec['id'] = $tproject_id; $test_spec['node_type_id'] = $hash_descr_id['testproject']; $test_spec['node_type'] = 'testproject'; $map_node_tccount = array(); $tplan_tcases = array(); if ($test_spec) { if (is_null($filters['tcase_id']) || $filters['tcase_id'] > 0) { // Step 1 - get item set with exec status. // This has to scopes: // 1. tree coloring according exec status on (Test plan, platform, build ) context // 2. produce sql that can be used to reduce item set on combination with filters // that can not be used on this step like: // a. test cases belonging to branch with root TEST SUITE // b. keyword filter on AND MODE // c. execution results on other builds, any build etc // // WE NEED TO ADD FILTERING on CUSTOM FIELD VALUES, WE HAVE NOT REFACTORED // THIS YET. // if (!is_null($sql2do = $tplan_mgr->{$objOptions->getTreeMethod}($tplan_id, $filters, $options))) { $doPinBall = false; if (is_array($sql2do)) { if ($doPinBall = $filters['keyword_filter_type'] == 'And') { $kmethod = "fetchRowsIntoMapAddRC"; $unionClause = " UNION ALL "; } else { $kmethod = "fetchRowsIntoMap"; $unionClause = ' UNION '; } $sql2run = $sql2do['exec'] . $unionClause . $sql2do['not_run']; } else { $kmethod = "fetchRowsIntoMap"; $sql2run = $sql2do; } $tplan_tcases = $dbHandler->{$kmethod}($sql2run, 'tcase_id'); if ($doPinBall && !is_null($tplan_tcases)) { $kwc = count($filters['keyword_id']); $ak = array_keys($tplan_tcases); $mx = null; foreach ($ak as $tk) { if ($tplan_tcases[$tk]['recordcount'] == $kwc) { $mx[$tk] = $tplan_tcases[$tk]; } } $tplan_tcases = null; $tplan_tcases = $mx; } $setTestCaseStatus = $tplan_tcases; } } if (is_null($tplan_tcases)) { $tplan_tcases = array(); } // OK, now we need to work on status filters // if "any" was selected as filtering status, don't filter by status $targetExecStatus = (array) (isset($objFilters->filter_result_result) ? $objFilters->filter_result_result : null); if (!is_null($targetExecStatus) && !in_array($resultsCfg['status_code']['all'], $targetExecStatus)) { applyStatusFilters($tplan_id, $tplan_tcases, $objFilters, $tplan_mgr, $resultsCfg['status_code']); } if (isset($my['filters']['filter_custom_fields']) && isset($test_spec['childNodes'])) { $test_spec['childNodes'] = filter_by_cf_values($dbHandler, $test_spec['childNodes'], $my['filters']['filter_custom_fields'], $hash_descr_id); } // here we have LOT OF CONFUSION, sometimes we use $my['options'] other $options $pnFilters = null; $pnOptions = array('hideTestCases' => $my['options']['hideTestCases'], 'viewType' => 'executionTree'); $testcase_counters = prepareExecTreeNode($dbHandler, $test_spec, $map_node_tccount, $tplan_tcases, $pnFilters, $pnOptions); foreach ($testcase_counters as $key => $value) { $test_spec[$key] = $testcase_counters[$key]; } $keys = array_keys($tplan_tcases); $renderTreeNodeOpt['hideTestCases'] = $my['options']['hideTestCases']; // $renderTreeNodeOpt['tc_action_enabled'] = 1; $renderTreeNodeOpt['tc_action_enabled'] = isset($my['options']['tc_action_enabled']) ? $my['options']['tc_action_enabled'] : 1; // $renderTreeNodeOpt['nodeHelpText'] = $my['options']['nodeHelpText']; $renderTreeNodeOpt['showTestCaseExecStatus'] = $my['options']['showTestCaseExecStatus']; $menustring = renderExecTreeNode(1, $test_spec, $tplan_tcases, $hash_id_descr, $menuUrl, $tcase_prefix, $renderTreeNodeOpt); } // if($test_spec) $treeMenu->rootnode = new stdClass(); $treeMenu->rootnode->name = $test_spec['text']; $treeMenu->rootnode->id = $test_spec['id']; $treeMenu->rootnode->leaf = $test_spec['leaf']; $treeMenu->rootnode->text = $test_spec['text']; $treeMenu->rootnode->position = $test_spec['position']; $treeMenu->rootnode->href = $test_spec['href']; if (!is_null($menustring)) { // Change key ('childNodes') to the one required by Ext JS tree. if (isset($test_spec['childNodes'])) { $menustring = str_ireplace('childNodes', 'children', json_encode($test_spec['childNodes'])); } // Remove null elements (Ext JS tree do not like it ). // :null happens on -> "children":null,"text" that must become "children":[],"text" // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); $menustring = str_ireplace(array(':null', ',null', 'null,', 'null'), array(':[]', '', '', ''), $menustring); } $treeMenu->menustring = $menustring; return array($treeMenu, $keys); }
/** * Filter out the testcases that don't have the given value * in their custom field(s) from the tree. * Recursive function. * * @author Andreas Simon * @since 1.9 * * @param array &$tcase_tree reference to test case set/tree to filter * @param array &$cf_hash reference to selected custom field information * @param resource &$db reference to DB handler object * @param int $node_type_testsuite ID of node type for testsuites * @param int $node_type_testcase ID of node type for testcase * * @return array $tcase_tree filtered tree structure * * @internal revisions: * * 20100702 - did some changes to logic in here and added a fix for array indexes */ function filter_by_cf_values(&$tcase_tree, &$cf_hash, &$db, $node_type_testsuite, $node_type_testcase) { static $tables = null; static $debugMsg = null; if (!$debugMsg) { $tables = tlObject::getDBTables('cfield_design_values'); $debugMsg = 'Function: ' . __FUNCTION__; } $node_deleted = false; // This code is in parts based on (NOT simply copy/pasted) // some filter code used in testplan class. // Implemented because we have a tree here, // not simple one-dimensional array of testcases like in tplan class. foreach ($tcase_tree as $key => $node) { if ($node['node_type_id'] == $node_type_testsuite) { $delete_suite = false; if (isset($node['childNodes']) && is_array($node['childNodes'])) { // node is a suite and has children, so recurse one level deeper $tcase_tree[$key]['childNodes'] = filter_by_cf_values($tcase_tree[$key]['childNodes'], $cf_hash, $db, $node_type_testsuite, $node_type_testcase); // now remove testsuite node if it is empty after coming back from recursion if (!count($tcase_tree[$key]['childNodes'])) { $delete_suite = true; } } else { // nothing in here, suite was already empty $delete_suite = true; } if ($delete_suite) { unset($tcase_tree[$key]); $node_deleted = true; } } else { if ($node['node_type_id'] == $node_type_testcase) { // node is testcase, check if we need to delete it $passed = false; foreach ($cf_hash as $cf_id => $cf_value) { // there will never be more than one record that has a field_id / node_id combination $sql = " /* {$debugMsg} */ SELECT value FROM {$tables['cfield_design_values']} " . " WHERE field_id = {$cf_id} " . " AND node_id = {$node['id']} "; $result = $db->exec_query($sql); $row = $db->fetch_array($result); // push both to arrays so we can compare $possibleValues = explode('|', $row['value']); $valuesSelected = explode('|', $cf_value); // we want to match any selected item from list and checkboxes. if (count($valuesSelected)) { foreach ($valuesSelected as $vs_id => $vs_value) { $found = array_search($vs_value, $possibleValues); if (is_int($found)) { $passed = true; } else { $passed = false; break; } } } // jumping out of foreach here creates an AND search // removing this if would cause OR search --> the first found value counts if (!$passed) { break; } } // now delete node if no match was found if (!$passed) { unset($tcase_tree[$key]); $node_deleted = true; } } } } // 20100702 - asimon // if we deleted a note, the numeric indexes of this array do have missing numbers, // which causes problems in later loop constructs in other functions that assume numeric keys // in these arrays without missing numbers in between - crashes JS tree! // -> so I have to fix the array indexes here starting from 0 without missing a key if ($node_deleted) { $tcase_tree = array_values($tcase_tree); } return $tcase_tree; }