]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9949 Also merge open issues from short living branches
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 19 Oct 2017 10:10:27 +0000 (12:10 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 20 Oct 2017 08:45:15 +0000 (18:45 +1000)
16 files changed:
server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java
sonar-core/src/main/java/org/sonar/core/issue/tracking/SimpleTracker.java
sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java
sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueFromWs.java

index f6c6bcd977b1c82874b6e0c46c9a62665ccee8b5..e9058d626b4ea95aec6a2ffff4941108a825cf16 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.core.issue;
 
+import java.util.Date;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
@@ -34,8 +35,9 @@ public class ShortBranchIssue implements Trackable {
   private final RuleKey ruleKey;
   private final String status;
   private final String branchName;
+  private final Date creationDate;
 
-  public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName) {
+  public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName, Date creationDate) {
     this.key = key;
     this.line = line;
     this.message = message;
@@ -43,6 +45,7 @@ public class ShortBranchIssue implements Trackable {
     this.ruleKey = ruleKey;
     this.status = status;
     this.branchName = branchName;
+    this.creationDate = creationDate;
   }
 
   public String getKey() {
@@ -80,6 +83,11 @@ public class ShortBranchIssue implements Trackable {
     return branchName;
   }
 
+  @Override
+  public Date getCreationDate() {
+    return creationDate;
+  }
+
   @Override
   public int hashCode() {
     return key.hashCode();
index 164f56d5b3540fe832c8cb04c2703a7e71d96ad5..ab8ccd3871f2cd54a18b7f82c91571d9ef3d2e98 100644 (file)
@@ -103,8 +103,8 @@ public class IssueDao implements Dao {
     mapper(dbSession).scrollNonClosedByModuleOrProject(module.projectUuid(), likeModuleUuidPath, handler);
   }
 
-  public List<ShortBranchIssueDto> selectResolvedOrConfirmedByComponentUuids(DbSession dbSession, Collection<String> componentUuids) {
-    return executeLargeInputs(componentUuids, mapper(dbSession)::selectResolvedOrConfirmedByComponentUuids);
+  public List<ShortBranchIssueDto> selectOpenByComponentUuids(DbSession dbSession, Collection<String> componentUuids) {
+    return executeLargeInputs(componentUuids, mapper(dbSession)::selectOpenByComponentUuids);
   }
 
   public void insert(DbSession session, IssueDto dto) {
index 4335a47112608a6ac817295303164a8446fa8b98..75ec4c9d54207431c23b6654c12741fa7f18d95b 100644 (file)
@@ -32,7 +32,7 @@ public interface IssueMapper {
 
   List<IssueDto> selectByKeys(List<String> keys);
 
-  List<ShortBranchIssueDto> selectResolvedOrConfirmedByComponentUuids(List<String> componentUuids);
+  List<ShortBranchIssueDto> selectOpenByComponentUuids(List<String> componentUuids);
 
   void insert(IssueDto issue);
 
index 3c1901041e4c5ceebcb7bc37c44e6abb2b44c941..79208ddd20da09badf8f59a956d93279e9786169 100644 (file)
@@ -27,6 +27,8 @@ import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.issue.ShortBranchIssue;
 
+import static org.sonar.api.utils.DateUtils.longToDate;
+
 public final class ShortBranchIssueDto implements Serializable {
 
   private String kee;
@@ -34,6 +36,7 @@ public final class ShortBranchIssueDto implements Serializable {
   private Integer line;
   private String checksum;
   private String status;
+  private Long issueCreationDate;
 
   // joins
   private String ruleKey;
@@ -114,7 +117,7 @@ public final class ShortBranchIssueDto implements Serializable {
     return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
   }
 
-  public static ShortBranchIssue toShortBranchIssue(ShortBranchIssueDto dto) {
-    return new ShortBranchIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getBranchName());
+  public ShortBranchIssue toShortBranchIssue() {
+    return new ShortBranchIssue(getKey(), getLine(), getMessage(), getChecksum(), getRuleKey(), getStatus(), getBranchName(), longToDate(issueCreationDate));
   }
 }
index 0a4b557880ced386309418a8500d7280d48597f1..5d7e051032f5821c144c7f79d364225136d36b31 100644 (file)
       ON i.project_uuid = b.uuid
       AND b.branch_type = 'SHORT'
       AND b.merge_branch_uuid = #{mergeBranchUuid,jdbcType=VARCHAR}
-      AND (i.status = 'CONFIRMED' OR i.status = 'RESOLVED')
+      AND i.status != 'CLOSED'
   </select>
 
 </mapper>
index 09d434355027ce267359d9d70c4a40376d48be86..545963ea5e593f21b14723d4426e7504e8c3c90d 100644 (file)
     </foreach>
   </select>
 
-  <select id="selectResolvedOrConfirmedByComponentUuids" parameterType="map" resultType="ShortBranchIssue">
+  <select id="selectOpenByComponentUuids" parameterType="map" resultType="ShortBranchIssue">
     select
       i.kee as kee,
       i.message as message,
       i.line as line,
       i.status as status,
       i.checksum as checksum,
+      i.issue_creation_date as issueCreationDate,
       r.plugin_rule_key as ruleKey,
       r.plugin_name as ruleRepo,
       b.kee as branchName
       <foreach collection="list" open="(" close=")" item="key" separator=",">
         #{key,jdbcType=VARCHAR}
       </foreach>
-      and (i.status = 'CONFIRMED' OR i.status = 'RESOLVED')
+      and i.status &lt;&gt; 'CLOSED'
   </select>
 
   <select id="scrollNonClosedByModuleOrProject" parameterType="map" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
index 8c4682215c153f6bf4e26c63aa3155e88b77d407..cc31fa3d9c7a4af8b8c76f848fa5cec55a0a212a 100644 (file)
@@ -184,7 +184,7 @@ public class IssueDaoTest {
   }
 
   @Test
-  public void selectResolvedOrConfirmedByComponentUuid() {
+  public void selectOpenByComponentUuid() {
     RuleDefinitionDto rule = db.rules().insert();
     ComponentDto project = db.components().insertMainBranch();
     ComponentDto projectBranch = db.components().insertProjectBranch(project,
@@ -200,13 +200,13 @@ public class IssueDaoTest {
     IssueDto wontfixIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX));
     IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE));
 
-    assertThat(underTest.selectResolvedOrConfirmedByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())))
+    assertThat(underTest.selectOpenByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())))
       .extracting("kee")
-      .containsOnly(confirmedIssue.getKey(), wontfixIssue.getKey(), fpIssue.getKey());
+      .containsOnly(openIssue.getKey(), reopenedIssue.getKey(), confirmedIssue.getKey(), wontfixIssue.getKey(), fpIssue.getKey());
   }
 
   @Test
-  public void selectResolvedOrConfirmedByComponentUuid_should_correctly_map_required_fields() {
+  public void selectOpenByComponentUuid_should_correctly_map_required_fields() {
     RuleDefinitionDto rule = db.rules().insert();
     ComponentDto project = db.components().insertMainBranch();
     ComponentDto projectBranch = db.components().insertProjectBranch(project,
@@ -216,7 +216,7 @@ public class IssueDaoTest {
     ComponentDto file = db.components().insertComponent(newFileDto(projectBranch));
     IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE"));
 
-    ShortBranchIssueDto fp = underTest.selectResolvedOrConfirmedByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())).get(0);
+    ShortBranchIssueDto fp = underTest.selectOpenByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())).get(0);
     assertThat(fp.getLine()).isEqualTo(fpIssue.getLine());
     assertThat(fp.getMessage()).isEqualTo(fpIssue.getMessage());
     assertThat(fp.getChecksum()).isEqualTo(fpIssue.getChecksum());
index 1beca5a1b642c2ef9a4eac569e6d245f0c9babd6..cf91a53b07cc5e5cd7c86b09161cb336ee7b7291 100644 (file)
@@ -56,7 +56,7 @@ public class ShortBranchIssuesLoader {
       return Collections.emptyList();
     }
     try (DbSession session = dbClient.openSession(false)) {
-      return dbClient.issueDao().selectResolvedOrConfirmedByComponentUuids(session, uuids)
+      return dbClient.issueDao().selectOpenByComponentUuids(session, uuids)
         .stream()
         .map(ShortBranchIssueDto::toShortBranchIssue)
         .collect(Collectors.toList());
index d9fca55cbcaad9345c62eed7e9d6880f2bf06723..41f1dd43c092474113ba50b5637b5184b7efb58b 100644 (file)
@@ -111,7 +111,7 @@ public class ShortBranchComponentsWithIssuesTest {
     setRootUuid(long1.uuid());
 
     assertThat(underTest.getUuids(fileWithNoIssues.getKey())).isEmpty();
-    assertThat(underTest.getUuids(fileWithOneOpenIssue.getKey())).isEmpty();
+    assertThat(underTest.getUuids(fileWithOneOpenIssue.getKey())).containsOnly(fileWithOneOpenIssue.uuid());
     assertThat(underTest.getUuids(fileWithOneResolvedIssue.getKey())).containsOnly(fileWithOneResolvedIssue.uuid());
     assertThat(underTest.getUuids(fileWithOneOpenTwoResolvedIssues.getKey())).containsOnly(fileWithOneOpenTwoResolvedIssues.uuid());
 
@@ -128,7 +128,7 @@ public class ShortBranchComponentsWithIssuesTest {
 
     assertThat(underTest.getUuids(fileWithOneResolvedIssue.getKey())).isEmpty();
     assertThat(underTest.getUuids(fileWithOneResolvedIssueOnLong2.getKey())).containsOnly(fileWithOneResolvedIssueOnLong2.uuid());
-    assertThat(underTest.getUuids(fileWithOneOpenIssueOnLong2.getKey())).isEmpty();
+    assertThat(underTest.getUuids(fileWithOneOpenIssueOnLong2.getKey())).containsOnly(fileWithOneOpenIssueOnLong2.uuid());
   }
 
   @Test
index 134f83b9d4eedb4e3d87f8f5ed47c3aa33ada077..c3b720e872cea877a59740d9324d1e699257fbcb 100644 (file)
 package org.sonar.server.computation.task.projectanalysis.issue;
 
 import com.google.common.collect.ImmutableMap;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import javax.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +65,7 @@ public class ShortBranchIssueMergerTest {
   @Test
   public void do_nothing_if_no_match() {
     when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.emptyList());
-    DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
+    DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
     copier.tryMerge(component, Collections.singleton(i));
 
     verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
@@ -71,7 +74,7 @@ public class ShortBranchIssueMergerTest {
 
   @Test
   public void do_nothing_if_no_new_issue() {
-    DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
+    DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
     when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i, "myBranch")));
     copier.tryMerge(component, Collections.emptyList());
 
@@ -81,9 +84,9 @@ public class ShortBranchIssueMergerTest {
 
   @Test
   public void update_status_on_matches() {
-    DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
+    DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
     ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1, "myBranch");
-    DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null);
+    DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null, new Date());
 
     when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue));
     when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1));
