Add commandline options for assertion configuration

This also enables assertion configuration for L8.

Bug: 139898386
Change-Id: Icbdd0f5f289eb3d121464a283e9bfbb25c13f095
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 2915114..7dd1027 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -10,10 +11,28 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Arrays;
 
-public class BaseCompilerCommandParser {
+public class BaseCompilerCommandParser<
+    C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>> {
 
-  static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+  static final Iterable<String> ASSERTIONS_USAGE_MESSAGE =
+      Arrays.asList(
+          "  --force-enable-assertions[:[<class name>|<package name>...]]",
+          "  --force-ea[:[<class name>|<package name>...]]",
+          "                          # Forcefully enable javac generated assertion code.",
+          "  --force-disable-assertions[:[<class name>|<package name>...]]",
+          "  --force-da[:[<class name>|<package name>...]]",
+          "                          # Forcefully disable javac generated assertion code. This",
+          "                          # is the default handling of javac assertion code when",
+          "                          # generating DEX file format.",
+          "  --force-passthrough-assertions[:[<class name>|<package name>...]]",
+          "  --force-pa[:[<class name>|<package name>...]]",
+          "                          # Don't change javac generated assertion code. This",
+          "                          # is the default handling of javac assertion code when",
+          "                          # generating class file format.");
+
+  void parseMinApi(B builder, String minApiString, Origin origin) {
     int minApi;
     try {
       minApi = Integer.parseInt(minApiString);
@@ -28,6 +47,81 @@
     builder.setMinApiLevel(minApi);
   }
 
+  private static String PACAKGE_ASSERTION_POSTFIX = "...";
+
+  private void addAssertionTransformation(
+      B builder, AssertionTransformation transformation, String scope) {
+    if (scope == null) {
+      builder.addAssertionsConfiguration(
+          b -> b.setTransformation(transformation).setScopeAll().build());
+    } else {
+      assert scope.length() > 0;
+      if (scope.endsWith(PACAKGE_ASSERTION_POSTFIX)) {
+        builder.addAssertionsConfiguration(
+            b ->
+                b.setTransformation(transformation)
+                    .setScopePackage(
+                        scope.substring(0, scope.length() - PACAKGE_ASSERTION_POSTFIX.length()))
+                    .build());
+      } else {
+        builder.addAssertionsConfiguration(
+            b -> b.setTransformation(transformation).setScopeClass(scope).build());
+      }
+    }
+  }
+
+  boolean tryParseAssertionArgument(B builder, String arg, Origin origin) {
+    String FORCE_ENABLE_ASSERTIONS = "--force-enable-assertions";
+    String FORCE_EA = "--force-ea";
+    String FORCE_DISABLE_ASSERTIONS = "--force-disable-assertions";
+    String FORCE_DA = "--force-da";
+    String FORCE_PASSTHROUGH_ASSERTIONS = "--force-passthrough-assertions";
+    String FORCE_PA = "--force-pa";
+
+    AssertionTransformation transformation = null;
+    String remaining = null;
+    if (arg.startsWith(FORCE_ENABLE_ASSERTIONS)) {
+      transformation = AssertionTransformation.ENABLE;
+      remaining = arg.substring(FORCE_ENABLE_ASSERTIONS.length());
+    } else if (arg.startsWith(FORCE_EA)) {
+      transformation = AssertionTransformation.ENABLE;
+      remaining = arg.substring(FORCE_EA.length());
+    } else if (arg.startsWith(FORCE_DISABLE_ASSERTIONS)) {
+      transformation = AssertionTransformation.DISABLE;
+      remaining = arg.substring(FORCE_DISABLE_ASSERTIONS.length());
+    } else if (arg.startsWith(FORCE_DA)) {
+      transformation = AssertionTransformation.DISABLE;
+      remaining = arg.substring(FORCE_DA.length());
+    } else if (arg.startsWith(FORCE_PASSTHROUGH_ASSERTIONS)) {
+      transformation = AssertionTransformation.PASSTHROUGH;
+      remaining = arg.substring(FORCE_PASSTHROUGH_ASSERTIONS.length());
+    } else if (arg.startsWith(FORCE_PA)) {
+      transformation = AssertionTransformation.PASSTHROUGH;
+      remaining = arg.substring(FORCE_PA.length());
+    }
+    if (transformation != null) {
+      if (remaining.length() == 0) {
+        addAssertionTransformation(builder, transformation, null);
+        return true;
+      } else {
+        if (remaining.length() == 1 || remaining.charAt(0) != ':') {
+          return false;
+        }
+        String classOrPackageScope = remaining.substring(1);
+        if (classOrPackageScope.contains(";")
+            || classOrPackageScope.contains("[")
+            || classOrPackageScope.contains("/")) {
+          builder.error(
+              new StringDiagnostic("Illegal assertion scope: " + classOrPackageScope, origin));
+        }
+        addAssertionTransformation(builder, transformation, remaining.substring(1));
+        return true;
+      }
+    } else {
+      return false;
+    }
+  }
+
   /**
    * This method must match the lookup in
    * {@link com.android.tools.r8.JdkClassFileProvider#fromJdkHome}.
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 1099cf9..fda08a6 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -21,7 +22,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class D8CommandParser extends BaseCompilerCommandParser {
+public class D8CommandParser extends BaseCompilerCommandParser<D8Command, D8Command.Builder> {
 
   private static final Set<String> OPTIONS_WITH_PARAMETER =
       ImmutableSet.of(
@@ -108,31 +109,33 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: d8 [options] <input-files>",
-              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-              " and options are:",
-              "  --debug                 # Compile with debugging information (default).",
-              "  --release               # Compile without debugging information.",
-              "  --output <file>         # Output result in <outfile>.",
-              "                          # <file> must be an existing directory or a zip file.",
-              "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
-              "  --classpath <file>      # Add <file> as a classpath resource.",
-              "  --min-api <number>      # Minimum Android API level compatibility, default: "
-                  + AndroidApiLevel.getDefault().getLevel()
-                  + ".",
-              "  --intermediate          # Compile an intermediate result intended for later",
-              "                          # merging.",
-              "  --file-per-class        # Produce a separate dex file per input class",
-              "  --no-desugaring         # Force disable desugaring.",
-              "  --desugared-lib <file>  # Specify desugared library configuration.",
-              "                          # <file> is a desugared library configuration (json).",
-              "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
-              "  --main-dex-list-output <file>",
-              "                          # Output resulting main dex list in <file>.",
-              "  --version               # Print the version of d8.",
-              "  --help                  # Print this message."));
-
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: d8 [options] <input-files>",
+                  " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+                  " and options are:",
+                  "  --debug                 # Compile with debugging information (default).",
+                  "  --release               # Compile without debugging information.",
+                  "  --output <file>         # Output result in <outfile>.",
+                  "                          # <file> must be an existing directory or a zip file.",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
+                  "  --classpath <file>      # Add <file> as a classpath resource.",
+                  "  --min-api <number>      # Minimum Android API level compatibility, default: "
+                      + AndroidApiLevel.getDefault().getLevel()
+                      + ".",
+                  "  --intermediate          # Compile an intermediate result intended for later",
+                  "                          # merging.",
+                  "  --file-per-class        # Produce a separate dex file per input class",
+                  "  --no-desugaring         # Force disable desugaring.",
+                  "  --desugared-lib <file>  # Specify desugared library configuration.",
+                  "                          # <file> is a desugared library configuration (json).",
+                  "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+                  "  --main-dex-list-output <file>",
+                  "                          # Output resulting main dex list in <file>."),
+              ASSERTIONS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of d8.",
+                  "  --help                  # Print this message.")));
   /**
    * Parse the D8 command-line.
    *
@@ -143,7 +146,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static D8Command.Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, D8Command.builder());
+    return new D8CommandParser().parse(args, origin, D8Command.builder());
   }
 
   /**
@@ -157,10 +160,10 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, D8Command.builder(handler));
+    return new D8CommandParser().parse(args, origin, D8Command.builder(handler));
   }
 
-  private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+  private D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
     CompilationMode compilationMode = null;
     Path outputPath = null;
     OutputMode outputMode = null;
@@ -250,11 +253,12 @@
         builder.setDisableDesugaring(true);
       } else if (arg.equals("--desugared-lib")) {
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
-      } else {
-        if (arg.startsWith("--")) {
+      } else if (arg.startsWith("--")) {
+        if (!tryParseAssertionArgument(builder, arg, origin)) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
           continue;
         }
+      } else {
         builder.addProgramFiles(Paths.get(arg));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 1ddd338..d190d6c 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -82,6 +81,7 @@
       int minApiLevel,
       Reporter diagnosticsHandler,
       DesugaredLibraryConfiguration libraryConfiguration,
+      List<AssertionsConfiguration> assertionsConfiguration,
       DexItemFactory factory) {
     super(
         inputApp,
@@ -94,7 +94,7 @@
         false,
         false,
         (name, checksum) -> true,
-        ImmutableList.of());
+        assertionsConfiguration);
     this.d8Command = d8Command;
     this.r8Command = r8Command;
     this.libraryConfiguration = libraryConfiguration;
@@ -315,6 +315,7 @@
           getMinApiLevel(),
           getReporter(),
           libraryConfiguration,
+          getAssertionsConfiguration(),
           factory);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 1dc83c1..bf35b67 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -10,12 +10,13 @@
 import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Set;
 
-public class L8CommandParser extends BaseCompilerCommandParser {
+public class L8CommandParser extends BaseCompilerCommandParser<L8Command, L8Command.Builder> {
 
   private static final Set<String> OPTIONS_WITH_PARAMETER =
       ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
@@ -32,23 +33,27 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: l8 [options] <input-files>",
-              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-              " and options are:",
-              "  --debug                 # Compile with debugging information (default).",
-              "  --release               # Compile without debugging information.",
-              "  --output <file>         # Output result in <outfile>.",
-              "                          # <file> must be an existing directory or a zip file.",
-              "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
-              "  --min-api <number>      # Minimum Android API level compatibility, default: "
-                  + AndroidApiLevel.getDefault().getLevel()
-                  + ".",
-              "  --pg-conf <file>        # Proguard configuration <file>.",
-              "  --desugared-lib <file>  # Specify desugared library configuration.",
-              "                          # <file> is a desugared library configuration (json).",
-              "  --version               # Print the version of l8.",
-              "  --help                  # Print this message."));
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: l8 [options] <input-files>",
+                  " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+                  " and options are:",
+                  "  --debug                 # Compile with debugging information (default).",
+                  "  --release               # Compile without debugging information.",
+                  "  --output <file>         # Output result in <outfile>.",
+                  "                          # <file> must be an existing directory or a zip file.",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
+                  "  --min-api <number>      # Minimum Android API level compatibility, default: "
+                      + AndroidApiLevel.getDefault().getLevel()
+                      + ".",
+                  "  --pg-conf <file>        # Proguard configuration <file>.",
+                  "  --desugared-lib <file>  # Specify desugared library configuration.",
+                  "                          # <file> is a desugared library configuration"
+                      + " (json)."),
+              ASSERTIONS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of l8.",
+                  "  --help                  # Print this message.")));
 
   /**
    * Parse the D8 command-line.
@@ -60,7 +65,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static L8Command.Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, L8Command.builder());
+    return new L8CommandParser().parse(args, origin, L8Command.builder());
   }
 
   /**
@@ -74,10 +79,10 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static L8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, L8Command.builder(handler));
+    return new L8CommandParser().parse(args, origin, L8Command.builder(handler));
   }
 
-  private static L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
+  private L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
     CompilationMode compilationMode = null;
     Path outputPath = null;
     OutputMode outputMode = null;
@@ -139,11 +144,12 @@
         builder.addProguardConfigurationFiles(Paths.get(nextArg));
       } else if (arg.equals("--desugared-lib")) {
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
-      } else {
-        if (arg.startsWith("--")) {
+      } else if (arg.startsWith("--")) {
+        if (!tryParseAssertionArgument(builder, arg, origin)) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
           continue;
         }
+      } else {
         builder.addProgramFiles(Paths.get(arg));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index dd9ab8e..f1541bd 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -8,12 +8,13 @@
 import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Set;
 
-public class R8CommandParser extends BaseCompilerCommandParser {
+public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Command.Builder> {
 
   private static final Set<String> OPTIONS_WITH_PARAMETER =
       ImmutableSet.of(
@@ -49,36 +50,40 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: r8 [options] <input-files>",
-              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-              " and options are:",
-              "  --release                # Compile without debugging information (default).",
-              "  --debug                  # Compile with debugging information.",
-              "  --dex                    # Compile program to DEX file format (default).",
-              "  --classfile              # Compile program to Java classfile format.",
-              "  --output <file>          # Output result in <file>.",
-              "                           # <file> must be an existing directory or a zip file.",
-              "  --lib <file|jdk-home>    # Add <file|jdk-home> as a library resource.",
-              "  --classpath <file>       # Add <file> as a classpath resource.",
-              "  --min-api <number>       # Minimum Android API level compatibility, default: "
-                  + AndroidApiLevel.getDefault().getLevel()
-                  + ".",
-              "  --pg-conf <file>         # Proguard configuration <file>.",
-              "  --pg-map-output <file>   # Output the resulting name and line mapping to <file>.",
-              "  --desugared-lib <file>   # Specify desugared library configuration.",
-              "                           # <file> is a desugared library configuration (json).",
-              "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
-              "  --no-minification        # Force disable minification of names.",
-              "  --no-data-resources      # Ignore all data resources.",
-              "  --no-desugaring          # Force disable desugaring.",
-              "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
-              "                           # primary dex file.",
-              "  --main-dex-list <file>   # List of classes to place in the primary dex file.",
-              "  --main-dex-list-output <file>  ",
-              "                           # Output the full main-dex list in <file>.",
-              "  --version                # Print the version of r8.",
-              "  --help                   # Print this message."));
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: r8 [options] <input-files>",
+                  " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+                  " and options are:",
+                  "  --release               # Compile without debugging information (default).",
+                  "  --debug                 # Compile with debugging information.",
+                  "  --dex                   # Compile program to DEX file format (default).",
+                  "  --classfile             # Compile program to Java classfile format.",
+                  "  --output <file>         # Output result in <file>.",
+                  "                          # <file> must be an existing directory or a zip file.",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
+                  "  --classpath <file>      # Add <file> as a classpath resource.",
+                  "  --min-api <number>      # Minimum Android API level compatibility, default: "
+                      + AndroidApiLevel.getDefault().getLevel()
+                      + ".",
+                  "  --pg-conf <file>        # Proguard configuration <file>.",
+                  "  --pg-map-output <file>  # Output the resulting name and line mapping to"
+                      + " <file>.",
+                  "  --desugared-lib <file>  # Specify desugared library configuration.",
+                  "                          # <file> is a desugared library configuration (json).",
+                  "  --no-tree-shaking       # Force disable tree shaking of unreachable classes.",
+                  "  --no-minification       # Force disable minification of names.",
+                  "  --no-data-resources     # Ignore all data resources.",
+                  "  --no-desugaring         # Force disable desugaring.",
+                  "  --main-dex-rules <file> # Proguard keep rules for classes to place in the",
+                  "                          # primary dex file.",
+                  "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+                  "  --main-dex-list-output <file>  ",
+                  "                          # Output the full main-dex list in <file>."),
+              ASSERTIONS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of r8.",
+                  "  --help                  # Print this message.")));
   /**
    * Parse the R8 command-line.
    *
@@ -213,10 +218,12 @@
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
       } else if (arg.equals("--no-data-resources")) {
         state.includeDataResources = false;
-      } else {
-        if (arg.startsWith("--")) {
+      } else if (arg.startsWith("--")) {
+        if (!tryParseAssertionArgument(builder, arg, argsOrigin)) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+          continue;
         }
+      } else {
         builder.addProgramFiles(Paths.get(arg));
       }
     }
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index db0baa3..fbacef6 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -13,6 +13,8 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.sdklib.AndroidVersion;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.D8CommandParser.OrderedClassFileResourceProvider;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.dex.Marker;
@@ -519,6 +521,51 @@
     assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(1, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(2, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertEquals(transformation, entries.get(1).getTransformation());
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
+  @Test
+  public void forceAssertionOption() throws Exception {
+    checkSingleForceAllAssertion(
+        parse("--force-enable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-disable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-passthrough-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-enable-assertions:ClassName", "--force-enable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-disable-assertions:ClassName", "--force-disable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-passthrough-assertions:ClassName",
+                "--force-passthrough-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+  }
+
   @Test(expected = CompilationFailedException.class)
   public void missingParameterForLastOption() throws CompilationFailedException {
     DiagnosticsChecker.checkErrorsContains(
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 558ba0d..060bdbb 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -8,6 +8,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
@@ -182,11 +184,79 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    L8Command l8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+    L8Command l8Command =
+        parse("--desugared-lib", ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString());
     assertFalse(
         l8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(1, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(2, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertEquals(transformation, entries.get(1).getTransformation());
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
+  @Test
+  public void forceAssertionOption() throws Exception {
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-enable-assertions",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-disable-assertions",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-passthrough-assertions",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-enable-assertions:ClassName",
+                "--force-enable-assertions:PackageName...",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-disable-assertions:ClassName",
+                "--force-disable-assertions:PackageName...",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-passthrough-assertions:ClassName",
+                "--force-passthrough-assertions:PackageName...",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+  }
+
   private L8Command parse(String... args) throws CompilationFailedException {
     return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 165b510..cc92bf3 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -10,6 +10,8 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.dex.Marker;
@@ -632,6 +634,51 @@
     runCustomResourceProcessing(false, false, 0);
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(1, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(2, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertEquals(transformation, entries.get(1).getTransformation());
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
+  @Test
+  public void forceAssertionOption() throws Exception {
+    checkSingleForceAllAssertion(
+        parse("--force-enable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-disable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-passthrough-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-enable-assertions:ClassName", "--force-enable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-disable-assertions:ClassName", "--force-disable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-passthrough-assertions:ClassName",
+                "--force-passthrough-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+  }
+
   @Test(expected = CompilationFailedException.class)
   public void missingParameterForLastOption() throws CompilationFailedException {
     DiagnosticsChecker.checkErrorsContains(