]> source.dussan.org Git - sonarqube.git/commitdiff
add top 5 rules on 'New Issues' and 'My New Issues' notifications 294/head
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 6 May 2015 12:25:01 +0000 (14:25 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Thu, 7 May 2015 06:39:55 +0000 (08:39 +0200)
13 files changed:
server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationFactory.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesStatistics.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplateTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesEmailTemplateTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java
server/sonar-server/src/test/resources/org/sonar/server/issue/notification/MyNewIssuesEmailTemplateTest/email_with_all_details.txt
server/sonar-server/src/test/resources/org/sonar/server/issue/notification/NewIssuesEmailTemplateTest/email_with_all_details.txt

index 6617ea1d7a044a37e16b408794fec8f2dbc47977..044abf8543a3fd91ee14097e83682aefde23f63f 100644 (file)
@@ -28,7 +28,7 @@ import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.plugins.emailnotifications.api.EmailMessage;
 import org.sonar.plugins.emailnotifications.api.EmailTemplate;
-import org.sonar.server.issue.notification.NewIssuesStatistics.METRIC;
+import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
@@ -82,6 +82,7 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
     message.append("Project: ").append(projectName).append(NEW_LINE).append(NEW_LINE);
     appendSeverity(message, notification);
     appendAssignees(message, notification);
+    appendRules(message, notification);
     appendTags(message, notification);
     appendComponents(message, notification);
     appendFooter(message, notification);
@@ -97,15 +98,15 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
   protected String subject(Notification notification, String projectName) {
     return String.format("%s: %s new issues (new debt: %s)",
       projectName,
-      notification.getFieldValue(METRIC.SEVERITY + COUNT),
-      notification.getFieldValue(METRIC.DEBT + COUNT));
+      notification.getFieldValue(Metric.SEVERITY + COUNT),
+      notification.getFieldValue(Metric.DEBT + COUNT));
   }
 
