/*
 * Copyright (C) 2014  Ohm Data
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package c5db;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class FutureMatchers {
  private static final int TIMEOUT = 4; // seconds

  public static <T> Matcher<Future<T>> resultsIn(Matcher<T> resultMatcher) {
    return returnsAFutureWhoseResult(resultMatcher);
  }

  public static <T> Matcher<Future<T>> returnsAFutureWhoseResult(Matcher<T> resultMatcher) {
    return new TypeSafeMatcher<Future<T>>() {
      public Throwable throwable = null;

      @Override
      protected boolean matchesSafely(Future<T> item) {
        try {
          return resultMatcher.matches(item.get(TIMEOUT, TimeUnit.SECONDS));
        } catch (Exception t) {
          throwable = t;
          return false;
        }
      }

      @Override
      public void describeTo(Description description) {
        description.appendText("a Future whose result: ").appendDescriptionOf(resultMatcher);
      }

      @Override
      public void describeMismatchSafely(Future<T> item, Description description) {
        if (throwable != null) {
          description.appendValue(throwable);
        } else {
          description.appendText("got a Future with result: ");
          try {
            description.appendValue(item.get());
          } catch (Exception ignore) {
          }
        }
      }
    };
  }
}

@@ -1120,16 +1120,180 @@
 );%0A  %7D%0A%0A
+  public static %3CT%3E Matcher%3CFuture%3CT%3E%3E resultsInException(Class%3C? extends Throwable%3E exceptionClass) %7B%0A    return returnsAFutureWithException(exceptionClass);%0A  %7D%0A%0A
   public
@@ -2307,11 +2307,1119 @@
   %7D;%0A  %7D
+%0A%0A  public static %3CT%3E Matcher%3CFuture%3CT%3E%3E returnsAFutureWithException(Class%3C? extends Throwable%3E exceptionClass) %7B%0A    return new TypeSafeMatcher%3CFuture%3CT%3E%3E() %7B%0A      public Throwable actualExceptionThrown = null;%0A      public T result;%0A%0A      @Override%0A      protected boolean matchesSafely(Future%3CT%3E item) %7B%0A        try %7B%0A          result = item.get(TIMEOUT, TimeUnit.SECONDS);%0A          return false;%0A        %7D catch (Throwable t) %7B%0A          actualExceptionThrown = t.getCause();%0A          return exceptionClass.isInstance(actualExceptionThrown);%0A        %7D%0A      %7D%0A%0A      @Override%0A      public void describeTo(Description description) %7B%0A        description.appendText(%22a Future resulting in: %22).appendText(exceptionClass.getCanonicalName());%0A      %7D%0A%0A      @Override%0A      public void describeMismatchSafely(Future%3CT%3E item, Description description) %7B%0A        description.appendText(%22got a Future with result: %22);%0A        if (actualExceptionThrown != null) %7B%0A          description.appendValue(actualExceptionThrown);%0A        %7D else %7B%0A          description.appendValue(result);%0A        %7D%0A      %7D%0A    %7D;%0A  %7D
 %0A%7D%0A

Add resultsInException matcher