From: Julien Lancelot Date: Wed, 30 Sep 2015 09:34:32 +0000 (+0200) Subject: SONAR-6397 Use ScmInfoRepo in IssueAssigner X-Git-Tag: 5.2-RC1~114 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7748b0ff2e8df66f02170884efa29c7544ac033c;p=sonarqube.git SONAR-6397 Use ScmInfoRepo in IssueAssigner --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java index 130aa49cd62..0fcb2485653 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java @@ -17,22 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + package org.sonar.server.computation.issue; import com.google.common.base.Optional; -import java.util.HashMap; -import java.util.Map; import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.apache.commons.lang.StringUtils; -import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.core.issue.DefaultIssue; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.server.computation.batch.BatchReportReader; import org.sonar.server.computation.component.Component; -import org.sonar.server.source.SourceService; +import org.sonar.server.computation.scm.ScmInfo; +import org.sonar.server.computation.scm.ScmInfoRepository; + +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; /** * Detect the SCM author and SQ assignee. @@ -45,21 +42,17 @@ import org.sonar.server.source.SourceService; */ public class IssueAssigner extends IssueVisitor { - private final DbClient dbClient; - private final SourceService sourceService; - private final BatchReportReader reportReader; + private static final Logger LOGGER = Loggers.get(IssueAssigner.class); + + private final ScmInfoRepository scmInfoRepository; private final DefaultAssignee defaultAssigne; private final ScmAccountToUser scmAccountToUser; - private long lastCommitDate = 0L; private String lastCommitAuthor = null; - private BatchReport.Changesets scmChangesets = null; + private ScmInfo scmChangesets = null; - public IssueAssigner(DbClient dbClient, SourceService sourceService, BatchReportReader reportReader, - ScmAccountToUser scmAccountToUser, DefaultAssignee defaultAssigne) { - this.dbClient = dbClient; - this.sourceService = sourceService; - this.reportReader = reportReader; + public IssueAssigner(ScmInfoRepository scmInfoRepository, ScmAccountToUser scmAccountToUser, DefaultAssignee defaultAssigne) { + this.scmInfoRepository = scmInfoRepository; this.scmAccountToUser = scmAccountToUser; this.defaultAssigne = defaultAssigne; } @@ -70,7 +63,7 @@ public class IssueAssigner extends IssueVisitor { // optimization - do not load SCM data of this component if there are no new issues loadScmChangesetsIfNeeded(component); - String scmAuthor = guessScmAuthor(issue.line()); + String scmAuthor = guessScmAuthor(issue); issue.setAuthorLogin(scmAuthor); if (scmAuthor != null) { String assigneeLogin = scmAccountToUser.getNullable(scmAuthor); @@ -85,17 +78,16 @@ public class IssueAssigner extends IssueVisitor { private void loadScmChangesetsIfNeeded(Component component) { if (scmChangesets == null) { - scmChangesets = loadScmChangesetsFromReport(component); - if (scmChangesets == null) { - scmChangesets = loadScmChangesetsFromDb(component); + Optional scmInfoOptional = scmInfoRepository.getScmInfo(component); + if (scmInfoOptional.isPresent()) { + scmChangesets = scmInfoOptional.get(); + lastCommitAuthor = scmChangesets.getLatestChangeset().getAuthor(); } - computeLastCommitDateAndAuthor(); } } @Override public void afterComponent(Component component) { - lastCommitDate = 0L; lastCommitAuthor = null; scmChangesets = null; } @@ -105,64 +97,16 @@ public class IssueAssigner extends IssueVisitor { * then get the last committer on the file. */ @CheckForNull - private String guessScmAuthor(@Nullable Integer line) { + private String guessScmAuthor(DefaultIssue issue) { + Integer line = issue.line(); String author = null; - if (line != null && line <= scmChangesets.getChangesetIndexByLineCount()) { - BatchReport.Changesets.Changeset changeset = scmChangesets.getChangeset(scmChangesets.getChangesetIndexByLine(line - 1)); - author = changeset.hasAuthor() ? changeset.getAuthor() : null; - } - return StringUtils.defaultIfEmpty(author, lastCommitAuthor); - } - - private BatchReport.Changesets loadScmChangesetsFromReport(Component component) { - return reportReader.readChangesets(component.getReportAttributes().getRef()); - } - - private BatchReport.Changesets loadScmChangesetsFromDb(Component component) { - DbSession dbSession = dbClient.openSession(false); - try { - Optional> lines = sourceService.getLines(dbSession, component.getUuid(), 1, Integer.MAX_VALUE); - Map changesetByRevision = new HashMap<>(); - BatchReport.Changesets.Builder scmBuilder = BatchReport.Changesets.newBuilder() - .setComponentRef(component.getReportAttributes().getRef()); - if (lines.isPresent()) { - for (DbFileSources.Line sourceLine : lines.get()) { - String scmRevision = sourceLine.getScmRevision(); - if (scmRevision == null || changesetByRevision.get(scmRevision) == null) { - BatchReport.Changesets.Changeset.Builder changeSetBuilder = BatchReport.Changesets.Changeset.newBuilder(); - if (sourceLine.hasScmAuthor()) { - changeSetBuilder.setAuthor(sourceLine.getScmAuthor()); - } - if (sourceLine.hasScmDate()) { - changeSetBuilder.setDate(sourceLine.getScmDate()); - } - if (scmRevision != null) { - changeSetBuilder.setRevision(scmRevision); - } - - BatchReport.Changesets.Changeset changeset = changeSetBuilder.build(); - scmBuilder.addChangeset(changeset); - scmBuilder.addChangesetIndexByLine(scmBuilder.getChangesetCount() - 1); - if (scmRevision != null) { - changesetByRevision.put(scmRevision, changeset); - } - } else { - scmBuilder.addChangesetIndexByLine(scmBuilder.getChangesetList().indexOf(changesetByRevision.get(scmRevision))); - } - } - } - return scmBuilder.build(); - } finally { - dbClient.closeSession(dbSession); - } - } - - private void computeLastCommitDateAndAuthor() { - for (BatchReport.Changesets.Changeset changeset : scmChangesets.getChangesetList()) { - if (changeset.hasAuthor() && changeset.hasDate() && changeset.getDate() > lastCommitDate) { - lastCommitDate = changeset.getDate(); - lastCommitAuthor = changeset.getAuthor(); + if (line != null && scmChangesets != null) { + if (scmChangesets.hasChangesetForLine(line)) { + author = scmChangesets.getChangesetForLine(line).getAuthor(); + } else { + LOGGER.warn("No SCM info has been found for issue {}", issue); } } + return defaultIfEmpty(author, lastCommitAuthor); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java new file mode 100644 index 00000000000..0eb392ccb42 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java @@ -0,0 +1,172 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.computation.issue; + +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.scm.Changeset; +import org.sonar.server.computation.scm.ScmInfoRepositoryRule; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.server.computation.component.ReportComponent.builder; + +public class IssueAssignerTest { + + static final int FILE_REF = 1; + static final Component FILE = builder(Component.Type.FILE, FILE_REF).setKey("FILE_KEY").setUuid("FILE_UUID").build(); + + @org.junit.Rule + public LogTester logTester = new LogTester(); + + @org.junit.Rule + public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule(); + + ScmAccountToUser scmAccountToUser = mock(ScmAccountToUser.class); + DefaultAssignee defaultAssignee = mock(DefaultAssignee.class); + + IssueAssigner underTest = new IssueAssigner(scmInfoRepository, scmAccountToUser, defaultAssignee); + + @Test + public void set_author_to_issue() throws Exception { + setSingleChangeset("john", 123456789L, "rev-1"); + DefaultIssue issue = new DefaultIssue().setNew(true).setLine(1); + + underTest.onIssue(FILE, issue); + + assertThat(issue.authorLogin()).isEqualTo("john"); + } + + @Test + public void nothing_to_do_if_issue_is_not_new() throws Exception { + setSingleChangeset("john", 123456789L, "rev-1"); + DefaultIssue issue = new DefaultIssue().setNew(false).setLine(1); + + underTest.onIssue(FILE, issue); + + assertThat(issue.authorLogin()).isNull(); + } + + @Test + public void nothing_to_do_if_no_changeset() throws Exception { + DefaultIssue issue = new DefaultIssue().setNew(true).setLine(1); + + underTest.onIssue(FILE, issue); + + assertThat(issue.authorLogin()).isNull(); + } + + @Test + public void set_assignee_to_issue() throws Exception { + addScmUser("john", "John C"); + setSingleChangeset("john", 123456789L, "rev-1"); + DefaultIssue issue = new DefaultIssue().setNew(true).setLine(1); + + underTest.onIssue(FILE, issue); + + assertThat(issue.assignee()).isEqualTo("John C"); + } + + @Test + public void set_default_assignee_if_author_not_found() throws Exception { + addScmUser("john", null); + setSingleChangeset("john", 123456789L, "rev-1"); + when(defaultAssignee.getLogin()).thenReturn("John C"); + DefaultIssue issue = new DefaultIssue().setNew(true).setLine(1); + + underTest.onIssue(FILE, issue); + + assertThat(issue.assignee()).isEqualTo("John C"); + } + + @Test + public void set_last_committer_when_line_is_null() throws Exception { + addScmUser("henry", "Henry V"); + Changeset changeset1 = Changeset.newChangesetBuilder() + .setAuthor("john") + .setDate(123456789L) + .setRevision("rev-1") + .build(); + // Latest changeset + Changeset changeset2 = Changeset.newChangesetBuilder() + .setAuthor("henry") + .setDate(1234567810L) + .setRevision("rev-2") + .build(); + scmInfoRepository.setScmInfo(FILE_REF, changeset1, changeset2, changeset1); + + DefaultIssue issue = new DefaultIssue() + .setNew(true) + .setLine(null); + + underTest.onIssue(FILE, issue); + + assertThat(issue.assignee()).isEqualTo("Henry V"); + } + + @Test + public void set_last_committer_when_line_is_bigger_than_changeset_size() throws Exception { + addScmUser("john", "John C"); + Changeset changeset = Changeset.newChangesetBuilder() + .setAuthor("john") + .setDate(123456789L) + .setRevision("rev-1") + .build(); + scmInfoRepository.setScmInfo(FILE_REF, changeset, changeset); + DefaultIssue issue = new DefaultIssue().setNew(true).setLine(3); + + underTest.onIssue(FILE, issue); + + assertThat(issue.assignee()).isEqualTo("John C"); + } + + @Test + public void display_warning_when_line_is_above_max_size() throws Exception { + setSingleChangeset("john", 123456789L, "rev-1"); + DefaultIssue issue = new DefaultIssue().setNew(true).setLine(2); + + underTest.onIssue(FILE, issue); + + assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("No SCM info has been found for issue DefaultIssue[key=,componentUuid=,componentKey=,moduleUuid=,moduleUuidPath=," + + "projectUuid=,projectKey=,ruleKey=,language=,severity=,manualSeverity=false,message=,line=2,effortToFix=,debt=," + + "status=,resolution=,reporter=,assignee=,checksum=,attributes=,authorLogin=,actionPlanKey=,comments=,tags=,l" + + "ocations=,creationDate=,updateDate=,closeDate=,currentChange=,changes=,isNew=true,beingClosed=false,onDisabledRule=false," + + "isChanged=false,sendNotifications=false,selectedAt=]"); + } + + private void setSingleChangeset(String author, Long date, String revision) { + scmInfoRepository.setScmInfo(FILE_REF, + Changeset.newChangesetBuilder() + .setAuthor(author) + .setDate(date) + .setRevision(revision) + .build()); + } + + private void addScmUser(String scmAccount, String userName) { + when(scmAccountToUser.getNullable(scmAccount)).thenReturn(userName); + } + +}