Handle assertions where the assertions enabled guard is checked more
than once

The following code pattern is in the Kotlin stdlib

  if (_Assertions.ENABLED)
    assert(...) { ... }
  }

Bug: b/226370094
Change-Id: I681acad967f8bc0ae05abbd55ea4d92989377699
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 3dd01d8..fa7a539 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1175,7 +1175,7 @@
       return timing;
     }
 
-    assertionsRewriter.run(method, code, timing);
+    assertionsRewriter.run(method, code, deadCodeRemover, timing);
 
     if (serviceLoaderRewriter != null) {
       assert appView.appInfo().hasLiveness();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index db7343c..1961b30 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -33,10 +33,13 @@
 import com.android.tools.r8.utils.ThrowingCharIterator;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
 import java.io.UTFDataFormatException;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 public class AssertionsRewriter {
@@ -273,6 +276,7 @@
    *   }
    * }
    * </pre>
+   *
    * For Kotlin the Class instance method desiredAssertionStatus() is only called for the class
    * kotlin._Assertions, where kotlin._Assertions.class.desiredAssertionStatus() is read into the
    * static field kotlin._Assertions.ENABLED.
@@ -310,7 +314,8 @@
    * }
    * </pre>
    *
-   * With the rewriting below and AssertionTransformation.DISABLE (and other rewritings) the resulting code is:
+   * With the rewriting below and AssertionTransformation.DISABLE (and other rewritings) the
+   * resulting code is:
    *
    * <pre>
    * class A {
@@ -331,23 +336,26 @@
    *   }
    * }
    * </pre>
-
+   *
    * NOTE: that in Kotlin the assertion condition is always calculated. So it is still present in
    * the code and even for AssertionTransformation.DISABLE.
    */
