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

jp.co.moneyforward.autotest.framework.testengine.AutotestEngineUtils Maven / Gradle / Ivy

The newest version!
package jp.co.moneyforward.autotest.framework.testengine;

import jp.co.moneyforward.autotest.framework.action.*;
import jp.co.moneyforward.autotest.framework.annotations.From;
import jp.co.moneyforward.autotest.framework.annotations.PreparedBy;
import jp.co.moneyforward.autotest.framework.annotations.To;
import jp.co.moneyforward.autotest.framework.core.AutotestException;
import jp.co.moneyforward.autotest.framework.core.AutotestRunner;
import jp.co.moneyforward.autotest.framework.exceptions.MethodInvocationException;
import jp.co.moneyforward.autotest.framework.internal.InternalUtils;
import jp.co.moneyforward.autotest.framework.utils.InsdogUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.MessageFormat;
import java.util.*;

import static java.util.stream.Collectors.joining;
import static jp.co.moneyforward.autotest.framework.action.ResolverBundle.resolverBundleFromDependenciesOf;
import static jp.co.moneyforward.autotest.framework.action.Scene.DEFAULT_DEFAULT_VARIABLE_NAME;
import static jp.co.moneyforward.autotest.framework.internal.InternalUtils.wrap;

public enum AutotestEngineUtils {
  ;
  
  public static  List topologicalSort(List specified, Map> graph) {
    Set visited = new LinkedHashSet<>();
    specified.forEach(each -> traverseDependencies(each, graph, visited));
    return new LinkedList<>(visited);
  }
  
  private static  void traverseDependencies(T node, Map> graph, Set visited) {
    if (visited.contains(node))
      return;
    if (!graph.containsKey(node)) {
      throw new NoSuchElementException("Unknown node:<" + node + "> was given. Known nodes are: " + graph.keySet());
    }
    graph.get(node).forEach(each -> traverseDependencies(each, graph, visited));
    visited.add(node);
  }
  
  static  List mergeListsByAppendingMissedOnes(List list1, List list2) {
    List ret = new ArrayList<>(list1);
    for (T item : list2) {
      if (!ret.contains(item)) {
        ret.add(item);
      }
    }
    return ret;
  }
  
  private static Scene createScene(PreparedBy preparedByValue, Class accessModelClass, AutotestRunner runner) {
    // defaultVariableName of Scene.Builder is only used during build process.
    // Once a scene is built, it won't be used neither by the scene nor the builder.
    Scene.Builder b = new Scene.Builder();
    Arrays.stream(preparedByValue.value())
          .map(n -> InternalUtils.findMethodByName(n, accessModelClass).orElseThrow(NoSuchElementException::new))
          .map(m -> methodToScene(m, runner)).forEach(b::add);
    return b.name("ensurer").build();
  }
  
  private static SceneCall sceneToSceneCall(Scene scene, ResolverBundle resolverBundle, String outputVariableStoreName) {
    return AutotestSupport.sceneToSceneCall(scene, outputVariableStoreName, resolverBundle);
  }
  
  private static Scene methodToScene(Method method, AutotestRunner runner) {
    try {
      return switch (method) {
        case Method m when Scene.class.isAssignableFrom(m.getReturnType()) ->
            methodToSceneByArrangeTimeInvocation(m, runner);
        case Method m when !Scene.class.isAssignableFrom(m.getReturnType()) ->
            methodToSceneByActTimeInvocation(m, runner);
        default -> unsupportedMethod(method);
      };
    } catch (IllegalAccessException | InvocationTargetException e) {
      throw new MethodInvocationException("Failed to create a scene with: " + method, e);
    }
  }
  
  private static Scene methodToSceneByArrangeTimeInvocation(Method method, AutotestRunner runner) throws IllegalAccessException, InvocationTargetException {
    return (Scene) method.invoke(runner);
  }
  
  private static Scene unsupportedMethod(Method method) {
    throw new UnsupportedOperationException(describeMethod(method));
  }
  
