Emulate Optional<T> and its int, long, double variants

Change-Id: Id69894008c48a4e3dca06f9f739f7ed2cd5e76e1
diff --git a/user/super/com/google/gwt/emul/java/util/Optional.java b/user/super/com/google/gwt/emul/java/util/Optional.java
new file mode 100644
index 0000000..51b5f15
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/Optional.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import static javaemul.internal.InternalPreconditions.checkCriticalElement;
+import static javaemul.internal.InternalPreconditions.checkCriticalNotNull;
+
+/**
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html">
+ * the official Java API doc</a> for details.
+ *
+ * @param <T> type of the wrapped reference
+ */
+public final class Optional<T> {
+
+  @SuppressWarnings("unchecked")
+  public static <T> Optional<T> empty() {
+    return (Optional<T>) EMPTY;
+  }
+
+  public static <T> Optional<T> of(T value) {
+    return new Optional<>(value);
+  }
+
+  public static <T> Optional<T> ofNullable(T value) {
+    return value == null ? empty() : of(value);
+  }
+
+  private static final Optional<?> EMPTY = new Optional<>();
+
+  private final T ref;
+
+  private Optional() {
+    ref = null;
+  }
+
+  private Optional(T ref) {
+    this.ref = checkCriticalNotNull(ref);
+  }
+
+  public boolean isPresent() {
+    return ref != null;
+  }
+
+  public T get() {
+    checkCriticalElement(isPresent());
+    return ref;
+  }
+
+  public void ifPresent(Consumer<? super T> consumer) {
+    if (isPresent()) {
+      consumer.accept(ref);
+    }
+  }
+
+  public Optional<T> filter(Predicate<? super T> predicate) {
+    checkCriticalNotNull(predicate);
+    if (!isPresent() || predicate.test(ref)) {
+      return this;
+    }
+    return empty();
+  }
+
+  public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
+    checkCriticalNotNull(mapper);
+    if (isPresent()) {
+      return ofNullable(mapper.apply(ref));
+    }
+    return empty();
+  }
+
+  public <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
+    checkCriticalNotNull(mapper);
+    if (isPresent()) {
+      return checkCriticalNotNull(mapper.apply(ref));
+    }
+    return empty();
+  }
+
+  public T orElse(T other) {
+    return isPresent() ? ref : other;
+  }
+
+  public T orElseGet(Supplier<? extends T> other) {
+    return isPresent() ? ref : other.get();
+  }
+
+  public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
+    if (isPresent()) {
+      return ref;
+    }
+    throw exceptionSupplier.get();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (!(obj instanceof Optional)) {
+      return false;
+    }
+    Optional<?> other = (Optional<?>) obj;
+    return Objects.equals(ref, other.ref);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(ref);
+  }
+
+  @Override
+  public String toString() {
+    return isPresent() ? "Optional.of(" + String.valueOf(ref) + ")" : "Optional.empty()";
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/OptionalDouble.java b/user/super/com/google/gwt/emul/java/util/OptionalDouble.java
new file mode 100644
index 0000000..e6b06af
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/OptionalDouble.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util;
+
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+import java.util.function.Supplier;
+
+import static javaemul.internal.InternalPreconditions.checkCriticalElement;
+
+/**
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/OptionalDouble.html">
+ * the official Java API doc</a> for details.
+ */
+public final class OptionalDouble {
+
+  public static OptionalDouble empty() {
+    return EMPTY;
+  }
+
+  public static OptionalDouble of(double value) {
+    return new OptionalDouble(value);
+  }
+
+  private static final OptionalDouble EMPTY = new OptionalDouble();
+
+  private final double ref;
+  private final boolean present;
+
+  private OptionalDouble() {
+    ref = 0;
+    present = false;
+  }
+
+  private OptionalDouble(double value) {
+    ref = value;
+    present = true;
+  }
+
+  public boolean isPresent() {
+    return present;
+  }
+
+  public double getAsDouble() {
+    checkCriticalElement(present);
+    return ref;
+  }
+
+  public void ifPresent(DoubleConsumer consumer) {
+    if (present) {
+      consumer.accept(ref);
+    }
+  }
+
+  public double orElse(double other) {
+    return present ? ref : other;
+  }
+
+  public double orElseGet(DoubleSupplier other) {
+    return present ? ref : other.getAsDouble();
+  }
+
+  public <X extends Throwable> double orElseThrow(Supplier<X> exceptionSupplier) throws X {
+    if (present) {
+      return ref;
+    }
+    throw exceptionSupplier.get();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (!(obj instanceof OptionalDouble)) {
+      return false;
+    }
+    OptionalDouble other = (OptionalDouble) obj;
+    return present == other.present && Double.compare(ref, other.ref) == 0;
+  }
+
+  @Override
+  public int hashCode() {
+    return present ? Double.hashCode(ref) : 0;
+  }
+
+  @Override
+  public String toString() {
+    return present ? "OptionalDouble.of(" + Double.toString(ref) + ")" : "OptionalDouble.empty()";
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/OptionalInt.java b/user/super/com/google/gwt/emul/java/util/OptionalInt.java
new file mode 100644
index 0000000..10b7ce6
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/OptionalInt.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util;
+
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+import java.util.function.Supplier;
+
+import static javaemul.internal.InternalPreconditions.checkCriticalElement;
+
+/**
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/OptionalInt.html">
+ * the official Java API doc</a> for details.
+ */
+public final class OptionalInt {
+
+  public static OptionalInt empty() {
+    return EMPTY;
+  }
+
+  public static OptionalInt of(int value) {
+    return new OptionalInt(value);
+  }
+
+  private static final OptionalInt EMPTY = new OptionalInt();
+
+  private final int ref;
+  private final boolean present;
+
+  private OptionalInt() {
+    ref = 0;
+    present = false;
+  }
+
+  private OptionalInt(int value) {
+    ref = value;
+    present = true;
+  }
+
+  public boolean isPresent() {
+    return present;
+  }
+
+  public int getAsInt() {
+    checkCriticalElement(present);
+    return ref;
+  }
+
+  public void ifPresent(IntConsumer consumer) {
+    if (present) {
+      consumer.accept(ref);
+    }
+  }
+
+  public int orElse(int other) {
+    return present ? ref : other;
+  }
+
+  public int orElseGet(IntSupplier other) {
+    return present ? ref : other.getAsInt();
+  }
+
+  public <X extends Throwable> int orElseThrow(Supplier<X> exceptionSupplier) throws X {
+    if (present) {
+      return ref;
+    }
+    throw exceptionSupplier.get();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (!(obj instanceof OptionalInt)) {
+      return false;
+    }
+    OptionalInt other = (OptionalInt) obj;
+    return present == other.present && Integer.compare(ref, other.ref) == 0;
+  }
+
+  @Override
+  public int hashCode() {
+    return present ? Integer.hashCode(ref) : 0;
+  }
+
+  @Override
+  public String toString() {
+    return present ? "OptionalInt.of(" + Integer.toString(ref) + ")" : "OptionalInt.empty()";
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/OptionalLong.java b/user/super/com/google/gwt/emul/java/util/OptionalLong.java
new file mode 100644
index 0000000..8551777
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/OptionalLong.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package java.util;
+
+import java.util.function.LongConsumer;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+import static javaemul.internal.InternalPreconditions.checkCriticalElement;
+
+/**
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/OptionalLong.html">
+ * the official Java API doc</a> for details.
+ */
+public final class OptionalLong {
+
+  public static OptionalLong empty() {
+    return EMPTY;
+  }
+
+  public static OptionalLong of(long value) {
+    return new OptionalLong(value);
+  }
+
+  private static final OptionalLong EMPTY = new OptionalLong();
+
+  private final long ref;
+  private final boolean present;
+
+  private OptionalLong() {
+    ref = 0;
+    present = false;
+  }
+
+  private OptionalLong(long value) {
+    ref = value;
+    present = true;
+  }
+
+  public boolean isPresent() {
+    return present;
+  }
+
+  public long getAsLong() {
+    checkCriticalElement(present);
+    return ref;
+  }
+
+  public void ifPresent(LongConsumer consumer) {
+    if (present) {
+      consumer.accept(ref);
+    }
+  }
+
+  public long orElse(long other) {
+    return present ? ref : other;
+  }
+
+  public long orElseGet(LongSupplier other) {
+    return present ? ref : other.getAsLong();
+  }
+
+  public <X extends Throwable> long orElseThrow(Supplier<X> exceptionSupplier) throws X {
+    if (present) {
+      return ref;
+    }
+    throw exceptionSupplier.get();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (!(obj instanceof OptionalLong)) {
+      return false;
+    }
+    OptionalLong other = (OptionalLong) obj;
+    return present == other.present && Long.compare(ref, other.ref) == 0;
+  }
+
+  @Override
+  public int hashCode() {
+    return present ? Long.hashCode(ref) : 0;
+  }
+
+  @Override
+  public String toString() {
+    return present ? "OptionalLong.of(" + Long.toString(ref) + ")" : "OptionalLong.empty()";
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/EmulJava8Suite.java b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
index ee43edf..053cbb1 100644
--- a/user/test/com/google/gwt/emultest/EmulJava8Suite.java
+++ b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.emultest;
 
+import com.google.gwt.emultest.java8.util.OptionalDoubleTest;
+import com.google.gwt.emultest.java8.util.OptionalIntTest;
+import com.google.gwt.emultest.java8.util.OptionalLongTest;
 import com.google.gwt.emultest.java8.util.OptionalTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
 
@@ -30,6 +33,9 @@
 
     //-- java.util
     suite.addTestSuite(OptionalTest.class);
+    suite.addTestSuite(OptionalIntTest.class);
+    suite.addTestSuite(OptionalLongTest.class);
+    suite.addTestSuite(OptionalDoubleTest.class);
 
     return suite;
   }
diff --git a/user/test/com/google/gwt/emultest/java8/util/OptionalDoubleTest.java b/user/test/com/google/gwt/emultest/java8/util/OptionalDoubleTest.java
new file mode 100644
index 0000000..c8361eb
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/OptionalDoubleTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java8.util;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.NoSuchElementException;
+import java.util.OptionalDouble;
+
+/**
+ * Tests for OptionalDouble JRE emulation.
+ */
+public class OptionalDoubleTest extends GWTTestCase {
+
+  private static final double REFERENCE = 10d;
+  private static final double OTHER_REFERENCE = 20d;
+  private boolean[] mutableFlag;
+  private OptionalDouble empty;
+  private OptionalDouble present;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    mutableFlag = new boolean[1];
+    empty = OptionalDouble.empty();
+    present = OptionalDouble.of(REFERENCE);
+  }
+
+  public void testIsPresent() {
+    // empty case
+    assertFalse(empty.isPresent());
+
+    // non-empty case
+    assertTrue(present.isPresent());
+  }
+
+  public void testGetAsDouble() {
+    // empty case
+    try {
+      empty.getAsDouble();
+      fail("Empty Optional should throw NoSuchElementException");
+    } catch (NoSuchElementException e) {
+      // expected
+    }
+
+    // non-empty case
+    assertEquals(REFERENCE, present.getAsDouble());
+  }
+
+  public void testIfPresent() {
+    // empty case
+    empty.ifPresent(null); // should not fail as per JavaDoc
+    empty.ifPresent(wrapped -> fail("Empty Optional should not execute consumer"));
+
+    // non-empty case
+    try {
+      present.ifPresent(null);
+      fail("Non-Empty Optional must throw NullPointerException if consumer is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    present.ifPresent((wrapped) -> {
+      assertEquals(REFERENCE, wrapped);
+      mutableFlag[0] = true;
+    });
+    assertTrue("Consumer not executed", mutableFlag[0]);
+  }
+
+  public void testOrElse() {
+    // empty case
+    assertEquals(OTHER_REFERENCE, empty.orElse(OTHER_REFERENCE));
+
+    // non-empty case
+    assertEquals(REFERENCE, present.orElse(OTHER_REFERENCE));
+  }
+
+  public void testOrElseGet() {
+    // empty case
+    try {
+      empty.orElseGet(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    assertEquals(OTHER_REFERENCE, empty.orElseGet(() -> OTHER_REFERENCE));
+
+    // non-empty case
+    assertEquals(REFERENCE, present.orElseGet(() -> {
+      fail("Optional must not execute supplier");
+      return OTHER_REFERENCE;
+    }));
+  }
+
+  public void testOrElseThrow() {
+    // empty case
+    try {
+      empty.orElseThrow(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(() -> null);
+      fail("Empty Optional must throw NullPointerException if supplier returns null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(IllegalStateException::new);
+      fail("Empty Optional must throw supplied exception");
+    } catch (IllegalStateException e) {
+      // expected
+    }
+
+    // non-empty case
+    try {
+      Object reference = present.orElseThrow(null);
+      assertEquals(REFERENCE, reference);
+    } catch (NullPointerException e) {
+      fail("Optional must not throw NullPointerException if supplier is null");
+    }
+
+    assertEquals(REFERENCE, present.orElseThrow(() -> {
+      fail("Optional must not execute supplier");
+      return new RuntimeException("should not execute");
+    }));
+  }
+
+  public void testEquals() {
+    // empty case
+    assertFalse(empty.equals(null));
+    assertFalse(empty.equals("should not be equal"));
+    assertFalse(empty.equals(present));
+    assertTrue(empty.equals(empty));
+    assertTrue(empty.equals(OptionalDouble.empty()));
+
+    // non empty case
+    assertFalse(present.equals(null));
+    assertFalse(present.equals("should not be equal"));
+    assertFalse(present.equals(empty));
+    assertFalse(present.equals(OptionalDouble.of(OTHER_REFERENCE)));
+    assertTrue(present.equals(present));
+    assertTrue(present.equals(OptionalDouble.of(REFERENCE)));
+  }
+
+  public void testHashcode() {
+    // empty case
+    assertEquals(0, empty.hashCode());
+
+    // non empty case
+    assertEquals(Double.hashCode(REFERENCE), present.hashCode());
+  }
+
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/OptionalIntTest.java b/user/test/com/google/gwt/emultest/java8/util/OptionalIntTest.java
new file mode 100644
index 0000000..f1080e9
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/OptionalIntTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java8.util;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.NoSuchElementException;
+import java.util.OptionalInt;
+
+/**
+ * Tests for OptionalInt JRE emulation.
+ */
+public class OptionalIntTest extends GWTTestCase {
+
+  private static final int REFERENCE = 10;
+  private static final int OTHER_REFERENCE = 20;
+  private boolean[] mutableFlag;
+  private OptionalInt empty;
+  private OptionalInt present;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    mutableFlag = new boolean[1];
+    empty = OptionalInt.empty();
+    present = OptionalInt.of(REFERENCE);
+  }
+
+  public void testIsPresent() {
+    // empty case
+    assertFalse(empty.isPresent());
+
+    // non-empty case
+    assertTrue(present.isPresent());
+  }
+
+  public void testGetAsInt() {
+    // empty case
+    try {
+      empty.getAsInt();
+      fail("Empty Optional should throw NoSuchElementException");
+    } catch (NoSuchElementException e) {
+      // expected
+    }
+
+    // non-empty case
+    assertEquals(REFERENCE, present.getAsInt());
+  }
+
+  public void testIfPresent() {
+    // empty case
+    empty.ifPresent(null); // should not fail as per JavaDoc
+    empty.ifPresent(wrapped -> fail("Empty Optional should not execute consumer"));
+
+    // non-empty case
+    try {
+      present.ifPresent(null);
+      fail("Non-Empty Optional must throw NullPointerException if consumer is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    present.ifPresent((wrapped) -> {
+      assertEquals(REFERENCE, wrapped);
+      mutableFlag[0] = true;
+    });
+    assertTrue("Consumer not executed", mutableFlag[0]);
+  }
+
+  public void testOrElse() {
+    // empty case
+    assertEquals(OTHER_REFERENCE, empty.orElse(OTHER_REFERENCE));
+
+    // non-empty case
+    assertEquals(REFERENCE, present.orElse(OTHER_REFERENCE));
+  }
+
+  public void testOrElseGet() {
+    // empty case
+    try {
+      empty.orElseGet(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    assertEquals(OTHER_REFERENCE, empty.orElseGet(() -> OTHER_REFERENCE));
+
+    // non-empty case
+    assertEquals(REFERENCE, present.orElseGet(() -> {
+      fail("Optional must not execute supplier");
+      return OTHER_REFERENCE;
+    }));
+  }
+
+  public void testOrElseThrow() {
+    // empty case
+    try {
+      empty.orElseThrow(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(() -> null);
+      fail("Empty Optional must throw NullPointerException if supplier returns null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(IllegalStateException::new);
+      fail("Empty Optional must throw supplied exception");
+    } catch (IllegalStateException e) {
+      // expected
+    }
+
+    // non-empty case
+    try {
+      Object reference = present.orElseThrow(null);
+      assertEquals(REFERENCE, reference);
+    } catch (NullPointerException e) {
+      fail("Optional must not throw NullPointerException if supplier is null");
+    }
+
+    assertEquals(REFERENCE, present.orElseThrow(() -> {
+      fail("Optional must not execute supplier");
+      return new RuntimeException("should not execute");
+    }));
+  }
+
+  public void testEquals() {
+    // empty case
+    assertFalse(empty.equals(null));
+    assertFalse(empty.equals("should not be equal"));
+    assertFalse(empty.equals(present));
+    assertTrue(empty.equals(empty));
+    assertTrue(empty.equals(OptionalInt.empty()));
+
+    // non empty case
+    assertFalse(present.equals(null));
+    assertFalse(present.equals("should not be equal"));
+    assertFalse(present.equals(empty));
+    assertFalse(present.equals(OptionalInt.of(OTHER_REFERENCE)));
+    assertTrue(present.equals(present));
+    assertTrue(present.equals(OptionalInt.of(REFERENCE)));
+  }
+
+  public void testHashcode() {
+    // empty case
+    assertEquals(0, empty.hashCode());
+
+    // non empty case
+    assertEquals(Integer.hashCode(REFERENCE), present.hashCode());
+  }
+
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/OptionalLongTest.java b/user/test/com/google/gwt/emultest/java8/util/OptionalLongTest.java
new file mode 100644
index 0000000..4d1396f
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/OptionalLongTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java8.util;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.NoSuchElementException;
+import java.util.OptionalLong;
+
+/**
+ * Tests for OptionalLong JRE emulation.
+ */
+public class OptionalLongTest extends GWTTestCase {
+
+  private static final long REFERENCE = 10L;
+  private static final long OTHER_REFERENCE = 20L;
+  private boolean[] mutableFlag;
+  private OptionalLong empty;
+  private OptionalLong present;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    mutableFlag = new boolean[1];
+    empty = OptionalLong.empty();
+    present = OptionalLong.of(REFERENCE);
+  }
+
+  public void testIsPresent() {
+    // empty case
+    assertFalse(empty.isPresent());
+
+    // non-empty case
+    assertTrue(present.isPresent());
+  }
+
+  public void testGetAsLong() {
+    // empty case
+    try {
+      empty.getAsLong();
+      fail("Empty Optional should throw NoSuchElementException");
+    } catch (NoSuchElementException e) {
+      // expected
+    }
+
+    // non-empty case
+    assertEquals(REFERENCE, present.getAsLong());
+  }
+
+  public void testIfPresent() {
+    // empty case
+    empty.ifPresent(null); // should not fail as per JavaDoc
+    empty.ifPresent(wrapped -> fail("Empty Optional should not execute consumer"));
+
+    // non-empty case
+    try {
+      present.ifPresent(null);
+      fail("Non-Empty Optional must throw NullPointerException if consumer is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    present.ifPresent((wrapped) -> {
+      assertEquals(REFERENCE, wrapped);
+      mutableFlag[0] = true;
+    });
+    assertTrue("Consumer not executed", mutableFlag[0]);
+  }
+
+  public void testOrElse() {
+    // empty case
+    assertEquals(OTHER_REFERENCE, empty.orElse(OTHER_REFERENCE));
+
+    // non-empty case
+    assertEquals(REFERENCE, present.orElse(OTHER_REFERENCE));
+  }
+
+  public void testOrElseGet() {
+    // empty case
+    try {
+      empty.orElseGet(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    assertEquals(OTHER_REFERENCE, empty.orElseGet(() -> OTHER_REFERENCE));
+
+    // non-empty case
+    assertEquals(REFERENCE, present.orElseGet(() -> {
+      fail("Optional must not execute supplier");
+      return OTHER_REFERENCE;
+    }));
+  }
+
+  public void testOrElseThrow() {
+    // empty case
+    try {
+      empty.orElseThrow(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(() -> null);
+      fail("Empty Optional must throw NullPointerException if supplier returns null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(IllegalStateException::new);
+      fail("Empty Optional must throw supplied exception");
+    } catch (IllegalStateException e) {
+      // expected
+    }
+
+    // non-empty case
+    try {
+      Object reference = present.orElseThrow(null);
+      assertEquals(REFERENCE, reference);
+    } catch (NullPointerException e) {
+      fail("Optional must not throw NullPointerException if supplier is null");
+    }
+
+    assertEquals(REFERENCE, present.orElseThrow(() -> {
+      fail("Optional must not execute supplier");
+      return new RuntimeException("should not execute");
+    }));
+  }
+
+  public void testEquals() {
+    // empty case
+    assertFalse(empty.equals(null));
+    assertFalse(empty.equals("should not be equal"));
+    assertFalse(empty.equals(present));
+    assertTrue(empty.equals(empty));
+    assertTrue(empty.equals(OptionalLong.empty()));
+
+    // non empty case
+    assertFalse(present.equals(null));
+    assertFalse(present.equals("should not be equal"));
+    assertFalse(present.equals(empty));
+    assertFalse(present.equals(OptionalLong.of(OTHER_REFERENCE)));
+    assertTrue(present.equals(present));
+    assertTrue(present.equals(OptionalLong.of(REFERENCE)));
+  }
+
+  public void testHashcode() {
+    // empty case
+    assertEquals(0, empty.hashCode());
+
+    // non empty case
+    assertEquals(Long.hashCode(REFERENCE), present.hashCode());
+  }
+
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/OptionalTest.java b/user/test/com/google/gwt/emultest/java8/util/OptionalTest.java
index fbb8daa..3384a46 100644
--- a/user/test/com/google/gwt/emultest/java8/util/OptionalTest.java
+++ b/user/test/com/google/gwt/emultest/java8/util/OptionalTest.java
@@ -17,34 +17,260 @@
 
 import com.google.gwt.junit.client.GWTTestCase;
 
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
 /**
  * Tests for Optional JRE emulation.
  */
 public class OptionalTest extends GWTTestCase {
 
+  private static final Object REFERENCE = new Object();
+  private static final Object OTHER_REFERENCE = new Object();
+  private boolean[] mutableFlag;
+  private Optional<Object> empty;
+  private Optional<Object> present;
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.emultest.EmulSuite";
   }
 
-  public void testSomething() {
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    mutableFlag = new boolean[1];
+    empty = Optional.empty();
+    present = Optional.of(REFERENCE);
+  }
+
+  public void testIsPresent() {
+    // empty case
+    assertFalse(empty.isPresent());
+
+    empty = Optional.ofNullable(null);
+    assertFalse(empty.isPresent());
+
+    // non-empty case
+    assertTrue(present.isPresent());
+
+    present = Optional.ofNullable(REFERENCE);
+    assertTrue(present.isPresent());
+  }
+
+  public void testGet() {
+    // empty case
     try {
-      requireNonNull(null, () -> "Must not be null");
-      fail("must throw NPE");
+      empty.get();
+      fail("Empty Optional should throw NoSuchElementException");
+    } catch (NoSuchElementException e) {
+      // expected
+    }
+
+    // non-empty case
+    assertSame(REFERENCE, present.get());
+  }
+
+  public void testIfPresent() {
+    // empty case
+    empty.ifPresent(null); // should not fail as per JavaDoc
+    empty.ifPresent(wrapped -> fail("Empty Optional should not execute consumer"));
+
+    // non-empty case
+    try {
+      present.ifPresent(null);
+      fail("Non-Empty Optional must throw NullPointerException if consumer is null");
     } catch (NullPointerException e) {
       // expected
     }
+
+    present.ifPresent((wrapped) -> {
+      assertSame(REFERENCE, wrapped);
+      mutableFlag[0] = true;
+    });
+    assertTrue("Consumer not executed", mutableFlag[0]);
   }
 
-  private static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
-    if (obj == null) {
-      throw new NullPointerException(messageSupplier.get());
+  public void testFilter() {
+    // empty case
+    try {
+      empty.filter(null);
+      fail("Optional must throw NullPointerException if predicate is null");
+    } catch (NullPointerException e) {
+      // expected
     }
-    return obj;
+
+    Optional<Object> filtered = empty.filter(wrapped -> true);
+    assertFalse(filtered.isPresent());
+
+    filtered = empty.filter(wrapped -> false);
+    assertFalse(filtered.isPresent());
+
+    // non-empty case
+    try {
+      present.filter(null);
+      fail("Optional must throw NullPointerException if predicate is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    filtered = present.filter(wrapped -> true);
+    assertSame(REFERENCE, filtered.get());
+
+    filtered = present.filter(wrapped -> false);
+    assertFalse(filtered.isPresent());
   }
 
-  @FunctionalInterface
-  private interface Supplier<T> {
-    T get();
+  public void testMap() {
+    // empty case
+    try {
+      empty.map(null);
+      fail("Optional must throw NullPointerException if mapper is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    empty.map(wrapped -> {
+      fail("Empty Optional must not execute mapper");
+      return "should not execute";
+    });
+
+    // non-empty case
+    try {
+      present.map(null);
+      fail("Optional must throw NullPointerException if mapper is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+    Optional<String> mapped = present.map(wrapped -> null);
+    assertFalse(mapped.isPresent());
+
+    mapped = present.map(Object::toString);
+    assertEquals(REFERENCE.toString(), mapped.get());
   }
+
+  public void testFlatMap() {
+    // empty case
+    try {
+      empty.flatMap(null);
+      fail("Optional must throw NullPointerException if mapper is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    empty.flatMap(wrapped -> {
+      fail("Empty Optional must not execute mapper");
+      return Optional.of("should not execute");
+    });
+
+    // non-empty case
+    try {
+      present.flatMap(null);
+      fail("Optional must throw NullPointerException if mapper is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      present.flatMap(wrapped -> null);
+      fail("Optional must throw NullPointerException if mapper returns null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    Optional<String> mapped = present.flatMap(wrapped -> Optional.empty());
+    assertFalse(mapped.isPresent());
+
+    mapped = present.flatMap(wrapped -> Optional.of(wrapped.toString()));
+    assertEquals(REFERENCE.toString(), mapped.get());
+  }
+
+  public void testOrElse() {
+    // empty case
+    assertSame(OTHER_REFERENCE, empty.orElse(OTHER_REFERENCE));
+
+    // non-empty case
+    assertSame(REFERENCE, present.orElse(OTHER_REFERENCE));
+  }
+
+  public void testOrElseGet() {
+    // empty case
+    try {
+      empty.orElseGet(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    assertSame(OTHER_REFERENCE, empty.orElseGet(() -> OTHER_REFERENCE));
+
+    // non-empty case
+    assertSame(REFERENCE, present.orElseGet(() -> {
+      fail("Optional must not execute supplier");
+      return OTHER_REFERENCE;
+    }));
+  }
+
+  public void testOrElseThrow() {
+    // empty case
+    try {
+      empty.orElseThrow(null);
+      fail("Empty Optional must throw NullPointerException if supplier is null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.<RuntimeException>orElseThrow(() -> null);
+      fail("Empty Optional must throw NullPointerException if supplier returns null");
+    } catch (NullPointerException e) {
+      // expected
+    }
+
+    try {
+      empty.orElseThrow(IllegalStateException::new);
+      fail("Empty Optional must throw supplied exception");
+    } catch (IllegalStateException e) {
+      // expected
+    }
+
+    // non-empty case
+    try {
+      Object reference = present.orElseThrow(null);
+      assertSame(REFERENCE, reference);
+    } catch (NullPointerException e) {
+      fail("Optional must not throw NullPointerException if supplier is null");
+    }
+
+    assertSame(REFERENCE, present.orElseThrow(() -> {
+      fail("Optional must not execute supplier");
+      return new RuntimeException("should not execute");
+    }));
+  }
+
+  public void testEquals() {
+    // empty case
+    assertFalse(empty.equals(null));
+    assertFalse(empty.equals("should not be equal"));
+    assertFalse(empty.equals(present));
+    assertTrue(empty.equals(empty));
+    assertTrue(empty.equals(Optional.empty()));
+
+    // non empty case
+    assertFalse(present.equals(null));
+    assertFalse(present.equals("should not be equal"));
+    assertFalse(present.equals(empty));
+    assertFalse(present.equals(Optional.of(OTHER_REFERENCE)));
+    assertTrue(present.equals(present));
+    assertTrue(present.equals(Optional.of(REFERENCE)));
+  }
+
+  public void testHashcode() {
+    // empty case
+    assertEquals(0, empty.hashCode());
+
+    // non empty case
+    assertEquals(REFERENCE.hashCode(), present.hashCode());
+  }
+
 }