@@ -96,11 +99,11 @@ public class ShortBranchIssueMergerTest {
 
   @Test
   public void prefer_resolved_issues() {
-    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null), "myBranch1");
-    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null), "myBranch2");
-    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE);
+    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_REOPENED, null, new Date()), "myBranch1");
+    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null, new Date()), "myBranch2");
+    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE, new Date());
     ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3");
-    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null);
+    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null, new Date());
 
     when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
     when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
@@ -108,7 +111,36 @@ public class ShortBranchIssueMergerTest {
     verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3");
   }
 
-  private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) {
+  @Test
+  public void prefer_confirmed_issues() {
+    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_REOPENED, null, new Date()), "myBranch1");
+    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_OPEN, null, new Date()), "myBranch2");
+    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
+    ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3");
+    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null, new Date());
+
+    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
+    when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
+    copier.tryMerge(component, Collections.singleton(newIssue));
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3");
+  }
+
+  @Test
+  public void prefer_older_issues() {
+    Instant now = Instant.now();
+    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_REOPENED, null, Date.from(now.plus(2, ChronoUnit.SECONDS))), "myBranch1");
+    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_OPEN, null, Date.from(now.plus(1, ChronoUnit.SECONDS))), "myBranch2");
+    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_OPEN, null, Date.from(now));
+    ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3");
+    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null, new Date());
+
+    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
+    when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
+    copier.tryMerge(component, Collections.singleton(newIssue));
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3");
+  }
+
+  private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution, Date creationDate) {
     DefaultIssue issue = new DefaultIssue();
     issue.setKey(key);
     issue.setRuleKey(RuleKey.of("repo", ruleKey));
@@ -116,10 +148,11 @@ public class ShortBranchIssueMergerTest {
     issue.setLine(1);
     issue.setStatus(status);
     issue.setResolution(resolution);
+    issue.setCreationDate(creationDate);
     return issue;
   }
 
   private ShortBranchIssue newShortBranchIssue(DefaultIssue i, String originBranch) {
-    return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), originBranch);
+    return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), originBranch, i.creationDate());
   }
 }
index 02032a1c62dcda9378af53709e199539c513540e..c7b0626e826e10a0c20a051f9ea0c736da8e96fa 100644 (file)
@@ -653,4 +653,9 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
   public String getStatus() {
     return status;
   }
+
+  @Override
+  public Date getCreationDate() {
+    return creationDate;
+  }
 }
index 690969933245b01993e549c94d1340d26425afed..60cc9b05782699cb52386dd3634824b4ab98b131 100644 (file)
@@ -29,13 +29,11 @@ import org.apache.commons.lang.StringUtils;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.RuleKey;
 
+import static java.util.Comparator.comparing;
+
 public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
 
   protected void match(Tracking<RAW, BASE> tracking, Function<Trackable, SearchKey> searchKeyFactory) {
-    match(tracking, searchKeyFactory, false);
-  }
-
-  protected void match(Tracking<RAW, BASE> tracking, Function<Trackable, SearchKey> searchKeyFactory, boolean preferResolved) {
 
     if (tracking.isComplete()) {
       return;
@@ -49,21 +47,25 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
     for (RAW raw : tracking.getUnmatchedRaws()) {
       SearchKey rawKey = searchKeyFactory.apply(raw);
       Collection<BASE> bases = baseSearch.get(rawKey);
-      if (!bases.isEmpty()) {
-        BASE match;
-        if (preferResolved) {
-          match = bases.stream()
-            .filter(i -> Issue.STATUS_RESOLVED.equals(i.getStatus()))
-            .findFirst()
-            .orElse(bases.iterator().next());
-        } else {
-          // TODO taking the first one. Could be improved if there are more than 2 issues on the same line.
-          // Message could be checked to take the best one.
-          match = bases.iterator().next();
-        }
-        tracking.match(raw, match);
-        baseSearch.remove(rawKey, match);
-      }
+      bases.stream()
+        .sorted(comparing(this::statusRank).reversed()
+          .thenComparing(comparing(Trackable::getCreationDate)))
+        .findFirst()
+        .ifPresent(match -> {
+          tracking.match(raw, match);
+          baseSearch.remove(rawKey, match);
+        });
+    }
+  }
+
+  private int statusRank(BASE i) {
+    switch (i.getStatus()) {
+      case Issue.STATUS_RESOLVED:
+        return 2;
+      case Issue.STATUS_CONFIRMED:
+        return 1;
+      default:
+        return 0;
     }
   }
 
index 54fd8ea2bba5a915290f6b46720be367333dc719..b5fb56a6f26c543841a182ded86470299e050642 100644 (file)
@@ -31,10 +31,10 @@ public class SimpleTracker<RAW extends Trackable, BASE extends Trackable> extend
     Tracking<RAW, BASE> tracking = new Tracking<>(rawInput, baseInput);
 
     // 1. match issues with same rule, same line and same line hash, but not necessarily with same message
-    match(tracking, LineAndLineHashKey::new, true);
+    match(tracking, LineAndLineHashKey::new);
 
     // 2. match issues with same rule, same message and same line hash
-    match(tracking, LineHashAndMessageKey::new, true);
+    match(tracking, LineHashAndMessageKey::new);
 
     return tracking;
   }
