/** * Parse a tarball of .gcov files. **/ public function Parse($filename) { // Create a new directory where we can extract our tarball. $dirName = sys_get_temp_dir() . '/' . pathinfo($filename, PATHINFO_FILENAME); mkdir($dirName); // Extract the tarball. $result = extract_tar($filename, $dirName); if ($result === false) { add_log('Could not extract ' . $filename . ' into ' . $dirName, 'GCovTarHandler::Parse', LOG_ERR); return false; } // Find the data.json file and extract the source directory from it. $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirName), RecursiveIteratorIterator::CHILD_FIRST); foreach ($iterator as $fileinfo) { if ($fileinfo->getFilename() == 'data.json') { $jsonContents = file_get_contents($fileinfo->getRealPath()); $jsonDecoded = json_decode($jsonContents, true); if (is_null($jsonDecoded) || !array_key_exists('Source', $jsonDecoded) || !array_key_exists('Binary', $jsonDecoded)) { DeleteDirectory($dirName); return false; } $this->SourceDirectory = $jsonDecoded['Source']; $this->BinaryDirectory = $jsonDecoded['Binary']; break; } } if (empty($this->SourceDirectory) || empty($this->BinaryDirectory)) { DeleteDirectory($dirName); return false; } // Check if any Labels.json files were included $iterator->rewind(); foreach ($iterator as $fileinfo) { if ($fileinfo->getFilename() == 'Labels.json') { $this->ParseLabelsFile($fileinfo); } } // Lookup some data used during coverage aggregation. $this->Build->ComputeTestingDayBounds(); if ($this->Build->Type === 'Nightly') { $aggregateBuild = get_aggregate_build($this->Build); $aggregateParentId = $aggregateBuild->GetParentId(); if ($aggregateParentId > 0) { $this->AggregateBuildId = $aggregateParentId; $aggregateParent = new Build(); $aggregateParent->Id = $aggregateParentId; $this->PreviousAggregateParentId = $aggregateParent->GetPreviousBuildId(); } else { $this->AggregateBuildId = $aggregateBuild->Id; } } // Recursively search for .gcov files and parse them. $iterator->rewind(); foreach ($iterator as $fileinfo) { if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'gcov') { $this->ParseGcovFile($fileinfo); } } // Search for uncovered files. if (file_exists("{$dirName}/uncovered")) { $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator("{$dirName}/uncovered"), RecursiveIteratorIterator::LEAVES_ONLY); foreach ($iterator as $fileinfo) { if ($fileinfo->isFile()) { // Get the path of this uncovered file relative to its // source directory. $relativePath = str_replace("{$dirName}/uncovered/", "./", $fileinfo); $this->ParseUncoveredSourceFile($fileinfo, $relativePath); } } } // Insert coverage summary $this->CoverageSummary->Insert(true); $this->CoverageSummary->ComputeDifference(); foreach ($this->SubProjectSummaries as $buildid => $subprojectSummary) { $subprojectSummary->Insert(true); $subprojectSummary->ComputeDifference(); } // Delete the directory when we're done. DeleteDirectory($dirName); return true; }
/** Update the aggregate coverage build to include these results. */ public function UpdateAggregate() { if (!$this->Build) { $this->Build = new Build(); $this->Build->Id = $this->BuildId; } $this->Build->FillFromId($this->BuildId); // Only nightly builds count towards aggregate coverage. if ($this->Build->Type !== 'Nightly' || $this->Build->Name === 'Aggregate Coverage') { return; } // Find the build ID for this day's edition of 'Aggregate Coverage'. $aggregateBuildId = null; if ($this->AggregateBuildId) { if ($this->Build->SubProjectId) { // For SubProject builds, AggregateBuildId refers to the parent. // Look up the ID of the appropriate child. $query = "SELECT id FROM build\n INNER JOIN subproject2build AS sp2b ON (build.id=sp2b.buildid)\n WHERE parentid='{$this->AggregateBuildId}' AND\n projectid='" . $this->Build->ProjectId . "' AND\n sp2b.subprojectid='" . $this->Build->SubProjectId . "'"; $row = pdo_single_row_query($query); if (!$row || !array_key_exists('id', $row)) { // An aggregate build for this SubProject doesn't exist yet. // Create it here. $aggregateBuild = create_aggregate_build($this->Build); $aggregateBuildId = $aggregateBuild->Id; } else { $aggregateBuildId = $row['id']; } } else { // For standalone builds AggregateBuildId is exactly what we're // looking for. $aggregateBuildId = $this->AggregateBuildId; } $aggregateBuild = new Build(); $aggregateBuild->Id = $aggregateBuildId; $aggregateBuild->FillFromId($aggregateBuildId); } else { // AggregateBuildId not specified, look it up here. $aggregateBuild = get_aggregate_build($this->Build); $aggregateBuildId = $aggregateBuild->Id; $aggregateBuild->FillFromId($aggregateBuildId); } // Abort if this log refers to a different version of the file // than the one already contained in the aggregate. $row = pdo_single_row_query("SELECT id, fullpath FROM coveragefile WHERE id='{$this->FileId}'"); $path = $row['fullpath']; $row = pdo_single_row_query("SELECT id FROM coveragefile AS cf\n INNER JOIN coveragefilelog AS cfl ON (cfl.fileid=cf.id)\n WHERE cfl.buildid='{$aggregateBuildId}' AND cf.fullpath='{$path}'"); if ($row && array_key_exists('id', $row) && $row['id'] != $this->FileId) { add_log("Not appending coverage of '{$path}' to aggregate as it " . 'already contains a different version of this file.', 'CoverageFileLog::UpdateAggregate', LOG_INFO, $this->BuildId); return; } // Append these results to the aggregate coverage log. $aggregateLog = clone $this; $aggregateLog->BuildId = $aggregateBuildId; $aggregateLog->Build = $aggregateBuild; $aggregateLog->Insert(true); // Update the aggregate coverage summary. $aggregateSummary = new CoverageSummary(); $aggregateSummary->BuildId = $aggregateBuildId; $coverageFile = new CoverageFile(); $coverageFile->Id = $this->FileId; $coverageFile->Load(); $coverageFile->Update($aggregateBuildId); // Query the log to get how many lines & branches were covered. // We do this after inserting the filelog because we want to // accurately reflect the union of the current and previously // existing results (if any). $stats = $aggregateLog->GetStats(); $aggregateCoverage = new Coverage(); $aggregateCoverage->CoverageFile = $coverageFile; $aggregateCoverage->LocUntested = $stats['locuntested']; $aggregateCoverage->LocTested = $stats['loctested']; if ($aggregateCoverage->LocTested > 0 || $aggregateCoverage->LocUntested > 0) { $aggregateCoverage->Covered = 1; } else { $aggregateCoverage->Covered = 0; } $aggregateCoverage->BranchesUntested = $stats['branchesuntested']; $aggregateCoverage->BranchesTested = $stats['branchestested']; // Add this Coverage to the summary. $aggregateSummary->AddCoverage($aggregateCoverage); // Insert/Update the aggregate summary. $aggregateSummary->Insert(true); $aggregateSummary->ComputeDifference($this->PreviousAggregateParentId); if ($this->Build->SubProjectId && $this->AggregateBuildId) { // Compute diff for the aggregate parent too. $aggregateParentSummary = new CoverageSummary(); $aggregateParentSummary->BuildId = $this->AggregateBuildId; $aggregateParentSummary->ComputeDifference(); } }