void () { @Language("JAVA") String text = """ class LQF { // xxx int f;<caret> }"""; configureByText(JavaFileType.INSTANCE, text); makeWholeEditorWindowVisible((EditorImpl)myEditor); // get "visible area first" optimization out of the way UIUtil.markAsFocused(getEditor().getContentComponent(), true); // to make ShowIntentionPass call its collectInformation() SeverityRegistrar.getSeverityRegistrar(getProject()); //preload inspection profile AtomicReference<String> fieldWarningText = new AtomicReference<>("1st run"); AtomicInteger stallMs = new AtomicInteger(0); AtomicBoolean slowToolFinished = new AtomicBoolean(); LocalInspectionTool slowTool = new MyFegnaInspection() { @Override public void inspectionFinished(@NotNull LocalInspectionToolSession session, @NotNull ProblemsHolder problemsHolder) { slowToolFinished.set(true); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { @Override public void visitField(@NotNull PsiField field) { holder.registerProblem(field.getNameIdentifier(), fieldWarningText.get()); super.visitField(field); } @Override public void visitElement(@NotNull PsiElement element) { // stall every other element to exacerbate latency problems if the order is wrong TimeoutUtil.sleep(stallMs.get()); } }; } }; AtomicBoolean fastToolFinished = new AtomicBoolean(); // highlights all "xxx" comments, only when there are no comments after it String fastToolText = "blah"; LocalInspectionTool fastTool = new MyFegnaInspection() { @Override public void inspectionFinished(@NotNull LocalInspectionToolSession session, @NotNull ProblemsHolder problemsHolder) { fastToolFinished.set(true); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { @Override public void visitComment(@NotNull PsiComment comment) { if (comment.getText().contains("xxx") && !comment.getContainingFile().getText().substring(comment.getTextOffset()+2).contains("//")) { holder.registerProblem(comment, fastToolText, ProblemHighlightType.WARNING); } } }; } }; disposeOnTearDown(() -> disableInspectionTool(fastTool.getShortName())); disposeOnTearDown(() -> disableInspectionTool(slowTool.getShortName())); for (Tools tools : ProjectInspectionProfileManager.getInstance(getProject()).getCurrentProfile().getAllEnabledInspectionTools(getProject())) { disableInspectionTool(tools.getTool().getShortName()); } enableInspectionTools(fastTool, slowTool); // both inspections should produce their results List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING); assertTrue(infos.toString(), ContainerUtil.exists(infos, i -> i.getDescription().equals(fieldWarningText.get()))); assertTrue(infos.toString(), ContainerUtil.exists(infos, i -> i.getDescription().equals(fastToolText))); fieldWarningText.set("Aha, field, finally!"); stallMs.set(100); type("// another comment"); fastToolFinished.set(false); slowToolFinished.set(false); makeWholeEditorWindowVisible((EditorImpl)myEditor); // get "visible area first" optimization out of the way // now when the LIP restarted, we should get back our inspection result very fast, despite very slow processing of every other element long deadline = System.currentTimeMillis() + 10_000; while (!daemonIsWorkingOrPending()) { PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); if (System.currentTimeMillis() > deadline) { fail("Too long waiting for daemon to start"); } } MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(getEditor().getDocument(), getProject(), true); try { boolean fastToolFinishedFaster = false; while (daemonIsWorkingOrPending()) { if (System.currentTimeMillis() > deadline) { fail("Too long waiting for daemon to finish\n"+ThreadDumper.dumpThreadsToString()); } PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); if (fastToolFinished.get() && !slowToolFinished.get()) { fastToolFinishedFaster = true; boolean found = !DaemonCodeAnalyzerEx.processHighlights(model, getProject(), HighlightSeverity.WARNING, 0, myEditor.getDocument().getTextLength(), info -> !fastToolText.equals(info.getDescription())); if (found) { fail("Inspection must have removed its own obsolete highlights as soon as it's finished, but got:" + Arrays.toString(model.getAllHighlighters())+"; thread dump:\n"+ThreadDumper.dumpThreadsToString()); } } } assertTrue("Fast inspection must have finished faster than the slow one, but it didn't", fastToolFinishedFaster); } finally { stallMs.set(0); } }