diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-05-27 09:38:17 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-05-28 09:29:03 +0200 |
commit | 57c01be1943a882a5a2fe0cc0fecab2fe75a861a (patch) | |
tree | 3d1240ef670ddedd00cf6b0450e72ef418cbc78e /sonar-batch/src | |
parent | d228fadfa0c0cc7284d4920c9bb485049ff3f68f (diff) | |
download | sonarqube-57c01be1943a882a5a2fe0cc0fecab2fe75a861a.tar.gz sonarqube-57c01be1943a882a5a2fe0cc0fecab2fe75a861a.zip |
SONAR-6370 move batch extensions out of core plugin
Decorators temporarily moved to package org.sonar.batch.compute of sonar-batch.
Diffstat (limited to 'sonar-batch/src')
62 files changed, 5406 insertions, 3 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index c584274eaf5..ba42319b7bd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -23,6 +23,29 @@ import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.compute.ApplyProjectRolesDecorator; +import org.sonar.batch.compute.BranchCoverageDecorator; +import org.sonar.batch.compute.CommentDensityDecorator; +import org.sonar.batch.compute.CountFalsePositivesDecorator; +import org.sonar.batch.compute.CountUnresolvedIssuesDecorator; +import org.sonar.batch.compute.CoverageDecorator; +import org.sonar.batch.compute.DirectoriesDecorator; +import org.sonar.batch.compute.FilesDecorator; +import org.sonar.batch.compute.ItBranchCoverageDecorator; +import org.sonar.batch.compute.ItCoverageDecorator; +import org.sonar.batch.compute.ItLineCoverageDecorator; +import org.sonar.batch.compute.LineCoverageDecorator; +import org.sonar.batch.compute.ManualMeasureDecorator; +import org.sonar.batch.compute.NewCoverageAggregator; +import org.sonar.batch.compute.NewCoverageFileAnalyzer; +import org.sonar.batch.compute.NewItCoverageFileAnalyzer; +import org.sonar.batch.compute.NewOverallCoverageFileAnalyzer; +import org.sonar.batch.compute.OverallBranchCoverageDecorator; +import org.sonar.batch.compute.OverallCoverageDecorator; +import org.sonar.batch.compute.OverallLineCoverageDecorator; +import org.sonar.batch.compute.TimeMachineConfigurationPersister; +import org.sonar.batch.compute.UnitTestDecorator; +import org.sonar.batch.compute.VariationDecorator; import org.sonar.batch.cpd.CpdComponents; import org.sonar.batch.debt.DebtDecorator; import org.sonar.batch.debt.IssueChangelogDebtCalculator; @@ -103,8 +126,31 @@ public class BatchComponents { IssueHandlers.class, InitialOpenIssuesSensor.class, + // to be moved to compute engine QProfileEventsDecorator.class, - + CountUnresolvedIssuesDecorator.class, + CountFalsePositivesDecorator.class, + UnitTestDecorator.class, + LineCoverageDecorator.class, + CoverageDecorator.class, + BranchCoverageDecorator.class, + ItLineCoverageDecorator.class, + ItCoverageDecorator.class, + ItBranchCoverageDecorator.class, + OverallLineCoverageDecorator.class, + OverallCoverageDecorator.class, + OverallBranchCoverageDecorator.class, + ApplyProjectRolesDecorator.class, + CommentDensityDecorator.class, + DirectoriesDecorator.class, + FilesDecorator.class, + ManualMeasureDecorator.class, + VariationDecorator.class, + TimeMachineConfigurationPersister.class, + NewCoverageFileAnalyzer.class, + NewItCoverageFileAnalyzer.class, + NewOverallCoverageFileAnalyzer.class, + NewCoverageAggregator.class, TimeMachineConfiguration.class ); components.addAll(CorePropertyDefinitions.all()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractCoverageDecorator.java new file mode 100644 index 00000000000..4960f991ea2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractCoverageDecorator.java @@ -0,0 +1,102 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.Collection; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; + +public abstract class AbstractCoverageDecorator implements Decorator { + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependedUpon + public Collection<Metric> generatedMetrics() { + return Arrays.asList(getGeneratedMetric(), getGeneratedMetricForNewCode()); + } + + @Override + public void decorate(final Resource resource, final DecoratorContext context) { + if (shouldDecorate(resource)) { + computeMeasure(context); + computeMeasureForNewCode(context); + } + } + + protected boolean shouldDecorate(final Resource resource) { + return !ResourceUtils.isUnitTestFile(resource); + } + + private void computeMeasure(DecoratorContext context) { + if (context.getMeasure(getGeneratedMetric()) == null) { + Long elements = countElements(context); + if (elements != null && elements > 0L) { + Long coveredElements = countCoveredElements(context); + context.saveMeasure(getGeneratedMetric(), calculateCoverage(coveredElements, elements)); + } + } + } + + private void computeMeasureForNewCode(DecoratorContext context) { + if (context.getMeasure(getGeneratedMetricForNewCode()) == null) { + Measure measure = new Measure(getGeneratedMetricForNewCode()); + boolean hasValue = false; + /* TODO remove this magic number */ + for (int periodIndex = 1; periodIndex <= 5; periodIndex++) { + Long elements = countElementsForNewCode(context, periodIndex); + + if (elements != null && elements > 0L) { + long coveredElements = countCoveredElementsForNewCode(context, periodIndex); + measure.setVariation(periodIndex, calculateCoverage(coveredElements, elements)); + hasValue = true; + } + } + if (hasValue) { + context.saveMeasure(measure); + } + } + } + + private double calculateCoverage(final long coveredLines, final long lines) { + return (100.0 * coveredLines) / lines; + } + + protected abstract Metric getGeneratedMetric(); + + protected abstract Long countElements(DecoratorContext context); + + protected abstract long countCoveredElements(DecoratorContext context); + + protected abstract Metric getGeneratedMetricForNewCode(); + + protected abstract Long countElementsForNewCode(DecoratorContext context, int periodIndex); + + protected abstract long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java b/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java new file mode 100644 index 00000000000..446c0820244 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java @@ -0,0 +1,262 @@ +/* + * 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.compute; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.lang.ObjectUtils; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.batch.RequiresDB; +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.Period; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.batch.protocol.output.BatchReport.Changesets.Changeset; +import org.sonar.batch.protocol.output.BatchReportReader; +import org.sonar.batch.report.ReportPublisher; + +/** + * @since 2.7 + */ +@RequiresDB +@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) +public abstract class AbstractNewCoverageFileAnalyzer implements Decorator { + + private final List<PeriodStruct> structs; + private final ReportPublisher publishReportJob; + private final BatchComponentCache resourceCache; + + public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, BatchComponentCache resourceCache) { + this(new ArrayList<PeriodStruct>(), publishReportJob, resourceCache); + for (Period period : timeMachineConfiguration.periods()) { + structs.add(new PeriodStruct(period.getIndex(), period.getDate())); + } + } + + AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs, ReportPublisher publishReportJob, BatchComponentCache resourceCache) { + this.resourceCache = resourceCache; + this.publishReportJob = publishReportJob; + this.structs = structs; + } + + public abstract Metric getCoverageLineHitsDataMetric(); + + public abstract Metric getConditionsByLineMetric(); + + public abstract Metric getCoveredConditionsByLineMetric(); + + public abstract Metric getNewLinesToCoverMetric(); + + public abstract Metric getNewUncoveredLinesMetric(); + + public abstract Metric getNewConditionsToCoverMetric(); + + public abstract Metric getNewUncoveredConditionsMetric(); + + @Override + public boolean shouldExecuteOnProject(Project project) { + return !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(getCoverageLineHitsDataMetric(), getConditionsByLineMetric(), getCoveredConditionsByLineMetric()); + } + + @DependedUpon + public List<Metric> generatesNewCoverageMetrics() { + return Arrays.asList(getNewLinesToCoverMetric(), getNewUncoveredLinesMetric(), getNewConditionsToCoverMetric(), getNewUncoveredConditionsMetric()); + } + + @Override + 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) { + BatchReportReader reader = new BatchReportReader(publishReportJob.getReportDir()); + BatchReport.Changesets componentScm = reader.readChangesets(resourceCache.get(context.getResource()).batchId()); + Measure hitsByLineMeasure = context.getMeasure(getCoverageLineHitsDataMetric()); + + if (componentScm != null && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) { + Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure); + Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(getConditionsByLineMetric())); + Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(getCoveredConditionsByLineMetric())); + + 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); + Changeset changeset = componentScm.getChangeset(componentScm.getChangesetIndexByLine(lineId - 1)); + Date date = changeset.hasDate() ? new Date(changeset.getDate()) : null; + 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(getNewLinesToCoverMetric()); + Measure newUncoveredLines = new Measure(getNewUncoveredLinesMetric()); + Measure newConditions = new Measure(getNewConditionsToCoverMetric()); + Measure newUncoveredConditions = new Measure(getNewUncoveredConditionsMetric()); + + for (PeriodStruct struct : structs) { + if (struct.hasNewCode()) { + newLines.setVariation(struct.index, (double) struct.getNewLines()); + newUncoveredLines.setVariation(struct.index, (double) (struct.getNewLines() - struct.getNewCoveredLines())); + newConditions.setVariation(struct.index, (double) struct.getNewConditions()); + newUncoveredConditions.setVariation(struct.index, (double) struct.getNewConditions() - struct.getNewCoveredConditions()); + } + } + + context.saveMeasure(newLines); + context.saveMeasure(newUncoveredLines); + context.saveMeasure(newConditions); + context.saveMeasure(newUncoveredConditions); + } + + private Map<Integer, Integer> parseCountByLine(@Nullable Measure measure) { + if (measure != null && measure.hasData()) { + return KeyValueFormat.parseIntInt(measure.getData()); + } + return new HashMap<>(); + } + + public static final class PeriodStruct { + int index; + Date date; + Integer newLines; + Integer newCoveredLines; + Integer newConditions; + Integer newCoveredConditions; + + PeriodStruct(int index, @Nullable Date date) { + this.index = index; + this.date = date; + } + + void reset() { + newLines = null; + newCoveredLines = null; + newConditions = null; + newCoveredConditions = null; + } + + void analyze(@Nullable Date lineDate, int hits, int conditions, int coveredConditions) { + if (lineDate == null) { + // TODO warning + + } else if (date == null || lineDate.after(date)) { + // TODO test if string comparison is faster or not + addLine(hits > 0); + addConditions(conditions, coveredConditions); + } + } + + void addLine(boolean covered) { + if (newLines == null) { + newLines = 0; + } + newLines += 1; + if (covered) { + if (newCoveredLines == null) { + newCoveredLines = 0; + } + newCoveredLines += 1; + } + } + + void addConditions(int count, int countCovered) { + if (newConditions == null) { + newConditions = 0; + } + newConditions += count; + if (count > 0) { + if (newCoveredConditions == null) { + newCoveredConditions = 0; + } + newCoveredConditions += countCovered; + } + } + + boolean hasNewCode() { + return newLines != null; + } + + public int getNewLines() { + return newLines != null ? newLines : 0; + } + + public int getNewCoveredLines() { + return newCoveredLines != null ? newCoveredLines : 0; + } + + public int getNewConditions() { + return newConditions != null ? newConditions : 0; + } + + public int getNewCoveredConditions() { + return newCoveredConditions != null ? newCoveredConditions : 0; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/ApplyProjectRolesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/ApplyProjectRolesDecorator.java new file mode 100644 index 00000000000..8861e1312f8 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/ApplyProjectRolesDecorator.java @@ -0,0 +1,60 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.RequiresDB; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Resource; +import org.sonar.api.security.ResourcePermissions; + +@RequiresDB +public class ApplyProjectRolesDecorator implements Decorator { + + private static final Set<String> QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.VIEW, Qualifiers.SUBVIEW); + private final ResourcePermissions resourcePermissions; + + public ApplyProjectRolesDecorator(ResourcePermissions resourcePermissions) { + this.resourcePermissions = resourcePermissions; + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + if (shouldDecorateResource(resource)) { + LoggerFactory.getLogger(ApplyProjectRolesDecorator.class).info("Grant default permissions to {}", resource.getKey()); + resourcePermissions.grantDefaultRoles(resource); + } + } + + private boolean shouldDecorateResource(Resource resource) { + return resource.getId() != null && QUALIFIERS.contains(resource.getQualifier()) && !resourcePermissions.hasRoles(resource); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/BranchCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/BranchCoverageDecorator.java new file mode 100644 index 00000000000..637ea6eea44 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/BranchCoverageDecorator.java @@ -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.compute; + +import java.util.Collection; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.batch.sensor.coverage.CoverageConstants; + +public final class BranchCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public Collection<Metric> dependsUponMetrics() { + return CoverageConstants.BRANCH_COVERAGE_METRICS; + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.BRANCH_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + return MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0L); + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredConditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0L); + + return conditions - uncoveredConditions; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_BRANCH_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + return MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_CONDITIONS_TO_COVER), periodIndex); + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long uncoveredConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_UNCOVERED_CONDITIONS), periodIndex, 0L); + long conditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_CONDITIONS_TO_COVER), periodIndex, 0L); + + return conditions - uncoveredConditions; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/CommentDensityDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/CommentDensityDecorator.java new file mode 100644 index 00000000000..0379552ffad --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/CommentDensityDecorator.java @@ -0,0 +1,90 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.List; +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.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; + +public class CommentDensityDecorator implements Decorator { + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return Arrays.<Metric>asList(CoreMetrics.NCLOC, CoreMetrics.COMMENT_LINES, CoreMetrics.PUBLIC_API, CoreMetrics.PUBLIC_UNDOCUMENTED_API); + } + + @DependedUpon + public List<Metric> generatesMetrics() { + return Arrays.<Metric>asList(CoreMetrics.COMMENT_LINES_DENSITY, CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY); + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + saveCommentsDensity(context); + savePublicApiDensity(context); + } + + private void saveCommentsDensity(DecoratorContext context) { + if (context.getMeasure(CoreMetrics.COMMENT_LINES_DENSITY) != null) { + return; + } + + Measure ncloc = context.getMeasure(CoreMetrics.NCLOC); + Measure comments = context.getMeasure(CoreMetrics.COMMENT_LINES); + if (MeasureUtils.hasValue(ncloc) && MeasureUtils.hasValue(comments) && (comments.getValue() + ncloc.getValue()) > 0) { + double val = 100.0 * (comments.getValue() / (comments.getValue() + ncloc.getValue())); + context.saveMeasure(new Measure(CoreMetrics.COMMENT_LINES_DENSITY, val)); + } + } + + private void savePublicApiDensity(DecoratorContext context) { + if (context.getMeasure(CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY) != null) { + return; + } + + Measure publicApi = context.getMeasure(CoreMetrics.PUBLIC_API); + Measure publicUndocApi = context.getMeasure(CoreMetrics.PUBLIC_UNDOCUMENTED_API); + + if (MeasureUtils.hasValue(publicApi) && MeasureUtils.hasValue(publicUndocApi) && publicApi.getValue() > 0) { + double documentedAPI = publicApi.getValue() - publicUndocApi.getValue(); + Double value = 100.0 * (documentedAPI / publicApi.getValue()); + context.saveMeasure(new Measure(CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY, value)); + } + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/CountFalsePositivesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/CountFalsePositivesDecorator.java new file mode 100644 index 00000000000..1eb5456457d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/CountFalsePositivesDecorator.java @@ -0,0 +1,86 @@ +/* + * 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.compute; + +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.issue.Issue; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; + +/** + * Computes the number of false-positives + * + * @since 3.6 + */ +@DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING) +public class CountFalsePositivesDecorator implements Decorator { + + private final ResourcePerspectives perspectives; + + public CountFalsePositivesDecorator(ResourcePerspectives perspectives) { + this.perspectives = perspectives; + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependedUpon + public Metric generatesFalsePositiveMeasure() { + return CoreMetrics.FALSE_POSITIVE_ISSUES; + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + Issuable issuable = perspectives.as(Issuable.class, resource); + if (issuable != null) { + int falsePositives = 0; + for (Issue issue : issuable.resolvedIssues()) { + if (Issue.RESOLUTION_FALSE_POSITIVE.equals(issue.resolution())) { + falsePositives++; + } + } + saveMeasure(context, CoreMetrics.FALSE_POSITIVE_ISSUES, falsePositives); + } + } + + private void saveMeasure(DecoratorContext context, Metric metric, int value) { + context.saveMeasure(metric, (double) (value + sumChildren(context, metric))); + } + + private int sumChildren(DecoratorContext context, Metric metric) { + return MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue(); + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java new file mode 100644 index 00000000000..cd57819911a --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java @@ -0,0 +1,304 @@ +/* + * 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.compute; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; +import org.apache.commons.lang.time.DateUtils; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.batch.RequiresDB; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.issue.Issue; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.RuleMeasure; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RulePriority; +import org.sonar.batch.components.Period; +import org.sonar.batch.components.TimeMachineConfiguration; + +/** + * Computes metrics related to number of issues. + * + * @since 3.6 + */ +@DependsUpon(DecoratorBarriers.ISSUES_TRACKED) +@RequiresDB +public class CountUnresolvedIssuesDecorator implements Decorator { + + private final ResourcePerspectives perspectives; + private final TimeMachineConfiguration timeMachineConfiguration; + + public CountUnresolvedIssuesDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration) { + this.perspectives = perspectives; + this.timeMachineConfiguration = timeMachineConfiguration; + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependedUpon + public List<Metric> generatesIssuesMetrics() { + return ImmutableList.<Metric>of( + CoreMetrics.VIOLATIONS, + CoreMetrics.BLOCKER_VIOLATIONS, + CoreMetrics.CRITICAL_VIOLATIONS, + CoreMetrics.MAJOR_VIOLATIONS, + CoreMetrics.MINOR_VIOLATIONS, + CoreMetrics.INFO_VIOLATIONS, + CoreMetrics.NEW_VIOLATIONS, + CoreMetrics.NEW_BLOCKER_VIOLATIONS, + CoreMetrics.NEW_CRITICAL_VIOLATIONS, + CoreMetrics.NEW_MAJOR_VIOLATIONS, + CoreMetrics.NEW_MINOR_VIOLATIONS, + CoreMetrics.NEW_INFO_VIOLATIONS, + CoreMetrics.OPEN_ISSUES, + CoreMetrics.REOPENED_ISSUES, + CoreMetrics.CONFIRMED_ISSUES + ); + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + Issuable issuable = perspectives.as(Issuable.class, resource); + if (issuable != null) { + Collection<Issue> issues = issuable.issues(); + boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context); + + Multiset<RulePriority> severityBag = HashMultiset.create(); + Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity = Maps.newHashMap(); + ListMultimap<RulePriority, Issue> issuesPerSeverity = ArrayListMultimap.create(); + int countOpen = 0; + int countReopened = 0; + int countConfirmed = 0; + + for (Issue issue : issues) { + severityBag.add(RulePriority.valueOf(issue.severity())); + Multiset<RuleKey> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity())); + rulesBag.add(issue.ruleKey()); + issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue); + + if (Issue.STATUS_OPEN.equals(issue.status())) { + countOpen++; + } else if (Issue.STATUS_REOPENED.equals(issue.status())) { + countReopened++; + } else if (Issue.STATUS_CONFIRMED.equals(issue.status())) { + countConfirmed++; + } + } + + for (RulePriority ruleSeverity : RulePriority.values()) { + saveIssuesForSeverity(context, ruleSeverity, severityBag); + saveIssuesPerRules(context, ruleSeverity, rulesPerSeverity); + saveNewIssuesForSeverity(context, ruleSeverity, issuesPerSeverity, shouldSaveNewMetrics); + saveNewIssuesPerRule(context, ruleSeverity, issues, shouldSaveNewMetrics); + } + + saveTotalIssues(context, issues); + saveNewIssues(context, issues, shouldSaveNewMetrics); + + saveMeasure(context, CoreMetrics.OPEN_ISSUES, countOpen); + saveMeasure(context, CoreMetrics.REOPENED_ISSUES, countReopened); + saveMeasure(context, CoreMetrics.CONFIRMED_ISSUES, countConfirmed); + } + } + + private void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) { + if (context.getMeasure(CoreMetrics.VIOLATIONS) == null) { + Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.VIOLATIONS); + Double sum = MeasureUtils.sum(true, childrenIssues); + context.saveMeasure(CoreMetrics.VIOLATIONS, sum + issues.size()); + } + } + + private void saveNewIssues(DecoratorContext context, Collection<Issue> issues, boolean shouldSaveNewMetrics) { + if (shouldSaveNewMetrics) { + Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS); + saveNewIssues(context, measure, issues); + } + } + + private void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) { + Metric metric = SeverityUtils.severityToIssueMetric(ruleSeverity); + if (context.getMeasure(metric) == null) { + Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric)); + int sum = MeasureUtils.sum(true, children).intValue() + severitiesBag.count(ruleSeverity); + context.saveMeasure(metric, (double) sum); + } + } + + private void saveNewIssuesForSeverity(DecoratorContext context, RulePriority severity, ListMultimap<RulePriority, Issue> issuesPerSeverities, boolean shouldSaveNewMetrics) { + if (shouldSaveNewMetrics) { + Metric metric = SeverityUtils.severityToNewMetricIssue(severity); + Measure measure = new Measure(metric); + saveNewIssues(context, measure, issuesPerSeverities.get(severity)); + } + } + + private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity) { + Metric metric = SeverityUtils.severityToIssueMetric(severity); + + Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric)); + for (Measure child : children) { + RuleMeasure childRuleMeasure = (RuleMeasure) child; + RuleKey ruleKey = childRuleMeasure.ruleKey(); + if (ruleKey != null && MeasureUtils.hasValue(childRuleMeasure)) { + Multiset<RuleKey> rulesBag = initRules(rulesPerSeverity, severity); + rulesBag.add(ruleKey, childRuleMeasure.getIntValue()); + } + } + + Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity); + if (rulesBag != null) { + for (Multiset.Entry<RuleKey> entry : rulesBag.entrySet()) { + RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount()); + measure.setSeverity(severity); + context.saveMeasure(measure); + } + } + } + + private void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection<Issue> issues, boolean shouldSaveNewMetrics) { + if (shouldSaveNewMetrics) { + Metric metric = SeverityUtils.severityToNewMetricIssue(severity); + ListMultimap<RuleKey, Measure> childMeasuresPerRuleKeys = ArrayListMultimap.create(); + ListMultimap<RuleKey, Issue> issuesPerRuleKeys = ArrayListMultimap.create(); + Set<RuleKey> ruleKeys = Sets.newHashSet(); + + Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric)); + for (Measure child : children) { + RuleMeasure childRuleMeasure = (RuleMeasure) child; + RuleKey ruleKey = childRuleMeasure.ruleKey(); + if (ruleKey != null) { + childMeasuresPerRuleKeys.put(ruleKey, childRuleMeasure); + ruleKeys.add(ruleKey); + } + } + + for (Issue issue : issues) { + if (RulePriority.valueOf(issue.severity()).equals(severity)) { + ruleKeys.add(issue.ruleKey()); + issuesPerRuleKeys.put(issue.ruleKey(), issue); + } + } + + for (RuleKey ruleKey : ruleKeys) { + RuleMeasure measure = RuleMeasure.createForRule(metric, ruleKey, null); + measure.setSeverity(severity); + for (Period period : timeMachineConfiguration.periods()) { + int variationIndex = period.getIndex(); + double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRuleKeys.get(ruleKey)) + countIssues(issuesPerRuleKeys.get(ruleKey), period); + measure.setVariation(variationIndex, sum); + } + context.saveMeasure(measure); + } + } + } + + private void saveNewIssues(DecoratorContext context, Measure measure, Collection<Issue> issues) { + for (Period period : timeMachineConfiguration.periods()) { + int variationIndex = period.getIndex(); + Collection<Measure> children = context.getChildrenMeasures(measure.getMetric()); + double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + countIssues(issues, period); + measure.setVariation(variationIndex, sum); + } + context.saveMeasure(measure); + } + + private void saveMeasure(DecoratorContext context, Metric metric, int value) { + context.saveMeasure(metric, (double) (value + sumChildren(context, metric))); + } + + private int sumChildren(DecoratorContext context, Metric metric) { + int sum = 0; + if (!ResourceUtils.isFile(context.getResource())) { + sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue(); + } + return sum; + } + + private Multiset<RuleKey> initRules(Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity, RulePriority severity) { + Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity); + if (rulesBag == null) { + rulesBag = HashMultiset.create(); + rulesPerSeverity.put(severity, rulesBag); + } + return rulesBag; + } + + private int countIssues(Collection<Issue> issues, Period period) { + // SONAR-3647 Use real snapshot date and not target date in order to stay consistent with other measure variations + Date datePlusOneSecond = period.getDate() != null ? DateUtils.addSeconds(period.getDate(), 1) : null; + return countIssuesAfterDate(issues, datePlusOneSecond); + } + + @VisibleForTesting + int countIssuesAfterDate(Collection<Issue> issues, @Nullable Date date) { + if (issues == null) { + return 0; + } + int count = 0; + for (Issue issue : issues) { + if (isAfter(issue, date)) { + count++; + } + } + return count; + } + + private boolean isAfter(Issue issue, @Nullable Date date) { + return date == null || (issue.creationDate() != null && DateUtils.truncatedCompareTo(issue.creationDate(), date, Calendar.SECOND) > 0); + } + + private boolean shouldSaveNewMetrics(DecoratorContext context) { + return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null; + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/CoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/CoverageDecorator.java new file mode 100644 index 00000000000..7d8bed2ab61 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/CoverageDecorator.java @@ -0,0 +1,86 @@ +/* + * 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.compute; + +import java.util.Collection; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.batch.sensor.coverage.CoverageConstants; + +public final class CoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public Collection<Metric> usedMetrics() { + return CoverageConstants.COVERAGE_METRICS; + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + long linesToCover = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0L); + + return linesToCover + conditions; + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredLines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.UNCOVERED_LINES), 0L); + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0L); + long uncoveredConditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0L); + + return lines + conditions - uncoveredConditions - uncoveredLines; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + Long newLinesToCover = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_LINES_TO_COVER), periodIndex); + if (newLinesToCover == null) { + return null; + } + + long newConditionsToCover = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_CONDITIONS_TO_COVER), periodIndex, 0L); + + return newLinesToCover + newConditionsToCover; + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long newLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_LINES_TO_COVER), periodIndex, 0L); + long newUncoveredLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_UNCOVERED_LINES), periodIndex, 0L); + long newUncoveredConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_UNCOVERED_CONDITIONS), periodIndex, 0L); + long newConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_CONDITIONS_TO_COVER), periodIndex, 0L); + + return newLines + newConditions - newUncoveredConditions - newUncoveredLines; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/DirectoriesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/DirectoriesDecorator.java new file mode 100644 index 00000000000..e1dc25e7740 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/DirectoriesDecorator.java @@ -0,0 +1,69 @@ +/* + * 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.compute; + +import java.util.Collection; +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.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; + +/** + * @since 2.2 + */ +public final class DirectoriesDecorator implements Decorator { + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependedUpon + public Metric generateDirectoriesMetric() { + return CoreMetrics.DIRECTORIES; + } + + /** + * {@inheritDoc} + */ + @Override + public void decorate(Resource resource, DecoratorContext context) { + if (MeasureUtils.hasValue(context.getMeasure(CoreMetrics.DIRECTORIES))) { + return; + } + + if (Resource.QUALIFIER_DIRECTORY.equals(resource.getQualifier())) { + context.saveMeasure(CoreMetrics.DIRECTORIES, 1.0); + + } else if (ResourceUtils.isSet(resource)) { + Collection<Measure> childrenMeasures = context.getChildrenMeasures(CoreMetrics.DIRECTORIES); + Double sum = MeasureUtils.sum(false, childrenMeasures); + if (sum != null) { + context.saveMeasure(CoreMetrics.DIRECTORIES, sum); + } + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/FilesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/FilesDecorator.java new file mode 100644 index 00000000000..27b0cbd07f8 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/FilesDecorator.java @@ -0,0 +1,65 @@ +/* + * 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.compute; + +import java.util.Collection; +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.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; + +/** + * @since 2.2 + */ +public final class FilesDecorator implements Decorator { + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependedUpon + public Metric generateDirectoriesMetric() { + return CoreMetrics.FILES; + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + if (MeasureUtils.hasValue(context.getMeasure(CoreMetrics.FILES))) { + return; + } + + if (Resource.QUALIFIER_CLASS.equals(resource.getQualifier()) || Resource.QUALIFIER_FILE.equals(resource.getQualifier())) { + context.saveMeasure(CoreMetrics.FILES, 1.0); + + } else { + Collection<Measure> childrenMeasures = context.getChildrenMeasures(CoreMetrics.FILES); + Double sum = MeasureUtils.sum(false, childrenMeasures); + if (sum != null) { + context.saveMeasure(CoreMetrics.FILES, sum); + } + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/ItBranchCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/ItBranchCoverageDecorator.java new file mode 100644 index 00000000000..926a34df070 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/ItBranchCoverageDecorator.java @@ -0,0 +1,73 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +public final class ItBranchCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return ImmutableList.<Metric>of(CoreMetrics.IT_UNCOVERED_CONDITIONS, CoreMetrics.IT_CONDITIONS_TO_COVER, + CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER); + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.IT_BRANCH_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + return MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_CONDITIONS_TO_COVER), 0L); + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredConditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_UNCOVERED_CONDITIONS), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_CONDITIONS_TO_COVER), 0L); + + return conditions - uncoveredConditions; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_IT_BRANCH_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + return MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER), periodIndex); + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long uncoveredConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS), periodIndex, 0L); + long conditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER), periodIndex, 0L); + + return conditions - uncoveredConditions; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/ItCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/ItCoverageDecorator.java new file mode 100644 index 00000000000..4d99b759170 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/ItCoverageDecorator.java @@ -0,0 +1,87 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +public final class ItCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public Collection<Metric> usedMetrics() { + return ImmutableList.<Metric>of(CoreMetrics.IT_LINES_TO_COVER, CoreMetrics.IT_UNCOVERED_LINES, CoreMetrics.NEW_IT_LINES_TO_COVER, + CoreMetrics.NEW_IT_UNCOVERED_LINES, CoreMetrics.IT_CONDITIONS_TO_COVER, CoreMetrics.IT_UNCOVERED_CONDITIONS, + CoreMetrics.NEW_IT_CONDITIONS_TO_COVER, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS); + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.IT_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_LINES_TO_COVER), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_CONDITIONS_TO_COVER), 0L); + + return lines + conditions; + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredLines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_UNCOVERED_LINES), 0L); + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_LINES_TO_COVER), 0L); + long uncoveredConditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_UNCOVERED_CONDITIONS), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_CONDITIONS_TO_COVER), 0L); + + return lines + conditions - uncoveredConditions - uncoveredLines; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_IT_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + Long newLinesToCover = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_LINES_TO_COVER), periodIndex); + if (newLinesToCover == null) { + return null; + } + + long newConditionsToCover = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER), periodIndex, 0L); + return newLinesToCover + newConditionsToCover; + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long newLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_LINES_TO_COVER), periodIndex, 0L); + long newUncoveredLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_UNCOVERED_LINES), periodIndex, 0L); + long newUncoveredConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS), periodIndex, 0L); + long newConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER), periodIndex, 0L); + + return newLines + newConditions - newUncoveredConditions - newUncoveredLines; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/ItLineCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/ItLineCoverageDecorator.java new file mode 100644 index 00000000000..dddddd0aac4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/ItLineCoverageDecorator.java @@ -0,0 +1,73 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +public final class ItLineCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return ImmutableList.<Metric>of(CoreMetrics.IT_UNCOVERED_LINES, CoreMetrics.IT_LINES_TO_COVER, CoreMetrics.NEW_IT_UNCOVERED_LINES, + CoreMetrics.NEW_IT_LINES_TO_COVER); + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.IT_LINE_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + return MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_LINES_TO_COVER), 0L); + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredLines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_UNCOVERED_LINES), 0L); + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.IT_LINES_TO_COVER), 0L); + + return lines - uncoveredLines; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_IT_LINE_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + return MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_LINES_TO_COVER), periodIndex); + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long uncoveredLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_UNCOVERED_LINES), periodIndex, 0L); + long lines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_IT_LINES_TO_COVER), periodIndex, 0L); + + return lines - uncoveredLines; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/LineCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/LineCoverageDecorator.java new file mode 100644 index 00000000000..71c6a034ac6 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/LineCoverageDecorator.java @@ -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.compute; + +import java.util.Collection; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.batch.sensor.coverage.CoverageConstants; + +public final class LineCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public Collection<Metric> dependsUponMetrics() { + return CoverageConstants.LINE_COVERAGE_METRICS; + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.LINE_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + return MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0L); + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredLines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.UNCOVERED_LINES), 0L); + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0L); + + return lines - uncoveredLines; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_LINE_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + return MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_LINES_TO_COVER), periodIndex); + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long uncoveredLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_UNCOVERED_LINES), periodIndex, 0L); + long lines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_LINES_TO_COVER), periodIndex, 0L); + + return lines - uncoveredLines; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/ManualMeasureDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/ManualMeasureDecorator.java new file mode 100644 index 00000000000..94346b30b8f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/ManualMeasureDecorator.java @@ -0,0 +1,79 @@ +/* + * 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.compute; + +import java.util.List; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.Phase; +import org.sonar.api.batch.RequiresDB; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.jpa.entity.ManualMeasure; + +import static com.google.common.base.Preconditions.checkState; + +@Phase(name = Phase.Name.PRE) +@RequiresDB +public class ManualMeasureDecorator implements Decorator { + + private DatabaseSession session; + private MetricFinder metricFinder; + + public ManualMeasureDecorator(DatabaseSession session, MetricFinder metricFinder) { + this.session = session; + this.metricFinder = metricFinder; + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + if (resource.getId() != null) { + List<ManualMeasure> manualMeasures = session.getResults(ManualMeasure.class, "resourceId", resource.getId()); + for (ManualMeasure manualMeasure : manualMeasures) { + context.saveMeasure(copy(manualMeasure)); + } + } + } + + private Measure copy(ManualMeasure manualMeasure) { + Metric metric = metricFinder.findById(manualMeasure.getMetricId()); + checkState(metric != null, "Unable to find manual metric with id: " + manualMeasure.getMetricId()); + + Measure measure = new Measure(metric); + measure.setValue(manualMeasure.getValue(), 5); + measure.setData(manualMeasure.getTextValue()); + measure.setDescription(manualMeasure.getDescription()); + return measure; + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/NewCoverageAggregator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/NewCoverageAggregator.java new file mode 100644 index 00000000000..1923d5211a6 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/NewCoverageAggregator.java @@ -0,0 +1,99 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang.ArrayUtils; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +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; + +@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) +public final class NewCoverageAggregator implements Decorator { + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependedUpon + public List<Metric> generatesNewCoverageMetrics() { + return Arrays.<Metric>asList( + CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES, CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS, + CoreMetrics.NEW_IT_LINES_TO_COVER, CoreMetrics.NEW_IT_UNCOVERED_LINES, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS, + CoreMetrics.NEW_OVERALL_LINES_TO_COVER, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS); + } + + @Override + 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); + aggregate(context, CoreMetrics.NEW_IT_LINES_TO_COVER, maxPeriods); + aggregate(context, CoreMetrics.NEW_IT_UNCOVERED_LINES, maxPeriods); + aggregate(context, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER, maxPeriods); + aggregate(context, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS, maxPeriods); + aggregate(context, CoreMetrics.NEW_OVERALL_LINES_TO_COVER, maxPeriods); + aggregate(context, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES, maxPeriods); + aggregate(context, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER, maxPeriods); + aggregate(context, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS, maxPeriods); + } + } + + 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); + } + } + + boolean shouldDecorate(Resource resource) { + return Scopes.isHigherThan(resource, Scopes.FILE); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/NewCoverageFileAnalyzer.java b/sonar-batch/src/main/java/org/sonar/batch/compute/NewCoverageFileAnalyzer.java new file mode 100644 index 00000000000..ba36d5b782b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/NewCoverageFileAnalyzer.java @@ -0,0 +1,73 @@ +/* + * 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.compute; + +import java.util.List; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.report.ReportPublisher; + +public class NewCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer { + + public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, BatchComponentCache resourceCache) { + super(timeMachineConfiguration, publishReportJob, resourceCache); + } + + NewCoverageFileAnalyzer(List<PeriodStruct> structs, ReportPublisher publishReportJob, BatchComponentCache resourceCache) { + super(structs, publishReportJob, resourceCache); + } + + @Override + public Metric getCoverageLineHitsDataMetric() { + return CoreMetrics.COVERAGE_LINE_HITS_DATA; + } + + @Override + public Metric getConditionsByLineMetric() { + return CoreMetrics.CONDITIONS_BY_LINE; + } + + @Override + public Metric getCoveredConditionsByLineMetric() { + return CoreMetrics.COVERED_CONDITIONS_BY_LINE; + } + + @Override + public Metric getNewLinesToCoverMetric() { + return CoreMetrics.NEW_LINES_TO_COVER; + } + + @Override + public Metric getNewUncoveredLinesMetric() { + return CoreMetrics.NEW_UNCOVERED_LINES; + } + + @Override + public Metric getNewConditionsToCoverMetric() { + return CoreMetrics.NEW_CONDITIONS_TO_COVER; + } + + @Override + public Metric getNewUncoveredConditionsMetric() { + return CoreMetrics.NEW_UNCOVERED_CONDITIONS; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/NewItCoverageFileAnalyzer.java b/sonar-batch/src/main/java/org/sonar/batch/compute/NewItCoverageFileAnalyzer.java new file mode 100644 index 00000000000..48d62305706 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/NewItCoverageFileAnalyzer.java @@ -0,0 +1,68 @@ +/* + * 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.compute; + +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.report.ReportPublisher; + +public class NewItCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer { + + public NewItCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, BatchComponentCache resourceCache) { + super(timeMachineConfiguration, publishReportJob, resourceCache); + } + + @Override + public Metric getCoverageLineHitsDataMetric() { + return CoreMetrics.IT_COVERAGE_LINE_HITS_DATA; + } + + @Override + public Metric getConditionsByLineMetric() { + return CoreMetrics.IT_CONDITIONS_BY_LINE; + } + + @Override + public Metric getCoveredConditionsByLineMetric() { + return CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE; + } + + @Override + public Metric getNewLinesToCoverMetric() { + return CoreMetrics.NEW_IT_LINES_TO_COVER; + } + + @Override + public Metric getNewUncoveredLinesMetric() { + return CoreMetrics.NEW_IT_UNCOVERED_LINES; + } + + @Override + public Metric getNewConditionsToCoverMetric() { + return CoreMetrics.NEW_IT_CONDITIONS_TO_COVER; + } + + @Override + public Metric getNewUncoveredConditionsMetric() { + return CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/NewOverallCoverageFileAnalyzer.java b/sonar-batch/src/main/java/org/sonar/batch/compute/NewOverallCoverageFileAnalyzer.java new file mode 100644 index 00000000000..1c2688c6243 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/NewOverallCoverageFileAnalyzer.java @@ -0,0 +1,68 @@ +/* + * 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.compute; + +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.report.ReportPublisher; + +public class NewOverallCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer { + + public NewOverallCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, BatchComponentCache resourceCache) { + super(timeMachineConfiguration, publishReportJob, resourceCache); + } + + @Override + public Metric getCoverageLineHitsDataMetric() { + return CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA; + } + + @Override + public Metric getConditionsByLineMetric() { + return CoreMetrics.OVERALL_CONDITIONS_BY_LINE; + } + + @Override + public Metric getCoveredConditionsByLineMetric() { + return CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE; + } + + @Override + public Metric getNewLinesToCoverMetric() { + return CoreMetrics.NEW_OVERALL_LINES_TO_COVER; + } + + @Override + public Metric getNewUncoveredLinesMetric() { + return CoreMetrics.NEW_OVERALL_UNCOVERED_LINES; + } + + @Override + public Metric getNewConditionsToCoverMetric() { + return CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER; + } + + @Override + public Metric getNewUncoveredConditionsMetric() { + return CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/OverallBranchCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/OverallBranchCoverageDecorator.java new file mode 100644 index 00000000000..271cfcd795f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/OverallBranchCoverageDecorator.java @@ -0,0 +1,73 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +public final class OverallBranchCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return ImmutableList.<Metric>of(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS, CoreMetrics.OVERALL_CONDITIONS_TO_COVER, + CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER); + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.OVERALL_BRANCH_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + return MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER), 0L); + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredConditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER), 0L); + + return conditions - uncoveredConditions; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + return MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER), periodIndex); + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long uncoveredConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS), periodIndex, 0L); + long conditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER), periodIndex, 0L); + + return conditions - uncoveredConditions; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/OverallCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/OverallCoverageDecorator.java new file mode 100644 index 00000000000..c4a4d037770 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/OverallCoverageDecorator.java @@ -0,0 +1,88 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +public final class OverallCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public Collection<Metric> usedMetrics() { + return ImmutableList.<Metric>of(CoreMetrics.OVERALL_LINES_TO_COVER, CoreMetrics.OVERALL_UNCOVERED_LINES, CoreMetrics.NEW_OVERALL_LINES_TO_COVER, + CoreMetrics.NEW_OVERALL_UNCOVERED_LINES, CoreMetrics.OVERALL_CONDITIONS_TO_COVER, CoreMetrics.OVERALL_UNCOVERED_CONDITIONS, + CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS); + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.OVERALL_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_LINES_TO_COVER), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER), 0L); + + return lines + conditions; + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredLines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_LINES), 0L); + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_LINES_TO_COVER), 0L); + long uncoveredConditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS), 0L); + long conditions = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER), 0L); + + return lines + conditions - uncoveredConditions - uncoveredLines; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_OVERALL_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + Long newLinesToCover = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_LINES_TO_COVER), periodIndex); + if (newLinesToCover == null) { + return null; + } + + long newConditionsToCover = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER), periodIndex, 0L); + + return newLinesToCover + newConditionsToCover; + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long newLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_LINES_TO_COVER), periodIndex, 0L); + long newUncoveredLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES), periodIndex, 0L); + long newUncoveredConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS), periodIndex, 0L); + long newConditions = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER), periodIndex, 0L); + + return newLines + newConditions - newUncoveredConditions - newUncoveredLines; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/OverallLineCoverageDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/OverallLineCoverageDecorator.java new file mode 100644 index 00000000000..a41440e6314 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/OverallLineCoverageDecorator.java @@ -0,0 +1,73 @@ +/* + * 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.compute; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +public final class OverallLineCoverageDecorator extends AbstractCoverageDecorator { + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return ImmutableList.<Metric>of(CoreMetrics.OVERALL_UNCOVERED_LINES, CoreMetrics.OVERALL_LINES_TO_COVER, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES, + CoreMetrics.NEW_OVERALL_LINES_TO_COVER); + } + + @Override + protected Metric getGeneratedMetric() { + return CoreMetrics.OVERALL_LINE_COVERAGE; + } + + @Override + protected Long countElements(DecoratorContext context) { + return MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_LINES_TO_COVER), 0L); + } + + @Override + protected long countCoveredElements(DecoratorContext context) { + long uncoveredLines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_LINES), 0L); + long lines = MeasureUtils.getValueAsLong(context.getMeasure(CoreMetrics.OVERALL_LINES_TO_COVER), 0L); + + return lines - uncoveredLines; + } + + @Override + protected Metric getGeneratedMetricForNewCode() { + return CoreMetrics.NEW_OVERALL_LINE_COVERAGE; + } + + @Override + protected Long countElementsForNewCode(DecoratorContext context, int periodIndex) { + return MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_LINES_TO_COVER), periodIndex); + } + + @Override + protected long countCoveredElementsForNewCode(DecoratorContext context, int periodIndex) { + long uncoveredLines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES), periodIndex, 0L); + long lines = MeasureUtils.getVariationAsLong(context.getMeasure(CoreMetrics.NEW_OVERALL_LINES_TO_COVER), periodIndex, 0L); + + return lines - uncoveredLines; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java b/sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java new file mode 100644 index 00000000000..fd92039ce98 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java @@ -0,0 +1,66 @@ +/* + * 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.compute; + +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.api.rules.RulePriority; + +final class SeverityUtils { + private SeverityUtils() { + // only static methods + } + + static Metric severityToIssueMetric(RulePriority severity) { + Metric metric; + if (severity.equals(RulePriority.BLOCKER)) { + metric = CoreMetrics.BLOCKER_VIOLATIONS; + } else if (severity.equals(RulePriority.CRITICAL)) { + metric = CoreMetrics.CRITICAL_VIOLATIONS; + } else if (severity.equals(RulePriority.MAJOR)) { + metric = CoreMetrics.MAJOR_VIOLATIONS; + } else if (severity.equals(RulePriority.MINOR)) { + metric = CoreMetrics.MINOR_VIOLATIONS; + } else if (severity.equals(RulePriority.INFO)) { + metric = CoreMetrics.INFO_VIOLATIONS; + } else { + throw new IllegalArgumentException("Unsupported severity: " + severity); + } + return metric; + } + + static Metric severityToNewMetricIssue(RulePriority severity) { + Metric metric; + if (severity.equals(RulePriority.BLOCKER)) { + metric = CoreMetrics.NEW_BLOCKER_VIOLATIONS; + } else if (severity.equals(RulePriority.CRITICAL)) { + metric = CoreMetrics.NEW_CRITICAL_VIOLATIONS; + } else if (severity.equals(RulePriority.MAJOR)) { + metric = CoreMetrics.NEW_MAJOR_VIOLATIONS; + } else if (severity.equals(RulePriority.MINOR)) { + metric = CoreMetrics.NEW_MINOR_VIOLATIONS; + } else if (severity.equals(RulePriority.INFO)) { + metric = CoreMetrics.NEW_INFO_VIOLATIONS; + } else { + throw new IllegalArgumentException("Unsupported severity: " + severity); + } + return metric; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java b/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java new file mode 100644 index 00000000000..1255c64a35f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java @@ -0,0 +1,83 @@ +/* + * 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.compute; + +import java.util.List; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.RequiresDB; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.batch.components.PastSnapshot; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.BatchComponentCache; + +import static org.sonar.api.utils.DateUtils.dateToLong; + +@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) +@RequiresDB +public final class TimeMachineConfigurationPersister implements Decorator { + + private final TimeMachineConfiguration timeMachineConfiguration; + private BatchComponentCache resourceCache; + private DatabaseSession session; + + public TimeMachineConfigurationPersister(TimeMachineConfiguration timeMachineConfiguration, BatchComponentCache resourceCache, DatabaseSession session) { + this.timeMachineConfiguration = timeMachineConfiguration; + this.resourceCache = resourceCache; + this.session = session; + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + if (ResourceUtils.isProject(resource)) { + persistConfiguration(resource); + } + } + + void persistConfiguration(Resource module) { + List<PastSnapshot> pastSnapshots = timeMachineConfiguration.getProjectPastSnapshots(); + Snapshot projectSnapshot = resourceCache.get(module).snapshot(); + for (PastSnapshot pastSnapshot : pastSnapshots) { + Snapshot snapshot = session.reattach(Snapshot.class, projectSnapshot.getId()); + updatePeriodParams(snapshot, pastSnapshot); + updatePeriodParams(projectSnapshot, pastSnapshot); + session.save(snapshot); + } + session.commit(); + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + private void updatePeriodParams(Snapshot snapshot, PastSnapshot pastSnapshot) { + int periodIndex = pastSnapshot.getIndex(); + snapshot.setPeriodMode(periodIndex, pastSnapshot.getMode()); + snapshot.setPeriodModeParameter(periodIndex, pastSnapshot.getModeParameter()); + snapshot.setPeriodDateMs(periodIndex, dateToLong(pastSnapshot.getDate())); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/UnitTestDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/UnitTestDecorator.java new file mode 100644 index 00000000000..3f5d641af0c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/UnitTestDecorator.java @@ -0,0 +1,95 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +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.MeasureUtils; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; + +public class UnitTestDecorator implements Decorator { + + @DependedUpon + public List<Metric> generatesMetrics() { + return Arrays.<Metric>asList(CoreMetrics.TEST_EXECUTION_TIME, CoreMetrics.TESTS, CoreMetrics.TEST_ERRORS, CoreMetrics.TEST_FAILURES, CoreMetrics.TEST_SUCCESS_DENSITY); + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return !Project.AnalysisType.STATIC.equals(project.getAnalysisType()); + } + + public boolean shouldDecorateResource(Resource resource) { + return ResourceUtils.isUnitTestFile(resource) || !ResourceUtils.isEntity(resource); + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + if (shouldDecorateResource(resource)) { + sumChildren(context, CoreMetrics.TEST_EXECUTION_TIME); + sumChildren(context, CoreMetrics.SKIPPED_TESTS); + Double tests = sumChildren(context, CoreMetrics.TESTS); + Double errors = sumChildren(context, CoreMetrics.TEST_ERRORS); + Double failures = sumChildren(context, CoreMetrics.TEST_FAILURES); + + if (isPositive(tests, true) && isPositive(errors, false) && isPositive(failures, false)) { + Double errorsAndFailuresRatio = (errors + failures) * 100.0 / tests; + context.saveMeasure(CoreMetrics.TEST_SUCCESS_DENSITY, 100.0 - errorsAndFailuresRatio); + } + } + } + + private boolean isPositive(Double d, boolean strict) { + return d != null && (strict ? d > 0.0 : d >= 0.0); + } + + private Double sumChildren(DecoratorContext jobContext, Metric metric) { + Collection<Measure> childrenMeasures = jobContext.getChildrenMeasures(metric); + if (childrenMeasures != null && !childrenMeasures.isEmpty()) { + Double sum = 0.0; + boolean hasChildrenMeasures = false; + for (Measure measure : childrenMeasures) { + if (MeasureUtils.hasValue(measure)) { + sum += measure.getValue(); + hasChildrenMeasures = true; + } + } + if (hasChildrenMeasures) { + jobContext.saveMeasure(metric, sum); + return sum; + } + } + return null; + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/VariationDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/VariationDecorator.java new file mode 100644 index 00000000000..f3885ef3f8c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/VariationDecorator.java @@ -0,0 +1,205 @@ +/* + * 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.compute; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.batch.RequiresDB; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.measures.RuleMeasure; +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.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.technicaldebt.batch.Characteristic; +import org.sonar.batch.components.PastMeasuresLoader; +import org.sonar.batch.components.PastSnapshot; +import org.sonar.batch.components.TimeMachineConfiguration; + +@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) +@RequiresDB +public class VariationDecorator implements Decorator { + + private List<PastSnapshot> projectPastSnapshots; + private MetricFinder metricFinder; + private PastMeasuresLoader pastMeasuresLoader; + private RuleFinder ruleFinder; + + public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration, RuleFinder ruleFinder) { + this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots(), ruleFinder); + } + + VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List<PastSnapshot> projectPastSnapshots, RuleFinder ruleFinder) { + this.pastMeasuresLoader = pastMeasuresLoader; + this.projectPastSnapshots = projectPastSnapshots; + this.metricFinder = metricFinder; + this.ruleFinder = ruleFinder; + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependsUpon + public Collection<Metric> dependsUponMetrics() { + return pastMeasuresLoader.getMetrics(); + } + + @Override + public void decorate(Resource resource, DecoratorContext context) { + for (PastSnapshot projectPastSnapshot : projectPastSnapshots) { + if (shouldComputeVariation(resource)) { + computeVariation(resource, context, projectPastSnapshot); + } + } + } + + boolean shouldComputeVariation(Resource resource) { + if (Scopes.FILE.equals(resource.getScope()) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier())) { + return false; + } + + // measures on files are currently purged, so past measures are not available on files + return StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()); + } + + private void computeVariation(Resource resource, DecoratorContext context, PastSnapshot pastSnapshot) { + List<Object[]> pastMeasures = pastMeasuresLoader.getPastMeasures(resource, pastSnapshot); + compareWithPastMeasures(context, pastSnapshot.getIndex(), pastMeasures); + } + + private void compareWithPastMeasures(DecoratorContext context, int index, List<Object[]> pastMeasures) { + Map<MeasureKey, Object[]> pastMeasuresByKey = new HashMap<>(); + for (Object[] pastMeasure : pastMeasures) { + pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure); + } + + // for each measure, search equivalent past measure + for (Measure measure : context.getMeasures(MeasuresFilters.all())) { + // compare with past measure + Integer metricId = measure.getMetric().getId(); + if (metricId == null) { + Metric metric = metricFinder.findByKey(measure.getMetric().getKey()); + if (metric == null) { + throw new IllegalStateException("Unknow metric with key: " + measure.getMetric().getKey()); + } + metricId = metric.getId(); + } + Characteristic characteristic = measure.getCharacteristic(); + Integer characteristicId = characteristic != null ? characteristic.id() : null; + Integer personId = measure.getPersonId(); + Integer ruleId = null; + if (measure instanceof RuleMeasure) { + Rule rule = ruleFinder.findByKey(((RuleMeasure) measure).ruleKey()); + if (rule != null) { + ruleId = rule.getId(); + } + } + + Object[] pastMeasure = pastMeasuresByKey.get(new MeasureKey(metricId, characteristicId, personId, ruleId)); + if (updateVariation(measure, pastMeasure, index)) { + context.saveMeasure(measure); + } + } + } + + boolean updateVariation(Measure measure, Object[] pastMeasure, int index) { + if (pastMeasure != null && PastMeasuresLoader.hasValue(pastMeasure) && measure.getValue() != null) { + double variation = measure.getValue() - PastMeasuresLoader.getValue(pastMeasure); + measure.setVariation(index, variation); + return true; + } + return false; + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } + + static final class MeasureKey { + int metricId; + Integer characteristicId; + Integer personId; + Integer ruleId; + + MeasureKey(Object[] pastFields) { + metricId = PastMeasuresLoader.getMetricId(pastFields); + characteristicId = PastMeasuresLoader.getCharacteristicId(pastFields); + personId = PastMeasuresLoader.getPersonId(pastFields); + ruleId = PastMeasuresLoader.getRuleId(pastFields); + } + + MeasureKey(int metricId, @Nullable Integer characteristicId, @Nullable Integer personId, @Nullable Integer ruleId) { + this.metricId = metricId; + this.characteristicId = characteristicId; + this.personId = personId; + this.ruleId = ruleId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MeasureKey that = (MeasureKey) o; + if (metricId != that.metricId) { + return false; + } + if (characteristicId != null ? !characteristicId.equals(that.characteristicId) : that.characteristicId != null) { + return false; + } + if (personId != null ? !personId.equals(that.personId) : that.personId != null) { + return false; + } + if (ruleId != null ? !ruleId.equals(that.ruleId) : that.ruleId != null) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = metricId; + result = 31 * result + (characteristicId != null ? characteristicId.hashCode() : 0); + result = 31 * result + (personId != null ? personId.hashCode() : 0); + result = 31 * result + (ruleId != null ? ruleId.hashCode() : 0); + return result; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/compute/package-info.java new file mode 100644 index 00000000000..7fd21f3ba7c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.batch.compute; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/ApplyProjectRolesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/ApplyProjectRolesDecoratorTest.java new file mode 100644 index 00000000000..cd48e6a315f --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/ApplyProjectRolesDecoratorTest.java @@ -0,0 +1,83 @@ +/* + * 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.compute; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.resources.Project; +import org.sonar.api.security.ResourcePermissions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ApplyProjectRolesDecoratorTest { + + private ResourcePermissions resourcePermissions; + private ApplyProjectRolesDecorator decorator; + + @Before + public void init() { + resourcePermissions = mock(ResourcePermissions.class); + decorator = new ApplyProjectRolesDecorator(resourcePermissions); + } + + @Test + public void alwaysExecute() { + assertThat(decorator.shouldExecuteOnProject(new Project("project"))).isTrue(); + } + + @Test + public void doNotGrantDefaultRolesWhenExistingPermissions() { + Project project = new Project("project"); + project.setId(10); + when(resourcePermissions.hasRoles(project)).thenReturn(true); + + decorator.decorate(project, null); + + verify(resourcePermissions, never()).grantDefaultRoles(project); + } + + @Test + public void doNotApplySecurityOnModules() { + Project project = new Project("project"); + Project module = new Project("module").setParent(project); + module.setId(10); + when(resourcePermissions.hasRoles(project)).thenReturn(false); + + decorator.decorate(module, null); + + verify(resourcePermissions, never()).grantDefaultRoles(module); + } + + @Test + public void grantDefaultRolesWhenNoPermissions() { + Project project = new Project("project"); + project.setId(10); + when(resourcePermissions.hasRoles(project)).thenReturn(false); + + decorator.decorate(project, null); + + verify(resourcePermissions).grantDefaultRoles(project); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/BranchCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/BranchCoverageDecoratorTest.java new file mode 100644 index 00000000000..a7f9e098376 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/BranchCoverageDecoratorTest.java @@ -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.compute; + +import org.junit.Before; +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.Qualifiers; +import org.sonar.api.resources.Scopes; + +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class BranchCoverageDecoratorTest { + private final BranchCoverageDecorator decorator = new BranchCoverageDecorator(); + private final Project resource = mock(Project.class); + + @Before + public void setUp() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(resource.getQualifier()).thenReturn(Qualifiers.PROJECT); + } + + @Test + public void shouldSaveBranchCoverage() { + DecoratorContext context = mockContext(20, 15); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.BRANCH_COVERAGE, 25.0); + } + + @Test + public void shouldNotSaveBranchCoverageIfMissingConditions() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(resource, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.BRANCH_COVERAGE), anyDouble()); + } + + private static DecoratorContext mockContext(int conditions, int uncoveredConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.CONDITIONS_TO_COVER, (double) conditions)); + when(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, (double) uncoveredConditions)); + return context; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/CommentDensityDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/CommentDensityDecoratorTest.java new file mode 100644 index 00000000000..dadbb945e90 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/CommentDensityDecoratorTest.java @@ -0,0 +1,76 @@ +/* + * 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.compute; + +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.test.IsMeasure; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class CommentDensityDecoratorTest { + + @Test + public void densityIsBalancedByNcloc() { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 300.0)); + when(context.getMeasure(CoreMetrics.COMMENT_LINES)).thenReturn(new Measure(CoreMetrics.COMMENT_LINES, 200.0)); + CommentDensityDecorator decorator = new CommentDensityDecorator(); + decorator.decorate(null, context); + // 200 / (200 + 300) = 40% + verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY, 40.0))); + } + + @Test + public void noDensityIfUnknownComments() { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 300.0)); + CommentDensityDecorator decorator = new CommentDensityDecorator(); + decorator.decorate(null, context); + verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY))); + } + + @Test + public void noDensityIfZeroNcloc() { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 0.0)); + when(context.getMeasure(CoreMetrics.COMMENT_LINES)).thenReturn(new Measure(CoreMetrics.COMMENT_LINES, 0.0)); + CommentDensityDecorator decorator = new CommentDensityDecorator(); + decorator.decorate(null, context); + verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY))); + } + + @Test + public void zeroDensityWhenZeroComments() { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 40.0)); + when(context.getMeasure(CoreMetrics.COMMENT_LINES)).thenReturn(new Measure(CoreMetrics.COMMENT_LINES, 0.0)); + CommentDensityDecorator decorator = new CommentDensityDecorator(); + decorator.decorate(null, context); + verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY, 0.0))); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/CountFalsePositivesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/CountFalsePositivesDecoratorTest.java new file mode 100644 index 00000000000..37eea57e8e6 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/CountFalsePositivesDecoratorTest.java @@ -0,0 +1,81 @@ +/* + * 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.compute; + +import java.util.Arrays; +import org.junit.Test; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; +import org.sonar.java.api.JavaClass; + +import static org.assertj.core.api.Assertions.assertThat; +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 CountFalsePositivesDecoratorTest { + + ResourcePerspectives perspectives = mock(ResourcePerspectives.class); + CountFalsePositivesDecorator decorator = new CountFalsePositivesDecorator(perspectives); + + @Test + public void should_count_false_positives() { + DefaultIssue falsePositive = new DefaultIssue().setRuleKey(RuleKey.parse("squid:AvoidCycles")) + .setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_RESOLVED); + DefaultIssue fixed = new DefaultIssue().setRuleKey(RuleKey.parse("squid:AvoidCycles")) + .setResolution(Issue.RESOLUTION_FIXED).setStatus(Issue.STATUS_RESOLVED); + + File file = File.create("foo.c"); + Issuable issuable = mock(Issuable.class); + when(perspectives.as(Issuable.class, file)).thenReturn(issuable); + when(issuable.resolvedIssues()).thenReturn(Arrays.<Issue>asList(falsePositive, fixed)); + + DecoratorContext context = mock(DecoratorContext.class); + decorator.decorate(file, context); + + verify(context).saveMeasure(CoreMetrics.FALSE_POSITIVE_ISSUES, 1.0); + } + + @Test + public void should_declare_metadata() { + assertThat(decorator.shouldExecuteOnProject(new Project("foo"))).isTrue(); + assertThat(decorator.generatesFalsePositiveMeasure()).isEqualTo(CoreMetrics.FALSE_POSITIVE_ISSUES); + assertThat(decorator.toString()).isEqualTo("CountFalsePositivesDecorator"); + } + + @Test + public void should_ignore_classes_and_methods() { + JavaClass javaClass = JavaClass.create("Foo.java"); + when(perspectives.as(Issuable.class, javaClass)).thenReturn(null); + + DecoratorContext context = mock(DecoratorContext.class); + decorator.decorate(javaClass, context); + + verifyZeroInteractions(context); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/CountUnresolvedIssuesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/CountUnresolvedIssuesDecoratorTest.java new file mode 100644 index 00000000000..8473e0a3da7 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/CountUnresolvedIssuesDecoratorTest.java @@ -0,0 +1,372 @@ +/* + * 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.compute; + +import com.google.common.collect.Lists; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.time.DateUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.RuleMeasure; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.Scopes; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.test.IsRuleMeasure; +import org.sonar.batch.components.Period; +import org.sonar.batch.components.TimeMachineConfiguration; + +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyDouble; +import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class CountUnresolvedIssuesDecoratorTest { + + CountUnresolvedIssuesDecorator decorator; + TimeMachineConfiguration timeMachineConfiguration; + Issuable issuable; + DecoratorContext context; + Resource resource; + Project project; + Rule ruleA1; + Rule ruleA2; + Rule ruleB1; + Date rightNow; + Date tenDaysAgo; + Date afterTenDaysAgo; + Date fiveDaysAgo; + Date afterFiveDaysAgo; + Date sameSecond; + + @Before + public void before() { + ruleA1 = Rule.create().setRepositoryKey("ruleA1").setKey("ruleA1").setName("nameA1"); + ruleA2 = Rule.create().setRepositoryKey("ruleA2").setKey("ruleA2").setName("nameA2"); + ruleB1 = Rule.create().setRepositoryKey("ruleB1").setKey("ruleB1").setName("nameB1"); + + rightNow = new Date(); + tenDaysAgo = DateUtils.addDays(rightNow, -10); + afterTenDaysAgo = DateUtils.addDays(tenDaysAgo, 1); + fiveDaysAgo = DateUtils.addDays(rightNow, -5); + afterFiveDaysAgo = DateUtils.addDays(fiveDaysAgo, 1); + sameSecond = DateUtils.truncate(rightNow, Calendar.SECOND); + + timeMachineConfiguration = mock(TimeMachineConfiguration.class); + when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, afterFiveDaysAgo), new Period(2, afterTenDaysAgo))); + + project = mock(Project.class); + resource = mock(Resource.class); + context = mock(DecoratorContext.class); + when(context.getResource()).thenReturn(resource); + when(context.getProject()).thenReturn(project); + when(context.getMeasure(CoreMetrics.NEW_VIOLATIONS)).thenReturn(null); + + issuable = mock(Issuable.class); + ResourcePerspectives perspectives = mock(ResourcePerspectives.class); + when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); + decorator = new CountUnresolvedIssuesDecorator(perspectives, timeMachineConfiguration); + } + + @Test + public void should_be_depended_upon_metric() { + assertThat(decorator.generatesIssuesMetrics()).hasSize(15); + } + + @Test + public void should_count_issues() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(issuable.issues()).thenReturn(createIssues()); + when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList()); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.VIOLATIONS, 4.0); + } + + @Test + public void should_do_nothing_when_issuable_is_null() { + ResourcePerspectives perspectives = mock(ResourcePerspectives.class); + when(perspectives.as(Issuable.class, resource)).thenReturn(null); + CountUnresolvedIssuesDecorator decorator = new CountUnresolvedIssuesDecorator(perspectives, timeMachineConfiguration); + + decorator.decorate(resource, context); + + verifyZeroInteractions(context); + } + + /** + * See http://jira.codehaus.org/browse/SONAR-1729 + */ + @Test + public void should_not_count_issues_if_measure_already_exists() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(issuable.issues()).thenReturn(createIssues()); + when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList()); + when(context.getMeasure(CoreMetrics.VIOLATIONS)).thenReturn(new Measure(CoreMetrics.VIOLATIONS, 3000.0)); + when(context.getMeasure(CoreMetrics.MAJOR_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.MAJOR_VIOLATIONS, 500.0)); + + decorator.decorate(resource, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.VIOLATIONS), anyDouble());// not changed + verify(context, never()).saveMeasure(eq(CoreMetrics.MAJOR_VIOLATIONS), anyDouble());// not changed + verify(context, times(1)).saveMeasure(eq(CoreMetrics.CRITICAL_VIOLATIONS), anyDouble());// did not exist + } + + @Test + public void should_save_zero_on_projects() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList()); + when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList()); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.VIOLATIONS, 0.0); + } + + @Test + public void should_save_zero_on_directories() { + when(resource.getScope()).thenReturn(Scopes.DIRECTORY); + when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList()); + when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList()); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.VIOLATIONS, 0.0); + } + + @Test + public void should_count_issues_by_severity() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(issuable.issues()).thenReturn(createIssues()); + when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList()); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.BLOCKER_VIOLATIONS, 0.0); + verify(context).saveMeasure(CoreMetrics.CRITICAL_VIOLATIONS, 2.0); + verify(context).saveMeasure(CoreMetrics.MAJOR_VIOLATIONS, 1.0); + verify(context).saveMeasure(CoreMetrics.MINOR_VIOLATIONS, 1.0); + verify(context).saveMeasure(CoreMetrics.INFO_VIOLATIONS, 0.0); + } + + @Test + public void should_count_issues_per_rule() { + List<Issue> issues = newArrayList(); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name())); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name())); + issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name())); + when(issuable.issues()).thenReturn(issues); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_VIOLATIONS, ruleA1, 2.0))); + verify(context, never()).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_VIOLATIONS, ruleA1, 0.0))); + verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_VIOLATIONS, ruleA2, 1.0))); + } + + @Test + public void same_rule_should_have_different_severities() { + List<Issue> issues = newArrayList(); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name())); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name())); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.MINOR.name())); + when(issuable.issues()).thenReturn(issues); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_VIOLATIONS, ruleA1, 2.0))); + verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MINOR_VIOLATIONS, ruleA1, 1.0))); + } + + @Test + public void should_count_issues_after_date() { + List<Issue> issues = createIssuesForNewMetrics(); + + assertThat(decorator.countIssuesAfterDate(null, fiveDaysAgo)).isEqualTo(0); + assertThat(decorator.countIssuesAfterDate(issues, fiveDaysAgo)).isEqualTo(1); // 1 rightNow + assertThat(decorator.countIssuesAfterDate(issues, tenDaysAgo)).isEqualTo(3); // 1 rightNow + 2 fiveDaysAgo + assertThat(decorator.countIssuesAfterDate(issues, sameSecond)).isEqualTo(0); // 0 + } + + @Test + public void should_clear_cache_after_execution() { + Issue issue1 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA1.getRepositoryKey(), ruleA1.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow); + Issue issue2 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA2.getRepositoryKey(), ruleA2.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow); + when(issuable.issues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2)); + + decorator.decorate(resource, context); + decorator.decorate(resource, context); + + verify(context, times(2)).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 1.0, 1.0))); + verify(context, never()).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 2.0, 2.0))); + } + + @Test + public void should_save_severity_new_issues() { + when(issuable.issues()).thenReturn(createIssuesForNewMetrics()); + + decorator.decorate(resource, context); + + // remember : period1 is 5daysAgo, period2 is 10daysAgo + verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0, 0.0))); + verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 1.0, 1.0))); + verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0, 1.0))); + verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0, 1.0))); + verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0, 0.0))); + } + + @Test + public void should_save_rule_new_issues() { + when(issuable.issues()).thenReturn(createIssuesForNewMetrics()); + + decorator.decorate(resource, context); + + // remember : period1 is 5daysAgo, period2 is 10daysAgo + verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, ruleA1, 1.0, 1.0))); + verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MAJOR_VIOLATIONS, ruleA2, 0.0, 1.0))); + verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MINOR_VIOLATIONS, ruleB1, 0.0, 1.0))); + } + + @Test + public void should_not_save_new_issues_if_measure_already_computed() { + when(context.getMeasure(CoreMetrics.NEW_VIOLATIONS)).thenReturn(new Measure()); + when(issuable.issues()).thenReturn(createIssuesForNewMetrics()); + + decorator.decorate(resource, context); + + verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_BLOCKER_VIOLATIONS))); + verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS))); + verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MAJOR_VIOLATIONS))); + verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MINOR_VIOLATIONS))); + verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_INFO_VIOLATIONS))); + verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS))); + } + + List<Issue> createIssues() { + List<Issue> issues = newArrayList(); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(Severity.CRITICAL).setStatus(Issue.STATUS_OPEN)); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(Severity.CRITICAL).setStatus(Issue.STATUS_REOPENED)); + issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(Severity.MAJOR).setStatus(Issue.STATUS_REOPENED)); + issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(Severity.MINOR).setStatus(Issue.STATUS_OPEN)); + return issues; + } + + List<Issue> createIssuesForNewMetrics() { + List<Issue> issues = newArrayList(); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow).setStatus(Issue.STATUS_OPEN)); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_OPEN)); + issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreationDate(fiveDaysAgo).setStatus(Issue.STATUS_REOPENED)); + issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_REOPENED)); + issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreationDate(fiveDaysAgo).setStatus(Issue.STATUS_OPEN)); + issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_OPEN)); + return issues; + } + + class IsVariationRuleMeasure extends ArgumentMatcher<Measure> { + Metric metric = null; + Rule rule = null; + Double var1 = null; + Double var2 = null; + + public IsVariationRuleMeasure(Metric metric, Rule rule, Double var1, Double var2) { + this.metric = metric; + this.rule = rule; + this.var1 = var1; + this.var2 = var2; + } + + public boolean matches(Object o) { + if (!(o instanceof RuleMeasure)) { + return false; + } + RuleMeasure m = (RuleMeasure) o; + return ObjectUtils.equals(metric, m.getMetric()) && + ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) && + ObjectUtils.equals(var1, m.getVariation1()) && + ObjectUtils.equals(var2, m.getVariation2()); + } + } + + class IsVariationMeasure extends ArgumentMatcher<Measure> { + Metric metric = null; + Double var1 = null; + Double var2 = null; + + public IsVariationMeasure(Metric metric, Double var1, Double var2) { + this.metric = metric; + this.var1 = var1; + this.var2 = var2; + } + + public boolean matches(Object o) { + if (!(o instanceof Measure)) { + return false; + } + Measure m = (Measure) o; + return ObjectUtils.equals(metric, m.getMetric()) && + ObjectUtils.equals(var1, m.getVariation1()) && + ObjectUtils.equals(var2, m.getVariation2()) && + !(m instanceof RuleMeasure); + } + } + + class IsMetricMeasure extends ArgumentMatcher<Measure> { + Metric metric = null; + + public IsMetricMeasure(Metric metric) { + this.metric = metric; + } + + public boolean matches(Object o) { + if (!(o instanceof Measure)) { + return false; + } + Measure m = (Measure) o; + return ObjectUtils.equals(metric, m.getMetric()); + } + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/CoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/CoverageDecoratorTest.java new file mode 100644 index 00000000000..5994f3e72e6 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/CoverageDecoratorTest.java @@ -0,0 +1,143 @@ +/* + * 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.compute; + +import java.util.Collection; +import org.junit.Before; +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.resources.Project; +import org.sonar.api.resources.Scopes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class CoverageDecoratorTest { + private CoverageDecorator decorator; + private final Project project = mock(Project.class); + + @Before + public void before() { + when(project.getScope()).thenReturn(Scopes.PROJECT); + decorator = new CoverageDecorator(); + } + + @Test + public void should_use_metrics() { + Collection<Metric> metrics = decorator.usedMetrics(); + + assertThat(metrics).containsOnly(CoreMetrics.LINES_TO_COVER, CoreMetrics.UNCOVERED_LINES, CoreMetrics.NEW_LINES_TO_COVER, + CoreMetrics.NEW_UNCOVERED_LINES, CoreMetrics.CONDITIONS_TO_COVER, CoreMetrics.UNCOVERED_CONDITIONS, + CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS); + } + + @Test + public void coverage() { + DecoratorContext context = mockContext(50, 40, 10, 8); + + decorator.decorate(project, context); + + // (50-40 covered lines + 10-8 covered conditions) / (50 lines + 10 conditions) + verify(context).saveMeasure(CoreMetrics.COVERAGE, 20.0); + } + + @Test + public void coverageCanBe0() { + DecoratorContext context = mockContext(50, 50, 5, 5); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.COVERAGE, 0.0); + } + + @Test + public void coverageCanBe100() { + DecoratorContext context = mockContext(50, 0, 5, 0); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.COVERAGE, 100.0); + } + + @Test + public void noCoverageIfNoElements() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(project, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.COVERAGE), anyDouble()); + } + + @Test + public void should_count_elements_for_new_code() { + Measure newLines = measureWithVariation(1, 100.0); + Measure newConditions = measureWithVariation(1, 1.0); + DecoratorContext context = mockNewContext(newLines, null, null, newConditions); + + long count = decorator.countElementsForNewCode(context, 1); + + assertThat(count).isEqualTo(101).isEqualTo(100 + 1); + } + + @Test + public void should_count_covered_elements_for_new_code() { + Measure newLines = measureWithVariation(1, 100.0); + Measure newUncoveredConditions = measureWithVariation(1, 10.0); + Measure newUncoveredLines = measureWithVariation(1, 5.0); + Measure newConditions = measureWithVariation(1, 1.0); + DecoratorContext context = mockNewContext(newLines, newUncoveredConditions, newUncoveredLines, newConditions); + + long count = decorator.countCoveredElementsForNewCode(context, 1); + + assertThat(count).isEqualTo(86).isEqualTo(100 + 1 - 10 - 5); + } + + private static DecoratorContext mockContext(int lines, int uncoveredLines, int conditions, int uncoveredConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.LINES_TO_COVER, (double) lines)); + when(context.getMeasure(CoreMetrics.UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.UNCOVERED_LINES, (double) uncoveredLines)); + when(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.CONDITIONS_TO_COVER, (double) conditions)); + when(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, (double) uncoveredConditions)); + return context; + } + + private static DecoratorContext mockNewContext(Measure newLines, Measure newUncoveredConditions, Measure newUncoveredLines, Measure newConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NEW_LINES_TO_COVER)).thenReturn(newLines); + when(context.getMeasure(CoreMetrics.NEW_UNCOVERED_LINES)).thenReturn(newUncoveredLines); + when(context.getMeasure(CoreMetrics.NEW_UNCOVERED_CONDITIONS)).thenReturn(newUncoveredConditions); + when(context.getMeasure(CoreMetrics.NEW_CONDITIONS_TO_COVER)).thenReturn(newConditions); + return context; + } + + private static Measure measureWithVariation(int period, double variation) { + Measure measure = mock(Measure.class); + when(measure.getVariation(period)).thenReturn(variation); + return measure; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/DirectoriesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/DirectoriesDecoratorTest.java new file mode 100644 index 00000000000..5e854bc9913 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/DirectoriesDecoratorTest.java @@ -0,0 +1,90 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.Collections; +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.Directory; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; + +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DirectoriesDecoratorTest { + + @Test + public void doNotInsertZeroOnFiles() { + DirectoriesDecorator decorator = new DirectoriesDecorator(); + Resource file = File.create("foo.php"); + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(file, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.DIRECTORIES), anyDouble()); + } + + @Test + public void directoryCountsForOne() { + DirectoriesDecorator decorator = new DirectoriesDecorator(); + Resource directory = Directory.create("org/foo"); + DecoratorContext context = mock(DecoratorContext.class); + decorator.decorate(directory, context); + verify(context).saveMeasure(CoreMetrics.DIRECTORIES, 1.0); + } + + @Test + public void countProjectDirectories() { + DirectoriesDecorator decorator = new DirectoriesDecorator(); + Resource project = new Project("project"); + DecoratorContext context = mock(DecoratorContext.class); + + when(context.getChildrenMeasures(CoreMetrics.DIRECTORIES)).thenReturn(Arrays.<Measure>asList( + new Measure(CoreMetrics.DIRECTORIES, 1.0), + new Measure(CoreMetrics.DIRECTORIES, 1.0), + new Measure(CoreMetrics.DIRECTORIES, 1.0) + )); + decorator.decorate(project, context); + verify(context).saveMeasure(CoreMetrics.DIRECTORIES, 3.0); + } + + @Test + public void noProjectValueWhenOnlyPackages() { + DirectoriesDecorator decorator = new DirectoriesDecorator(); + Resource project = new Project("project"); + DecoratorContext context = mock(DecoratorContext.class); + when(context.getChildrenMeasures(CoreMetrics.DIRECTORIES)).thenReturn(Collections.<Measure>emptyList()); + when(context.getChildrenMeasures(CoreMetrics.PACKAGES)).thenReturn(Arrays.<Measure>asList( + new Measure(CoreMetrics.PACKAGES, 1.0), + new Measure(CoreMetrics.PACKAGES, 1.0) + )); + decorator.decorate(project, context); + verify(context, never()).saveMeasure(eq(CoreMetrics.DIRECTORIES), anyDouble()); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/FilesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/FilesDecoratorTest.java new file mode 100644 index 00000000000..f750aebcdde --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/FilesDecoratorTest.java @@ -0,0 +1,111 @@ +/* + * 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.compute; + +import java.util.Arrays; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +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.Qualifiers; +import org.sonar.api.resources.Resource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class FilesDecoratorTest { + + private FilesDecorator decorator; + + @Mock + private DecoratorContext context; + + @Mock + private Resource resource; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + decorator = new FilesDecorator(); + } + + @Test + public void generatesMetrics() { + assertThat(decorator.generateDirectoriesMetric()).isEqualTo(CoreMetrics.FILES); + } + + @Test + public void shouldExecute() { + assertThat(decorator.shouldExecuteOnProject(mock(Project.class))).isEqualTo(true); + } + + @Test + public void shouldNotSaveIfMeasureAlreadyExists() { + when(context.getMeasure(CoreMetrics.FILES)).thenReturn(new Measure(CoreMetrics.FILES, 1.0)); + + decorator.decorate(resource, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.FILES), anyDouble()); + } + + @Test + public void shouldSaveOneForFile() { + when(resource.getQualifier()).thenReturn(Qualifiers.FILE); + + decorator.decorate(resource, context); + + verify(context, times(1)).saveMeasure(eq(CoreMetrics.FILES), eq(1d)); + } + + @Test + public void shouldSaveOneForClass() { + when(resource.getQualifier()).thenReturn(Qualifiers.CLASS); + + decorator.decorate(resource, context); + + verify(context, times(1)).saveMeasure(eq(CoreMetrics.FILES), eq(1d)); + } + + @Test + public void shouldSumChildren() { + when(context.getChildrenMeasures(CoreMetrics.FILES)).thenReturn(Arrays.asList(new Measure(CoreMetrics.FILES, 2.0), new Measure(CoreMetrics.FILES, 3.0))); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(eq(CoreMetrics.FILES), eq(5.0)); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/ItBranchCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/ItBranchCoverageDecoratorTest.java new file mode 100644 index 00000000000..0e5b70a37e3 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/ItBranchCoverageDecoratorTest.java @@ -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.compute; + +import org.junit.Before; +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.Qualifiers; +import org.sonar.api.resources.Scopes; + +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ItBranchCoverageDecoratorTest { + private final ItBranchCoverageDecorator decorator = new ItBranchCoverageDecorator(); + private final Project resource = mock(Project.class); + + @Before + public void setUp() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(resource.getQualifier()).thenReturn(Qualifiers.PROJECT); + } + + @Test + public void shouldSaveBranchCoverage() { + DecoratorContext context = mockContext(20, 15); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.IT_BRANCH_COVERAGE, 25.0); + } + + @Test + public void shouldNotSaveBranchCoverageIfMissingConditions() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(resource, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.IT_BRANCH_COVERAGE), anyDouble()); + } + + private static DecoratorContext mockContext(int conditions, int uncoveredConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.IT_CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.IT_CONDITIONS_TO_COVER, (double) conditions)); + when(context.getMeasure(CoreMetrics.IT_UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.IT_UNCOVERED_CONDITIONS, (double) uncoveredConditions)); + return context; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/ItCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/ItCoverageDecoratorTest.java new file mode 100644 index 00000000000..839577fb1aa --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/ItCoverageDecoratorTest.java @@ -0,0 +1,142 @@ +/* + * 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.compute; + +import java.util.Collection; +import org.junit.Before; +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.resources.Project; +import org.sonar.api.resources.Scopes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ItCoverageDecoratorTest { + private final ItCoverageDecorator decorator = new ItCoverageDecorator(); + private final Project project = mock(Project.class); + + @Before + public void before() { + when(project.getScope()).thenReturn(Scopes.PROJECT); + } + + @Test + public void should_use_metrics() { + Collection<Metric> metrics = decorator.usedMetrics(); + + assertThat(metrics).containsOnly(CoreMetrics.IT_LINES_TO_COVER, CoreMetrics.IT_UNCOVERED_LINES, CoreMetrics.NEW_IT_LINES_TO_COVER, + CoreMetrics.NEW_IT_UNCOVERED_LINES, CoreMetrics.IT_CONDITIONS_TO_COVER, CoreMetrics.IT_UNCOVERED_CONDITIONS, + CoreMetrics.NEW_IT_CONDITIONS_TO_COVER, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS); + } + + @Test + public void coverage() { + DecoratorContext context = mockContext(50, 40, 10, 8); + + decorator.decorate(project, context); + + // (50-40 covered lines + 10-8 covered conditions) / (50 lines + 10 conditions) + verify(context).saveMeasure(CoreMetrics.IT_COVERAGE, 20.0); + } + + @Test + public void coverageCanBe0() { + DecoratorContext context = mockContext(50, 50, 5, 5); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.IT_COVERAGE, 0.0); + } + + @Test + public void coverageCanBe100() { + DecoratorContext context = mockContext(50, 0, 5, 0); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.IT_COVERAGE, 100.0); + } + + @Test + public void noCoverageIfNoElements() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(project, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.IT_COVERAGE), anyDouble()); + } + + @Test + public void should_count_elements_for_new_code() { + Measure newLines = measureWithVariation(1, 100.0); + Measure newConditions = measureWithVariation(1, 1.0); + DecoratorContext context = mockNewContext(newLines, null, null, newConditions); + + long count = decorator.countElementsForNewCode(context, 1); + + assertThat(count).isEqualTo(101).isEqualTo(100 + 1); + } + + @Test + public void should_count_covered_elements_for_new_code() { + Measure newLines = measureWithVariation(1, 100.0); + Measure newUncoveredConditions = measureWithVariation(1, 10.0); + Measure newUncoveredLines = measureWithVariation(1, 5.0); + Measure newConditions = measureWithVariation(1, 1.0); + DecoratorContext context = mockNewContext(newLines, newUncoveredConditions, newUncoveredLines, newConditions); + + long count = decorator.countCoveredElementsForNewCode(context, 1); + + assertThat(count).isEqualTo(86).isEqualTo(100 + 1 - 10 - 5); + } + + private static DecoratorContext mockContext(int lines, int uncoveredLines, int conditions, int uncoveredConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.IT_LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.IT_LINES_TO_COVER, (double) lines)); + when(context.getMeasure(CoreMetrics.IT_UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.IT_UNCOVERED_LINES, (double) uncoveredLines)); + when(context.getMeasure(CoreMetrics.IT_CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.IT_CONDITIONS_TO_COVER, (double) conditions)); + when(context.getMeasure(CoreMetrics.IT_UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.IT_UNCOVERED_CONDITIONS, (double) uncoveredConditions)); + return context; + } + + private static DecoratorContext mockNewContext(Measure newLines, Measure newUncoveredConditions, Measure newUncoveredLines, Measure newConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NEW_IT_LINES_TO_COVER)).thenReturn(newLines); + when(context.getMeasure(CoreMetrics.NEW_IT_UNCOVERED_LINES)).thenReturn(newUncoveredLines); + when(context.getMeasure(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS)).thenReturn(newUncoveredConditions); + when(context.getMeasure(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER)).thenReturn(newConditions); + return context; + } + + private static Measure measureWithVariation(int period, double variation) { + Measure measure = mock(Measure.class); + when(measure.getVariation(period)).thenReturn(variation); + return measure; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/ItLineCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/ItLineCoverageDecoratorTest.java new file mode 100644 index 00000000000..95e7177b840 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/ItLineCoverageDecoratorTest.java @@ -0,0 +1,99 @@ +/* + * 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.compute; + +import java.util.List; +import org.junit.Before; +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.resources.Project; +import org.sonar.api.resources.Scopes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ItLineCoverageDecoratorTest { + private final ItLineCoverageDecorator decorator = new ItLineCoverageDecorator(); + private final Project project = mock(Project.class); + + @Before + public void before() { + when(project.getScope()).thenReturn(Scopes.PROJECT); + } + + @Test + public void should_depend_on_coverage_metrics() { + List<Metric> metrics = decorator.dependsUponMetrics(); + + assertThat(metrics).containsOnly(CoreMetrics.IT_UNCOVERED_LINES, CoreMetrics.IT_LINES_TO_COVER, CoreMetrics.NEW_IT_UNCOVERED_LINES, CoreMetrics.NEW_IT_LINES_TO_COVER); + } + + @Test + public void lineCoverage() { + DecoratorContext context = mockContext(50, 10); + + decorator.decorate(project, context); + + // 50-10 covered lines / 50 lines + verify(context).saveMeasure(CoreMetrics.IT_LINE_COVERAGE, 80.0); + } + + @Test + public void zeroCoveredLines() { + DecoratorContext context = mockContext(50, 50); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.IT_LINE_COVERAGE, 0.0); + } + + @Test + public void allCoveredLines() { + DecoratorContext context = mockContext(50, 00); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.IT_LINE_COVERAGE, 100.0); + } + + @Test + public void noLineCoverageIfNoLines() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(project, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.IT_LINE_COVERAGE), anyDouble()); + } + + private static DecoratorContext mockContext(int lines, int uncoveredLines) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.IT_LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.IT_LINES_TO_COVER, (double) lines)); + when(context.getMeasure(CoreMetrics.IT_UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.IT_UNCOVERED_LINES, (double) uncoveredLines)); + return context; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/LineCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/LineCoverageDecoratorTest.java new file mode 100644 index 00000000000..2a49f83aaf4 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/LineCoverageDecoratorTest.java @@ -0,0 +1,98 @@ +/* + * 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.compute; + +import org.junit.Before; +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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class LineCoverageDecoratorTest { + + private LineCoverageDecorator decorator; + private final Project project = mock(Project.class); + + @Before + public void before() { + when(project.getScope()).thenReturn(Scopes.PROJECT); + decorator = new LineCoverageDecorator(); + } + + @Test + public void should_depend_on_coverage_metrics() { + assertThat(decorator.dependsUponMetrics()).containsOnly(CoreMetrics.UNCOVERED_LINES, CoreMetrics.LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES, + CoreMetrics.NEW_LINES_TO_COVER); + } + + @Test + public void lineCoverage() { + DecoratorContext context = mockContext(50, 10); + + decorator.decorate(project, context); + + // 50-10 covered lines / 50 lines + verify(context).saveMeasure(CoreMetrics.LINE_COVERAGE, 80.0); + } + + @Test + public void zeroCoveredLines() { + DecoratorContext context = mockContext(50, 50); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.LINE_COVERAGE, 0.0); + } + + @Test + public void allCoveredLines() { + DecoratorContext context = mockContext(50, 00); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.LINE_COVERAGE, 100.0); + } + + @Test + public void noLineCoverageIfNoLines() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(project, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.LINE_COVERAGE), anyDouble()); + } + + private static DecoratorContext mockContext(int lines, int uncoveredLines) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.LINES_TO_COVER, (double) lines)); + when(context.getMeasure(CoreMetrics.UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.UNCOVERED_LINES, (double) uncoveredLines)); + return context; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/ManualMeasureDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/ManualMeasureDecoratorTest.java new file mode 100644 index 00000000000..bd86438ad05 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/ManualMeasureDecoratorTest.java @@ -0,0 +1,52 @@ +/* + * 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.compute; + +import org.junit.Test; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.File; +import org.sonar.api.test.IsMeasure; +import org.sonar.core.metric.DefaultMetricFinder; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class ManualMeasureDecoratorTest extends AbstractDbUnitTestCase { + + private Metric reviewNote = new Metric.Builder("review_note", "Note", Metric.ValueType.FLOAT).create().setId(2); + + @Test + public void testCopyManualMeasures() throws Exception { + setupData("testCopyManualMeasures"); + + File javaFile = File.create("Foo.java"); + javaFile.setId(40); + + ManualMeasureDecorator decorator = new ManualMeasureDecorator(getSession(), new DefaultMetricFinder(getSessionFactory())); + DecoratorContext context = mock(DecoratorContext.class); + decorator.decorate(javaFile, context); + + verify(context).saveMeasure(argThat(new IsMeasure(reviewNote, 6.0, "six"))); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/NewCoverageAggregatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/NewCoverageAggregatorTest.java new file mode 100644 index 00000000000..7951e3a7add --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/NewCoverageAggregatorTest.java @@ -0,0 +1,90 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.Collections; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Matchers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NewCoverageAggregatorTest { + + @Test + public void shouldNotSaveDataWhenNoMeasures() { + NewCoverageAggregator aggregator = new NewCoverageAggregator(); + DecoratorContext context = mock(DecoratorContext.class); + when(context.getChildrenMeasures(CoreMetrics.NEW_LINES_TO_COVER)).thenReturn(Collections.<Measure>emptyList()); + + aggregator.aggregate(context, CoreMetrics.NEW_LINES_TO_COVER, 3); + + verify(context, never()).saveMeasure(Matchers.<Measure>anyObject()); + } + + @Test + public void shouldNotsetZeroWhenNoValueOnPeriod() { + NewCoverageAggregator aggregator = new NewCoverageAggregator(); + DecoratorContext context = mock(DecoratorContext.class); + when(context.getChildrenMeasures(CoreMetrics.NEW_LINES_TO_COVER)).thenReturn(Arrays.asList(newMeasure(null, 3.0, 2.0), newMeasure(null, 13.0, null))); + + aggregator.aggregate(context, CoreMetrics.NEW_LINES_TO_COVER, 3); + + verify(context).saveMeasure(argThat(new ArgumentMatcher<Measure>() { + @Override + public boolean matches(Object o) { + Measure m = (Measure) o; + return m.getVariation1() == null; + } + })); + } + + @Test + public void shouldSumValues() { + NewCoverageAggregator aggregator = new NewCoverageAggregator(); + DecoratorContext context = mock(DecoratorContext.class); + when(context.getChildrenMeasures(CoreMetrics.NEW_LINES_TO_COVER)).thenReturn(Arrays.asList(newMeasure(null, 3.0, 2.0), newMeasure(null, 13.0, null))); + + aggregator.aggregate(context, CoreMetrics.NEW_LINES_TO_COVER, 3); + + verify(context).saveMeasure(argThat(new ArgumentMatcher<Measure>() { + @Override + public boolean matches(Object o) { + Measure m = (Measure) o; + return m.getVariation2() == 16.0 && m.getVariation3() == 2.0; + } + })); + } + + private Measure newMeasure(Double variation1, Double variation2, Double variation3) { + return new Measure(CoreMetrics.NEW_LINES_TO_COVER) + .setVariation1(variation1) + .setVariation2(variation2) + .setVariation3(variation3); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/NewCoverageFileAnalyzerTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/NewCoverageFileAnalyzerTest.java new file mode 100644 index 00000000000..6deb673fa50 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/NewCoverageFileAnalyzerTest.java @@ -0,0 +1,297 @@ +/* + * 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.compute; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentMatcher; +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.resources.File; +import org.sonar.api.resources.Resource; +import org.sonar.api.utils.DateUtils; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.batch.protocol.output.BatchReport.Changesets.Changeset; +import org.sonar.batch.protocol.output.BatchReportWriter; +import org.sonar.batch.report.ReportPublisher; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NewCoverageFileAnalyzerTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private DecoratorContext context; + private NewCoverageFileAnalyzer decorator; + private BatchReportWriter writer; + + @Before + public void prepare() throws Exception { + context = mock(DecoratorContext.class); + Resource f = File.create("src/Foo.java").setEffectiveKey("foo:src/Foo.java"); + when(context.getResource()).thenReturn(f); + BatchComponentCache cache = new BatchComponentCache(); + cache.add(f, null); + List<AbstractNewCoverageFileAnalyzer.PeriodStruct> structs = Arrays.asList( + new AbstractNewCoverageFileAnalyzer.PeriodStruct(1, newDate("2009-12-25")), + new AbstractNewCoverageFileAnalyzer.PeriodStruct(3, newDate("2011-02-18"))); + ReportPublisher publishReportJob = mock(ReportPublisher.class); + java.io.File reportBaseDir = temp.newFolder(); + when(publishReportJob.getReportDir()).thenReturn(reportBaseDir); + writer = new BatchReportWriter(reportBaseDir); + decorator = new NewCoverageFileAnalyzer(structs, publishReportJob, cache); + + } + + @Test + public void shouldDoNothingIfNoScmData() { + when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)) + .thenReturn(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=10")); + + decorator.doDecorate(context); + verify(context, never()).saveMeasure(any(Measure.class)); + } + + @Test + public void shouldDoNothingIfNoCoverageData() { + writer.writeComponentChangesets(BatchReport.Changesets.newBuilder() + .setComponentRef(1) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2008-05-18T00:00:00+0000").getTime()) + .build()) + .addChangesetIndexByLine(0) + .build()); + + decorator.doDecorate(context); + + verify(context, never()).saveMeasure(any(Measure.class)); + } + + @Test + public void shouldGetNewLines() { + when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn( + new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "10=2;11=3")); + writer.writeComponentChangesets(BatchReport.Changesets.newBuilder() + .setComponentRef(1) + .addChangeset(Changeset.newBuilder() + .build()) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2007-01-15T00:00:00+0000").getTime()) + .build()) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2011-01-01T00:00:00+0000").getTime()) + .build()) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(1) + .addChangesetIndexByLine(2) + .build()); + + 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, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 3, null))); + + // 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() { + 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")); + writer.writeComponentChangesets(BatchReport.Changesets.newBuilder() + .setComponentRef(1) + .addChangeset(Changeset.newBuilder() + .build()) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2007-01-15T00:00:00+0000").getTime()) + .build()) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2011-01-01T00:00:00+0000").getTime()) + .build()) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(1) + .addChangesetIndexByLine(2) + .build()); + + 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, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 3, null))); + + // 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() { + 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")); + writer.writeComponentChangesets(BatchReport.Changesets.newBuilder() + .setComponentRef(1) + .addChangeset(Changeset.newBuilder() + .build()) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2007-01-15T00:00:00+0000").getTime()) + .build()) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2011-01-01T00:00:00+0000").getTime()) + .build()) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(1) + .addChangesetIndexByLine(2) + .build()); + + 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))); + } + + @Test + public void shouldLeaveNullValueWhenNothingHasChanged() { + + when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA)).thenReturn( + new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "2=1;3=1")); + when(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE)).thenReturn( + new Measure(CoreMetrics.CONDITIONS_BY_LINE, "2=1")); + when(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE)).thenReturn( + new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "2=1")); + writer.writeComponentChangesets(BatchReport.Changesets.newBuilder() + .setComponentRef(1) + .addChangeset(Changeset.newBuilder() + .setDate(DateUtils.parseDateTime("2008-08-02T13:56:37+0200").getTime()) + .build()) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .addChangesetIndexByLine(0) + .build()); + + decorator.doDecorate(context); + + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 1, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 1, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 1, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 1, null))); + + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_LINES_TO_COVER, 3, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_LINES, 3, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_CONDITIONS_TO_COVER, 3, null))); + verify(context).saveMeasure(argThat(new VariationMatcher(CoreMetrics.NEW_UNCOVERED_CONDITIONS, 3, null))); + } + + static class VariationMatcher extends ArgumentMatcher<Measure> { + Metric metric; + int index; + Double variation; + + VariationMatcher(Metric metric, int index, Double variation) { + this.metric = metric; + this.index = index; + this.variation = variation; + } + + @Override + 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; + } + } + + private Date newDate(String s) throws ParseException { + return new SimpleDateFormat(DateUtils.DATE_FORMAT).parse(s); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/OverallBranchCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/OverallBranchCoverageDecoratorTest.java new file mode 100644 index 00000000000..9c5e585c969 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/OverallBranchCoverageDecoratorTest.java @@ -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.compute; + +import org.junit.Before; +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.Qualifiers; +import org.sonar.api.resources.Scopes; + +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OverallBranchCoverageDecoratorTest { + private final OverallBranchCoverageDecorator decorator = new OverallBranchCoverageDecorator(); + private final Project resource = mock(Project.class); + + @Before + public void setUp() { + when(resource.getScope()).thenReturn(Scopes.PROJECT); + when(resource.getQualifier()).thenReturn(Qualifiers.PROJECT); + } + + @Test + public void shouldSaveBranchCoverage() { + DecoratorContext context = mockContext(20, 15); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(CoreMetrics.OVERALL_BRANCH_COVERAGE, 25.0); + } + + @Test + public void shouldNotSaveBranchCoverageIfMissingConditions() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(resource, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.OVERALL_BRANCH_COVERAGE), anyDouble()); + } + + private static DecoratorContext mockContext(int conditions, int uncoveredConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER, (double) conditions)); + when(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS, (double) uncoveredConditions)); + return context; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/OverallCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/OverallCoverageDecoratorTest.java new file mode 100644 index 00000000000..d694227c754 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/OverallCoverageDecoratorTest.java @@ -0,0 +1,142 @@ +/* + * 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.compute; + +import java.util.Collection; +import org.junit.Before; +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.resources.Project; +import org.sonar.api.resources.Scopes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OverallCoverageDecoratorTest { + private final OverallCoverageDecorator decorator = new OverallCoverageDecorator(); + private final Project project = mock(Project.class); + + @Before + public void before() { + when(project.getScope()).thenReturn(Scopes.PROJECT); + } + + @Test + public void should_use_metrics() { + Collection<Metric> metrics = decorator.usedMetrics(); + + assertThat(metrics).containsOnly(CoreMetrics.OVERALL_LINES_TO_COVER, CoreMetrics.OVERALL_UNCOVERED_LINES, CoreMetrics.NEW_OVERALL_LINES_TO_COVER, + CoreMetrics.NEW_OVERALL_UNCOVERED_LINES, CoreMetrics.OVERALL_CONDITIONS_TO_COVER, CoreMetrics.OVERALL_UNCOVERED_CONDITIONS, + CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS); + } + + @Test + public void coverage() { + DecoratorContext context = mockContext(50, 40, 10, 8); + + decorator.decorate(project, context); + + // (50-40 covered lines + 10-8 covered conditions) / (50 lines + 10 conditions) + verify(context).saveMeasure(CoreMetrics.OVERALL_COVERAGE, 20.0); + } + + @Test + public void coverageCanBe0() { + DecoratorContext context = mockContext(50, 50, 5, 5); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.OVERALL_COVERAGE, 0.0); + } + + @Test + public void coverageCanBe100() { + DecoratorContext context = mockContext(50, 0, 5, 0); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.OVERALL_COVERAGE, 100.0); + } + + @Test + public void noCoverageIfNoElements() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(project, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.OVERALL_COVERAGE), anyDouble()); + } + + @Test + public void should_count_elements_for_new_code() { + Measure newLines = measureWithVariation(1, 100.0); + Measure newConditions = measureWithVariation(1, 1.0); + DecoratorContext context = mockNewContext(newLines, null, null, newConditions); + + long count = decorator.countElementsForNewCode(context, 1); + + assertThat(count).isEqualTo(101).isEqualTo(100 + 1); + } + + @Test + public void should_count_covered_elements_for_new_code() { + Measure newLines = measureWithVariation(1, 100.0); + Measure newUncoveredConditions = measureWithVariation(1, 10.0); + Measure newUncoveredLines = measureWithVariation(1, 5.0); + Measure newConditions = measureWithVariation(1, 1.0); + DecoratorContext context = mockNewContext(newLines, newUncoveredConditions, newUncoveredLines, newConditions); + + long count = decorator.countCoveredElementsForNewCode(context, 1); + + assertThat(count).isEqualTo(86).isEqualTo(100 + 1 - 10 - 5); + } + + private static DecoratorContext mockContext(int lines, int uncoveredLines, int conditions, int uncoveredConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.OVERALL_LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.OVERALL_LINES_TO_COVER, (double) lines)); + when(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.OVERALL_UNCOVERED_LINES, (double) uncoveredLines)); + when(context.getMeasure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.OVERALL_CONDITIONS_TO_COVER, (double) conditions)); + when(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS, (double) uncoveredConditions)); + return context; + } + + private static DecoratorContext mockNewContext(Measure newLines, Measure newUncoveredConditions, Measure newUncoveredLines, Measure newConditions) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.NEW_OVERALL_LINES_TO_COVER)).thenReturn(newLines); + when(context.getMeasure(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES)).thenReturn(newUncoveredLines); + when(context.getMeasure(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS)).thenReturn(newUncoveredConditions); + when(context.getMeasure(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER)).thenReturn(newConditions); + return context; + } + + private static Measure measureWithVariation(int period, double variation) { + Measure measure = mock(Measure.class); + when(measure.getVariation(period)).thenReturn(variation); + return measure; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/OverallLineCoverageDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/OverallLineCoverageDecoratorTest.java new file mode 100644 index 00000000000..17d54526a35 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/OverallLineCoverageDecoratorTest.java @@ -0,0 +1,100 @@ +/* + * 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.compute; + +import java.util.List; +import org.junit.Before; +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.resources.Project; +import org.sonar.api.resources.Scopes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OverallLineCoverageDecoratorTest { + private final OverallLineCoverageDecorator decorator = new OverallLineCoverageDecorator(); + private final Project project = mock(Project.class); + + @Before + public void before() { + when(project.getScope()).thenReturn(Scopes.PROJECT); + } + + @Test + public void should_depend_on_coverage_metrics() { + List<Metric> metrics = decorator.dependsUponMetrics(); + + assertThat(metrics).containsOnly(CoreMetrics.OVERALL_UNCOVERED_LINES, CoreMetrics.OVERALL_LINES_TO_COVER, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES, + CoreMetrics.NEW_OVERALL_LINES_TO_COVER); + } + + @Test + public void lineCoverage() { + DecoratorContext context = mockContext(50, 10); + + decorator.decorate(project, context); + + // 50-10 covered lines / 50 lines + verify(context).saveMeasure(CoreMetrics.OVERALL_LINE_COVERAGE, 80.0); + } + + @Test + public void zeroCoveredLines() { + DecoratorContext context = mockContext(50, 50); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.OVERALL_LINE_COVERAGE, 0.0); + } + + @Test + public void allCoveredLines() { + DecoratorContext context = mockContext(50, 00); + + decorator.decorate(project, context); + + verify(context).saveMeasure(CoreMetrics.OVERALL_LINE_COVERAGE, 100.0); + } + + @Test + public void noLineCoverageIfNoLines() { + DecoratorContext context = mock(DecoratorContext.class); + + decorator.decorate(project, context); + + verify(context, never()).saveMeasure(eq(CoreMetrics.OVERALL_LINE_COVERAGE), anyDouble()); + } + + private static DecoratorContext mockContext(int lines, int uncoveredLines) { + DecoratorContext context = mock(DecoratorContext.class); + when(context.getMeasure(CoreMetrics.OVERALL_LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.OVERALL_LINES_TO_COVER, (double) lines)); + when(context.getMeasure(CoreMetrics.OVERALL_UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.OVERALL_UNCOVERED_LINES, (double) uncoveredLines)); + return context; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest.java new file mode 100644 index 00000000000..2c48e9c6614 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest.java @@ -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.compute; + +import java.util.Arrays; +import org.junit.Test; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.DateUtils; +import org.sonar.batch.components.PastSnapshot; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TimeMachineConfigurationPersisterTest extends AbstractDbUnitTestCase { + + @Test + public void shouldSaveConfigurationInSnapshotsTable() { + setupData("shared"); + + TimeMachineConfiguration timeMachineConfiguration = mock(TimeMachineConfiguration.class); + PastSnapshot vs1 = new PastSnapshot("days", DateUtils.parseDate("2009-01-25"), getSession().getSingleResult(Snapshot.class, "id", 100)) + .setModeParameter("30").setIndex(1); + PastSnapshot vs3 = new PastSnapshot("version", DateUtils.parseDate("2008-12-13"), getSession().getSingleResult(Snapshot.class, "id", 300)) + .setModeParameter("1.2.3").setIndex(3); + when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Arrays.asList(vs1, vs3)); + Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", 1000); + + BatchComponentCache resourceCache = new BatchComponentCache(); + Project project = new Project("foo"); + resourceCache.add(project, null).setSnapshot(projectSnapshot); + + TimeMachineConfigurationPersister persister = new TimeMachineConfigurationPersister(timeMachineConfiguration, resourceCache, getSession()); + + persister.persistConfiguration(project); + + checkTables("shouldSaveConfigurationInSnapshotsTable", "snapshots"); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/UnitTestDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/UnitTestDecoratorTest.java new file mode 100644 index 00000000000..eb9bd93a4d1 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/UnitTestDecoratorTest.java @@ -0,0 +1,87 @@ +/* + * 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.compute; + +import java.util.Arrays; +import org.hamcrest.Matchers; +import org.junit.Before; +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.resources.Project; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.doubleThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class UnitTestDecoratorTest { + + private UnitTestDecorator decorator; + private DecoratorContext context; + + @Before + public void setUp() { + decorator = new UnitTestDecorator(); + context = mock(DecoratorContext.class); + } + + @Test + public void generatesMetrics() { + assertThat(decorator.generatesMetrics()).hasSize(5); + } + + @Test + public void doNotDecorateStaticAnalysis() { + Project project = mock(Project.class); + when(project.getAnalysisType()).thenReturn(Project.AnalysisType.STATIC); + assertThat(decorator.shouldExecuteOnProject(project)).isFalse(); + + when(project.getAnalysisType()).thenReturn(Project.AnalysisType.DYNAMIC); + assertThat(decorator.shouldExecuteOnProject(project)).isTrue(); + } + + @Test + public void shouldSumChildren() { + Project project = mock(Project.class); + mockChildrenMeasures(CoreMetrics.TESTS, 3.0); + mockChildrenMeasures(CoreMetrics.TEST_ERRORS, 1.0); + mockChildrenMeasures(CoreMetrics.TEST_FAILURES, 1.0); + mockChildrenMeasures(CoreMetrics.SKIPPED_TESTS, 1.0); + mockChildrenMeasures(CoreMetrics.TEST_EXECUTION_TIME, 1.0); + + decorator.decorate(project, context); + + verify(context).saveMeasure(eq(CoreMetrics.TESTS), eq(6.0)); + verify(context).saveMeasure(eq(CoreMetrics.TEST_ERRORS), eq(2.0)); + verify(context).saveMeasure(eq(CoreMetrics.TEST_FAILURES), eq(2.0)); + verify(context).saveMeasure(eq(CoreMetrics.SKIPPED_TESTS), eq(2.0)); + verify(context).saveMeasure(eq(CoreMetrics.TEST_EXECUTION_TIME), eq(2.0)); + verify(context).saveMeasure(eq(CoreMetrics.TEST_SUCCESS_DENSITY), doubleThat(Matchers.closeTo(33.3, 0.1))); + } + + private void mockChildrenMeasures(Metric metric, double value) { + when(context.getChildrenMeasures(metric)).thenReturn(Arrays.asList(new Measure(metric, value), new Measure(metric, value))); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/compute/VariationDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/compute/VariationDecoratorTest.java new file mode 100644 index 00000000000..21f26e09090 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/compute/VariationDecoratorTest.java @@ -0,0 +1,149 @@ +/* + * 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.compute; + +import java.util.Arrays; +import java.util.Date; +import org.junit.Test; +import org.mockito.Matchers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.measures.RuleMeasure; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.batch.components.PastMeasuresLoader; +import org.sonar.batch.components.PastSnapshot; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class VariationDecoratorTest extends AbstractDbUnitTestCase { + + public static final int NCLOC_ID = 12; + public static final Metric NCLOC = new Metric("ncloc").setId(NCLOC_ID); + + public static final int COVERAGE_ID = 16; + public static final Metric COVERAGE = new Metric("coverage").setId(COVERAGE_ID); + + public static final int VIOLATIONS_ID = 17; + public static final Metric VIOLATIONS = new Metric("violations").setId(VIOLATIONS_ID); + + @Test + public void shouldComputeVariations() { + TimeMachineConfiguration timeMachineConfiguration = mock(TimeMachineConfiguration.class); + VariationDecorator decorator = new VariationDecorator(mock(PastMeasuresLoader.class), mock(MetricFinder.class), timeMachineConfiguration, mock(RuleFinder.class)); + + assertThat(decorator.shouldComputeVariation(new Project("foo"))).isTrue(); + assertThat(decorator.shouldComputeVariation(File.create("foo/bar.c"))).isFalse(); + } + + @Test + public void shouldCompareAndSaveVariation() { + Resource dir = Directory.create("org/foo"); + + PastMeasuresLoader pastMeasuresLoader = mock(PastMeasuresLoader.class); + PastSnapshot pastSnapshot1 = new PastSnapshot("days", new Date()).setIndex(1); + PastSnapshot pastSnapshot3 = new PastSnapshot("days", new Date()).setIndex(3); + + // first past analysis + when(pastMeasuresLoader.getPastMeasures(dir, pastSnapshot1)).thenReturn(Arrays.asList( + new Object[] {NCLOC_ID, null, null, null, 180.0}, + new Object[] {COVERAGE_ID, null, null, null, 75.0})); + + // second past analysis + when(pastMeasuresLoader.getPastMeasures(dir, pastSnapshot3)).thenReturn(Arrays.<Object[]>asList( + new Object[] {NCLOC_ID, null, null, null, 240.0})); + + // current analysis + DecoratorContext context = mock(DecoratorContext.class); + Measure currentNcloc = newMeasure(NCLOC, 200.0); + Measure currentCoverage = newMeasure(COVERAGE, 80.0); + when(context.getMeasures(Matchers.<MeasuresFilter>anyObject())).thenReturn(Arrays.asList(currentNcloc, currentCoverage)); + + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3), mock(RuleFinder.class)); + decorator.decorate(dir, context); + + // context updated for each variation : 2 times for ncloc and 1 time for coverage + verify(context, times(3)).saveMeasure(Matchers.<Measure>anyObject()); + + assertThat(currentNcloc.getVariation1()).isEqualTo(20.0); + assertThat(currentNcloc.getVariation2()).isNull(); + assertThat(currentNcloc.getVariation3()).isEqualTo(-40.0); + + assertThat(currentCoverage.getVariation1()).isEqualTo(5.0); + assertThat(currentCoverage.getVariation2()).isNull(); + assertThat(currentCoverage.getVariation3()).isNull(); + } + + @Test + public void shouldComputeVariationOfRuleMeasures() { + RuleFinder ruleFinder = mock(RuleFinder.class); + + Rule rule1 = Rule.create("repo", "rule1"); + rule1.setId(1); + Rule rule2 = Rule.create("repo", "rule2"); + rule2.setId(2); + + when(ruleFinder.findByKey(rule1.ruleKey())).thenReturn(rule1); + when(ruleFinder.findByKey(rule2.ruleKey())).thenReturn(rule2); + + Resource dir = Directory.create("org/foo"); + + PastMeasuresLoader pastMeasuresLoader = mock(PastMeasuresLoader.class); + PastSnapshot pastSnapshot1 = new PastSnapshot("days", new Date()).setIndex(1); + + // first past analysis + when(pastMeasuresLoader.getPastMeasures(dir, pastSnapshot1)).thenReturn(Arrays.asList( + new Object[] {VIOLATIONS_ID, null, null, null, 180.0},// total + new Object[] {VIOLATIONS_ID, null, null, rule1.getId(), 100.0},// rule 1 + new Object[] {VIOLATIONS_ID, null, null, rule2.getId(), 80.0})); // rule 2 + + // current analysis + DecoratorContext context = mock(DecoratorContext.class); + Measure violations = newMeasure(VIOLATIONS, 200.0); + Measure violationsRule1 = RuleMeasure.createForRule(VIOLATIONS, rule1, 130.0); + Measure violationsRule2 = RuleMeasure.createForRule(VIOLATIONS, rule2, 70.0); + when(context.getMeasures(Matchers.<MeasuresFilter>anyObject())).thenReturn(Arrays.asList(violations, violationsRule1, violationsRule2)); + + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1), ruleFinder); + decorator.decorate(dir, context); + + // context updated for each variation + verify(context, times(3)).saveMeasure(Matchers.<Measure>anyObject()); + + assertThat(violations.getVariation1()).isEqualTo(20.0); + } + + private Measure newMeasure(Metric metric, double value) { + return new Measure(metric, value); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java index 41bdd9c67df..8d24834d97b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java @@ -66,7 +66,7 @@ public class MeasuresMediumTest { .newScanTask(new File(projectDir, "sonar-project.properties")) .start(); - assertThat(result.allMeasures()).hasSize(61); + assertThat(result.allMeasures()).hasSize(90); } @Test @@ -93,7 +93,7 @@ public class MeasuresMediumTest { .build()) .start(); - assertThat(result.allMeasures()).hasSize(25); + assertThat(result.allMeasures()).hasSize(33); assertThat(result.allMeasures()).contains(new DefaultMeasure<Integer>() .forMetric(CoreMetrics.LINES) diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ManualMeasureDecoratorTest/testCopyManualMeasures.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/ManualMeasureDecoratorTest/testCopyManualMeasures.xml new file mode 100644 index 00000000000..0307f16e6e3 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ManualMeasureDecoratorTest/testCopyManualMeasures.xml @@ -0,0 +1,11 @@ +<dataset> + <metrics delete_historical_data="[null]" id="1" NAME="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name="" + enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/> + <metrics delete_historical_data="[null]" id="2" NAME="review_note" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name="" + enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/> + + + <manual_measures id="1" metric_id="2" resource_id="30" value="3.14" text_value="pi" created_at="[null]" updated_at="[null]" description="this is pi" user_login="me"/> + <manual_measures id="2" metric_id="2" resource_id="40" value="6" text_value="six" created_at="[null]" updated_at="[null]" description="this is six" user_login="me"/> + +</dataset>
\ No newline at end of file diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml new file mode 100644 index 00000000000..836bb4630e9 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar</artifactId> + <packaging>pom</packaging> + <version>1.8-SNAPSHOT</version> + +</project>
\ No newline at end of file diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ProjectLinksSensorTest/shouldSaveLinks.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/ProjectLinksSensorTest/shouldSaveLinks.xml new file mode 100644 index 00000000000..a44ea429019 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ProjectLinksSensorTest/shouldSaveLinks.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar</artifactId> + <packaging>pom</packaging> + <version>1.8-SNAPSHOT</version> + <url>http://sonar.codehaus.org</url> + + <organization> + <name>SonarSource SA</name> + <url>http://www.sonarsource.com</url> + </organization> + <inceptionYear>2009</inceptionYear> + + <issueManagement> + <system>jira</system> + <url>http://jira.codehaus.org/browse/SONAR</url> + </issueManagement> + + <mailingLists> + <mailingList> + <name>Sonar users mailing list</name> + <subscribe>http://xircles.codehaus.org/projects/sonar/lists</subscribe> + <unsubscribe>http://xircles.codehaus.org/projects/sonar/lists</unsubscribe> + <post>user@sonar.codehaus.org</post> + <archive>http://www.nabble.com/Sonar-f30151.html</archive> + </mailingList> + </mailingLists> + + <scm> + <connection>scm:svn:http://svn.codehaus.org/sonar/trunk</connection> + <developerConnection>scm:svn:https://svn.codehaus.org/sonar/trunk</developerConnection> + <url>http://svn.sonar.codehaus.org</url> + </scm> + + <ciManagement> + <system>bamboo</system> + <url>http://bamboo.ci.codehaus.org/browse/SONAR/</url> + </ciManagement> + + <licenses> + <license> + <name>GNU Lesser General Public License (LGPL), v.3</name> + <url>http://www.gnu.org/licenses/lgpl.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + +</project>
\ No newline at end of file diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest/shared.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest/shared.xml new file mode 100644 index 00000000000..70c8178d4e8 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest/shared.xml @@ -0,0 +1,42 @@ +<dataset> + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="100" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1225544280000" build_date="1225544280000" version="[null]" path="" + status="P" islast="false" depth="0" /> + + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="200" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1229345880000" build_date="1229345880000" version="[null]" path="" + status="P" islast="false" depth="0" /> + + <!-- Snapshot of previous version --> + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="300" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1229173080000" build_date="1229173080000" version="1.2.3" path="" + status="P" islast="false" depth="0" /> + + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1235566680000" build_date="1235566680000" version="[null]" path="" + status="P" islast="false" depth="0" /> +</dataset> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest/shouldSaveConfigurationInSnapshotsTable-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest/shouldSaveConfigurationInSnapshotsTable-result.xml new file mode 100644 index 00000000000..34fdf2d4d32 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/TimeMachineConfigurationPersisterTest/shouldSaveConfigurationInSnapshotsTable-result.xml @@ -0,0 +1,42 @@ +<dataset> + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="100" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1225544280000" build_date="1225544280000" version="[null]" path="" + status="P" islast="false" depth="0" /> + + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="200" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1229345880000" build_date="1229345880000" version="[null]" path="" + status="P" islast="false" depth="0" /> + + <!-- Snapshot of previous version --> + <snapshots purge_status="[null]" + period1_mode="[null]" period1_param="[null]" period1_date="[null]" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="[null]" period3_param="[null]" period3_date="[null]" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="300" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1229173080000" build_date="1229173080000" version="1.2.3" path="" + status="P" islast="false" depth="0" /> + + <snapshots purge_status="[null]" + period1_mode="days" period1_param="30" period1_date="1225544280000" + period2_mode="[null]" period2_param="[null]" period2_date="[null]" + period3_mode="version" period3_param="1.2.3" period3_date="1229173080000" + period4_mode="[null]" period4_param="[null]" period4_date="[null]" + period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="1235566680000" build_date="1235566680000" version="[null]" path="" + status="P" islast="false" depth="0" /> +</dataset> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shared.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shared.xml new file mode 100644 index 00000000000..519ce8d5157 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shared.xml @@ -0,0 +1,21 @@ +<dataset> + + <rules tags="[null]" system_tags="[null]" id="30" name="Check Header" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck" + plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" status="READY" + is_template="[false]" template_id="[null]"/> + + <rules tags="[null]" system_tags="[null]" id="31" name="Equals Avoid Null" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck" + plugin_config_key="Checker/TreeWalker/EqualsAvoidNull" plugin_name="checkstyle" description="[null]" priority="4" status="READY" + is_template="[false]" template_id="[null]"/> + + <projects id="200" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar" root_id="[null]" + name="Bar" long_name="org.foo.Bar" description="[null]" + enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" /> + + <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" + period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" + id="1000" project_id="200" parent_snapshot_id="[null]" root_project_id="100" root_snapshot_id="[null]" + scope="FIL" qualifier="CLA" created_at="1225544280000" build_date="1225544280000" version="[null]" path="" + status="U" islast="false" depth="3"/> + +</dataset> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shouldCopyPermanentIdFromReferenceViolation-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shouldCopyPermanentIdFromReferenceViolation-result.xml new file mode 100644 index 00000000000..061041849aa --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shouldCopyPermanentIdFromReferenceViolation-result.xml @@ -0,0 +1,21 @@ +<dataset> + + <rules tags="[null]" system_tags="[null]" id="30" name="Check Header" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck" + plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" status="READY" + is_template="[false]" template_id="[null]"/> + + <rules tags="[null]" system_tags="[null]" id="31" name="Equals Avoid Null" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck" + plugin_config_key="Checker/TreeWalker/EqualsAvoidNull" plugin_name="checkstyle" description="[null]" priority="4" status="READY" + is_template="[false]" template_id="[null]"/> + + <projects id="200" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar" root_id="[null]" + name="Bar" long_name="org.foo.Bar" description="[null]" + enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" /> + + <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" + period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="1000" project_id="200" + parent_snapshot_id="[null]" root_project_id="100" root_snapshot_id="[null]" + scope="FIL" qualifier="CLA" created_at="1225544280000" build_date="1225544280000" version="[null]" path="" + status="U" islast="false" depth="3"/> + +</dataset> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shouldSaveViolations-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shouldSaveViolations-result.xml new file mode 100644 index 00000000000..f1bbc0bda6f --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationPersisterDecoratorTest/shouldSaveViolations-result.xml @@ -0,0 +1,20 @@ +<dataset> + <rules tags="[null]" system_tags="[null]" id="30" name="Check Header" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck" + plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" status="READY" + is_template="[false]" template_id="[null]"/> + + <rules tags="[null]" system_tags="[null]" id="31" name="Equals Avoid Null" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck" + plugin_config_key="Checker/TreeWalker/EqualsAvoidNull" plugin_name="checkstyle" description="[null]" priority="4" status="READY" + is_template="[false]" template_id="[null]"/> + + <projects id="200" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar" root_id="[null]" + name="Bar" long_name="org.foo.Bar" description="[null]" + enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" /> + + <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" + period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="1000" project_id="200" + parent_snapshot_id="[null]" root_project_id="100" root_snapshot_id="[null]" + scope="FIL" qualifier="CLA" created_at="1225544280000" build_date="1225544280000" version="[null]" path="" + status="U" islast="false" depth="3"/> + +</dataset> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example1-v1.txt b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example1-v1.txt new file mode 100644 index 00000000000..1920333ddb6 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example1-v1.txt @@ -0,0 +1,12 @@ +package example1; + +public class Toto { + + public void doSomething() { + // doSomething + } + + public void doSomethingElse() { + // doSomethingElse + } +} diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example1-v2.txt b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example1-v2.txt new file mode 100644 index 00000000000..231532452b2 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example1-v2.txt @@ -0,0 +1,22 @@ +package example1; + +public class Toto { + + public Toto(){} + + public void doSomethingNew() { + // doSomethingNew + } + + public void doSomethingElseNew() { + // doSomethingElseNew + } + + public void doSomething() { + // doSomething + } + + public void doSomethingElse() { + // doSomethingElse + } +} diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example2-v1.txt b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example2-v1.txt new file mode 100644 index 00000000000..a920afe459b --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example2-v1.txt @@ -0,0 +1,7 @@ +package example2; + +public class Toto { + void method1() { + System.out.println("toto"); + } +} diff --git a/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example2-v2.txt b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example2-v2.txt new file mode 100644 index 00000000000..c5c8250cf65 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/compute/ViolationTrackingTest/example2-v2.txt @@ -0,0 +1,16 @@ +package example2; + +public class Toto { + + void method2() { + System.out.println("toto"); + } + + void method1() { + System.out.println("toto"); + } + + void method3() { + System.out.println("toto"); + } +} |