PageRenderTime 51ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/results/resultsImport.php

https://bitbucket.org/pfernandez/testlink1.9.6
PHP | 631 lines | 405 code | 71 blank | 155 comment | 67 complexity | 98b3064197f88dd88a1c2da5dca89c41 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0
  1. <?php
  2. /**
  3. * TestLink Open Source Project - http://testlink.sourceforge.net/
  4. * This script is distributed under the GNU General Public License 2 or later.
  5. *
  6. * Results import from XML file
  7. *
  8. * @package TestLink
  9. * @author Kevin Levy
  10. * @copyright 2010, TestLink community
  11. * @version CVS: $Id: resultsImport.php,v 1.20 2010/08/23 19:27:47 franciscom Exp $
  12. *
  13. * @internal Revisions:
  14. *
  15. * 20100823 - franciscom - BUGID 3543 - added execution_type
  16. * 20100821 - franciscom - BUGID 3470 - reopened
  17. * 20100328 - franciscom - BUGID 3470, BUGID 3475
  18. * 20100328 - franciscom - BUGID 3331 add bug id management
  19. * 20100214 - franciscom - xml managed using simpleXML
  20. *
  21. **/
  22. require('../../config.inc.php');
  23. require_once('common.php');
  24. require_once('csv.inc.php');
  25. require_once('xml.inc.php');
  26. testlinkInitPage($db);
  27. $templateCfg = templateConfiguration();
  28. $args = init_args($db);
  29. $gui = new stdClass();
  30. // 20100821 - franciscom
  31. // CRITIC:
  32. // All this logics is done to extract from referer important parameters
  33. // like: build id, etc.
  34. // Is not very clear why we have choose to use this logic, but when doing the filter refactoring
  35. // changes on key names (from build_id -> setting_build, and others), broken the code.
  36. //
  37. // On 20100821 I've (franciscom) choose a different approach:
  38. // changing the javascript function openImportResult() in test_automation.js.
  39. // Then I will remove this logic.
  40. //
  41. // $ref=$_SERVER['HTTP_REFERER'];
  42. // $url_array=preg_split('/[?=&]/',$ref);
  43. // $key2extract = array('build_id' => 'buildID','platform_id' => 'platformID', 'tplan_id' => 'tplanID');
  44. // foreach($key2extract as $accessKey => $memberKey)
  45. // {
  46. // if( in_array($accessKey,$url_array) )
  47. // {
  48. // $dummyIndex = array_search($accessKey,$url_array) + 1;
  49. // $args->$memberKey=$url_array[$dummyIndex];
  50. // }
  51. //
  52. // }
  53. $gui->import_title=lang_get('title_results_import_to');
  54. $gui->buildID=$args->buildID;
  55. $gui->platformID=$args->platformID;
  56. $gui->tplanID=$args->tplanID;
  57. $gui->file_check=array('status_ok' => 1, 'msg' => 'ok');
  58. $gui->importTypes=array("XML" => "XML");
  59. $gui->importLimit = config_get('import_file_max_size_bytes');
  60. $gui->doImport = ($args->importType != "");
  61. $gui->testprojectName=$args->testprojectName;
  62. $resultMap=null;
  63. $dest=TL_TEMP_PATH . session_id()."-results.import";
  64. $container_description=lang_get('import_xml_results');
  65. if ($args->doUpload)
  66. {
  67. // check the uploaded file
  68. $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null;
  69. if (($source != 'none') && ($source != ''))
  70. {
  71. $gui->file_check['status_ok']=1;
  72. if($gui->file_check['status_ok'])
  73. {
  74. if (move_uploaded_file($source, $dest))
  75. {
  76. switch($args->importType)
  77. {
  78. case 'XML':
  79. $pcheck_fn="check_xml_execution_results";
  80. $pimport_fn="importExecutionResultsFromXML";
  81. break;
  82. }
  83. if ($pcheck_fn)
  84. {
  85. $gui->file_check=$pcheck_fn($dest);
  86. if($gui->file_check['status_ok'])
  87. {
  88. if ($pimport_fn)
  89. {
  90. $resultMap=$pimport_fn($db,$dest,$args);
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. else
  98. {
  99. $gui->file_check=array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import'));
  100. $args->importType=null;
  101. }
  102. }
  103. $gui->resultMap=$resultMap;
  104. $smarty=new TLSmarty();
  105. $smarty->assign('gui',$gui);
  106. $smarty->display($templateCfg->template_dir . $templateCfg->default_template);
  107. ?>
  108. <?php
  109. /*
  110. function:
  111. args :
  112. returns:
  113. */
  114. function importExecutionResultsFromXML(&$db,$fileName,$context)
  115. {
  116. $resultMap=null;
  117. $xml = @simplexml_load_file($fileName);
  118. if($xml !== FALSE)
  119. {
  120. $resultMap = importResults($db,$xml,$context);
  121. }
  122. return $resultMap;
  123. }
  124. /*
  125. function:
  126. args :
  127. returns:
  128. */
  129. function importResults(&$db,&$xml,$context)
  130. {
  131. $resultMap = null;
  132. if($xml->getName() == 'results')
  133. {
  134. // check if additional data (context execution) has been provided,
  135. // if yes overwrite GUI selection with value get from file
  136. //
  137. $executionContext = $context;
  138. $contextKeys = array('testproject' => 'tprojectID', 'testplan' => 'tplanID',
  139. 'build' => 'buildID', 'platform' => 'platformID');
  140. foreach( $contextKeys as $xmlkey => $execkey)
  141. {
  142. if( ($joker = $xml->$xmlkey) )
  143. {
  144. $executionContext->$execkey = (int) $joker['id'];
  145. }
  146. }
  147. $xmlTCExec = $xml->xpath("//testcase");
  148. $resultData = importExecutionsFromXML($xmlTCExec);
  149. if ($resultData)
  150. {
  151. $resultMap = saveImportedResultData($db,$resultData,$executionContext);
  152. }
  153. }
  154. return $resultMap;
  155. }
  156. /*
  157. function: saveImportedResultData
  158. args :
  159. returns:
  160. rev:
  161. 20100823 - franciscom - BUGID 3543 - added execution_type
  162. 20100328 - franciscom - BUGID 3331 manage bug id
  163. */
  164. function saveImportedResultData(&$db,$resultData,$context)
  165. {
  166. if (!$resultData)
  167. {
  168. return;
  169. }
  170. $debugMsg = ' FUNCTION: ' . __FUNCTION__;
  171. $tables = tlObjectWithDB::getDBTables(array('executions','execution_bugs'));
  172. $l18n = array('import_results_tc_not_found' => '' ,'import_results_invalid_result' => '',
  173. 'tproject_id_not_found' => '', 'import_results_ok' => '');
  174. foreach($l18n as $key => $value)
  175. {
  176. $l18n[$key] = lang_get($key);
  177. }
  178. // Get Column definitions to get size dinamically instead of create constants
  179. $columnDef = array();
  180. $adodbObj = $db->get_dbmgr_object();
  181. $columnDef['execution_bugs'] = $adodbObj->MetaColumns($tables['execution_bugs']);
  182. $keySet = array_keys($columnDef['execution_bugs']);
  183. foreach($keySet as $keyName)
  184. {
  185. if( ($keylow=strtolower($keyName)) != $keyName )
  186. {
  187. $columnDef['execution_bugs'][$keylow] = $columnDef['execution_bugs'][$keyName];
  188. unset($columnDef['execution_bugs'][$keyName]);
  189. }
  190. }
  191. $user=new tlUser($context->userID);
  192. $user->readFromDB($db);
  193. $tcase_mgr=new testcase($db);
  194. $resulstCfg=config_get('results');
  195. $tcaseCfg=config_get('testcase_cfg');
  196. $resultMap=array();
  197. $tplan_mgr=null;
  198. $tc_qty=sizeof($resultData);
  199. if($tc_qty)
  200. {
  201. $tplan_mgr=new testplan($db);
  202. $tproject_mgr=new testproject($db);
  203. }
  204. // Need to do checks on common settings
  205. //
  206. // test project exists
  207. //
  208. // test plan id:
  209. // belongs to target test project
  210. // is active
  211. // build id:
  212. // belongs to target test plan
  213. // is open
  214. //
  215. // platform id:
  216. // is linked to target test plan
  217. //
  218. $checks['status_ok'] = true;
  219. $checks['msg'] = null;
  220. $dummy = $tproject_mgr->get_by_id($context->tprojectID);
  221. $checks['status_ok'] = !is_null($dummy);
  222. if( !$checks['status_ok'] )
  223. {
  224. $checks['msg'][] = sprintf($l19n['tproject_id_not_found'],$context->tprojectID);
  225. }
  226. // if( $checks['status_ok'] )
  227. // {
  228. //
  229. // }
  230. if( !$checks['status_ok'] )
  231. {
  232. foreach($checks['msg'] as $warning )
  233. {
  234. $resultMap[]=array($warning);
  235. }
  236. }
  237. $doIt = $checks['status_ok'];
  238. // --------------------------------------------------------------------
  239. for($idx=0; $doIt && $idx < $tc_qty;$idx++)
  240. {
  241. $tester_id = 0;
  242. $tester_name = '';
  243. $using_external_id = false;
  244. $message = null;
  245. $status_ok = true;
  246. $tcase_exec = $resultData[$idx];
  247. $checks = check_exec_values($db,$tcase_mgr,$user_mgr,$tcaseCfg,$tcase_exec,$columnDef['execution_bugs']);
  248. $status_ok = $checks['status_ok'];
  249. if($status_ok)
  250. {
  251. $tcase_id=$checks['tcase_id'];
  252. $tcase_external_id=trim($tcase_exec['tcase_external_id']);
  253. $tester_id=$checks['tester_id'];
  254. // external_id has precedence over internal id
  255. $using_external_id = ($tcase_external_id != "");
  256. }
  257. else
  258. {
  259. foreach($checks['msg'] as $warning )
  260. {
  261. $resultMap[]=array($warning);
  262. }
  263. }
  264. if( $status_ok)
  265. {
  266. $tcase_identity=$using_external_id ? $tcase_external_id : $tcase_id;
  267. $result_code=strtolower($tcase_exec['result']);
  268. $result_is_acceptable=isset($resulstCfg['code_status'][$result_code]) ? true : false;
  269. $notes=$tcase_exec['notes'];
  270. $message=null;
  271. $filters = array('tcase_id' => $tcase_id, 'build_id' => $context->buildID,
  272. 'platform_id' => $context->platformID);
  273. $linked_cases=$tplan_mgr->get_linked_tcversions($context->tplanID,$filters);
  274. $info_on_case=$linked_cases[$tcase_id];
  275. if (!$linked_cases)
  276. {
  277. $message=sprintf($l18n['import_results_tc_not_found'],$tcase_identity);
  278. }
  279. else if (!$result_is_acceptable)
  280. {
  281. $message=sprintf($l18n['import_results_invalid_result'],$tcase_identity,$tcase_exec['result']);
  282. }
  283. else
  284. {
  285. $tcversion_id=$info_on_case['tcversion_id'];
  286. $version=$info_on_case['version'];
  287. $notes=$db->prepare_string(trim($notes));
  288. // N.B.: db_now() returns an string ready to be used in an SQL insert
  289. // example '2008-09-04', while $tcase_exec["timestamp"] => 2008-09-04
  290. //
  291. $execution_ts=($tcase_exec['timestamp'] != '') ? "'" . $tcase_exec["timestamp"] . "'": $db->db_now();
  292. if($tester_id != 0)
  293. {
  294. $tester_name=$tcase_exec['tester'];
  295. }
  296. else
  297. {
  298. $tester_name=$user->login;
  299. $tester_id=$context->userID;
  300. }
  301. // BUGID 3543 - added execution_type
  302. $sql = " /* $debugMsg */ " .
  303. " INSERT INTO {$tables['executions']} (build_id,tester_id,status,testplan_id," .
  304. " tcversion_id,execution_ts,notes,tcversion_number,platform_id,execution_type)" .
  305. " VALUES ({$context->buildID}, {$tester_id},'{$result_code}',{$context->tplanID}, ".
  306. " {$tcversion_id},{$execution_ts},'{$notes}', {$version}, " .
  307. " {$context->platformID}, {$tcase_exec['execution_type']})";
  308. $db->exec_query($sql);
  309. // BUGID 3331
  310. if( isset($tcase_exec['bug_id']) )
  311. {
  312. $execution_id = $db->insert_id($tables['executions']);
  313. foreach($tcase_exec['bug_id'] as $bug_id)
  314. {
  315. $bug_id = trim($bug_id);
  316. $sql = " /* $debugMsg */ " .
  317. " SELECT execution_id AS check_qty FROM {$tables['execution_bugs']} " .
  318. " WHERE bug_id = '{$bug_id}' AND execution_id={$execution_id} ";
  319. $rs = $db->get_recordset($sql);
  320. if( is_null($rs) )
  321. {
  322. $sql = " /* $debugMsg */ " .
  323. " INSERT INTO {$tables['execution_bugs']} (bug_id,execution_id)" .
  324. " VALUES ('" . $db->prepare_string($bug_id) . "', {$execution_id} )";
  325. $db->exec_query($sql);
  326. }
  327. }
  328. }
  329. $message=sprintf($l18n['import_results_ok'],$tcase_identity,$version,$tester_name,
  330. $resulstCfg['code_status'][$result_code],$execution_ts);
  331. }
  332. }
  333. if( !is_null($message) )
  334. {
  335. $resultMap[]=array($message);
  336. }
  337. }
  338. return $resultMap;
  339. }
  340. /*
  341. function: importExecutionsFromXML
  342. args :
  343. returns:
  344. */
  345. function importExecutionsFromXML($xmlTCExecSet)
  346. {
  347. $execInfoSet=null;
  348. if($xmlTCExecSet)
  349. {
  350. $jdx=0;
  351. $exec_qty=sizeof($xmlTCExecSet);
  352. for($idx=0; $idx <$exec_qty ; $idx++)
  353. {
  354. $xmlTCExec=$xmlTCExecSet[$idx];
  355. $execInfo = importExecutionFromXML($xmlTCExec);
  356. if ($execInfo)
  357. {
  358. $execInfoSet[$jdx++]=$execInfo;
  359. }
  360. }
  361. }
  362. return $execInfoSet;
  363. }
  364. /*
  365. function: importExecutionFromXML()
  366. args :
  367. returns:
  368. */
  369. function importExecutionFromXML(&$xmlTCExec)
  370. {
  371. if (!$xmlTCExec)
  372. {
  373. return null;
  374. }
  375. $execInfo=array();;
  376. $execInfo['tcase_id'] = isset($xmlTCExec["id"]) ? (int)$xmlTCExec["id"] : 0;
  377. $execInfo['tcase_external_id'] = (string) $xmlTCExec["external_id"];
  378. // Developer Note - 20100328 - franciscom:
  379. // seems that no PHP error is generated when trying to access an undefined
  380. // property. Do not know if will not be better anyway to use property_exists()
  381. //
  382. $execInfo['tcase_name'] = (string) $xmlTCExec->name;
  383. $execInfo['result'] = (string) trim($xmlTCExec->result);
  384. $execInfo['notes'] = (string) trim($xmlTCExec->notes);
  385. $execInfo['timestamp'] = (string) trim($xmlTCExec->timestamp);
  386. $execInfo['tester'] = (string) trim($xmlTCExec->tester);
  387. $execInfo['execution_type'] = (int) trim($xmlTCExec->execution_type); //BUGID 3543
  388. $bugQty = count($xmlTCExec->bug_id);
  389. if( ($bugQty = count($xmlTCExec->bug_id)) > 0 )
  390. {
  391. foreach($xmlTCExec->bug_id as $bug)
  392. {
  393. $execInfo['bug_id'][] = (string) $bug; // BUGID 3331
  394. }
  395. }
  396. return $execInfo;
  397. }
  398. /*
  399. function:
  400. Check if at least the file starts seems OK
  401. */
  402. function check_xml_execution_results($fileName)
  403. {
  404. $file_check=array('status_ok' => 0, 'msg' => 'xml_ko');
  405. $xml = @simplexml_load_file($fileName);
  406. if($xml !== FALSE)
  407. {
  408. $file_check=array('status_ok' => 1, 'msg' => 'ok');
  409. $elementName = $xml->getName();
  410. if($elementName != 'results')
  411. {
  412. $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_results_import_format'));
  413. }
  414. }
  415. return $file_check;
  416. }
  417. /*
  418. function: init_args(&$dbHandler)
  419. args :
  420. returns:
  421. */
  422. function init_args(&$dbHandler)
  423. {
  424. $args=new stdClass();
  425. $_REQUEST=strings_stripSlashes($_REQUEST);
  426. $args->importType=isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null;
  427. // BUGID 3470
  428. // Need to use REQUEST because sometimes data arrives on GET and other on POST (has hidden fields)
  429. $args->buildID = isset($_REQUEST['buildID']) ? intval($_REQUEST['buildID']) : null;
  430. $args->platformID = isset($_REQUEST['platformID']) ? intval($_REQUEST['platformID']) : null;
  431. $args->tplanID = isset($_REQUEST['tplanID']) ? intval($_REQUEST['tplanID']) : null;
  432. $args->tplanID = !is_null($args->tplanID) ? $args->tplanID : $_SESSION['testplanID'];
  433. $args->tprojectID = isset($_REQUEST['tprojectID']) ? intval($_REQUEST['tprojectID']) : null;
  434. if( is_null($args->tprojectID))
  435. {
  436. $args->tprojectID = $_SESSION['testprojectID'];
  437. $args->testprojectName = $_SESSION['testprojectName'];
  438. }
  439. else
  440. {
  441. $tproject_mgr = new testproject($dbHandler);
  442. $dummy = $tproject_mgr->get_by_id($args->tprojectID);
  443. $args->testprojectName = $dummy['name'];
  444. }
  445. $args->doUpload=isset($_REQUEST['UploadFile']) ? 1 : 0;
  446. $args->userID=$_SESSION['userID'];
  447. return $args;
  448. }
  449. /*
  450. function: check_exec_values()
  451. args :
  452. returns: map
  453. keys:
  454. status_ok -> value=true / false
  455. tcase_id: test case id if controls OK
  456. tester_id: tester_id if controls OK
  457. msg -> array with localized messages
  458. */
  459. function check_exec_values(&$db,&$tcase_mgr,&$user_mgr,$tcaseCfg,$execValues,&$columnDef)
  460. {
  461. $tables = tlObjectWithDB::getDBTables(array('users','execution_bugs'));
  462. $checks=array('status_ok' => false, 'tcase_id' => 0, 'tester_id' => 0, 'msg' => array());
  463. $tcase_id=$execValues['tcase_id'];
  464. $tcase_external_id=trim($execValues['tcase_external_id']);
  465. // external_id has precedence over internal id
  466. $using_external_id = ($tcase_external_id != "");
  467. if($using_external_id)
  468. {
  469. // need to get internal id
  470. $checks['tcase_id']=$tcase_mgr->getInternalID($tcase_external_id,$tcaseCfg->glue_character);
  471. $checks['status_ok']=intval($checks['tcase_id']) > 0 ? true : false;
  472. if(!$checks['status_ok'])
  473. {
  474. $checks['msg'][]=sprintf(lang_get('tcase_external_id_do_not_exists'),$tcase_external_id);
  475. }
  476. }
  477. else
  478. {
  479. // before using internal id, I want to check it's a number
  480. $checks['tcase_id']=$tcase_id;
  481. $checks['status_ok']=intval($checks['tcase_id']) > 0 ? true : false;
  482. if(!$checks['status_ok'])
  483. {
  484. $checks['msg'][]=sprintf(lang_get('tcase_id_is_not_number'),$tcase_id);
  485. }
  486. }
  487. if($checks['status_ok'])
  488. {
  489. // useful for user feedback
  490. $identity=$using_external_id ? $tcase_external_id : $checks['tcase_id'];
  491. }
  492. if($checks['status_ok'] && $execValues['timestamp'] != '' )
  493. {
  494. $checks['status_ok']=isValidISODateTime($execValues['timestamp']);
  495. if(!$checks['status_ok'])
  496. {
  497. $checks['msg'][]=sprintf(lang_get('invalid_execution_timestamp'),$identity,$execValues['timestamp']);
  498. }
  499. }
  500. if($checks['status_ok'] && $execValues['tester'] != '' )
  501. {
  502. $sql = "SELECT id,login FROM {$tables['users']} WHERE login ='" .
  503. $db->prepare_string($execValues['tester']) . "'";
  504. $userInfo=$db->get_recordset($sql);
  505. if(!is_null($userInfo) && isset($userInfo[0]['id']) )
  506. {
  507. $checks['tester_id']=$userInfo[0]['id'];
  508. }
  509. else
  510. {
  511. $checks['status_ok']=false;
  512. $checks['msg'][]=sprintf(lang_get('invalid_tester'),$identity,$execValues['tester']);
  513. }
  514. }
  515. // BUGID 3331
  516. $execValues['bug_id'] = isset($execValues['bug_id']) ? trim((string) $execValues['bug_id']) : '';
  517. if($checks['status_ok'] && $execValues['bug_id'] != '' )
  518. {
  519. if( ($field_len = strlen($execValues['bug_id'])) > $columnDef['bug_id']->max_length )
  520. {
  521. $checks['status_ok']=false;
  522. $checks['msg'][]=sprintf(lang_get('bug_id_invalid_len'),$field_len,$columnDef['bug_id']->max_length);
  523. }
  524. }
  525. // BUGID 3543
  526. if($checks['status_ok'] && isset($execValues['execution_type']) )
  527. {
  528. $execDomain = $tcase_mgr->get_execution_types();
  529. $checks['status_ok'] = isset($execDomain[$execValues['execution_type']]);
  530. if( !$checks['status_ok'] )
  531. {
  532. $checks['msg'][]=sprintf(lang_get('invalid_exec_type'),$execValues['execution_type']);
  533. }
  534. }
  535. return $checks;
  536. }
  537. ?>