  private static Scene methodToSceneByActTimeInvocation(Method method, AutotestRunner runner) {
    Method m = validateMethodToDefineSceneIndirectly(method);
    String inputVariableName = "*ALL*";
    String outputVariableName = Optional.ofNullable(m.getAnnotation(To.class))
                                        .map(To::value)
                                        .orElse(Scene.DUMMY_OUTPUT_VARIABLE_NAME);
    return Scene.begin()
                .add(outputVariableName,
                     methodToAct(runner, m),
                     inputVariableName)
                .end();
  }
  
  private static Act methodToAct(AutotestRunner runner, Method method) {
    return InsdogUtils.func((Object in) -> {
      try {
        @SuppressWarnings("unchecked") Map vars = (Map) in;
        return method.invoke(runner, composeArgsFor(method, vars));
      } catch (RuntimeException e) {
        throw new AutotestException(MessageFormat.format("Failed to execute: {0}: {1}",
                                                         composeDescriptionFor(method, runner, in),
                                                         e.getMessage()),
                                    e);
      } catch (IllegalAccessException | InvocationTargetException e) {
        throw wrap(e);
      }
    }).describe(method.getName() + composeParameterNames(method));
  }
  
  private static String composeParameterNames(Method method) {
    return Arrays.stream(method.getParameters())
                 .map(AutotestEngineUtils::describeParameterName)
                 .collect(joining(", ", "(", ")"));
  }
  
  private static Object[] composeArgsFor(Method method, Map in) {
    List errors = new ArrayList<>();
    Object[] ret = Arrays.stream(method.getParameters())
                         .map(AutotestEngineUtils::describeParameterName)
                         .peek(from -> {
                           if (!in.containsKey(from))
                             errors.add(from);
                         })
                         .map(in::get)
                         .toArray();
    if (!errors.isEmpty())
      throw new AutotestException("Undefined variables: " + errors, null);
    return ret;
  }
  
  private static String composeDescriptionFor(Method m, AutotestRunner runner, Object in) {
    return MessageFormat.format("Failed to invoke: {0} on: <{1}> with: <{2}>", describeMethod(m), runner, in);
  }
  
  private static String describeMethod(Method m) {
    return MessageFormat.format("{0}#{1}{2}",
                                m.getDeclaringClass().getCanonicalName(),
                                m.getName(),
                                Arrays.stream(m.getParameterTypes())
                                      .map(Class::getSimpleName)
                                      .collect(joining(",", "(", ")")));
  }
  
  private static String describeParameterName(Parameter p) {
    return p.isAnnotationPresent(From.class) ? p.getAnnotation(From.class).value()
                                             : DEFAULT_DEFAULT_VARIABLE_NAME;
  }
  
  static Call methodToCall(Method method, Class accessModelClass, AutotestRunner runner) {
    PreparedBy[] preparedByAnnotations = method.getAnnotationsByType(PreparedBy.class);
    if (preparedByAnnotations.length > 0) {
      return new EnsuredCall(sceneToSceneCall(methodToScene(method, runner),
                                              resolverBundleFromDependenciesOf(method, accessModelClass),
                                              InternalUtils.nameOf(method)),
                             annotationsToEnsurers(preparedByAnnotations, accessModelClass, runner, method),
                             resolverBundleFromDependenciesOf(method, accessModelClass));
    }
    return sceneToSceneCall(methodToScene(method, runner),
                            resolverBundleFromDependenciesOf(method, accessModelClass),
                            InternalUtils.nameOf(method));
  }
  
  private static List annotationsToEnsurers(PreparedBy[] preparedByAnnotations, Class accessModelClass, AutotestRunner runner, Method targetMethod) {
    return Arrays.stream(preparedByAnnotations).map(ann -> new SceneCall(createScene(ann, accessModelClass, runner), resolverBundleFromDependenciesOf(targetMethod, accessModelClass), InternalUtils.nameOf(targetMethod))).toList();
  }
  
  private static Method validateMethodToDefineSceneIndirectly(Method method) {
    return method;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy