<td id="resource_language"><%= Api::Utils.language_name(@project.language) -%></td>
</tr>
<% end %>
+
<%
- profile_measure=@snapshot.measure(Metric::PROFILE)
- if profile_measure
+ profiles_measure = measure(Metric::PROFILES)
+ if profiles_measure && !profiles_measure.data.blank?
+ profiles = JSON.parse profiles_measure.data
+ %>
+ <tr>
+ <td><%= message('widget.description.profiles') -%>:</td>
+ <td><span id="resource_profile">
+ <% profiles.each_with_index do |profile, i| %>
+ <%= Api::Utils.language_name(profile['language']) -%>: <%= link_to profile['name'], {:controller => '/rules_configuration', :action => 'index', :id => profile['id']}, :id => profile['language'] + '_profile_link' -%></span> (<%= message('widget.description.profile_version_x', :params => profile['version']) -%>)
+ <% if i < (profiles.size - 1) %>
+ <br/>
+ <% end %>
+ <% end %>
+ </td>
+ </tr>
+ <%
+ else
+ profile_measure=@snapshot.measure(Metric::PROFILE)
+ if profile_measure
%>
<tr>
<td><%= message('widget.description.profile') -%>:</td>
<td><span id="resource_profile"><%= link_to profile_measure.data, {:controller => '/rules_configuration', :action => 'index', :id => profile_measure.value.to_i}, :id => 'profile_link' -%></span> (<%= message('widget.description.profile_version_x', :params => format_measure('profile_version', :default => '1')) -%>)</td>
</tr>
- <% end %>
+ <% end
+ end %>
+
<%
using_default=false
quality_gate=Property.value('sonar.qualitygate', @resource && @resource.id, nil)
--- /dev/null
+/*
+ * 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.rule;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+/**
+ * Aggregate which Quality profiles have been used on the current module.
+ */
+public class QProfileDecorator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return project.getModules().size() > 0;
+ }
+
+ @Override
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (!ResourceUtils.isProject(resource)) {
+ return;
+ }
+ UsedQProfiles profiles = UsedQProfiles.empty();
+ for (Measure childProfilesMeasure : context.getChildrenMeasures(CoreMetrics.PROFILES)) {
+ UsedQProfiles childProfiles = UsedQProfiles.fromJSON(childProfilesMeasure.getData());
+ profiles = profiles.merge(childProfiles);
+ }
+
+ Measure detailsMeasure = new Measure(CoreMetrics.PROFILES, profiles.toJSON());
+ context.saveMeasure(detailsMeasure);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+}
*/
package org.sonar.batch.rule;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.resources.Project;
import org.sonar.core.qualityprofile.db.QualityProfileDao;
+import java.util.List;
+
/**
* Stores which Quality profiles have been used on the current module.
*/
}
public boolean shouldExecuteOnProject(Project project) {
- return true;
+ // Should be only executed on leaf modules
+ return project.getModules().isEmpty();
}
public void analyse(Project project, SensorContext context) {
+ List<ModuleQProfiles.QProfile> profiles = Lists.newArrayList();
for (String language : fs.languages()) {
ModuleQProfiles.QProfile qProfile = moduleQProfiles.findByLanguage(language);
if (qProfile != null) {
dao.updateUsedColumn(qProfile.id(), true);
+ profiles.add(qProfile);
}
}
- if (fs.languages().size() == 1) {
- String language = Iterables.getOnlyElement(fs.languages());
- ModuleQProfiles.QProfile qProfile = moduleQProfiles.findByLanguage(language);
- if (qProfile != null) {
- Measure measure = new Measure(CoreMetrics.PROFILE, qProfile.name()).setValue((double)qProfile.id());
- Measure measureVersion = new Measure(CoreMetrics.PROFILE_VERSION, qProfile.version().doubleValue());
- context.saveMeasure(measure);
- context.saveMeasure(measureVersion);
- }
+ if (profiles.size() > 0) {
+ UsedQProfiles used = UsedQProfiles.fromProfiles(profiles);
+ Measure detailsMeasure = new Measure(CoreMetrics.PROFILES, used.toJSON());
+ context.saveMeasure(detailsMeasure);
+ }
+
+ // For backward compatibility
+ if (profiles.size() == 1) {
+ ModuleQProfiles.QProfile qProfile = profiles.get(0);
+ Measure measure = new Measure(CoreMetrics.PROFILE, qProfile.name()).setValue((double) qProfile.id());
+ Measure measureVersion = new Measure(CoreMetrics.PROFILE_VERSION, qProfile.version().doubleValue());
+ context.saveMeasure(measure);
+ context.saveMeasure(measureVersion);
}
}
--- /dev/null
+/*
+ * 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.rule;
+
+import com.google.common.collect.Maps;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+
+import javax.annotation.concurrent.Immutable;
+
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Map;
+
+@Immutable
+public class UsedQProfiles {
+
+ private Map<String, Map<Integer, ModuleQProfiles.QProfile>> profilesByLanguage = Maps.newLinkedHashMap();
+
+ private UsedQProfiles() {
+ }
+
+ public static final UsedQProfiles fromProfiles(Iterable<QProfile> profiles) {
+ UsedQProfiles result = new UsedQProfiles();
+ for (QProfile qProfile : profiles) {
+ result.add(qProfile);
+ }
+ return result;
+ }
+
+ public static final UsedQProfiles empty() {
+ return new UsedQProfiles();
+ }
+
+ public static final UsedQProfiles fromProfiles(QProfile... profiles) {
+ return fromProfiles(Arrays.asList(profiles));
+ }
+
+ public static final UsedQProfiles fromJSON(String json) {
+ UsedQProfiles result = new UsedQProfiles();
+ JsonArray root = new JsonParser().parse(json).getAsJsonArray();
+ for (JsonElement elt : root) {
+ JsonObject profile = elt.getAsJsonObject();
+ result.add(new QProfile(profile.get("id").getAsInt(), profile.get("name").getAsString(), profile.get("language").getAsString(), profile.get("version").getAsInt()));
+ }
+ return result;
+ }
+
+ public final String toJSON() {
+ StringWriter json = new StringWriter();
+ JsonWriter writer = JsonWriter.of(json);
+ writer.beginArray();
+ for (String languageKey : profilesByLanguage.keySet()) {
+ for (ModuleQProfiles.QProfile qProfile : profilesByLanguage.get(languageKey).values()) {
+ writer.beginObject()
+ .prop("id", qProfile.id())
+ .prop("name", qProfile.name())
+ .prop("version", qProfile.version())
+ .prop("language", qProfile.language())
+ .endObject();
+ }
+ }
+ writer.endArray();
+ writer.close();
+ return json.toString();
+ }
+
+ public final UsedQProfiles merge(UsedQProfiles other) {
+ return empty().mergeInPlace(this).mergeInPlace(other);
+ }
+
+ private void add(ModuleQProfiles.QProfile profile) {
+ if (!profilesByLanguage.containsKey(profile.language())) {
+ profilesByLanguage.put(profile.language(), Maps.<Integer, ModuleQProfiles.QProfile>newLinkedHashMap());
+ }
+ QProfile alreadyAdded = profilesByLanguage.get(profile.language()).get(profile.id());
+ if (alreadyAdded == null
+ // Keep only latest version
+ || profile.version() > alreadyAdded.version()) {
+ profilesByLanguage.get(profile.language()).put(profile.id(), profile);
+ }
+ }
+
+ private UsedQProfiles addAll(Iterable<QProfile> profiles) {
+ for (QProfile profile : profiles) {
+ this.add(profile);
+ }
+ return this;
+ }
+
+ private UsedQProfiles mergeInPlace(UsedQProfiles other) {
+ for (Map<Integer, QProfile> byIds : other.profilesByLanguage.values()) {
+ this.addAll(byIds.values());
+ }
+ return this;
+ }
+
+}
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project;
import org.sonar.api.scan.filesystem.FileExclusions;
-import org.sonar.batch.*;
+import org.sonar.batch.DefaultProjectClasspath;
+import org.sonar.batch.DefaultSensorContext;
+import org.sonar.batch.DefaultTimeMachine;
+import org.sonar.batch.ProjectTree;
+import org.sonar.batch.ResourceFilters;
+import org.sonar.batch.ViolationFilters;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ExtensionMatcher;
import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
import org.sonar.batch.qualitygate.QualityGateProvider;
import org.sonar.batch.qualitygate.QualityGateVerifier;
-import org.sonar.batch.rule.*;
-import org.sonar.batch.scan.filesystem.*;
+import org.sonar.batch.rule.ActiveRulesProvider;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.rule.QProfileDecorator;
+import org.sonar.batch.rule.QProfileSensor;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.rule.RulesProfileProvider;
+import org.sonar.batch.scan.filesystem.ComponentIndexer;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.filesystem.DeprecatedFileFilters;
+import org.sonar.batch.scan.filesystem.ExclusionFilters;
+import org.sonar.batch.scan.filesystem.FileIndexer;
+import org.sonar.batch.scan.filesystem.FileSystemLogger;
+import org.sonar.batch.scan.filesystem.InputFileBuilderFactory;
+import org.sonar.batch.scan.filesystem.LanguageDetectionFactory;
+import org.sonar.batch.scan.filesystem.ModuleFileSystemInitializer;
+import org.sonar.batch.scan.filesystem.ModuleInputFileCache;
+import org.sonar.batch.scan.filesystem.PreviousFileHashLoader;
+import org.sonar.batch.scan.filesystem.ProjectFileSystemAdapter;
+import org.sonar.batch.scan.filesystem.StatusDetectionFactory;
import org.sonar.batch.scan.report.JsonReport;
import org.sonar.core.component.ScanPerspectives;
import org.sonar.core.measure.MeasurementFilters;
new ActiveRulesProvider(),
new RulesProfileProvider(),
QProfileSensor.class,
+ QProfileDecorator.class,
CheckFactory.class,
// report
--- /dev/null
+/*
+ * 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.rule;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.test.IsMeasure;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class QProfileDecoratorTest {
+
+ Project project = mock(Project.class);
+ Project moduleA = mock(Project.class);
+ Project moduleB = mock(Project.class);
+ DecoratorContext decoratorContext = mock(DecoratorContext.class);
+
+ @Test
+ public void don_t_run_on_leaf() throws Exception {
+ QProfileDecorator decorator = new QProfileDecorator();
+ when(project.getModules()).thenReturn(Collections.<Project>emptyList());
+ assertThat(decorator.shouldExecuteOnProject(project)).isFalse();
+
+ when(project.getModules()).thenReturn(Arrays.asList(moduleA, moduleB));
+ assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
+ }
+
+ @Test
+ public void aggregate() throws Exception {
+ Measure measureModuleA = new Measure(CoreMetrics.PROFILES, "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"}]");
+ Measure measureModuleB = new Measure(CoreMetrics.PROFILES, "[{\"id\":3,\"name\":\"Php One\",\"version\":30,\"language\":\"php\"}]");
+ when(decoratorContext.getChildrenMeasures(CoreMetrics.PROFILES)).thenReturn(Arrays.asList(measureModuleA, measureModuleB));
+
+ when(project.getScope()).thenReturn(Scopes.PROJECT);
+
+ QProfileDecorator decorator = new QProfileDecorator();
+ decorator.decorate(project, decoratorContext);
+
+ verify(decoratorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.PROFILES,
+ "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"},{\"id\":3,\"name\":\"Php One\",\"version\":30,\"language\":\"php\"}]")));
+ }
+
+ @Test
+ public void aggregate_several_profile_same_language() throws Exception {
+ Measure measureModuleA = new Measure(CoreMetrics.PROFILES, "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"}]");
+ Measure measureModuleB = new Measure(CoreMetrics.PROFILES, "[{\"id\":3,\"name\":\"Java Three\",\"version\":30,\"language\":\"java\"}]");
+ when(decoratorContext.getChildrenMeasures(CoreMetrics.PROFILES)).thenReturn(Arrays.asList(measureModuleA, measureModuleB));
+
+ when(project.getScope()).thenReturn(Scopes.PROJECT);
+
+ QProfileDecorator decorator = new QProfileDecorator();
+ decorator.decorate(project, decoratorContext);
+
+ verify(decoratorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.PROFILES,
+ "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"},{\"id\":3,\"name\":\"Java Three\",\"version\":30,\"language\":\"java\"}]")));
+ }
+
+ @Test
+ public void aggregate_several_profile_same_id() throws Exception {
+ Measure measureModuleA = new Measure(CoreMetrics.PROFILES, "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"}]");
+ Measure measureModuleB = new Measure(CoreMetrics.PROFILES, "[{\"id\":2,\"name\":\"Java Two\",\"version\":30,\"language\":\"java\"}]");
+ when(decoratorContext.getChildrenMeasures(CoreMetrics.PROFILES)).thenReturn(Arrays.asList(measureModuleA, measureModuleB));
+
+ when(project.getScope()).thenReturn(Scopes.PROJECT);
+
+ QProfileDecorator decorator = new QProfileDecorator();
+ decorator.decorate(project, decoratorContext);
+
+ verify(decoratorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.PROFILES,
+ "[{\"id\":2,\"name\":\"Java Two\",\"version\":30,\"language\":\"java\"}]")));
+ }
+}
import java.util.Collections;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
public class QProfileSensorTest extends AbstractDaoTestCase {
sensor.analyse(project, sensorContext);
checkTable("mark_profiles_as_used", "rules_profiles");
-
- // no measures on multi-language modules
- verifyZeroInteractions(sensorContext);
}
@Test
verify(sensorContext).saveMeasure(argThat(new IsMeasure(CoreMetrics.PROFILE, "Java Two")));
verify(sensorContext).saveMeasure(argThat(new IsMeasure(CoreMetrics.PROFILE_VERSION, 20.0)));
+ verify(sensorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.PROFILES, "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"}]")));
+ }
+
+ @Test
+ public void store_measures_on_multi_lang_module() throws Exception {
+ setupData("shared");
+
+ QualityProfileDao dao = new QualityProfileDao(getMyBatis());
+ when(moduleQProfiles.findByLanguage("java")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(2)));
+ when(moduleQProfiles.findByLanguage("php")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(3)));
+ when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
+ fs.addLanguages("java", "php");
+
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, dao);
+ assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
+ sensor.analyse(project, sensorContext);
+
+ verify(sensorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.PROFILES,
+ "[{\"id\":2,\"name\":\"Java Two\",\"version\":20,\"language\":\"java\"},{\"id\":3,\"name\":\"Php One\",\"version\":30,\"language\":\"php\"}]")));
}
}
--- /dev/null
+/*
+ * 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.rule;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class UsedQProfilesTest {
+
+ @Test
+ public void serialization() throws Exception {
+
+ ModuleQProfiles.QProfile java = new ModuleQProfiles.QProfile(1, "Sonar Way", "java", 1);
+ ModuleQProfiles.QProfile php = new ModuleQProfiles.QProfile(2, "Sonar Way", "php", 1);
+
+ UsedQProfiles used = UsedQProfiles.fromProfiles(java, php);
+ assertThat(used.toJSON()).isEqualTo(
+ "[{\"id\":1,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"php\"}]");
+ }
+
+ @Test
+ public void deserialization() throws Exception {
+ UsedQProfiles used = UsedQProfiles
+ .fromJSON("[{\"id\":1,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"php\"}]");
+
+ assertThat(used.toJSON()).isEqualTo(
+ "[{\"id\":1,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"php\"}]");
+ }
+
+ @Test
+ public void merge() throws Exception {
+ UsedQProfiles first = UsedQProfiles
+ .fromJSON("[{\"id\":1,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"java\"}]");
+
+ UsedQProfiles second = UsedQProfiles
+ .fromJSON("[{\"id\":2,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"php\"}]");
+
+ assertThat(first.merge(second).toJSON()).isEqualTo(
+ "[{\"id\":1,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"php\"}]");
+ }
+
+ @Test
+ public void merge_no_duplicate_ids() throws Exception {
+ UsedQProfiles first = UsedQProfiles
+ .fromJSON("[{\"id\":1,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":2,\"language\":\"php\"}]");
+
+ UsedQProfiles second = UsedQProfiles
+ .fromJSON("[{\"id\":1,\"name\":\"Sonar Way\",\"version\":2,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":1,\"language\":\"php\"}]");
+
+ assertThat(first.merge(second).toJSON()).isEqualTo(
+ "[{\"id\":1,\"name\":\"Sonar Way\",\"version\":2,\"language\":\"java\"},{\"id\":2,\"name\":\"Sonar Way\",\"version\":2,\"language\":\"php\"}]");
+ }
+
+}
widget.description.key=Key
widget.description.language=Language
widget.description.profile=Profile
+widget.description.profiles=Profiles
widget.description.profile_version_x=version {0}
widget.description.qualitygate=Quality Gate
widget.description.alerts=Displays a summary of the project's quality gate status.
.setDomain(DOMAIN_GENERAL)
.create();
+ /**
+ * @deprecated since 4.4 doesn't support multi-language. See {@link #PROFILES_KEY}
+ */
+ @Deprecated
public static final String PROFILE_KEY = "profile";
+ /**
+ * @deprecated since 4.4 doesn't support multi-language. See {@link #PROFILES_KEY}
+ */
+ @Deprecated
public static final Metric PROFILE = new Metric.Builder(PROFILE_KEY, "Profile", Metric.ValueType.DATA)
.setDescription("Selected quality profile")
.setDomain(DOMAIN_GENERAL)
/**
* @since 2.9
+ * @deprecated since 4.4 doesn't support multi-language. See {@link #PROFILES_KEY}
*/
+ @Deprecated
public static final String PROFILE_VERSION_KEY = "profile_version";
-
/**
* @since 2.9
+ * @deprecated since 4.4 doesn't support multi-language. See {@link #PROFILES_KEY}
*/
+ @Deprecated
public static final Metric PROFILE_VERSION = new Metric.Builder(PROFILE_VERSION_KEY, "Profile version", Metric.ValueType.INT)
.setDescription("Selected quality profile version")
.setQualitative(false)
.setHidden(true)
.create();
+ /**
+ * @since 4.4
+ */
+ public static final String PROFILES_KEY = "profiles";
+
+ /**
+ * @since 4.4
+ */
+ public static final Metric PROFILES = new Metric.Builder(PROFILES_KEY, "Profiles", Metric.ValueType.DATA)
+ .setDescription("Details of quality profiles used during analysis")
+ .setQualitative(false)
+ .setDomain(DOMAIN_GENERAL)
+ .setHidden(true)
+ .create();
+
private static final List<Metric> METRICS;
static {
@Test
public void read_metrics_from_class_reflection() {
List<Metric> metrics = CoreMetrics.getMetrics();
- assertThat(metrics).hasSize(151);
+ assertThat(metrics).hasSize(152);
assertThat(metrics).contains(CoreMetrics.NCLOC, CoreMetrics.DIRECTORIES);
}
ALERT_STATUS = 'alert_status'
QUALITY_GATE_DETAILS = 'quality_gate_details'
PROFILE='profile'
+ PROFILES='profiles'
private