public void testLoopLimits() {
    // right below limit: ok
    exec("for (int x = 0; x < 9999; ++x) {}");

    PainlessError expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("for (int x = 0; x < 10000; ++x) {}");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));
  }
  public void testInfiniteLoops() {
    PainlessError expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("boolean x = true; while (x) {}");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("while (true) {int y = 5}");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("while (true) { boolean x = true; while (x) {} }");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("while (true) { boolean x = false; while (x) {} }");
              fail("should have hit PainlessError");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("boolean x = true; for (;x;) {}");
              fail("should have hit PainlessError");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("for (;;) {int x = 5}");
              fail("should have hit PainlessError");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    expected =
        expectThrows(
            PainlessError.class,
            () -> {
              exec("def x = true; do {int y = 5;} while (x)");
              fail("should have hit PainlessError");
            });
    assertTrue(
        expected
            .getMessage()
            .contains(
                "The maximum number of statements that can be executed in a loop has been reached."));

    RuntimeException parseException =
        expectThrows(
            RuntimeException.class,
            () -> {
              exec("try { int x } catch (PainlessError error) {}");
              fail("should have hit ParseException");
            });
    assertTrue(parseException.getMessage().contains("Not a type [PainlessError]."));
  }