extensions.add(SizeWidget.class);
extensions.add(EventsWidget.class);
extensions.add(CustomMeasuresWidget.class);
+ extensions.add(NewCoverageWidget.class);
// chart
extensions.add(XradarChart.class);
extensions.add(ViolationPersisterDecorator.class);
extensions.add(NewViolationsDecorator.class);
extensions.add(TimeMachineConfigurationPersister.class);
- extensions.add(NewCoverageDecorator.class);
+ extensions.add(NewCoverageFileAnalyzer.class);
+ extensions.add(NewCoverageAggregator.class);
return extensions;
}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.timemachine;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+
+import java.util.Arrays;
+import java.util.List;
+
+public final class NewCoverageAggregator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesNewCoverageMetrics() {
+ return Arrays.asList(CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES,
+ CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorate(resource)) {
+ int maxPeriods = (Qualifiers.isView(resource, true) ? 3 : 5);
+ aggregate(context, CoreMetrics.NEW_LINES_TO_COVER, maxPeriods);
+ aggregate(context, CoreMetrics.NEW_UNCOVERED_LINES, maxPeriods);
+ aggregate(context, CoreMetrics.NEW_CONDITIONS_TO_COVER, maxPeriods);
+ aggregate(context, CoreMetrics.NEW_UNCOVERED_CONDITIONS, maxPeriods);
+ }
+ }
+
+ private void aggregate(DecoratorContext context, Metric metric, int maxPeriods) {
+ int[] variations = {0,0,0,0,0};
+ boolean[] hasValues = {false,false,false,false,false};
+ for (Measure child : context.getChildrenMeasures(metric)) {
+ for (int indexPeriod=1 ; indexPeriod<=maxPeriods ; indexPeriod++) {
+ Double variation = child.getVariation(indexPeriod);
+ if (variation!=null) {
+ variations[indexPeriod-1]=variations[indexPeriod-1] + variation.intValue();
+ hasValues[indexPeriod-1]=true;
+ }
+ }
+ }
+
+ if (ArrayUtils.contains(hasValues, true)) {
+ Measure measure = new Measure(metric);
+ for (int index=0 ; index<5 ; index++) {
+ if (hasValues[index]) {
+ measure.setVariation(index+1, (double)variations[index]);
+ }
+ }
+ context.saveMeasure(measure);
+ }
+ }
+
+ private boolean shouldDecorate(Resource resource) {
+ return Scopes.isHigherThan(resource, Scopes.FILE);
+ }
+}
+++ /dev/null
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.plugins.core.timemachine;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import org.apache.commons.lang.NumberUtils;
-import org.apache.commons.lang.ObjectUtils;
-import org.sonar.api.batch.Decorator;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.batch.DependedUpon;
-import org.sonar.api.batch.DependsUpon;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.Scopes;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.batch.components.PastSnapshot;
-import org.sonar.batch.components.TimeMachineConfiguration;
-
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @since 2.7
- */
-public final class NewCoverageDecorator implements Decorator {
-
- private List<PeriodStruct> structs;
-
- public NewCoverageDecorator(TimeMachineConfiguration timeMachineConfiguration) {
- structs = Lists.newArrayList();
- for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
- structs.add(new PeriodStruct(pastSnapshot));
- }
- }
-
- NewCoverageDecorator(List<PeriodStruct> structs) {
- this.structs = structs;
- }
-
-
- public boolean shouldExecuteOnProject(Project project) {
- return project.isLatestAnalysis() && !structs.isEmpty();
- }
-
- private boolean shouldDecorate(Resource resource) {
- return isFile(resource);
- }
-
- @DependsUpon
- public List<Metric> dependsOnMetrics() {
- return Arrays.asList(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, CoreMetrics.COVERAGE_LINE_HITS_DATA,
- CoreMetrics.CONDITIONS_BY_LINE, CoreMetrics.COVERED_CONDITIONS_BY_LINE);
- }
-
- @DependedUpon
- public List<Metric> generatesNewCoverageMetrics() {
- return Arrays.asList(CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES,
- CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS);
- }
-
- public void decorate(Resource resource, DecoratorContext context) {
- if (shouldDecorate(resource)) {
- doDecorate(context);
- }
- }
-
- void doDecorate(DecoratorContext context) {
- if (parse(context)) {
- compute(context);
- }
- }
-
- private boolean parse(DecoratorContext context) {
- Measure lastCommits = context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
- Measure hitsByLineMeasure = context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA);
-
- if (lastCommits != null && lastCommits.hasData() && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) {
- Map<Integer, Date> datesByLine = KeyValueFormat.parseIntDateTime(lastCommits.getData());
- Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
- Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE));
- Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE));
-
- reset();
-
- for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
- int lineId = entry.getKey();
- int hits = entry.getValue();
- int conditions = (Integer)ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
- int coveredConditions = (Integer)ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
- Date date = datesByLine.get(lineId);
- for (PeriodStruct struct : structs) {
- struct.analyze(date, hits, conditions, coveredConditions);
- }
- }
-
- return true;
- }
- return false;
- }
-
- private void reset() {
- for (PeriodStruct struct : structs) {
- struct.reset();
- }
- }
-
- private void compute(DecoratorContext context) {
- Measure newLines = new Measure(CoreMetrics.NEW_LINES_TO_COVER);
- Measure newUncoveredLines = new Measure(CoreMetrics.NEW_UNCOVERED_LINES);
- Measure newConditions = new Measure(CoreMetrics.NEW_CONDITIONS_TO_COVER);
- Measure newUncoveredConditions = new Measure(CoreMetrics.NEW_UNCOVERED_CONDITIONS);
-
- for (PeriodStruct struct : structs) {
- newLines.setVariation(struct.index, (double)struct.newLines);
- newUncoveredLines.setVariation(struct.index, (double) (struct.newLines - struct.newCoveredLines));
- newConditions.setVariation(struct.index, (double)struct.newConditions);
- newUncoveredConditions.setVariation(struct.index, (double)struct.newConditions-struct.newCoveredConditions);
- }
-
- context.saveMeasure(newLines);
- context.saveMeasure(newUncoveredLines);
- context.saveMeasure(newConditions);
- context.saveMeasure(newUncoveredConditions);
- }
-
-
- private Map<Integer, Integer> parseCountByLine(Measure measure) {
- if (measure != null && measure.hasData()) {
- return KeyValueFormat.parseIntInt(measure.getData());
- }
- return Maps.newHashMap();
- }
-
-
- private boolean isFile(Resource resource) {
- return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
- }
-
- public static final class PeriodStruct {
- int index;
- Date date;
- int newLines = 0, newCoveredLines = 0, newConditions = 0, newCoveredConditions = 0;
-
- PeriodStruct(PastSnapshot pastSnapshot) {
- this.index = pastSnapshot.getIndex();
- this.date = pastSnapshot.getDate();
- }
-
- PeriodStruct(int index, Date date) {
- this.index = index;
- this.date = date;
- }
-
- void reset() {
- newLines = 0;
- newCoveredLines = 0;
- newConditions = 0;
- newCoveredConditions = 0;
- }
-
- void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
- if (lineDate == null) {
- //TODO warning
-
- } else if (lineDate.after(date)) {
- // TODO experiment if date comparison is faster or not
- addLine(hits > 0);
- addConditions(conditions, coveredConditions);
- }
- }
-
- void addLine(boolean covered) {
- newLines += 1;
- if (covered) {
- newCoveredLines += 1;
- }
- }
-
- void addConditions(int count, int countCovered) {
- newConditions += count;
- if (count > 0) {
- newCoveredConditions += countCovered;
- }
- }
- }
-}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.timemachine;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.ObjectUtils;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.batch.components.PastSnapshot;
+import org.sonar.batch.components.TimeMachineConfiguration;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @since 2.7
+ */
+public final class NewCoverageFileAnalyzer implements Decorator {
+
+ private List<PeriodStruct> structs;
+
+ public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration) {
+ structs = Lists.newArrayList();
+ for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
+ structs.add(new PeriodStruct(pastSnapshot));
+ }
+ }
+
+ NewCoverageFileAnalyzer(List<PeriodStruct> structs) {
+ this.structs = structs;
+ }
+
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return project.isLatestAnalysis() && !structs.isEmpty();
+ }
+
+ private boolean shouldDecorate(Resource resource) {
+ return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
+ }
+
+ @DependsUpon
+ public List<Metric> dependsOnMetrics() {
+ return Arrays.asList(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, CoreMetrics.COVERAGE_LINE_HITS_DATA,
+ CoreMetrics.CONDITIONS_BY_LINE, CoreMetrics.COVERED_CONDITIONS_BY_LINE);
+ }
+
+ @DependedUpon
+ public List<Metric> generatesNewCoverageMetrics() {
+ return Arrays.asList(CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES,
+ CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorate(resource)) {
+ doDecorate(context);
+ }
+ }
+
+ void doDecorate(DecoratorContext context) {
+ if (parse(context)) {
+ compute(context);
+ }
+ }
+
+ private boolean parse(DecoratorContext context) {
+ Measure lastCommits = context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
+ Measure hitsByLineMeasure = context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA);
+
+ if (lastCommits != null && lastCommits.hasData() && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) {
+ Map<Integer, Date> datesByLine = KeyValueFormat.parseIntDateTime(lastCommits.getData());
+ Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
+ Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE));
+ Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE));
+
+ reset();
+
+ for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
+ int lineId = entry.getKey();
+ int hits = entry.getValue();
+ int conditions = (Integer)ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
+ int coveredConditions = (Integer)ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
+ Date date = datesByLine.get(lineId);
+ for (PeriodStruct struct : structs) {
+ struct.analyze(date, hits, conditions, coveredConditions);
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ private void reset() {
+ for (PeriodStruct struct : structs) {
+ struct.reset();
+ }
+ }
+
+ private void compute(DecoratorContext context) {
+ Measure newLines = new Measure(CoreMetrics.NEW_LINES_TO_COVER);
+ Measure newUncoveredLines = new Measure(CoreMetrics.NEW_UNCOVERED_LINES);
+ Measure newConditions = new Measure(CoreMetrics.NEW_CONDITIONS_TO_COVER);
+ Measure newUncoveredConditions = new Measure(CoreMetrics.NEW_UNCOVERED_CONDITIONS);
+
+ for (PeriodStruct struct : structs) {
+ newLines.setVariation(struct.index, (double)struct.newLines);
+ newUncoveredLines.setVariation(struct.index, (double) (struct.newLines - struct.newCoveredLines));
+ newConditions.setVariation(struct.index, (double)struct.newConditions);
+ newUncoveredConditions.setVariation(struct.index, (double)struct.newConditions-struct.newCoveredConditions);
+ }
+
+ context.saveMeasure(newLines);
+ context.saveMeasure(newUncoveredLines);
+ context.saveMeasure(newConditions);
+ context.saveMeasure(newUncoveredConditions);
+ }
+
+
+ private Map<Integer, Integer> parseCountByLine(Measure measure) {
+ if (measure != null && measure.hasData()) {
+ return KeyValueFormat.parseIntInt(measure.getData());
+ }
+ return Maps.newHashMap();
+ }
+
+
+ public static final class PeriodStruct {
+ int index;
+ Date date;
+ int newLines = 0, newCoveredLines = 0, newConditions = 0, newCoveredConditions = 0;
+
+ PeriodStruct(PastSnapshot pastSnapshot) {
+ this.index = pastSnapshot.getIndex();
+ this.date = pastSnapshot.getTargetDate();
+ }
+
+ PeriodStruct(int index, Date date) {
+ this.index = index;
+ this.date = date;
+ }
+
+ void reset() {
+ newLines = 0;
+ newCoveredLines = 0;
+ newConditions = 0;
+ newCoveredConditions = 0;
+ }
+
+ void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
+ if (lineDate == null) {
+ //TODO warning
+
+ } else if (lineDate.after(date)) {
+ // TODO test if string comparison is faster or not
+ addLine(hits > 0);
+ addConditions(conditions, coveredConditions);
+ }
+ }
+
+ void addLine(boolean covered) {
+ newLines += 1;
+ if (covered) {
+ newCoveredLines += 1;
+ }
+ }
+
+ void addConditions(int count, int countCovered) {
+ newConditions += count;
+ if (count > 0) {
+ newCoveredConditions += countCovered;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.widgets;
+
+import org.sonar.api.web.AbstractRubyTemplate;
+import org.sonar.api.web.Description;
+import org.sonar.api.web.RubyRailsWidget;
+import org.sonar.api.web.WidgetCategory;
+
+@WidgetCategory({"Tests"})
+@Description("TODO")
+public class NewCoverageWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "new_coverage";
+ }
+
+ public String getTitle() {
+ return "New coverage";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/core/widgets/new_coverage.html.erb";
+ }
+}
\ No newline at end of file
--- /dev/null
+<h2>New coverage</h2>
+ <table>
+ <tr>
+ <td>New lines</td>
+ <% m=measure('new_lines_to_cover') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>New uncovered</td>
+ <% m=measure('new_uncovered_lines') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>New conditions</td>
+ <% m=measure('new_conditions_to_cover') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>New uncovered conditions</td>
+ <% m=measure('new_uncovered_conditions') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ </table>
+++ /dev/null
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.plugins.core.timemachine;
-
-import org.apache.commons.lang.NumberUtils;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-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.measures.Metric;
-import org.sonar.api.utils.DateUtils;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-import static org.mockito.Mockito.*;
-
-public class NewCoverageDecoratorTest {
-
- @Test
- public void shouldDoNothingIfNoScmData() throws ParseException {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA))
- .thenReturn(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=10"));
-
- NewCoverageDecorator decorator = newDecorator();
- decorator.doDecorate(context);
- verify(context, never()).saveMeasure((Measure) anyObject());
- }
-
- @Test
- public void shouldDoNothingIfNoCoverageData() throws ParseException {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE))
- .thenReturn(new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2008-05-18T00:00:00+0000"));
-
- NewCoverageDecorator decorator = newDecorator();
- decorator.doDecorate(context);
-
- verify(context, never()).saveMeasure((Measure) anyObject());
- }
-
- @Test
- public void shouldGetNewLines() throws ParseException {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn(
- new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3"));
- when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
-
- NewCoverageDecorator decorator = newDecorator();
- decorator.doDecorate(context);
-
- // line 11 has been updated after date1 (2009-12-25). This line is covered.
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 1, 1.0)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 1, 0.0)));
-
- // no line have been updated after date3 (2011-02-18)
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 3, 0.0)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 3, 0.0)));
-
- // no data on other periods
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 2, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 4, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 5, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 2, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 4, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 5, null)));
- }
-
- @Test
- public void shouldGetNewConditions() throws ParseException {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn(
- new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3"));
- when(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.CONDITIONS_BY_LINE, "11=4"));
- when(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "11=1"));
- when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
-
- NewCoverageDecorator decorator = newDecorator();
- decorator.doDecorate(context);
-
- // line 11 has been updated after date1 (2009-12-25). This line has 1 covered condition amongst 4
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 1, 4.0)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 1, 3.0)));
-
- // no line have been updated after date3 (2011-02-18)
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 3, 0.0)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 3, 0.0)));
-
- // no data on other periods
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 2, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 4, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 5, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 2, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 4, null)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 5, null)));
- }
-
- @Test
- public void shouldNotGetNewConditionsWhenNewLineHasNoConditions() throws ParseException {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn(
- new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3"));
- when(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.CONDITIONS_BY_LINE, "10=1"));
- when(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "10=1"));
- when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
- new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
-
- NewCoverageDecorator decorator = newDecorator();
- decorator.doDecorate(context);
-
- // line 11 has been updated after date1 (2009-12-25) but it has no conditions
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 1, 0.0)));
- verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 1, 0.0)));
- }
-
-
- static class VariationMatcher extends BaseMatcher<Measure> {
- Metric metric;
- int index;
- Double variation;
-
- VariationMatcher(Metric metric, int index, Double variation) {
- this.metric = metric;
- this.index = index;
- this.variation = variation;
- }
-
- public boolean matches(Object o) {
- Measure m = (Measure)o;
- if (m.getMetric().equals(metric)) {
- if ((variation==null && m.getVariation(index)==null) ||
- (variation!=null && variation.equals(m.getVariation(index)))) {
- return true;
- }
- }
- return false;
- }
-
- public void describeTo(Description description) {
-
- }
- }
-
- private NewCoverageDecorator newDecorator() throws ParseException {
- List<NewCoverageDecorator.PeriodStruct> structs = Arrays.asList(
- new NewCoverageDecorator.PeriodStruct(1, newDate("2009-12-25")),
- new NewCoverageDecorator.PeriodStruct(3, newDate("2011-02-18")));
- return new NewCoverageDecorator(structs);
- }
-
- private Date newDate(String s) throws ParseException {
- return new SimpleDateFormat(DateUtils.DATE_FORMAT).parse(s);
- }
-}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.timemachine;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+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.measures.Metric;
+import org.sonar.api.utils.DateUtils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import static org.mockito.Mockito.*;
+
+public class NewCoverageFileAnalyzerTest {
+
+ @Test
+ public void shouldDoNothingIfNoScmData() throws ParseException {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA))
+ .thenReturn(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=10"));
+
+ NewCoverageFileAnalyzer decorator = newDecorator();
+ decorator.doDecorate(context);
+ verify(context, never()).saveMeasure((Measure) anyObject());
+ }
+
+ @Test
+ public void shouldDoNothingIfNoCoverageData() throws ParseException {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE))
+ .thenReturn(new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2008-05-18T00:00:00+0000"));
+
+ NewCoverageFileAnalyzer decorator = newDecorator();
+ decorator.doDecorate(context);
+
+ verify(context, never()).saveMeasure((Measure) anyObject());
+ }
+
+ @Test
+ public void shouldGetNewLines() throws ParseException {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn(
+ new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3"));
+ when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
+
+ NewCoverageFileAnalyzer decorator = newDecorator();
+ decorator.doDecorate(context);
+
+ // line 11 has been updated after date1 (2009-12-25). This line is covered.
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 1, 1.0)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 1, 0.0)));
+
+ // no line have been updated after date3 (2011-02-18)
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 3, 0.0)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 3, 0.0)));
+
+ // no data on other periods
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 2, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 4, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 5, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 2, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 4, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 5, null)));
+ }
+
+ @Test
+ public void shouldGetNewConditions() throws ParseException {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn(
+ new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3"));
+ when(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.CONDITIONS_BY_LINE, "11=4"));
+ when(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "11=1"));
+ when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
+
+ NewCoverageFileAnalyzer decorator = newDecorator();
+ decorator.doDecorate(context);
+
+ // line 11 has been updated after date1 (2009-12-25). This line has 1 covered condition amongst 4
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 1, 4.0)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 1, 3.0)));
+
+ // no line have been updated after date3 (2011-02-18)
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 3, 0.0)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 3, 0.0)));
+
+ // no data on other periods
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 2, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 4, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 5, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 2, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 4, null)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 5, null)));
+ }
+
+ @Test
+ public void shouldNotGetNewConditionsWhenNewLineHasNoConditions() throws ParseException {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn(
+ new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3"));
+ when(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.CONDITIONS_BY_LINE, "10=1"));
+ when(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "10=1"));
+ when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
+ new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
+
+ NewCoverageFileAnalyzer decorator = newDecorator();
+ decorator.doDecorate(context);
+
+ // line 11 has been updated after date1 (2009-12-25) but it has no conditions
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 1, 0.0)));
+ verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 1, 0.0)));
+ }
+
+
+ static class VariationMatcher extends BaseMatcher<Measure> {
+ Metric metric;
+ int index;
+ Double variation;
+
+ VariationMatcher(Metric metric, int index, Double variation) {
+ this.metric = metric;
+ this.index = index;
+ this.variation = variation;
+ }
+
+ public boolean matches(Object o) {
+ Measure m = (Measure)o;
+ if (m.getMetric().equals(metric)) {
+ if ((variation==null && m.getVariation(index)==null) ||
+ (variation!=null && variation.equals(m.getVariation(index)))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void describeTo(Description description) {
+
+ }
+ }
+
+ private NewCoverageFileAnalyzer newDecorator() throws ParseException {
+ List<NewCoverageFileAnalyzer.PeriodStruct> structs = Arrays.asList(
+ new NewCoverageFileAnalyzer.PeriodStruct(1, newDate("2009-12-25")),
+ new NewCoverageFileAnalyzer.PeriodStruct(3, newDate("2011-02-18")));
+ return new NewCoverageFileAnalyzer(structs);
+ }
+
+ private Date newDate(String s) throws ParseException {
+ return new SimpleDateFormat(DateUtils.DATE_FORMAT).parse(s);
+ }
+}
--- /dev/null
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar 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.
+#
+# Sonar 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 Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+#
+class BrowseController < ApplicationController
+
+ SECTION=Navigation::SECTION_RESOURCE
+
+ def index
+ @resource = Project.by_key(params[:id])
+
+ if (@resource && has_role?(:user, @resource))
+ @snapshot=@resource.last_snapshot
+ render_resource()
+ else
+ access_denied
+ end
+ end
+
+ private
+
+
+ def render_resource
+ @source = @snapshot.source
+ if @source
+ source_lines=Java::OrgSonarServerUi::JRubyFacade.new.colorizeCode(@source.data, @snapshot.project.language).split("\n")
+ load_scm()
+
+ @lines=[]
+ current_revision=nil
+ source_lines.each_with_index do |source, index|
+ line=Line.new(source)
+ @lines<<line
+
+ line.revision=@revisions_by_line[index+1]
+ line.author=@authors_by_line[index+1]
+ line.date=@dates_by_line[index+1]
+
+ if line.revision
+ if current_revision!=line.revision
+ current_revision=line.revision
+ line.display_scm=true
+ end
+ end
+ end
+ end
+
+ if (params[:tab]=='violations')
+ load_violations_tab()
+ elsif (params[:tab]=='coverage')
+ load_coverage_tab()
+ else
+ load_source_tab()
+ end
+
+ params[:layout]='false'
+ end
+
+ def load_scm()
+ @scm_available=(@snapshot.measure('last_commit_datetimes_by_line')!=nil)
+ @display_scm=(params[:scm]=='true')
+ if @display_scm
+ @authors_by_line=load_distribution('authors_by_line')
+ @revisions_by_line=load_distribution('revisions_by_line')
+ @dates_by_line=load_distribution('last_commit_datetimes_by_line')
+ else
+ @authors_by_line={}
+ @revisions_by_line={}
+ @dates_by_line={}
+ end
+ end
+
+ def load_distribution(metric_key)
+ m=@snapshot.measure(metric_key)
+ m ? m.data_as_line_distribution() : {}
+ end
+
+ def load_coverage_tab
+ @display_coverage=true
+ @hits_by_line=load_distribution('coverage_line_hits_data')
+ @conditions_by_line=load_distribution('conditions_by_line')
+ @covered_conditions_by_line=load_distribution('covered_conditions_by_line')
+
+ @hits_by_line.each_pair do |line_id,hits|
+ line=@lines[line_id-1]
+ if line
+ line.hits=hits.to_i
+ line.conditions=@conditions_by_line[line_id].to_i
+ line.covered_conditions=@covered_conditions_by_line[line_id].to_i
+ end
+ end
+ end
+
+ def load_violations_tab
+ @display_violations=true
+ @global_violations=[]
+
+ conditions='snapshot_id=?'
+ values=[@snapshot.id]
+ unless params[:rule].blank?
+ if params[:rule].include?(':')
+ rule=Rule.by_key_or_id(params[:rule])
+ conditions += ' AND rule_id=?'
+ values<<(rule ? rule.id : -1)
+ else
+ # severity
+ conditions += ' AND failure_level=?'
+ values<<params[:rule].to_i
+ end
+ end
+
+ unless params[:date].blank?
+ conditions+=' AND created_at>=?'
+ values<<Date::strptime(params[:date])
+ end
+
+ RuleFailure.find(:all, :include => 'rule', :conditions => [conditions] + values, :order => 'failure_level DESC').each do |violation|
+ # sorted by severity => from blocker to info
+ if violation.line && violation.line>0
+ @lines[violation.line-1].add_violation(violation)
+ else
+ @global_violations<<violation
+ end
+ end
+ end
+
+ def load_source_tab
+
+ end
+
+ class Line
+ attr_accessor :source, :revision, :author, :date, :display_scm, :violations, :hits, :conditions, :covered_conditions
+
+ def initialize(source)
+ @source=source
+ @display_scm=false
+ end
+
+ def add_violation(violation)
+ @violations||=[]
+ @violations<<violation
+ @visible=true
+ end
+
+ def violations?
+ @violations && @violations.size>0
+ end
+
+ def violation_severity
+ if @violations && @violations.size>0
+ @violations[0].failure_level
+ else
+ nil
+ end
+ end
+ end
+end
\ No newline at end of file
--- /dev/null
+<img src="<%= ApplicationController.root_context -%>/images/priority/<%=violation.failure_level-%>.png"/>
+
+<span class="rulename"><a onclick="window.open(this.href,'rule','height=800,width=900,scrollbars=1,resizable=1');return false;" href="<%= url_for :controller => 'rules', :action => 'show', :id => violation.rule.key, :layout => 'false' -%>"><%= h(violation.rule.name) -%></a></span>
+ ยป
+<%= h(violation.message) -%>
\ No newline at end of file
--- /dev/null
+<style>
+#source_metrics {
+ width: 100%;
+}
+#source_metrics {
+ width: 100%;
+}
+.sources2 {
+ width: 100%;
+ border: 1px solid #DDD;
+ margin: 0;
+}
+.sources2 td.lid {
+ background-color: #ECECEC;
+ border-right: 1px solid #DDDDDD;
+ border-left: 1px solid #DDDDDD;
+ text-align: right;
+ padding: 2px 0.5em 0 0.5em;
+ vertical-align: top;
+ font-size: 11px;
+}
+.sources2 td.lid a {
+ text-decoration: none;
+ color: #AAA;
+}
+.sources2 td.scm {
+ border-right: 1px solid #DDD;
+ background-color: #ECECEC;
+}
+.sources2 td.revision {
+ border-top: 1px solid #DDD;
+ vertical-align: top;
+ padding: 2px 0.3em;
+ white-space: nowrap;
+}
+.sources2 span.date, .sources2 span.date a {
+ color: #AAA;
+ font-size: 11px;
+ text-decoration: none;
+}
+.sources2 span.author, .sources2 span.author a {
+ font-size: 11px;
+}
+.sources2 td.rule {
+ padding: 1px 0.5em;
+}
+.sources2 td.violations {
+ background-color: #ECECEC;
+ min-width: 400px;
+}
+span.rulename, span.rulename a {
+ color: #4183C4;
+ text-decoration: none;
+}
+span.rulename a:hover {
+ text-decoration: underline;
+}
+.sources2 td.violations img {
+ vertical-align: sub;
+}
+.sources2 td.line {
+ padding-left: 1em;
+ width: 100%;
+ white-space: pre;
+ font-size: 12px;
+ font-family: monospace;
+}
+.sources2 td.section {
+ border-top: 1px solid #DDD;
+ border-bottom: 1px solid #DDD;
+}
+.sources2 td.ind {
+ border-right: 1px solid #DDD;
+ min-width: 1.5em;
+ padding: 0 0.3em;
+ text-align: center;
+ vertical-align: middle;
+}
+.sources2 td.ok {
+ background-color: #ACE97C;
+ border-top: 1px solid #6EC563;
+ border-bottom: 1px solid #6EC563;
+}
+.sources2 td.sev0, .sources2 td.sev1, .sources2 td.warn {
+ /* info, minor */
+ background-color: #FFF6BF;
+ border-top: 1px solid #FFD324;
+ border-bottom: 1px solid #FFD324;
+}
+.sources2 td.sev2, .sources2 td.sev3, .sources2 td.sev4, .sources2 td.ko {
+ /* major, critical, blocker */
+ background-color: #FF9090;
+ border-top: 1px solid #FF5252;
+ border-bottom: 1px solid #FF5252;
+}
+
+#source_title {
+ padding: 10px 0;
+}
+#source_title span.h1 {
+ font-size: 16px;
+ margin-right: 10px;
+}
+.source_links {
+ font-size: 11px;
+}
+#global_violations {
+ width: 100%;
+ border: 1px solid #DDD;
+ margin-bottom: 10px;
+}
+#global_violations td {
+ background-color: #ECECEC;
+ padding: 3px 0.5em;
+}
+#global_violations td img, #source_title img {
+ vertical-align: sub;
+}
+</style>
+
+<div id="source_title">
+ <span class="h1"><%= qualifier_icon(@resource) -%> <%= @resource.long_name -%></span>
+ <% if @lines %>
+ | <span class="source_link"><a href="<%= ApplicationController.root_context -%>/api/sources?resource=<%= @resource.key -%>&format=txt" target="raw">raw</a></span>
+ <% end %>
+</div>
+<table id="source_metrics" cellpadding="0" cellspacing="0" border="0">
+
+</table>
+
+<% if @display_violations && @global_violations && @global_violations.size>0 -%>
+ <table id="global_violations" cellpadding="0" cellspacing="0" border="0">
+ <% @global_violations.each do |violation| %>
+ <tr>
+ <td><%= render :partial => 'violation', :locals => {:violation => violation} -%></td>
+ </tr>
+ <% end %>
+ </table>
+<% end %>
+
+<% if @lines && @lines.size>0 %>
+<table id="sources" class="sources2 code" cellpadding="0" cellspacing="0" border="0">
+ <%
+ @lines.each_with_index do |line, index|
+ status=hits_status=conditions_status=''
+ if @display_coverage && line.hits
+ hits_status=(line.hits>0 ? 'ok' : 'ko')
+ if line.conditions && line.conditions>0 && line.covered_conditions
+ if line.covered_conditions==0
+ status='ko'
+ conditions_status='ko'
+ elsif line.covered_conditions==line.conditions
+ status=''
+ conditions_status='ok'
+ else
+ conditions_status='warn'
+ status='warn'
+ end
+ elsif line.hits
+ status=(line.hits>0 ? '' : 'ko')
+ end
+ elsif @display_violations
+ status="sev#{line.violation_severity}"
+ end
+ %>
+ <tr>
+ <% if @display_scm
+ if line.display_scm
+ title = "Revision #{h(line.revision)} (#{line.date})"
+ %>
+ <td class="scm revision"><span class="date"><a href="#" title="<%= title -%>" alt="<%= title -%>"><%= line.date[0..9] -%></a></span> <span class="author"><%= h(line.author) -%></span></td>
+ <% else %>
+ <td class="scm"></td>
+ <% end
+ end %>
+ <% if @display_violations %>
+ <td class="rule <%= 'violations section' if line.violations? -%>">
+ <% if line.violations?
+ line.violations.each_with_index do |violation, violation_index| %>
+ <%= '<br/>' if violation_index>0 %>
+ <%= render :partial => 'violation', :locals => {:violation => violation} -%>
+ <%
+ end
+ end
+ %>
+ </td>
+ <% end %>
+ <td class="lid <%= ' section' if line.violations? -%>" id="L<%= index+1 -%>"><a name="L<%= index+1 -%>" href="#L<%= index+1 -%>"><%= index + 1 -%></a></td>
+
+ <% if @display_coverage %>
+ <td class="ind <%= hits_status -%>"><%= line.hits -%></td>
+ <td class="ind <%= conditions_status -%>"><% if line.conditions && line.conditions>0 %><%= line.covered_conditions -%>/<%= line.conditions -%><% end %></td>
+ <% end %>
+ <td class="line <%= status -%>"><%= line.source -%></td>
+ </tr>
+ <% end %>
+</table>
+<% end %>
\ No newline at end of file