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

com.github.dakusui.crest.core.Assertion Maven / Gradle / Ivy

package com.github.dakusui.crest.core;

import org.junit.ComparisonFailure;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import static java.util.Objects.requireNonNull;

/**
 * Represents one invocation of 'assertThat' method.
 *
 * @param  Type of object to be verified.
 */
public interface Assertion {
  void perform(T value);

  boolean test(Predicate predicate, Object value);

   O apply(Function function, I value);

   Optional thrownExceptionFor(Predicate predicate, I value);

  static  void assertThat(String message, T value, Matcher matcher) {
    create(message, matcher).perform(value);
  }

  static  void assertThat(T value, Matcher matcher) {
    assertThat(null, value, matcher);
  }

  static  Assertion create(String messageOnFailure, Matcher matcher) {
    return new Impl(messageOnFailure, matcher);
  }

  List exceptions();

  class Impl implements Assertion {
    private final Matcher matcher;
    private final Map predicates = new HashMap<>();
    private final Map  functions  = new HashMap<>();
    private final String messageOnFailure;
    private final List exceptions = new LinkedList<>();

    Impl(String messageOnFailure, Matcher matcher) {
      this.messageOnFailure = messageOnFailure; // this can be null
      this.matcher = requireNonNull(matcher);
    }

    @Override
    public void perform(T value) {
      if (!this.matcher.matches(value, this))
        throwComparisonFailure(messageOnFailure, value, matcher);
    }

    @Override
    public boolean test(Predicate predicate, Object value) {
      Object ret = applyPredicate(predicate, value);
      return ret instanceof Boolean ? (Boolean) ret : false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public  O apply(Function function, I value) {
      return ((Function) functions.computeIfAbsent(function, this::memoize)).apply(value);
    }

    @Override
    public  Optional thrownExceptionFor(Predicate predicate, I value) {
      Object ret = applyPredicate(predicate, value);
      if (ret instanceof ExceptionHolder)
        return Optional.of(((ExceptionHolder) ret).get());
      return Optional.empty();
    }

    @Override
    public List exceptions() {
      return this.exceptions;
    }

    @SuppressWarnings("unchecked")
    private  Object applyPredicate(Predicate predicate, I value) {
      return predicates.computeIfAbsent(predicate, this::memoize).apply(value);
    }

    @SuppressWarnings("SimplifiableConditionalExpression")
    private  Function memoize(Predicate predicate) {
      Map memo = new HashMap<>();
      return (I i) -> memo.computeIfAbsent(i, v -> tryToTest(predicate, v));
    }

    private  Object tryToTest(Predicate predicate, I value) {
      try {
        return predicate.test(value);
      } catch (Exception e) {
        exceptions.add(e);
        return ExceptionHolder.create(e);
      }
    }

    private  Function memoize(Function function) {
      Map memo = new HashMap<>();
      return (I i) -> memo.computeIfAbsent(i, function);
    }

    private void throwComparisonFailure(String messageOnFailure, T value, Matcher matcher) {
      requireNonNull(matcher);
      throw new ComparisonFailure(
          messageOnFailure,
          String.join("\n", matcher.describeExpectation(this)),
          String.join("\n", matcher.describeMismatch(value, this))
      );
    }

    private interface ExceptionHolder extends Supplier {
      static ExceptionHolder create(Exception t) {
        return () -> requireNonNull(t);
      }
    }
  }
}