/** * Parse an individual .gcov file. **/ public function ParseGcovFile($fileinfo) { $coverageFileLog = new CoverageFileLog(); $coverageFileLog->AggregateBuildId = $this->AggregateBuildId; $coverageFileLog->PreviousAggregateParentId = $this->PreviousAggregateParentId; $coverageFile = new CoverageFile(); $coverage = new Coverage(); $coverage->CoverageFile = $coverageFile; // Begin parsing this file. // The first thing we look for is the full path to this source file. $file = new SplFileObject($fileinfo); $path = ''; while (!$file->eof()) { $gcovLine = $file->current(); $term = ':Source:'; $pos = strpos($gcovLine, $term); if ($pos !== false) { $path = substr($gcovLine, $pos + strlen($term)); break; } $file->next(); } if (empty($path)) { return; } // Check if this file belongs to a different SubProject. $buildid = $this->Build->Id; if (!empty($this->SubProjectPath) && strpos($path, $this->SubProjectPath) === false) { // Find the SubProject that corresponds to this path. $query = "SELECT id, name, path FROM subproject\n WHERE projectid = {$this->ProjectId} AND\n endtime = '1980-01-01 00:00:00' AND\n path != '' AND\n '{$path}' LIKE CONCAT('%',path,'%')"; $result = pdo_query($query); if (!$result || pdo_num_rows($result) == 0) { add_log("No SubProject found for '{$path}'", 'ParseGcovFile', LOG_INFO, $this->ProjectId, $this->Build->Id); return; } $row = pdo_fetch_array($result); $subprojectid = $row['id']; $subprojectname = $row['name']; $subprojectpath = $row['path']; // Find the sibling build that performed this SubProject. $siblingBuild = new Build(); $query = 'SELECT b.id FROM build AS b INNER JOIN subproject2build AS sp2b ON (sp2b.buildid=b.id) WHERE b.parentid= (SELECT parentid FROM build WHERE id=' . $this->Build->Id . ")\n AND sp2b.subprojectid={$subprojectid}"; $row = pdo_single_row_query($query); if ($row && array_key_exists('id', $row)) { $buildid = $row['id']; $siblingBuild->Id = $buildid; $siblingBuild->FillFromId($buildid); } else { // Build doesn't exist yet, add it here. $siblingBuild->Name = $this->Build->Name; $siblingBuild->ProjectId = $this->ProjectId; $siblingBuild->SiteId = $this->Build->SiteId; $siblingBuild->SetParentId($this->Build->GetParentId()); $siblingBuild->SetStamp($this->Build->GetStamp()); $siblingBuild->SetSubProject($subprojectname); $siblingBuild->StartTime = $this->Build->StartTime; $siblingBuild->EndTime = $this->Build->EndTime; $siblingBuild->SubmitTime = gmdate(FMT_DATETIME); add_build($siblingBuild, 0); $buildid = $siblingBuild->Id; } $coverageFileLog->Build = $siblingBuild; // Remove any part of the file path that comes before // the subproject path. $path = substr($path, strpos($path, $subprojectpath)); // Replace the subproject path with '.' $path = substr_replace($path, '.', 0, strlen($subprojectpath)); } else { // If this source file isn't from the source or binary directory // we shouldn't include it in our coverage report. if (!empty($this->SubProjectPath) && strpos($path, $this->SubProjectPath) !== false) { $path = substr($path, strpos($path, $this->SubProjectPath)); $path = substr_replace($path, '.', 0, strlen($this->SubProjectPath)); } elseif (strpos($path, $this->SourceDirectory) !== false) { $path = str_replace($this->SourceDirectory, '.', trim($path)); } elseif (strpos($path, $this->BinaryDirectory) !== false) { $path = str_replace($this->BinaryDirectory, '.', trim($path)); } else { return; } $coverageFileLog->Build = $this->Build; } // Get a reference to the coverage summary for this build. if ($buildid === $this->Build->Id) { $coverageSummary = $this->CoverageSummary; } else { if (!array_key_exists($buildid, $this->SubProjectSummaries)) { $coverageSummary = new CoverageSummary(); $coverageSummary->BuildId = $buildid; $this->SubProjectSummaries[$buildid] = $coverageSummary; } else { $coverageSummary = $this->SubProjectSummaries[$buildid]; } } // Use a regexp to resolve any /../ in this path. // We can't use realpath() because we're referencing a path that // doesn't exist on the server. // For a source file that contains: // #include "src/../include/foo.h" // CDash will report the covered file as include/foo.h $pattern = "#/[^/]*?/\\.\\./#"; while (strpos($path, "/../") !== false) { $path = preg_replace($pattern, "/", $path, 1); } $coverageFile->FullPath = trim($path); $lineNumber = 0; // The lack of rewind is intentional. while (!$file->eof()) { $gcovLine = $file->current(); // "Ordinary" entries in a .gcov file take the following format: // <lineNumber>: <timesHit>: <source code at that line> // So we check if this line matches the format & parse the // data out of it if so. $fields = explode(':', $gcovLine, 3); if (count($fields) > 2) { // Separate out delimited values from this line. $timesHit = trim($fields[0]); $lineNumber = trim($fields[1]); $sourceLine = rtrim($fields[2]); if ($lineNumber > 0) { $coverageFile->File .= $sourceLine; // cannot be <br/> for backward compatibility. $coverageFile->File .= '<br>'; } // This is how gcov indicates a line of unexecutable code. if ($timesHit === '-') { $file->next(); continue; } // This is how gcov indicates an uncovered line. if ($timesHit === '#####') { $timesHit = 0; } $coverageFileLog->AddLine($lineNumber - 1, $timesHit); $file->next(); } else { $coveredBranches = 0; $uncoveredBranches = 0; $throwBranches = 0; $fallthroughBranches = 0; while (count($fields) < 3 && !$file->eof()) { // Parse branch coverage here. if (substr($gcovLine, 0, 6) === 'branch') { // Figure out whether this branch was covered or not. if (strpos($gcovLine, 'taken 0%') !== false) { $uncoveredBranches += 1; } else { $coveredBranches += 1; } // Also keep track of the different types of branches encountered. if (strpos($gcovLine, '(throw)') !== false) { $throwBranches += 1; } elseif (strpos($gcovLine, '(fallthrough)') !== false) { $fallthroughBranches += 1; } } $file->next(); $gcovLine = $file->current(); $fields = explode(':', $gcovLine); } // Don't report branch coverage for this line if we only // encountered (throw) and (fallthrough) branches here. $totalBranches = $coveredBranches + $uncoveredBranches; if ($totalBranches > 0 && $totalBranches > $throwBranches + $fallthroughBranches) { $coverageFileLog->AddBranch($lineNumber - 1, $coveredBranches, $totalBranches); } } } // Save these models to the database. $coverageFile->TrimLastNewline(); $coverageFile->Update($buildid); $coverageFileLog->BuildId = $buildid; $coverageFileLog->FileId = $coverageFile->Id; $coverageFileLog->Insert(true); // Query the filelog 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 = $coverageFileLog->GetStats(); $coverage->LocUntested = $stats['locuntested']; $coverage->LocTested = $stats['loctested']; $coverage->Covered = 1; $coverage->BranchesUntested = $stats['branchesuntested']; $coverage->BranchesTested = $stats['branchestested']; // Add any labels. if (array_key_exists($path, $this->Labels)) { foreach ($this->Labels[$path] as $labelText) { $label = new Label(); $label->SetText($labelText); $coverage->AddLabel($label); } } // Add this Coverage to our summary. $coverageSummary->AddCoverage($coverage); }
function testBuildModel() { $this->startCodeCoverage(); $build = new Build(); $builderror = new BuildError(); $builderror->Type = 0; $builderror->Text = 'error'; $buildwarning = new BuildError(); $buildwarning->Type = 1; $buildwarning->Text = 'warning'; if ($build->GetName() !== false) { $this->fail("GetName didn't return false for empty build id"); return 1; } if ($build->GetLabels() !== false) { $this->fail("GetLabels didn't return false for empty build id"); return 1; } if ($build->GetGroup() !== false) { $this->fail("GetGroup didn't return false for empty build id"); return 1; } if ($build->GetNumberOfErrors() !== false) { $this->fail("GetNumberOfErrors didn't return false for empty build id"); return 1; } if ($build->GetNumberOfWarnings() !== false) { $this->fail("GetNumberOfWarnings didn't return false for empty build id"); return 1; } if ($build->SetSubProject('1234') !== false) { $this->fail("SetSubProject didn't return false for empty project id"); return 1; } if ($build->GetSubProjectName() !== false) { $this->fail("GetSubProjectName didn't return false for empty build id"); return 1; } if ($build->GetErrorDifferences() !== false) { $this->fail("GetErrorDifferences didn't return false for empty build id"); return 1; } if ($build->ComputeUpdateStatistics() !== false) { $this->fail("ComputeUpdateStatistics didn't return false for empty build id"); return 1; } if ($build->ComputeDifferences() !== false) { $this->fail("ComputeDifferences didn't return false for empty build id"); return 1; } if ($build->ComputeConfigureDifferences() !== false) { $this->fail("ComputeConfigureDifferences didn't return false for empty build id"); return 1; } if ($build->ComputeTestTiming() !== false) { $this->fail("ComputeTestTiming didn't return false for empty build id"); return 1; } if ($build->InsertLabelAssociations() !== false) { $this->fail("InsertLabelAssocations didn't return false for empty build id"); return 1; } if ($build->UpdateEndTime('2010-08-07') !== false) { $this->fail("UpdateEndTime didn't return false for empty build id"); return 1; } if ($build->SaveTotalTestsTime('100') !== false) { $this->fail("SaveTotalTestsTime didn't return false for empty build id"); return 1; } $build->Id = '1'; if ($build->ComputeTestTiming() !== false) { $this->fail("ComputeTestTiming didn't return false for empty project id"); return 1; } if ($build->ComputeUpdateStatistics() !== false) { $this->fail("ComputeUpdateStatistics didn't return false for empty project id"); return 1; } $build->ProjectId = '2'; $build->SiteId = '1'; $build->SetSubProject('8567'); if (strpos(file_get_contents($this->logfilename), "New subproject detected") === false) { $this->fail("'New subproject detected' not found in log after calling SetSubProject for invalid subproject id"); return 1; } if ($build->Exists() == false) { $this->fail("Exists returned false for a valid build id"); return 1; } $build->Id = '98765'; $build->SetStamp('20100610-1901-Experimental'); $build->Type = ''; //force this empty for coverage purposes $build->StartTime = '2009-12-18 14:19:11'; $build->EndTime = '2009-12-18 14:20:23'; $build->SubmitTime = '2012-01-25 16:43:11'; if ($build->Exists() == true) { $this->fail("Exists returned true for an invalid build id"); return 1; } $build->Save(); $build->Append = true; $build->InsertErrors = true; $build->AddError($builderror); $build->AddError($buildwarning); $build->Save(); $this->stopCodeCoverage(); return 0; }
/** Function to deal with the external tool mechanism */ function post_submit() { include "models/buildfile.php"; // We expect POST to contain the following values. $vars = array('project', 'build', 'stamp', 'site', 'track', 'type', 'starttime', 'endtime', 'datafilesmd5'); foreach ($vars as $var) { if (!isset($_POST[$var]) || empty($_POST[$var])) { $response_array['status'] = 1; $response_array['description'] = 'Variable \'' . $var . '\' not set but required.'; echo json_encode($response_array); return; } } $projectname = htmlspecialchars(pdo_real_escape_string($_POST['project'])); $buildname = htmlspecialchars(pdo_real_escape_string($_POST['build'])); $buildstamp = htmlspecialchars(pdo_real_escape_string($_POST['stamp'])); $sitename = htmlspecialchars(pdo_real_escape_string($_POST['site'])); $track = htmlspecialchars(pdo_real_escape_string($_POST['track'])); $type = htmlspecialchars(pdo_real_escape_string($_POST['type'])); $starttime = htmlspecialchars(pdo_real_escape_string($_POST['starttime'])); $endtime = htmlspecialchars(pdo_real_escape_string($_POST['endtime'])); // Check if we have the CDash@Home scheduleid $scheduleid = 0; if (isset($_POST["clientscheduleid"])) { $scheduleid = pdo_real_escape_numeric($_POST["clientscheduleid"]); } // Add the build $build = new Build(); $build->ProjectId = get_project_id($projectname); $build->StartTime = gmdate(FMT_DATETIME, $starttime); $build->EndTime = gmdate(FMT_DATETIME, $endtime); $build->SubmitTime = gmdate(FMT_DATETIME); $build->Name = $buildname; $build->InsertErrors = false; // we have no idea if we have errors at this point $build->SetStamp($buildstamp); // Get the site id $site = new Site(); $site->Name = $sitename; $site->Insert(); $build->SiteId = $site->Id; // Make this an "append" build, so that it doesn't result in a separate row // from the rest of the "normal" submission. $build->Append = true; // TODO: Check the labels and generator and other optional if (isset($_POST["generator"])) { $build->Generator = htmlspecialchars(pdo_real_escape_string($_POST['generator'])); } $subprojectname = ""; if (isset($_POST["subproject"])) { $subprojectname = htmlspecialchars(pdo_real_escape_string($_POST['subproject'])); $build->SetSubProject($subprojectname); } // Check if this build already exists. $buildid = $build->GetIdFromName($subprojectname); // If not, add a new one. if ($buildid === 0) { $buildid = add_build($build, $scheduleid); } // Returns the OK submission $response_array['status'] = 0; $response_array['buildid'] = $buildid; $buildfile = new BuildFile(); // Check if the files exists foreach ($_POST['datafilesmd5'] as $md5) { $buildfile->md5 = $md5; $old_buildid = $buildfile->MD5Exists(); if (!$old_buildid) { $response_array['datafilesmd5'][] = 0; } else { $response_array['datafilesmd5'][] = 1; // Associate this build file with the new build if it has been previously // uploaded. require_once "copy_build_data.php"; copy_build_data($old_buildid, $buildid, $type); } } echo json_encode($response_array); }
function create_aggregate_build($build, $siteid = null) { require_once 'include/ctestparserutils.php'; if (is_null($siteid)) { $siteid = get_server_siteid(); } $aggregate_build = new Build(); $aggregate_build->Name = 'Aggregate Coverage'; $aggregate_build->SiteId = $siteid; $date = substr($build->GetStamp(), 0, strpos($build->GetStamp(), '-')); $aggregate_build->SetStamp($date . "-0000-Nightly"); $aggregate_build->ProjectId = $build->ProjectId; $aggregate_build->StartTime = $build->StartTime; $aggregate_build->EndTime = $build->EndTime; $aggregate_build->SubmitTime = gmdate(FMT_DATETIME); $aggregate_build->SetSubProject($build->GetSubProjectName()); $aggregate_build->InsertErrors = false; add_build($aggregate_build); return $aggregate_build; }