diff --git a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php --- a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php @@ -37,15 +37,17 @@ protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); - $repository_phid = $request->getValue('repositoryPHID'); + $repository_identifier = $request->getValue('repositoryPHID'); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) - ->withPHIDs(array($repository_phid)) + ->withIdentifiers(array($repository_identifier)) ->executeOne(); if (!$repository) { throw new Exception( - pht('No repository exists with PHID "%s".', $repository_phid)); + pht( + 'No repository exists with identifier "%s".', + $repository_identifier)); } $commit_name = $request->getValue('commit'); @@ -112,6 +114,114 @@ $chunk); } $conn->saveTransaction(); + + // Find all the directories we need to update aggregate coverage for. + $affected_directories = array(); + foreach ($coverage as $path => $coverage) { + $ancestors = DiffusionPathIDQuery::expandPathToRoot($path); + + // Get rid of the path itself. + array_shift($ancestors); + foreach ($ancestors as $ancestor) { + $affected_directories[$ancestor] = $ancestor; + } + } + + // Load all the available coverage. This is much easier than trying to + // be surgical about it. Note that we're excluding coverage which starts + // with "+", since that's aggregate coverage, and we don't want to add + // it up twice. + $all_coverage = queryfx_all( + $conn, + 'SELECT pathID, coverage FROM %T WHERE commitID = %d + AND coverage NOT LIKE %>', + $table_name, + $commit->getID(), + '+'); + $all_coverage = ipull($all_coverage, 'coverage', 'pathID'); + + if ($all_coverage) { + $all_covered_paths = queryfx_all( + $conn, + 'SELECT id, path FROM %T WHERE id IN (%Ld)', + PhabricatorRepository::TABLE_PATH, + array_keys($all_coverage)); + $all_covered_paths = ipull($all_covered_paths, 'path', 'id'); + } else { + $all_covered_paths = array(); + } + + $path_coverage = array(); + foreach ($all_coverage as $path_id => $coverage) { + if (!isset($all_covered_paths[$path_id])) { + continue; + } + $path_string = $all_covered_paths[$path_id]; + $path_coverage[$path_string] = $coverage; + } + + $ancestor_coverage = array(); + foreach ($path_coverage as $path => $coverage) { + $ancestors = DiffusionPathIDQuery::expandPathToRoot($path); + array_shift($ancestors); + foreach ($ancestors as $ancestor) { + if (!isset($affected_directories[$ancestor])) { + continue; + } + if (!isset($ancestor_coverage[$ancestor])) { + $ancestor_coverage[$ancestor] = ''; + } + $ancestor_coverage[$ancestor] .= $coverage; + } + } + + $expect = array( + 'U', + 'C', + 'N', + 'X', + ); + + foreach ($ancestor_coverage as $path => $aggregate_coverage) { + $counts = array(); + foreach ($expect as $char) { + $counts[$char] = substr_count($aggregate_coverage, $char); + } + // Mark this as aggregate coverage in JSON format. + $ancestor_coverage[$path] = '+J'.phutil_json_encode($counts); + } + + $ancestor_paths = array_keys($ancestor_coverage); + $ancestor_map = id(new DiffusionPathIDQuery($ancestor_paths)) + ->loadPathIDs(); + + $ancestor_sql = array(); + foreach ($ancestor_coverage as $path => $aggregate_coverage) { + if (empty($ancestor_map[$path])) { + continue; + } + + $path_id = $ancestor_map[$path]; + + $sql[] = qsprintf( + $conn, + '(%d, %d, %d, %s)', + $branch->getID(), + $path_id, + $commit->getID(), + $aggregate_coverage); + } + + $conn->openTransaction(); + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { + queryfx( + $conn, + 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q'. + ' ON DUPLICATE KEY UPDATE coverage = VALUES(coverage)', + $table_name, + $chunk); + } + $conn->saveTransaction(); } }