Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.tngtech.jgiven.impl.ScenarioModelBuilder Maven / Gradle / Ivy
package com.tngtech.jgiven.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import com.tngtech.jgiven.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.tngtech.jgiven.attachment.Attachment;
import com.tngtech.jgiven.config.AbstractJGivenConfiguration;
import com.tngtech.jgiven.config.ConfigurationUtil;
import com.tngtech.jgiven.config.DefaultConfiguration;
import com.tngtech.jgiven.config.TagConfiguration;
import com.tngtech.jgiven.exception.JGivenWrongUsageException;
import com.tngtech.jgiven.format.ObjectFormatter;
import com.tngtech.jgiven.impl.format.ParameterFormattingUtil;
import com.tngtech.jgiven.impl.intercept.ScenarioListener;
import com.tngtech.jgiven.impl.params.DefaultAsProvider;
import com.tngtech.jgiven.impl.util.AnnotationUtil;
import com.tngtech.jgiven.impl.util.AssertionUtil;
import com.tngtech.jgiven.impl.util.ReflectionUtil;
import com.tngtech.jgiven.impl.util.WordUtil;
import com.tngtech.jgiven.report.model.ExecutionStatus;
import com.tngtech.jgiven.report.model.InvocationMode;
import com.tngtech.jgiven.report.model.NamedArgument;
import com.tngtech.jgiven.report.model.ReportModel;
import com.tngtech.jgiven.report.model.ScenarioCaseModel;
import com.tngtech.jgiven.report.model.ScenarioModel;
import com.tngtech.jgiven.report.model.StepFormatter;
import com.tngtech.jgiven.report.model.StepModel;
import com.tngtech.jgiven.report.model.StepStatus;
import com.tngtech.jgiven.report.model.Tag;
import com.tngtech.jgiven.report.model.Word;
public class ScenarioModelBuilder implements ScenarioListener {
private static final Logger log = LoggerFactory.getLogger( ScenarioModelBuilder.class );
private static final Set STACK_TRACE_FILTER = ImmutableSet
.of( "sun.reflect", "com.tngtech.jgiven.impl.intercept", "com.tngtech.jgiven.impl.intercept", "$$EnhancerByCGLIB$$",
"java.lang.reflect", "net.sf.cglib.proxy", "com.sun.proxy" );
private static final boolean FILTER_STACK_TRACE = Config.config().filterStackTrace();
private ScenarioModel scenarioModel;
private ScenarioCaseModel scenarioCaseModel;
private StepModel currentStep;
private final Stack parentSteps = new Stack();
/**
* In case the current step is a step with nested steps, this list contains these steps
*/
private List nestedSteps;
private Word introWord;
private long scenarioStartedNanos;
private AbstractJGivenConfiguration configuration = new DefaultConfiguration();
private ReportModel reportModel;
public void setReportModel( ReportModel reportModel ) {
this.reportModel = reportModel;
}
@Override
public void scenarioStarted( String description ) {
scenarioStartedNanos = System.nanoTime();
String readableDescription = description;
if( description.contains( "_" ) ) {
readableDescription = description.replace( '_', ' ' );
} else if( !description.contains( " " ) ) {
readableDescription = WordUtil.camelCaseToCapitalizedReadableText( description );
}
scenarioCaseModel = new ScenarioCaseModel();
scenarioModel = new ScenarioModel();
scenarioModel.addCase( scenarioCaseModel );
scenarioModel.setDescription( readableDescription );
}
public void addStepMethod( Method paramMethod, List arguments, InvocationMode mode, boolean hasNestedSteps ) {
StepModel stepModel = createStepModel( paramMethod, arguments, mode );
if( parentSteps.empty() ) {
getCurrentScenarioCase().addStep( stepModel );
} else {
parentSteps.peek().addNestedStep( stepModel );
}
if( hasNestedSteps ) {
parentSteps.push( stepModel );
}
currentStep = stepModel;
}
StepModel createStepModel( Method paramMethod, List arguments, InvocationMode mode ) {
StepModel stepModel = new StepModel();
stepModel.setName( getDescription( paramMethod ) );
ExtendedDescription extendedDescriptionAnnotation = paramMethod.getAnnotation( ExtendedDescription.class );
if( extendedDescriptionAnnotation != null ) {
stepModel.setExtendedDescription( extendedDescriptionAnnotation.value() );
}
List nonHiddenArguments = filterHiddenArguments( arguments, paramMethod.getParameterAnnotations() );
ParameterFormattingUtil parameterFormattingUtil = new ParameterFormattingUtil( configuration );
List> formatters = parameterFormattingUtil.getFormatter( paramMethod.getParameterTypes(), getNames( arguments ),
paramMethod.getParameterAnnotations() );
stepModel.setWords( new StepFormatter( stepModel.getName(), nonHiddenArguments, formatters ).buildFormattedWords() );
if( introWord != null ) {
stepModel.addIntroWord( introWord );
introWord = null;
}
stepModel.setStatus( mode.toStepStatus() );
return stepModel;
}
private List filterHiddenArguments( List arguments, Annotation[][] parameterAnnotations ) {
List result = Lists.newArrayList();
for( int i = 0; i < parameterAnnotations.length; i++ ) {
if( !AnnotationUtil.isHidden( parameterAnnotations[i] ) ) {
result.add( arguments.get( i ) );
}
}
return result;
}
@Override
public void introWordAdded( String value ) {
introWord = new Word();
introWord.setIntroWord( true );
introWord.setValue( value );
}
@Override
public void stepCommentAdded( List arguments ) {
if( arguments == null || arguments.size() != 1 ) {
throw new JGivenWrongUsageException( "A step comment method must have exactly one parameter." );
}
if( !( arguments.get( 0 ).getValue() instanceof String ) ) {
throw new JGivenWrongUsageException( "The step comment method parameter must be a string." );
}
if( currentStep == null ) {
throw new JGivenWrongUsageException( "A step comment must be added after the corresponding step, "
+ "but no step has been executed yet." );
}
currentStep.setComment( (String) arguments.get( 0 ).getValue() );
}
private ScenarioCaseModel getCurrentScenarioCase() {
if( scenarioCaseModel == null ) {
scenarioStarted( "A Scenario" );
}
return scenarioCaseModel;
}
@Override
public void stepMethodInvoked( Method method, List arguments, InvocationMode mode, boolean hasNestedSteps ) {
if( method.isAnnotationPresent( IntroWord.class ) ) {
introWordAdded( getDescription( method ) );
} else if( method.isAnnotationPresent( StepComment.class ) ) {
stepCommentAdded( arguments );
} else {
addTags( method.getAnnotations() );
addTags( method.getDeclaringClass().getAnnotations() );
addStepMethod( method, arguments, mode, hasNestedSteps );
}
}
public void setMethodName( String methodName ) {
scenarioModel.setTestMethodName( methodName );
}
public void setArguments( List arguments ) {
scenarioCaseModel.setExplicitArguments( arguments );
}
public void setParameterNames( List parameterNames ) {
scenarioModel.setExplicitParameters( removeUnderlines( parameterNames ) );
}
private static List removeUnderlines( List parameterNames ) {
List result = Lists.newArrayListWithCapacity( parameterNames.size() );
for( String paramName : parameterNames ) {
result.add( WordUtil.fromSnakeCase( paramName ) );
}
return result;
}
private String getDescription( Method paramMethod ) {
if( paramMethod.isAnnotationPresent( Hidden.class ) ) {
return "";
}
Description description = paramMethod.getAnnotation( Description.class );
if( description != null ) {
return description.value();
}
As as = paramMethod.getAnnotation( As.class );
AsProvider provider = as != null
? ReflectionUtil.newInstance( as.provider() )
: new DefaultAsProvider();
return provider.as( as, paramMethod );
}
@Deprecated
public void setSuccess( boolean success ) {
scenarioCaseModel.setSuccess( success );
}
public void setStatus( ExecutionStatus status ) {
scenarioCaseModel.setStatus( status );
}
public void setException( Throwable throwable ) {
scenarioCaseModel.setErrorMessage( throwable.getClass().getName() + ": " + throwable.getMessage() );
scenarioCaseModel.setStackTrace( getStackTrace( throwable, FILTER_STACK_TRACE ) );
}
private List getStackTrace( Throwable exception, boolean filterStackTrace ) {
StackTraceElement[] stackTraceElements = exception.getStackTrace();
ArrayList stackTrace = new ArrayList( stackTraceElements.length );
outer:
for( StackTraceElement element : stackTraceElements ) {
if( filterStackTrace ) {
for( String filter : STACK_TRACE_FILTER ) {
if( element.getClassName().contains( filter ) ) {
continue outer;
}
}
}
stackTrace.add( element.toString() );
}
return stackTrace;
}
@Override
public void stepMethodFailed( Throwable t ) {
if( currentStep != null ) {
currentStep.setStatus( StepStatus.FAILED );
}
}
@Override
public void stepMethodFinished( long durationInNanos, boolean hasNestedSteps ) {
if( hasNestedSteps && !parentSteps.isEmpty() ) {
currentStep = parentSteps.peek();
}
if( currentStep != null ) {
currentStep.setDurationInNanos( durationInNanos );
if( hasNestedSteps ) {
if( currentStep.getStatus() != StepStatus.FAILED ) {
currentStep.setStatus( getStatusFromNestedSteps( currentStep.getNestedSteps() ) );
}
parentSteps.pop();
}
}
if( !hasNestedSteps && !parentSteps.isEmpty() ) {
currentStep = parentSteps.peek();
}
}
private StepStatus getStatusFromNestedSteps( List nestedSteps ) {
StepStatus status = StepStatus.PASSED;
for( StepModel nestedModel : nestedSteps ) {
StepStatus nestedStatus = nestedModel.getStatus();
switch( nestedStatus ) {
case FAILED:
return StepStatus.FAILED;
case PENDING:
status = StepStatus.PENDING;
break;
}
}
return status;
}
@Override
public void scenarioFailed( Throwable e ) {
setSuccess( false );
setStatus( ExecutionStatus.FAILED );
setException( e );
}
@Override
public void scenarioStarted( Class> testClass, Method method, List namedArguments ) {
readConfiguration( testClass );
readAnnotations( testClass, method );
scenarioModel.setClassName( testClass.getName() );
setParameterNames( getNames( namedArguments ) );
// must come at last
setMethodName( method.getName() );
ParameterFormattingUtil parameterFormattingUtil = new ParameterFormattingUtil( configuration );
List> formatter = parameterFormattingUtil.getFormatter( method.getParameterTypes(), getNames( namedArguments ),
method.getParameterAnnotations() );
setArguments( parameterFormattingUtil.toStringList( formatter, getValues( namedArguments ) ) );
setCaseDescription( testClass, method, namedArguments );
}
private void setCaseDescription( Class> testClass, Method method, List namedArguments ) {
CaseAs annotation = null;
if( method.isAnnotationPresent( CaseAs.class ) ) {
annotation = method.getAnnotation( CaseAs.class );
} else if( testClass.isAnnotationPresent( CaseAs.class ) ) {
annotation = testClass.getAnnotation( CaseAs.class );
}
if( annotation != null ) {
CaseAsProvider caseDescriptionProvider = ReflectionUtil.newInstance( annotation.provider() );
String value = annotation.value();
List> values;
if( annotation.formatValues() ) {
values = scenarioCaseModel.getExplicitArguments();
} else {
values = getValues( namedArguments );
}
String caseDescription = caseDescriptionProvider.as( value, scenarioModel.getExplicitParameters(), values );
scenarioCaseModel.setDescription( caseDescription );
}
}
private List getValues( List namedArguments ) {
List result = Lists.newArrayList();
for( NamedArgument a : namedArguments ) {
result.add( a.value );
}
return result;
}
private List getNames( List namedArguments ) {
List result = Lists.newArrayList();
for( NamedArgument a : namedArguments ) {
result.add( a.name );
}
return result;
}
private void readConfiguration( Class> testClass ) {
configuration = ConfigurationUtil.getConfiguration( testClass );
}
private void readAnnotations( Class> testClass, Method method ) {
String scenarioDescription = method.getName();
if( method.isAnnotationPresent( Description.class ) ) {
scenarioDescription = method.getAnnotation( Description.class ).value();
} else if( method.isAnnotationPresent( As.class ) ) {
As as = method.getAnnotation( As.class );
AsProvider provider = ReflectionUtil.newInstance( as.provider() );
scenarioDescription = provider.as( as, method );
}
scenarioStarted( scenarioDescription );
if( method.isAnnotationPresent( ExtendedDescription.class ) ) {
scenarioModel.setExtendedDescription( method.getAnnotation( ExtendedDescription.class ).value() );
}
if( method.isAnnotationPresent( NotImplementedYet.class ) || method.isAnnotationPresent( Pending.class ) ) {
scenarioCaseModel.setStatus( ExecutionStatus.SCENARIO_PENDING );
}
if( scenarioCaseModel.getCaseNr() == 1 ) {
addTags( testClass.getAnnotations() );
addTags( method.getAnnotations() );
}
}
public void addTags( Annotation... annotations ) {
for( Annotation annotation : annotations ) {
addTags( toTags( annotation ) );
}
}
private void addTags( List tags ) {
if( tags.isEmpty() ) {
return;
}
if( reportModel != null ) {
this.reportModel.addTags( tags );
}
if( scenarioModel != null ) {
this.scenarioModel.addTags( tags );
}
}
public List toTags( Annotation annotation ) {
Class extends Annotation> annotationType = annotation.annotationType();
TagConfiguration tagConfig = toTagConfiguration( annotationType );
if( tagConfig == null ) {
return Collections.emptyList();
}
return toTags( tagConfig, Optional.of( annotation ) );
}
private List toTags( TagConfiguration tagConfig, Optional annotation ) {
Tag tag = new Tag( tagConfig.getAnnotationFullType() );
tag.setType( tagConfig.getAnnotationType() );
if( !Strings.isNullOrEmpty( tagConfig.getName() ) ) {
tag.setName( tagConfig.getName() );
}
if( tagConfig.isPrependType() ) {
tag.setPrependType( true );
}
tag.setShowInNavigation( tagConfig.showInNavigation() );
if( !Strings.isNullOrEmpty( tagConfig.getCssClass() ) ) {
tag.setCssClass( tagConfig.getCssClass() );
}
if( !Strings.isNullOrEmpty( tagConfig.getColor() ) ) {
tag.setColor( tagConfig.getColor() );
}
if( !Strings.isNullOrEmpty( tagConfig.getStyle() ) ) {
tag.setStyle( tagConfig.getStyle() );
}
Object value = tagConfig.getDefaultValue();
if( !Strings.isNullOrEmpty( tagConfig.getDefaultValue() ) ) {
tag.setValue( tagConfig.getDefaultValue() );
}
tag.setTags( tagConfig.getTags() );
if( tagConfig.isIgnoreValue() || !annotation.isPresent() ) {
tag.setDescription( getDescriptionFromGenerator( tagConfig, annotation.orNull(), tagConfig.getDefaultValue() ) );
tag.setHref( getHref( tagConfig, annotation.orNull(), value ) );
return Arrays.asList( tag );
}
try {
Method method = annotation.get().annotationType().getMethod( "value" );
value = method.invoke( annotation.get() );
if( value != null ) {
if( value.getClass().isArray() ) {
Object[] objectArray = (Object[]) value;
if( tagConfig.isExplodeArray() ) {
List explodedTags = getExplodedTags( tag, objectArray, annotation.get(), tagConfig );
return explodedTags;
}
tag.setValue( toStringList( objectArray ) );
} else {
tag.setValue( String.valueOf( value ) );
}
}
} catch( NoSuchMethodException ignore ) {
} catch( Exception e ) {
log.error( "Error while getting 'value' method of annotation " + annotation.get(), e );
}
tag.setDescription( getDescriptionFromGenerator( tagConfig, annotation.get(), value ) );
tag.setHref( getHref( tagConfig, annotation.get(), value ) );
return Arrays.asList( tag );
}
private TagConfiguration toTagConfiguration( Class extends Annotation> annotationType ) {
IsTag isTag = annotationType.getAnnotation( IsTag.class );
if( isTag != null ) {
return fromIsTag( isTag, annotationType );
}
return configuration.getTagConfiguration( annotationType );
}
public TagConfiguration fromIsTag( IsTag isTag, Class extends Annotation> annotationType ) {
String name = Strings.isNullOrEmpty( isTag.name() ) ? isTag.type() : isTag.name();
return TagConfiguration.builder( annotationType )
.defaultValue( isTag.value() )
.description( isTag.description() )
.explodeArray( isTag.explodeArray() )
.ignoreValue( isTag.ignoreValue() )
.prependType( isTag.prependType() )
.name( name )
.descriptionGenerator( isTag.descriptionGenerator() )
.cssClass( isTag.cssClass() )
.color( isTag.color() )
.style( isTag.style() )
.tags( getTagNames( isTag, annotationType ) )
.href( isTag.href() )
.hrefGenerator( isTag.hrefGenerator() )
.showInNavigation( isTag.showInNavigation() )
.build();
}
private List getTagNames( IsTag isTag, Class extends Annotation> annotationType ) {
List tags = getTags( isTag, annotationType );
reportModel.addTags( tags );
List tagNames = Lists.newArrayList();
for( Tag tag : tags ) {
tagNames.add( tag.toIdString() );
}
return tagNames;
}
private List getTags( IsTag isTag, Class extends Annotation> annotationType ) {
List allTags = Lists.newArrayList();
for( Annotation a : annotationType.getAnnotations() ) {
if( a.annotationType().isAnnotationPresent( IsTag.class ) ) {
List tags = toTags( a );
for( Tag tag : tags ) {
allTags.add( tag );
}
}
}
return allTags;
}
private List toStringList( Object[] value ) {
Object[] array = value;
List values = Lists.newArrayList();
for( Object v : array ) {
values.add( String.valueOf( v ) );
}
return values;
}
private String getDescriptionFromGenerator( TagConfiguration tagConfiguration, Annotation annotation, Object value ) {
try {
return tagConfiguration.getDescriptionGenerator().newInstance().generateDescription( tagConfiguration, annotation, value );
} catch( Exception e ) {
throw new JGivenWrongUsageException(
"Error while trying to generate the description for annotation " + annotation + " using DescriptionGenerator class "
+ tagConfiguration.getDescriptionGenerator() + ": " + e.getMessage(),
e );
}
}
private String getHref( TagConfiguration tagConfiguration, Annotation annotation, Object value ) {
try {
return tagConfiguration.getHrefGenerator().newInstance().generateHref( tagConfiguration, annotation, value );
} catch( Exception e ) {
throw new JGivenWrongUsageException(
"Error while trying to generate the href for annotation " + annotation + " using HrefGenerator class "
+ tagConfiguration.getHrefGenerator() + ": " + e.getMessage(),
e );
}
}
private List getExplodedTags( Tag originalTag, Object[] values, Annotation annotation, TagConfiguration tagConfig ) {
List result = Lists.newArrayList();
for( Object singleValue : values ) {
Tag newTag = originalTag.copy();
newTag.setValue( String.valueOf( singleValue ) );
newTag.setDescription( getDescriptionFromGenerator( tagConfig, annotation, singleValue ) );
newTag.setHref( getHref( tagConfig, annotation, singleValue ) );
result.add( newTag );
}
return result;
}
@Override
public void scenarioFinished() {
AssertionUtil.assertTrue( scenarioStartedNanos > 0, "Scenario has no start time" );
long durationInNanos = System.nanoTime() - scenarioStartedNanos;
scenarioCaseModel.setDurationInNanos( durationInNanos );
scenarioModel.addDurationInNanos( durationInNanos );
reportModel.addScenarioModelOrMergeWithExistingOne( scenarioModel );
}
@Override
public void attachmentAdded( Attachment attachment ) {
currentStep.setAttachment( attachment );
}
@Override
public void extendedDescriptionUpdated( String extendedDescription ) {
currentStep.setExtendedDescription( extendedDescription );
}
@Override
public void sectionAdded( String sectionTitle ) {
StepModel stepModel = new StepModel();
stepModel.setName( sectionTitle );
stepModel.addWords( new Word( sectionTitle ) );
stepModel.setIsSectionTitle( true );
getCurrentScenarioCase().addStep( stepModel );
}
@Override
public void tagAdded( Class extends Annotation> annotationClass, String... values ) {
TagConfiguration tagConfig = toTagConfiguration( annotationClass );
if( tagConfig == null ) {
return;
}
List tags = toTags( tagConfig, Optional.absent() );
if( tags.isEmpty() ) {
return;
}
if( values.length > 0 ) {
addTags( getExplodedTags( Iterables.getOnlyElement( tags ), values, null, tagConfig ) );
} else {
addTags( tags );
}
}
public ReportModel getReportModel() {
return reportModel;
}
public ScenarioModel getScenarioModel() {
return scenarioModel;
}
public ScenarioCaseModel getScenarioCaseModel() {
return scenarioCaseModel;
}
}