 * @author Kurt Alfred Kluever
 */
@NullMarked
@GwtCompatible
public final class OptionalTest extends TestCase {
  @SuppressWarnings("NullOptional")
  public void testToJavaUtil_static() {
    assertNull(Optional.toJavaUtil(null));
    assertEquals(java.util.Optional.empty(), Optional.toJavaUtil(Optional.absent()));
    assertEquals(java.util.Optional.of("abc"), Optional.toJavaUtil(Optional.of("abc")));
  }

  public void testToJavaUtil_instance() {
    assertEquals(java.util.Optional.empty(), Optional.absent().toJavaUtil());
    assertEquals(java.util.Optional.of("abc"), Optional.of("abc").toJavaUtil());
  }

  @SuppressWarnings("NullOptional")
  public void testFromJavaUtil() {
    assertNull(Optional.fromJavaUtil(null));
    assertEquals(Optional.absent(), Optional.fromJavaUtil(java.util.Optional.empty()));
    assertEquals(Optional.of("abc"), Optional.fromJavaUtil(java.util.Optional.of("abc")));
  }

  public void testAbsent() {
    Optional<String> optionalName = Optional.absent();
    assertFalse(optionalName.isPresent());
  }

  public void testOf() {
    assertEquals("training", Optional.of("training").get());
  }

  public void testOf_null() {
    assertThrows(NullPointerException.class, () -> Optional.of(null));
  }

  public void testFromNullable() {
    Optional<String> optionalName = Optional.fromNullable("bob");
    assertEquals("bob", optionalName.get());
  }

  public void testFromNullable_null() {
    // not promised by spec, but easier to test
    assertSame(Optional.absent(), Optional.fromNullable(null));
  }

  public void testIsPresent_no() {
    assertFalse(Optional.absent().isPresent());
  }

  @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional
  public void testIsPresent_yes() {
    assertTrue(Optional.of("training").isPresent());
  }

  public void testGet_absent() {
    Optional<String> optional = Optional.absent();
    assertThrows(IllegalStateException.class, optional::get);
  }

  public void testGet_present() {
    assertEquals("training", Optional.of("training").get());
  }

  @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional
  public void testOr_t_present() {
    assertEquals("a", Optional.of("a").or("default"));
  }

  public void testOr_t_absent() {
    assertEquals("default", Optional.absent().or("default"));
  }

  @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional
  public void testOr_supplier_present() {
    assertEquals("a", Optional.of("a").or(Suppliers.ofInstance("fallback")));
  }

  public void testOr_supplier_absent() {
    assertEquals("fallback", Optional.absent().or(Suppliers.ofInstance("fallback")));
  }

  public void testOr_nullSupplier_absent() {
    Supplier<Object> nullSupplier = (Supplier<Object>) Suppliers.<@Nullable Object>ofInstance(null);
    Optional<Object> absentOptional = Optional.absent();
    assertThrows(NullPointerException.class, () -> absentOptional.or(nullSupplier));
  }

  @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional
  public void testOr_nullSupplier_present() {
    Supplier<String> nullSupplier = (Supplier<String>) Suppliers.<@Nullable String>ofInstance(null);
    assertEquals("a", Optional.of("a").or(nullSupplier));
  }

  @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional
  public void testOr_optional_present() {
    assertEquals(Optional.of("a"), Optional.of("a").or(Optional.of("fallback")));
  }

  public void testOr_optional_absent() {
    assertEquals(Optional.of("fallback"), Optional.absent().or(Optional.of("fallback")));
  }

  @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional
  public void testOrNull_present() {
    assertEquals("a", Optional.of("a").orNull());
  }

  public void testOrNull_absent() {
    assertNull(Optional.absent().orNull());
  }

  public void testAsSet_present() {
    Set<String> expected = Collections.singleton("a");
    assertEquals(expected, Optional.of("a").asSet());
  }

  public void testAsSet_absent() {
    assertTrue("Returned set should be empty", Optional.absent().asSet().isEmpty());
  }

  public void testAsSet_presentIsImmutable() {
    Set<String> presentAsSet = Optional.of("a").asSet();
    assertThrows(UnsupportedOperationException.class, () -> presentAsSet.add("b"));
  }

  public void testAsSet_absentIsImmutable() {
    Set<Object> absentAsSet = Optional.absent().asSet();
    assertThrows(UnsupportedOperationException.class, () -> absentAsSet.add("foo"));
  }

  public void testTransform_absent() {
    assertEquals(Optional.absent(), Optional.absent().transform(Functions.identity()));
    assertEquals(Optional.absent(), Optional.absent().transform(Functions.toStringFunction()));
  }

  public void testTransform_presentIdentity() {
    assertEquals(Optional.of("a"), Optional.of("a").transform(Functions.identity()));
  }

  public void testTransform_presentToString() {
    assertEquals(Optional.of("42"), Optional.of(42).transform(Functions.toStringFunction()));
  }

  public void testTransform_present_functionReturnsNull() {
    assertThrows(NullPointerException.class, () -> Optional.of("a").transform(input -> null));
  }

  public void testTransform_absent_functionReturnsNull() {
    assertEquals(Optional.absent(), Optional.absent().transform(input -> null));
  }

  public void testEqualsAndHashCode() {
    new EqualsTester()
        .addEqualityGroup(Optional.absent(), reserialize(Optional.absent()))