index 0d04b81e36682efabc7fbbc9cf32d902cc73a3fe..6c6af607afc8832efba34b1ea9a4d0faff86fda5 100644 (file)
  */
 package org.sonar.core.issue.tracking;
 
+import java.util.Date;
 import javax.annotation.CheckForNull;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.DefaultIssue;
 
 public interface Trackable {
 
@@ -42,4 +44,9 @@ public interface Trackable {
   RuleKey getRuleKey();
 
   String getStatus();
+
+  /**
+   * Functional creation date for the issue. See {@link DefaultIssue#creationDate()}
+   */
+  Date getCreationDate();
 }
index 59e106f1433dcbcbbaecb8447185887297d7d1d8..c894c65fcdb6c844ba048ba7a739210f2025227a 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.core.issue.tracking;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.commons.codec.digest.DigestUtils;
@@ -186,7 +187,7 @@ public class TrackerTest {
   @Test
   public void do_not_fail_if_raw_line_does_not_exist() {
     FakeInput baseInput = new FakeInput();
-    FakeInput rawInput = new FakeInput("H1").addIssue(new Issue(200, "H200", RULE_SYSTEM_PRINT, "msg", org.sonar.api.issue.Issue.STATUS_OPEN));
+    FakeInput rawInput = new FakeInput("H1").addIssue(new Issue(200, "H200", RULE_SYSTEM_PRINT, "msg", org.sonar.api.issue.Issue.STATUS_OPEN, new Date()));
 
     Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
 
@@ -386,12 +387,14 @@ public class TrackerTest {
     private final Integer line;
     private final String message, lineHash;
     private final String status;
+    private final Date creationDate;
 
-    Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, String message, String status) {
+    Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, String message, String status, Date creationDate) {
       this.line = line;
       this.lineHash = lineHash;
       this.ruleKey = ruleKey;
       this.status = status;
+      this.creationDate = creationDate;
       this.message = trim(message);
     }
 
@@ -419,6 +422,11 @@ public class TrackerTest {
     public String getStatus() {
       return status;
     }
+
+    @Override
+    public Date getCreationDate() {
+      return creationDate;
+    }
   }
 
   private static class FakeInput implements Input<Issue> {
@@ -438,7 +446,7 @@ public class TrackerTest {
     }
 
     Issue createIssueOnLine(int line, RuleKey ruleKey, String message) {
-      Issue issue = new Issue(line, lineHashes.get(line - 1), ruleKey, message, org.sonar.api.issue.Issue.STATUS_OPEN);
+      Issue issue = new Issue(line, lineHashes.get(line - 1), ruleKey, message, org.sonar.api.issue.Issue.STATUS_OPEN, new Date());
       issues.add(issue);
       return issue;
     }
@@ -447,7 +455,7 @@ public class TrackerTest {
      * No line (line 0)
      */
     Issue createIssue(RuleKey ruleKey, String message) {
-      Issue issue = new Issue(null, "", ruleKey, message, org.sonar.api.issue.Issue.STATUS_OPEN);
+      Issue issue = new Issue(null, "", ruleKey, message, org.sonar.api.issue.Issue.STATUS_OPEN, new Date());
       issues.add(issue);
       return issue;
     }
index a0888d20341fe7842341e4ec75503aacf06cc838..2ddba9e201537cfb6d73c7fb872fc4a00a8a42de 100644 (file)
  */
 package org.sonar.scanner.issue.tracking;
 
+import java.util.Date;
 import javax.annotation.CheckForNull;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.DateUtils;
 import org.sonar.core.issue.tracking.Trackable;
 
 import static org.apache.commons.lang.StringUtils.trim;
@@ -65,7 +67,12 @@ public class ServerIssueFromWs implements Trackable {
 
   @Override
   public String getStatus() {
-    throw new UnsupportedOperationException();
+    return dto.getStatus();
+  }
+
+  @Override
+  public Date getCreationDate() {
+    return DateUtils.longToDate(dto.getCreationDate());
   }
 
 }