-  private boolean doNotHaveValue(Notification notification, METRIC metric) {
+  private boolean doNotHaveValue(Notification notification, Metric metric) {
     return notification.getFieldValue(metric + DOT + "1" + LABEL) == null;
   }
 
-  private void genericAppendOfMetric(METRIC metric, String label, StringBuilder message, Notification notification) {
+  private void genericAppendOfMetric(Metric metric, String label, StringBuilder message, Notification notification) {
     if (doNotHaveValue(notification, metric)) {
       return;
     }
@@ -130,22 +131,26 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
   }
 
   protected void appendAssignees(StringBuilder message, Notification notification) {
-    genericAppendOfMetric(METRIC.ASSIGNEE, "Assignees", message, notification);
+    genericAppendOfMetric(Metric.ASSIGNEE, "Assignees", message, notification);
   }
 
   protected void appendComponents(StringBuilder message, Notification notification) {
-    genericAppendOfMetric(METRIC.COMPONENT, "Most impacted files", message, notification);
+    genericAppendOfMetric(Metric.COMPONENT, "Most impacted files", message, notification);
   }
 
   protected void appendTags(StringBuilder message, Notification notification) {
-    genericAppendOfMetric(METRIC.TAGS, "Tags", message, notification);
+    genericAppendOfMetric(Metric.TAG, "Tags", message, notification);
+  }
+
+  protected void appendRules(StringBuilder message, Notification notification) {
+    genericAppendOfMetric(Metric.RULE, "Rules", message, notification);
   }
 
   protected void appendSeverity(StringBuilder message, Notification notification) {
     message
       .append(String.format("%s new issues (new debt: %s)",
-        notification.getFieldValue(METRIC.SEVERITY + COUNT),
-        notification.getFieldValue(METRIC.DEBT + COUNT)))
+        notification.getFieldValue(Metric.SEVERITY + COUNT),
+        notification.getFieldValue(Metric.DEBT + COUNT)))
       .append(NEW_LINE).append(NEW_LINE)
       .append(TAB)
       .append("Severity")
@@ -153,10 +158,10 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
       .append(TAB)
       .append(TAB);
 
-    for (Iterator<String> severityIterator = Lists.reverse(Severity.ALL).iterator(); severityIterator.hasNext();) {
+    for (Iterator<String> severityIterator = Lists.reverse(Severity.ALL).iterator(); severityIterator.hasNext(); ) {
       String severity = severityIterator.next();
       String severityLabel = i18n.message(getLocale(), "severity." + severity, severity);
-      message.append(severityLabel).append(": ").append(notification.getFieldValue(METRIC.SEVERITY + DOT + severity + COUNT));
+      message.append(severityLabel).append(": ").append(notification.getFieldValue(Metric.SEVERITY + DOT + severity + COUNT));
       if (severityIterator.hasNext()) {
         message.append(TAB);
       }
index 8cd969cc9b18b86cf291f970c77aee42866171cd..fb500e4894938780299f880f8bec55bfe7c1e92b 100644 (file)
@@ -24,7 +24,7 @@ import org.sonar.api.config.EmailSettings;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.notifications.Notification;
 import org.sonar.api.utils.DateUtils;
-import org.sonar.server.issue.notification.NewIssuesStatistics.METRIC;
+import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;
 
 import java.util.Date;
 
@@ -50,7 +50,7 @@ public class MyNewIssuesEmailTemplate extends AbstractNewIssuesEmailTemplate {
   @Override
   protected String subject(Notification notification, String projectName) {
     return String.format("You have %s new issues on project %s",
-      notification.getFieldValue(METRIC.SEVERITY + COUNT),
+      notification.getFieldValue(Metric.SEVERITY + COUNT),
       projectName);
   }
 
index 615420541cd51ef75709dd8c06b51fb68e2809da..ce4343f3de3805332135e851f3640be6ba08df66 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.issue.notification;
 
 import org.sonar.api.utils.Durations;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.user.index.UserIndex;
 
 import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_ASSIGNEE;
@@ -30,8 +31,8 @@ public class MyNewIssuesNotification extends NewIssuesNotification {
 
   public static final String TYPE = "my-new-issues";
 
-  MyNewIssuesNotification(UserIndex userIndex, DbClient dbClient, Durations durations) {
-    super(TYPE, userIndex, dbClient, durations);
+  MyNewIssuesNotification(UserIndex userIndex, RuleIndex ruleIndex, DbClient dbClient, Durations durations) {
+    super(TYPE, userIndex, ruleIndex, dbClient, durations);
   }
 
   public MyNewIssuesNotification setAssignee(String assignee) {
index 58a9ed66f4e6f816eb80963e5a411c23a45a65f0..bafb6daf244dd167818340f83058052cf00f3af4 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.issue.notification;
 import com.google.common.collect.Multiset;
 import org.sonar.api.component.Component;
 import org.sonar.api.notifications.Notification;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.Duration;
@@ -30,7 +31,9 @@ import org.sonar.core.component.ComponentDto;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.server.db.DbClient;
-import org.sonar.server.issue.notification.NewIssuesStatistics.METRIC;
+import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.user.index.UserDoc;
 import org.sonar.server.user.index.UserIndex;
 
@@ -38,8 +41,11 @@ import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 
-import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.*;
-import static org.sonar.server.issue.notification.NewIssuesStatistics.METRIC.SEVERITY;
+import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_DATE;
+import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_KEY;
+import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_NAME;
+import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_UUID;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.SEVERITY;
 
 public class NewIssuesNotification extends Notification {
 
@@ -47,18 +53,21 @@ public class NewIssuesNotification extends Notification {
   private static final long serialVersionUID = -6305871981920103093L;
   private static final String COUNT = ".count";
   private static final String LABEL = ".label";
+  private static final String DOT = ".";
 
   private final transient UserIndex userIndex;
+  private final transient RuleIndex ruleIndex;
   private final transient DbClient dbClient;
   private final transient Durations durations;
 
-  NewIssuesNotification(UserIndex userIndex, DbClient dbClient, Durations durations) {
-    this(TYPE, userIndex, dbClient, durations);
+  NewIssuesNotification(UserIndex userIndex, RuleIndex ruleIndex, DbClient dbClient, Durations durations) {
+    this(TYPE, userIndex, ruleIndex, dbClient, durations);
   }
 
-  protected NewIssuesNotification(String type, UserIndex userIndex, DbClient dbClient, Durations durations) {
+  protected NewIssuesNotification(String type, UserIndex userIndex, RuleIndex ruleIndex, DbClient dbClient, Durations durations) {
     super(type);
     this.userIndex = userIndex;
+    this.ruleIndex = ruleIndex;
     this.dbClient = dbClient;
     this.durations = durations;
   }
@@ -82,20 +91,33 @@ public class NewIssuesNotification extends Notification {
     setAssigneesStatistics(stats);
     setTagsStatistics(stats);
     setComponentsStatistics(stats);
+    setRuleStatistics(stats);
 
     return this;
   }
 
+  protected void setRuleStatistics(NewIssuesStatistics.Stats stats) {
+    Metric metric = Metric.RULE;
+    List<Multiset.Entry<String>> metricStats = stats.statsForMetric(metric);
+    for (int i = 0; i < 5 && i < metricStats.size(); i++) {
+      String ruleKey = metricStats.get(i).getElement();
+      Rule rule = ruleIndex.getByKey(RuleKey.parse(ruleKey));
+      String name = rule.name() + " (" + rule.language() + ")";
+      setFieldValue(metric + DOT + (i + 1) + LABEL, name);
+      setFieldValue(metric + DOT + (i + 1) + COUNT, String.valueOf(metricStats.get(i).getCount()));
+    }
+  }
+
   protected void setComponentsStatistics(NewIssuesStatistics.Stats stats) {
-    METRIC metric = METRIC.COMPONENT;
+    Metric metric = Metric.COMPONENT;
     List<Multiset.Entry<String>> componentStats = stats.statsForMetric(metric);
     DbSession dbSession = dbClient.openSession(false);
     try {
       for (int i = 0; i < 5 && i < componentStats.size(); i++) {
         String uuid = componentStats.get(i).getElement();
         String componentName = dbClient.componentDao().getByUuid(dbSession, uuid).name();
-        setFieldValue(metric + "." + (i + 1) + LABEL, componentName);
-        setFieldValue(metric + "." + (i + 1) + COUNT, String.valueOf(componentStats.get(i).getCount()));
+        setFieldValue(metric + DOT + (i + 1) + LABEL, componentName);
+        setFieldValue(metric + DOT + (i + 1) + COUNT, String.valueOf(componentStats.get(i).getCount()));
       }
     } finally {
       MyBatis.closeQuietly(dbSession);
@@ -103,35 +125,35 @@ public class NewIssuesNotification extends Notification {
   }
 
   protected void setTagsStatistics(NewIssuesStatistics.Stats stats) {
-    METRIC metric = METRIC.TAGS;
+    Metric metric = Metric.TAG;
     List<Multiset.Entry<String>> metricStats = stats.statsForMetric(metric);
     for (int i = 0; i < 5 && i < metricStats.size(); i++) {
-      setFieldValue(metric + "." + (i + 1) + COUNT, String.valueOf(metricStats.get(i).getCount()));
-      setFieldValue(metric + "." + (i + 1) + ".label", metricStats.get(i).getElement());
+      setFieldValue(metric + DOT + (i + 1) + COUNT, String.valueOf(metricStats.get(i).getCount()));
+      setFieldValue(metric + DOT + (i + 1) + ".label", metricStats.get(i).getElement());
     }
   }
 
   protected void setAssigneesStatistics(NewIssuesStatistics.Stats stats) {
-    METRIC metric = METRIC.ASSIGNEE;
+    Metric metric = Metric.ASSIGNEE;
     List<Multiset.Entry<String>> metricStats = stats.statsForMetric(metric);
     for (int i = 0; i < 5 && i < metricStats.size(); i++) {
       String login = metricStats.get(i).getElement();
       UserDoc user = userIndex.getNullableByLogin(login);
       String name = user == null ? login : user.name();
-      setFieldValue(metric + "." + (i + 1) + LABEL, name);
-      setFieldValue(metric + "." + (i + 1) + COUNT, String.valueOf(metricStats.get(i).getCount()));
+      setFieldValue(metric + DOT + (i + 1) + LABEL, name);
+      setFieldValue(metric + DOT + (i + 1) + COUNT, String.valueOf(metricStats.get(i).getCount()));
     }
   }
 
   public NewIssuesNotification setDebt(Duration debt) {
-    setFieldValue(METRIC.DEBT + COUNT, durations.format(Locale.ENGLISH, debt));
+    setFieldValue(Metric.DEBT + COUNT, durations.format(Locale.ENGLISH, debt));
     return this;
   }
 
   protected void setSeverityStatistics(NewIssuesStatistics.Stats stats) {
     setFieldValue(SEVERITY + COUNT, String.valueOf(stats.countForMetric(SEVERITY)));
     for (String severity : Severity.ALL) {
-      setFieldValue(SEVERITY + "." + severity + COUNT, String.valueOf(stats.countForMetric(SEVERITY, severity)));
+      setFieldValue(SEVERITY + DOT + severity + COUNT, String.valueOf(stats.countForMetric(SEVERITY, severity)));
     }
   }
 
index 6686587a82c90864e6d38f9400b987ac071e6b5d..6fa32e95b07ecc4e3fb90ce3179c47a2cc385a1f 100644 (file)
@@ -23,24 +23,27 @@ package org.sonar.server.issue.notification;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.utils.Durations;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.user.index.UserIndex;
 
 public class NewIssuesNotificationFactory implements ServerComponent {
   private final UserIndex userIndex;
+  private final RuleIndex ruleIndex;
   private final DbClient dbClient;
   private final Durations durations;
 
-  public NewIssuesNotificationFactory(UserIndex userIndex, DbClient dbClient, Durations durations) {
+  public NewIssuesNotificationFactory(UserIndex userIndex, RuleIndex ruleIndex, DbClient dbClient, Durations durations) {
     this.userIndex = userIndex;
+    this.ruleIndex = ruleIndex;
     this.dbClient = dbClient;
     this.durations = durations;
   }
 
   public MyNewIssuesNotification newMyNewIssuesNotification() {
-    return new MyNewIssuesNotification(userIndex, dbClient, durations);
+    return new MyNewIssuesNotification(userIndex, ruleIndex, dbClient, durations);
   }
 
   public NewIssuesNotification newNewIssuesNotication() {
-    return new NewIssuesNotification(userIndex, dbClient, durations);
+    return new NewIssuesNotification(userIndex, ruleIndex, dbClient, durations);
   }
 }
index f3eecd1fe130b1057b1184057aa6228b536e675c..396a4eab0ed2a79bb1489aa34f2d2978a5b43081 100644 (file)
@@ -23,6 +23,7 @@ package org.sonar.server.issue.notification;
 import com.google.common.collect.HashMultiset;
 import com.google.common.collect.Multiset;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.Duration;
 import org.sonar.core.util.MultiSets;
 
@@ -32,7 +33,11 @@ import java.util.List;
 import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.server.issue.notification.NewIssuesStatistics.METRIC.*;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.ASSIGNEE;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.COMPONENT;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.RULE;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.SEVERITY;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.TAG;
 
 public class NewIssuesStatistics {
   private Map<String, Stats> assigneesStatistics = new LinkedHashMap<>();
@@ -65,11 +70,11 @@ public class NewIssuesStatistics {
     return globalStatistics.hasIssues();
   }
 
-  enum METRIC {
-    SEVERITY(true), TAGS(true), COMPONENT(true), ASSIGNEE(true), DEBT(false);
+  enum Metric {
+    SEVERITY(true), TAG(true), COMPONENT(true), ASSIGNEE(true), DEBT(false), RULE(true);
     private final boolean isComputedByDistribution;
 
-    METRIC(boolean isComputedByDistribution) {
+    Metric(boolean isComputedByDistribution) {
       this.isComputedByDistribution = isComputedByDistribution;
     }
 
@@ -79,11 +84,11 @@ public class NewIssuesStatistics {
   }
 
   public static class Stats {
-    private final Map<METRIC, Multiset<String>> distributions = new EnumMap<>(METRIC.class);
+    private final Map<Metric, Multiset<String>> distributions = new EnumMap<>(Metric.class);
     private long debtInMinutes = 0L;
 
     public Stats() {
-      for (METRIC metric : METRIC.values()) {
+      for (Metric metric : Metric.values()) {
         if (metric.isComputedByDistribution()) {
           distributions.put(metric, HashMultiset.<String>create());
         }
@@ -93,11 +98,15 @@ public class NewIssuesStatistics {
     public void add(Issue issue) {
       distributions.get(SEVERITY).add(issue.severity());
       distributions.get(COMPONENT).add(issue.componentUuid());
+      RuleKey ruleKey = issue.ruleKey();
+      if (ruleKey != null) {
+        distributions.get(RULE).add(ruleKey.toString());
+      }
       if (issue.assignee() != null) {
         distributions.get(ASSIGNEE).add(issue.assignee());
       }
       for (String tag : issue.tags()) {
-        distributions.get(TAGS).add(tag);
+        distributions.get(TAG).add(tag);
       }
       Duration debt = issue.debt();
       if (debt != null) {
@@ -105,11 +114,11 @@ public class NewIssuesStatistics {
       }
     }
 
-    public int countForMetric(METRIC metric) {
+    public int countForMetric(Metric metric) {
       return distributionFor(metric).size();
     }
 
-    public int countForMetric(METRIC metric, String label) {
+    public int countForMetric(Metric metric, String label) {
       return distributionFor(metric).count(label);
     }
 
@@ -121,11 +130,11 @@ public class NewIssuesStatistics {
       return !distributionFor(SEVERITY).isEmpty();
     }
 
-    public List<Multiset.Entry<String>> statsForMetric(METRIC metric) {
+    public List<Multiset.Entry<String>> statsForMetric(Metric metric) {
       return MultiSets.listOrderedByHighestCounts(distributionFor(metric));
     }
 
-    private Multiset<String> distributionFor(METRIC metric) {
+    private Multiset<String> distributionFor(Metric metric) {
       checkArgument(metric.isComputedByDistribution());
       return distributions.get(metric);
     }
index 509c6d8cc0d82561e2d9a18c71f696d067c4c1d2..8ab8fe5635469d705e506a18f8c8a16df8f5c1ef 100644 (file)
@@ -37,10 +37,16 @@ import java.util.Date;
 import java.util.Locale;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.sonar.server.issue.notification.NewIssuesStatistics.METRIC.*;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.COMPONENT;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.DEBT;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.RULE;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.SEVERITY;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.TAG;
 
 public class MyNewIssuesEmailTemplateTest {
 
@@ -83,6 +89,7 @@ public class MyNewIssuesEmailTemplateTest {
   public void format_email_with_all_fields_filled() throws Exception {
     Notification notification = newNotification();
     addTags(notification);
+    addRules(notification);
     addComponents(notification);
 
     EmailMessage message = sut.format(notification);
@@ -149,10 +156,10 @@ public class MyNewIssuesEmailTemplateTest {
 
   private void addTags(Notification notification) {
     notification
-      .setFieldValue(TAGS + ".1.label", "oscar")
-      .setFieldValue(TAGS + ".1.count", "3")
-      .setFieldValue(TAGS + ".2.label", "cesar")
-      .setFieldValue(TAGS + ".2.count", "10");
+      .setFieldValue(TAG + ".1.label", "oscar")
+      .setFieldValue(TAG + ".1.count", "3")
+      .setFieldValue(TAG + ".2.label", "cesar")
+      .setFieldValue(TAG + ".2.count", "10");
   }
 
   private void addComponents(Notification notification) {
@@ -162,4 +169,12 @@ public class MyNewIssuesEmailTemplateTest {
       .setFieldValue(COMPONENT + ".2.label", "/path/to/directory")
       .setFieldValue(COMPONENT + ".2.count", "7");
   }
+
+  private void addRules(Notification notification) {
+    notification
+      .setFieldValue(RULE + ".1.label", "Rule the Universe (Clojure)")
+      .setFieldValue(RULE + ".1.count", "42")
+      .setFieldValue(RULE + ".2.label", "Rule the World (Java)")
+      .setFieldValue(RULE + ".2.count", "5");
+  }
 }
index 80ac17846f92d1852f4b721b330d29d1310dbbfc..840179fa11d6de742704ad2b79a9ab2fe1cd66fc 100644 (file)
@@ -23,6 +23,7 @@ package org.sonar.server.issue.notification;
 import org.junit.Test;
 import org.sonar.api.utils.Durations;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.user.index.UserIndex;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -31,7 +32,7 @@ import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate
 
 public class MyNewIssuesNotificationTest {
 
-  MyNewIssuesNotification sut = new MyNewIssuesNotification(mock(UserIndex.class), mock(DbClient.class), mock(Durations.class));
+  MyNewIssuesNotification sut = new MyNewIssuesNotification(mock(UserIndex.class), mock(RuleIndex.class), mock(DbClient.class), mock(Durations.class));
 
   @Test
   public void set_assignee() throws Exception {
index 4de5ac346a305e5e325d13bb428713fc0fa2bbea..723f9f5ddbb44cff7d7c68b9cc196710f08fb7a4 100644 (file)
@@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.sonar.server.issue.notification.NewIssuesStatistics.METRIC.*;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.*;
 
 public class NewIssuesEmailTemplateTest {
 
@@ -97,6 +97,7 @@ public class NewIssuesEmailTemplateTest {
   public void format_email_with_all_fields_filled() throws Exception {
     Notification notification = newNotification();
     addAssignees(notification);
+    addRules(notification);
     addTags(notification);
     addComponents(notification);
 
@@ -154,10 +155,10 @@ public class NewIssuesEmailTemplateTest {
 
   private void addTags(Notification notification) {
     notification
-      .setFieldValue(TAGS + ".1.label", "oscar")
-      .setFieldValue(TAGS + ".1.count", "3")
-      .setFieldValue(TAGS + ".2.label", "cesar")
-      .setFieldValue(TAGS + ".2.count", "10");
+      .setFieldValue(TAG + ".1.label", "oscar")
+      .setFieldValue(TAG + ".1.count", "3")
+      .setFieldValue(TAG + ".2.label", "cesar")
+      .setFieldValue(TAG + ".2.count", "10");
   }
 
   private void addComponents(Notification notification) {
@@ -167,4 +168,12 @@ public class NewIssuesEmailTemplateTest {
       .setFieldValue(COMPONENT + ".2.label", "/path/to/directory")
       .setFieldValue(COMPONENT + ".2.count", "7");
   }
+
+  private void addRules(Notification notification) {
+    notification
+      .setFieldValue(RULE + ".1.label", "Rule the Universe (Clojure)")
+      .setFieldValue(RULE + ".1.count", "42")
+      .setFieldValue(RULE + ".2.label", "Rule the World (Java)")
+      .setFieldValue(RULE + ".2.count", "5");
+  }
 }
index 67338fbd425dc649c0c96da1de8fe987e718a580..ef9bf673dfa22cd2edc680c90d35f7f3925861e1 100644 (file)
 
 package org.sonar.server.issue.notification;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.Duration;
@@ -32,6 +34,10 @@ import org.sonar.core.component.ComponentDto;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.server.component.ComponentTesting;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.index.RuleDoc;
+import org.sonar.server.rule.index.RuleIndex;
+import org.sonar.server.rule.index.RuleNormalizer;
 import org.sonar.server.user.index.UserIndex;
 
 import java.util.Date;
@@ -42,15 +48,21 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.sonar.server.issue.notification.NewIssuesStatistics.METRIC.*;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.ASSIGNEE;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.COMPONENT;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.DEBT;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.RULE;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.SEVERITY;
+import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.TAG;
 
 public class NewIssuesNotificationTest {
 
   NewIssuesStatistics.Stats stats = new NewIssuesStatistics.Stats();
   UserIndex userIndex = mock(UserIndex.class);
+  RuleIndex ruleIndex = mock(RuleIndex.class);
   DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS);
   Durations durations = mock(Durations.class);
-  NewIssuesNotification sut = new NewIssuesNotification(userIndex, dbClient, durations);
+  NewIssuesNotification sut = new NewIssuesNotification(userIndex, ruleIndex, dbClient, durations);
 
   @Test
   public void set_project() throws Exception {
@@ -83,6 +95,8 @@ public class NewIssuesNotificationTest {
     addIssueNTimes(newIssue2(), 3);
     when(dbClient.componentDao().getByUuid(any(DbSession.class), eq("file-uuid")).name()).thenReturn("file-name");
     when(dbClient.componentDao().getByUuid(any(DbSession.class), eq("directory-uuid")).name()).thenReturn("directory-name");
+    when(ruleIndex.getByKey(RuleKey.of("SonarQube", "rule-the-world"))).thenReturn(newRule("Rule the World", "Java"));
+    when(ruleIndex.getByKey(RuleKey.of("SonarQube", "rule-the-universe"))).thenReturn(newRule("Rule the Universe", "Clojure"));
 
     sut.setStatistics(component, stats);
 
@@ -92,14 +106,18 @@ public class NewIssuesNotificationTest {
     assertThat(sut.getFieldValue(ASSIGNEE + ".1.count")).isEqualTo("5");
     assertThat(sut.getFieldValue(ASSIGNEE + ".2.label")).isEqualTo("keenan");
     assertThat(sut.getFieldValue(ASSIGNEE + ".2.count")).isEqualTo("3");
-    assertThat(sut.getFieldValue(TAGS + ".1.label")).isEqualTo("owasp");
-    assertThat(sut.getFieldValue(TAGS + ".1.count")).isEqualTo("8");
-    assertThat(sut.getFieldValue(TAGS + ".2.label")).isEqualTo("bug");
-    assertThat(sut.getFieldValue(TAGS + ".2.count")).isEqualTo("5");
+    assertThat(sut.getFieldValue(TAG + ".1.label")).isEqualTo("owasp");
+    assertThat(sut.getFieldValue(TAG + ".1.count")).isEqualTo("8");
+    assertThat(sut.getFieldValue(TAG + ".2.label")).isEqualTo("bug");
+    assertThat(sut.getFieldValue(TAG + ".2.count")).isEqualTo("5");
     assertThat(sut.getFieldValue(COMPONENT + ".1.label")).isEqualTo("file-name");
     assertThat(sut.getFieldValue(COMPONENT + ".1.count")).isEqualTo("5");
     assertThat(sut.getFieldValue(COMPONENT + ".2.label")).isEqualTo("directory-name");
     assertThat(sut.getFieldValue(COMPONENT + ".2.count")).isEqualTo("3");
+    assertThat(sut.getFieldValue(RULE + ".1.label")).isEqualTo("Rule the World (Java)");
+    assertThat(sut.getFieldValue(RULE + ".1.count")).isEqualTo("5");
+    assertThat(sut.getFieldValue(RULE + ".2.label")).isEqualTo("Rule the Universe (Clojure)");
+    assertThat(sut.getFieldValue(RULE + ".2.count")).isEqualTo("3");
     assertThat(sut.getDefaultMessage()).startsWith("8 new issues on project-long-name");
   }
 
@@ -124,6 +142,7 @@ public class NewIssuesNotificationTest {
       .setComponentUuid("file-uuid")
       .setSeverity(Severity.INFO)
       .setTags(Lists.newArrayList("bug", "owasp"))
+      .setRuleKey(RuleKey.of("SonarQube", "rule-the-world"))
       .setDebt(Duration.create(5L));
   }
 
@@ -133,6 +152,14 @@ public class NewIssuesNotificationTest {
       .setComponentUuid("directory-uuid")
       .setSeverity(Severity.BLOCKER)
       .setTags(Lists.newArrayList("owasp"))
+      .setRuleKey(RuleKey.of("SonarQube", "rule-the-universe"))
       .setDebt(Duration.create(10L));
   }
+
+  private Rule newRule(String name, String language) {
+    return new RuleDoc(ImmutableMap.<String, Object>of(
+      RuleNormalizer.RuleField.NAME.field(), name,
+      RuleNormalizer.RuleField.LANGUAGE.field(), language
+      ));
+  }
 }
index a23f6570126592c369df0301904a4e439abc4791..947e1741492bfb5c3505807367b288e17f91dd60 100644 (file)
@@ -23,9 +23,10 @@ package org.sonar.server.issue.notification;
 import com.google.common.collect.Lists;
 import org.junit.Test;
 import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.Duration;
-import org.sonar.server.issue.notification.NewIssuesStatistics.METRIC;
+import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -41,16 +42,18 @@ public class NewIssuesStatisticsTest {
     sut.add(issue.setAssignee("james"));
     sut.add(issue.setAssignee("keenan"));
 
-    assertThat(countDistribution(METRIC.ASSIGNEE, "maynard")).isEqualTo(1);
-    assertThat(countDistribution(METRIC.ASSIGNEE, "james")).isEqualTo(1);
-    assertThat(countDistribution(METRIC.ASSIGNEE, "keenan")).isEqualTo(1);
-    assertThat(countDistribution(METRIC.ASSIGNEE, "wrong.login")).isEqualTo(0);
-    assertThat(countDistribution(METRIC.COMPONENT, "file-uuid")).isEqualTo(3);
-    assertThat(countDistribution(METRIC.COMPONENT, "wrong-uuid")).isEqualTo(0);
-    assertThat(countDistribution(METRIC.SEVERITY, Severity.INFO)).isEqualTo(3);
-    assertThat(countDistribution(METRIC.SEVERITY, Severity.CRITICAL)).isEqualTo(0);
-    assertThat(countDistribution(METRIC.TAGS, "owasp")).isEqualTo(3);
-    assertThat(countDistribution(METRIC.TAGS, "wrong-tag")).isEqualTo(0);
+    assertThat(countDistribution(Metric.ASSIGNEE, "maynard")).isEqualTo(1);
+    assertThat(countDistribution(Metric.ASSIGNEE, "james")).isEqualTo(1);
+    assertThat(countDistribution(Metric.ASSIGNEE, "keenan")).isEqualTo(1);
+    assertThat(countDistribution(Metric.ASSIGNEE, "wrong.login")).isEqualTo(0);
+    assertThat(countDistribution(Metric.COMPONENT, "file-uuid")).isEqualTo(3);
+    assertThat(countDistribution(Metric.COMPONENT, "wrong-uuid")).isEqualTo(0);
+    assertThat(countDistribution(Metric.SEVERITY, Severity.INFO)).isEqualTo(3);
+    assertThat(countDistribution(Metric.SEVERITY, Severity.CRITICAL)).isEqualTo(0);
+    assertThat(countDistribution(Metric.TAG, "owasp")).isEqualTo(3);
+    assertThat(countDistribution(Metric.TAG, "wrong-tag")).isEqualTo(0);
+    assertThat(countDistribution(Metric.RULE, "SonarQube:rule-the-world")).isEqualTo(3);
+    assertThat(countDistribution(Metric.RULE, "SonarQube:has-a-fake-rule")).isEqualTo(0);
     assertThat(sut.globalStatistics().debt().toMinutes()).isEqualTo(15L);
     assertThat(sut.globalStatistics().hasIssues()).isTrue();
     assertThat(sut.hasIssues()).isTrue();
@@ -62,7 +65,7 @@ public class NewIssuesStatisticsTest {
     assertThat(sut.globalStatistics().hasIssues()).isFalse();
   }
 
-  private int countDistribution(METRIC metric, String label) {
+  private int countDistribution(Metric metric, String label) {
     return sut.globalStatistics().countForMetric(metric, label);
   }
 
@@ -72,6 +75,7 @@ public class NewIssuesStatisticsTest {
       .setComponentUuid("file-uuid")
       .setNew(true)
       .setSeverity(Severity.INFO)
+      .setRuleKey(RuleKey.of("SonarQube", "rule-the-world"))
       .setTags(Lists.newArrayList("bug", "owasp"))
       .setDebt(Duration.create(5L));
   }
index 39fa601f01dd356d81c8948e3249c2bb760f473a..c2e53d7a50f96b2dc3d40965aae00d335c7d33c5 100644 (file)
@@ -5,6 +5,10 @@ Project: Struts
     Severity
         Blocker: 0    Critical: 5    Major: 10    Minor: 3    Info: 1
 
+    Rules
+        Rule the Universe (Clojure): 42
+        Rule the World (Java): 5
+
     Tags
         oscar: 3
         cesar: 10
index bddf6f7309aeeb7a617ef3e334a57576fe793768..64d4aeb2f3f34e85b229e21d8a5c338f8713a3dc 100644 (file)
@@ -9,6 +9,10 @@ Project: Struts
         robin.williams: 5
         al.pacino: 7
 
+    Rules
+        Rule the Universe (Clojure): 42
+        Rule the World (Java): 5
+
     Tags
         oscar: 3
         cesar: 10