All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.javaspec.runner.ClassExampleGateway Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package info.javaspec.runner;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import info.javaspec.dsl.Because;
import info.javaspec.dsl.Cleanup;
import info.javaspec.dsl.Establish;
import info.javaspec.dsl.It;
import info.javaspec.util.DfsSearch;
import info.javaspec.util.ReflectionUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

final class ClassExampleGateway implements ExampleGateway {
  private final Class contextClass;
  private final ExampleFactory factory;
  
  ClassExampleGateway(Class contextClass) {
    this(contextClass, ClassExampleGateway::makeExample);
  }
  
  ClassExampleGateway(Class contextClass, ExampleFactory factory) {
    this.contextClass = contextClass;
    this.factory = factory;
  }
  
  @FunctionalInterface
  interface ExampleFactory {
    Example makeExample(Class contextClass, Field it, List runBefore, List runAfter);
  }
  
  private static Example makeExample(Class contextClass, Field it, List runBefore, List runAfter) {
    return new FieldExample(nameContext(contextClass), it, runBefore, runAfter);
  }
  
  /* Validation */
  
  @Override
  public List findInitializationErrors() {
    List list = new LinkedList();
    if(isStepSequenceAmbiguous(Establish.class))
      list.add(new UnknownStepExecutionSequenceException(contextClass, Establish.class.getSimpleName()));
    if(isStepSequenceAmbiguous(Because.class))
      list.add(new UnknownStepExecutionSequenceException(contextClass, Because.class.getSimpleName()));
    if(isStepSequenceAmbiguous(Cleanup.class))
      list.add(new UnknownStepExecutionSequenceException(contextClass, Cleanup.class.getSimpleName()));
    
    return list;
  }
  
  private boolean isStepSequenceAmbiguous(Class typeOfStep) {
    //No guarantee that reflection will sort fields by order of declaration; running them out of order could fail
    DfsSearch> contextSearch = new DfsSearch> (contextClass, ClassExampleGateway::readInnerClasses);
    return contextSearch.anyNodeMatches(contextClass -> {
      List thereCanBeOnlyOne = ReflectionUtil.fieldsOfType(typeOfStep, contextClass).collect(toList());
      return thereCanBeOnlyOne.size() > 1; 
    });
  }
  
  public static class UnknownStepExecutionSequenceException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public UnknownStepExecutionSequenceException(Class contextClass, String whatStepIsAmbiguous) {
      super(String.format("Impossible to determine running order of multiple %s functions in test context %s",
        whatStepIsAmbiguous, contextClass.getName()));
    }
  }
  
  /* Context */
  
  @Override
  public Context getRootContext() {
    return readContext(contextClass);
  }
  
  @Override
  public String getRootContextName() {
    return getRootContext().name;
  }
  
  @Override
  public Set getSubContexts(Context context) {
    return readInnerClasses((Class) context.id)
      .filter(x -> treeContainsItField(x))
      .map(ClassExampleGateway::readContext)
      .collect(toSet());
  }
  
  private static boolean treeContainsItField(Class subtreeRoot) {
    DfsSearch> searchForItFields = new DfsSearch>(subtreeRoot, ClassExampleGateway::readInnerClasses);
    return searchForItFields.anyNodeMatches(x -> ReflectionUtil.hasFieldsOfType(It.class, x));
  }
  
  private static Stream> readInnerClasses(Class parent) {
    return Stream.of(parent.getDeclaredClasses()).filter(x -> !Modifier.isStatic(x.getModifiers()));
  }
  
  private static Context readContext(Class contextClass) {
    Set examples = ReflectionUtil.fieldsOfType(It.class, contextClass)
      .map(Field::getName)
      .collect(toSet());
    return new Context(contextClass, nameContext(contextClass), examples);
  }
  
  private static String nameContext(Class contextClass) {
    return contextClass.getSimpleName();
  }
  
  /* Examples */
  
  @Override
  public Stream getExamples() {
    ExampleWalker tree = new ExampleWalker(factory);
    return tree.dfsTraversal(contextClass);
  }
  
  @Override
  public boolean hasExamples() {
    return getExamples().anyMatch(x -> true);
  }
  
  private static class ExampleWalker {
    private final ExampleFactory factory;
    private final List examples;
    
    public ExampleWalker(ExampleFactory factory) {
      this.factory = factory;
      this.examples = new LinkedList();
    }
    
    public Stream dfsTraversal(Class rootContext) {
      appendExamples(rootContext, new ArrayList(), new ArrayList());
      return examples.stream();
    }
    
    private void appendExamples(Class context, List ancestorBefores, List ancestorAfters) {
      List befores = outsideInBefores(context, ancestorBefores);
      List afters = insideOutAfters(context, ancestorAfters);
      ReflectionUtil.fieldsOfType(It.class, context)
        .map(it -> factory.makeExample(context, it, befores, afters))
        .forEach(examples::add);
      readInnerClasses(context).forEach(subcontext -> appendExamples(subcontext, befores, afters));
    }
    
    private static List outsideInBefores(Class contextClass, List ancestors) {
      List befores = new ArrayList(ancestors);
      ReflectionUtil.fieldsOfType(Establish.class, contextClass).forEach(befores::add);
      ReflectionUtil.fieldsOfType(Because.class, contextClass).forEach(befores::add);
      return befores;
    }
    
    private static List insideOutAfters(Class contextClass, List ancestors) {
      List afters = new ArrayList();
      ReflectionUtil.fieldsOfType(Cleanup.class, contextClass).forEach(afters::add);
      afters.addAll(ancestors);
      return afters;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy