Pārlūkot izejas kodu

SONAR-5216 Store and display used quality profiles for multi-language analysis

tags/4.4-RC1
Julien HENRY pirms 10 gadiem
vecāks
revīzija
0bab6ec48f

+ 23
- 3
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb Parādīt failu

@@ -15,15 +15,35 @@
<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)

+ 59
- 0
sonar-batch/src/main/java/org/sonar/batch/rule/QProfileDecorator.java Parādīt failu

@@ -0,0 +1,59 @@
/*
* 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();
}

}

+ 20
- 11
sonar-batch/src/main/java/org/sonar/batch/rule/QProfileSensor.java Parādīt failu

@@ -19,7 +19,7 @@
*/
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;
@@ -28,6 +28,8 @@ import org.sonar.api.measures.Measure;
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.
*/
@@ -44,25 +46,32 @@ public class QProfileSensor implements Sensor {
}

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);
}
}


+ 119
- 0
sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java Parādīt failu

@@ -0,0 +1,119 @@
/*
* 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;
}

}

+ 26
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java Parādīt failu

@@ -28,7 +28,12 @@ import org.sonar.api.batch.rule.CheckFactory;
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;
@@ -52,8 +57,25 @@ import org.sonar.batch.phases.PhasesTimeProfiler;
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;
@@ -134,6 +156,7 @@ public class ModuleScanContainer extends ComponentContainer {
new ActiveRulesProvider(),
new RulesProfileProvider(),
QProfileSensor.class,
QProfileDecorator.class,
CheckFactory.class,

// report

+ 103
- 0
sonar-batch/src/test/java/org/sonar/batch/rule/QProfileDecoratorTest.java Parādīt failu

@@ -0,0 +1,103 @@
/*
* 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\"}]")));
}
}

+ 26
- 4
sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java Parādīt failu

@@ -31,7 +31,11 @@ import org.sonar.core.qualityprofile.db.QualityProfileDao;
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 {

@@ -76,9 +80,6 @@ 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
@@ -97,5 +98,26 @@ public class QProfileSensorTest extends AbstractDaoTestCase {

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\"}]")));
}
}

+ 72
- 0
sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java Parādīt failu

@@ -0,0 +1,72 @@
/*
* 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\"}]");
}

}

+ 1
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Parādīt failu

@@ -1053,6 +1053,7 @@ widget.description.description=Displays general project information.
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.

+ 27
- 1
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java Parādīt failu

@@ -2216,7 +2216,15 @@ public final class CoreMetrics {
.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)
@@ -2224,12 +2232,15 @@ public final class CoreMetrics {

/**
* @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)
@@ -2237,6 +2248,21 @@ public final class CoreMetrics {
.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 {

+ 1
- 1
sonar-plugin-api/src/test/java/org/sonar/api/resources/CoreMetricsTest.java Parādīt failu

@@ -32,7 +32,7 @@ public class CoreMetricsTest {
@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);
}


+ 1
- 0
sonar-server/src/main/webapp/WEB-INF/app/models/metric.rb Parādīt failu

@@ -378,6 +378,7 @@ class Metric < ActiveRecord::Base
ALERT_STATUS = 'alert_status'
QUALITY_GATE_DETAILS = 'quality_gate_details'
PROFILE='profile'
PROFILES='profiles'

private


Notiek ielāde…
Atcelt
Saglabāt