]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9442 Add details of added/updated/disabled rules in notification
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 21 Jun 2017 09:18:26 +0000 (11:18 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 29 Jun 2017 15:23:19 +0000 (17:23 +0200)
27 files changed:
it/it-plugins/foo-plugin-v1/pom.xml [deleted file]
it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/Foo.java [deleted file]
it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/FooPlugin.java [deleted file]
it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/package-info.java [deleted file]
it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooBasicProfile.java [deleted file]
it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java [deleted file]
it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/package-info.java [deleted file]
it/it-plugins/foo-plugin-v2/pom.xml [deleted file]
it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/Foo.java [deleted file]
it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/FooPlugin.java [deleted file]
it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/package-info.java [deleted file]
it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooBasicProfile.java [deleted file]
it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java [deleted file]
it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/package-info.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotification.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotificationSender.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotificationTemplate.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RegisterQualityProfiles.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotificationSenderTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotificationTemplateTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotificationTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java
tests/plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooBasicProfile.java
tests/plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java
tests/plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooBasicProfile.java
tests/plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java
tests/src/test/java/org/sonarqube/tests/qualityProfile/BuiltInQualityProfilesNotificationTest.java

diff --git a/it/it-plugins/foo-plugin-v1/pom.xml b/it/it-plugins/foo-plugin-v1/pom.xml
deleted file mode 100644 (file)
index e34322b..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.sonarsource.sonarqube</groupId>
-    <artifactId>sonarqube</artifactId>
-    <version>6.5-SNAPSHOT</version>
-    <relativePath>../..</relativePath>
-  </parent>
-  <artifactId>foo-plugin-v1</artifactId>
-  <name>SonarQube :: Plugins :: Foo (V1)</name>
-  <packaging>sonar-plugin</packaging>
-  <description>Sample of plugin to document and test available APIs</description>
-
-  <properties>
-    <sonar.skip>true</sonar.skip>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.google.code.findbugs</groupId>
-      <artifactId>jsr305</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.sonarsource.sonarqube</groupId>
-      <artifactId>sonar-plugin-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-
-    <!-- unit testing -->
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
-        <artifactId>sonar-packaging-maven-plugin</artifactId>
-        <configuration>
-          <pluginKey>foo</pluginKey>
-          <pluginName>Foo</pluginName>
-          <pluginClass>org.sonar.foo.FooPlugin</pluginClass>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/Foo.java b/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/Foo.java
deleted file mode 100644 (file)
index a2b2d23..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo;
-
-import org.sonar.api.resources.Language;
-
-public class Foo implements Language {
-
-  public static final String KEY = "foo";
-  public static final String NAME = "Foo";
-
-  @Override
-  public String getKey() {
-    return KEY;
-  }
-
-  @Override
-  public String getName() {
-    return NAME;
-  }
-
-  @Override
-  public String[] getFileSuffixes() {
-    return new String[0];
-  }
-}
diff --git a/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/FooPlugin.java b/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/FooPlugin.java
deleted file mode 100644 (file)
index 17677e0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo;
-
-import org.sonar.api.Plugin;
-import org.sonar.foo.rule.FooBasicProfile;
-import org.sonar.foo.rule.FooRulesDefinition;
-
-/**
- * Plugin entry-point, as declared in pom.xml.
- */
-public class FooPlugin implements Plugin {
-
-  @Override
-  public void define(Context context) {
-    context.addExtensions(
-      Foo.class,
-      FooRulesDefinition.class,
-      FooBasicProfile.class);
-  }
-
-}
diff --git a/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/package-info.java b/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/package-info.java
deleted file mode 100644 (file)
index 24613bb..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.foo;
-
-import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooBasicProfile.java b/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooBasicProfile.java
deleted file mode 100644 (file)
index 3a53e92..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo.rule;
-
-import org.sonar.api.profiles.ProfileDefinition;
-import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.utils.ValidationMessages;
-
-import static org.sonar.api.rules.RulePriority.MAJOR;
-import static org.sonar.foo.Foo.KEY;
-import static org.sonar.foo.rule.FooRulesDefinition.FOO_REPOSITORY;
-
-public class FooBasicProfile extends ProfileDefinition {
-
-  @Override
-  public RulesProfile createProfile(ValidationMessages validation) {
-    final RulesProfile profile = RulesProfile.create("Basic", KEY);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "UnchangedRule"), MAJOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "ChangedRule"), MAJOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "RemovedRule"), MAJOR);
-    return profile;
-  }
-}
diff --git a/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java b/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java
deleted file mode 100644 (file)
index 7257dd7..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo.rule;
-
-import org.sonar.api.server.rule.RulesDefinition;
-import org.sonar.foo.Foo;
-
-public class FooRulesDefinition implements RulesDefinition {
-
-  public static final String FOO_REPOSITORY = "foo";
-
-  @Override
-  public void define(Context context) {
-    defineRulesXoo(context);
-  }
-
-  private static void defineRulesXoo(Context context) {
-    NewRepository repo = context.createRepository(FOO_REPOSITORY, Foo.KEY).setName("Foo");
-    createRule(repo, "UnchangedRule");
-    createRule(repo, "ChangedRule");
-    createRule(repo, "RemovedRule");
-    repo.done();
-  }
-
-  private static NewRule createRule(NewRepository repo, String key) {
-    return repo.createRule(key).setName(key).setHtmlDescription(key);
-  }
-
-}
diff --git a/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/package-info.java b/it/it-plugins/foo-plugin-v1/src/main/java/org/sonar/foo/rule/package-info.java
deleted file mode 100644 (file)
index b3209c8..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.foo.rule;
-
-import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/it/it-plugins/foo-plugin-v2/pom.xml b/it/it-plugins/foo-plugin-v2/pom.xml
deleted file mode 100644 (file)
index db2fc2c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.sonarsource.sonarqube</groupId>
-    <artifactId>sonarqube</artifactId>
-    <version>6.5-SNAPSHOT</version>
-    <relativePath>../..</relativePath>
-  </parent>
-  <artifactId>foo-plugin-v2</artifactId>
-  <name>SonarQube :: Plugins :: Foo (V2)</name>
-  <packaging>sonar-plugin</packaging>
-  <description>Sample of plugin to document and test available APIs</description>
-
-  <properties>
-    <sonar.skip>true</sonar.skip>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.google.code.findbugs</groupId>
-      <artifactId>jsr305</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.sonarsource.sonarqube</groupId>
-      <artifactId>sonar-plugin-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-
-    <!-- unit testing -->
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
-        <artifactId>sonar-packaging-maven-plugin</artifactId>
-        <configuration>
-          <pluginKey>foo</pluginKey>
-          <pluginName>Foo</pluginName>
-          <pluginClass>org.sonar.foo.FooPlugin</pluginClass>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/Foo.java b/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/Foo.java
deleted file mode 100644 (file)
index a2b2d23..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo;
-
-import org.sonar.api.resources.Language;
-
-public class Foo implements Language {
-
-  public static final String KEY = "foo";
-  public static final String NAME = "Foo";
-
-  @Override
-  public String getKey() {
-    return KEY;
-  }
-
-  @Override
-  public String getName() {
-    return NAME;
-  }
-
-  @Override
-  public String[] getFileSuffixes() {
-    return new String[0];
-  }
-}
diff --git a/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/FooPlugin.java b/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/FooPlugin.java
deleted file mode 100644 (file)
index 17677e0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo;
-
-import org.sonar.api.Plugin;
-import org.sonar.foo.rule.FooBasicProfile;
-import org.sonar.foo.rule.FooRulesDefinition;
-
-/**
- * Plugin entry-point, as declared in pom.xml.
- */
-public class FooPlugin implements Plugin {
-
-  @Override
-  public void define(Context context) {
-    context.addExtensions(
-      Foo.class,
-      FooRulesDefinition.class,
-      FooBasicProfile.class);
-  }
-
-}
diff --git a/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/package-info.java b/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/package-info.java
deleted file mode 100644 (file)
index 24613bb..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.foo;
-
-import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooBasicProfile.java b/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooBasicProfile.java
deleted file mode 100644 (file)
index 1c2d051..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo.rule;
-
-import org.sonar.api.profiles.ProfileDefinition;
-import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.utils.ValidationMessages;
-
-import static org.sonar.api.rules.RulePriority.MAJOR;
-import static org.sonar.api.rules.RulePriority.MINOR;
-import static org.sonar.foo.Foo.KEY;
-import static org.sonar.foo.rule.FooRulesDefinition.FOO_REPOSITORY;
-
-public class FooBasicProfile extends ProfileDefinition {
-
-  @Override
-  public RulesProfile createProfile(ValidationMessages validation) {
-    final RulesProfile profile = RulesProfile.create("Basic", KEY);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "UnchangedRule"), MAJOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "ChangedRule"), MINOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "NewRule"), MAJOR);
-    return profile;
-  }
-}
diff --git a/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java b/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/FooRulesDefinition.java
deleted file mode 100644 (file)
index aacc233..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.foo.rule;
-
-import org.sonar.api.server.rule.RulesDefinition;
-import org.sonar.foo.Foo;
-
-public class FooRulesDefinition implements RulesDefinition {
-
-  public static final String FOO_REPOSITORY = "foo";
-
-  @Override
-  public void define(Context context) {
-    defineRulesXoo(context);
-  }
-
-  private static void defineRulesXoo(Context context) {
-    NewRepository repo = context.createRepository(FOO_REPOSITORY, Foo.KEY).setName("Foo");
-    createRule(repo, "UnchangedRule");
-    createRule(repo, "ChangedRule");
-    createRule(repo, "NewRule");
-    repo.done();
-  }
-
-  private static NewRule createRule(NewRepository repo, String key) {
-    return repo.createRule(key).setName(key).setHtmlDescription(key);
-  }
-
-}
diff --git a/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/package-info.java b/it/it-plugins/foo-plugin-v2/src/main/java/org/sonar/foo/rule/package-info.java
deleted file mode 100644 (file)
index b3209c8..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.foo.rule;
-
-import javax.annotation.ParametersAreNonnullByDefault;
index 9c2a906147ca9c82658822223056527902b5bdb7..6609ae3d829faff69825a51621e42e1442fea859 100644 (file)
@@ -27,6 +27,8 @@ import java.util.stream.IntStream;
 import org.sonar.api.notifications.Notification;
 
 import static com.google.common.base.Preconditions.checkState;
