Backport OptionalInt/OptionalLong/OptionalDouble.stream()

Test: tools/test.py --dex_vm all --no-internal -v *Backport*Test*
Test: tools/test.py --no-internal -v *GenerateBackportMethods*
Change-Id: I85714b4ce8fc06c9c2656cf82532ea6803d09ce1
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index f2e7147..424604f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1428,22 +1428,40 @@
           new StatifyingMethodGenerator(
               method, BackportedMethods::OptionalMethods_or, "or", optionalType));
 
-      // Optional.stream()
-      name = factory.createString("stream");
-      proto = factory.createProto(factory.streamType);
-      method = factory.createMethod(optionalType, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::OptionalMethods_stream, "stream", optionalType));
-
-      // Optional{void,Int,Long,Double}.ifPresentOrElse(consumer,runnable)
+      // Optional{void,Int,Long,Double}.stream()
       DexType[] optionalTypes =
-          new DexType[]{
+          new DexType[] {
               optionalType,
               factory.createType(factory.createString("Ljava/util/OptionalDouble;")),
               factory.createType(factory.createString("Ljava/util/OptionalLong;")),
-              factory.createType(factory.createString("Ljava/util/OptionalInt;"))
+              factory.createType(factory.createString("Ljava/util/OptionalInt;")),
           };
+      DexType[] streamReturnTypes =
+          new DexType[] {
+              factory.streamType,
+              factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
+              factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
+              factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
+          };
+      TemplateMethodFactory[] streamMethodFactories =
+          new TemplateMethodFactory[] {
+              BackportedMethods::OptionalMethods_stream,
+              BackportedMethods::OptionalMethods_streamDouble,
+              BackportedMethods::OptionalMethods_streamLong,
+              BackportedMethods::OptionalMethods_streamInt,
+          };
+      name = factory.createString("stream");
+      for (int i = 0; i < optionalTypes.length; i++) {
+        DexType optional = optionalTypes[i];
+        DexType streamReturnType = streamReturnTypes[i];
+        proto = factory.createProto(streamReturnType);
+        method = factory.createMethod(optional, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, streamMethodFactories[i], "stream", optional));
+      }
+
+      // Optional{void,Int,Long,Double}.ifPresentOrElse(consumer,runnable)
       DexType[] consumerTypes =
           new DexType[]{
               factory.consumerType,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 3494e60..bd87524 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -5292,6 +5292,168 @@
         ImmutableList.of());
   }
 
+  public static CfCode OptionalMethods_streamDouble(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        2,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/OptionalDouble;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("Z")),
+                    options.itemFactory.createString("isPresent")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/OptionalDouble;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("D")),
+                    options.itemFactory.createString("getAsDouble")),
+                false),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/stream/DoubleStream;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/stream/DoubleStream;"),
+                        options.itemFactory.createType("D")),
+                    options.itemFactory.createString("of")),
+                true),
+            new CfReturn(ValueType.OBJECT),
+            label2,
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/stream/DoubleStream;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/stream/DoubleStream;")),
+                    options.itemFactory.createString("empty")),
+                true),
+            new CfReturn(ValueType.OBJECT),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode OptionalMethods_streamInt(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        1,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/OptionalInt;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("Z")),
+                    options.itemFactory.createString("isPresent")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/OptionalInt;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("I")),
+                    options.itemFactory.createString("getAsInt")),
+                false),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/stream/IntStream;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/stream/IntStream;"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("of")),
+                true),
+            new CfReturn(ValueType.OBJECT),
+            label2,
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/stream/IntStream;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/stream/IntStream;")),
+                    options.itemFactory.createString("empty")),
+                true),
+            new CfReturn(ValueType.OBJECT),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode OptionalMethods_streamLong(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        2,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/OptionalLong;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("Z")),
+                    options.itemFactory.createString("isPresent")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/OptionalLong;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("J")),
+                    options.itemFactory.createString("getAsLong")),
+                false),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/stream/LongStream;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/stream/LongStream;"),
+                        options.itemFactory.createType("J")),
+                    options.itemFactory.createString("of")),
+                true),
+            new CfReturn(ValueType.OBJECT),
+            label2,
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/stream/LongStream;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/stream/LongStream;")),
+                    options.itemFactory.createString("empty")),
+                true),
+            new CfReturn(ValueType.OBJECT),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode ShortMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
diff --git a/src/test/examplesJava9/backport/OptionalBackportJava9Main.java b/src/test/examplesJava9/backport/OptionalBackportJava9Main.java
index b337875..c25226f 100644
--- a/src/test/examplesJava9/backport/OptionalBackportJava9Main.java
+++ b/src/test/examplesJava9/backport/OptionalBackportJava9Main.java
@@ -5,9 +5,6 @@
 package backport;
 
 import java.util.Optional;
