Skip to content

Added support for requirement refinement #445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class LinkedSpecificationItem
private final Set<String> coveredArtifactTypes = new HashSet<>();
private final Set<String> coveredArtifactTypesFromApprovedItems = new HashSet<>();
private final Set<String> overCoveredArtifactTypes = new HashSet<>();
private boolean isRefined = false;
private boolean isRefinedApproved = false;

/**
* Create a new instance of class {@link LinkedSpecificationItem}.
Expand Down Expand Up @@ -142,6 +144,10 @@ public void addLinkToItemWithStatus(final LinkedSpecificationItem item, final Li
cacheApprovedCoveredArtifactType(item);
coveredArtifactTypes.add(item.getArtifactType());
addMyItemIdToCoveringItem(item);
if (item.getArtifactType() != null && item.getArtifactType().equals(this.getArtifactType()))
{
isRefined = true;
}
break;
case COVERED_UNWANTED:
cacheOverCoveredArtifactType(item);
Expand Down Expand Up @@ -171,6 +177,10 @@ private void cacheApprovedCoveredArtifactType(final LinkedSpecificationItem cove
if (coveringItem.isApproved())
{
coveredArtifactTypesFromApprovedItems.add(coveringItem.getArtifactType());
if (coveringItem.getArtifactType() != null && coveringItem.getArtifactType().equals(this.getArtifactType()))
{
isRefinedApproved = true;
}
}
}

Expand Down Expand Up @@ -300,6 +310,26 @@ public List<String> getUncoveredApprovedArtifactTypes()
return uncovered;
}

/**
* Check if this item is covered by an item of the same artifactType.
*
* @return true if this item is covered by an item of the same artifactType.
*/
public boolean isRefined()
{
return isRefined;
}

/**
* Check if this item is covered by an item of the same artifactType and both items are approved.
*
* @return if this item is approved and is covered by an item of the same artifactType that is also approved.
*/
public boolean isRefinedApproved()
{
return isRefinedApproved;
}

/**
* Check if the item is covered shallow (i.e. if for all needed artifact
* types coverage exists without recursive search).
Expand Down Expand Up @@ -447,12 +477,12 @@ private boolean hasBadLinks()

private boolean areAllArtifactTypesCovered()
{
return this.getCoveredArtifactTypes().containsAll(this.getNeedsArtifactTypes());
return isRefined || this.getCoveredArtifactTypes().containsAll(this.getNeedsArtifactTypes());
}

private boolean areAllCoveredArtifactTypesApproved()
{
return this.getCoveredApprovedArtifactTypes().containsAll(this.getNeedsArtifactTypes());
return isRefinedApproved || this.getCoveredApprovedArtifactTypes().containsAll(this.getNeedsArtifactTypes());
}

private boolean isApproved()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.itsallcode.openfasttrace.core;

import java.util.HashMap;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -83,7 +82,8 @@ private void linkMatchingRevision(final LinkedSpecificationItem covering,
final LinkedSpecificationItem covered)
{
final String coveringArtifactType = covering.getArtifactType();
if (covered.getItem().getNeedsArtifactTypes().contains(coveringArtifactType))
if (covered.getItem().getNeedsArtifactTypes().contains(coveringArtifactType) ||
covered.getArtifactType().equals(covering.getArtifactType()))
{
if (covered.hasDuplicates())
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package org.itsallcode.openfasttrace.core;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.*;
import static org.itsallcode.openfasttrace.api.core.LinkStatus.*;
import static org.itsallcode.openfasttrace.api.core.SpecificationItemId.createId;
import static org.itsallcode.openfasttrace.testutil.core.ItemBuilderFactory.item;
import static org.itsallcode.openfasttrace.testutil.core.SampleArtifactTypes.IMPL;
import static org.itsallcode.openfasttrace.testutil.core.SampleArtifactTypes.REQ;
import static org.itsallcode.openfasttrace.testutil.core.SampleArtifactTypes.UTEST;
import static org.itsallcode.openfasttrace.testutil.core.SampleArtifactTypes.*;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.*;
import java.util.stream.Collectors;

import org.itsallcode.openfasttrace.api.core.DeepCoverageStatus;
import org.itsallcode.openfasttrace.api.core.LinkStatus;
import org.itsallcode.openfasttrace.api.core.LinkedSpecificationItem;
import org.itsallcode.openfasttrace.api.core.SpecificationItem;
Expand Down Expand Up @@ -198,6 +196,16 @@ private void assertItemHasLinksWithStatus(final LinkedSpecificationItem itemUnde
}
}

private static void assertItemRefined( final LinkedSpecificationItem item, final boolean approved )
{
if(approved) {
assertThat( item.getId() + " is refined by approved item", item.isRefinedApproved(), is(true));
} else {
assertThat( item.getId() + " is refined by an item", item.isRefined(), is(true));
}
}


private String createDebugInfoFromExpectedStatuses(
final Map<LinkStatus, Integer> expectedStatuses)
{
Expand Down Expand Up @@ -295,7 +303,7 @@ void testCoverageForDifferentArtifactTypes()
.addCoveredId(REQ, "to-be-covered", 42) //
.build();
final SpecificationItem unwanted = item() //
.id(REQ, "unwanted", 1) //
.id(ITEST, "unwanted", 1) //
.addCoveredId(REQ, "to-be-covered", 42) //
.build();
final List<LinkedSpecificationItem> linkedItems = linkItems(covering, covered, unwanted);
Expand All @@ -316,4 +324,35 @@ void testCoverageForDifferentArtifactTypes()
}
}

@Test
void testRefiningItemCoverItem()
{
final SpecificationItem covered = item() //
.id(REQ, "to-be-covered", 42) //
.addNeedsArtifactType(IMPL) //
.addNeedsArtifactType(UTEST) //
.build();
final SpecificationItem coveringReq = item() //
.id(REQ, "covering", 1) //
.addCoveredId(REQ, "to-be-covered", 42) //
.build();
final SpecificationItem coveringImpl = item() //
.id(IMPL, "covering", 1) //
.addCoveredId(REQ, "to-be-covered", 42) //
.build();
final List<LinkedSpecificationItem> linkedItems = linkItems(coveringReq, covered, coveringImpl);
final Optional<LinkedSpecificationItem> linkedCovered = findLinkedItem(covered,linkedItems);
if (linkedCovered.isPresent())
{
assertItemRefined(linkedCovered.get(), false);
assertItemRefined(linkedCovered.get(), true);
assertThat(linkedCovered.get().isCoveredShallow(),is(true));
assertThat(linkedCovered.get().isCoveredShallowWithApprovedItems(),is(true));
assertThat(linkedCovered.get().getDeepCoverageStatus(),equalTo(DeepCoverageStatus.COVERED));
assertThat(linkedCovered.get().getDeepCoverageStatusOnlyAcceptApprovedItems(),equalTo(DeepCoverageStatus.COVERED));
} else {
fail("Covered item " + covered.getId() + " not found in linked items");
}
}

}