package com.google.inject.testing.throwingproviders;

import static com.google.common.truth.Truth.assertThat;
import static com.google.inject.testing.throwingproviders.CheckedProviderSubject.assertThat;

import com.google.common.truth.ExpectFailure;
import com.google.common.truth.SimpleSubjectBuilder;
import com.google.inject.throwingproviders.CheckedProvider;
import com.google.inject.throwingproviders.CheckedProviders;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * Unit tests for {@link CheckedProviderSubject}.
 *
 * @author eatnumber1@google.com (Russ Harmon)
 */
@RunWith(JUnit4.class)
public class CheckedProviderSubjectTest {
  public @Rule ExpectFailure expect = new ExpectFailure();

  private interface StringCheckedProvider extends CheckedProvider<String> {}

  @Test
  public void providedValue_gotExpected_expectSuccess() {
    String expected = "keep Summer safe";
    CheckedProvider<String> provider = CheckedProviders.of(StringCheckedProvider.class, expected);

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

  @Test
  public void providedValue_gotUnexpected_expectFailure() {
    String expected = "keep Summer safe";
    String unexpected = "Summer is unsafe";
    CheckedProvider<String> provider = CheckedProviders.of(StringCheckedProvider.class, unexpected);
    String message =
        String.format(
            "value provided by <%s>: Not true that <%s> is equal to <%s>",
            getReturningProviderName(unexpected), unexpected, expected);

    expectWhenTesting().that(provider).providedValue().isEqualTo(expected);
    assertThat(expect.getFailure()).hasMessageThat().isEqualTo(message);
  }

  private static final class SummerException extends RuntimeException {}

  @Test
  public void providedValue_throws_expectFailure() {
    CheckedProvider<String> provider =
        CheckedProviders.throwing(StringCheckedProvider.class, SummerException.class);
    String message =
        String.format(
            "checked provider <%s> threw an exception",
            getThrowingProviderName(SummerException.class.getName()));

    expectWhenTesting().that(provider).providedValue();
    AssertionError expected = expect.getFailure();
    assertThat(expected).hasCauseThat().isInstanceOf(SummerException.class);
    assertThat(expected).hasMessageThat().isEqualTo(message);
  }

  @Test
  public void thrownException_threwExpected_expectSuccess() {
    CheckedProvider<?> provider =
        CheckedProviders.throwing(StringCheckedProvider.class, SummerException.class);

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

  @Test
  public void thrownException_threwUnexpected_expectFailure() {
    Class<? extends Throwable> expected = SummerException.class;
    Class<? extends Throwable> unexpected = UnsupportedOperationException.class;
    CheckedProvider<String> provider =
        CheckedProviders.throwing(StringCheckedProvider.class, unexpected);
    String message =
        String.format(
            "exception thrown by <%s>: Not true that <%s> is an instance of <%s>. "
                + "It is an instance of <%s>",
            getThrowingProviderName(UnsupportedOperationException.class.getName()),
            UnsupportedOperationException.class.getName(),
            SummerException.class.getName(),
            UnsupportedOperationException.class.getName());

    expectWhenTesting().that(provider).thrownException().isInstanceOf(expected);
    assertThat(expect.getFailure()).hasMessageThat().isEqualTo(message);
  }

  @Test
  public void thrownException_gets_expectFailure() {
    String getValue = "keep WINTER IS COMING safe";
    CheckedProvider<String> provider = CheckedProviders.of(StringCheckedProvider.class, getValue);
    String message =
        String.format(
            "Not true that <%s> threw <an exception>. It provided <%s>",
            getReturningProviderName(getValue), getValue);

    expectWhenTesting().that(provider).thrownException();
    assertThat(expect.getFailure()).hasMessageThat().isEqualTo(message);
  }

  private SimpleSubjectBuilder<
          CheckedProviderSubject<String, CheckedProvider<String>>, CheckedProvider<String>>
      expectWhenTesting() {
    return expect
        .whenTesting()
        .about(CheckedProviderSubject.<String, CheckedProvider<String>>checkedProviders());
  }

  private String getReturningProviderName(String providing) {
    return String.format("generated CheckedProvider returning <%s>", providing);
  }

  private String getThrowingProviderName(String throwing) {
    return String.format("generated CheckedProvider throwing <%s>", throwing);
  }
}

@@ -195,16 +195,61 @@
 ertThat;
+%0Aimport static java.util.regex.Pattern.quote;
 %0A%0Aimport
@@ -1487,34 +1487,39 @@
 provided by %3C%25s%3E
+(
 : 
+%7C%5Cn)
 Not true that %3C%25
@@ -1544,32 +1544,38 @@
 %3E%22,%0A            
+quote(
 getReturningProv
@@ -1594,18 +1594,25 @@
 xpected)
+)
 , 
+quote(
 unexpect
@@ -1613,18 +1613,25 @@
 expected
+)
 , 
+quote(
 expected
@@ -1627,24 +1627,25 @@
 te(expected)
+)
 ;%0A%0A    expec
@@ -1760,33 +1760,31 @@
 ssageThat().
-isEqualTo
+matches
 (message);%0A 
@@ -3162,18 +3162,23 @@
  by %3C%25s%3E
+(
 : 
+%7C%5Cn)
 Not true
@@ -3265,32 +3265,38 @@
 %3E%22,%0A            
+quote(
 getThrowingProvi
@@ -3349,16 +3349,17 @@
 tName())
+)
 ,%0A      
@@ -3356,32 +3356,38 @@
 )),%0A            
+quote(
 UnsupportedOpera
@@ -3407,32 +3407,33 @@
 .class.getName()
+)
 ,%0A            Su
@@ -3430,16 +3430,22 @@
         
+quote(
 SummerEx
@@ -3467,16 +3467,17 @@
 etName()
+)
 ,%0A      
@@ -3482,16 +3482,22 @@
         
+quote(
 Unsuppor
@@ -3534,16 +3534,17 @@
 tName())
+)
 ;%0A%0A    e
@@ -3668,33 +3668,31 @@
 ssageThat().
-isEqualTo
+matches
 (message);%0A 

Update tests for when withMessage() messages are displayed on separate lines from the main failure message.