-import java.util.OptionalDouble;
-import java.util.OptionalInt;
-import java.util.OptionalLong;
 
 public final class OptionalBackportJava9Main {
 
@@ -15,9 +12,6 @@
     testOr();
     testOrNull();
     testIfPresentOrElse();
-    testIfPresentOrElseInt();
-    testIfPresentOrElseLong();
-    testIfPresentOrElseDouble();
     testStream();
   }
 
@@ -69,27 +63,6 @@
     emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
   }
 
-  private static void testIfPresentOrElseInt() {
-    OptionalInt value = OptionalInt.of(1);
-    OptionalInt emptyValue = OptionalInt.empty();
-    value.ifPresentOrElse(val -> {}, () -> assertTrue(false));
-    emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
-  }
-
-  private static void testIfPresentOrElseLong() {
-    OptionalLong value = OptionalLong.of(1L);
-    OptionalLong emptyValue = OptionalLong.empty();
-    value.ifPresentOrElse(val -> {}, () -> assertTrue(false));
-    emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
-  }
-
-  private static void testIfPresentOrElseDouble() {
-    OptionalDouble value = OptionalDouble.of(1.0d);
-    OptionalDouble emptyValue = OptionalDouble.empty();
-    value.ifPresentOrElse(val -> {}, () -> assertTrue(false));
-    emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
-  }
-
   private static void testStream() {
     Optional<String> value = Optional.of("value");
     Optional<String> emptyValue = Optional.empty();
diff --git a/src/test/examplesJava9/backport/OptionalDoubleBackportJava9Main.java b/src/test/examplesJava9/backport/OptionalDoubleBackportJava9Main.java
new file mode 100644
index 0000000..9b08718
--- /dev/null
+++ b/src/test/examplesJava9/backport/OptionalDoubleBackportJava9Main.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package backport;
+
+import java.util.OptionalDouble;
+
+public final class OptionalDoubleBackportJava9Main {
+
+  public static void main(String[] args) {
+    testIfPresentOrElseDouble();
+    testStreamDouble();
+  }
+
+  private static void testIfPresentOrElseDouble() {
+    OptionalDouble value = OptionalDouble.of(1.0d);
+    OptionalDouble emptyValue = OptionalDouble.empty();
+    value.ifPresentOrElse(val -> {}, () -> assertTrue(false));
+    emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
+  }
+
+  private static void testStreamDouble() {
+    OptionalDouble value = OptionalDouble.of(2d);
+    OptionalDouble emptyValue = OptionalDouble.empty();
+    assertTrue(value.stream().count() == 1);
+    assertTrue(emptyValue.stream().count() == 0);
+  }
+
+  private static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("Expected <true> but was <false>");
+    }
+  }
+}
diff --git a/src/test/examplesJava9/backport/OptionalIntBackportJava9Main.java b/src/test/examplesJava9/backport/OptionalIntBackportJava9Main.java
new file mode 100644
index 0000000..fedc472
--- /dev/null
+++ b/src/test/examplesJava9/backport/OptionalIntBackportJava9Main.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package backport;
+
+import java.util.OptionalInt;
+
+public final class OptionalIntBackportJava9Main {
+
+  public static void main(String[] args) {
+    testIfPresentOrElseInt();
+    testStreamInt();
+  }
+
+  private static void testIfPresentOrElseInt() {
+    OptionalInt value = OptionalInt.of(1);
+    OptionalInt emptyValue = OptionalInt.empty();
+    value.ifPresentOrElse(val -> {}, () -> assertTrue(false));
+    emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
+  }
+
+  private static void testStreamInt() {
+    OptionalInt value = OptionalInt.of(2);
+    OptionalInt emptyValue = OptionalInt.empty();
+    assertTrue(value.stream().count() == 1);
+    assertTrue(emptyValue.stream().count() == 0);
+  }
+
+  private static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("Expected <true> but was <false>");
+    }
+  }
+}
diff --git a/src/test/examplesJava9/backport/OptionalLongBackportJava9Main.java b/src/test/examplesJava9/backport/OptionalLongBackportJava9Main.java
new file mode 100644
index 0000000..07438e1
--- /dev/null
+++ b/src/test/examplesJava9/backport/OptionalLongBackportJava9Main.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package backport;
+
+import java.util.OptionalLong;
+
+public final class OptionalLongBackportJava9Main {
+
+  public static void main(String[] args) {
+    testIfPresentOrElseLong();
+    testStreamLong();
+  }
+
+  private static void testIfPresentOrElseLong() {
+    OptionalLong value = OptionalLong.of(1L);
+    OptionalLong emptyValue = OptionalLong.empty();
+    value.ifPresentOrElse(val -> {}, () -> assertTrue(false));
+    emptyValue.ifPresentOrElse(val -> assertTrue(false), () -> {});
+  }
+
+  private static void testStreamLong() {
+    OptionalLong value = OptionalLong.of(2L);
+    OptionalLong emptyValue = OptionalLong.empty();
+    assertTrue(value.stream().count() == 1);
+    assertTrue(emptyValue.stream().count() == 0);
+  }
+
+  private static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("Expected <true> but was <false>");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index 0429fe5..31bc82f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Optional;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -32,8 +33,12 @@
       Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
 
   public OptionalBackportJava9Test(TestParameters parameters) {
-    super(parameters, Short.class, TEST_JAR, "backport.OptionalBackportJava9Main");
+    super(parameters, Optional.class, TEST_JAR, "backport.OptionalBackportJava9Main");
     // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
     // an actual API level, migrate these tests to OptionalBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("of");
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
new file mode 100644
index 0000000..5aa0e1c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public final class OptionalDoubleBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalDoubleBackportJava9Test(TestParameters parameters) {
+    super(parameters, OptionalDouble.class, TEST_JAR, "backport.OptionalDoubleBackportJava9Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalDoubleBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("of");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
new file mode 100644
index 0000000..71a8dd9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.OptionalInt;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public final class OptionalIntBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalIntBackportJava9Test(TestParameters parameters) {
+    super(parameters, OptionalInt.class, TEST_JAR, "backport.OptionalIntBackportJava9Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalIntBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("of");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
new file mode 100644
index 0000000..b0dfd62
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.OptionalLong;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public final class OptionalLongBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalLongBackportJava9Test(TestParameters parameters) {
+    super(parameters, OptionalLong.class, TEST_JAR, "backport.OptionalLongBackportJava9Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalLongBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("getAsLong");
+    ignoreInvokes("isPresent");
+    ignoreInvokes("of");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/OptionalMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/OptionalMethods.java
index 43c749a..6a1bbc2 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/OptionalMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/OptionalMethods.java
@@ -14,6 +14,9 @@
 import java.util.function.IntConsumer;
 import java.util.function.LongConsumer;
 import java.util.function.Supplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
 import java.util.stream.Stream;
 
 public class OptionalMethods {
@@ -73,4 +76,28 @@
       return Stream.empty();
     }
   }
+
+  public static IntStream streamInt(OptionalInt receiver) {
+    if (receiver.isPresent()) {
+      return IntStream.of(receiver.getAsInt());
+    } else {
+      return IntStream.empty();
+    }
+  }
+
+  public static LongStream streamLong(OptionalLong receiver) {
+    if (receiver.isPresent()) {
+      return LongStream.of(receiver.getAsLong());
+    } else {
+      return LongStream.empty();
+    }
+  }
+
+  public static DoubleStream streamDouble(OptionalDouble receiver) {
+    if (receiver.isPresent()) {
+      return DoubleStream.of(receiver.getAsDouble());
+    } else {
+      return DoubleStream.empty();
+    }
+  }
 }