* @package testreport * @version $Id$ * @link https://www.zentao.net */ class testreportModel extends model { /** * 创建一个测试报告。 * Create a test report. * * @param object $testreport * @access public * @return int|false */ public function create(object $testreport): int|false { $this->dao->insert(TABLE_TESTREPORT)->data($testreport)->autocheck() ->batchCheck($this->config->testreport->create->requiredFields, 'notempty') ->batchCheck('begin,end', 'notempty') ->check('end', 'ge', $testreport->begin) ->exec(); if(dao::isError()) return false; $reportID = $this->dao->lastInsertID(); $this->loadModel('file')->updateObjectID($this->post->uid, $reportID, 'testreport'); $this->file->saveUpload('testreport', $reportID); return $reportID; } /** * 更新一个测试报告。 * Update a report. * * @param object $report * @param object $oldReport * @access public * @return array|bool */ public function update(object $report, object $oldReport): array|bool { $this->dao->update(TABLE_TESTREPORT)->data($report, 'deleteFiles,renameFiles,files')->autocheck() ->batchCheck($this->config->testreport->edit->requiredFields, 'notempty') ->batchCheck('begin,end', 'notempty') ->check('end', 'ge', $report->begin) ->where('id')->eq($report->id) ->exec(); if(dao::isError()) return false; $this->loadModel('file')->processFileDiffsForObject('testreport', $oldReport, $report); return common::createChanges($oldReport, $report); } /** * 通过 id 获取测试报告。 * Get report by id. * * @param int $reportID * @access public * @return object|false */ public function getById(int $reportID): object|false { $report = $this->dao->select('*')->from(TABLE_TESTREPORT)->where('id')->eq($reportID)->fetch(); if(!$report) return false; $report = $this->loadModel('file')->replaceImgURL($report, 'report'); $report->files = $this->file->getByObject('testreport', $reportID); return $report; } /** * 获取报告列表。 * Get report list. * * @param int $objectID * @param string $objectType * @param int $extra * @param string $orderBy * @param object $pager * @access public * @return array */ public function getList(int $objectID, string $objectType, int $extra = 0, string $orderBy = 'id_desc', ?object $pager = null): array { if(common::isTutorialMode()) return $this->loadModel('tutorial')->getTestReports(); return $this->dao->select('*')->from(TABLE_TESTREPORT) ->where('deleted')->eq(0) ->beginIF($objectType == 'execution')->andWhere('execution')->eq($objectID)->fi() ->beginIF($objectType == 'project')->andWhere('project')->eq($objectID)->fi() ->beginIF($objectType == 'product' && $extra)->andWhere('objectID')->eq($extra)->andWhere('objectType')->eq('testtask')->fi() ->beginIF($objectType == 'product' && !$extra)->andWhere('product')->eq($objectID)->fi() ->orderBy($orderBy) ->page($pager) ->fetchAll('id', false); } /** * 获取测试单的用例列表。 * Get task cases. * * @param array $tasks * @param string $begin * @param string $end * @param string $idList * @param object $pager * @access public * @return array */ public function getTaskCases(array $tasks, string $begin, string $end, string $idList = '', ?object $pager = null): array { $cases = $this->dao->select('t2.*, t1.task, t1.assignedTo, t1.status, t1.lastRunDate AS maxRunDate')->from(TABLE_TESTRUN)->alias('t1') ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') ->where('t1.task')->in(array_keys($tasks)) ->andWhere('t2.deleted')->eq('0') ->beginIF($idList)->andWhere('t2.id')->in($idList)->fi() ->page($pager) ->fetchGroup('task', 'id'); $results = $this->dao->select('t1.*, t2.task')->from(TABLE_TESTRESULT)->alias('t1') ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.run = t2.id') ->where('t2.task')->in(array_keys($tasks)) ->andWhere('t1.date')->ge($begin) ->andWhere('t1.date')->le($end . " 23:59:59") ->orderBy('date') ->fetchGroup('task', 'case'); foreach($cases as $taskID => $caseList) { $taskResults = zget($results, $taskID, array()); foreach($caseList as $caseID => $case) { $result = zget($taskResults, $caseID, ''); $case->lastRunner = $result ? $result->lastRunner : ''; $case->lastRunDate = $result ? $result->date : ''; $case->lastRunResult = $result ? $result->caseResult : ''; $case->status = ($result && $result->caseResult == 'blocked') ? 'blocked' : 'normal'; } } return $cases; } /** * 获取测试报告的用例列表。 * Get case id list. * * @param int $reportID * @access public * @return array */ public function getCaseIdList(int $reportID): array { return $this->dao->select('`case`')->from(TABLE_TESTREPORT)->alias('t1') ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.tasks = t2.task') ->leftJoin(TABLE_CASE)->alias('t3')->on('t2.case = t3.id') ->where('t1.id')->eq($reportID) ->andWhere('t1.deleted')->eq('0') ->andWhere('t3.deleted')->eq('0') ->fetchPairs('case'); } /** * 获取报告概况。 * Get report summary. * * @param array $tasks * @param array $cases * @param string $begin * @param string $end * @access public * @return string */ public function getResultSummary(array $tasks, array $cases, string $begin, string $end): string { $caseCount = 0; $caseIdList = array(); foreach($cases as $caseList) { $caseCount += count($caseList); $caseIdList = array_merge($caseIdList, array_keys($caseList)); } $results = $this->dao->select('t1.*')->from(TABLE_TESTRESULT)->alias('t1') ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.run = t2.id') ->where('t2.task')->in(array_keys($tasks)) ->andWhere('t1.`case`')->in($caseIdList) ->andWhere('t1.date')->ge($begin) ->andWhere('t1.date')->le($end . " 23:59:59") ->orderBy('date') ->fetchAll('id', false); $failResults = 0; $runCasesResults = array(); foreach($results as $result) $runCasesResults[$result->run] = $result->caseResult; foreach($runCasesResults as $lastResult) if($lastResult != 'pass') $failResults++; return sprintf($this->lang->testreport->caseSummary, $caseCount, count($runCasesResults), count($results), $failResults); } /** * 获取测试报告的用例执行结果。 * Get per run result for testreport. * * @param array $tasks * @param array|string $cases * @param string $begin * @param string $end * @access public * @return string */ public function getPerCaseResult4Report(array $tasks, array|string $cases, string $begin, string $end): array { /* Get case result. */ $datas = $this->dao->select("t1.caseResult AS name, COUNT('t1.*') AS value")->from(TABLE_TESTRESULT)->alias('t1') ->leftJoin(TABLE_TESTRUN)->alias('t2') ->on('t1.run = t2.id') ->where('t2.task')->in(array_keys($tasks)) ->andwhere('t1.date = t2.lastRunDate') ->andWhere('t1.`case`')->in($cases) ->andWhere('t1.date')->ge($begin) ->andWhere('t1.date')->le($end . " 23:59:59") ->groupBy('t1.caseResult') ->orderBy('value DESC') ->fetchAll('name'); if(!$datas) return array(); /* Set case result language item. */ $this->app->loadLang('testcase'); foreach($datas as $result => $data) $data->name = isset($this->lang->testcase->resultList[$result]) ? $this->lang->testcase->resultList[$result] : $this->lang->testtask->unexecuted; return $datas; } /** * 获取测试报告的用例执行人。 * Get per case runner for testreport. * * @param array $tasks * @param array|string $cases * @param string $begin * @param string $end * @access public * @return array */ public function getPerCaseRunner4Report(array $tasks, array|string $cases, string $begin, string $end): array { /* Get the last runner and the number of runs. */ $datas = $this->dao->select("t1.lastRunner AS name, COUNT('t1.*') AS value")->from(TABLE_TESTRESULT)->alias('t1') ->leftJoin(TABLE_TESTRUN)->alias('t2') ->on('t1.run= t2.id') ->where('t2.task')->in(array_keys($tasks)) ->andwhere('t1.date = t2.lastRunDate') ->andWhere('t1.`case`')->in($cases) ->andWhere('t1.date')->ge($begin) ->andWhere('t1.date')->le($end . " 23:59:59") ->groupBy('t1.lastRunner') ->orderBy('value DESC') ->fetchAll('name'); if(!$datas) return array(); /* Set the realname of the last runner. */ $users = $this->loadModel('user')->getPairs('noclosed|noletter'); foreach($datas as $result => $data) $data->name = $result ? zget($users, $result, $result) : $this->lang->testtask->unexecuted; return $datas; } /** * 为测试报告获取 bugs。 * Get bugs for test * * @param array|bool $builds * @param int|array $product * @param string $begin * @param string $end * @param string $type * @access public * @return array|false */ public function getBugs4Test(array|bool $builds, int|array $product, string $begin, string $end, string $type = 'build'): array|false { $bugIdList = ''; if(is_array($builds)) { $childBuilds = $this->getChildBuilds($builds); foreach($builds as $build) $bugIdList .= $build->bugs . ','; foreach($childBuilds as $childBuild) $bugIdList .= $childBuild->bugs . ','; } $bugIdList = array_unique(array_filter(explode(',', $bugIdList))); return $this->dao->select('*')->from(TABLE_BUG)->where('deleted')->eq(0) ->andWhere('product')->in($product) ->andWhere('openedDate')->lt("{$begin} 23:59:59") ->beginIF(is_array($builds) && $type == 'build')->andWhere('id')->in($bugIdList)->fi() ->beginIF(!is_array($builds) && $type == 'build')->andWhere("(resolvedBuild = 'trunk' and resolvedDate >= '{$begin}' and resolvedDate <= '{$end} 23:59:59')")->fi() ->beginIF(in_array($type, array('project', 'execution')))->andWhere('id')->in($bugIdList)->fi() ->fetchAll('id', false); } /** * 获取测试报告的需求。 * Get stories for test. * * @param array $builds * @access public * @return array */ public function getStories4Test(array $builds): array { $storyIdList = ''; $childBuilds = $this->getChildBuilds($builds); foreach($builds as $build) $storyIdList .= $build->stories . ','; foreach($childBuilds as $childBuild) $storyIdList .= $childBuild->stories . ','; $storyIdList = array_unique(array_filter(explode(',', $storyIdList))); return $this->dao->select('*')->from(TABLE_STORY)->where('deleted')->eq('0')->andWhere('id')->in($storyIdList)->fetchAll('id', false); } /** * 获取测试报告键对。 * Get pairs. * * @param int $productID * @param int $appendID * @access public * @return array */ public function getPairs(int $productID = 0, int $appendID = 0): array { return $this->dao->select('id,title')->from(TABLE_TESTREPORT) ->where('deleted')->eq(0) ->beginIF($productID)->andWhere('product')->eq($productID)->fi() ->beginIF($appendID)->orWhere('id')->eq($appendID)->fi() ->orderBy('id_desc') ->fetchPairs(); } /** * 判断操作是否可以点击。 * Judge an action is clickable or not. * * @param object $report * @param string $action * @access public * @return bool */ public function isClickable(object $report, string $action): bool { return true; } /** * Get child builds. * * @param array $builds * @access public * @return array */ public function getChildBuilds(array $builds): array { $childBuildIDList = ''; foreach($builds as $build) $childBuildIDList .= $build->builds . ','; $childBuildIDList = array_unique(array_filter(explode(',', $childBuildIDList))); if(empty($childBuildIDList)) return array(); return $this->dao->select('id,name,bugs,stories')->from(TABLE_BUILD)->where('id')->in($childBuildIDList)->fetchAll('id'); } }