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

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

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

import jp.co.moneyforward.autotest.framework.annotations.AutotestExecution;
import jp.co.moneyforward.autotest.framework.annotations.ClosedBy;
import jp.co.moneyforward.autotest.framework.annotations.Given;
import jp.co.moneyforward.autotest.framework.annotations.When;

import java.util.*;
import java.util.stream.Stream;

import static java.util.Arrays.asList;
import static jp.co.moneyforward.autotest.framework.testengine.AutotestEngineUtils.mergeListsByAppendingMissedOnes;

///
/// Used with `Spec#planExecutionWith`.
/// Specifies how actual execution of actions (given by `Spec#{beforeAll,beforeEach,value,afterEach,afterAll}`) is planned.
///
/// @see AutotestExecution.Spec#planExecutionWith()
///
public enum PlanningStrategy {
  ///
  /// Actions will be executed as they are specified.
  /// That is, actions specified for `beforeAll` will be executed in `beforeAll` test execution phase JUnit, as such.
  ///
  PASSTHROUGH {
    @Override
    public AutotestEngine.ExecutionPlan planExecution(AutotestExecution.Spec executionSpec,
                                                      Map> sceneCallGraph,
                                                      Map> assertions) {
      return new AutotestEngine.ExecutionPlan(
          asList(executionSpec.beforeAll()),
          asList(executionSpec.beforeEach()),
          asList(executionSpec.value()),
          asList(executionSpec.afterEach()),
          asList(executionSpec.afterAll()),
          sceneCallGraph,
          composeDependencyMapInMain(List.of(executionSpec.value()), sceneCallGraph)
      );
    }
  },
  ///
  /// Actions will be executed based on dependency resolution.
  /// That is, ones specified in `value` will be considered "true" tests and actions depended on by them will be considered "arranging" (or "set up") actions.
  /// The framework ensures ones not explicitly specified in `value` but depended on by one in the step to be included in `beforeAll` step.
  ///
  /// If an action is annotated with `@ClosedBy` and it is in `beforeAll` step, the referenced action will be included in `afterAll` step.
  /// This addition is done in the reverse order, where actions which have `@ClosedBy` are found.
  /// At this addition, the target action (an action by specified by `@ClosedBy` annotation) should be wrapped by the **actionunit** 's `When` action, so that it will be performed the original action has succeeded.
  /// (Without this mechanism, a releasing action will be executed even if a resource to be released is not allocated because of a failure)
  ///
  /// Similarly, if an action in `beforeEach` has `@ClosedBy`, the referenced action will be included in `afterEach` step.
  ///
  /// Suppose, `open` has the `@ClosedBy("close")`.
  /// If it is in `beforeAll`, `close` will be included in `afterAll`.
  /// If `close` is already in `afterAll`, it will not be added again.
  /// If it is in `beforeEach`, `close` will be included in `afterEach`.
  ///
  /// This feature is useful to ensure allocated resources are released.
  ///
  /// @see ClosedBy
  /// @see When
  /// @see Given
  ///
  DEPENDENCY_BASED {
    
    @Override
    public AutotestEngine.ExecutionPlan planExecution(AutotestExecution.Spec executionSpec,
                                                      Map> sceneCallGraph,
                                                      Map> assertions) {
      List explicitlySpecified = List.of(executionSpec.value());
      List sorted = AutotestEngineUtils.topologicalSort(explicitlySpecified, sceneCallGraph);
      String firstSpecified = sorted.stream()
                                    .filter(explicitlySpecified::contains)
                                    .findFirst()
                                    .orElseThrow(NoSuchElementException::new);
      List beforeAll = sorted.subList(0, sorted.indexOf(firstSpecified));
      AutotestEngine.ExecutionPlan executionPlan = composeSceneCallGraph(executionSpec, sceneCallGraph, beforeAll, sorted.subList(sorted.indexOf(firstSpecified), sorted.size()));
      return buildExecutionPlanWithDependencyMap(sceneCallGraph, includeAssertions(executionPlan, assertions));
    }
    
    private static AutotestEngine.ExecutionPlan includeAssertions(AutotestEngine.ExecutionPlan executionPlan, Map> assertions) {
      return new AutotestEngine.ExecutionPlan(executionPlan.beforeAll(),
                                              executionPlan.beforeEach(),
                                              includeAssertions(executionPlan.value(), assertions),
                                              executionPlan.afterEach(),
                                              executionPlan.afterAll(),
                                              executionPlan.dependencies(),
                                              new HashMap<>());
    }
    
    private static AutotestEngine.ExecutionPlan buildExecutionPlanWithDependencyMap(Map> sceneCallGraph, AutotestEngine.ExecutionPlan executionPlan) {
      return new AutotestEngine.ExecutionPlan(
          executionPlan.beforeAll(),
          executionPlan.beforeEach(),
          executionPlan.value(),
          executionPlan.afterEach(),
          executionPlan.afterAll(),
          executionPlan.dependencies(),
          composeDependencyMapInMain(executionPlan.value(), sceneCallGraph)
      );
    }
    
    private static AutotestEngine.ExecutionPlan composeSceneCallGraph(AutotestExecution.Spec executionSpec, Map> sceneCallGraph, List beforeAll, List main) {
      return new AutotestEngine.ExecutionPlan(
          mergeListsByAppendingMissedOnes(List.of(executionSpec.beforeAll()), beforeAll),
          asList(executionSpec.beforeEach()),
          main,
          asList(executionSpec.afterEach()),
          asList(executionSpec.afterAll()),
          sceneCallGraph,
          composeDependencyMapInMain(main, sceneCallGraph));
    }
    
    
    private static List includeAssertions(List value, Map> assertions) {
      return value.stream()
                  .flatMap(s -> Stream.concat(Stream.of(s),
                                              assertions.containsKey(s) ? assertions.get(s).stream()
                                                                        : Stream.empty()))
                  .toList();
    }
  };
  
  public static Map> composeDependencyMapInMain(List main, Map> sceneCallGraph) {
    var ret = new HashMap>();
    for (var each : main) {
      ret.put(each, new ArrayList<>(intersect(allDependenciesOf(each, sceneCallGraph), new LinkedHashSet<>(main))));
    }
    return ret;
  }
  
  private static LinkedHashSet intersect(LinkedHashSet a, LinkedHashSet b) {
    LinkedHashSet ret = new LinkedHashSet<>(a);
    ret.retainAll(b);
    return ret;
  }
  
  private static LinkedHashSet allDependenciesOf(String scene, Map> sceneCallGraph) {
    var ret = new LinkedHashSet();
    dependenciesOf(ret, scene, sceneCallGraph);
    return ret;
  }
  
  private static void dependenciesOf(LinkedHashSet out, String scene, Map> sceneCallGraph) {
    if (sceneCallGraph.containsKey(scene)) {
      List dependenciesOfScene = sceneCallGraph.get(scene);
      out.addAll(dependenciesOfScene);
      for (var each : dependenciesOfScene) {
        dependenciesOf(out, each, sceneCallGraph);
      }
    }
  }
  
  ///
  /// Returns an execution plan based on the design which this instance specifies.
  ///
  /// @param executionSpec A "spec" object of the execution given at runtime.
  /// @param sceneCallGraph A graph that describes relationships between sceneCalls.
  /// @param assertions A map from a normal scene to assertion scenes.
  /// @return An execution plan.
  ///
  public abstract AutotestEngine.ExecutionPlan planExecution(AutotestExecution.Spec executionSpec, Map> sceneCallGraph, Map> assertions);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy