/*
* Copyright 2013 - 2014 Felix Müller
*
* This file is part of CodeQ Invest.
*
* CodeQ Invest is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CodeQ Invest 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CodeQ Invest. If not, see <http://www.gnu.org/licenses/>.
*/
package org.codeqinvest.investment;
import org.codeqinvest.investment.profit.ProfitCalculator;
import org.codeqinvest.quality.Artefact;
import org.codeqinvest.quality.QualityCriteria;
import org.codeqinvest.quality.QualityRequirement;
import org.codeqinvest.quality.QualityViolation;
import org.codeqinvest.quality.analysis.QualityAnalysis;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class QualityInvestmentPlanServiceTest {
private QualityInvestmentPlanService investmentPlanService;
private ProfitCalculator profitCalculator;
private QualityAnalysis analysis;
@Before
public void setUp() {
profitCalculator = mock(ProfitCalculator.class);
investmentPlanService = new QualityInvestmentPlanService(profitCalculator);
QualityViolation violation1 = new QualityViolation(new Artefact("org.A", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cc", "<", 5.0)), 10, 0, 0.0, "ncloc");
QualityViolation violation2 = new QualityViolation(new Artefact("org.project.B", ""), createRequirementOnlyWithCriteria(new QualityCriteria("rfc", "<=", 50.0)), 60, 0, 0.0, "ncloc");
QualityViolation violation3 = new QualityViolation(new Artefact("C", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cov", ">", 80.0)), 10, 0, 0.0, "ncloc");
analysis = QualityAnalysis.success(null, Arrays.asList(violation1, violation2, violation3));
// overall rc = 10 + 40 + 30 = 80
// possible overall profit = 300
when(profitCalculator.calculateProfit(violation1)).thenReturn(100.0);
when(profitCalculator.calculateProfit(violation2)).thenReturn(80.2);
when(profitCalculator.calculateProfit(violation3)).thenReturn(120.1);
}
private QualityRequirement createRequirementOnlyWithCriteria(QualityCriteria criteria) {
return new QualityRequirement(null, 0, 0, 0.0, "", criteria);
}
@Test
public void zeroProfitWhenThereAreNoQualityViolations() {
QualityAnalysis analysisWithoutViolations = QualityAnalysis.success(null, Collections.<QualityViolation>emptyList());
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysisWithoutViolations, "", 100);
assertThat(qualityInvestmentPlan.getProfitInMinutes())
.as("When there are no quality violations the overall profit should be zero.")
.isZero();
}
@Test
public void calculatesOverallProfitForAllQualityViolations() {
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 80);
assertThat(qualityInvestmentPlan.getProfitInMinutes())
.as("The overall profit should be calculated based on all quality violations.")
.isEqualTo(300);
assertThat(qualityInvestmentPlan.getInvestmentInMinutes()).isEqualTo(80);
}
@Test
public void whenInvestmentIsBiggerThanPossibleOverallProfitOnlyThePossibleProfitShouldBeCalculated() {
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 100);
assertThat(qualityInvestmentPlan.getProfitInMinutes()).isEqualTo(300);
assertThat(qualityInvestmentPlan.getInvestmentInMinutes()).isEqualTo(80);
}
@Test
public void whenInvestmentIsSmallerThanPossibleOverallProfitOnlyTheInvestedAmountShouldBeCalculated() {
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 35);
assertThat(qualityInvestmentPlan.getProfitInMinutes()).isEqualTo(220);
assertThat(qualityInvestmentPlan.getInvestmentInMinutes()).isEqualTo(20);
}
@Test
public void calculatesRoiBasedOnRemediationCostsAndProfit() {
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 80);
assertThat(qualityInvestmentPlan.getRoi()).isEqualTo(375); // 300 / 80 * 100
}
@Test
public void containsAllQualityViolationsSortedByProfitAndRemediationCosts() {
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 80);
QualityInvestmentPlanEntry[] investments = qualityInvestmentPlan.getEntries().toArray(new QualityInvestmentPlanEntry[0]);
assertThat(investments).hasSize(3);
assertThat(investments[0].getArtefactLongName()).isEqualTo("C");
assertThat(investments[1].getArtefactLongName()).isEqualTo("org.A");
assertThat(investments[2].getArtefactLongName()).isEqualTo("org.project.B");
}
@Test
public void shouldOnlyConsiderArtefactThatStartWithBasePackageName() {
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "org.project", 80);
assertThat(qualityInvestmentPlan.getEntries()).hasSize(1);
assertThat(qualityInvestmentPlan.getEntries().iterator().next().getArtefactLongName()).isEqualTo("org.project.B");
}
@Test
public void shouldChooseViolationsWithMoreProfit() {
QualityViolation violation1 = new QualityViolation(new Artefact("org.A", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cc", "<", 5.0)), 50, 0, 0.0, "ncloc");
QualityViolation violation2 = new QualityViolation(new Artefact("org.project.B", ""), createRequirementOnlyWithCriteria(new QualityCriteria("rfc", "<=", 50.0)), 50, 0, 0.0, "ncloc");
QualityViolation violation3 = new QualityViolation(new Artefact("C", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cov", ">", 80.0)), 10, 0, 0.0, "ncloc");
analysis = QualityAnalysis.success(null, Arrays.asList(violation1, violation2, violation3));
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 50);
assertThat(qualityInvestmentPlan.getEntries()).hasSize(1);
assertThat(qualityInvestmentPlan.getEntries().iterator().next().getArtefactLongName()).isEqualTo("C");
}
@Test
public void shouldChooseViolationWithBiggerRoiWhenProfitIsTheSame() {
QualityViolation violationWithSmallerRoi = new QualityViolation(new Artefact("org.A", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cc", "<", 5.0)), 50, 0, 0.0, "ncloc");
QualityViolation violationWithBiggerRoi = new QualityViolation(new Artefact("B", ""), createRequirementOnlyWithCriteria(new QualityCriteria("rfc", "<=", 50.0)), 40, 0, 0.0, "ncloc");
analysis = QualityAnalysis.success(null, Arrays.asList(violationWithSmallerRoi, violationWithBiggerRoi));
when(profitCalculator.calculateProfit(violationWithSmallerRoi)).thenReturn(100.0);
when(profitCalculator.calculateProfit(violationWithBiggerRoi)).thenReturn(100.0);
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 50);
assertThat(qualityInvestmentPlan.getEntries()).hasSize(1);
assertThat(qualityInvestmentPlan.getEntries().iterator().next().getArtefactLongName()).isEqualTo("B");
}
@Test
public void shouldNotConsiderViolationsWithNegativeProfit() {
when(profitCalculator.calculateProfit(any(QualityViolation.class))).thenReturn(-0.1);
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "", 50);
assertThat(qualityInvestmentPlan.getProfitInMinutes()).isZero();
assertThat(qualityInvestmentPlan.getRoi()).isZero();
assertThat(qualityInvestmentPlan.getEntries()).isEmpty();
}
@Test
public void shouldGenerateInvestmentPlanForOneArtefact() {
QualityViolation violation1 = new QualityViolation(new Artefact("org.project.A", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cc", "<", 5.0)), 50, 0, 0.0, "ncloc");
QualityViolation violation2 = new QualityViolation(new Artefact("org.project.AB", ""), createRequirementOnlyWithCriteria(new QualityCriteria("rfc", "<=", 50.0)), 50, 0, 0.0, "ncloc");
QualityViolation violation3 = new QualityViolation(new Artefact("org.project.ABC", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cov", ">", 80.0)), 10, 0, 0.0, "ncloc");
analysis = QualityAnalysis.success(null, Arrays.asList(violation1, violation2, violation3));
when(profitCalculator.calculateProfit(violation1)).thenReturn(100.0);
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "org.project.A", 50);
assertThat(qualityInvestmentPlan.getEntries()).hasSize(1);
assertThat(qualityInvestmentPlan.getEntries().iterator().next().getArtefactLongName()).isEqualTo("org.project.A");
}
@Test
public void shouldGenerateInvestmentPlanForOneSubPackage() {
QualityViolation violation1 = new QualityViolation(new Artefact("org.project.a.test.A", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cc", "<", 5.0)), 50, 0, 0.0, "ncloc");
QualityViolation violation2 = new QualityViolation(new Artefact("org.project.b.B", ""), createRequirementOnlyWithCriteria(new QualityCriteria("rfc", "<=", 50.0)), 50, 0, 0.0, "ncloc");
QualityViolation violation3 = new QualityViolation(new Artefact("org.project.C", ""), createRequirementOnlyWithCriteria(new QualityCriteria("cov", ">", 80.0)), 10, 0, 0.0, "ncloc");
analysis = QualityAnalysis.success(null, Arrays.asList(violation1, violation2, violation3));
when(profitCalculator.calculateProfit(violation1)).thenReturn(100.0);
QualityInvestmentPlan qualityInvestmentPlan = investmentPlanService.computeInvestmentPlan(analysis, "org.project.a.test.A", 50);
assertThat(qualityInvestmentPlan.getEntries()).hasSize(1);
assertThat(qualityInvestmentPlan.getEntries().iterator().next().getArtefactLongName()).isEqualTo("org.project.a.test.A");
}
}