void (final int commandIndex, File baseDir) { if (commandIndex >= commands.size()) { callback.message(null, "Finished OK " + passedStages.size() + " tests", StatusCallback.Type.message); actionCallback.complete(null); return; } CommandDescriptor commandDescriptor = commands.get(commandIndex); PlaybackCommand command = createCommand(commandDescriptor.fullLine, commandDescriptor.line, commandDescriptor.scriptDir); if (isStopRequested || command == null) { callback.message(null, "Stopped", StatusCallback.Type.message); actionCallback.cancel(false); return; } @SuppressWarnings("unchecked") Set<Class<?>> facadeClassesClone = (Set<Class<?>>)facadeClasses.clone(); PlaybackContext context = new PlaybackContext(this, callback, commandIndex, myRobot, useDirectActionCall, useTypingTargets, command, baseDir, facadeClassesClone) { private final long myTimeStamp = myContextTimestamp; @Override public void pushStage(StageInfo info) { currentStageDepth.add(info); } @Override public StageInfo popStage() { if (!currentStageDepth.isEmpty()) { return currentStageDepth.remove(currentStageDepth.size() - 1); } return null; } @Override public int getCurrentStageDepth() { return currentStageDepth.size(); } @Override public void addPassed(StageInfo stage) { passedStages.add(stage); } @Override public boolean isDisposed() { return myTimeStamp != myContextTimestamp || isStopRequested; } @Override public void storeRegistryValue(String key) { if (!registryValues.containsKey(key)) { registryValues.put(key, Registry.stringValue(key)); } } @Override public void setProject(@Nullable Project project) { myRunner.setProject(project); } @Override public @NotNull Project getProject() { Project project = myRunner.getProject(); if (project == null) { throw new IllegalStateException( "Project is null. Use a project-aware runner and check if its project has been set up properly"); } return project; } }; commandStartStopProcessor.startOfCommand(commandDescriptor.fullLine); CompletableFuture<?> commandFuture = command.execute(context); Context initialContext = Context.current(); commandFuture.whenComplete((result, error) -> { if (error != null) { commandStartStopProcessor.endOfCommand(error.getMessage()); callback.message(null, "Stopped: " + error, StatusCallback.Type.message); LOG.warn("Callback step stopped with error: " + error, error); actionCallback.completeExceptionally(error); return; } commandStartStopProcessor.endOfCommand(null); try (Scope ignored = initialContext.makeCurrent()) { if (command.canGoFurther()) { int delay = getDelay(command); if (delay > 0) { if (SwingUtilities.isEventDispatchThread()) { EdtScheduledExecutorService.getInstance().schedule(Context.current().wrap(() -> { if (!onStop.isDisposed()) { executeFrom(commandIndex + 1, context.getBaseDir()); } }), delay, TimeUnit.MILLISECONDS); } else { LockSupport.parkUntil(System.currentTimeMillis() + delay); executeFrom(commandIndex + 1, context.getBaseDir()); } } else { executeFrom(commandIndex + 1, context.getBaseDir()); } } else { callback.message(null, "Stopped: cannot go further", StatusCallback.Type.message); actionCallback.complete(null); } } }); }