package com.google.inject.throwingproviders;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.inject.testing.throwingproviders.CheckedProviderSubject.assertThat;
import static org.junit.Assert.fail;

import com.google.inject.TypeLiteral;
import java.util.Arrays;
import junit.framework.TestCase;

/**
 * Unit tests for {@link CheckedProviders}.
 *
 * @author eatnumber1@google.com (Russ Harmon)
 */
public final class CheckedProvidersTest extends TestCase {
  private static interface StringCheckedProvider extends CheckedProvider<String> {}

  public void testCheckedProviderClass_get_returnsValidString() {
    String expected = "rick";

    StringCheckedProvider provider = CheckedProviders.of(StringCheckedProvider.class, expected);

    assertThat(provider).providedValue().isEqualTo(expected);
  }

  public void testCheckedProviderTypeLiteral_get_returnsValidString() {
    String expected = "morty";

    StringCheckedProvider provider =
        CheckedProviders.of(TypeLiteral.get(StringCheckedProvider.class), expected);

    assertThat(provider).providedValue().isEqualTo(expected);
  }

  public void testCheckedProviderClassNull_get_returnsNull() {
    StringCheckedProvider provider = CheckedProviders.of(StringCheckedProvider.class, null);

    assertThat(provider).providedValue().isNull();
  }

  public void testCheckedProviderTypeLiteralNull_get_returnsNull() {
    StringCheckedProvider provider =
        CheckedProviders.of(TypeLiteral.get(StringCheckedProvider.class), null);

    assertThat(provider).providedValue().isNull();
  }

  private static final class FooException extends Exception {}

  private interface FooCheckedProvider extends CheckedProvider<Object> {
    @Override
    Object get() throws FooException;
  }

  public void testThrowingCheckedProviderClass_get_throwsException() {
    FooCheckedProvider provider =
        CheckedProviders.throwing(FooCheckedProvider.class, FooException.class);

    assertThat(provider).thrownException().isInstanceOf(FooException.class);
  }

  public void testThrowingCheckedProviderTypeLiteral_get_throwsException() {
    FooCheckedProvider provider =
        CheckedProviders.throwing(TypeLiteral.get(FooCheckedProvider.class), FooException.class);

    assertThat(provider).thrownException().isInstanceOf(FooException.class);
  }

  private interface MoreMethodsCheckedProvider<T> extends CheckedProvider<T> {
    @Override
    T get() throws FooException;

    void otherMethod();
  }

  public void testUnsupportedMethods_otherMethod_throwsIllegalArgumentException()
      throws NoSuchMethodException {
    String message =
        String.format(
            "%s may not declare any new methods, but declared %s",
            MoreMethodsCheckedProvider.class.getName(),
            Arrays.toString(MoreMethodsCheckedProvider.class.getDeclaredMethods()));

    try {
      CheckedProviders.of(
          new TypeLiteral<MoreMethodsCheckedProvider<String>>() {},
          "SHOW ME WHAT YOU GOT");
      fail("Expected an exception to be thrown");
    } catch (IllegalArgumentException e) {
      assertThat(e).hasMessage(message);
    }
  }

  private static final class StringException extends RuntimeException {
    StringException(String arg) {}
  }

  public void testCheckThrowable_unsupportedThrowableConstructor_throwsIllegalArgumentException() {
    String message =
        String.format(
            "Thrown exception <%s> must have a no-argument constructor",
            StringException.class.getName());

    try {
      CheckedProviders.throwing(FooCheckedProvider.class, StringException.class);
      fail("Expected an exception to be thrown");
    } catch (IllegalArgumentException e) {
      assertThat(e).hasMessage(message);
      assert_()
          .withFailureMessage(String.format("exception <%s> with cause", e))
          .that(e.getCause())
          .isInstanceOf(NoSuchMethodException.class);
    }
  }

  private static final class BarException extends Exception {}

  public void testCheckThrowable_checkedExceptionNotDeclared_throwsIllegalArgumentException()
      throws Exception {
    String message =
        String.format(
            "Thrown exception <%s> is not declared to be thrown by <%s>",
            BarException.class.getName(), FooCheckedProvider.class.getMethod("get"));

    try {
      CheckedProviders.throwing(FooCheckedProvider.class, BarException.class);
      fail("Expected an exception to be thrown");
    } catch (IllegalArgumentException e) {
      assertThat(e).hasMessage(message);
    }
  }

  private static final class ExpectedRuntimeException extends RuntimeException {}

  public void testCheckThrowable_runtimeExceptionNotDeclared_throwsExpectedRuntimeException()
      throws Exception {
    FooCheckedProvider provider =
        CheckedProviders.throwing(FooCheckedProvider.class, ExpectedRuntimeException.class);

    try {
      provider.get();
      fail("Expected an exception to be thrown");
    } catch (ExpectedRuntimeException e) {
      // expected
    }
  }

  private static final class ExpectedError extends Error {}

  public void testCheckThrowable_errorNotDeclared_throwsExpectedError() throws Exception {
    FooCheckedProvider provider =
        CheckedProviders.throwing(FooCheckedProvider.class, ExpectedError.class);

    try {
      provider.get();
      fail("Expected an exception to be thrown");
    } catch (ExpectedError e) {
      // expected
    }
  }
}

@@ -152,101 +152,8 @@
 t_;%0A
-import static com.google.inject.testing.throwingproviders.CheckedProviderSubject.assertThat;%0A
 impo
@@ -584,32 +584,49 @@
 rnsValidString()
+ throws Exception
  %7B%0A    String ex
@@ -732,33 +732,32 @@
 ass, expected);%0A
-%0A
     assertThat(p
@@ -763,32 +763,22 @@
 provider
-).providedValue(
+.get()
 ).isEqua
@@ -866,16 +866,33 @@
 String()
+ throws Exception
  %7B%0A    S
@@ -1040,17 +1040,16 @@
 ected);%0A
-%0A
     asse
@@ -1063,32 +1063,22 @@
 provider
-).providedValue(
+.get()
 ).isEqua
@@ -1149,32 +1149,49 @@
 et_returnsNull()
+ throws Exception
  %7B%0A    StringChe
@@ -1262,33 +1262,32 @@
 r.class, null);%0A
-%0A
     assertThat(p
@@ -1293,32 +1293,22 @@
 provider
-).providedValue(
+.get()
 ).isNull
@@ -1382,16 +1382,33 @@
 nsNull()
+ throws Exception
  %7B%0A    S
@@ -1520,17 +1520,16 @@
  null);%0A
-%0A
     asse
@@ -1547,24 +1547,14 @@
 ider
-).providedValue(
+.get()
 ).is
@@ -1950,85 +1950,92 @@
 s);%0A
-%0A    assertThat(provider).thrownException().isInstanceOf(FooException.class);
+    try %7B%0A      provider.get();%0A      fail();%0A    %7D catch (FooException expected) %7B%7D
 %0A  %7D
@@ -2249,85 +2249,92 @@
 s);%0A
-%0A    assertThat(provider).thrownException().isInstanceOf(FooException.class);
+    try %7B%0A      provider.get();%0A      fail();%0A    %7D catch (FooException expected) %7B%7D
 %0A  %7D

Change throwingprovider tests to not depend on testlib. Otherwise we have a cyclic dependency in maven. ([] can handle different deps for compile/test and separate out the cycle, but maven can't.)