/**
   * Test that the executeWhileTargetAvailale interface works properly for a single operation with a
   * single step when the target is stopped.
   */
  @Test
  public void executeSingleStepSingleOpWhileTargetStopped() throws Throwable {
    // The target is currently stopped.

    // A single step that will set a breakpoint at PrintHello, which we will then make sure hits
    final Step[] steps =
        new Step[] {
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(bpTargetDmc, true, false, null, 0, "PrintHello", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          }
        };

    Query<Boolean> query =
        new Query<Boolean>() {
          @Override
          protected void execute(DataRequestMonitor<Boolean> rm) {
            fRunCtrl.executeWithTargetAvailable(fContainerDmc, steps, rm);
          }
        };
    try {
      fRunCtrl.getExecutor().execute(query);
      query.get(500, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      fail(e.getMessage());
    } catch (ExecutionException e) {
      fail(e.getCause().getMessage());
    } catch (TimeoutException e) {
      fail(TIMEOUT_MESSAGE);
    }

    // Now resume the target and check that we stop at the breakpoint.

    ServiceEventWaitor<ISuspendedDMEvent> suspendedEventWaitor =
        new ServiceEventWaitor<ISuspendedDMEvent>(
            getGDBLaunch().getSession(), ISuspendedDMEvent.class);

    SyncUtil.resume();

    // Wait up to 3 second for the target to suspend. Should happen within 2 second.
    suspendedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(3000));
  }
  /**
   * Test that the executeWhileTargetAvailale interface works properly for concurrent operations
   * with a single step when the target is running. This tests verifies that we properly handle
   * concurrent operations that are dependent on each other; this means that the second operation
   * needs to complete for the second one to complete.
   */
  @Test
  public void executeSingleStepConcurrentAndDependentOpWhileTargetRunning() throws Throwable {
    final String location = "PrintHello";
    final String location2 = "PrintHi";
    final Step[] steps =
        new Step[] {
          new Step() {
            @Override
            public void execute(final RequestMonitor rm) {
              final IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(bpTargetDmc, true, false, null, 0, location, 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm) {
                    @Override
                    protected void handleSuccess() {
                      // Send another such operation and wait for it to complete to mark the
                      // original one as completed
                      fRunCtrl.executeWithTargetAvailable(
                          fContainerDmc,
                          new Step[] {
                            new Step() {
                              @Override
                              public void execute(final RequestMonitor otherRm) {
                                fGDBCtrl.queueCommand(
                                    fGDBCtrl
                                        .getCommandFactory()
                                        .createMIBreakInsert(
                                            bpTargetDmc, true, false, null, 0, location2, 0),
                                    new DataRequestMonitor<MIBreakInsertInfo>(
                                        fGDBCtrl.getExecutor(), otherRm));
                              }
                            }
                          },
                          rm);
                    }
                  });
            }
          }
        };

    // The target is currently stopped so we resume it
    ServiceEventWaitor<ISuspendedDMEvent> suspendedEventWaitor =
        new ServiceEventWaitor<ISuspendedDMEvent>(
            getGDBLaunch().getSession(), ISuspendedDMEvent.class);

    SyncUtil.resume();

    Query<Boolean> query =
        new Query<Boolean>() {
          @Override
          protected void execute(final DataRequestMonitor<Boolean> rm) {
            fRunCtrl.executeWithTargetAvailable(fContainerDmc, steps, rm);
          }
        };
    try {
      fRunCtrl.getExecutor().execute(query);
      query.get(500, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      fail(e.getMessage());
    } catch (ExecutionException e) {
      fail(e.getCause().getMessage());
    } catch (TimeoutException e) {
      fail(TIMEOUT_MESSAGE);
    }

    for (int i = 0; i < 2; i++) {
      // Wait up to 3 second for the target to suspend. Should happen within 2 seconds.
      suspendedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(3000));

      // Now resume the target and check that we stop at all the breakpoints.
      suspendedEventWaitor =
          new ServiceEventWaitor<ISuspendedDMEvent>(
              getGDBLaunch().getSession(), ISuspendedDMEvent.class);

      SyncUtil.resume();
    }
  }
  /**
   * Test that the executeWhileTargetAvailale interface works properly for a single operation with
   * multiple steps when the target is running and one of the steps fails.
   */
  @Test
  public void executeMultiStepSingleOpWhileTargetRunningWithError() throws Throwable {
    // Multiple steps that will set three temp breakpoints at three different lines
    // We then check that the target will stop three times
    final Step[] steps =
        new Step[] {
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(bpTargetDmc, true, false, null, 0, "PrintHello", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          },
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(
                          bpTargetDmc, true, false, "invalid condition", 0, "PrintHi", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          },
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(bpTargetDmc, true, false, null, 0, "PrintBonjour", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          }
        };

    // The target is currently stopped so we resume it
    ServiceEventWaitor<ISuspendedDMEvent> suspendedEventWaitor =
        new ServiceEventWaitor<ISuspendedDMEvent>(
            getGDBLaunch().getSession(), ISuspendedDMEvent.class);

    SyncUtil.resume();

    Query<Boolean> query =
        new Query<Boolean>() {
          @Override
          protected void execute(DataRequestMonitor<Boolean> rm) {
            fRunCtrl.executeWithTargetAvailable(fContainerDmc, steps, rm);
          }
        };

    boolean caughtError = false;
    try {
      fRunCtrl.getExecutor().execute(query);
      query.get(500, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      fail(e.getMessage());
    } catch (ExecutionException e) {
      caughtError = true;
    } catch (TimeoutException e) {
      fail(TIMEOUT_MESSAGE);
    }

    Assert.assertTrue("Did not catch the error of the step", caughtError);

    // Now make sure the target stop of the first breakpoint
    // Wait up to 3 second for the target to suspend. Should happen within two seconds.
    suspendedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(3000));
  }
  /**
   * Test that the executeWhileTargetAvailale interface works properly for concurrent operations
   * with a single step when the target is running.
   */
  @Test
  public void executeSingleStepConcurrentOpWhileTargetRunning() throws Throwable {
    final int NUM_CONCURRENT = 3;

    String[] locations = {"PrintHello", "PrintHi", "PrintBonjour"};
    final Step[][] steps = new Step[NUM_CONCURRENT][1]; // one step for each concurrent operation
    for (int i = 0; i < steps.length; i++) {
      final String location = locations[i];
      steps[i] =
          new Step[] {
            new Step() {
              @Override
              public void execute(RequestMonitor rm) {
                IBreakpointsTargetDMContext bpTargetDmc =
                    DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

                fGDBCtrl.queueCommand(
                    fGDBCtrl
                        .getCommandFactory()
                        .createMIBreakInsert(bpTargetDmc, true, false, null, 0, location, 0),
                    new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
              }
            }
          };
    }

    // The target is currently stopped so we resume it
    ServiceEventWaitor<ISuspendedDMEvent> suspendedEventWaitor =
        new ServiceEventWaitor<ISuspendedDMEvent>(
            getGDBLaunch().getSession(), ISuspendedDMEvent.class);

    SyncUtil.resume();

    Query<Boolean> query =
        new Query<Boolean>() {
          @Override
          protected void execute(final DataRequestMonitor<Boolean> rm) {
            CountingRequestMonitor crm =
                new CountingRequestMonitor(fGDBCtrl.getExecutor(), null) {
                  @Override
                  protected void handleCompleted() {
                    rm.done();
                  };
                };

            int index;
            for (index = 0; index < steps.length; index++) {
              fRunCtrl.executeWithTargetAvailable(fContainerDmc, steps[index], crm);
            }

            crm.setDoneCount(index);
          }
        };
    try {
      fRunCtrl.getExecutor().execute(query);
      query.get(500, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      fail(e.getMessage());
    } catch (ExecutionException e) {
      fail(e.getCause().getMessage());
    } catch (TimeoutException e) {
      fail(TIMEOUT_MESSAGE);
    }

    for (int i = 0; i < steps.length; i++) {
      // Wait up to 3 second for the target to suspend. Should happen within 2 seconds.
      suspendedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(3000));

      // Now resume the target and check that we stop at all the breakpoints.
      suspendedEventWaitor =
          new ServiceEventWaitor<ISuspendedDMEvent>(
              getGDBLaunch().getSession(), ISuspendedDMEvent.class);

      SyncUtil.resume();
    }
  }
  /**
   * Test that the executeWhileTargetAvailale interface works properly for a single operation with
   * multiple steps when the target is stopped and one of the steps fails.
   */
  @Test
  public void executeMultiStepSingleOpWhileTargetStoppedWithError() throws Throwable {
    // The target is currently stopped.

    // Multiple steps that will set three temp breakpoints at three different lines
    // We then check that the target will stop three times
    final Step[] steps =
        new Step[] {
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(bpTargetDmc, true, false, null, 0, "PrintHello", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          },
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(
                          bpTargetDmc, true, false, "invalid condition", 0, "PrintHi", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          },
          new Step() {
            @Override
            public void execute(RequestMonitor rm) {
              IBreakpointsTargetDMContext bpTargetDmc =
                  DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class);

              fGDBCtrl.queueCommand(
                  fGDBCtrl
                      .getCommandFactory()
                      .createMIBreakInsert(bpTargetDmc, true, false, null, 0, "PrintBonjour", 0),
                  new DataRequestMonitor<MIBreakInsertInfo>(fGDBCtrl.getExecutor(), rm));
            }
          }
        };

    Query<Boolean> query =
        new Query<Boolean>() {
          @Override
          protected void execute(DataRequestMonitor<Boolean> rm) {
            fRunCtrl.executeWithTargetAvailable(fContainerDmc, steps, rm);
          }
        };
    try {
      fRunCtrl.getExecutor().execute(query);
      query.get(500, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      fail(e.getMessage());
    } catch (ExecutionException e) {
      // We want to detect the error, so this is success
      return;
    } catch (TimeoutException e) {
      fail(TIMEOUT_MESSAGE);
    }

    fail("Did not detect the error of the step");
  }