+import static java.lang.Integer.parseInt;
+import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
 import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationSender.BUILT_IN_QUALITY_PROFILES;
 
@@ -35,6 +37,10 @@ public class BuiltInQualityProfilesNotification {
   private static final String NUMBER_OF_PROFILES = "numberOfProfiles";
   private static final String PROFILE_NAME = ".profileName";
   private static final String LANGUAGE = ".language";
+  private static final String NEW_RULES = ".newRules";
+  private static final String UPDATED_RULES = ".updatedRules";
+  private static final String REMOVED_RULES = ".removedRules";
+
   private final List<Profile> profiles = new ArrayList<>();
 
   public BuiltInQualityProfilesNotification addProfile(Profile profile) {
@@ -48,8 +54,11 @@ public class BuiltInQualityProfilesNotification {
     AtomicInteger count = new AtomicInteger();
     profiles.forEach(profile -> {
       int index = count.getAndIncrement();
-      notification.setFieldValue(index + ".profileName", profile.getProfileName());
-      notification.setFieldValue(index + ".language", profile.getLanguage());
+      notification.setFieldValue(index + PROFILE_NAME, profile.getProfileName());
+      notification.setFieldValue(index + LANGUAGE, profile.getLanguage());
+      notification.setFieldValue(index + NEW_RULES, String.valueOf(profile.getNewRules()));
+      notification.setFieldValue(index + UPDATED_RULES, String.valueOf(profile.getUpdatedRules()));
+      notification.setFieldValue(index + REMOVED_RULES, String.valueOf(profile.getRemovedRules()));
     });
     return notification;
   }
@@ -62,13 +71,22 @@ public class BuiltInQualityProfilesNotification {
     checkState(numberOfProfilesText != null, "Could not read the built-in quality profile notification");
     Integer numberOfProfiles = Integer.valueOf(numberOfProfilesText);
     IntStream.rangeClosed(0, numberOfProfiles - 1)
-      .mapToObj(index -> new Profile(
-        requireNonNull(notification.getFieldValue(index + PROFILE_NAME)),
-        requireNonNull(notification.getFieldValue(index + LANGUAGE))))
+      .mapToObj(index -> Profile.newBuilder(
+        getNonNullFieldValue(notification, index + PROFILE_NAME),
+        getNonNullFieldValue(notification, index + LANGUAGE))
+        .setNewRules(parseInt(getNonNullFieldValue(notification, index + NEW_RULES)))
+        .setUpdatedRules(parseInt(getNonNullFieldValue(notification, index + UPDATED_RULES)))
+        .setRemovedRules(parseInt(getNonNullFieldValue(notification, index + REMOVED_RULES)))
+        .build())
       .forEach(notif::addProfile);
     return notif;
   }
 
+  private static String getNonNullFieldValue(Notification notification, String key) {
+    String value = notification.getFieldValue(key);
+    return requireNonNull(value, format("Notification field '%s' is null", key));
+  }
+
   public List<Profile> getProfiles() {
     return profiles;
   }
@@ -76,10 +94,16 @@ public class BuiltInQualityProfilesNotification {
   public static class Profile {
     private final String profileName;
     private final String language;
+    private final int newRules;
+    private final int updatedRules;
+    private final int removedRules;
 
-    public Profile(String profileName, String language) {
-      this.profileName = profileName;
-      this.language = language;
+    public Profile(Builder builder) {
+      this.profileName = builder.profileName;
+      this.language = builder.language;
+      this.newRules = builder.newRules;
+      this.updatedRules = builder.updatedRules;
+      this.removedRules = builder.removedRules;
     }
 
     public String getProfileName() {
@@ -89,5 +113,56 @@ public class BuiltInQualityProfilesNotification {
     public String getLanguage() {
       return language;
     }
+
+    public int getNewRules() {
+      return newRules;
+    }
+
+    public int getUpdatedRules() {
+      return updatedRules;
+    }
+
+    public int getRemovedRules() {
+      return removedRules;
+    }
+
+    public static Builder newBuilder(String profileName, String language) {
+      return new Builder(profileName, language);
+    }
+
+    public static class Builder {
+      private final String profileName;
+      private final String language;
+      private int newRules;
+      private int updatedRules;
+      private int removedRules;
+
+      private Builder(String profileName, String language) {
+        this.profileName = requireNonNull(profileName, "profileName should not be null");
+        this.language = requireNonNull(language, "language should not be null");
+      }
+
+      public Builder setNewRules(int newRules) {
+        checkState(newRules >= 0, "newRules should not be negative");
+        this.newRules = newRules;
+        return this;
+      }
+
+      public Builder setUpdatedRules(int updatedRules) {
+        checkState(updatedRules >= 0, "updatedRules should not be negative");
+        this.updatedRules = updatedRules;
+        return this;
+      }
+
+      public Builder setRemovedRules(int removedRules) {
+        checkState(removedRules >= 0, "removedRules should not be negative");
+        this.removedRules = removedRules;
+        return this;
+      }
+
+      public Profile build() {
+        return new Profile(this);
+      }
+    }
   }
 }
index 329ad9da3cc8e0bba2509ec3f47f021e7e5981b8..fee337a98320db8798a8c6d55fea992027b46a0d 100644 (file)
 
 package org.sonar.server.qualityprofile;
 
-import java.util.List;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
 import org.sonar.api.resources.Languages;
 import org.sonar.server.notification.NotificationManager;
 import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
 
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED;
+
 public class BuiltInQualityProfilesNotificationSender {
 
   static final String BUILT_IN_QUALITY_PROFILES = "built-in-quality-profiles";
@@ -37,13 +42,21 @@ public class BuiltInQualityProfilesNotificationSender {
     this.languages = languages;
   }
 
-  void send(List<QProfileName> changedProfiles) {
+  void send(Multimap<QProfileName, ActiveRuleChange> changedProfiles) {
     BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification();
-    changedProfiles.stream()
+    changedProfiles.keySet().stream()
       .map(changedProfile -> {
         String profileName = changedProfile.getName();
         String languageName = languages.get(changedProfile.getLanguage()).getName();
-        return new Profile(profileName, languageName);
+        Collection<ActiveRuleChange> activeRuleChanges = changedProfiles.get(changedProfile);
+        int newRules = (int) activeRuleChanges.stream().map(ActiveRuleChange::getType).filter(ACTIVATED::equals).count();
+        int updatedRules = (int) activeRuleChanges.stream().map(ActiveRuleChange::getType).filter(UPDATED::equals).count();
+        int removedRules = (int) activeRuleChanges.stream().map(ActiveRuleChange::getType).filter(DEACTIVATED::equals).count();
+        return Profile.newBuilder(profileName, languageName)
+          .setNewRules(newRules)
+          .setUpdatedRules(updatedRules)
+          .setRemovedRules(removedRules)
+          .build();
       })
       .forEach(notification::addProfile);
     notificationManager.scheduleForSending(notification.serialize());
index 222525acfac0f14d8836f61aee2fb75db8a013ab..82db0ca0b9ef92b79c8d6831b4c4ce2f65055a6e 100644 (file)
@@ -40,7 +40,23 @@ public class BuiltInQualityProfilesNotificationTemplate extends EmailTemplate {
     StringBuilder message = new StringBuilder("Built-in quality profiles have been updated:\n");
     profilesNotification.getProfiles().stream()
       .sorted(Comparator.comparing(Profile::getLanguage).thenComparing(Profile::getProfileName))
-      .forEach(profile -> message.append("\"").append(profile.getProfileName()).append("\" - ").append(profile.getLanguage()).append("\n"));
+      .forEach(profile -> {
+        message.append("\"")
+          .append(profile.getProfileName()).append("\" - ")
+          .append(profile.getLanguage()).append("\n");
+        int newRules = profile.getNewRules();
+        if (newRules > 0) {
+          message.append(" ").append(newRules).append(" new rules\n");
+        }
+        int updatedRules = profile.getUpdatedRules();
+        if (updatedRules > 0) {
+          message.append(" ").append(updatedRules).append(" rules have been updated\n");
+        }
+        int removedRules = profile.getRemovedRules();
+        if (removedRules > 0) {
+          message.append(" ").append(removedRules).append(" rules removed\n");
+        }
+      });
 
     message.append(
       "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
index d343bbf526f4cd56fe45dcee4eaaf5a38e62d2c8..50490dc11b6c540b7769a24b25f04f2d60a18ea2 100644 (file)
@@ -19,7 +19,8 @@
  */
 package org.sonar.server.qualityprofile;
 
-import java.util.ArrayList;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -70,16 +71,14 @@ public class RegisterQualityProfiles {
 
       Map<QProfileName, RulesProfileDto> persistedRuleProfiles = loadPersistedProfiles(dbSession);
 
-      List<QProfileName> changedProfiles = new ArrayList<>();
+      Multimap<QProfileName, ActiveRuleChange> changedProfiles = ArrayListMultimap.create();
       builtInQProfiles.forEach(builtIn -> {
         RulesProfileDto ruleProfile = persistedRuleProfiles.get(builtIn.getQProfileName());
         if (ruleProfile == null) {
           register(dbSession, batchDbSession, builtIn);
         } else {
           List<ActiveRuleChange> changes = update(dbSession, builtIn, ruleProfile);
-          if (!changes.isEmpty()) {
-            changedProfiles.add(builtIn.getQProfileName());
-          }
+          changedProfiles.putAll(builtIn.getQProfileName(), changes);
         }
       });
       if (!changedProfiles.isEmpty()) {
index 819412888d52e0b5e990bf9af6668bfdd3a6e514..9bd803d14bc4fc9d5cd9a75c4b2fb951b92c106e 100644 (file)
  */
 package org.sonar.server.qualityprofile;
 
-import java.util.List;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.assertj.core.groups.Tuple;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.notifications.Notification;
+import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Languages;
-import org.sonar.server.language.LanguageTesting;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.server.notification.NotificationManager;
 import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
 
-import static java.util.Collections.singletonList;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
 import static org.assertj.core.api.Java6Assertions.assertThat;
 import static org.assertj.core.api.Java6Assertions.tuple;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.sonar.server.language.LanguageTesting.newLanguage;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED;
 
 public class BuiltInQualityProfilesNotificationSenderTest {
 
+  private static final Random RANDOM = new Random();
   private NotificationManager notificationManager = mock(NotificationManager.class);
 
   @Test
-  public void add_profile_to_notification() throws Exception {
-    String profileName = randomLowerCaseText();
-    String languageKey = randomLowerCaseText();
-    String languageName = randomLowerCaseText();
-    List<QProfileName> profileNames = singletonList(new QProfileName(languageKey, profileName));
-    Languages languages = new Languages(LanguageTesting.newLanguage(languageKey, languageName));
+  public void add_profile_to_notification_for_added_rules() throws Exception {
+    Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create();
+    Languages languages = new Languages();
+    Tuple expectedTuple = addProfile(profiles, languages, ACTIVATED);
+
+    BuiltInQualityProfilesNotificationSender underTest = new BuiltInQualityProfilesNotificationSender(notificationManager, languages);
+    underTest.send(profiles);
+
+    ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
+    verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture());
+    verifyNoMoreInteractions(notificationManager);
+    assertThat(BuiltInQualityProfilesNotification.parse(notificationArgumentCaptor.getValue()).getProfiles())
+      .extracting(Profile::getProfileName, Profile::getLanguage, Profile::getNewRules)
+      .containsExactlyInAnyOrder(expectedTuple);
+  }
+
+  @Test
+  public void add_profile_to_notification_for_updated_rules() throws Exception {
+    Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create();
+    Languages languages = new Languages();
+    Tuple expectedTuple = addProfile(profiles, languages, UPDATED);
+
+    BuiltInQualityProfilesNotificationSender underTest = new BuiltInQualityProfilesNotificationSender(notificationManager, languages);
+    underTest.send(profiles);
+
+    ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
+    verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture());
+    verifyNoMoreInteractions(notificationManager);
+    assertThat(BuiltInQualityProfilesNotification.parse(notificationArgumentCaptor.getValue()).getProfiles())
+      .extracting(Profile::getProfileName, Profile::getLanguage, Profile::getUpdatedRules)
+      .containsExactlyInAnyOrder(expectedTuple);
+  }
+
+  @Test
+  public void add_profile_to_notification_for_removed_rules() throws Exception {
+    Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create();
+    Languages languages = new Languages();
+    Tuple expectedTuple = addProfile(profiles, languages, DEACTIVATED);
 
     BuiltInQualityProfilesNotificationSender underTest = new BuiltInQualityProfilesNotificationSender(notificationManager, languages);
-    underTest.send(profileNames);
+    underTest.send(profiles);
 
     ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
     verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture());
     verifyNoMoreInteractions(notificationManager);
     assertThat(BuiltInQualityProfilesNotification.parse(notificationArgumentCaptor.getValue()).getProfiles())
-      .extracting(Profile::getProfileName, Profile::getLanguage)
-      .containsExactlyInAnyOrder(tuple(profileName, languageName));
+      .extracting(Profile::getProfileName, Profile::getLanguage, Profile::getRemovedRules)
+      .containsExactlyInAnyOrder(expectedTuple);
+  }
+
+  @Test
+  public void add_multiple_profiles_to_notification() throws Exception {
+    Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create();
+    Languages languages = new Languages();
+    Tuple expectedTuple1 = addProfile(profiles, languages, ACTIVATED);
+    Tuple expectedTuple2 = addProfile(profiles, languages, ACTIVATED);
+
+    BuiltInQualityProfilesNotificationSender underTest = new BuiltInQualityProfilesNotificationSender(notificationManager, languages);
+    underTest.send(profiles);
+
+    ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
+    verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture());
+    verifyNoMoreInteractions(notificationManager);
+    assertThat(BuiltInQualityProfilesNotification.parse(notificationArgumentCaptor.getValue()).getProfiles())
+      .extracting(Profile::getProfileName, Profile::getLanguage, Profile::getNewRules)
+      .containsExactlyInAnyOrder(expectedTuple1, expectedTuple2);
+  }
+
+  private Tuple addProfile(Multimap<QProfileName, ActiveRuleChange> profiles, Languages languages, ActiveRuleChange.Type type) {
+    String profileName = randomLowerCaseText();
+    Language language = newLanguage(randomLowerCaseText(), randomLowerCaseText());
+    languages.add(language);
+    int numberOfChanges = RANDOM.nextInt(1000);
+    profiles.putAll(
+      new QProfileName(language.getKey(), profileName),
+      IntStream.range(0, numberOfChanges).mapToObj(i -> new ActiveRuleChange(type, ActiveRuleKey.parse("qp:repo:rule" + i))).collect(Collectors.toSet()));
+    return tuple(profileName, language.getName(), numberOfChanges);
   }
 
   private static String randomLowerCaseText() {
     return randomAlphanumeric(20).toLowerCase();
   }
-}
\ No newline at end of file
+}
index 27a65edfb2b7d8a0e9c9b76c924a40785fc68be9..1bd78367688aa2d07b5428addd701326712b7dae 100644 (file)
@@ -32,16 +32,118 @@ public class BuiltInQualityProfilesNotificationTemplateTest {
   private BuiltInQualityProfilesNotificationTemplate underTest = new BuiltInQualityProfilesNotificationTemplate();
 
   @Test
-  public void notification_contains_list_of_quality_profiles() {
+  public void notification_contains_list_of_new_rules() {
     String profileName = randomAlphanumeric(20);
     String language = randomAlphanumeric(20);
     BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
-      .addProfile(new Profile(profileName, language));
+      .addProfile(Profile.newBuilder(profileName, language)
+        .setNewRules(2)
+        .build());
 
     EmailMessage emailMessage = underTest.format(notification.serialize());
 
     assertThat(emailMessage.getMessage()).isEqualTo("Built-in quality profiles have been updated:\n" +
       "\"" + profileName + "\" - " + language + "\n" +
+      " 2 new rules\n" +
       "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
   }
+
+  @Test
+  public void notification_contains_list_of_updated_rules() {
+    String profileName = randomAlphanumeric(20);
+    String language = randomAlphanumeric(20);
+    BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
+      .addProfile(Profile.newBuilder(profileName, language)
+        .setUpdatedRules(2)
+        .build());
+
+    EmailMessage emailMessage = underTest.format(notification.serialize());
+
+    assertThat(emailMessage.getMessage()).isEqualTo("Built-in quality profiles have been updated:\n" +
+      "\"" + profileName + "\" - " + language + "\n" +
+      " 2 rules have been updated\n" +
+      "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
+  }
+
+  @Test
+  public void notification_contains_list_of_removed_rules() {
+    String profileName = randomAlphanumeric(20);
+    String language = randomAlphanumeric(20);
+    BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
+      .addProfile(Profile.newBuilder(profileName, language)
+        .setRemovedRules(2)
+        .build());
+
+    EmailMessage emailMessage = underTest.format(notification.serialize());
+
+    assertThat(emailMessage.getMessage()).isEqualTo("Built-in quality profiles have been updated:\n" +
+      "\"" + profileName + "\" - " + language + "\n" +
+      " 2 rules removed\n" +
+      "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
+  }
+
+  @Test
+  public void notification_contains_list_of_new_updated_and_removed_rules() {
+    String profileName = randomAlphanumeric(20);
+    String language = randomAlphanumeric(20);
+    BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
+      .addProfile(Profile.newBuilder(profileName, language)
+        .setNewRules(2)
+        .setUpdatedRules(3)
+        .setRemovedRules(4)
+        .build());
+
+    EmailMessage emailMessage = underTest.format(notification.serialize());
+
+    assertThat(emailMessage.getMessage()).isEqualTo("Built-in quality profiles have been updated:\n" +
+      "\"" + profileName + "\" - " + language + "\n" +
+      " 2 new rules\n" +
+      " 3 rules have been updated\n" +
+      " 4 rules removed\n" +
+      "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
+  }
+
+  @Test
+  public void notification_contains_many_profiles() {
+    String profileName1 = "profile1_" + randomAlphanumeric(20);
+    String language1 = "lang1_" + randomAlphanumeric(20);
+    String profileName2 = "profile1_" + randomAlphanumeric(20);
+    String language2 = "lang2_" + randomAlphanumeric(20);
+    BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
+      .addProfile(Profile.newBuilder(profileName1, language1)
+        .setNewRules(2)
+        .build())
+      .addProfile(Profile.newBuilder(profileName2, language2)
+        .setNewRules(13)
+        .build());
+
+    EmailMessage emailMessage = underTest.format(notification.serialize());
+
+    assertThat(emailMessage.getMessage()).isEqualTo("Built-in quality profiles have been updated:\n" +
+      "\"" + profileName1 + "\" - " + language1 + "\n" +
+      " 2 new rules\n" +
+      "\"" + profileName2 + "\" - " + language2 + "\n" +
+      " 13 new rules\n" +
+      "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
+  }
+
+  @Test
+  public void notification_contains_profiles_sorted_by_language_then_by_profile_name() {
+    String language1 = "lang1_" + randomAlphanumeric(20);
+    String language2 = "lang2_" + randomAlphanumeric(20);
+    String profileName1 = "profile1_" + randomAlphanumeric(20);
+    String profileName2 = "profile2_" + randomAlphanumeric(20);
+    String profileName3 = "profile3_" + randomAlphanumeric(20);
+    BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
+      .addProfile(Profile.newBuilder(profileName3, language2).build())
+      .addProfile(Profile.newBuilder(profileName2, language1).build())
+      .addProfile(Profile.newBuilder(profileName1, language2).build());
+
+    EmailMessage emailMessage = underTest.format(notification.serialize());
+
+    assertThat(emailMessage.getMessage()).containsSequence(
+      "\"" + profileName2 + "\" - " + language1,
+      "\"" + profileName1 + "\" - " + language2,
+      "\"" + profileName3 + "\" - " + language2);
+  }
 }
index 7ea0b93b029643c8d2c42a140f34800004a80103..9180e904311a263178f5e071a1ff7da00bb306d5 100644 (file)
@@ -50,11 +50,17 @@ public class BuiltInQualityProfilesNotificationTest {
     String profileName = randomAlphanumeric(20);
     String language = randomAlphanumeric(20);
 
-    Notification notification = new BuiltInQualityProfilesNotification().addProfile(new Profile(profileName, language)).serialize();
+    Notification notification = new BuiltInQualityProfilesNotification()
+      .addProfile(Profile.newBuilder(profileName, language)
+        .setNewRules(3)
+        .setUpdatedRules(5)
+        .setRemovedRules(7)
+        .build())
+      .serialize();
     BuiltInQualityProfilesNotification result = BuiltInQualityProfilesNotification.parse(notification);
 
-    assertThat(result.getProfiles()).extracting(Profile::getProfileName, Profile::getLanguage)
-      .containsExactlyInAnyOrder(tuple(profileName, language));
+    assertThat(result.getProfiles()).extracting(Profile::getProfileName, Profile::getLanguage, Profile::getNewRules, Profile::getUpdatedRules, Profile::getRemovedRules)
+      .containsExactlyInAnyOrder(tuple(profileName, language, 3, 5, 7));
   }
 
   @Test
@@ -65,8 +71,8 @@ public class BuiltInQualityProfilesNotificationTest {
     String language2 = randomAlphanumeric(20);
 
     Notification notification = new BuiltInQualityProfilesNotification()
-      .addProfile(new Profile(profileName1, language1))
-      .addProfile(new Profile(profileName2, language2))
+      .addProfile(Profile.newBuilder(profileName1, language1).build())
+      .addProfile(Profile.newBuilder(profileName2, language2).build())
       .serialize();
     BuiltInQualityProfilesNotification result = BuiltInQualityProfilesNotification.parse(notification);
 
index 362486e0fec56bd59689e2cc3aa356399cd29ccd..e11f76806f90702d25d52a3ddf2253230e53b3c1 100644 (file)
@@ -19,8 +19,8 @@
  */
 package org.sonar.server.qualityprofile;
 
+import com.google.common.collect.Multimap;
 import java.util.Arrays;
-import java.util.List;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -53,6 +53,8 @@ import static org.sonar.api.rules.Rule.create;
 import static org.sonar.api.rules.RulePriority.MAJOR;
 import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto;
 import static org.sonar.server.language.LanguageTesting.newLanguage;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
 
 public class RegisterQualityProfilesNotificationTest {
 
@@ -116,40 +118,73 @@ public class RegisterQualityProfilesNotificationTest {
 
     underTest.start();
 
-    ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+    ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
     verify(builtInQualityProfilesNotification).send(captor.capture());
-    List<QProfileName> updatedProfiles = captor.<List<QProfileName>>getValue();
-    assertThat(updatedProfiles)
+    Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
+    assertThat(updatedProfiles.keySet())
       .extracting(QProfileName::getName, QProfileName::getLanguage)
       .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
+    assertThat(updatedProfiles.values())
+      .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
+      .containsExactlyInAnyOrder(tuple(newRule.getId(), ACTIVATED));
   }
 
   @Test
-  public void only_send_one_notification_when_several_built_in_profiles_contain_new_rules() {
+  public void send_notification_when_built_in_profile_contains_deactivated_rule() {
     String language = newLanguageKey();
     RuleDefinitionDto existingRule = db.rules().insert(r -> r.setLanguage(language));
-    RuleDefinitionDto newRule = db.rules().insert(r -> r.setLanguage(language));
+    RulesProfileDto dbProfile = insertBuiltInProfile(language);
+    activateRuleInDb(dbProfile, existingRule, MAJOR);
+    addPluginProfile(dbProfile);
+    builtInQProfileRepositoryRule.initialize();
+
+    underTest.start();
+
+    ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
+    verify(builtInQualityProfilesNotification).send(captor.capture());
+    Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
+    assertThat(updatedProfiles.keySet())
+      .extracting(QProfileName::getName, QProfileName::getLanguage)
+      .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
+    assertThat(updatedProfiles.values())
+      .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
+      .containsExactlyInAnyOrder(tuple(existingRule.getId(), DEACTIVATED));
+  }
 
+  @Test
+  public void only_send_one_notification_when_several_built_in_profiles_contain_new_rules() {
+    String language = newLanguageKey();
+
+    RuleDefinitionDto existingRule1 = db.rules().insert(r -> r.setLanguage(language));
+    RuleDefinitionDto newRule1 = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile1 = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile1, existingRule, MAJOR);
-    addPluginProfile(dbProfile1, existingRule, newRule);
+    activateRuleInDb(dbProfile1, existingRule1, MAJOR);
+    addPluginProfile(dbProfile1, existingRule1, newRule1);
 
+    RuleDefinitionDto existingRule2 = db.rules().insert(r -> r.setLanguage(language));
+    RuleDefinitionDto newRule2 = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile2 = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile2, existingRule, MAJOR);
-    addPluginProfile(dbProfile2, existingRule, newRule);
+    activateRuleInDb(dbProfile2, existingRule2, MAJOR);
+    addPluginProfile(dbProfile2, existingRule2, newRule2);
     builtInQProfileRepositoryRule.initialize();
 
     underTest.start();
 
-    ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+    ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
     verify(builtInQualityProfilesNotification).send(captor.capture());
-    List<QProfileName> updatedProfiles = captor.<List<QProfileName>>getValue();
-    assertThat(updatedProfiles)
+    Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
+    assertThat(updatedProfiles.keySet())
       .extracting(QProfileName::getName, QProfileName::getLanguage)
       .containsExactlyInAnyOrder(
         tuple(dbProfile1.getName(), dbProfile1.getLanguage()),
         tuple(dbProfile2.getName(), dbProfile2.getLanguage())
       );
+    assertThat(updatedProfiles.values())
+      .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
+      .containsExactlyInAnyOrder(
+        tuple(newRule1.getId(), ACTIVATED),
+        tuple(newRule2.getId(), ACTIVATED)
+      );
   }
 
   private void addPluginProfile(RulesProfileDto dbProfile, RuleDefinitionDto... dbRules) {
index 3a53e9293ffa7bb093e710aece0c341ca00f9e49..73f54dc30f4eb29b94ede2d873038c8782a7647c 100644 (file)
@@ -21,7 +21,9 @@ package org.sonar.foo.rule;
 
 import org.sonar.api.profiles.ProfileDefinition;
 import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
 import org.sonar.api.utils.ValidationMessages;
 
 import static org.sonar.api.rules.RulePriority.MAJOR;
@@ -30,12 +32,28 @@ import static org.sonar.foo.rule.FooRulesDefinition.FOO_REPOSITORY;
 
 public class FooBasicProfile extends ProfileDefinition {
 
+  private final RuleFinder ruleFinder;
+
+  public FooBasicProfile(RuleFinder ruleFinder) {
+    this.ruleFinder = ruleFinder;
+  }
+
   @Override
   public RulesProfile createProfile(ValidationMessages validation) {
     final RulesProfile profile = RulesProfile.create("Basic", KEY);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "UnchangedRule"), MAJOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "ChangedRule"), MAJOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "RemovedRule"), MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "UnchangedRule", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "ChangedRule", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "ToBeDeactivatedRule", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "ToBeRemovedRule", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "RuleWithUnchangedParameter", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "RuleWithChangedParameter", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "RuleWithRemovedParameter", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "RuleWithAddedParameter", MAJOR);
     return profile;
   }
+
+  private ActiveRule activateRule(RulesProfile profile, String repo, String key, RulePriority severity) {
+    return profile.activateRule(ruleFinder.findByKey(repo, key), severity);
+  }
+
 }
index 7257dd726817c3f100de26c416d9740bfd5b83c5..039cea9fd2bac63e27cc3d5a94a73d264dd3af25 100644 (file)
@@ -35,7 +35,12 @@ public class FooRulesDefinition implements RulesDefinition {
     NewRepository repo = context.createRepository(FOO_REPOSITORY, Foo.KEY).setName("Foo");
     createRule(repo, "UnchangedRule");
     createRule(repo, "ChangedRule");
-    createRule(repo, "RemovedRule");
+    createRule(repo, "ToBeDeactivatedRule");
+    createRule(repo, "ToBeRemovedRule");
+    createRule(repo, "RuleWithUnchangedParameter").createParam("unchanged").setDefaultValue("10");
+    createRule(repo, "RuleWithChangedParameter").createParam("toBeChanged").setDefaultValue("10");
+    createRule(repo, "RuleWithRemovedParameter").createParam("toBeRemoved").setDefaultValue("10");
+    createRule(repo, "RuleWithAddedParameter").createParam("added");
     repo.done();
   }
 
index 1c2d051f7cf018dd3194878275055ba5106b1484..aceb76331e26058857825673ee40b9639b2f0039 100644 (file)
@@ -21,7 +21,9 @@ package org.sonar.foo.rule;
 
 import org.sonar.api.profiles.ProfileDefinition;
 import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
 import org.sonar.api.utils.ValidationMessages;
 
 import static org.sonar.api.rules.RulePriority.MAJOR;
@@ -31,12 +33,29 @@ import static org.sonar.foo.rule.FooRulesDefinition.FOO_REPOSITORY;
 
 public class FooBasicProfile extends ProfileDefinition {
 
+  private final RuleFinder ruleFinder;
+
+  public FooBasicProfile(RuleFinder ruleFinder) {
+    this.ruleFinder = ruleFinder;
+  }
+
   @Override
   public RulesProfile createProfile(ValidationMessages validation) {
     final RulesProfile profile = RulesProfile.create("Basic", KEY);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "UnchangedRule"), MAJOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "ChangedRule"), MINOR);
-    profile.activateRule(Rule.create(FOO_REPOSITORY, "NewRule"), MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "UnchangedRule", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "ChangedRule", MINOR);
+    activateRule(profile, FOO_REPOSITORY, "NewRule", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "RuleWithUnchangedParameter", MAJOR);
+    // Update of the default value of a parameter is not taken into account in quality profile as long as it's not also explicitly set in the quality profile
+    // TODO Remove the parameter value when SONAR_9475 will be fixed
+    activateRule(profile, FOO_REPOSITORY, "RuleWithChangedParameter", MAJOR).setParameter("toBeChanged", "20");
+    activateRule(profile, FOO_REPOSITORY, "RuleWithRemovedParameter", MAJOR);
+    activateRule(profile, FOO_REPOSITORY, "RuleWithAddedParameter", MAJOR);
     return profile;
   }
+
+  private ActiveRule activateRule(RulesProfile profile, String repo, String key, RulePriority severity) {
+    return profile.activateRule(ruleFinder.findByKey(repo, key), severity);
+  }
+
 }
index aacc233526226c2b65e5e2a8c6455d87642a9622..a0ef0ccbdf791318b9c23e9d06f7a62aaed1aa8a 100644 (file)
@@ -36,6 +36,12 @@ public class FooRulesDefinition implements RulesDefinition {
     createRule(repo, "UnchangedRule");
     createRule(repo, "ChangedRule");
     createRule(repo, "NewRule");
+    createRule(repo, "ToBeDeactivatedRule");
+    createRule(repo, "RuleWithUnchangedParameter").createParam("unchanged").setDefaultValue("10");
+    // Update of the default value of a parameter is not taken into account in quality profile as long as it's not also explicitly set in the quality profile
+    createRule(repo, "RuleWithChangedParameter").createParam("toBeChanged").setDefaultValue("20");
+    createRule(repo, "RuleWithRemovedParameter");
+    createRule(repo, "RuleWithAddedParameter").createParam("added").setDefaultValue("10");
     repo.done();
   }
 
index 4f287380171e49a59c3fe30114617592be52a15a..7845f3ced145e6bdd64404526b7a21575b62ae54 100644 (file)
@@ -70,8 +70,7 @@ public class BuiltInQualityProfilesNotificationTest {
       .addPlugin(pluginArtifact("foo-plugin-v1"))
       .setServerProperty("email.smtp_host.secured", "localhost")
       .setServerProperty("email.smtp_port.secured", Integer.toString(smtpServer.getServer().getPort()))
-      // .setServerProperty("sonar.web.javaAdditionalOpts", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")// FIXME
-      // remove web debugging
+      //.setServerProperty("sonar.web.javaAdditionalOpts", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")// FIXME remove web debugging
       .build();
     orchestrator.start();
 
@@ -101,8 +100,7 @@ public class BuiltInQualityProfilesNotificationTest {
       .addPlugin(pluginArtifact("foo-plugin-v1"))
       .setServerProperty("email.smtp_host.secured", "localhost")
       .setServerProperty("email.smtp_port.secured", Integer.toString(smtpServer.getServer().getPort()))
-      // .setServerProperty("sonar.web.javaAdditionalOpts", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")// FIXME
-      // remove web debugging
+      //.setServerProperty("sonar.web.javaAdditionalOpts", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")// FIXME remove web debugging
       .build();
     orchestrator.start();
 
@@ -139,6 +137,9 @@ public class BuiltInQualityProfilesNotificationTest {
       .containsSequence(
         "Built-in quality profiles have been updated:",
         "\"Basic\" - Foo",
+        " 1 new rules",
+        " 3 rules have been updated",
+        " 1 rules removed",
         "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.")
       .isEqualTo(messages.get(1).getMimeMessage().getContent().toString());
   }
@@ -151,14 +152,6 @@ public class BuiltInQualityProfilesNotificationTest {
     }
   }
 
-  private String getContent(MimeMessage mimeMessage1) {
-    try {
-      return mimeMessage1.getContent().toString();
-    } catch (Exception e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   private String getAllRecipients(MimeMessage mimeMessage) {
     try {
       return mimeMessage.getHeader("To", null);