From 0fb8f232694d2e144b4c05acf53a91d7ba52bfd0 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 21 Sep 2023 14:08:28 +0800 Subject: [PATCH] * testtask: refactor the importUnitResult method. --- module/testtask/control.php | 44 ++--- module/testtask/model.php | 372 +++++++++++++++++++++++------------- 2 files changed, 248 insertions(+), 168 deletions(-) diff --git a/module/testtask/control.php b/module/testtask/control.php index 6bb0659033..c6be5c0a91 100644 --- a/module/testtask/control.php +++ b/module/testtask/control.php @@ -1013,57 +1013,39 @@ class testtask extends control } /** + * 导入单元测试结果。 * Import unit results. * * @param int $productID * @access public * @return void */ - public function importUnitResult($productID) + public function importUnitResult(int $productID) { + /* 检查是否有权限访问要导入单元测试结果的产品。*/ + /* Check if user have permission to access the product into which unit test results are to be imported. */ + $productID = $this->loadModel('product')->checkAccess($productID, $this->products); + if($_POST) { - $taskID = $this->testtask->importUnitResult($productID); - if(dao::isError()) return print(js::error(dao::getError())); + $task = $this->testtaskZen->buildTaskForImportUnitResult($productID); + $taskID = $this->testtask->importUnitResult($task); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - $this->loadModel('action')->create('testtask', $taskID, 'opened'); - return print(js::locate($this->createLink('testtask', 'unitCases', "taskID=$taskID"), 'parent')); - } - - /* Set menu. */ - $productID = $this->loadModel('product')->checkAccess($productID, $this->products); - if($this->app->tab == 'project') - { - $this->lang->scrum->menu->qa['subMenu']->testcase['subModule'] = 'testtask'; - $this->lang->scrum->menu->qa['subMenu']->testtask['subModule'] = ''; - $this->loadModel('project')->setMenu($this->session->project); - - /* Replace language. */ - $project = $this->project->getByID($this->session->project); - if(!empty($project->model) and $project->model == 'waterfall') - { - $this->lang->testtask->execution = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->testtask->execution); - } - } - else - { - $this->loadModel('qa')->setMenu($productID); - $this->app->rawModule = 'testcase'; + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('unitCases', "taskID=$taskID"))); } $this->app->loadLang('job'); $this->app->rawModule = 'testcase'; + $this->loadModel('qa')->setMenu($productID); - $projectID = $this->app->tab == 'qa' ? 0 : $this->session->project; - $executions = empty($productID) ? array() : $this->loadModel('product')->getExecutionPairsByProduct($productID, '', $projectID); + $executions = empty($productID) ? array() : $this->product->getExecutionPairsByProduct($productID); $builds = empty($productID) ? array() : $this->loadModel('build')->getBuildPairs($productID, 'all', 'notrunk', 0, 'execution', '', false); $this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testtask->importUnitResult; + $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted|noclosed'); $this->view->executions = $executions; $this->view->builds = $builds; - $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted|noclosed'); - $this->view->productID = $productID; - $this->view->projectID = $projectID; $this->display(); } diff --git a/module/testtask/model.php b/module/testtask/model.php index e106b14116..8e05dc2850 100755 --- a/module/testtask/model.php +++ b/module/testtask/model.php @@ -1899,190 +1899,289 @@ class testtaskModel extends model } /** - * Import unit results. + * 导入单元测试结果。 + * Import unit test results. * - * @param int $productID + * @param object $task * @access public - * @return string + * @return bool|int */ - public function importUnitResult($productID) + public function importUnitResult(object $task): bool|int { - $file = $this->loadModel('file')->getUpload('resultFile'); - if(empty($file)) + /* 获取上传的单元测试文件。*/ + /* Get uploaded unit test file. */ + $files = $this->loadModel('file')->getUpload('resultFile'); + if(empty($files[0])) { - dao::$errors[] = $this->lang->testtask->unitXMLFormat; + dao::$errors['resultFile'][] = $this->lang->testtask->unitXMLFormat; return false; } - $file = $file[0]; - $fileName = $this->file->savePath . $this->file->getSaveName($file['pathname']); - move_uploaded_file($file['tmpname'], $fileName); - if(simplexml_load_file($fileName) === false) + /* 使用 simplexml 读取文件。*/ + /* Use simplexml to read file. */ + $file = $files[0]; + $filePath = $this->file->savePath . $this->file->getSaveName($file['pathname']); + move_uploaded_file($file['tmpname'], $filePath); + $parsedXML = simplexml_load_file($filePath); + if($parsedXML === false) { - dao::$errors[] = $this->lang->testtask->cannotBeParsed; + dao::$errors['resultFile'][] = $this->lang->testtask->cannotBeParsed; return false; } - $frame = $this->post->frame; - unset($_POST['frame']); + /* 解析 xml 文件。*/ + /* Parse xml file. */ + $data = $this->parseXMLResult($parsedXML, $task->product, $task->frame); + if(empty($data['cases']) && $task->frame == 'cppunit') $data = $this->parseCppXMLResult($parsedXML, $task->product, $task->frame); + if(empty($data['cases'])) + { + dao::$errors['resultFile'][] = $this->lang->testtask->noImportData; + return false; + } - $data = $this->parseXMLResult($fileName, $productID, $frame); - if($frame == 'cppunit' and empty($data['cases'])) $data = $this->parseCppXMLResult($fileName, $productID, $frame); - - /* Create task. */ - $this->post->set('auto', 'unit'); - $testtaskID = $this->create(); - - unlink($fileName); - unset($_SESSION['resultFile']); + /* 创建测试单。*/ + /* Create test task. */ + unset($task->frame); + //$taskID = $this->create($task); + $taskID = 620; if(dao::isError()) return false; - return $this->processAutoResult($testtaskID, $productID, $data['suites'], $data['cases'], $data['results'], $data['suiteNames'], $data['caseTitles'], 'unit'); + $this->loadModel('action')->create('testtask', $taskID, 'opened'); + + unlink($filePath); + unset($_SESSION['resultFile']); + + /* 导入单元测试结果文件中包含的数据。*/ + /* Import data in the unit test results. */ + $this->importDataOfUnitResult($taskID, $task->product, $data['suites'], $data['cases'], $data['results'], $data['suiteNames'], $data['caseTitles'], 'unit'); + + return $taskID; } /** - * Process auto test result. + * 导入单元测试结果文件中包含的数据。 + * Import data in the unit test results. * - * @param int $testtaskID + * @param int $taskID * @param int $productID * @param array $suites * @param array $cases * @param array $results * @param array $suiteNames * @param array $caseTitles - * @param string $auto unit|func + * @param string $auto unit|func * @access public - * @return int + * @return bool */ - public function processAutoResult($testtaskID, $productID, $suites, $cases, $results, $suiteNames = array(), $caseTitles = array(), $auto = 'unit') + public function importDataOfUnitResult(int $taskID, int $productID, array $suites, array $cases, array $results, array $suiteNames, array $caseTitles, string $auto = 'unit'): bool { - if(empty($cases)) return print(js::alert($this->lang->testtask->noImportData)); - - /* Import cases and link task and insert result. */ $this->loadModel('action'); - $existSuites = $this->dao->select('*')->from(TABLE_TESTSUITE)->where('name')->in($suiteNames)->andWhere('product')->eq($productID)->andWhere('type')->eq($auto)->andWhere('deleted')->eq(0)->fetchPairs('name', 'id'); + + /* 初始化对象便于在循环中使用。*/ + /* Initialize the object for use in loops. */ + $suiteCase = new stdclass(); + $testRun = new stdclass(); + $testRun->task = $taskID; + $testRun->status = 'done'; + + $existSuites = $suiteNames ? $this->getExistSuitesOfUnitResult($suiteNames, $productID, $auto) : array(); + foreach($suites as $suiteIndex => $suite) { $suiteID = 0; - if($suite) + if($suite) $suiteID = isset($existSuites[$suite->name]) ? $existSuites[$suite->name] : $this->importSuiteOfUnitResult($suite); + + $suiteCase->suite = $suiteID; + + $importCases = zget($cases, $suiteIndex, array()); + $existCases = !empty($caseTitles[$suiteIndex]) ? $this->getExistCasesOfUnitResult($caseTitles[$suiteIndex], $suiteID, $productID, $auto) : array(); + foreach($importCases as $key => $case) { - if(!isset($existSuites[$suite->name])) - { - $this->dao->insert(TABLE_TESTSUITE)->data($suite)->exec(); - $suiteID = $this->dao->lastInsertID(); - $this->action->create('testsuite', $suiteID, 'opened'); - } - else - { - $suiteID = $existSuites[$suite->name]; - } - } + $caseID = $this->importCaseOfUnitResult($case, $existCases); + $runID = $this->importRunOfUnitResult($case, $caseID, $testRun); + if($suiteID) $this->linkImportedCaseToSuite($case, $caseID, $suiteCase); - if($suiteID) - { - $existCases = $this->dao->select('t1.*')->from(TABLE_CASE)->alias('t1') - ->leftJoin(TABLE_SUITECASE)->alias('t2')->on('t1.id=t2.case') - ->where('t1.title')->in($caseTitles[$suiteIndex]) - ->andWhere('t1.product')->eq($productID) - ->beginIF($auto == 'unit')->andWhere('t1.auto')->eq($auto)->fi() - ->andWhere('t1.deleted')->eq(0) - ->orderBy('id') - ->fetchPairs('title', 'id'); - } - else - { - $existCases = $this->dao->select('*')->from(TABLE_CASE) - ->where('title')->in($caseTitles[$suiteIndex]) - ->beginIF($auto == 'unit')->andWhere('auto')->eq($auto)->fi() - ->andWhere('product')->eq($productID) - ->andWhere('deleted')->eq(0) - ->orderBy('id') - ->fetchPairs('title', 'id'); - } - - foreach($cases[$suiteIndex] as $i => $case) - { - if(isset($case->id)) - { - unset($case->steps); - - $caseID = $case->id; - $oldCase = $this->dao->select('*')->from(TABLE_CASE)->where('id')->eq($caseID)->fetch(); - $case->version = $oldCase->version; - $case->openedDate = $oldCase->openedDate; - - $changes = common::createChanges($oldCase, $case); - if($changes) - { - $this->dao->update(TABLE_CASE)->data($case)->where('id')->eq($caseID)->exec(); - $actionID = $this->action->create('case', $caseID, 'Edited'); - $this->action->logHistory($actionID, $changes); - } - } - elseif(!isset($existCases[$case->title])) - { - $this->dao->insert(TABLE_CASE)->data($case, 'steps')->exec(); - $caseID = $this->dao->lastInsertID(); - $case->steps = isset($case->steps) ? $case->steps : array(); - foreach($case->steps as $caseStep) - { - $caseStep->case = $caseID; - $caseStep->version = 1; - $this->dao->insert(TABLE_CASESTEP)->data($caseStep)->exec(); - } - $this->action->create('case', $caseID, 'Opened'); - } - else - { - $caseID = $existCases[$case->title]; - } - - $testrun = new stdclass(); - $testrun->task = $testtaskID; - $testrun->case = $caseID; - $testrun->version = $case->version; - $testrun->lastRunner = $case->lastRunner; - $testrun->lastRunDate = $case->lastRunDate; - $testrun->lastRunResult = $case->lastRunResult; - $testrun->status = 'done'; - - $this->dao->replace(TABLE_TESTRUN)->data($testrun)->exec(); - $runID = $this->dao->lastInsertID(); - - if($suiteID) - { - $suitecase = new stdclass(); - $suitecase->suite = $suiteID; - $suitecase->case = $caseID; - $suitecase->version = $case->version; - $suitecase->product = $case->product; - $this->dao->replace(TABLE_SUITECASE)->data($suitecase)->exec(); - } - - $testresult = $results[$suiteIndex][$i]; + $testresult = $results[$suiteIndex][$key]; $testresult->run = $runID; $testresult->case = $caseID; $this->dao->insert(TABLE_TESTRESULT)->data($testresult)->exec(); } } - return $testtaskID; + return !dao::isError(); + } + + /** + * 导入单元测试结果文件中的测试套件。 + * Import test suite in the unit test results. + * + * @param object $suite + * @access private + * @return int + */ + private function importSuiteOfUnitResult(object $suite): int + { + $this->dao->insert(TABLE_TESTSUITE)->data($suite)->exec(); + $suiteID = $this->dao->lastInsertID(); + $this->action->create('testsuite', $suiteID, 'opened'); + + return $suiteID; + } + + /** + * 导入单元测试结果文件中的测试用例。 + * Import test case in the unit test results. + * + * @param object $case + * @param array $existCases + * @access private + * @return int + */ + private function importCaseOfUnitResult(object &$case, array $existCases): int + { + if(!empty($case->id)) + { + unset($case->steps); + + $caseID = $case->id; + $oldCase = $this->dao->select('*')->from(TABLE_CASE)->where('id')->eq($caseID)->fetch(); + $case->version = $oldCase->version; + $case->openedDate = $oldCase->openedDate; + + $changes = common::createChanges($oldCase, $case); + if($changes) + { + $this->dao->update(TABLE_CASE)->data($case)->where('id')->eq($caseID)->exec(); + $actionID = $this->action->create('case', $caseID, 'Edited'); + $this->action->logHistory($actionID, $changes); + } + + return $caseID; + } + + if(!isset($existCases[$case->title])) + { + $this->dao->insert(TABLE_CASE)->data($case, 'steps')->exec(); + $caseID = $this->dao->lastInsertID(); + $steps = zget($case, 'steps', array()); + foreach($steps as $step) + { + $step->case = $caseID; + $step->version = 1; + $this->dao->insert(TABLE_CASESTEP)->data($step)->exec(); + } + $this->action->create('case', $caseID, 'Opened'); + + return $caseID; + } + + return $existCases[$case->title]; + } + + /** + * 导入单元测试结果文件中的测试结果。 + * Import test result in the unit test results. + * + * @param object $case + * @param int $caseID + * @param object $testRun + * @access private + * @return int + */ + private function importRunOfUnitResult(object $case, int $caseID, object $testRun): int + { + $testRun->case = $caseID; + $testRun->version = $case->version; + $testRun->lastRunner = $case->lastRunner; + $testRun->lastRunDate = $case->lastRunDate; + $testRun->lastRunResult = $case->lastRunResult; + + $this->dao->replace(TABLE_TESTRUN)->data($testRun)->exec(); + + return $this->dao->lastInsertID(); + } + + /** + * 将导入的测试用例关联到测试套件。 + * Link imported case to suite. + * + * @param object $case + * @param int $caseID + * @param object $suiteCase + * @access private + * @return bool + */ + private function linkImportedCaseToSuite(object $case, int $caseID, object $suiteCase): bool + { + $suiteCase->case = $caseID; + $suiteCase->version = $case->version; + $suiteCase->product = $case->product; + + $this->dao->replace(TABLE_SUITECASE)->data($suiteCase)->exec(); + + return !dao::isError(); + } + + /** + * 获取单元测试结果文件中已存在的测试套件。 + * Get exist suites in the unit test results. + * + * @param array $names + * @param int $productID + * @param string $auto + * @access public + * @return array + */ + private function getExistSuitesOfUnitResult(array $names, int $productID, string $auto): array + { + if(!$names) return array(); + + return $this->dao->select('name, id')->from(TABLE_TESTSUITE) + ->where('name')->in($names) + ->andWhere('product')->eq($productID) + ->andWhere('type')->eq($auto) + ->andWhere('deleted')->eq('0') + ->fetchPairs(); + } + + /** + * 获取单元测试结果文件中已存在的测试用例。 + * Get exist cases in the unit test results. + * + * @param array $titles + * @param int $suiteID + * @param int $productID + * @param string $auto + * @access public + * @return array + */ + private function getExistCasesOfUnitResult(array $titles, int $suiteID, int $productID, string $auto): array + { + if(!$titles) return array(); + + $this->dao->select('t1.title, t1.id')->from(TABLE_CASE)->alias('t1'); + + if($suiteID) $this->dao->leftJoin(TABLE_SUITECASE)->alias('t2')->on('t1.id=t2.case'); + + return $this->dao->where('t1.title')->in($titles) + ->andWhere('t1.product')->eq($productID) + ->beginIF($auto == 'unit')->andWhere('t1.auto')->eq($auto)->fi() + ->andWhere('t1.deleted')->eq('0') + ->orderBy('t1.id') + ->fetchPairs(); } /** * Parse cppunit XML result. * - * @param string $fileName + * @param object $parsedXML * @param int $productID * @param string $frame * @access public * @return array */ - public function parseCppXMLResult($fileName, $productID, $frame) + public function parseCppXMLResult(object $parsedXML, int $productID, string $frame): array { - /* Parse result xml. */ - $parsedXML = simplexml_load_file($fileName); - /* Get testcase node. */ $failNodes = $parsedXML->xpath('FailedTests/FailedTest'); $passNodes = $parsedXML->xpath('SuccessfulTests/Test'); @@ -2144,17 +2243,16 @@ class testtaskModel extends model /** * Parse unit result from xml. * - * @param string $fileName + * @param object $parsedXML * @param int $productID * @param string $frame * @access public * @return array */ - public function parseXMLResult($fileName, $productID, $frame) + public function parseXMLResult(object $parsedXML, int $productID, string $frame): array { /* Parse result xml. */ - $rules = zget($this->config->testtask->unitResultRules, $frame, $this->config->testtask->unitResultRules->common); - $parsedXML = simplexml_load_file($fileName); + $rules = zget($this->config->testtask->unitResultRules, $frame, $this->config->testtask->unitResultRules->common); /* Get testcase node. */ $matchPaths = $rules['path'];