Skip to content
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

Dynamic container junit5 #943

Merged
merged 7 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
@@ -1,23 +1,30 @@
package com.mastercard.test.flow.assrt.junit5;

import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.opentest4j.IncompleteExecutionException;
import org.opentest4j.TestAbortedException;

import static com.mastercard.test.flow.assrt.Order.CHAIN_TAG_PREFIX;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import com.mastercard.test.flow.Flow;
import com.mastercard.test.flow.Model;
import com.mastercard.test.flow.assrt.AbstractFlocessor;
import com.mastercard.test.flow.assrt.History.Result;
import com.mastercard.test.flow.util.Tags;

/**
* Integrates {@link Flow} processing into junit 5. This should be used as the
Expand Down Expand Up @@ -53,33 +60,82 @@ public Flocessor( String title, Model model ) {
* @return A stream of test cases
*/
public Stream<DynamicNode> tests() {
return flows().map( flow -> dynamicTest(
flow.meta().id(),
testSource( flow ),
() -> {
try {
process( flow );
history.recordResult( flow, Result.SUCCESS );
}
catch( IncompleteExecutionException iee ) {
// not strictly required to record the skipped outcome in the history, as it
// does not inform the processing of later flows. That may change in the future
// though, so for now we're going to live with the mutation testing complaint
history.recordResult( flow, Result.SKIP );
throw iee;
}
catch( AssertionError ae ) {
history.recordResult( flow, Result.UNEXPECTED );
throw ae;
}
catch( Exception e ) {
// not strictly required to record the error outcome in the history, as it
// does not inform the processing of later flows. That may change in the future
// though, so for now we're going to live with the mutation testing complaint
history.recordResult( flow, Result.ERROR );
throw e;
}
} ) );
List<DynamicNode> nodes = new ArrayList<>();
List<Flow> currentChain = new ArrayList<>();
String currentChainId = null;

// Iterate over flows once and separate them into chained and non-chained
for( Flow flow : flows().collect( Collectors.toList() ) ) {
Optional<String> chainSuffix = Tags.suffix( flow.meta().tags(), CHAIN_TAG_PREFIX );
if( chainSuffix.isPresent() ) {
String chainId = chainSuffix.get();
if( currentChainId == null || currentChainId.equals( chainId ) ) {
currentChainId = chainId;
currentChain.add( flow );
}
else {
// End of the previous chain, add the current chain to nodes
nodes.add( createDynamicContainer( currentChain ) );
currentChain.clear();
currentChainId = chainId;
currentChain.add( flow );
}
}
else {
if( currentChainId != null ) {
// End of the current chain, add the current chain to nodes
nodes.add( createDynamicContainer( currentChain ) );
currentChain.clear();
currentChainId = null;
}
nodes.add( dynamicTest(
flow.meta().id(),
testSource( flow ),
() -> processFlow( flow ) ) );
}
}

// If the last flows were part of a chain, add them as well
if( !currentChain.isEmpty() ) {
nodes.add( createDynamicContainer( currentChain ) );
}
return nodes.stream();
}

private void processFlow( Flow flow ) {
try {
process( flow );
history.recordResult( flow, Result.SUCCESS );
}
catch( IncompleteExecutionException iee ) {
// not strictly required to record the skipped outcome in the history, as it
// does not inform the processing of later flows. That may change in the future
// though, so for now we're going to live with the mutation testing complaint
history.recordResult( flow, Result.SKIP );
throw iee;
}
catch( AssertionError ae ) {
history.recordResult( flow, Result.UNEXPECTED );
throw ae;
}
catch( Exception e ) {
// not strictly required to record the error outcome in the history, as it
// does not inform the processing of later flows. That may change in the future
// though, so for now we're going to live with the mutation testing complaint
history.recordResult( flow, Result.ERROR );
throw e;
}
}

