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

org.quicktheories.core.stateful.Parallel Maven / Gradle / Ivy

package org.quicktheories.core.stateful;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Parallel {
  
  private final TimeUnit unit;
  private final int timeout;
  
  public Parallel(int timeout, TimeUnit unit) {
    this.unit = unit;
    this.timeout = timeout;
  }


  /**
   * Checks a stateful SUT (system under test) against a model in parallel.
   * 
   * Parallelisation is not used to improve performance - multiple threads are 
   * used to flush out concurrency issues.
   * 
   * Supplied commands will first be run in sequence and compared against the model,
   * then run concurrently. All possible valid end states of the system will be
   * calculated, then the actual end state compared to this.
   * 
   * As the number of possible end states increases rapidly with the number of commands,
   * command lists should usually be constrained to 10 or less.
   * 
   * The model class *must* correctly implement both equals and hashcode.
   * 
   * @param  System under test
   * @param  Model of system 
   * @param initialState Initial state of the system
   * @param commands Commands to be executed
   * @param toSut Mapping from model to system in that state.
   * @param readState Function that returns current state of system
   * @param threads Number of threads to use
   */
  public  void parallelCheck(M initialState,
      List> commands, Function toSut,
      Function readState, int threads) {
    Sequential.modelCheck(initialState, commands, toSut, readState);
    S sut = toSut.apply(initialState);

    Set validEndStates = calculatePossibleEndStates(initialState, commands).collect(Collectors.toSet());

    ExecutorService executor = Executors.newFixedThreadPool(threads);
    Stream rs = commands.stream()
        .map(command -> (Runnable) () -> command.run(sut));
    List> futures = rs.map(r -> executor.submit(r))
        .collect(Collectors.toList());

    waitForCompletion(futures);
    executor.shutdown();

    M finalState = readState.apply(sut);
    if (!validEndStates.contains(finalState)) {
      throw new AssertionError("Final state " + finalState + " not valid.\n Allowable states :- " 
    + validEndStates.stream().map(s -> s.toString()).collect(Collectors.joining(", ") ));
    }
  }

  private void waitForCompletion(List> futures) {
    for (Future each : futures) {
      try {
        each.get(timeout, unit);
      } catch (InterruptedException | ExecutionException | TimeoutException e) {
        throw new RuntimeException("Error executing step", e);
      }
    }
  }

  static  Stream calculatePossibleEndStates(M initial,
      List> commands) {
    return permutations(commands).map(s -> endState(initial, s));
  }

  private static  M endState(M initial,
      Stream> commands) {
    M state = initial;
    for (Command each : commands.collect(Collectors.toList())) {
      state = each.nextState(state);
    }
    return state;
  }

  private static  Stream> permutations(final List items) {
    return IntStream.range(0, factorial(items.size()))
        .mapToObj(i -> permutation(i, items).stream());
  }

  private static int factorial(final int num) {
    return IntStream.rangeClosed(2, num).reduce(1, (x, y) -> x * y);
  }

  private static  List permutation(final int count,
      final LinkedList input, final List output) {
    if (input.isEmpty()) {
      return output;
    }

    final int factorial = factorial(input.size() - 1);
    output.add(input.remove(count / factorial));
    return permutation(count % factorial, input, output);
  }

  private static  List permutation(final int count, final List items) {
    return permutation(count, new LinkedList<>(items), new ArrayList<>());
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy