model.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <?php
  2. /**
  3. * The model file of testreport module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Yidong Wang <yidong@cnezsoft.com>
  8. * @package testreport
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. */
  12. class testreportModel extends model
  13. {
  14. /**
  15. * 创建一个测试报告。
  16. * Create a test report.
  17. *
  18. * @param object $testreport
  19. * @access public
  20. * @return int|false
  21. */
  22. public function create($testreport)
  23. {
  24. $this->dao->insert(TABLE_TESTREPORT)->data($testreport)->autocheck()
  25. ->batchCheck($this->config->testreport->create->requiredFields, 'notempty')
  26. ->batchCheck('begin,end', 'notempty')
  27. ->check('end', 'ge', $testreport->begin)
  28. ->exec();
  29. if(dao::isError()) return false;
  30. $reportID = $this->dao->lastInsertID();
  31. $this->loadModel('file')->updateObjectID($this->post->uid, $reportID, 'testreport');
  32. $this->file->saveUpload('testreport', $reportID);
  33. return $reportID;
  34. }
  35. /**
  36. * 更新一个测试报告。
  37. * Update a report.
  38. *
  39. * @param object $report
  40. * @param object $oldReport
  41. * @access public
  42. * @return array|bool
  43. */
  44. public function update($report, $oldReport)
  45. {
  46. $this->dao->update(TABLE_TESTREPORT)->data($report, 'deleteFiles,renameFiles,files')->autocheck()
  47. ->batchCheck($this->config->testreport->edit->requiredFields, 'notempty')
  48. ->batchCheck('begin,end', 'notempty')
  49. ->check('end', 'ge', $report->begin)
  50. ->where('id')->eq($report->id)
  51. ->exec();
  52. if(dao::isError()) return false;
  53. $this->loadModel('file')->processFileDiffsForObject('testreport', $oldReport, $report);
  54. return common::createChanges($oldReport, $report);
  55. }
  56. /**
  57. * 通过 id 获取测试报告。
  58. * Get report by id.
  59. *
  60. * @param int $reportID
  61. * @access public
  62. * @return object|false
  63. */
  64. public function getById($reportID)
  65. {
  66. $report = $this->dao->select('*')->from(TABLE_TESTREPORT)->where('id')->eq($reportID)->fetch();
  67. if(!$report) return false;
  68. $report = $this->loadModel('file')->replaceImgURL($report, 'report');
  69. $report->files = $this->file->getByObject('testreport', $reportID);
  70. return $report;
  71. }
  72. /**
  73. * 获取报告列表。
  74. * Get report list.
  75. *
  76. * @param int $objectID
  77. * @param string $objectType
  78. * @param int $extra
  79. * @param string $orderBy
  80. * @param object $pager
  81. * @access public
  82. * @return array
  83. */
  84. public function getList($objectID, $objectType, $extra = 0, $orderBy = 'id_desc', $pager = null)
  85. {
  86. if(common::isTutorialMode()) return $this->loadModel('tutorial')->getTestReports();
  87. return $this->dao->select('*')->from(TABLE_TESTREPORT)
  88. ->where('deleted')->eq(0)
  89. ->beginIF($objectType == 'execution')->andWhere('execution')->eq($objectID)->fi()
  90. ->beginIF($objectType == 'project')->andWhere('project')->eq($objectID)->fi()
  91. ->beginIF($objectType == 'product' && $extra)->andWhere('objectID')->eq($extra)->andWhere('objectType')->eq('testtask')->fi()
  92. ->beginIF($objectType == 'product' && !$extra)->andWhere('product')->eq($objectID)->fi()
  93. ->orderBy($orderBy)
  94. ->page($pager)
  95. ->fetchAll('id', false);
  96. }
  97. /**
  98. * 获取测试单的用例列表。
  99. * Get task cases.
  100. *
  101. * @param array $tasks
  102. * @param string $begin
  103. * @param string $end
  104. * @param string $idList
  105. * @param object $pager
  106. * @access public
  107. * @return array
  108. */
  109. public function getTaskCases($tasks, $begin, $end, $idList = '', $pager = null)
  110. {
  111. $cases = $this->dao->select('t2.*, t1.task, t1.assignedTo, t1.status, t1.lastRunDate AS maxRunDate')->from(TABLE_TESTRUN)->alias('t1')
  112. ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id')
  113. ->where('t1.task')->in(array_keys($tasks))
  114. ->andWhere('t2.deleted')->eq('0')
  115. ->beginIF($idList)->andWhere('t2.id')->in($idList)->fi()
  116. ->page($pager)
  117. ->fetchGroup('task', 'id');
  118. $results = $this->dao->select('t1.*, t2.task')->from(TABLE_TESTRESULT)->alias('t1')
  119. ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.run = t2.id')
  120. ->where('t2.task')->in(array_keys($tasks))
  121. ->andWhere('t1.date')->ge($begin)
  122. ->andWhere('t1.date')->le($end . " 23:59:59")
  123. ->orderBy('date')
  124. ->fetchGroup('task', 'case');
  125. foreach($cases as $taskID => $caseList)
  126. {
  127. $taskResults = zget($results, $taskID, array());
  128. foreach($caseList as $caseID => $case)
  129. {
  130. $result = zget($taskResults, $caseID, '');
  131. $case->lastRunner = $result ? $result->lastRunner : '';
  132. $case->lastRunDate = $result ? $result->date : '';
  133. $case->lastRunResult = $result ? $result->caseResult : '';
  134. $case->status = ($result && $result->caseResult == 'blocked') ? 'blocked' : 'normal';
  135. }
  136. }
  137. return $cases;
  138. }
  139. /**
  140. * 获取测试报告的用例列表。
  141. * Get case id list.
  142. *
  143. * @param int $reportID
  144. * @access public
  145. * @return array
  146. */
  147. public function getCaseIdList($reportID)
  148. {
  149. return $this->dao->select('`case`')->from(TABLE_TESTREPORT)->alias('t1')
  150. ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.tasks = t2.task')
  151. ->leftJoin(TABLE_CASE)->alias('t3')->on('t2.case = t3.id')
  152. ->where('t1.id')->eq($reportID)
  153. ->andWhere('t1.deleted')->eq('0')
  154. ->andWhere('t3.deleted')->eq('0')
  155. ->fetchPairs('case');
  156. }
  157. /**
  158. * 获取报告概况。
  159. * Get report summary.
  160. *
  161. * @param array $tasks
  162. * @param array $cases
  163. * @param string $begin
  164. * @param string $end
  165. * @access public
  166. * @return string
  167. */
  168. public function getResultSummary($tasks, $cases, $begin, $end)
  169. {
  170. $caseCount = 0;
  171. $caseIdList = array();
  172. foreach($cases as $caseList)
  173. {
  174. $caseCount += count($caseList);
  175. $caseIdList = array_merge($caseIdList, array_keys($caseList));
  176. }
  177. $results = $this->dao->select('t1.*')->from(TABLE_TESTRESULT)->alias('t1')
  178. ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.run = t2.id')
  179. ->where('t2.task')->in(array_keys($tasks))
  180. ->andWhere('t1.`case`')->in($caseIdList)
  181. ->andWhere('t1.date')->ge($begin)
  182. ->andWhere('t1.date')->le($end . " 23:59:59")
  183. ->orderBy('date')
  184. ->fetchAll('id', false);
  185. $failResults = 0;
  186. $runCasesResults = array();
  187. foreach($results as $result) $runCasesResults[$result->run] = $result->caseResult;
  188. foreach($runCasesResults as $lastResult) if($lastResult != 'pass') $failResults++;
  189. return sprintf($this->lang->testreport->caseSummary, $caseCount, count($runCasesResults), count($results), $failResults);
  190. }
  191. /**
  192. * 获取测试报告的用例执行结果。
  193. * Get per run result for testreport.
  194. *
  195. * @param array $tasks
  196. * @param array|string $cases
  197. * @param string $begin
  198. * @param string $end
  199. * @access public
  200. * @return string
  201. */
  202. public function getPerCaseResult4Report($tasks, $cases, $begin, $end)
  203. {
  204. /* Get case result. */
  205. $datas = $this->dao->select("t1.caseResult AS name, COUNT('t1.*') AS value")->from(TABLE_TESTRESULT)->alias('t1')
  206. ->leftJoin(TABLE_TESTRUN)->alias('t2')
  207. ->on('t1.run = t2.id')
  208. ->where('t2.task')->in(array_keys($tasks))
  209. ->andwhere('t1.date = t2.lastRunDate')
  210. ->andWhere('t1.`case`')->in($cases)
  211. ->andWhere('t1.date')->ge($begin)
  212. ->andWhere('t1.date')->le($end . " 23:59:59")
  213. ->groupBy('t1.caseResult')
  214. ->orderBy('value DESC')
  215. ->fetchAll('name');
  216. if(!$datas) return array();
  217. /* Set case result language item. */
  218. $this->app->loadLang('testcase');
  219. foreach($datas as $result => $data) $data->name = isset($this->lang->testcase->resultList[$result]) ? $this->lang->testcase->resultList[$result] : $this->lang->testtask->unexecuted;
  220. return $datas;
  221. }
  222. /**
  223. * 获取测试报告的用例执行人。
  224. * Get per case runner for testreport.
  225. *
  226. * @param array $tasks
  227. * @param array|string $cases
  228. * @param string $begin
  229. * @param string $end
  230. * @access public
  231. * @return array
  232. */
  233. public function getPerCaseRunner4Report($tasks, $cases, $begin, $end)
  234. {
  235. /* Get the last runner and the number of runs. */
  236. $datas = $this->dao->select("t1.lastRunner AS name, COUNT('t1.*') AS value")->from(TABLE_TESTRESULT)->alias('t1')
  237. ->leftJoin(TABLE_TESTRUN)->alias('t2')
  238. ->on('t1.run= t2.id')
  239. ->where('t2.task')->in(array_keys($tasks))
  240. ->andwhere('t1.date = t2.lastRunDate')
  241. ->andWhere('t1.`case`')->in($cases)
  242. ->andWhere('t1.date')->ge($begin)
  243. ->andWhere('t1.date')->le($end . " 23:59:59")
  244. ->groupBy('t1.lastRunner')
  245. ->orderBy('value DESC')
  246. ->fetchAll('name');
  247. if(!$datas) return array();
  248. /* Set the realname of the last runner. */
  249. $users = $this->loadModel('user')->getPairs('noclosed|noletter');
  250. foreach($datas as $result => $data) $data->name = $result ? zget($users, $result, $result) : $this->lang->testtask->unexecuted;
  251. return $datas;
  252. }
  253. /**
  254. * 为测试报告获取 bugs。
  255. * Get bugs for test
  256. *
  257. * @param array|bool $builds
  258. * @param int|array $product
  259. * @param string $begin
  260. * @param string $end
  261. * @param string $type
  262. * @access public
  263. * @return array|false
  264. */
  265. public function getBugs4Test($builds, $product, $begin, $end, $type = 'build')
  266. {
  267. $bugIdList = '';
  268. if(is_array($builds))
  269. {
  270. $childBuilds = $this->getChildBuilds($builds);
  271. foreach($builds as $build) $bugIdList .= $build->bugs . ',';
  272. foreach($childBuilds as $childBuild) $bugIdList .= $childBuild->bugs . ',';
  273. }
  274. $bugIdList = array_unique(array_filter(explode(',', $bugIdList)));
  275. return $this->dao->select('*')->from(TABLE_BUG)->where('deleted')->eq(0)
  276. ->andWhere('product')->in($product)
  277. ->andWhere('openedDate')->lt("{$begin} 23:59:59")
  278. ->beginIF(is_array($builds) && $type == 'build')->andWhere('id')->in($bugIdList)->fi()
  279. ->beginIF(!is_array($builds) && $type == 'build')->andWhere("(resolvedBuild = 'trunk' and resolvedDate >= '{$begin}' and resolvedDate <= '{$end} 23:59:59')")->fi()
  280. ->beginIF(in_array($type, array('project', 'execution')))->andWhere('id')->in($bugIdList)->fi()
  281. ->fetchAll('id', false);
  282. }
  283. /**
  284. * 获取测试报告的需求。
  285. * Get stories for test.
  286. *
  287. * @param array $builds
  288. * @access public
  289. * @return array
  290. */
  291. public function getStories4Test($builds)
  292. {
  293. $storyIdList = '';
  294. $childBuilds = $this->getChildBuilds($builds);
  295. foreach($builds as $build) $storyIdList .= $build->stories . ',';
  296. foreach($childBuilds as $childBuild) $storyIdList .= $childBuild->stories . ',';
  297. $storyIdList = array_unique(array_filter(explode(',', $storyIdList)));
  298. return $this->dao->select('*')->from(TABLE_STORY)->where('deleted')->eq('0')->andWhere('id')->in($storyIdList)->fetchAll('id', false);
  299. }
  300. /**
  301. * 获取测试报告键对。
  302. * Get pairs.
  303. *
  304. * @param int $productID
  305. * @param int $appendID
  306. * @access public
  307. * @return array
  308. */
  309. public function getPairs($productID = 0, $appendID = 0)
  310. {
  311. return $this->dao->select('id,title')->from(TABLE_TESTREPORT)
  312. ->where('deleted')->eq(0)
  313. ->beginIF($productID)->andWhere('product')->eq($productID)->fi()
  314. ->beginIF($appendID)->orWhere('id')->eq($appendID)->fi()
  315. ->orderBy('id_desc')
  316. ->fetchPairs();
  317. }
  318. /**
  319. * 判断操作是否可以点击。
  320. * Judge an action is clickable or not.
  321. *
  322. * @param object $report
  323. * @param string $action
  324. * @access public
  325. * @return bool
  326. */
  327. public function isClickable($report, $action)
  328. {
  329. return true;
  330. }
  331. /**
  332. * Get child builds.
  333. *
  334. * @param array $builds
  335. * @access public
  336. * @return array
  337. */
  338. public function getChildBuilds($builds)
  339. {
  340. $childBuildIDList = '';
  341. foreach($builds as $build) $childBuildIDList .= $build->builds . ',';
  342. $childBuildIDList = array_unique(array_filter(explode(',', $childBuildIDList)));
  343. if(empty($childBuildIDList)) return array();
  344. return $this->dao->select('id,name,bugs,stories')->from(TABLE_BUILD)->where('id')->in($childBuildIDList)->fetchAll('id');
  345. }
  346. }