]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4366 Move a core plugin component to batch (remove server dependency on batch...
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 20 Mar 2014 11:34:40 +0000 (12:34 +0100)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 20 Mar 2014 11:35:42 +0000 (12:35 +0100)
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java [deleted file]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/qualitygate/GenerateQualityGateEvents.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/qualitygate/GenerateQualityGateEventsTest.java [new file with mode: 0644]

index 3b4d49268d3ba058ed8e194d5554f36f4359d0fc..463c7cc3243459ca159ba9c023445d2f9d4c925a 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.plugins.core;
 
+import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
+
 import com.google.common.collect.ImmutableList;
 import org.sonar.api.*;
 import org.sonar.api.checks.NoSonarFilter;
@@ -300,7 +302,6 @@ public final class CorePlugin extends SonarPlugin {
       ProjectLinksSensor.class,
       UnitTestDecorator.class,
       VersionEventsSensor.class,
-      GenerateAlertEvents.class,
       LineCoverageDecorator.class,
       CoverageDecorator.class,
       BranchCoverageDecorator.class,
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java
deleted file mode 100644 (file)
index 4d6a855..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.plugins.core.sensors;
-
-import org.sonar.api.batch.*;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.Metric.Level;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationManager;
-import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.ResourceUtils;
-import org.sonar.batch.qualitygate.QualityGate;
-
-import java.util.List;
-
-public class GenerateAlertEvents implements Decorator {
-
-  private final QualityGate qualityGate;
-  private final TimeMachine timeMachine;
-  private NotificationManager notificationManager;
-
-  public GenerateAlertEvents(QualityGate qualityGate, TimeMachine timeMachine, NotificationManager notificationManager) {
-    this.qualityGate = qualityGate;
-    this.timeMachine = timeMachine;
-    this.notificationManager = notificationManager;
-  }
-
-  public boolean shouldExecuteOnProject(Project project) {
-    return qualityGate.isEnabled();
-  }
-
-  @DependsUpon
-  public Metric dependsUponAlertStatus() {
-    return CoreMetrics.ALERT_STATUS;
-  }
-
-  public void decorate(Resource resource, DecoratorContext context) {
-    if (!shouldDecorateResource(resource)) {
-      return;
-    }
-    Measure currentStatus = context.getMeasure(CoreMetrics.ALERT_STATUS);
-    if (currentStatus == null) {
-      return;
-    }
-
-    TimeMachineQuery query = new TimeMachineQuery(resource).setOnlyLastAnalysis(true).setMetrics(CoreMetrics.ALERT_STATUS);
-    List<Measure> measures = timeMachine.getMeasures(query);
-
-    Measure pastStatus = measures != null && measures.size() == 1 ? measures.get(0) : null;
-    String alertText = currentStatus.getAlertText();
-    Level alertLevel = currentStatus.getDataAsLevel();
-    String alertName = null;
-    boolean isNewAlert = true;
-    if (pastStatus != null && pastStatus.getDataAsLevel() != alertLevel) {
-      // The alert status has changed
-      alertName = getName(pastStatus, currentStatus);
-      if (pastStatus.getDataAsLevel() != Metric.Level.OK) {
-        // There was already a Orange/Red alert, so this is no new alert: it has just changed
-        isNewAlert = false;
-      }
-      createEvent(context, alertName, alertText);
-      notifyUsers(resource, alertName, alertText, alertLevel, isNewAlert);
-
-    } else if (pastStatus == null && alertLevel != Metric.Level.OK) {
-      // There were no defined alerts before, so this one is a new one
-      alertName = getName(currentStatus);
-      createEvent(context, alertName, alertText);
-      notifyUsers(resource, alertName, alertText, alertLevel, isNewAlert);
-    }
-
-  }
-
-  protected void notifyUsers(Resource resource, String alertName, String alertText, Level alertLevel, boolean isNewAlert) {
-    Notification notification = new Notification("alerts")
-        .setDefaultMessage("Alert on " + resource.getLongName() + ": " + alertName)
-        .setFieldValue("projectName", resource.getLongName())
-        .setFieldValue("projectKey", resource.getKey())
-        .setFieldValue("projectId", String.valueOf(resource.getId()))
-        .setFieldValue("alertName", alertName)
-        .setFieldValue("alertText", alertText)
-        .setFieldValue("alertLevel", alertLevel.toString())
-        .setFieldValue("isNewAlert", Boolean.toString(isNewAlert));
-    notificationManager.scheduleForSending(notification);
-  }
-
-  private boolean shouldDecorateResource(Resource resource) {
-    return ResourceUtils.isRootProject(resource);
-  }
-
-  private String getName(Measure pastStatus, Measure currentStatus) {
-    return getName(currentStatus) + " (was " + getName(pastStatus) + ")";
-
-  }
-
-  private String getName(Measure currentStatus) {
-    return currentStatus.getDataAsLevel().getColorName();
-  }
-
-  private void createEvent(DecoratorContext context, String name, String description) {
-    context.createEvent(name, description, Event.CATEGORY_ALERT, null);
-  }
-}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.java
deleted file mode 100644 (file)
index 8d6b8a1..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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.plugins.core.sensors;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.batch.Event;
-import org.sonar.api.batch.TimeMachine;
-import org.sonar.api.batch.TimeMachineQuery;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationManager;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.test.ProjectTestBuilder;
-import org.sonar.batch.qualitygate.QualityGate;
-
-import java.util.Arrays;
-import java.util.Date;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.*;
-import static org.mockito.Mockito.*;
-
-public class GenerateAlertEventsTest {
-  private GenerateAlertEvents decorator;
-  private DecoratorContext context;
-  private QualityGate qualityGate;
-  private TimeMachine timeMachine;
-  private NotificationManager notificationManager;
-  private Project project;
-
-  @Before
-  public void setup() {
-    context = mock(DecoratorContext.class);
-    timeMachine = mock(TimeMachine.class);
-    qualityGate = mock(QualityGate.class);
-    notificationManager = mock(NotificationManager.class);
-    decorator = new GenerateAlertEvents(qualityGate, timeMachine, notificationManager);
-    project = new ProjectTestBuilder().build();
-  }
-
-  @Test
-  public void shouldDependUponAlertStatus() {
-    assertThat(decorator.dependsUponAlertStatus()).isEqualTo(CoreMetrics.ALERT_STATUS);
-  }
-
-  @Test
-  public void shouldNotDecorateIfNoThresholds() {
-    assertThat(decorator.shouldExecuteOnProject(project)).isFalse();
-  }
-
-  @Test
-  public void shouldDecorateIfQualityGateEnabled() {
-    when(qualityGate.isEnabled()).thenReturn(true);
-    assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
-  }
-
-  @Test
-  public void shouldNotDecorateIfNotRootProject() {
-    decorator.decorate(new File("Foo"), context);
-    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
-  }
-
-  @Test
-  public void shouldCreateEventWhenNewErrorAlert() {
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
-
-    decorator.decorate(project, context);
-
-    verify(context).createEvent(Metric.Level.ERROR.getColorName(), "desc", Event.CATEGORY_ALERT, null);
-    verifyNotificationSent("Red", "desc", "ERROR", "true");
-  }
-
-  @Test
-  public void shouldCreateEventWhenNewWarnAlert() {
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.WARN, "desc"));
-
-    decorator.decorate(project, context);
-
-    verify(context).createEvent(Metric.Level.WARN.getColorName(), "desc", Event.CATEGORY_ALERT, null);
-    verifyNotificationSent("Orange", "desc", "WARN", "true");
-  }
-
-  @Test
-  public void shouldCreateEventWhenWarnToError() {
-    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.WARN, "desc")));
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
-
-    decorator.decorate(project, context);
-
-    verify(context).createEvent("Red (was Orange)", "desc", Event.CATEGORY_ALERT, null);
-    verifyNotificationSent("Red (was Orange)", "desc", "ERROR", "false");
-  }
-
-  @Test
-  public void shouldCreateEventWhenErrorToOk() {
-    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.OK, null));
-
-    decorator.decorate(project, context);
-
-    verify(context).createEvent("Green (was Red)", null, Event.CATEGORY_ALERT, null);
-    verifyNotificationSent("Green (was Red)", null, "OK", "false");
-  }
-
-  @Test
-  public void shouldCreateEventWhenOkToError() {
-    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.OK, null)));
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
-
-    decorator.decorate(project, context);
-
-    verify(context).createEvent("Red (was Green)", "desc", Event.CATEGORY_ALERT, null);
-    verifyNotificationSent("Red (was Green)", "desc", "ERROR", "true");
-  }
-
-  @Test
-  public void shouldCreateEventWhenErrorToWarn() {
-    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.WARN, "desc"));
-
-    decorator.decorate(project, context);
-
-    verify(context).createEvent("Orange (was Red)", "desc", Event.CATEGORY_ALERT, null);
-    verifyNotificationSent("Orange (was Red)", "desc", "WARN", "false");
-  }
-
-  @Test
-  public void shouldNotCreateEventWhenNoAlertStatus() {
-    decorator.decorate(project, context);
-
-    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
-    verify(notificationManager, never()).scheduleForSending(any(Notification.class));
-  }
-
-  @Test
-  public void shouldNotCreateEventWhenSameLevel() {
-    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
-
-    decorator.decorate(project, context);
-
-    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
-    verify(notificationManager, never()).scheduleForSending(any(Notification.class));
-  }
-
-  @Test
-  public void shouldNotCreateEventIfNoMoreAlertStatus() {
-    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
-    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(null);
-
-    decorator.decorate(project, context);
-
-    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
-    verify(notificationManager, never()).scheduleForSending(any(Notification.class));
-  }
-
-  private Measure newAlertStatus(Metric.Level level, String label) {
-    Measure measure = new Measure(CoreMetrics.ALERT_STATUS, level);
-    measure.setAlertStatus(level);
-    measure.setAlertText(label);
-    return measure;
-  }
-
-  private void verifyNotificationSent(String alertName, String alertText, String alertLevel, String isNewAlert) {
-    Notification notification = new Notification("alerts")
-      .setDefaultMessage("Alert on " + project.getLongName() + ": " + alertName)
-      .setFieldValue("projectName", project.getLongName())
-      .setFieldValue("projectKey", project.getKey())
-      .setFieldValue("projectId", String.valueOf(project.getId()))
-      .setFieldValue("alertName", alertName)
-      .setFieldValue("alertText", alertText)
-      .setFieldValue("alertLevel", alertLevel)
-      .setFieldValue("isNewAlert", isNewAlert);
-    verify(notificationManager, times(1)).scheduleForSending(eq(notification));
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/GenerateQualityGateEvents.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/GenerateQualityGateEvents.java
new file mode 100644 (file)
index 0000000..2d99ed9
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.batch.qualitygate;
+
+import org.sonar.api.batch.*;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Metric.Level;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationManager;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+import java.util.List;
+
+public class GenerateQualityGateEvents implements Decorator {
+
+  private final QualityGate qualityGate;
+  private final TimeMachine timeMachine;
+  private NotificationManager notificationManager;
+
+  public GenerateQualityGateEvents(QualityGate qualityGate, TimeMachine timeMachine, NotificationManager notificationManager) {
+    this.qualityGate = qualityGate;
+    this.timeMachine = timeMachine;
+    this.notificationManager = notificationManager;
+  }
+
+  public boolean shouldExecuteOnProject(Project project) {
+    return qualityGate.isEnabled();
+  }
+
+  @DependsUpon
+  public Metric dependsUponAlertStatus() {
+    return CoreMetrics.ALERT_STATUS;
+  }
+
+  public void decorate(Resource resource, DecoratorContext context) {
+    if (!shouldDecorateResource(resource)) {
+      return;
+    }
+    Measure currentStatus = context.getMeasure(CoreMetrics.ALERT_STATUS);
+    if (currentStatus == null) {
+      return;
+    }
+
+    TimeMachineQuery query = new TimeMachineQuery(resource).setOnlyLastAnalysis(true).setMetrics(CoreMetrics.ALERT_STATUS);
+    List<Measure> measures = timeMachine.getMeasures(query);
+
+    Measure pastStatus = measures != null && measures.size() == 1 ? measures.get(0) : null;
+    String alertText = currentStatus.getAlertText();
+    Level alertLevel = currentStatus.getDataAsLevel();
+    String alertName = null;
+    boolean isNewAlert = true;
+    if (pastStatus != null && pastStatus.getDataAsLevel() != alertLevel) {
+      // The alert status has changed
+      alertName = getName(pastStatus, currentStatus);
+      if (pastStatus.getDataAsLevel() != Metric.Level.OK) {
+        // There was already a Orange/Red alert, so this is no new alert: it has just changed
+        isNewAlert = false;
+      }
+      createEvent(context, alertName, alertText);
+      notifyUsers(resource, alertName, alertText, alertLevel, isNewAlert);
+
+    } else if (pastStatus == null && alertLevel != Metric.Level.OK) {
+      // There were no defined alerts before, so this one is a new one
+      alertName = getName(currentStatus);
+      createEvent(context, alertName, alertText);
+      notifyUsers(resource, alertName, alertText, alertLevel, isNewAlert);
+    }
+
+  }
+
+  protected void notifyUsers(Resource resource, String alertName, String alertText, Level alertLevel, boolean isNewAlert) {
+    Notification notification = new Notification("alerts")
+        .setDefaultMessage("Alert on " + resource.getLongName() + ": " + alertName)
+        .setFieldValue("projectName", resource.getLongName())
+        .setFieldValue("projectKey", resource.getKey())
+        .setFieldValue("projectId", String.valueOf(resource.getId()))
+        .setFieldValue("alertName", alertName)
+        .setFieldValue("alertText", alertText)
+        .setFieldValue("alertLevel", alertLevel.toString())
+        .setFieldValue("isNewAlert", Boolean.toString(isNewAlert));
+    notificationManager.scheduleForSending(notification);
+  }
+
+  private boolean shouldDecorateResource(Resource resource) {
+    return ResourceUtils.isRootProject(resource);
+  }
+
+  private String getName(Measure pastStatus, Measure currentStatus) {
+    return getName(currentStatus) + " (was " + getName(pastStatus) + ")";
+
+  }
+
+  private String getName(Measure currentStatus) {
+    return currentStatus.getDataAsLevel().getColorName();
+  }
+
+  private void createEvent(DecoratorContext context, String name, String description) {
+    context.createEvent(name, description, Event.CATEGORY_ALERT, null);
+  }
+}
index 73c2f6803bddee97510182d4438dd22ca90a99c4..835aa9be91f1a7b1bf952449997643c36d400edb 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.batch.scan;
 
+import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchExtension;
@@ -125,6 +127,7 @@ public class ModuleScanContainer extends ComponentContainer {
       // quality gates
       new QualityGateProvider(),
       QualityGateVerifier.class,
+      GenerateQualityGateEvents.class,
 
       // rules
       ModuleQProfiles.class,
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/GenerateQualityGateEventsTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/GenerateQualityGateEventsTest.java
new file mode 100644 (file)
index 0000000..edf86e9
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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.batch.qualitygate;
+
+import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.Event;
+import org.sonar.api.batch.TimeMachine;
+import org.sonar.api.batch.TimeMachineQuery;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationManager;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.test.ProjectTestBuilder;
+import org.sonar.batch.qualitygate.QualityGate;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+public class GenerateQualityGateEventsTest {
+  private GenerateQualityGateEvents decorator;
+  private DecoratorContext context;
+  private QualityGate qualityGate;
+  private TimeMachine timeMachine;
+  private NotificationManager notificationManager;
+  private Project project;
+
+  @Before
+  public void setup() {
+    context = mock(DecoratorContext.class);
+    timeMachine = mock(TimeMachine.class);
+    qualityGate = mock(QualityGate.class);
+    notificationManager = mock(NotificationManager.class);
+    decorator = new GenerateQualityGateEvents(qualityGate, timeMachine, notificationManager);
+    project = new ProjectTestBuilder().build();
+  }
+
+  @Test
+  public void shouldDependUponAlertStatus() {
+    assertThat(decorator.dependsUponAlertStatus()).isEqualTo(CoreMetrics.ALERT_STATUS);
+  }
+
+  @Test
+  public void shouldNotDecorateIfNoThresholds() {
+    assertThat(decorator.shouldExecuteOnProject(project)).isFalse();
+  }
+
+  @Test
+  public void shouldDecorateIfQualityGateEnabled() {
+    when(qualityGate.isEnabled()).thenReturn(true);
+    assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
+  }
+
+  @Test
+  public void shouldNotDecorateIfNotRootProject() {
+    decorator.decorate(new File("Foo"), context);
+    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+  }
+
+  @Test
+  public void shouldCreateEventWhenNewErrorAlert() {
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+
+    decorator.decorate(project, context);
+
+    verify(context).createEvent(Metric.Level.ERROR.getColorName(), "desc", Event.CATEGORY_ALERT, null);
+    verifyNotificationSent("Red", "desc", "ERROR", "true");
+  }
+
+  @Test
+  public void shouldCreateEventWhenNewWarnAlert() {
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.WARN, "desc"));
+
+    decorator.decorate(project, context);
+
+    verify(context).createEvent(Metric.Level.WARN.getColorName(), "desc", Event.CATEGORY_ALERT, null);
+    verifyNotificationSent("Orange", "desc", "WARN", "true");
+  }
+
+  @Test
+  public void shouldCreateEventWhenWarnToError() {
+    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.WARN, "desc")));
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+
+    decorator.decorate(project, context);
+
+    verify(context).createEvent("Red (was Orange)", "desc", Event.CATEGORY_ALERT, null);
+    verifyNotificationSent("Red (was Orange)", "desc", "ERROR", "false");
+  }
+
+  @Test
+  public void shouldCreateEventWhenErrorToOk() {
+    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.OK, null));
+
+    decorator.decorate(project, context);
+
+    verify(context).createEvent("Green (was Red)", null, Event.CATEGORY_ALERT, null);
+    verifyNotificationSent("Green (was Red)", null, "OK", "false");
+  }
+
+  @Test
+  public void shouldCreateEventWhenOkToError() {
+    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.OK, null)));
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+
+    decorator.decorate(project, context);
+
+    verify(context).createEvent("Red (was Green)", "desc", Event.CATEGORY_ALERT, null);
+    verifyNotificationSent("Red (was Green)", "desc", "ERROR", "true");
+  }
+
+  @Test
+  public void shouldCreateEventWhenErrorToWarn() {
+    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.WARN, "desc"));
+
+    decorator.decorate(project, context);
+
+    verify(context).createEvent("Orange (was Red)", "desc", Event.CATEGORY_ALERT, null);
+    verifyNotificationSent("Orange (was Red)", "desc", "WARN", "false");
+  }
+
+  @Test
+  public void shouldNotCreateEventWhenNoAlertStatus() {
+    decorator.decorate(project, context);
+
+    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+    verify(notificationManager, never()).scheduleForSending(any(Notification.class));
+  }
+
+  @Test
+  public void shouldNotCreateEventWhenSameLevel() {
+    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+
+    decorator.decorate(project, context);
+
+    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+    verify(notificationManager, never()).scheduleForSending(any(Notification.class));
+  }
+
+  @Test
+  public void shouldNotCreateEventIfNoMoreAlertStatus() {
+    when(timeMachine.getMeasures(any(TimeMachineQuery.class))).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+    when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(null);
+
+    decorator.decorate(project, context);
+
+    verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+    verify(notificationManager, never()).scheduleForSending(any(Notification.class));
+  }
+
+  private Measure newAlertStatus(Metric.Level level, String label) {
+    Measure measure = new Measure(CoreMetrics.ALERT_STATUS, level);
+    measure.setAlertStatus(level);
+    measure.setAlertText(label);
+    return measure;
+  }
+
+  private void verifyNotificationSent(String alertName, String alertText, String alertLevel, String isNewAlert) {
+    Notification notification = new Notification("alerts")
+      .setDefaultMessage("Alert on " + project.getLongName() + ": " + alertName)
+      .setFieldValue("projectName", project.getLongName())
+      .setFieldValue("projectKey", project.getKey())
+      .setFieldValue("projectId", String.valueOf(project.getId()))
+      .setFieldValue("alertName", alertName)
+      .setFieldValue("alertText", alertText)
+      .setFieldValue("alertLevel", alertLevel)
+      .setFieldValue("isNewAlert", isNewAlert);
+    verify(notificationManager, times(1)).scheduleForSending(eq(notification));
+  }
+}