PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/testcases/tcImport.php

https://bitbucket.org/pfernandez/testlink1.9.6
PHP | 1058 lines | 706 code | 142 blank | 210 comment | 93 complexity | 8b2c3ba7ffa2ae92915c510c61da8a67 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. * Filename $RCSfile: tcImport.php,v $
  7. * @version $Revision: 1.78 $
  8. * @modified $Date: 2010/08/21 11:54:46 $ by $Author: franciscom $
  9. *
  10. * Scope: control test specification import
  11. *
  12. * Revision:
  13. * 20100821 - franciscom - changes to getStepsFromSimpleXMLObj() due to:
  14. * BUGID 3695: Test Case Steps - Export/Import - missing attribute execution type
  15. *
  16. * 20100719 - amitkhullar - BUGID 3609 - fix for keyword import error
  17. * 20100620 - franciscom - Trying to reduce memory problems using statics on
  18. * saveImportedTCData() after issue 3521
  19. * 20100619 - franciscom - added file size control
  20. * 20100409 - franciscom - added import importance and execution_type
  21. * 20100317 - franciscom - BUGID 3236 - work in progress
  22. * 20100214 - franciscom - refactoring to use only simpleXML functions
  23. * 20100106 - franciscom - Multiple Test Case Steps Feature
  24. * 20090831 - franciscom - preconditions
  25. * 20090506 - Requirements refactoring
  26. * 20090221 - BUGID - Improvement on messages to user when XML file contains
  27. * Custom Field Information.
  28. * 20090206 - BUGID - Import TC-REQ relationship - franciscom
  29. * 20090117 - BUGID 1991 - franciscom
  30. * BUGID 1992 - contribution for XLS import - franciscom
  31. * 20090106 - BUGID - franciscom - added logic to import Test Cases custom field values
  32. * 20081001 - franciscom - added logic to manage too long testcase name
  33. * 20080813 - havlatm - added a few logging
  34. *
  35. * *********************************************************************************** */
  36. require('../../config.inc.php');
  37. require_once('common.php');
  38. require_once('csv.inc.php');
  39. require_once('xml.inc.php');
  40. require_once('../../third_party/phpexcel/reader.php');
  41. testlinkInitPage($db);
  42. $templateCfg = templateConfiguration();
  43. $pcheck_fn=null;
  44. $args = init_args();
  45. $gui = new stdClass();
  46. $gui->importLimitBytes = config_get('import_file_max_size_bytes');
  47. $gui->importLimitKB = ($gui->importLimitBytes / 1024);
  48. $gui->hitCriteria = $args->hit_criteria;
  49. $gui->useRecursion = $args->useRecursion;
  50. $gui->containerID = $args->container_id;
  51. $gui->bImport = tlStringLen($args->importType);
  52. $gui->bIntoProject = $args->bIntoProject;
  53. $gui->resultMap = null;
  54. $dest_common = TL_TEMP_PATH . session_id(). "-importtcs";
  55. $dest_files = array('XML' => $dest_common . ".xml",'XLS' => $dest_common . ".xls");
  56. $dest=$dest_files['XML'];
  57. if(!is_null($args->importType))
  58. {
  59. $dest = $dest_files[$args->importType];
  60. }
  61. $gui->file_check = array('status_ok' => 1, 'msg' => 'ok');
  62. if($args->useRecursion)
  63. {
  64. $gui->import_title = lang_get('title_tsuite_import_to');
  65. $gui->container_description = lang_get('test_suite');
  66. }
  67. else
  68. {
  69. $gui->import_title = lang_get('title_tc_import_to');
  70. $gui->container_description = lang_get('test_case');
  71. }
  72. $gui->container_name = '';
  73. if($args->container_id)
  74. {
  75. $tree_mgr = new tree($db);
  76. $node_info = $tree_mgr->get_node_hierarchy_info($args->container_id);
  77. unset($tree_mgr);
  78. $gui->container_name = $node_info['name'];
  79. if($args->container_id == $args->tproject_id)
  80. {
  81. $gui->container_description=lang_get('testproject');
  82. }
  83. }
  84. if ($args->do_upload)
  85. {
  86. // check the uploaded file
  87. $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null;
  88. tLog('Uploaded file: '.$source);
  89. $doIt = false;
  90. $gui->file_check = null;
  91. if (($source != 'none') && ($source != ''))
  92. {
  93. // ATTENTION:
  94. // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using
  95. // Firefox and Chrome.
  96. if( !($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes) )
  97. {
  98. $gui->file_check['status_ok'] = 0;
  99. $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'),$_FILES['uploadedFile']['size'],$gui->importLimitBytes);
  100. }
  101. }
  102. if($doIt)
  103. {
  104. $gui->file_check['status_ok'] = 1;
  105. if (move_uploaded_file($source, $dest))
  106. {
  107. tLog('Renamed uploaded file: '.$source);
  108. switch($args->importType)
  109. {
  110. case 'XML':
  111. $pcheck_fn = "check_xml_tc_tsuite";
  112. $pimport_fn = "importTestCaseDataFromXML";
  113. break;
  114. case 'XLS':
  115. $pcheck_fn = null;
  116. $pimport_fn = "importTestCaseDataFromSpreadsheet";
  117. break;
  118. }
  119. if(!is_null($pcheck_fn))
  120. {
  121. $gui->file_check = $pcheck_fn($dest,$args->useRecursion);
  122. }
  123. }
  124. if($gui->file_check['status_ok'] && $pimport_fn)
  125. {
  126. tLog('Check is Ok.');
  127. $opt = array();
  128. $opt['useRecursion'] = $args->useRecursion;
  129. $opt['importIntoProject'] = $args->bIntoProject;
  130. $opt['duplicateLogic'] = array('hitCriteria' => $args->hit_criteria,
  131. 'actionOnHit' => $args->action_on_duplicated_name);
  132. $gui->resultMap = $pimport_fn($db,$dest,$args->container_id,$args->tproject_id,$args->userID,$opt);
  133. }
  134. }
  135. else if(is_null($gui->file_check))
  136. {
  137. tLog('Missing upload file','WARNING');
  138. $gui->file_check = array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import'));
  139. $args->importType = null;
  140. }
  141. }
  142. if($args->useRecursion)
  143. {
  144. $obj_mgr = new testsuite($db);
  145. $gui->actionOptions=null;
  146. $gui->hitOptions=null;
  147. }
  148. else
  149. {
  150. $obj_mgr = new testcase($db);
  151. $gui->actionOptions=array('update_last_version' => lang_get('update_last_testcase_version'),
  152. 'generate_new' => lang_get('generate_new_testcase'),
  153. 'create_new_version' => lang_get('create_new_testcase_version'));
  154. $gui->hitOptions=array('name' => lang_get('same_name'),
  155. 'internalID' => lang_get('same_internalID'),
  156. 'externalID' => lang_get('same_externalID'));
  157. }
  158. $gui->testprojectName = $_SESSION['testprojectName'];
  159. $gui->importTypes = $obj_mgr->get_import_file_types();
  160. $gui->action_on_duplicated_name=$args->action_on_duplicated_name;
  161. $smarty = new TLSmarty();
  162. $smarty->assign('gui',$gui);
  163. $smarty->display($templateCfg->template_dir . $templateCfg->default_template);
  164. // --------------------------------------------------------------------------------------
  165. /*
  166. function: importTestCaseDataFromXML
  167. args :
  168. returns:
  169. */
  170. function importTestCaseDataFromXML(&$db,$fileName,$parentID,$tproject_id,$userID,$options=null)
  171. {
  172. tLog('importTestCaseDataFromXML called for file: '. $fileName);
  173. $xmlTCs = null;
  174. $resultMap = null;
  175. $my = array();
  176. $my['options'] = array('useRecursion' => false, 'importIntoProject' => 0,
  177. 'duplicateLogic' => array('hitCriteria' => 'name', 'actionOnHit' => null));
  178. $my['options'] = array_merge($my['options'], (array)$options);
  179. foreach($my['options'] as $varname => $value)
  180. {
  181. $$varname = $value;
  182. }
  183. if (file_exists($fileName))
  184. {
  185. $xml = @simplexml_load_file($fileName);
  186. if($xml !== FALSE)
  187. {
  188. $xmlKeywords = $xml->xpath('//keywords');
  189. $kwMap = null;
  190. if ($xmlKeywords)
  191. {
  192. $tproject = new testproject($db);
  193. $loop2do = sizeof($xmlKeywords);
  194. for($idx = 0; $idx < $loop2do ;$idx++)
  195. {
  196. $tproject->importKeywordsFromSimpleXML($tproject_id,$xmlKeywords[$idx]);
  197. }
  198. $kwMap = $tproject->get_keywords_map($tproject_id);
  199. $kwMap = array_flip($kwMap);
  200. }
  201. if (!$useRecursion && ($xml->getName() == 'testcases') )
  202. {
  203. $resultMap = importTestCasesFromSimpleXML($db,$xml,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic);
  204. }
  205. if ($useRecursion && ($xml->getName() == 'testsuite'))
  206. {
  207. $resultMap = importTestSuitesFromSimpleXML($db,$xml,$parentID,$tproject_id,$userID,$kwMap,$importIntoProject);
  208. }
  209. }
  210. }
  211. return $resultMap;
  212. }
  213. // --------------------------------------------------------------------------------------
  214. /*
  215. function: saveImportedTCData
  216. args :
  217. returns:
  218. rev:
  219. 20100317 - franciscom - manage different criteria to decide that test case
  220. is present on system
  221. 20090204 - franciscom - use value of node_order readed from file
  222. configure create to rename test case if exists
  223. */
  224. function saveImportedTCData(&$db,$tcData,$tproject_id,$container_id,
  225. $userID,$kwMap,$duplicatedLogic = array('hitCriteria' => 'name', 'actionOnHit' => null))
  226. {
  227. static $messages;
  228. static $fieldSizeCfg;
  229. static $feedbackMsg;
  230. static $tcase_mgr;
  231. static $tproject_mgr;
  232. static $req_spec_mgr;
  233. static $req_mgr;
  234. static $safeSizeCfg;
  235. static $linkedCustomFields;
  236. static $tprojectHas;
  237. static $reqSpecSet;
  238. static $getVersionOpt;
  239. if (!$tcData)
  240. {
  241. return;
  242. }
  243. // $tprojectHas = array('customFields' => false, 'reqSpec' => false);
  244. $hasCustomFieldsInfo=false;
  245. $hasRequirements=false;
  246. if(is_null($messages))
  247. {
  248. $feedbackMsg = array();
  249. $messages = array();
  250. $fieldSizeCfg = config_get('field_size');
  251. $tcase_mgr = new testcase($db);
  252. $tproject_mgr = new testproject($db);
  253. $req_spec_mgr = new requirement_spec_mgr($db);
  254. $req_mgr = new requirement_mgr($db);
  255. $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import');
  256. $messages['reqspec_warning'] = lang_get('no_reqspec_defined_can_not_import');
  257. $messages['already_exists_updated'] = lang_get('already_exists_updated');
  258. $messages['original_name'] = lang_get('original_name');
  259. $messages['testcase_name_too_long'] = lang_get('testcase_name_too_long');
  260. $messages['start_warning'] = lang_get('start_warning');
  261. $messages['end_warning'] = lang_get('end_warning');
  262. $messages['testlink_warning'] = lang_get('testlink_warning');
  263. $messages['start_feedback'] = $messages['start_warning'] . "\n" . $messages['testlink_warning'] . "\n";
  264. $feedbackMsg['cfield']=lang_get('cf_value_not_imported_missing_cf_on_testproject');
  265. $feedbackMsg['tcase'] = lang_get('testcase');
  266. $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport');
  267. $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport');
  268. // because name can be changed automatically during item creation
  269. // to avoid name conflict adding a suffix automatically generated,
  270. // is better to use a max size < max allowed size
  271. $safeSizeCfg = new stdClass();
  272. $safeSizeCfg->testcase_name=($fieldSizeCfg->testcase_name) * 0.8;
  273. // Get CF with scope design time and allowed for test cases linked to this test project
  274. // $customFields=$tproject_mgr->get_linked_custom_fields($tproject_id,'testcase','name');
  275. // function get_linked_cfields_at_design($tproject_id,$enabled,$filters=null,
  276. // $node_type=null,$node_id=null,$access_key='id')
  277. //
  278. $linkedCustomFields = $tcase_mgr->cfield_mgr->get_linked_cfields_at_design($tproject_id,1,null,'testcase',null,'name');
  279. $tprojectHas['customFields']=!is_null($linkedCustomFields);
  280. // BUGID - 20090205 - franciscom
  281. $reqSpecSet = $tproject_mgr->getReqSpec($tproject_id,null,array('RSPEC.id','NH.name AS title'),'title');
  282. $tprojectHas['reqSpec'] = (!is_null($reqSpecSet) && count($reqSpecSet) > 0);
  283. $getVersionOpt = array('output' => 'minimun');
  284. }
  285. $resultMap = array();
  286. $tc_qty = sizeof($tcData);
  287. for($idx = 0; $idx <$tc_qty ; $idx++)
  288. {
  289. $tc = $tcData[$idx];
  290. $name = $tc['name'];
  291. $summary = $tc['summary'];
  292. $steps = $tc['steps'];
  293. $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : testcase::DEFAULT_ORDER;
  294. $externalid = $tc['externalid'];
  295. $internalid = $tc['internalid'];
  296. $preconditions = $tc['preconditions'];
  297. $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL;
  298. $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM;
  299. $name_len = tlStringLen($name);
  300. if($name_len > $fieldSizeCfg->testcase_name)
  301. {
  302. // Will put original name inside summary
  303. $xx = $messages['start_feedback'];
  304. $xx .= sprintf($messages['testcase_name_too_long'],$name_len, $fieldSizeCfg->testcase_name) . "\n";
  305. $xx .= $messages['original_name'] . "\n" . $name. "\n" . $messages['end_warning'] . "\n";
  306. $summary = nl2br($xx) . $summary;
  307. $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name);
  308. }
  309. $kwIDs = null;
  310. if (isset($tc['keywords']) && $tc['keywords'])
  311. {
  312. $kwIDs = implode(",",buildKeywordList($kwMap,$tc['keywords']));
  313. }
  314. $doCreate=true;
  315. if( $duplicatedLogic['actionOnHit'] == 'update_last_version' )
  316. {
  317. switch($duplicatedLogic['hitCriteria'])
  318. {
  319. case 'name':
  320. $info = $tcase_mgr->getDuplicatesByName($name,$container_id);
  321. break;
  322. case 'internalID':
  323. $dummy = $tcase_mgr->tree_manager->get_node_hierarchy_info($internalid,$container_id);
  324. if( !is_null($dummy) )
  325. {
  326. $info[$internalid] = $dummy;
  327. }
  328. break;
  329. case 'externalID':
  330. $info = $tcase_mgr->get_by_external($externalid,$container_id);
  331. break;
  332. }
  333. if( !is_null($info) )
  334. {
  335. $tcase_qty = count($info);
  336. switch($tcase_qty)
  337. {
  338. case 1:
  339. $doCreate=false;
  340. $tcase_id = key($info);
  341. $last_version = $tcase_mgr->get_last_version_info($tcase_id,$getVersionOpt);
  342. $tcversion_id = $last_version['id'];
  343. $ret = $tcase_mgr->update($tcase_id,$tcversion_id,$name,$summary,
  344. $preconditions,$steps,$userID,$kwIDs,
  345. $node_order,$exec_type,$importance);
  346. $resultMap[] = array($name,$messages['already_exists_updated']);
  347. break;
  348. case 0:
  349. $doCreate=true;
  350. break;
  351. default:
  352. $doCreate=false;
  353. break;
  354. }
  355. }
  356. }
  357. if( $doCreate )
  358. {
  359. $createOptions = array( 'check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME,
  360. 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit']);
  361. if ($ret = $tcase_mgr->create($container_id,$name,$summary,$preconditions,$steps,
  362. $userID,$kwIDs,$node_order,testcase::AUTOMATIC_ID,
  363. $exec_type,$importance,$createOptions))
  364. {
  365. $resultMap[] = array($name,$ret['msg']);
  366. }
  367. }
  368. // 20090106 - franciscom
  369. // Custom Fields Management
  370. // Check if CF with this name and that can be used on Test Cases is defined in current Test Project.
  371. // If Check fails => give message to user.
  372. // Else Import CF data
  373. //
  374. $hasCustomFieldsInfo = (isset($tc['customfields']) && !is_null($tc['customfields']));
  375. if($hasCustomFieldsInfo)
  376. {
  377. if($tprojectHas['customFields'])
  378. {
  379. $msg = processCustomFields($tcase_mgr,$name,$ret['id'],$tc['customfields'],$linkedCustomFields,$feedbackMsg);
  380. if( !is_null($msg) )
  381. {
  382. $resultMap = array_merge($resultMap,$msg);
  383. }
  384. }
  385. else
  386. {
  387. // Can not import Custom Fields Values, give feedback
  388. $msg[]=array($name,$messages['cf_warning']);
  389. $resultMap = array_merge($resultMap,$msg);
  390. }
  391. }
  392. // BUGID - 20090205 - franciscom
  393. // Requirements Management
  394. // Check if Requirement ...
  395. // If Check fails => give message to user.
  396. // Else Import
  397. //
  398. $hasRequirements=(isset($tc['requirements']) && !is_null($tc['requirements']));
  399. if($hasRequirements)
  400. {
  401. if( $tprojectHas['reqSpec'] )
  402. {
  403. $msg = processRequirements($db,$req_mgr,$name,$ret['id'],$tc['requirements'],$reqSpecSet,$feedbackMsg);
  404. if( !is_null($msg) )
  405. {
  406. $resultMap = array_merge($resultMap,$msg);
  407. }
  408. }
  409. else
  410. {
  411. $msg[]=array($name,$messages['reqspec_warning']);
  412. $resultMap = array_merge($resultMap,$msg);
  413. }
  414. }
  415. }
  416. return $resultMap;
  417. }
  418. // --------------------------------------------------------------------------------------
  419. /*
  420. function: buildKeywordList
  421. args :
  422. returns:
  423. */
  424. function buildKeywordList($kwMap,$keywords)
  425. {
  426. $items = array();
  427. $loop2do = sizeof($keywords);
  428. for($jdx = 0; $jdx <$loop2do ; $jdx++)
  429. {
  430. $items[] = $kwMap[trim($keywords[$jdx]['name'])];
  431. }
  432. return $items;
  433. }
  434. // --------------------------------------------------------------------------------------
  435. // --------------------------------------------------------------------------------------
  436. /*
  437. function: Check if at least the file starts seems OK
  438. */
  439. function check_xml_tc_tsuite($fileName,$recursiveMode)
  440. {
  441. $xml = @simplexml_load_file($fileName);
  442. $file_check = array('status_ok' => 0, 'msg' => 'xml_load_ko');
  443. if($xml !== FALSE)
  444. {
  445. $file_check = array('status_ok' => 1, 'msg' => 'ok');
  446. $elementName = $xml->getName();
  447. if($recursiveMode)
  448. {
  449. if($elementName != 'testsuite')
  450. {
  451. $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_xml_tsuite_file'));
  452. }
  453. }
  454. else
  455. {
  456. if($elementName != 'testcases' && $elementName != 'testcase')
  457. {
  458. $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_xml_tcase_file'));
  459. }
  460. }
  461. }
  462. return $file_check;
  463. }
  464. // *****************************************************************************************
  465. // Contributed code - lightbulb
  466. // *****************************************************************************************
  467. /*
  468. function: importTestCaseDataFromSpreadsheet
  469. convert a XLS file to XML, and call importTestCaseDataFromXML() to do import.
  470. args: db [reference]: db object
  471. fileName: XLS file name
  472. parentID: testcases parent node (container)
  473. tproject_id: testproject where to import testcases
  474. userID: who is doing import.
  475. useRecursion: 1 -> recursive, used when importing testsuites
  476. [importIntoProject]: default 0
  477. returns: map
  478. rev:
  479. Original code by lightbulb.
  480. Refactoring by franciscom
  481. */
  482. function importTestCaseDataFromSpreadsheet(&$db,$fileName,$parentID,$tproject_id,
  483. $userID,$useRecursion,$importIntoProject = 0)
  484. {
  485. $xmlTCs = null;
  486. $resultMap = null;
  487. $xml_filename=$fileName . '.xml';
  488. create_xml_tcspec_from_xls($fileName,$xml_filename);
  489. $resultMap=importTestCaseDataFromXML($db,$xml_filename,$parentID,$tproject_id,$userID,
  490. $useRecursion,$importIntoProject);
  491. unlink($fileName);
  492. unlink($xml_filename);
  493. return $resultMap;
  494. }
  495. // --------------------------------------------------------------------------------------
  496. /*
  497. function: create_xml_tcspec_from_xls
  498. Using an XSL file, that contains testcase specifications
  499. creates an XML testlink test specification file.
  500. XLS format:
  501. Column Description
  502. 1 test case name
  503. 2 summary
  504. 3 steps
  505. 4 expectedresults
  506. First row contains header: name,summary,steps,expectedresults
  507. and must be skipped.
  508. args: xls_filename
  509. xml_filename
  510. returns:
  511. */
  512. function create_xml_tcspec_from_xls($xls_filename,$xml_filename)
  513. {
  514. define('FIRST_DATA_ROW',2);
  515. define('IDX_COL_NAME',1);
  516. define('IDX_COL_SUMMARY',2);
  517. define('IDX_COL_STEPS',3);
  518. define('IDX_COL_EXPRESULTS',4);
  519. $xls_handle = new Spreadsheet_Excel_Reader();
  520. $xls_handle->setOutputEncoding(config_get('charset'));
  521. $xls_handle->read($xls_filename);
  522. $xls_rows = $xls_handle->sheets[0]['cells'];
  523. $xls_row_qty = sizeof($xls_rows);
  524. if($xls_row_qty < FIRST_DATA_ROW)
  525. {
  526. return; // >>>----> bye!
  527. }
  528. $xmlFileHandle = fopen($xml_filename, 'w') or die("can't open file");
  529. fwrite($xmlFileHandle,"<testcases>\n");
  530. for($idx = FIRST_DATA_ROW; $idx <= $xls_row_qty; $idx++ )
  531. {
  532. $name = htmlspecialchars($xls_rows[$idx][IDX_COL_NAME]);
  533. fwrite($xmlFileHandle,"<testcase name=" . '"' . $name. '"'.">\n");
  534. // $summary = htmlspecialchars(iconv("CP1252","UTF-8",$xls_rows[$idx][IDX_COL_SUMMARY]));
  535. // 20090117 - contribution - BUGID 1992 // 20090402 - BUGID 1519
  536. // $summary = str_replace('&#x2026;',"...",$xls_rows[$idx][IDX_COL_SUMMARY]);
  537. $summary = convert_special_char($xls_rows[$idx][IDX_COL_SUMMARY]);
  538. $summary = nl2p(htmlspecialchars($summary));
  539. fwrite($xmlFileHandle,"<summary><![CDATA[" . $summary . "]]></summary>\n");
  540. // 20090117 - BUGID 1991,1992 // 20090402 - BUGID 1519
  541. // $steps = str_replace('&#x2026;',"...",$xls_rows[$idx][IDX_COL_STEPS]);
  542. $steps = convert_special_char($xls_rows[$idx][IDX_COL_STEPS]);
  543. $steps = nl2p(htmlspecialchars($steps));
  544. fwrite($xmlFileHandle,"<steps><![CDATA[".$steps."]]></steps>\n");
  545. // 20090117 - BUGID 1991,1992 // 20090402 - BUGID 1519
  546. // $expresults = str_replace('&#x2026;',"...",$xls_rows[$idx][IDX_COL_EXPRESULTS]);
  547. $expresults = convert_special_char($xls_rows[$idx][IDX_COL_EXPRESULTS]);
  548. $expresults = nl2p(htmlspecialchars($expresults));
  549. fwrite($xmlFileHandle,"<expectedresults><![CDATA[".$expresults."]]></expectedresults>\n");
  550. fwrite($xmlFileHandle,"</testcase>\n");
  551. }
  552. fwrite($xmlFileHandle,"</testcases>\n");
  553. fclose($xmlFileHandle);
  554. }
  555. // 20090402 - BUGID 1519: Extract this function from create_xml_tcspec_from_xls()
  556. function convert_special_char($target_string)
  557. {
  558. $from_char = iconv("CP1252", config_get('charset'), '\205');
  559. $to_char = "...";
  560. if ($from_char)
  561. {
  562. return str_replace($from_char, $to_char, $target_string);
  563. }
  564. else
  565. {
  566. return $string;
  567. }
  568. }
  569. /* 20090117 -
  570. contribution by mirosvad -
  571. Convert new line characters from XLS to HTML
  572. */
  573. function nl2p($str)
  574. {
  575. return str_replace('<p></p>', '', '<p>' . preg_replace('#\n|\r#', '</p>$0<p>', $str) . '</p>'); //MS
  576. }
  577. /*
  578. function:
  579. args :
  580. returns:
  581. */
  582. function init_args()
  583. {
  584. $args = new stdClass();
  585. $_REQUEST = strings_stripSlashes($_REQUEST);
  586. $key='action_on_duplicated_name';
  587. $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'generate_new';
  588. $key='hit_criteria';
  589. $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'name';
  590. $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null;
  591. $args->useRecursion = isset($_REQUEST['useRecursion']) ? $_REQUEST['useRecursion'] : 0;
  592. $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null;
  593. $args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0;
  594. $args->bIntoProject = isset($_REQUEST['bIntoProject']) ? intval($_REQUEST['bIntoProject']) : 0;
  595. $args->containerType = isset($_REQUEST['containerType']) ? intval($_REQUEST['containerType']) : 0;
  596. $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0;
  597. $args->userID = $_SESSION['userID'];
  598. $args->tproject_id = $_SESSION['testprojectID'];
  599. return $args;
  600. }
  601. /**
  602. * processCustomFields
  603. *
  604. * Analise custom field info related to test case being imported.
  605. * If everything OK, assign to test case.
  606. * Else return an array of messages.
  607. *
  608. *
  609. */
  610. function processCustomFields(&$tcaseMgr,$tcaseName,$tcaseId,$cfValues,$cfDefinition,$messages)
  611. {
  612. static $missingCfMsg;
  613. $cf2insert=null;
  614. $resultMsg=null;
  615. foreach($cfValues as $value)
  616. {
  617. if( isset($cfDefinition[$value['name']]) )
  618. {
  619. $cf2insert[$cfDefinition[$value['name']]['id']]=array('type_id' => $cfDefinition[$value['name']]['type'],
  620. 'cf_value' => $value['value']);
  621. }
  622. else
  623. {
  624. if( !isset($missingCfMsg[$value['name']]) )
  625. {
  626. $missingCfMsg[$value['name']] = sprintf($messages['cfield'],$value['name'],$messages['tcase']);
  627. }
  628. $resultMsg[] = array($tcaseName,$missingCfMsg[$value['name']]);
  629. }
  630. }
  631. $tcaseMgr->cfield_mgr->design_values_to_db($cf2insert,$tcaseId,null,'simple');
  632. return $resultMsg;
  633. }
  634. /**
  635. * processRequirements
  636. *
  637. * Analise requirements info related to test case being imported.
  638. * If everything OK, assign to test case.
  639. * Else return an array of messages.
  640. *
  641. *
  642. */
  643. function processRequirements(&$dbHandler,&$reqMgr,$tcaseName,$tcaseId,$tcReq,$reqSpecSet,$messages)
  644. {
  645. static $missingReqMsg;
  646. static $missingReqSpecMsg;
  647. static $cachedReqSpec;
  648. $resultMsg=null;
  649. $tables = tlObjectWithDB::getDBTables(array('requirements'));
  650. foreach($tcReq as $ydx => $value)
  651. {
  652. $doit=false;
  653. if( ($doit=isset($reqSpecSet[$value['req_spec_title']])) )
  654. {
  655. if( !(isset($cachedReqSpec[$value['req_spec_title']])) )
  656. {
  657. // $cachedReqSpec
  658. // key: Requirement Specification Title
  659. // value: map with follogin keys
  660. // id => requirement specification id
  661. // req => map with key: requirement document id
  662. $cachedReqSpec[$value['req_spec_title']]['id']=$reqSpecSet[$value['req_spec_title']]['id'];
  663. $cachedReqSpec[$value['req_spec_title']]['req']=null;
  664. }
  665. }
  666. if($doit)
  667. {
  668. $useit=false;
  669. $req_spec_id=$cachedReqSpec[$value['req_spec_title']]['id'];
  670. // Check if requirement with desired document id exists on requirement specification.
  671. // If not => create message for user feedback.
  672. if( !($useit=isset($cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']])) )
  673. {
  674. $sql = " SELECT REQ.id from {$tables['requirements']} REQ " .
  675. " WHERE REQ.req_doc_id='{$dbHandler->prepare_string($value['doc_id'])}' " .
  676. " AND REQ.srs_id={$req_spec_id} ";
  677. $rsx=$dbHandler->get_recordset($sql);
  678. if( $useit=((!is_null($rsx) && count($rsx) > 0) ? true : false) )
  679. {
  680. $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']]=$rsx[0]['id'];
  681. }
  682. }
  683. if($useit)
  684. {
  685. $reqMgr->assign_to_tcase($cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']],$tcaseId);
  686. }
  687. else
  688. {
  689. if( !isset($missingReqMsg[$value['doc_id']]) )
  690. {
  691. $missingReqMsg[$value['doc_id']]=sprintf($messages['req'],
  692. $value['doc_id'],$value['req_spec_title']);
  693. }
  694. $resultMsg[] = array($tcaseName,$missingReqMsg[$value['doc_id']]);
  695. }
  696. }
  697. else
  698. {
  699. // Requirement Specification not found
  700. if( !isset($missingReqSpecMsg[$value['req_spec_title']]) )
  701. {
  702. $missingReqSpecMsg[$value['req_spec_title']]=sprintf($messages['req_spec'],$value['req_spec_title']);
  703. }
  704. $resultMsg[] = array($tcaseName,$missingReqSpecMsg[$value['req_spec_title']]);
  705. }
  706. } //foreach
  707. return $resultMsg;
  708. }
  709. /**
  710. *
  711. *
  712. */
  713. function importTestCasesFromSimpleXML(&$db,&$simpleXMLObj,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic)
  714. {
  715. $resultMap = null;
  716. $xmlTCs = $simpleXMLObj->xpath('//testcase');
  717. $tcData = getTestCaseSetFromSimpleXMLObj($xmlTCs);
  718. if ($tcData)
  719. {
  720. $resultMap = saveImportedTCData($db,$tcData,$tproject_id,$parentID,$userID,$kwMap,$duplicateLogic);
  721. }
  722. return $resultMap;
  723. }
  724. /**
  725. *
  726. *
  727. * @internal revisions
  728. * 20100317 - added internalid - BUGID 3236
  729. */
  730. function getTestCaseSetFromSimpleXMLObj($xmlTCs)
  731. {
  732. $tcSet = null;
  733. if (!$xmlTCs)
  734. {
  735. return $tcSet;
  736. }
  737. $jdx = 0;
  738. $loops2do=sizeof($xmlTCs);
  739. $tcaseSet = array();
  740. $tcXML['elements'] = array('string' => array("summary","preconditions"),
  741. 'integer' => array("node_order","externalid","execution_type","importance"));
  742. $tcXML['attributes'] = array('string' => array("name"), 'integer' =>array('internalid'));
  743. for($idx = 0; $idx < $loops2do; $idx++)
  744. {
  745. $dummy = getItemsFromSimpleXMLObj(array($xmlTCs[$idx]),$tcXML);
  746. $tc = $dummy[0];
  747. if ($tc)
  748. {
  749. // Test Case Steps
  750. $steps = getStepsFromSimpleXMLObj($xmlTCs[$idx]->steps->step);
  751. $tc['steps'] = $steps;
  752. $keywords = getKeywordsFromSimpleXMLObj($xmlTCs[$idx]->keywords->keyword);
  753. if ($keywords)
  754. {
  755. $tc['keywords'] = $keywords;
  756. }
  757. $cf = getCustomFieldsFromSimpleXMLObj($xmlTCs[$idx]->custom_fields->custom_field);
  758. if($cf)
  759. {
  760. $tc['customfields'] = $cf;
  761. }
  762. $requirements = getRequirementsFromSimpleXMLObj($xmlTCs[$idx]->requirements->requirement);
  763. if($requirements)
  764. {
  765. $tc['requirements'] = $requirements;
  766. }
  767. }
  768. $tcaseSet[$jdx++] = $tc;
  769. }
  770. return $tcaseSet;
  771. }
  772. /**
  773. *
  774. *
  775. * @internal revisions
  776. * 20100821 - franciscom - BUGID 3695 - added "execution_type"
  777. */
  778. function getStepsFromSimpleXMLObj($simpleXMLItems)
  779. {
  780. $itemStructure['elements'] = array('string' => array("actions","expectedresults"),
  781. 'integer' => array("step_number","execution_type"));
  782. $itemStructure['transformations'] = array("expectedresults" => "expected_results");
  783. $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure);
  784. // need to do this due to (maybe) a wrong name choice for XML element
  785. if( !is_null($items) )
  786. {
  787. $loop2do = count($items);
  788. for($idx=0; $idx < $loop2do; $idx++)
  789. {
  790. $items[$idx]['expected_results'] = '';
  791. if( isset($items[$idx]['expectedresults']) )
  792. {
  793. $items[$idx]['expected_results'] = $items[$idx]['expectedresults'];
  794. unset($items[$idx]['expectedresults']);
  795. }
  796. }
  797. }
  798. new dBug($items);
  799. return $items;
  800. }
  801. function getCustomFieldsFromSimpleXMLObj($simpleXMLItems)
  802. {
  803. $itemStructure['elements'] = array('string' => array("name","value"));
  804. $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure);
  805. return $items;
  806. }
  807. function getRequirementsFromSimpleXMLObj($simpleXMLItems)
  808. {
  809. $itemStructure['elements'] = array('string' => array("req_spec_title","doc_id","title"));
  810. $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure);
  811. return $items;
  812. }
  813. function getKeywordsFromSimpleXMLObj($simpleXMLItems)
  814. {
  815. $itemStructure['elements'] = array('string' => array("notes"));
  816. $itemStructure['attributes'] = array('string' => array("name"));
  817. $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure);
  818. return $items;
  819. }
  820. /*
  821. function: importTestSuite
  822. args :
  823. returns:
  824. rev: 20090204 - franciscom - added node_order
  825. */
  826. function importTestSuitesFromSimpleXML(&$dbHandler,&$xml,$parentID,$tproject_id,$userID,$kwMap,$importIntoProject = 0)
  827. {
  828. static $tsuiteXML;
  829. static $tsuiteMgr;
  830. static $myself;
  831. static $callCounter = 0;
  832. $resultMap = array();
  833. // $callCounter++;
  834. if(is_null($tsuiteXML) )
  835. {
  836. $tsuiteXML = array();
  837. $tsuiteXML['elements'] = array('string' => array("details"),
  838. 'integer' => array("node_order"));
  839. $tsuiteXML['attributes'] = array('string' => array("name"));
  840. $tsuiteMgr = new testsuite($dbHandler);
  841. $myself = __FUNCTION__;
  842. }
  843. if($xml->getName() == 'testsuite')
  844. {
  845. // getItemsFromSimpleXMLObj() first argument must be an array
  846. $dummy = getItemsFromSimpleXMLObj(array($xml),$tsuiteXML);
  847. $tsuite = current($dummy);
  848. $tsuiteID = $parentID;
  849. if ($tsuite['name'] != "")
  850. {
  851. $ret = $tsuiteMgr->create($parentID,$tsuite['name'],$tsuite['details'],$tsuite['node_order']);
  852. $tsuiteID = $ret['id'];
  853. unset($tsuite);
  854. unset($dummy);
  855. if (!$tsuiteID)
  856. {
  857. return null;
  858. }
  859. }
  860. else if($importIntoProject)
  861. {
  862. $tsuiteID = $tproject_id;
  863. }
  864. $childrenNodes = $xml->children();
  865. $loop2do = sizeof($childrenNodes);
  866. for($idx = 0; $idx < $loop2do; $idx++)
  867. {
  868. $target = $childrenNodes[$idx];
  869. switch($target->getName())
  870. {
  871. case 'testcase':
  872. // getTestCaseSetFromSimpleXMLObj() first argument must be an array
  873. $tcData = getTestCaseSetFromSimpleXMLObj(array($target));
  874. // TEST
  875. $resultMap = array_merge($resultMap,saveImportedTCData($dbHandler,$tcData,$tproject_id,$tsuiteID,$userID,$kwMap));
  876. unset($tcData);
  877. break;
  878. case 'testsuite':
  879. $resultMap = array_merge($resultMap,$myself($dbHandler,$target,$tsuiteID,$tproject_id,$userID,$kwMap));
  880. break;
  881. // do not understand why we need to do this particular logic.
  882. // Need to understand
  883. case 'details':
  884. if (!$importIntoProject)
  885. {
  886. $keywords = getKeywordsFromSimpleXMLObj($target->xpath("//keyword"));
  887. if($keywords)
  888. {
  889. $kwIDs = buildKeywordList($kwMap,$keywords);
  890. $tsuiteMgr->addKeywords($tsuiteID,$kwIDs);
  891. }
  892. }
  893. break;
  894. }
  895. }
  896. }
  897. return $resultMap;
  898. }
  899. ?>