From e71d93b4fa657654006f4036e85c6ec135f30e7b Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 16 May 2018 17:37:57 +0200 Subject: [PATCH] SONAR-10684 Create internal API to block analysis based on ncloc --- .../org/sonar/db/measure/LiveMeasureDao.java | 6 +- .../sonar/db/measure/LiveMeasureMapper.java | 3 +- .../org/sonar/db/measure/SumNclocDbQuery.java | 58 +++++++ .../sonar/db/measure/LiveMeasureMapper.xml | 6 +- .../sonar/db/measure/LiveMeasureDaoTest.java | 27 +++- .../step/ReportComputationSteps.java | 3 + .../step/PostMeasuresComputationCheck.java | 58 +++++++ .../PostMeasuresComputationChecksStep.java | 91 +++++++++++ .../step/ReportComputationStepsTest.java | 16 +- ...PostMeasuresComputationChecksStepTest.java | 141 ++++++++++++++++++ 10 files changed, 392 insertions(+), 17 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/measure/SumNclocDbQuery.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationCheck.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStep.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStepTest.java diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java index 52cfdb646ae..dc07aa60d50 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java @@ -88,7 +88,11 @@ public class LiveMeasureDao implements Dao { * If Main Branch = 100 LOCs and the "largest long-lived branch" is 80 LOCs, I'm expecting to consider the value 100. */ public long sumNclocOfBiggestLongLivingBranch(DbSession dbSession) { - Long ncloc = mapper(dbSession).sumNclocOfBiggestLongLivingBranch(NCLOC_KEY, KeyType.BRANCH, BranchType.LONG); + return sumNclocOfBiggestLongLivingBranch(dbSession, SumNclocDbQuery.builder().build()); + } + + public long sumNclocOfBiggestLongLivingBranch(DbSession dbSession, SumNclocDbQuery dbQuery) { + Long ncloc = mapper(dbSession).sumNclocOfBiggestLongLivingBranch(NCLOC_KEY, KeyType.BRANCH, BranchType.LONG, dbQuery.getProjectUuidToExclude()); return ncloc == null ? 0L : ncloc; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java index c0fca2f6a1e..ed3977d684b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java @@ -46,7 +46,8 @@ public interface LiveMeasureMapper { Long sumNclocOfBiggestLongLivingBranch( @Param("ncloc") String nclocKey, @Param("branch") KeyType branchOrPullRequest, - @Param("branchType") BranchType branchType); + @Param("branchType") BranchType branchType, + @Nullable @Param("projectUuidToExclude") String projectUuidToExclude); void insert( @Param("dto") LiveMeasureDto dto, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/SumNclocDbQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/SumNclocDbQuery.java new file mode 100644 index 00000000000..b9e3a502632 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/SumNclocDbQuery.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.db.measure; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class SumNclocDbQuery { + + private final String projectUuidToExclude; + + public SumNclocDbQuery(Builder builder) { + projectUuidToExclude = builder.projectUuidToExclude; + } + + @CheckForNull + public String getProjectUuidToExclude() { + return projectUuidToExclude; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String projectUuidToExclude; + + private Builder() { + // to enforce use of builder() + } + + public Builder setProjectUuidToExclude(@Nullable String projectUuidToExclude) { + this.projectUuidToExclude = projectUuidToExclude; + return this; + } + + public SumNclocDbQuery build() { + return new SumNclocDbQuery(this); + } + } +} diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml index 6b571f5dc71..999aa79aec9 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml @@ -41,7 +41,7 @@ inner join metrics m on m.id = lm.metric_id inner join projects p on p.uuid = lm.component_uuid inner join project_branches b on b.uuid = p.uuid - where + m.name = #{ncloc, jdbcType=VARCHAR} and p.enabled = ${_true} and p.scope = 'PRJ' @@ -49,6 +49,10 @@ and p.copy_component_uuid is null and b.branch_type = #{branchType, jdbcType=VARCHAR} and b.key_type = #{branch, jdbcType=VARCHAR} + + and b.project_uuid <> #{projectUuidToExclude,jdbcType=VARCHAR} + + group by b.project_uuid ) sumncloc diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java index ed1b1e10e0f..51d83d6d795 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java @@ -211,9 +211,9 @@ public class LiveMeasureDaoTest { db.measures().insertLiveMeasure(simpleProject, ncloc, m -> m.setValue(10d)); ComponentDto projectWithBiggerLongLivingBranch = db.components().insertMainBranch(organization); - ComponentDto bigLongLivingBranch = db.components().insertProjectBranch(projectWithBiggerLongLivingBranch, b -> b.setBranchType(BranchType.LONG)); + ComponentDto bigLongLivingLongBranch = db.components().insertProjectBranch(projectWithBiggerLongLivingBranch, b -> b.setBranchType(BranchType.LONG)); db.measures().insertLiveMeasure(projectWithBiggerLongLivingBranch, ncloc, m -> m.setValue(100d)); - db.measures().insertLiveMeasure(bigLongLivingBranch, ncloc, m -> m.setValue(200d)); + db.measures().insertLiveMeasure(bigLongLivingLongBranch, ncloc, m -> m.setValue(200d)); ComponentDto projectWithLinesButNoLoc = db.components().insertMainBranch(organization); db.measures().insertLiveMeasure(projectWithLinesButNoLoc, lines, m -> m.setValue(365d)); @@ -234,6 +234,29 @@ public class LiveMeasureDaoTest { assertThat(result).isEqualTo(0L); } + @Test + public void countNcloc_and_exclude_project() { + OrganizationDto organization = db.organizations().insert(); + MetricDto ncloc = db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.toString())); + + ComponentDto simpleProject = db.components().insertMainBranch(organization); + db.measures().insertLiveMeasure(simpleProject, ncloc, m -> m.setValue(10d)); + + ComponentDto projectWithBiggerLongLivingBranch = db.components().insertMainBranch(organization); + ComponentDto bigLongLivingBranch = db.components().insertProjectBranch(projectWithBiggerLongLivingBranch, b -> b.setBranchType(BranchType.LONG)); + db.measures().insertLiveMeasure(projectWithBiggerLongLivingBranch, ncloc, m -> m.setValue(100d)); + db.measures().insertLiveMeasure(bigLongLivingBranch, ncloc, m -> m.setValue(200d)); + + ComponentDto projectToExclude = db.components().insertMainBranch(organization); + ComponentDto projectToExcludeBranch = db.components().insertProjectBranch(projectToExclude, b -> b.setBranchType(BranchType.LONG)); + db.measures().insertLiveMeasure(projectToExclude, ncloc, m -> m.setValue(300d)); + db.measures().insertLiveMeasure(projectToExcludeBranch, ncloc, m -> m.setValue(400d)); + + long result = underTest.sumNclocOfBiggestLongLivingBranch(db.getSession(), SumNclocDbQuery.builder().setProjectUuidToExclude(projectToExclude.uuid()).build()); + + assertThat(result).isEqualTo(10L + 200L); + } + @Test public void insert_data() { byte[] data = "text_value".getBytes(StandardCharsets.UTF_8); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java index 8cb71963b0b..852be83599e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java @@ -25,6 +25,7 @@ import org.sonar.server.computation.task.container.TaskContainer; import org.sonar.server.computation.task.projectanalysis.filemove.FileMoveDetectionStep; import org.sonar.server.computation.task.step.ComputationStep; import org.sonar.server.computation.task.step.ExecuteStatelessInitExtensionsStep; +import org.sonar.server.computation.task.step.PostMeasuresComputationChecksStep; /** * Ordered list of steps classes and instances to be executed for batch processing @@ -70,6 +71,8 @@ public class ReportComputationSteps extends AbstractComputationSteps { LoadMeasureComputersStep.class, ExecuteVisitorsStep.class, + PostMeasuresComputationChecksStep.class, + // Must be executed after computation of all measures ComputeMeasureVariationsStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationCheck.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationCheck.java new file mode 100644 index 00000000000..2ba9c1ca31d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationCheck.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.server.computation.task.step; + +import org.sonar.api.ExtensionPoint; +import org.sonar.api.ce.ComputeEngineSide; +import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; + +/** + * Extension point that is called during processing of a task + * by {@link PostMeasuresComputationChecksStep}. + * + * It is stateless, the same instance is reused for all tasks. + * As a consequence Compute Engine task components can't be injected + * as dependencies. + */ +@ComputeEngineSide +@ExtensionPoint +public interface PostMeasuresComputationCheck { + + /** + * This method can make the task fail by throwing a {@link RuntimeException} + */ + void onCheck(Context context); + + interface Context { + + /** + * Return the project UUID, as returned by {@link AnalysisMetadataHolder#getProject()#getUuid()}. + * + * It means that when analyzing a non main-branch, it will be the UUID of the project, not the UUId of the branch/pull-request. + */ + String getProjectUuid(); + + /** + * Return the ncloc computed for the current analysis + */ + int getNcloc(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStep.java new file mode 100644 index 00000000000..844643fa22e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStep.java @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.server.computation.task.step; + +import com.google.common.base.Optional; +import org.sonar.api.ce.ComputeEngineSide; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; +import org.sonar.server.computation.task.projectanalysis.measure.Measure; +import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository; +import org.sonar.server.computation.task.projectanalysis.metric.Metric; +import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository; + +/** + * Execute {@link PostMeasuresComputationCheck} instances in no specific order. + * If an extension fails (throws an exception), consecutive extensions + * won't be called. + */ +@ComputeEngineSide +public class PostMeasuresComputationChecksStep implements ComputationStep { + + private final TreeRootHolder treeRootHolder; + private final MetricRepository metricRepository; + private final MeasureRepository measureRepository; + private final AnalysisMetadataHolder analysisMetadataHolder; + private final PostMeasuresComputationCheck[] extensions; + + public PostMeasuresComputationChecksStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository, + AnalysisMetadataHolder analysisMetadataHolder, + PostMeasuresComputationCheck[] extensions) { + this.treeRootHolder = treeRootHolder; + this.metricRepository = metricRepository; + this.measureRepository = measureRepository; + this.analysisMetadataHolder = analysisMetadataHolder; + this.extensions = extensions; + } + + /** + * Used when zero {@link PostMeasuresComputationCheck} are registered into container. + */ + public PostMeasuresComputationChecksStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository, + AnalysisMetadataHolder analysisMetadataHolder) { + this(treeRootHolder, metricRepository, measureRepository, analysisMetadataHolder, new PostMeasuresComputationCheck[0]); + } + + @Override + public void execute() { + PostMeasuresComputationCheck.Context context = new ContextImpl(); + for (PostMeasuresComputationCheck extension : extensions) { + extension.onCheck(context); + } + } + + @Override + public String getDescription() { + return "Checks executed after computation of measures"; + } + + private class ContextImpl implements PostMeasuresComputationCheck.Context { + + @Override + public String getProjectUuid() { + return analysisMetadataHolder.getProject().getUuid(); + } + + @Override + public int getNcloc() { + Metric nclocMetric = metricRepository.getByKey(CoreMetrics.NCLOC_KEY); + Optional nclocMeasure = measureRepository.getRawMeasure(treeRootHolder.getRoot(), nclocMetric); + return nclocMeasure.isPresent() ? nclocMeasure.get().getIntValue() : 0; + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationStepsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationStepsTest.java index 8e42d781866..3df6f260507 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationStepsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationStepsTest.java @@ -24,8 +24,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.core.platform.ComponentContainer; -import org.sonar.core.platform.ContainerPopulator; -import org.sonar.server.computation.task.container.TaskContainer; import org.sonar.server.computation.task.container.TaskContainerImpl; import static org.mockito.Mockito.mock; @@ -39,11 +37,8 @@ public class ReportComputationStepsTest { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Component not found: " + ExtractReportStep.class); - TaskContainerImpl computeEngineContainer = new TaskContainerImpl(new ComponentContainer(), new ContainerPopulator() { - @Override - public void populateContainer(TaskContainer container) { - // do nothing - } + TaskContainerImpl computeEngineContainer = new TaskContainerImpl(new ComponentContainer(), container -> { + // do nothing }); Lists.newArrayList(new ReportComputationSteps(computeEngineContainer).instances()); @@ -60,11 +55,8 @@ public class ReportComputationStepsTest { addSingleton(reportExtractionStep); } }; - TaskContainerImpl computeEngineContainer = new TaskContainerImpl(componentContainer, new ContainerPopulator() { - @Override - public void populateContainer(TaskContainer container) { - // do nothing - } + TaskContainerImpl computeEngineContainer = new TaskContainerImpl(componentContainer, container -> { + // do nothing }); Lists.newArrayList(new ReportComputationSteps(computeEngineContainer).instances()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStepTest.java new file mode 100644 index 00000000000..b7bf64abac1 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/step/PostMeasuresComputationChecksStepTest.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.server.computation.task.step; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule; +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule; +import org.sonar.server.computation.task.projectanalysis.measure.Measure; +import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepositoryRule; +import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule; +import org.sonar.server.computation.task.step.PostMeasuresComputationCheck.Context; +import org.sonar.server.project.Project; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.sonar.api.measures.CoreMetrics.NCLOC; +import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.DUMB_PROJECT; + +public class PostMeasuresComputationChecksStepTest { + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT); + @Rule + public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(NCLOC); + @Rule + public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + @Rule + public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void execute_extensions() { + PostMeasuresComputationCheck check1 = mock(PostMeasuresComputationCheck.class); + PostMeasuresComputationCheck check2 = mock(PostMeasuresComputationCheck.class); + + newStep(check1, check2).execute(); + + InOrder inOrder = inOrder(check1, check2); + inOrder.verify(check1).onCheck(any(Context.class)); + inOrder.verify(check2).onCheck(any(Context.class)); + } + + @Test + public void context_contains_project_uuid_from_analysis_metada_holder() { + analysisMetadataHolder.setProject(new Project("project_uuid", "project_key", "project_name")); + PostMeasuresComputationCheck check = mock(PostMeasuresComputationCheck.class); + + newStep(check).execute(); + + ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); + verify(check).onCheck(contextArgumentCaptor.capture()); + assertThat(contextArgumentCaptor.getValue().getProjectUuid()).isEqualTo("project_uuid"); + } + + @Test + public void context_contains_ncloc_when_available() { + PostMeasuresComputationCheck check = mock(PostMeasuresComputationCheck.class); + measureRepository.addRawMeasure(DUMB_PROJECT.getReportAttributes().getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(10)); + + newStep(check).execute(); + + ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); + verify(check).onCheck(contextArgumentCaptor.capture()); + assertThat(contextArgumentCaptor.getValue().getNcloc()).isEqualTo(10); + } + + @Test + public void ncloc_is_zero_in_context_when_not_available() { + PostMeasuresComputationCheck check = mock(PostMeasuresComputationCheck.class); + + newStep(check).execute(); + + ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); + verify(check).onCheck(contextArgumentCaptor.capture()); + assertThat(contextArgumentCaptor.getValue().getNcloc()).isEqualTo(0); + } + + @Test + public void do_nothing_if_no_extensions() { + // no failure + newStep().execute(); + } + + @Test + public void fail_if_an_extension_throws_an_exception() { + PostMeasuresComputationCheck check1 = mock(PostMeasuresComputationCheck.class); + PostMeasuresComputationCheck check2 = mock(PostMeasuresComputationCheck.class); + doThrow(new IllegalStateException("BOOM")).when(check2).onCheck(any(Context.class)); + PostMeasuresComputationCheck check3 = mock(PostMeasuresComputationCheck.class); + + try { + newStep(check1, check2, check3).execute(); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("BOOM"); + verify(check1).onCheck(any(Context.class)); + verify(check3, never()).onCheck(any(Context.class)); + } + } + + @Test + public void test_getDescription() { + assertThat(newStep().getDescription()).isNotEmpty(); + } + + private PostMeasuresComputationChecksStep newStep(PostMeasuresComputationCheck... postMeasuresComputationChecks) { + if (postMeasuresComputationChecks.length == 0){ + return new PostMeasuresComputationChecksStep(treeRootHolder, metricRepository, measureRepository, analysisMetadataHolder); + } + return new PostMeasuresComputationChecksStep(treeRootHolder, metricRepository, measureRepository, analysisMetadataHolder, postMeasuresComputationChecks); + } +} -- 2.39.5