tao.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. <?php
  2. class testcaseTao extends testcaseModel
  3. {
  4. /**
  5. * 获取用例的基础数据。
  6. * Fetch base info of a case.
  7. *
  8. * @param int $caseID
  9. * @access protected
  10. * @return object|false
  11. */
  12. protected function fetchBaseInfo($caseID)
  13. {
  14. return $this->dao->select('*')->from(TABLE_CASE)->where('id')->eq($caseID)->fetch();
  15. }
  16. /**
  17. * 查询场景名称。
  18. * Fetch scene name.
  19. *
  20. * @param int $sceneID
  21. * @access protected
  22. * @return void
  23. */
  24. protected function fetchSceneName($sceneID)
  25. {
  26. return $this->dao->findByID($sceneID)->from(TABLE_SCENE)->fetch('title');
  27. }
  28. /**
  29. * 通过用例的 id 列表查询步骤。
  30. * Fetch step by id list.
  31. *
  32. * @param array $caseIdList
  33. * @access protected
  34. * @return array
  35. */
  36. protected function fetchStepsByList($caseIdList)
  37. {
  38. return $this->dao->select('t2.*')->from(TABLE_CASE)->alias('t1')
  39. ->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.case')
  40. ->where('t1.id')->in($caseIdList)
  41. ->andWhere('t1.version=t2.version')
  42. ->orderBy('t2.id')
  43. ->fetchGroup('case');
  44. }
  45. /**
  46. * 获取某个项目下某个模块的用例列表。
  47. * Get project cases of a module.
  48. *
  49. * @param int|array $productID
  50. * @param int|string $branch
  51. * @param int $moduleIdList
  52. * @param string $browseType
  53. * @param string $auto no|unit
  54. * @param string $caseType
  55. * @param string $orderBy
  56. * @param object $pager
  57. * @access protected
  58. * @return array
  59. */
  60. protected function getModuleProjectCases($productID, $branch = 0, $moduleIdList = 0, $browseType = '', $auto = 'no', $caseType = '', $orderBy = 'id_desc', $pager = null)
  61. {
  62. return $this->dao->select('DISTINCT t1.product, t1.case, t1.version, t2.*, t4.title AS storyTitle')->from(TABLE_PROJECTCASE)->alias('t1')
  63. ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id')
  64. ->leftJoin(TABLE_PROJECTSTORY)->alias('t3')->on('t3.story = t2.story')
  65. ->leftJoin(TABLE_STORY)->alias('t4')->on('t3.story = t4.id')
  66. ->where('t1.project')->eq((int)$this->session->project)
  67. ->beginIF(!empty($productID))->andWhere('t2.product')->in($productID)->fi()
  68. ->beginIF(!empty($productID) && $branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi()
  69. ->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
  70. ->beginIF($browseType == 'wait')->andWhere('t2.status')->eq($browseType)->fi()
  71. ->beginIF($auto == 'auto' || $auto == 'unit')->andWhere('t2.auto')->eq($auto)->fi()
  72. ->beginIF($auto != 'auto' && $auto != 'unit')->andWhere('t2.auto')->ne('unit')->fi()
  73. ->beginIF($caseType)->andWhere('t2.type')->eq($caseType)->fi()
  74. ->andWhere('t2.deleted')->eq('0')
  75. ->orderBy($orderBy)
  76. ->page($pager, 't1.`case`')
  77. ->fetchAll('id');
  78. }
  79. /**
  80. * 获取待确认的用例列表。
  81. * Get need confirm case list.
  82. *
  83. * @param int|array $productID
  84. * @param string|int $branch
  85. * @param array $modules
  86. * @param string $auto
  87. * @param string $caseType
  88. * @param string $sort
  89. * @param object $pager
  90. * @access protected
  91. * @return array
  92. */
  93. protected function getNeedConfirmList($productID, $branch, $modules, $auto, $caseType, $sort, $pager = null)
  94. {
  95. return $this->dao->select('distinct t1.*, t2.title AS storyTitle')->from(TABLE_CASE)->alias('t1')
  96. ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
  97. ->leftJoin(TABLE_PROJECTCASE)->alias('t3')->on('t1.id = t3.case')
  98. ->where('t2.status')->eq('active')
  99. ->andWhere('t1.deleted')->eq('0')
  100. ->andWhere('t2.version > t1.storyVersion')
  101. ->beginIF(!empty($productID))->andWhere('t1.product')->in($productID)->fi()
  102. ->beginIF(!empty($productID) && $branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
  103. ->beginIF($this->app->tab == 'project')->andWhere('t3.project')->eq($this->session->project)->fi()
  104. ->beginIF($modules)->andWhere('t1.module')->in($modules)->fi()
  105. ->beginIF($auto == 'auto' || $auto == 'unit')->andWhere('t1.auto')->eq($auto)->fi()
  106. ->beginIF($auto != 'auto' && $auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi()
  107. ->beginIF($caseType)->andWhere('t1.type')->eq($caseType)->fi()
  108. ->orderBy($sort)
  109. ->page($pager, 't1.id')
  110. ->fetchAll();
  111. }
  112. /**
  113. * 根据套件获取用例。
  114. * Get cases by suite.
  115. *
  116. * @param int $productID
  117. * @param int|string $branch
  118. * @param int $suiteID
  119. * @param array $moduleIdList
  120. * @param string $auto no|unit
  121. * @param string $orderBy
  122. * @param object $pager
  123. * @access public
  124. * @return array
  125. */
  126. public function getBySuite($productID, $branch = 0, $suiteID = 0, $moduleIdList = 0, $auto = 'no', $orderBy = 'id_desc', $pager = null)
  127. {
  128. return $this->dao->select('t1.*, t2.title AS storyTitle, t3.version AS version')->from(TABLE_CASE)->alias('t1')
  129. ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
  130. ->leftJoin(TABLE_SUITECASE)->alias('t3')->on('t1.id = t3.case')
  131. ->where('t1.product')->eq($productID)
  132. ->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
  133. ->beginIF($this->app->tab == 'project')->andWhere('t1.project')->eq($this->session->project)->fi()
  134. ->andWhere('t3.suite')->eq($suiteID)
  135. ->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
  136. ->beginIF($auto == 'auto' || $auto == 'unit')->andWhere('t1.auto')->eq($auto)->fi()
  137. ->beginIF($auto != 'auto' && $auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi()
  138. ->andWhere('t1.deleted')->eq('0')
  139. ->orderBy($orderBy)
  140. ->page($pager)
  141. ->fetchAll('id');
  142. }
  143. /**
  144. * 插入用例的步骤。
  145. * Insert the steps of the case.
  146. *
  147. * @param int $caseID
  148. * @param array $steps
  149. * @param array $expects
  150. * @param array $stepTypes
  151. * @access protected
  152. * @return void
  153. * @param int $version
  154. */
  155. protected function insertSteps($caseID, $steps, $expects, $stepTypes, $version = 1)
  156. {
  157. $preGrade = 0;
  158. $parentStepID = $grandPaStepID = 0;
  159. foreach($steps as $stepKey => $stepDesc)
  160. {
  161. /* 跳过步骤描述为空的步骤。 */
  162. /* If step desc is empty, skip it. */
  163. if(empty($stepDesc)) continue;
  164. /* 计算步骤类型和层级。 */
  165. /* Set step type and step grade. */
  166. $stepType = $stepTypes[$stepKey];
  167. $grade = substr_count((string)$stepKey, '.');
  168. /* 如果当前步骤层级为0,父ID和祖父ID清0。 */
  169. /* If step grade is zero, set parent step id and grand step id to zero. */
  170. if($grade == 0)
  171. {
  172. $parentStepID = $grandPaStepID = 0;
  173. }
  174. /* 如果前一个步骤的层级比当前步骤的层级大,将父ID设置为祖父ID,祖父ID清0。 */
  175. /* If previous step grade is greater than current step grade, set parent step id to grand step id, and set grand step id to zero. */
  176. elseif($preGrade > $grade)
  177. {
  178. $parentStepID = $grandPaStepID;
  179. $grandPaStepID = 0;
  180. }
  181. /* 构建步骤数据,插入步骤。 */
  182. /* Build step data, and insert it. */
  183. $step = new stdClass();
  184. $step->type = $stepType;
  185. $step->parent = $parentStepID;
  186. $step->case = $caseID;
  187. $step->version = $version;
  188. $step->desc = rtrim(htmlSpecialString($stepDesc));
  189. $step->expect = $stepType == 'group' ? '' : rtrim(htmlSpecialString(zget($expects, $stepKey, '')));
  190. $this->dao->insert(TABLE_CASESTEP)->data($step)->autoCheck()->exec();
  191. /* 如果步骤类型是group,将祖父ID设置为父ID,父ID设置为当前步骤ID。 */
  192. /* If step type is group, set grand step id to parent step id and set parent step id to current step id. */
  193. if($stepType == 'group')
  194. {
  195. $grandPaStepID = $parentStepID;
  196. $parentStepID = $this->dao->lastInsertID();
  197. }
  198. $preGrade = $grade;
  199. }
  200. }
  201. /**
  202. * 获取用例步骤。
  203. * Get case steps.
  204. *
  205. * @param int $caseID
  206. * @param int $version
  207. * @access public
  208. * @return array
  209. */
  210. public function getSteps($caseID, $version)
  211. {
  212. $steps = $this->dao->select('`id`,`desc`,`expect`,`type`,`parent`')->from(TABLE_CASESTEP)->where('`case`')->eq($caseID)->andWhere('version')->eq($version)->orderBy('id')->fetchAll('id');
  213. return $this->processSteps($steps);
  214. }
  215. /**
  216. * 处理用例步骤。
  217. * Process case steps.
  218. *
  219. * @param array $steps
  220. * @access public
  221. * @return array
  222. */
  223. public function processSteps($steps)
  224. {
  225. $caseSteps = array();
  226. $preGrade = 1;
  227. $parentSteps = array();
  228. $key = array(0, 0, 0);
  229. foreach($steps as $step)
  230. {
  231. $parentSteps[$step->id] = $step->parent;
  232. $grade = 1;
  233. if(isset($parentSteps[$step->parent])) $grade = isset($parentSteps[$parentSteps[$step->parent]]) ? 3 : 2;
  234. if($grade > $preGrade)
  235. {
  236. $key[$grade - 1] = 1;
  237. }
  238. else
  239. {
  240. if($grade < $preGrade)
  241. {
  242. if($grade < 2) $key[1] = 0;
  243. if($grade < 3) $key[2] = 0;
  244. }
  245. $key[$grade - 1] ++;
  246. }
  247. $name = implode('.', $key);
  248. $name = str_replace('.0', '', $name);
  249. $data = new stdclass();
  250. $data->name = str_replace('.0', '', $name);
  251. $data->id = $step->id;
  252. $data->step = htmlspecialchars_decode($step->desc);
  253. $data->desc = htmlspecialchars_decode($step->desc);
  254. $data->expect = htmlspecialchars_decode($step->expect);
  255. $data->type = $step->type;
  256. $data->parent = $step->parent;
  257. $data->grade = $grade;
  258. $caseSteps[] = $data;
  259. $preGrade = $grade;
  260. }
  261. return $caseSteps;
  262. }
  263. /**
  264. * 获取相关的需求。
  265. * Get related stories.
  266. *
  267. * @param array $cases
  268. * @access public
  269. * @return array
  270. */
  271. protected function getRelatedStories($cases)
  272. {
  273. $relatedStoryIdList = array();
  274. foreach($cases as $case) $relatedStoryIdList[$case->story] = $case->story;
  275. return $this->dao->select('id, title')->from(TABLE_STORY)->where('id')->in($relatedStoryIdList)->fetchPairs();
  276. }
  277. /**
  278. * 获取相关的用例。
  279. * Get related cases.
  280. *
  281. * @param array $cases
  282. * @access public
  283. * @return array
  284. */
  285. protected function getRelatedCases($cases)
  286. {
  287. $relatedCaseIdList = array();
  288. foreach($cases as $case)
  289. {
  290. $linkCases = explode(',', $case->linkCase);
  291. foreach($linkCases as $linkCaseID)
  292. {
  293. if($linkCaseID) $relatedCaseIdList[$linkCaseID] = trim($linkCaseID);
  294. }
  295. }
  296. return $this->dao->select('id, title')->from(TABLE_CASE)->where('id')->in($relatedCaseIdList)->fetchPairs();
  297. }
  298. /**
  299. * 获取相关的步骤。
  300. * Get related steps.
  301. *
  302. * @param array $caseIdList
  303. * @access public
  304. * @return array
  305. */
  306. protected function getRelatedSteps($caseIdList)
  307. {
  308. return $this->dao->select('DISTINCT t2.id, t2.parent, t2.`case`, t2.version, t2.type, t2.`desc`, t2.expect')
  309. ->from(TABLE_CASE)->alias('t1')
  310. ->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.`case` and t1.version = t2.version')
  311. ->where('t1.id')->in($caseIdList)
  312. ->orderBy('t2.id')
  313. ->fetchGroup('case', 'id');
  314. }
  315. /**
  316. * 获取相关的附件。
  317. * Get related files.
  318. *
  319. * @param array $caseIdList
  320. * @access public
  321. * @return array
  322. */
  323. protected function getRelatedFiles($caseIdList)
  324. {
  325. return $this->dao->select('*')->from(TABLE_FILE)->where('objectType')->eq('testcase')->andWhere('objectID')->in($caseIdList)->andWhere('extra')->ne('editor')->fetchGroup('objectID', 'id');
  326. }
  327. /**
  328. * 创建一个测试用例。
  329. * Create a test case.
  330. *
  331. * @param object $case
  332. * @access protected
  333. * @return bool
  334. */
  335. protected function doCreate($case)
  336. {
  337. if(empty($case->product)) $this->config->testcase->create->requiredFields = str_replace(array('story', 'scene'), '', $this->config->testcase->create->requiredFields);
  338. if(!empty($case->lib)) $this->config->testcase->create->requiredFields = str_replace('product', '', $this->config->testcase->create->requiredFields);
  339. $this->dao->insert(TABLE_CASE)->data($case, 'steps,expects,files,labels,stepType,needReview,scriptFile,scriptName')
  340. ->autoCheck()
  341. ->batchCheck($this->config->testcase->create->requiredFields, 'notempty')
  342. ->checkFlow()
  343. ->exec();
  344. return !dao::isError();
  345. }
  346. /**
  347. * 创建一个测试用例名称和前置条件。
  348. * Create a test case spec.
  349. *
  350. * @param int $caseID
  351. * @param object $case
  352. * @param array|string $files
  353. * @access protected
  354. * @return void
  355. */
  356. protected function doCreateSpec($caseID, $case, $files = array())
  357. {
  358. if(empty($caseID)) return;
  359. $spec = new stdclass();
  360. $spec->case = $caseID;
  361. $spec->version = zget($case, 'version', 1);
  362. $spec->title = $case->title;
  363. $spec->precondition = zget($case, 'precondition', '');
  364. $spec->files = is_string($files) ? $files : implode(',', array_keys($files));
  365. $this->dao->insert(TABLE_CASESPEC)->data($spec)->exec();
  366. }
  367. /**
  368. * 更新一个测试用例。
  369. * Update a test case.
  370. *
  371. * @param object $case
  372. * @access protected
  373. * @return bool
  374. */
  375. protected function doUpdate($case)
  376. {
  377. /* Remove the require field named story when the case is a lib case.*/
  378. $requiredFields = $this->config->testcase->edit->requiredFields;
  379. if(!empty($case->lib)) $requiredFields = str_replace(array('story', 'scene'), '', $requiredFields);
  380. $this->dao->update(TABLE_CASE)->data($case, 'deleteFiles,uid,stepChanged,comment,steps,expects,stepType,linkBug,renameFiles,addedFiles,files')
  381. ->autoCheck()
  382. ->batchCheck($requiredFields, 'notempty')
  383. ->checkFlow()
  384. ->where('id')->eq((int)$case->id)
  385. ->exec();
  386. return !dao::isError();
  387. }
  388. /*
  389. * 处理用例和项目的关系。
  390. * Deal with the relationship between the case and project when edit the case.
  391. *
  392. * @param object $oldCase
  393. * @param object $case
  394. * @access public
  395. * @return void
  396. */
  397. /**
  398. * @param object $oldCase
  399. * @param object $case
  400. */
  401. protected function updateCase2Project($oldCase, $case)
  402. {
  403. $productChanged = $oldCase->product != $case->product;
  404. $storyChanged = $oldCase->story != zget($case, 'story', $oldCase->story);
  405. if(!$productChanged && !$storyChanged) return true;
  406. if($productChanged) $this->dao->update(TABLE_PROJECTCASE)->set('product')->eq($case->product)->set('version')->eq($case->version)->where('`case`')->eq($oldCase->id)->exec();
  407. if($storyChanged)
  408. {
  409. /* 取消之前需求对应项目和用例的关联关系。*/
  410. /* If the new related story isn't linked the project, unlink the case. */
  411. $projects = $this->dao->select('project')->from(TABLE_PROJECTSTORY)->where('story')->eq($oldCase->story)->fetchAll('project');
  412. $this->dao->delete()->from(TABLE_PROJECTCASE)->where('project')->in(array_keys($projects))->andWhere('`case`')->eq($oldCase->id)->exec();
  413. /* 设置需求对应项目和用例的关联关系。*/
  414. /* If the new related story is not null, make the case link the project which link the new related story. */
  415. if(!empty($case->story))
  416. {
  417. $projects = $this->dao->select('project')->from(TABLE_PROJECTSTORY)->where('story')->eq($case->story)->fetchAll('project');
  418. if($projects)
  419. {
  420. $projects = array_keys($projects);
  421. $lastOrders = $this->dao->select('project, MAX(`order`) AS lastOrder')->from(TABLE_PROJECTCASE)->where('project')->in($projects)->groupBy('project')->fetchPairs();
  422. foreach($projects as $projectID)
  423. {
  424. $lastOrder = isset($lastOrders[$projectID]) ? $lastOrders[$projectID] : 0;
  425. $data = new stdclass();
  426. $data->project = $projectID;
  427. $data->product = $case->product;
  428. $data->case = $oldCase->id;
  429. $data->version = $oldCase->version;
  430. $data->order = ++ $lastOrder;
  431. $this->dao->replace(TABLE_PROJECTCASE)->data($data)->autoCheck()->exec();
  432. }
  433. }
  434. }
  435. }
  436. return !dao::isError();
  437. }
  438. /**
  439. * 更新用例步骤。
  440. * Update step.
  441. *
  442. * @param object $case
  443. * @param object $oldCase
  444. * @access protected
  445. * @return bool
  446. */
  447. protected function updateStep($case, $oldCase)
  448. {
  449. if($oldCase->lib && empty($oldCase->product))
  450. {
  451. $fromcaseVersion = $this->dao->select('fromCaseVersion')->from(TABLE_CASE)->where('fromCaseID')->eq($case->id)->fetch('fromCaseVersion');
  452. $fromcaseVersion = (int)$fromcaseVersion + 1;
  453. $this->dao->update(TABLE_CASE)->set('`fromCaseVersion`')->eq($fromcaseVersion)->where('`fromCaseID`')->eq($case->id)->exec();
  454. }
  455. if($case->steps)
  456. {
  457. $this->insertSteps($oldCase->id, $case->steps, $case->expects, (array)$case->stepType, $case->version);
  458. }
  459. else
  460. {
  461. foreach($oldCase->steps as $step)
  462. {
  463. unset($step->id);
  464. unset($step->name);
  465. unset($step->step);
  466. unset($step->grade);
  467. $step->case = $oldCase->id;
  468. $step->version = $case->version;
  469. $this->dao->insert(TABLE_CASESTEP)->data($step)->autoCheck()->exec();
  470. }
  471. }
  472. return !dao::isError();
  473. }
  474. /**
  475. * 更新用例和 bug 的关联关系。
  476. * Link bugs with case.
  477. *
  478. * @param int $caseID
  479. * @param array $linkedBugs
  480. * @param object $case
  481. * @access protected
  482. * @return bool
  483. */
  484. protected function linkBugs($caseID, $linkedBugs, $case)
  485. {
  486. $toLinkBugs = $case->linkBug;
  487. $newBugs = array_diff($toLinkBugs, $linkedBugs);
  488. $removeBugs = array_diff($linkedBugs, $toLinkBugs);
  489. foreach($newBugs as $bugID)
  490. {
  491. $this->dao->update(TABLE_BUG)->set('`case`')->eq($caseID)->set('caseVersion')->eq($case->version)->set('`story`')->eq($case->story)->set('storyVersion')->eq($case->storyVersion)->where('id')->eq($bugID)->exec();
  492. if($this->config->edition != 'open')
  493. {
  494. $relation = new stdClass();
  495. $relation->relation = 'generated';
  496. $relation->AID = $bugID;
  497. $relation->AType = 'bug';
  498. $relation->BID = $caseID;
  499. $relation->BType = 'testcase';
  500. $relation->product = 0;
  501. $this->dao->replace(TABLE_RELATION)->data($relation)->exec();
  502. }
  503. }
  504. foreach($removeBugs as $bugID)
  505. {
  506. $this->dao->update(TABLE_BUG)->set('`case`')->eq(0)->set('caseVersion')->eq(0)->set('`story`')->eq(0)->set('storyVersion')->eq(0)->where('id')->eq($bugID)->exec();
  507. if($this->config->edition != 'open')
  508. {
  509. $this->dao->delete()->from(TABLE_RELATION)
  510. ->where('relation')->eq('generated')
  511. ->andWhere('AID')->eq($bugID)
  512. ->andWhere('AType')->eq('bug')
  513. ->andWhere('BID')->eq($caseID)
  514. ->andWhere('BType')->eq('testcase')
  515. ->exec();
  516. }
  517. }
  518. return !dao::isError();
  519. }
  520. /**
  521. * 解除用例和测试单的关联。
  522. * Unlink case from test task.
  523. *
  524. * @param int $caseID
  525. * @param array $testtasks
  526. * @access protected
  527. * @return bool
  528. * @param int $branch
  529. */
  530. protected function unlinkCaseFromTesttask($caseID, $branch, $testtasks)
  531. {
  532. $this->loadModel('action');
  533. foreach($testtasks as $taskID => $testtask)
  534. {
  535. if(strpos(",{$testtask->branch},", ",{$branch},") === false && $taskID)
  536. {
  537. $this->dao->delete()->from(TABLE_TESTRUN)->where('task')->eq($taskID)->andWhere('`case`')->eq($caseID)->exec();
  538. $this->action->create('case' ,$caseID, 'unlinkedfromtesttask', '', $taskID);
  539. }
  540. }
  541. return !dao::isError();
  542. }
  543. /**
  544. * 获取待评审的用例数量。
  545. * Get the amount of cases pending review.
  546. *
  547. * @access protected
  548. * @return int
  549. */
  550. protected function getReviewAmount()
  551. {
  552. return $this->dao->select('COUNT(id) AS count')->from(TABLE_CASE)->where('status')->eq('wait')->fetch('count');
  553. }
  554. /**
  555. * 导入步骤。
  556. * Import steps.
  557. *
  558. * @param int $caseID
  559. * @param array $steps
  560. * @access public
  561. * @return bool
  562. */
  563. public function importSteps($caseID, $steps)
  564. {
  565. /* 插入步骤。 */
  566. /* Insert steps. */
  567. $parentSteps = array();
  568. foreach($steps as $stepID => $step)
  569. {
  570. $step->case = $caseID;
  571. if(!empty($parentSteps[$step->parent])) $step->parent = $parentSteps[$step->parent];
  572. unset($step->id);
  573. $this->dao->insert(TABLE_CASESTEP)->data($step)->exec();
  574. $parentSteps[$stepID] = $this->dao->lastInsertID();
  575. }
  576. return !dao::isError();
  577. }
  578. /**
  579. * 导入文件。
  580. * Import files.
  581. *
  582. * @param int $caseID
  583. * @param array $files
  584. * @access public
  585. * @return bool
  586. */
  587. public function importFiles($caseID, $files)
  588. {
  589. /* 插入文件。 */
  590. /* Insert files. */
  591. foreach($files as $fileID => $file)
  592. {
  593. if(isset($file->oldpathname))
  594. {
  595. $file->pathname = str_replace('.', "copy{$caseID}.", $file->oldpathname);
  596. if(!empty($file->oldpathname))
  597. {
  598. $originName = pathinfo($file->oldpathname, PATHINFO_FILENAME);
  599. $datePath = substr($file->oldpathname, 0, 6);
  600. $originFile = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . "{$datePath}/" . $originName;
  601. $copyName = $originName . 'copy' . $caseID;
  602. $copyFile = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . "{$datePath}/" . $copyName;
  603. copy($originFile, $copyFile);
  604. }
  605. unset($file->oldpathname);
  606. }
  607. $file->objectID = $caseID;
  608. $file->addedBy = $this->app->user->account;
  609. $file->addedDate = helper::now();
  610. $file->downloads = 0;
  611. unset($file->id);
  612. $this->dao->insert(TABLE_FILE)->data($file)->exec();
  613. }
  614. return !dao::isError();
  615. }
  616. /**
  617. * 保存场景。
  618. * Save scene.
  619. *
  620. * @param array $sceneData
  621. * @param array $sceneList
  622. * @access protected
  623. * @return array
  624. */
  625. protected function saveScene($sceneData, $sceneList)
  626. {
  627. $scene = new stdclass();
  628. $scene->title = $sceneData['name'];
  629. $scene->product = $sceneData['product'];
  630. $scene->branch = $sceneData['branch'];
  631. $scene->module = $sceneData['module'] ?? 0;
  632. if(!isset($sceneData['id']))
  633. {
  634. $scene->openedBy = $this->app->user->account;
  635. $scene->openedDate = helper::now();
  636. $this->dao->insert(TABLE_SCENE)->data($scene)->autoCheck()->exec();
  637. $sceneID = $this->dao->lastInsertID();
  638. $this->dao->update(TABLE_SCENE)->set('sort')->eq($sceneID)->where('id')->eq($sceneID)->exec();
  639. }
  640. else
  641. {
  642. $sceneID = $sceneData['id'];
  643. $scene->lastEditedBy = $this->app->user->account;
  644. $scene->lastEditedDate = helper::now();
  645. $affectedRows = $this->dao->update(TABLE_SCENE)->data($scene)->where('id')->eq($sceneID)->exec();
  646. if(empty($affectedRows)) return array('result' => 'fail', 'message' => sprintf($this->lang->testcase->errorSceneNotExist, $sceneID));
  647. }
  648. if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
  649. $tmpPId = $sceneData['tmpPId'];
  650. $pScene = zget($sceneList, $tmpPId, array());
  651. $this->fixScenePath($sceneID, $pScene);
  652. if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
  653. return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'sceneID' => $sceneID);
  654. }
  655. /**
  656. * 调整场景的路径。
  657. * Fix the scene path.
  658. *
  659. * @param int $sceneID
  660. * @param array $pScene
  661. * @access protected
  662. * @return bool
  663. */
  664. protected function fixScenePath($sceneID, $pScene = array())
  665. {
  666. $parent = 0;
  667. $grade = 1;
  668. $path = ",{$sceneID},";
  669. if(!empty($pScene))
  670. {
  671. $parent = $pScene['id'];
  672. $parentScene = $this->dao->findById((int)$parent)->from(TABLE_SCENE)->fetch();
  673. $path = $parentScene->path . "{$sceneID},";
  674. $grade = $parentScene->grade + 1;
  675. }
  676. $this->dao->update(TABLE_SCENE)->set('parent')->eq($parent)->set('path')->eq($path)->set('grade')->eq($grade)->where('id')->eq($sceneID)->exec();
  677. return !dao::isError();
  678. }
  679. /**
  680. * 基于用例构建场景数据。
  681. * Build scene base on case.
  682. *
  683. * @param object $scene
  684. * @param array $fieldTypes
  685. * @access public
  686. * @return object
  687. */
  688. public function buildSceneBaseOnCase($scene, $fieldTypes)
  689. {
  690. /* Set default value for the fields exist in TABLE_CASE but not in TABLE_SCENE. */
  691. foreach($fieldTypes as $field => $type)
  692. {
  693. if(!isset($scene->{$field})) $scene->{$field} = $type['rule'] == 'int' ? '0' : '';
  694. }
  695. $scene->caseID = $scene->id;
  696. $scene->bugs = 0;
  697. $scene->results = 0;
  698. $scene->caseFails = 0;
  699. $scene->stepNumber = 0;
  700. $scene->isScene = true;
  701. return $scene;
  702. }
  703. /**
  704. * 通过搜索获取执行的用例。
  705. * Get execution cases by search.
  706. *
  707. * @param int $executionID
  708. * @param int $productID
  709. * @param int $branchID
  710. * @param int $paramID
  711. * @param string $orderBy
  712. * @param object $pager
  713. * @access public
  714. * @return array
  715. * */
  716. public function getExecutionCasesBySearch($executionID = 0, $productID = 0, $branchID = 0, $paramID = 0, $orderBy = 'id_desc', $pager = null)
  717. {
  718. if($paramID)
  719. {
  720. $query = $this->loadModel('search')->getQuery($paramID);
  721. if($query)
  722. {
  723. $this->session->set('executionCaseQuery', $query->sql);
  724. $this->session->set('executionCaseForm', $query->form);
  725. }
  726. }
  727. if($this->session->executionCaseQuery === false) $this->session->set('executionCaseQuery', ' 1 = 1');
  728. $caseQuery = '(' . $this->session->executionCaseQuery;
  729. /* 处理用例查询中的产品条件。*/
  730. if(strpos($this->session->executionCaseQuery, "`product` = 'all'") !== false)
  731. {
  732. $caseQuery = str_replace("`product` = 'all'", '1 = 1', $caseQuery);
  733. $caseQuery .= ' AND t2.`product` ' . helper::dbIN($this->app->user->view->products);
  734. }
  735. elseif($productID)
  736. {
  737. $caseQuery .= " AND t2.`product` ='$productID'";
  738. }
  739. /* 处理用例查询中的产品分支条件。*/
  740. if(strpos($caseQuery, "`branch` = 'all'") !== false) $caseQuery = str_replace("`branch` = 'all'", '1 = 1', $caseQuery);
  741. if($branchID !== 'all' && strpos($caseQuery, '`branch` =') === false) $caseQuery .= " AND t2.`branch` in ('$branchID')";
  742. /* 处理用例查询中的版本条件。*/
  743. $caseQuery = str_replace(array('`version`', ' `product`', ' `project`'), array('t2.`version`', ' t2.`product`', ' t1.`project`'), $caseQuery);
  744. $caseQuery .= ')';
  745. return $this->dao->select('distinct t1.*, t2.*')->from(TABLE_PROJECTCASE)->alias('t1')
  746. ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id')
  747. ->where('t1.project')->eq($executionID)
  748. ->andWhere('t2.deleted')->eq('0')
  749. ->andWhere($caseQuery)
  750. ->orderBy($orderBy)
  751. ->page($pager)
  752. ->fetchAll('id');
  753. }
  754. }