@@ -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) |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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\"}]"))); | |||
} | |||
} |
@@ -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\"}]"))); | |||
} | |||
} |
@@ -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\"}]"); | |||
} | |||
} |
@@ -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. |
@@ -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 { |
@@ -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); | |||
} | |||
@@ -378,6 +378,7 @@ class Metric < ActiveRecord::Base | |||
ALERT_STATUS = 'alert_status' | |||
QUALITY_GATE_DETAILS = 'quality_gate_details' | |||
PROFILE='profile' | |||
PROFILES='profiles' | |||
private | |||