]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6397 Use ScmInfoRepo in IssueAssigner
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 30 Sep 2015 09:34:32 +0000 (11:34 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 2 Oct 2015 09:48:29 +0000 (11:48 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java [new file with mode: 0644]

index 130aa49cd6218b5fa7f6b76ab12062274a40d5d6..0fcb2485653ea96059e64de72270a4f1d911b656 100644 (file)
  * 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<ScmInfo> 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<Iterable<DbFileSources.Line>> lines = sourceService.getLines(dbSession, component.getUuid(), 1, Integer.MAX_VALUE);
-      Map<String, BatchReport.Changesets.Changeset> 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 (file)
index 0000000..0eb392c
--- /dev/null
@@ -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=<null>,componentUuid=<null>,componentKey=<null>,moduleUuid=<null>,moduleUuidPath=<null>," +
+      "projectUuid=<null>,projectKey=<null>,ruleKey=<null>,language=<null>,severity=<null>,manualSeverity=false,message=<null>,line=2,effortToFix=<null>,debt=<null>," +
+      "status=<null>,resolution=<null>,reporter=<null>,assignee=<null>,checksum=<null>,attributes=<null>,authorLogin=<null>,actionPlanKey=<null>,comments=<null>,tags=<null>,l" +
+      "ocations=<null>,creationDate=<null>,updateDate=<null>,closeDate=<null>,currentChange=<null>,changes=<null>,isNew=true,beingClosed=false,onDisabledRule=false," +
+      "isChanged=false,sendNotifications=false,selectedAt=<null>]");
+  }
+
+  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);
+  }
+
+}