Add debug tests for synchronized blocks.

Two of these tests witness errors in the debug info and are ignored.

R=ager, shertz

Bug: 65567013
Change-Id: I7d8315018ce0e43b4094d119baf41e890929acf8
diff --git a/src/test/debugTestResources/SynchronizedBlock.java b/src/test/debugTestResources/SynchronizedBlock.java
new file mode 100644
index 0000000..e1cd8c2
--- /dev/null
+++ b/src/test/debugTestResources/SynchronizedBlock.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2017, 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.
+
+public class SynchronizedBlock {
+
+  public static int emptyBlock(Object obj) {
+    int x = 123;
+    synchronized (obj) {}
+    int y = 456;
+    return x + y;
+  }
+
+  public static int nonThrowingBlock(Object obj) {
+    int x = 123;
+    synchronized (obj) {
+      dontThrow();
+    }
+    int y = 456;
+    return x + y;
+  }
+
+  public static int throwingBlock(Object obj) {
+    try {
+      int x = 123;
+      synchronized (obj) {
+        doThrow();
+      }
+      int y = 456;
+      return x + y;
+    } catch (Throwable e) { return 42; } // one line to avoid Java vs Art issue.
+  }
+
+  public static int nestedNonThrowingBlock(Object obj1, Object obj2) {
+    int x = 123;
+    synchronized (obj1) {
+      dontThrow();
+      synchronized (obj2) {
+        dontThrow();
+      }
+    }
+    int y = 456;
+    return x + y;
+  }
+
+  public static int nestedThrowingBlock(Object obj1, Object obj2) {
+    try {
+      int x = 123;
+      synchronized (obj1) {
+        dontThrow();
+        synchronized (obj2) {
+          doThrow();
+        }
+      }
+      int y = 456;
+      return x + y;
+    } catch (Throwable e) { return 42; } // one line to avoid Java vs Art issue.
+  }
+
+  public static void dontThrow() {
+    return;
+  }
+
+  public static void doThrow() {
+    throw new RuntimeException();
+  }
+
+  public static void main(String[] args) {
+    System.out.println(emptyBlock(new Object()));
+    System.out.println(nonThrowingBlock(new Object()));
+    System.out.println(throwingBlock(new Object()));
+    System.out.println(nestedNonThrowingBlock(new Object(), new Object()));
+    System.out.println(nestedThrowingBlock(new Object(), new Object()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java b/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
new file mode 100644
index 0000000..42f0174
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
@@ -0,0 +1,202 @@
+// Copyright (c) 2017, 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.debug;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Test single stepping behaviour of synchronized blocks.
+ */
+public class SynchronizedBlockTest extends DebugTestBase {
+
+  public static final String CLASS = "SynchronizedBlock";
+  public static final String FILE = "SynchronizedBlock.java";
+
+  @Test
+  public void testEmptyBlock() throws Throwable {
+    final String method = "emptyBlock";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 8),
+        checkLocal("obj"),
+        stepOver(),
+        checkLine(FILE, 9),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 10),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 11),
+        checkLocal("obj"),
+        checkLocal("x"),
+        checkLocal("y"),
+        run());
+  }
+
+  @Test
+  public void testNonThrowingBlock() throws Throwable {
+    final String method = "nonThrowingBlock";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 15),
+        checkLocal("obj"),
+        stepOver(),
+        checkLine(FILE, 16),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 17),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 18),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 19),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 20),
+        checkLocal("obj"),
+        checkLocal("x"),
+        checkLocal("y"),
+        run());
+  }
+
+  @Test
+  @Ignore("b/65567013")
+  public void testThrowingBlock() throws Throwable {
+    final String method = "throwingBlock";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 25),
+        checkLocal("obj"),
+        stepOver(),
+        checkLine(FILE, 26),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 27),
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 28), // synchronized block end
+        checkLocal("obj"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 31), // catch handler
+        checkLocal("obj"),
+        checkNoLocal("x"),
+        stepOver(),
+        run());
+  }
+
+  @Test
+  public void testNestedNonThrowingBlock() throws Throwable {
+    final String method = "nestedNonThrowingBlock";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 35),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        stepOver(),
+        checkLine(FILE, 36),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 37),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 38),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 39),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 40),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 41),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 42),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 43),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        checkLocal("y"),
+        run());
+  }
+
+  @Test
+  @Ignore("b/65567013")
+  public void testNestedThrowingBlock() throws Throwable {
+    final String method = "nestedThrowingBlock";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 48),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        stepOver(),
+        checkLine(FILE, 49),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 50),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 51),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 52),
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 53), // inner synchronize block end
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 54), // outer synchronize block end
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkLocal("x"),
+        stepOver(),
+        checkLine(FILE, 57), // catch handler
+        checkLocal("obj1"),
+        checkLocal("obj2"),
+        checkNoLocal("x"),
+        run());
+  }
+}