-  public void run(DexEncodedMethod method, IRCode code, Timing timing) {
+  public void run(
+      DexEncodedMethod method, IRCode code, DeadCodeRemover deadCodeRemover, Timing timing) {
     if (enabled) {
       timing.begin("Rewrite assertions");
-      runInternal(method, code);
+      if (runInternal(method, code)) {
+        deadCodeRemover.run(code, timing);
+      }
       assert code.isConsistentSSA(appView);
       timing.end();
     }
   }
 
-  private void runInternal(DexEncodedMethod method, IRCode code) {
+  private boolean runInternal(DexEncodedMethod method, IRCode code) {
     ConfigurationEntryWithDexString configuration = getTransformationForMethod(method);
     if (configuration.isPassthrough()) {
-      return;
+      return false;
     }
     DexEncodedMethod clinit;
     // If the <clinit> of this class did not have have code to turn on assertions don't try to
@@ -357,20 +365,25 @@
     } else {
       DexClass clazz = appView.definitionFor(method.getHolderType());
       if (clazz == null) {
-        return;
+        return false;
       }
       clinit = clazz.getClassInitializer();
     }
     // For the transformation to rewrite the throw with a callback collect information on the
     // blocks covered by the if (!$assertionsDisabled or ENABLED) condition together with weather
     // the assertion handling is on the true or false branch.
-    Map<If, Boolean> assertionEntryIfs = new IdentityHashMap<>();
+    Map<If, Boolean> assertionEntryIfs = new Reference2BooleanOpenHashMap<>();
     Map<Throw, BasicBlock> throwSuccessorAfterHandler = new IdentityHashMap<>();
+    Set<BasicBlock> assertionBlocks = Sets.newIdentityHashSet();
+    Map<If, Boolean> additionalAssertionsEnabledIfs = new Reference2BooleanOpenHashMap<>();
     if (configuration.isAssertionHandler()) {
       LazyBox<DominatorTree> dominatorTree = new LazyBox<>(() -> new DominatorTree(code));
       code.getBlocks()
           .forEach(
               basicBlock -> {
+                if (assertionBlocks.contains(basicBlock)) {
+                  return;
+                }
                 If theIf = isCheckAssertionsEnabledBlock(basicBlock);
                 if (theIf != null) {
                   // All blocks dominated by the if is the assertion code. For Java it is on the
@@ -381,13 +394,30 @@
                           theIf.lhs().getDefinition().asStaticGet());
                   BasicBlock assertionBlockEntry =
                       theIf.targetFromBoolean(conditionForAssertionBlock);
+                  List<BasicBlock> dominatedBlocks =
+                      dominatorTree.computeIfAbsent().dominatedBlocks(assertionBlockEntry);
                   Throw throwInstruction =
-                      dominatedBlocksHasSingleThrow(
-                          assertionBlockEntry, dominatorTree.computeIfAbsent());
+                      dominatedBlocksHasSingleThrow(assertionBlockEntry, dominatedBlocks);
                   if (throwInstruction != null) {
                     assertionEntryIfs.put(theIf, conditionForAssertionBlock);
                     throwSuccessorAfterHandler.put(
                         throwInstruction, theIf.targetFromBoolean(!conditionForAssertionBlock));
+                    // Collect any additional assertions enabled checks dominated by the current
+                    // assertions entry check.
+                    dominatedBlocks.forEach(
+                        block -> {
+                          If additionalAssertionsEnabledIf = isCheckAssertionsEnabledBlock(block);
+                          if (additionalAssertionsEnabledIf != null) {
+                            additionalAssertionsEnabledIfs.put(
+                                additionalAssertionsEnabledIf,
+                                !isUsingJavaAssertionsDisabledField(
+                                    additionalAssertionsEnabledIf
+                                        .lhs()
+                                        .getDefinition()
+                                        .asStaticGet()));
+                          }
+                        });
+                    assertionBlocks.addAll(dominatedBlocks);
                   }
                 }
               });
@@ -399,6 +429,7 @@
         clinit != null && clinit.getOptimizationInfo().isInitializerEnablingJavaVmAssertions();
     // This code will process the assertion code in all methods including <clinit>.
     InstructionListIterator iterator = code.instructionListIterator();
+    boolean needsDeadCodeRemoval = false;
     while (iterator.hasNext()) {
       Instruction current = iterator.next();
       if (current.isInvokeMethod()) {
@@ -444,11 +475,12 @@
         if (current.isIf()) {
           If ifInstruction = current.asIf();
           if (assertionEntryIfs.containsKey(ifInstruction)) {
-            ifInstruction
-                .targetFromBoolean(!assertionEntryIfs.get(ifInstruction))
-                .unlinkSinglePredecessorSiblingsAllowed();
-            ifInstruction.lhs().removeUser(ifInstruction);
-            iterator.replaceCurrentInstruction(new Goto());
+            forceAssertionsEnabled(ifInstruction, assertionEntryIfs, iterator);
+            needsDeadCodeRemoval = true;
+          }
+          if (additionalAssertionsEnabledIfs.containsKey(ifInstruction)) {
+            forceAssertionsEnabled(ifInstruction, additionalAssertionsEnabledIfs, iterator);
+            needsDeadCodeRemoval = true;
           }
         } else if (current.isThrow()) {
           Throw throwInstruction = current.asThrow();
@@ -467,6 +499,7 @@
         }
       }
     }
+    return needsDeadCodeRemoval;
   }
 
   private void rewriteKotlinAssertionEnable(
@@ -539,10 +572,9 @@
         : null;
   }
 
-  private Throw dominatedBlocksHasSingleThrow(BasicBlock block, DominatorTree dominatorTree) {
+  private Throw dominatedBlocksHasSingleThrow(BasicBlock block, List<BasicBlock> dominatedBlocks) {
     Throw theThrow = null;
-    List<BasicBlock> blocks = dominatorTree.dominatedBlocks(block);
-    for (BasicBlock current : blocks) {
+    for (BasicBlock current : dominatedBlocks) {
       if (current.exit().isReturn()) {
         return null;
       }
@@ -555,4 +587,13 @@
     }
     return theThrow;
   }
+
+  private void forceAssertionsEnabled(
+      If ifInstruction, Map<If, Boolean> targetMap, InstructionListIterator iterator) {
+    ifInstruction
+        .targetFromBoolean(!targetMap.get(ifInstruction))
+        .unlinkSinglePredecessorSiblingsAllowed();
+    ifInstruction.lhs().removeUser(ifInstruction);
+    iterator.replaceCurrentInstruction(new Goto());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
index 84bd1ef..b70b311 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
@@ -4,12 +4,13 @@
 
 package com.android.tools.r8.rewrite.assertions;
 
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.references.MethodReference;
@@ -70,10 +71,21 @@
 
   protected abstract List<Path> getKotlinFiles() throws IOException;
 
+  protected boolean transformKotlinClasses() {
+    return false;
+  }
+
+  protected byte[] transformedKotlinClasses(Path kotlinClasses) throws IOException {
+    assert false;
+    return null;
+  }
+
   protected abstract String getTestClassName();
 
   protected void configureR8(R8FullTestBuilder builder) {}
 
+  protected void configureResultR8(R8TestCompileResult builder) {}
+
   private Path kotlinStdlibLibraryForRuntime() throws Exception {
     Path kotlinStdlibCf = kotlinc.getKotlinStdlibJar();
     if (parameters.getRuntime().isCf()) {
@@ -107,6 +119,16 @@
     }
   }
 
+  private void addKotlinClasses(TestBuilder<?, ?> builder) {
+    builder.applyIf(
+        transformKotlinClasses(),
+        b ->
+            b.addProgramClassFileData(
+                transformedKotlinClasses(
+                    compiledForAssertions.getForConfiguration(kotlinc, targetVersion))),
+        b -> b.addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)));
+  }
+
   @Test
   public void testD8() throws Exception {
     assumeTrue(parameters.isDexRuntime());
@@ -114,7 +136,7 @@
         .apply(this::configureKotlinStdlib)
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(AssertionHandlers.class)
-        .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
+        .apply(this::addKotlinClasses)
         .addAssertionsConfiguration(
             builder ->
                 builder
@@ -131,7 +153,7 @@
         .apply(this::configureKotlinStdlib)
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(AssertionHandlers.class)
-        .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
+        .apply(this::addKotlinClasses)
         .addAssertionsConfiguration(
             builder ->
                 builder
@@ -140,13 +162,8 @@
                     .build())
         .addKeepMainRule(getTestClassName())
         .apply(this::configureR8)
-        .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary)
         .compile()
-        .applyIf(
-            !kotlinStdlibAsLibrary,
-            result ->
-                result.assertAllWarningMessagesMatch(
-                    equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
+        .apply(this::configureResultR8)
         .run(parameters.getRuntime(), getTestClassName())
         .assertSuccessWithOutput(getExpectedOutput());
   }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
new file mode 100644
index 0000000..d0177bb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2022, 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.rewrite.assertions.kotlinassertionhandlerdoublecheck;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.AssertionConfigurationAssertionHandlerKotlinTestBase;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest
+    extends AssertionConfigurationAssertionHandlerKotlinTestBase {
+
+  public AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean kotlinStdlibAsClasspath,
+      boolean useJvmAssertions)
+      throws IOException {
+    super(parameters, kotlinParameters, kotlinStdlibAsClasspath, useJvmAssertions);
+  }
+
+  @Override
+  protected String getExpectedOutput() {
+    return StringUtils.lines("assertionHandler: doubleCheckAssertion");
+  }
+
+  @Override
+  protected MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+  }
+
+  @Override
+  protected List<Path> getKotlinFiles() throws IOException {
+    return getKotlinFilesInTestPackage(getClass().getPackage());
+  }
+
+  @Override
+  protected boolean transformKotlinClasses() {
+    return true;
+  }
+
+  @Override
+  protected byte[] transformedKotlinClasses(Path kotlinClasses) throws IOException {
+    Path compiledKotlinClasses = temp.newFolder().toPath();
+    ZipUtils.unzip(
+        compiledForAssertions.getForConfiguration(kotlinc, targetVersion), compiledKotlinClasses);
+    String testClassPath =
+        DescriptorUtils.getPackageBinaryNameFromJavaType(getTestClassName())
+            + FileUtils.CLASS_EXTENSION;
+    String assertionsMockBinaryName =
+        DescriptorUtils.getPackageBinaryNameFromJavaType(
+            getClass().getPackage().getName() + ".AssertionsMock");
+    // Rewrite the static get on AssertionsMock.Enabled to static get on kotlin._Assertions.ENABLED
+    return transformer(
+            compiledKotlinClasses.resolve(testClassPath),
+            Reference.classFromTypeName(getTestClassName()))
+        .transformFieldInsnInMethod(
+            "doubleCheckAssertionsEnabled",
+            (opcode, owner, name, descriptor, continuation) -> {
+              continuation.visitFieldInsn(
+                  opcode,
+                  owner.equals(assertionsMockBinaryName) ? "kotlin/_Assertions" : owner,
+                  name,
+                  descriptor);
+            })
+        .transform();
+  }
+
+  @Override
+  protected String getTestClassName() {
+    return getClass().getPackage().getName() + ".AssertionDoubleCheckKt";
+  }
+
+  @Override
+  protected void configureR8(R8FullTestBuilder builder) {
+    boolean referencesNotNull =
+        !kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72) && !kotlinStdlibAsLibrary;
+    builder.applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionDoubleCheck.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionDoubleCheck.kt
new file mode 100644
index 0000000..3351bcd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionDoubleCheck.kt
@@ -0,0 +1,28 @@
+// Copyright (c) 2022, 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.rewrite.assertions.kotlinassertionhandlerdoublecheck
+
+fun doubleCheckAssertionsEnabled() {
+  // AssertionsMock.ENABLED will be rewritten to kotlin._Assertions.ENABLED to
+  // test
+  //   * two checks on kotlin._Assertions.ENABLED and
+  //   * check on both kotlin._Assertions.ENABLED and $assertionsDisabled
+  // before entering the assertion code.
+  //
+  // This is testing code like this found in kotlin-stdlib:
+  //
+  //   if (_Assertions.ENABLED)
+  //     assert(...) { ... }
+  //   }
+  //
+  // E.g. in kotlin/io/files/FileTreeWalk.kt.
+  if (AssertionsMock.ENABLED) {
+    assert(false) { "doubleCheckAssertion" }
+  }
+}
+
+fun main() {
+  doubleCheckAssertionsEnabled();
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionsMock.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionsMock.kt
new file mode 100644
index 0000000..bd05154
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionsMock.kt
@@ -0,0 +1,10 @@
+// Copyright (c) 2022, 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.rewrite.assertions.kotlinassertionhandlerdoublecheck
+
+object AssertionsMock {
+    @JvmField
+    val ENABLED: Boolean = false
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
index e460253..aad753e 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
@@ -4,9 +4,12 @@
 
 package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlersimple;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
@@ -62,6 +65,17 @@
         !kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72)
             && !kotlinStdlibAsLibrary
             && !useJvmAssertions;
-    builder.applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull"));
+    builder
+        .applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull"))
+        .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary);
+  }
+
+  @Override
+  protected void configureResultR8(R8TestCompileResult builder) {
+    builder.applyIf(
+        !kotlinStdlibAsLibrary,
+        result ->
+            result.assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
index 93b3b57..c2848c3 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
@@ -4,8 +4,11 @@
 
 package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlerwithexceptions;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
@@ -66,6 +69,16 @@
     builder
         .addKeepRules("-keep class **.*Kt { assertionsWith*(); simpleAssertion(); }")
         .addDontWarn("org.jetbrains.annotations.NotNull")
-        .applyIf(!kotlinStdlibAsLibrary, b -> b.addDontWarn("org.jetbrains.annotations.Nullable"));
+        .applyIf(!kotlinStdlibAsLibrary, b -> b.addDontWarn("org.jetbrains.annotations.Nullable"))
+        .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary);
+  }
+
+  @Override
+  protected void configureResultR8(R8TestCompileResult builder) {
+    builder.applyIf(
+        !kotlinStdlibAsLibrary,
+        result ->
+            result.assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")));
   }
 }