package net.thucydides.core.requirements.reports; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import net.thucydides.core.ThucydidesSystemProperty; import net.thucydides.core.issues.IssueTracking; import net.thucydides.core.model.*; import net.thucydides.core.releases.ReleaseManager; import net.thucydides.core.reports.TestOutcomes; import net.thucydides.core.reports.html.ReportNameProvider; import net.thucydides.core.requirements.RequirementsTagProvider; import net.thucydides.core.requirements.model.Requirement; import net.thucydides.core.util.EnvironmentVariables; import java.util.Collection; import java.util.List; import java.util.Set; import static ch.lambdaj.Lambda.*; import static org.hamcrest.Matchers.hasItem; /** * A set of test results for a list of high-level requirements. */ public class RequirementsOutcomes { private final List<RequirementOutcome> requirementOutcomes; private final TestOutcomes testOutcomes; private final Optional<Requirement> parentRequirement; private final EnvironmentVariables environmentVariables; private final IssueTracking issueTracking; private final List<RequirementsTagProvider> requirementsTagProviders; private final ReleaseManager releaseManager; public final static Integer DEFAULT_TESTS_PER_REQUIREMENT = 4; public RequirementsOutcomes(List<Requirement> requirements, TestOutcomes testOutcomes, IssueTracking issueTracking, EnvironmentVariables environmentVariables, List<RequirementsTagProvider> requirementsTagProviders) { this(null, requirements, testOutcomes, issueTracking, environmentVariables, requirementsTagProviders); } public RequirementsOutcomes(Requirement parentRequirement, List<Requirement> requirements, TestOutcomes testOutcomes, IssueTracking issueTracking, EnvironmentVariables environmentVariables, List<RequirementsTagProvider> requirementsTagProviders) { this.testOutcomes = testOutcomes; this.parentRequirement = Optional.fromNullable(parentRequirement); this.environmentVariables = environmentVariables; this.issueTracking = issueTracking; this.requirementsTagProviders = requirementsTagProviders; this.requirementOutcomes = buildRequirementOutcomes(requirements, requirementsTagProviders); this.releaseManager = new ReleaseManager(environmentVariables, new ReportNameProvider()); } private List<RequirementOutcome> buildRequirementOutcomes(List<Requirement> requirements, List<RequirementsTagProvider> requirementsTagProviders) { List<RequirementOutcome> outcomes = Lists.newArrayList(); for (Requirement requirement : requirements) { buildRequirements(outcomes, requirementsTagProviders, requirement); } return outcomes; } public RequirementsOutcomes requirementsOfType(String type) { List<Requirement> matchingRequirements = Lists.newArrayList(); List<TestOutcome> matchingTests = Lists.newArrayList(); for(RequirementOutcome requirementOutcome : getFlattenedRequirementOutcomes()) { if (requirementOutcome.getRequirement().getType().equalsIgnoreCase(type)) { matchingRequirements.add(requirementOutcome.getRequirement()); matchingTests.addAll(requirementOutcome.getTestOutcomes().getOutcomes()); } } return new RequirementsOutcomes(matchingRequirements, TestOutcomes.of(matchingTests), issueTracking, environmentVariables, requirementsTagProviders); } private void buildRequirements(List<RequirementOutcome> outcomes, List<RequirementsTagProvider> requirementsTagProviders, Requirement requirement) { TestOutcomes outcomesForRequirement = testOutcomes.forRequirement(requirement); int requirementsWithoutTests = countRequirementsWithoutTestsIn(requirement); int estimatedUnimplementedTests = requirementsWithoutTests * estimatedTestsPerRequirement(); outcomes.add(new RequirementOutcome(requirement, outcomesForRequirement, requirementsWithoutTests, estimatedUnimplementedTests, issueTracking)); } private int countRequirementsWithoutTestsIn(Requirement rootRequirement) { List<Requirement> flattenedRequirements = getFlattenedRequirements(rootRequirement); int requirementsWithoutTests = 0; for(Requirement requirement : flattenedRequirements) { TestOutcomes matchingOutcomes = testOutcomes.withTag(requirement.asTag()); if (matchingOutcomes.getTotal() == 0) { requirementsWithoutTests++; } } return requirementsWithoutTests; } public int getFlattenedRequirementCount() { int requirementCount = 0; for (RequirementOutcome requirement : requirementOutcomes) { requirementCount += requirement.getFlattenedRequirementCount(); } return requirementCount; } private List<Requirement> getFlattenedRequirements(Requirement rootRequirement) { List<Requirement> flattenedRequirements = Lists.newArrayList(); flattenedRequirements.add(rootRequirement); flattenedRequirements.addAll(rootRequirement.getNestedChildren()); return flattenedRequirements; } public Optional<Requirement> getParentRequirement() { return parentRequirement; } public int getRequirementCount() { return requirementOutcomes.size(); } public List<RequirementOutcome> getRequirementOutcomes() { return ImmutableList.copyOf(requirementOutcomes); } public String getType() { if (requirementOutcomes.isEmpty()) { return "requirement"; } else { return requirementOutcomes.get(0).getRequirement().getType(); } } public String getChildrenType() { return typeOfFirstChildPresent(); } public List<String> getTypes() { List<Requirement> requirements = getAllRequirements(); List<String> types = Lists.newArrayList(); for (Requirement requirement : requirements) { if (!types.contains(requirement.getType())) { types.add(requirement.getType()); } } return types; } private String typeOfFirstChildPresent() { for (RequirementOutcome outcome : requirementOutcomes) { if (!outcome.getRequirement().getChildren().isEmpty()) { Requirement firstChildRequirement = outcome.getRequirement().getChildren().get(0); return firstChildRequirement.getType(); } } return null; } public TestOutcomes getTestOutcomes() { return testOutcomes; } @Override public String toString() { return "RequirementsOutcomes{" + "requirementOutcomes=" + requirementOutcomes + ", parentRequirement=" + parentRequirement + '}'; } public int getCompletedRequirementsCount() { int completedRequirements = 0; for (RequirementOutcome requirementOutcome : requirementOutcomes) { if (requirementOutcome.isComplete()) { completedRequirements++; } } return completedRequirements; } public int getFailingRequirementsCount() { int failingRequirements = 0; for (RequirementOutcome requirementOutcome : requirementOutcomes) { if (requirementOutcome.isFailure() || requirementOutcome.isError()) { failingRequirements++; } } return failingRequirements; } public int getPendingRequirementsCount() { int total = 0; for (RequirementOutcome requirementOutcome : requirementOutcomes) { if (requirementOutcome.isPending()) { total++; } } return total; } public int getIgnoredRequirementsCount() { int total = 0; for (RequirementOutcome requirementOutcome : requirementOutcomes) { if (requirementOutcome.isIgnored()) { total++; } } return total; } public int getRequirementsWithoutTestsCount() { int requirementsWithNoTests = 0; List<RequirementOutcome> flattenedRequirementOutcomes = getFlattenedRequirementOutcomes(); for (Requirement requirement : getAllRequirements()) { if (!testsRecordedFor(flattenedRequirementOutcomes, requirement)) { requirementsWithNoTests++; } } return requirementsWithNoTests; } private boolean testsRecordedFor(List<RequirementOutcome> outcomes, Requirement requirement) { for (RequirementOutcome outcome : outcomes) { if (outcome.testsRequirement(requirement) && outcome.getTestCount() > 0) { return true; } } return false; } private List<Requirement> getAllRequirements() { List<Requirement> allRequirements = Lists.newArrayList(); for (RequirementOutcome outcome : requirementOutcomes) { addFlattenedRequirements(outcome.getRequirement(), allRequirements); } return ImmutableList.copyOf(allRequirements); } public int getTotalRequirements() { return getAllRequirements().size(); } private void addFlattenedRequirements(Requirement requirement, List<Requirement> allRequirements) { allRequirements.add(requirement); for (Requirement child : requirement.getChildren()) { addFlattenedRequirements(child, allRequirements); } } List<RequirementOutcome> flattenedRequirementOutcomes = null; public List<RequirementOutcome> getFlattenedRequirementOutcomes() { if (flattenedRequirementOutcomes == null) { flattenedRequirementOutcomes = getFlattenedRequirementOutcomes(requirementOutcomes); } return flattenedRequirementOutcomes; } public List<RequirementOutcome> getFlattenedRequirementOutcomes(List<RequirementOutcome> outcomes) { Set<RequirementOutcome> flattenedOutcomes = Sets.newHashSet(); for (RequirementOutcome requirementOutcome : outcomes) { flattenedOutcomes.add(requirementOutcome); for(Requirement requirement : requirementOutcome.getRequirement().getChildren()) { // TestTag requirementTag = TestTag.withName(requirement.getName()).andType(requirement.getType()); // TestOutcomes testOutcomesForRequirement = requirementOutcome.getTestOutcomes().withTag(requirementTag); TestOutcomes testOutcomesForRequirement = requirementOutcome.getTestOutcomes().withTag(requirement.asTag()); flattenedOutcomes.add(new RequirementOutcome(requirement, testOutcomesForRequirement, issueTracking)); List<Requirement> childRequirements = requirement.getChildren(); RequirementsOutcomes childOutcomes = new RequirementsOutcomes(childRequirements, testOutcomesForRequirement, issueTracking, environmentVariables, requirementsTagProviders); flattenedOutcomes.addAll(getFlattenedRequirementOutcomes(childOutcomes.getRequirementOutcomes())); } } return ImmutableList.copyOf(flattenedOutcomes); } public OutcomeCounter getTotal() { return count(TestType.ANY); } public OutcomeCounter count(TestType testType) { return new OutcomeCounter(testType, getTestOutcomes()); } public OutcomeCounter count(String testType) { return count(TestType.valueOf(testType.toUpperCase())); } public int getTotalTestCount() { return testOutcomes.getTotal(); } /** * @return Formatted version of the test coverage metrics */ public RequirementsPercentageFormatter getFormattedPercentage() { return new RequirementsPercentageFormatter(getProportion()); } public RequirementsPercentageFormatter getFormattedPercentage(String testType) { return new RequirementsPercentageFormatter(proportionOf(testType)); } public RequirementsPercentageFormatter getFormattedPercentage(TestType testType) { return new RequirementsPercentageFormatter(proportionOf(testType)); } private int totalEstimatedAndImplementedTests() { int totalImplementedTests = getTotalTestCount(); return totalImplementedTests + getEstimatedUnimplementedTests(); } public int getEstimatedUnimplementedTests() { return getRequirementsWithoutTestsCount() * estimatedTestsPerRequirement(); } private int estimatedTestsPerRequirement() { return environmentVariables.getPropertyAsInteger(ThucydidesSystemProperty.THUCYDIDES_ESTIMATED_TESTS_PER_REQUIREMENT.toString(), DEFAULT_TESTS_PER_REQUIREMENT); } public RequirementsProportionCounter getProportion() { return proportionOf(TestType.ANY); } public RequirementsProportionCounter proportionOf(String testType) { return proportionOf(TestType.valueOf(testType.toUpperCase())); } public RequirementsProportionCounter proportionOf(TestType testType) { return new RequirementsProportionCounter(testType, testOutcomes, totalEstimatedAndImplementedTests()); } public RequirementsOutcomes getReleasedRequirementsFor(Release release) { List<Requirement> matchingRequirements = Lists.newArrayList(); List<TestOutcome> matchingTestOutcomes = Lists.newArrayList(); // Add all test outcomes with a matching release List<RequirementOutcome> requirementOutcomes = releaseManager.enrichRequirementsOutcomesWithReleaseTags(getRequirementOutcomes()); for(RequirementOutcome outcome : requirementOutcomes) { Collection<String> releaseVersions = outcome.getReleaseVersions(); if (releaseVersions.contains(release.getName())) { List<TestOutcome> outcomesForRelease = outcomesForRelease(outcome.getTestOutcomes().getOutcomes(), release.getName()); if (!outcomesForRelease.isEmpty()) { matchingTestOutcomes.addAll(outcomesForRelease); matchingRequirements.add(outcome.getRequirement()); } } } matchingRequirements = removeRequirementsWithoutTestsFrom(matchingRequirements); return new RequirementsOutcomes(Lists.newArrayList(matchingRequirements), TestOutcomes.of(matchingTestOutcomes), issueTracking, environmentVariables, requirementsTagProviders); } private List<Requirement> removeRequirementsWithoutTestsFrom(List<Requirement> requirements) { List<Requirement> prunedRequirements = Lists.newArrayList(); for(Requirement requirement : requirements) { if (testsExistFor(requirement)) { List<Requirement> prunedChildren = removeRequirementsWithoutTestsFrom(requirement.getChildren()); prunedRequirements.add(requirement.withChildren(prunedChildren)); } } return ImmutableList.copyOf(prunedRequirements); } private boolean testsExistFor(Requirement requirement) { TestTag requirementTag = TestTag.withName(requirement.getName()).andType(requirement.getType()); return !getTestOutcomes().withTag(requirementTag).getOutcomes().isEmpty(); //return !getTestOutcomes().withTag(requirement.asTag()).getOutcomes().isEmpty(); } private List<TestOutcome> outcomesForRelease(List<? extends TestOutcome> outcomes, String releaseName) { releaseManager.enrichOutcomesWithReleaseTags(outcomes); return (List<TestOutcome>) filter(having(on(TestOutcome.class).getVersions(), hasItem(releaseName)), outcomes); } }