private DynamicContainer createDynamicContainer( List<Flow> chain ) {
List<DynamicTest> tests = chain.stream()
.map( flow -> dynamicTest(
flow.meta().id(),
testSource( flow ),
() -> processFlow( flow ) ) )
.collect( Collectors.toList() );

return DynamicContainer.dynamicContainer( "Chain: " + chain.get( 0 ).meta().id(), tests );
therealryan marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.mastercard.test.flow.assrt.junit5;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;

import com.mastercard.test.flow.Flow;
import com.mastercard.test.flow.Model;
import com.mastercard.test.flow.builder.Chain;
import com.mastercard.test.flow.builder.Creator;
import com.mastercard.test.flow.builder.Deriver;
import com.mastercard.test.flow.util.TaggedGroup;

@SuppressWarnings("static-method")
class FlocessorTest {

/**
* Validates the {@link Flocessor} class for DynamicContainer creation of
* chained flows
*/
@Test
void tests() {

Flocessor flocessor = new Flocessor( "Test Flocessor", new ChainedMdl() );
List<DynamicNode> nodes = flocessor.tests().collect( Collectors.toList() );
assertEquals( 5, nodes.size() );

// assert first test is a DynamicTest
assertInstanceOf( DynamicTest.class, nodes.get( 0 ) );
assertEquals( "a []", nodes.get( 0 ).getDisplayName() );

// assert second test is a DynamicContainer
assertInstanceOf( DynamicContainer.class, nodes.get( 1 ) );
List<DynamicNode> chain1 = ((DynamicContainer) nodes.get( 1 )).getChildren()
.collect( Collectors.toList() );

// assert all children are present in the correct order
assertEquals( "b1 [chain:b]", chain1.get( 0 ).getDisplayName() );
assertEquals( "b2 [chain:b]", chain1.get( 1 ).getDisplayName() );
assertEquals( "d [chain:b]", chain1.get( 2 ).getDisplayName() );

// assert test after a chain is a DynamicTest
assertInstanceOf( DynamicTest.class, nodes.get( 2 ) );
assertEquals( "c []", nodes.get( 2 ).getDisplayName() );

List<DynamicNode> chain2 = ((DynamicContainer) nodes.get( 3 )).getChildren()
.collect( Collectors.toList() );

assertEquals( "d1 [chain:d]", chain2.get( 0 ).getDisplayName() );
assertEquals( "d2 [chain:d]", chain2.get( 1 ).getDisplayName() );

// assert last test is a DynamicContainer
List<DynamicNode> chain3 = ((DynamicContainer) nodes.get( 4 )).getChildren()
.collect( Collectors.toList() );

assertEquals( "e1 [chain:e]", chain3.get( 0 ).getDisplayName() );
assertEquals( "e2 [chain:e]", chain3.get( 1 ).getDisplayName() );

}

public static class ChainedMdl implements Model {

private Flow success = Creator.build( flow -> flow
.meta( data -> data
.description( "a" ) ) );
private Chain chain1 = new Chain( "b" );

private Flow chainedChain1 = Creator.build( chain1, flow -> flow
.meta( data -> data
.description( "b1" ) ) );
private Flow derivedChain1 = Deriver.build( chainedChain1, chain1, flow -> flow
.meta( data -> data
.description( "b2" ) ) );

private Flow successChild = Creator.build( flow -> flow
.meta( data -> data
.description( "c" ) ) );

private Flow chainedChain2 = Creator.build( new Chain( "d" ), flow -> flow
.meta( data -> data
.description( "d1" ) ) );
private Flow derivedChain2 = Deriver.build( chainedChain2, new Chain( "d" ), flow -> flow
.meta( data -> data
.description( "d2" ) ) );

private Flow derivedChildChain1 = Deriver.build( derivedChain1, chain1, flow -> flow
.meta( data -> data
.description( "d" ) ) );

private Chain chain3 = new Chain( "e" );

private Flow chainedChain3 = Creator.build( chain3, flow -> flow
.meta( data -> data
.description( "e1" ) ) );
private Flow derivedChain3 = Deriver.build( chainedChain3, chain3, flow -> flow
.meta( data -> data
.description( "e2" ) ) );

@Override
public Stream<Flow> flows( Set<String> include, Set<String> exclude ) {
return Stream.of( success, chainedChain1, derivedChain1, chainedChain2, derivedChain2,
successChild, derivedChildChain1,
chainedChain3, derivedChain3 );
}

@Override
public Model listener( Listener l ) {
throw new UnsupportedOperationException();
}

@Override
public String title() {
return "Mdl";
}

@Override
public TaggedGroup tags() {
throw new UnsupportedOperationException();
}

@Override
public Stream<Model> subModels() {
return Stream.empty();
}

}
}
Loading