777098f5474c7d3f7e12f6ec6f08a653c8310462
[idea/community.git] / platform / testFramework / src / com / intellij / testFramework / fixtures / impl / CodeInsightTestFixtureImpl.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.testFramework.fixtures.impl;
3
4 import com.intellij.analysis.AnalysisScope;
5 import com.intellij.application.options.CodeStyle;
6 import com.intellij.codeHighlighting.RainbowHighlighter;
7 import com.intellij.codeInsight.AutoPopupController;
8 import com.intellij.codeInsight.TargetElementUtil;
9 import com.intellij.codeInsight.completion.CompletionType;
10 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
11 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
12 import com.intellij.codeInsight.daemon.GutterMark;
13 import com.intellij.codeInsight.daemon.impl.*;
14 import com.intellij.codeInsight.folding.CodeFoldingManager;
15 import com.intellij.codeInsight.highlighting.actions.HighlightUsagesAction;
16 import com.intellij.codeInsight.intention.IntentionAction;
17 import com.intellij.codeInsight.intention.impl.CachedIntentions;
18 import com.intellij.codeInsight.intention.impl.IntentionListStep;
19 import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
20 import com.intellij.codeInsight.lookup.LookupElement;
21 import com.intellij.codeInsight.lookup.LookupManager;
22 import com.intellij.codeInsight.lookup.impl.LookupImpl;
23 import com.intellij.codeInspection.InspectionProfileEntry;
24 import com.intellij.codeInspection.InspectionToolProvider;
25 import com.intellij.codeInspection.LocalInspectionTool;
26 import com.intellij.codeInspection.ex.InspectionToolWrapper;
27 import com.intellij.facet.Facet;
28 import com.intellij.facet.FacetManager;
29 import com.intellij.find.FindManager;
30 import com.intellij.find.findUsages.FindUsagesHandler;
31 import com.intellij.find.findUsages.FindUsagesManager;
32 import com.intellij.find.findUsages.FindUsagesOptions;
33 import com.intellij.find.impl.FindManagerImpl;
34 import com.intellij.ide.IdeEventQueue;
35 import com.intellij.ide.actions.searcheverywhere.ClassSearchEverywhereContributor;
36 import com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributor;
37 import com.intellij.ide.startup.StartupManagerEx;
38 import com.intellij.ide.startup.impl.StartupManagerImpl;
39 import com.intellij.ide.structureView.StructureViewBuilder;
40 import com.intellij.ide.structureView.newStructureView.StructureViewComponent;
41 import com.intellij.ide.util.scopeChooser.ScopeDescriptor;
42 import com.intellij.injected.editor.DocumentWindow;
43 import com.intellij.injected.editor.EditorWindow;
44 import com.intellij.injected.editor.VirtualFileWindow;
45 import com.intellij.lang.LanguageStructureViewBuilder;
46 import com.intellij.lang.annotation.HighlightSeverity;
47 import com.intellij.lang.injection.InjectedLanguageManager;
48 import com.intellij.mock.MockProgressIndicator;
49 import com.intellij.openapi.Disposable;
50 import com.intellij.openapi.actionSystem.AnAction;
51 import com.intellij.openapi.actionSystem.DataContext;
52 import com.intellij.openapi.actionSystem.IdeActions;
53 import com.intellij.openapi.actionSystem.Presentation;
54 import com.intellij.openapi.application.Application;
55 import com.intellij.openapi.application.ApplicationManager;
56 import com.intellij.openapi.application.ReadAction;
57 import com.intellij.openapi.application.WriteAction;
58 import com.intellij.openapi.command.WriteCommandAction;
59 import com.intellij.openapi.components.ServiceManager;
60 import com.intellij.openapi.editor.Document;
61 import com.intellij.openapi.editor.Editor;
62 import com.intellij.openapi.editor.EditorFactory;
63 import com.intellij.openapi.editor.Inlay;
64 import com.intellij.openapi.editor.colors.EditorColorsManager;
65 import com.intellij.openapi.editor.colors.EditorColorsScheme;
66 import com.intellij.openapi.editor.ex.EditorEx;
67 import com.intellij.openapi.editor.ex.util.EditorUtil;
68 import com.intellij.openapi.editor.impl.DocumentImpl;
69 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
70 import com.intellij.openapi.editor.markup.RangeHighlighter;
71 import com.intellij.openapi.editor.markup.TextAttributes;
72 import com.intellij.openapi.extensions.ExtensionPointName;
73 import com.intellij.openapi.extensions.ExtensionsArea;
74 import com.intellij.openapi.fileEditor.*;
75 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
76 import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
77 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
78 import com.intellij.openapi.fileTypes.FileType;
79 import com.intellij.openapi.fileTypes.FileTypeManager;
80 import com.intellij.openapi.module.Module;
81 import com.intellij.openapi.module.ModuleManager;
82 import com.intellij.openapi.progress.ProcessCanceledException;
83 import com.intellij.openapi.project.DumbService;
84 import com.intellij.openapi.project.Project;
85 import com.intellij.openapi.roots.ModuleRootManager;
86 import com.intellij.openapi.roots.ProjectRootManager;
87 import com.intellij.openapi.roots.impl.ProjectRootManagerImpl;
88 import com.intellij.openapi.util.*;
89 import com.intellij.openapi.util.io.FileUtil;
90 import com.intellij.openapi.util.registry.Registry;
91 import com.intellij.openapi.util.text.StringUtil;
92 import com.intellij.openapi.vcs.readOnlyHandler.ReadonlyStatusHandlerImpl;
93 import com.intellij.openapi.vfs.*;
94 import com.intellij.openapi.vfs.impl.VirtualFilePointerTracker;
95 import com.intellij.psi.*;
96 import com.intellij.psi.codeStyle.CodeStyleSettings;
97 import com.intellij.psi.impl.PsiManagerEx;
98 import com.intellij.psi.impl.PsiManagerImpl;
99 import com.intellij.psi.impl.source.PsiFileImpl;
100 import com.intellij.psi.impl.source.tree.FileElement;
101 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
102 import com.intellij.psi.search.SearchScope;
103 import com.intellij.psi.stubs.StubTextInconsistencyException;
104 import com.intellij.psi.util.PsiUtilBase;
105 import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor;
106 import com.intellij.refactoring.rename.*;
107 import com.intellij.rt.execution.junit.FileComparisonFailure;
108 import com.intellij.testFramework.*;
109 import com.intellij.testFramework.fixtures.*;
110 import com.intellij.testFramework.utils.inlays.CaretAndInlaysInfo;
111 import com.intellij.testFramework.utils.inlays.InlayHintsChecker;
112 import com.intellij.ui.components.breadcrumbs.Crumb;
113 import com.intellij.ui.content.Content;
114 import com.intellij.usageView.UsageInfo;
115 import com.intellij.usageView.UsageViewContentManager;
116 import com.intellij.usages.*;
117 import com.intellij.usages.impl.UsageViewImpl;
118 import com.intellij.util.*;
119 import com.intellij.util.containers.ContainerUtil;
120 import com.intellij.util.indexing.FileBasedIndex;
121 import com.intellij.util.indexing.FileBasedIndexExtension;
122 import com.intellij.util.indexing.FindSymbolParameters;
123 import com.intellij.util.io.ReadOnlyAttributeUtil;
124 import com.intellij.util.ui.UIUtil;
125 import junit.framework.ComparisonFailure;
126 import one.util.streamex.StreamEx;
127 import org.jetbrains.annotations.NotNull;
128 import org.jetbrains.annotations.Nullable;
129 import org.jetbrains.annotations.TestOnly;
130
131 import java.io.File;
132 import java.io.IOException;
133 import java.io.UncheckedIOException;
134 import java.nio.file.Path;
135 import java.nio.file.Paths;
136 import java.util.*;
137 import java.util.concurrent.Future;
138 import java.util.concurrent.TimeUnit;
139 import java.util.concurrent.atomic.AtomicBoolean;
140 import java.util.function.Predicate;
141 import java.util.stream.Collectors;
142 import java.util.stream.Stream;
143
144 import static com.intellij.openapi.util.io.FileUtil.toSystemDependentName;
145 import static com.intellij.testFramework.RunAll.runAll;
146 import static org.junit.Assert.*;
147
148 /**
149  * @author Dmitry Avdeev
150  */
151 @TestOnly
152 public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsightTestFixture {
153   private static final Function<IntentionAction, String> INTENTION_NAME_FUN = intentionAction -> '"' + intentionAction.getText() + '"';
154
155   private static final String RAINBOW = "rainbow";
156   private static final String FOLD = "fold";
157
158   private final IdeaProjectTestFixture myProjectFixture;
159   private final TempDirTestFixture myTempDirFixture;
160   private PsiManagerImpl myPsiManager;
161   private VirtualFile myFile;
162
163   // Strong references to PSI files configured by the test (to avoid tree access assertions after PSI has been GC'ed)
164   private PsiFile myPsiFile;
165   private PsiFile[] myAllPsiFiles;
166
167   private Editor myEditor;
168   private EditorTestFixture myEditorTestFixture;
169   private String myTestDataPath;
170   private VirtualFileFilter myVirtualFileFilter = new FileTreeAccessFilter();
171   private boolean myAllowDirt;
172   private boolean myCaresAboutInjection = true;
173   private VirtualFilePointerTracker myVirtualFilePointerTracker;
174   private ResourceBundle[] myMessageBundles = new ResourceBundle[0];
175
176   public CodeInsightTestFixtureImpl(@NotNull IdeaProjectTestFixture projectFixture, @NotNull TempDirTestFixture tempDirTestFixture) {
177     myProjectFixture = projectFixture;
178     myTempDirFixture = tempDirTestFixture;
179   }
180
181   private void setFileAndEditor(VirtualFile file, Editor editor) {
182     myFile = file;
183     myEditor = editor;
184     myEditorTestFixture = new EditorTestFixture(getProject(), editor, file);
185     myPsiFile = ReadAction.compute(() -> PsiManager.getInstance(getProject()).findFile(myFile));
186   }
187
188   private void clearFileAndEditor() {
189     myFile = null;
190     myEditor = null;
191     myEditorTestFixture = null;
192     myPsiFile = null;
193     myAllPsiFiles = null;
194   }
195
196   private static void addGutterIconRenderer(GutterMark renderer, int offset, @NotNull SortedMap<Integer, List<GutterMark>> result) {
197     if (renderer == null) return;
198
199     List<GutterMark> renderers = result.computeIfAbsent(offset, __ -> new SmartList<>());
200     renderers.add(renderer);
201   }
202
203   private static void removeDuplicatedRangesForInjected(@NotNull List<? extends HighlightInfo> infos) {
204     infos.sort((o1, o2) -> {
205       final int i = o2.startOffset - o1.startOffset;
206       return i != 0 ? i : o1.getSeverity().myVal - o2.getSeverity().myVal;
207     });
208     HighlightInfo prevInfo = null;
209     for (Iterator<? extends HighlightInfo> it = infos.iterator(); it.hasNext();) {
210       final HighlightInfo info = it.next();
211       if (prevInfo != null &&
212           info.getSeverity() == HighlightInfoType.SYMBOL_TYPE_SEVERITY &&
213           info.getDescription() == null &&
214           info.startOffset == prevInfo.startOffset &&
215           info.endOffset == prevInfo.endOffset) {
216         it.remove();
217       }
218       prevInfo = info.type == HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT ? info : null;
219     }
220   }
221
222   @NotNull
223   @TestOnly
224   public static List<HighlightInfo> instantiateAndRun(@NotNull PsiFile file,
225                                                       @NotNull Editor editor,
226                                                       @NotNull int[] toIgnore,
227                                                       boolean canChangeDocument) {
228     Project project = file.getProject();
229     ensureIndexesUpToDate(project);
230     DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
231     TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
232     DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
233
234     ProcessCanceledException exception = null;
235     int retries = 1000;
236     for (int i = 0; i < retries; i++) {
237       int oldDelay = settings.getAutoReparseDelay();
238       try {
239         settings.setAutoReparseDelay(0);
240         List<HighlightInfo> infos = new ArrayList<>();
241         EdtTestUtil.runInEdtAndWait(() -> {
242           codeAnalyzer.runPasses(file, editor.getDocument(), Collections.singletonList(textEditor), toIgnore, canChangeDocument, null);
243           IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
244           if (policy != null) {
245             policy.waitForHighlighting(project, editor);
246           }
247           infos.addAll(DaemonCodeAnalyzerImpl.getHighlights(editor.getDocument(), null, project));
248         });
249         infos.addAll(DaemonCodeAnalyzerEx.getInstanceEx(project).getFileLevelHighlights(project, file));
250         return infos;
251       }
252       catch (ProcessCanceledException e) {
253         Throwable cause = e.getCause();
254         if (cause != null && cause.getClass() != Throwable.class) {
255           // canceled because of an exception, no need to repeat the same a lot times
256           throw e;
257         }
258
259         EdtTestUtil.runInEdtAndWait(() -> {
260           PsiDocumentManager.getInstance(project).commitAllDocuments();
261           UIUtil.dispatchAllInvocationEvents();
262         });
263         exception = e;
264       }
265       finally {
266         settings.setAutoReparseDelay(oldDelay);
267       }
268     }
269     throw new AssertionError("Unable to highlight after " + retries + " retries", exception);
270   }
271
272   public static void ensureIndexesUpToDate(@NotNull Project project) {
273     if (!DumbService.isDumb(project)) {
274       ReadAction.run(() -> {
275         for (FileBasedIndexExtension<?,?> extension : FileBasedIndexExtension.EXTENSION_POINT_NAME.getExtensionList()) {
276           FileBasedIndex.getInstance().ensureUpToDate(extension.getName(), project, null);
277         }
278       });
279     }
280   }
281
282   @NotNull
283   public static List<IntentionAction> getAvailableIntentions(@NotNull Editor editor, @NotNull PsiFile file) {
284     return ReadAction.compute(() -> doGetAvailableIntentions(editor, file));
285   }
286
287   @NotNull
288   private static List<IntentionAction> doGetAvailableIntentions(@NotNull Editor editor, @NotNull PsiFile file) {
289     IdeaTestExecutionPolicy current = IdeaTestExecutionPolicy.current();
290     if (current != null) {
291       current.waitForHighlighting(file.getProject(), editor);
292     }
293     ShowIntentionsPass.IntentionsInfo intentions = ShowIntentionsPass.getActionsToShow(editor, file, false);
294
295     List<IntentionAction> result = new ArrayList<>();
296     IntentionListStep intentionListStep = new IntentionListStep(null, editor, file, file.getProject(),
297                                                                 CachedIntentions.create(file.getProject(), file, editor, intentions));
298     for (Map.Entry<IntentionAction, List<IntentionAction>> entry : intentionListStep.getActionsWithSubActions().entrySet()) {
299       result.add(entry.getKey());
300       result.addAll(entry.getValue());
301     }
302
303     List<HighlightInfo> infos = DaemonCodeAnalyzerEx.getInstanceEx(file.getProject()).getFileLevelHighlights(file.getProject(), file);
304     for (HighlightInfo info : infos) {
305       List<Pair<HighlightInfo.IntentionActionDescriptor, TextRange>> fixRanges = info.quickFixActionRanges;
306       if (fixRanges != null) {
307         for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : fixRanges) {
308           HighlightInfo.IntentionActionDescriptor actionInGroup = pair.first;
309           if (actionInGroup.getAction().isAvailable(file.getProject(), editor, file)) {
310             result.add(actionInGroup.getAction());
311             List<IntentionAction> options = actionInGroup.getOptions(file, editor);
312             if (options != null) {
313               for (IntentionAction subAction : options) {
314                 if (subAction.isAvailable(file.getProject(), editor, file)) {
315                   result.add(subAction);
316                 }
317               }
318             }
319           }
320         }
321       }
322     }
323     return result;
324   }
325
326   @NotNull
327   @Override
328   public String getTempDirPath() {
329     return myTempDirFixture.getTempDirPath();
330   }
331
332   @NotNull
333   @Override
334   public TempDirTestFixture getTempDirFixture() {
335     return myTempDirFixture;
336   }
337
338   @NotNull
339   @Override
340   public VirtualFile copyFileToProject(@NotNull String sourcePath) {
341     return copyFileToProject(sourcePath, sourcePath);
342   }
343
344   @NotNull
345   @Override
346   public VirtualFile copyFileToProject(@NotNull String sourcePath, @NotNull String targetPath) {
347     String testDataPath = getTestDataPath();
348     File sourceFile = new File(testDataPath, toSystemDependentName(sourcePath));
349     if (!sourceFile.exists()) {
350       File candidate = new File(sourcePath);
351       if (candidate.isAbsolute()) {
352         sourceFile = candidate;
353         if (FileUtil.pathsEqual(targetPath, sourcePath)) {
354           Path testDataPathObj = Paths.get(testDataPath);
355           Path targetPathObj = Paths.get(targetPath);
356           if (targetPathObj.startsWith(testDataPathObj) && !targetPathObj.equals(testDataPathObj)) {
357             targetPath = testDataPathObj.relativize(targetPathObj).toString();
358           }
359           else {
360             throw new IllegalArgumentException("Cannot guess target path for '" + sourcePath + "'; please specify explicitly");
361           }
362         }
363       }
364     }
365
366     targetPath = FileUtil.toSystemIndependentName(targetPath);
367
368     VirtualFile targetFile = myTempDirFixture.getFile(targetPath);
369
370     if (!sourceFile.exists() && targetFile != null && targetPath.equals(sourcePath)) {
371       return targetFile;
372     }
373
374     assertFileEndsWithCaseSensitivePath(sourceFile);
375
376     assertTrue("Cannot find source file: " + sourcePath + "; test data path: " + testDataPath, sourceFile.exists());
377     assertTrue("Not a file: " + sourceFile, sourceFile.isFile());
378
379     if (targetFile == null) {
380       targetFile = myTempDirFixture.createFile(targetPath);
381       VfsTestUtil.assertFilePathEndsWithCaseSensitivePath(targetFile, sourcePath);
382       targetFile.putUserData(VfsTestUtil.TEST_DATA_FILE_PATH, sourceFile.getAbsolutePath());
383     }
384
385     copyContent(sourceFile, targetFile);
386
387     return targetFile;
388   }
389
390   private static void assertFileEndsWithCaseSensitivePath(@Nullable File sourceFile) {
391     if (sourceFile == null) return;
392     try {
393       String sourceName = sourceFile.getPath();
394       File realFile = sourceFile.getCanonicalFile();
395       String realFileName = realFile.getPath();
396       if (!sourceName.equals(realFileName) && sourceName.equalsIgnoreCase(realFileName)) {
397         fail("Please correct case-sensitivity of path to prevent test failure on case-sensitive file systems:\n" +
398              "     path " + sourceFile.getPath() + "\n" +
399              "real path " + realFile.getPath());
400       }
401     }
402     catch (IOException e) {
403       throw new UncheckedIOException(e);
404     }
405   }
406
407   private static void copyContent(File sourceFile, VirtualFile targetFile) {
408     try {
409       WriteAction.runAndWait(() -> {
410         targetFile.setBinaryContent(FileUtil.loadFileBytes(sourceFile));
411         // update the document now, otherwise MemoryDiskConflictResolver will do it later at unexpected moment of time
412         FileDocumentManager.getInstance().reloadFiles(targetFile);
413       });
414     }
415     catch (IOException e) {
416       throw new RuntimeException(e);
417     }
418   }
419
420   @NotNull
421   @Override
422   public VirtualFile copyDirectoryToProject(@NotNull String sourcePath, @NotNull String targetPath) {
423     final String testDataPath = getTestDataPath();
424
425     final File fromFile = new File(testDataPath + "/" + sourcePath);
426     if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
427       return myTempDirFixture.copyAll(fromFile.getPath(), targetPath);
428     }
429
430     final File targetFile = new File(getTempDirPath() + "/" + targetPath);
431     try {
432       FileUtil.copyDir(fromFile, targetFile);
433     }
434     catch (IOException e) {
435       throw new RuntimeException(e);
436     }
437
438     final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(targetFile);
439     assertNotNull(file);
440     file.refresh(false, true);
441
442     IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
443     if (policy != null) {
444       PsiDirectory directory = ReadAction.compute(() -> PsiManager.getInstance(getProject()).findDirectory(file));
445       assertNotNull(directory);
446       policy.testDirectoryConfigured(directory);
447     }
448
449     return file;
450   }
451
452   @Override
453   public void enableInspections(@NotNull InspectionProfileEntry... inspections) {
454     assertInitialized();
455     InspectionsKt.enableInspectionTools(getProject(), myProjectFixture.getTestRootDisposable(), inspections);
456   }
457
458   @SafeVarargs
459   @Override
460   public final void enableInspections(@NotNull Class<? extends LocalInspectionTool>... inspections) {
461     enableInspections(Arrays.asList(inspections));
462   }
463
464   @Override
465   public void enableInspections(@NotNull Collection<Class<? extends LocalInspectionTool>> inspections) {
466     List<InspectionProfileEntry> tools = InspectionTestUtil.instantiateTools(inspections);
467     enableInspections(tools.toArray(new InspectionProfileEntry[0]));
468   }
469
470   @Override
471   public void disableInspections(@NotNull InspectionProfileEntry... inspections) {
472     InspectionsKt.disableInspections(getProject(), inspections);
473   }
474
475   @Override
476   public void enableInspections(@NotNull InspectionToolProvider... providers) {
477     List<Class<? extends LocalInspectionTool>> classes = Stream.of(providers)
478       .flatMap(p -> Stream.of(p.getInspectionClasses()))
479       .filter(LocalInspectionTool.class::isAssignableFrom)
480       .collect(Collectors.toList());
481     enableInspections(classes);
482   }
483
484   @Override
485   public long testHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings, @NotNull String... filePaths) {
486     if (filePaths.length > 0) {
487       configureByFilesInner(filePaths);
488     }
489     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings);
490   }
491
492   @Override
493   public long testHighlightingAllFiles(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings, @NotNull String... paths) {
494     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings, Stream.of(paths).map(this::copyFileToProject));
495   }
496
497   @Override
498   public long testHighlightingAllFiles(boolean checkWarnings,
499                                        boolean checkInfos,
500                                        boolean checkWeakWarnings,
501                                        @NotNull VirtualFile... files) {
502     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings, Stream.of(files));
503   }
504
505   private long collectAndCheckHighlighting(boolean checkWarnings,
506                                            boolean checkInfos,
507                                            boolean checkWeakWarnings,
508                                            Stream<? extends VirtualFile> files) {
509     List<Trinity<PsiFile, Editor, ExpectedHighlightingData>> data = files.map(file -> {
510       PsiFile psiFile = myPsiManager.findFile(file);
511       assertNotNull(psiFile);
512       Document document = PsiDocumentManager.getInstance(getProject()).getDocument(psiFile);
513       assertNotNull(document);
514       ExpectedHighlightingData datum =
515         new ExpectedHighlightingData(document, checkWarnings, checkWeakWarnings, checkInfos, false, psiFile, myMessageBundles);
516       datum.init();
517       return Trinity.create(psiFile, createEditor(file), datum);
518     })
519       .collect(Collectors.toList());
520     long elapsed = 0;
521     for (Trinity<PsiFile, Editor, ExpectedHighlightingData> trinity : data) {
522       setFileAndEditor(trinity.first.getVirtualFile(), trinity.second);
523       elapsed += collectAndCheckHighlighting(trinity.third);
524     }
525     return elapsed;
526   }
527
528   @Override
529   public long checkHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings) {
530     return checkHighlighting(checkWarnings, checkInfos, checkWeakWarnings, false);
531   }
532
533   @Override
534   public long checkHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings, boolean ignoreExtraHighlighting) {
535     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings, ignoreExtraHighlighting);
536   }
537
538   @Override
539   public long checkHighlighting() {
540     return checkHighlighting(true, false, true);
541   }
542
543   @Override
544   public long testHighlighting(@NotNull String... filePaths) {
545     return testHighlighting(true, false, true, filePaths);
546   }
547
548   @Override
549   public long testHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings, @NotNull VirtualFile file) {
550     openFileInEditor(file);
551     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings);
552   }
553
554   @NotNull
555   @Override
556   public HighlightTestInfo testFile(@NotNull String... filePath) {
557     return new HighlightTestInfo(myProjectFixture.getTestRootDisposable(), filePath) {
558       @Override
559       public HighlightTestInfo doTest() {
560         configureByFiles(filePaths);
561         ExpectedHighlightingData data =
562           new ExpectedHighlightingData(myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, false, getFile(),
563                                        myMessageBundles);
564         if (checkSymbolNames) data.checkSymbolNames();
565         data.init();
566         collectAndCheckHighlighting(data);
567         return this;
568       }
569     };
570   }
571
572   @Override
573   public void openFileInEditor(@NotNull final VirtualFile file) {
574     setFileAndEditor(file, createEditor(file));
575   }
576
577   @Override
578   public void testInspection(@NotNull String testDir, @NotNull InspectionToolWrapper<?,?> toolWrapper) {
579     VirtualFile sourceDir = copyDirectoryToProject(new File(testDir, "src").getPath(), "");
580     PsiDirectory psiDirectory = getPsiManager().findDirectory(sourceDir);
581     assertNotNull(psiDirectory);
582
583     AnalysisScope scope = new AnalysisScope(psiDirectory);
584     scope.invalidate();
585
586     GlobalInspectionContextForTests globalContext =
587       InspectionsKt.createGlobalContextForTool(scope, getProject(), Collections.<InspectionToolWrapper<?, ?>>singletonList(toolWrapper));
588
589     InspectionTestUtil.runTool(toolWrapper, scope, globalContext);
590     InspectionTestUtil.compareToolResults(globalContext, toolWrapper, false, new File(getTestDataPath(), testDir).getPath());
591   }
592
593   @Override
594   @Nullable
595   public PsiReference getReferenceAtCaretPosition(@NotNull final String... filePaths) {
596     if (filePaths.length > 0) {
597       configureByFilesInner(filePaths);
598     }
599     return getFile().findReferenceAt(myEditor.getCaretModel().getOffset());
600   }
601
602   @Override
603   @NotNull
604   public PsiReference getReferenceAtCaretPositionWithAssertion(@NotNull final String... filePaths) {
605     final PsiReference reference = getReferenceAtCaretPosition(filePaths);
606     assertNotNull("no reference found at " + myEditor.getCaretModel().getLogicalPosition(), reference);
607     return reference;
608   }
609
610   @Override
611   @NotNull
612   public List<IntentionAction> getAvailableIntentions(@NotNull final String... filePaths) {
613     if (filePaths.length > 0) {
614       configureByFilesInner(filePaths);
615     }
616     return getAvailableIntentions();
617   }
618
619   @Override
620   @NotNull
621   public List<IntentionAction> getAllQuickFixes(@NotNull final String... filePaths) {
622     if (filePaths.length != 0) {
623       configureByFilesInner(filePaths);
624     }
625     return myEditorTestFixture.getAllQuickFixes();
626   }
627
628   @Override
629   @NotNull
630   public List<IntentionAction> getAvailableIntentions() {
631     doHighlighting();
632     return ReadAction.compute(() -> getAvailableIntentions(getHostEditor(), getHostFileAtCaret()));
633   }
634
635   @NotNull
636   private Editor getHostEditor() {
637     return InjectedLanguageUtil.getTopLevelEditor(getEditor());
638   }
639
640   private PsiFile getHostFileAtCaret() {
641     return Objects.requireNonNull(PsiUtilBase.getPsiFileInEditor(getHostEditor(), getProject()));
642   }
643
644   @NotNull
645   @Override
646   public List<IntentionAction> filterAvailableIntentions(@NotNull String hint) {
647     return ContainerUtil.filter(getAvailableIntentions(), action -> action.getText().startsWith(hint));
648   }
649
650   @NotNull
651   @Override
652   public IntentionAction findSingleIntention(@NotNull String hint) {
653     final List<IntentionAction> list = filterAvailableIntentions(hint);
654     if (list.isEmpty()) {
655       fail("\"" + hint + "\" not in [" + StringUtil.join(getAvailableIntentions(), INTENTION_NAME_FUN, ", ") + "]");
656     }
657     else if (list.size() > 1) {
658       fail("Too many intentions found for \"" + hint + "\": [" + StringUtil.join(list, INTENTION_NAME_FUN, ", ") + "]");
659     }
660     return UsefulTestCase.assertOneElement(list);
661   }
662
663   @Override
664   public IntentionAction getAvailableIntention(@NotNull final String intentionName, @NotNull final String... filePaths) {
665     List<IntentionAction> intentions = getAvailableIntentions(filePaths);
666     IntentionAction action = CodeInsightTestUtil.findIntentionByText(intentions, intentionName);
667     if (action == null) {
668       //noinspection UseOfSystemOutOrSystemErr
669       System.out.println(intentionName + " not found among " + StringUtil.join(intentions, IntentionAction::getText, ","));
670     }
671     return action;
672   }
673
674   @Override
675   public void launchAction(@NotNull final IntentionAction action) {
676     EdtTestUtil.runInEdtAndWait(() -> invokeIntention(action, getHostFileAtCaret(), getHostEditor(), action.getText()));
677   }
678
679   @Override
680   public void testCompletion(@NotNull String[] filesBefore, @NotNull @TestDataFile String fileAfter) {
681     testCompletionTyping(filesBefore, "", fileAfter);
682   }
683
684   @Override
685   public void testCompletionTyping(@NotNull final String[] filesBefore, @NotNull String toType, @NotNull final String fileAfter) {
686     assertInitialized();
687     configureByFiles(filesBefore);
688     complete(CompletionType.BASIC);
689     type(toType);
690     try {
691       checkResultByFile(fileAfter);
692     }
693     catch (RuntimeException e) {
694       //noinspection UseOfSystemOutOrSystemErr
695       System.out.println("LookupElementStrings = " + getLookupElementStrings());
696       throw e;
697     }
698   }
699
700   protected void assertInitialized() {
701     assertNotNull("setUp() hasn't been called", myPsiManager);
702   }
703
704   @Override
705   public void testCompletion(@NotNull String fileBefore,
706                              @NotNull String fileAfter,
707                              @TestDataFile @NotNull String... additionalFiles) {
708     testCompletionTyping(fileBefore, "", fileAfter, additionalFiles);
709   }
710
711   @Override
712   public void testCompletionTyping(@NotNull @TestDataFile String fileBefore,
713                                    @NotNull String toType,
714                                    @NotNull @TestDataFile String fileAfter,
715                                    @TestDataFile @NotNull String... additionalFiles) {
716     testCompletionTyping(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, fileBefore)), toType, fileAfter);
717   }
718
719   @Override
720   public void testCompletionVariants(@NotNull final String fileBefore, @NotNull final String... expectedItems) {
721     assertInitialized();
722     final List<String> result = getCompletionVariants(fileBefore);
723     assertNotNull(result);
724     UsefulTestCase.assertSameElements(result, expectedItems);
725   }
726
727   @Override
728   public List<String> getCompletionVariants(@NotNull final String... filesBefore) {
729     assertInitialized();
730     configureByFiles(filesBefore);
731     final LookupElement[] items = complete(CompletionType.BASIC);
732     assertNotNull("No lookup was shown, probably there was only one lookup element that was inserted automatically", items);
733     return getLookupElementStrings();
734   }
735
736   @Override
737   @Nullable
738   public List<String> getLookupElementStrings() {
739     assertInitialized();
740     return myEditorTestFixture.getLookupElementStrings();
741   }
742
743   @Override
744   public void finishLookup(final char completionChar) {
745     myEditorTestFixture.finishLookup(completionChar);
746   }
747
748   @Override
749   public void testRename(@NotNull final String fileBefore,
750                          @NotNull String fileAfter,
751                          @NotNull String newName,
752                          @TestDataFile @NotNull String... additionalFiles) {
753     assertInitialized();
754     configureByFiles(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, fileBefore)));
755     testRename(fileAfter, newName);
756   }
757
758   @Override
759   public void testRenameUsingHandler(@NotNull String fileBefore,
760                                      @NotNull String fileAfter,
761                                      @NotNull String newName,
762                                      @NotNull String... additionalFiles) {
763     assertInitialized();
764     configureByFiles(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, fileBefore)));
765     testRenameUsingHandler(fileAfter, newName);
766   }
767
768   @Override
769   public void testRenameUsingHandler(@NotNull final String fileAfter, @NotNull final String newName) {
770     renameElementAtCaretUsingHandler(newName);
771     checkResultByFile(fileAfter);
772   }
773
774   @Override
775   public void testRename(@NotNull final String fileAfter, @NotNull final String newName) {
776     renameElementAtCaret(newName);
777     checkResultByFile(fileAfter);
778   }
779
780   @Override
781   @NotNull
782   public PsiElement getElementAtCaret() {
783     assertInitialized();
784     return myEditorTestFixture.getElementAtCaret();
785   }
786
787   @Override
788   public void renameElementAtCaret(@NotNull final String newName) {
789     renameElement(getElementAtCaret(), newName);
790   }
791
792   @Override
793   public void renameElementAtCaretUsingHandler(@NotNull final String newName) {
794     final DataContext editorContext = ((EditorEx)myEditor).getDataContext();
795     final DataContext context = dataId -> PsiElementRenameHandler.DEFAULT_NAME.getName().equals(dataId)
796            ? newName
797            : editorContext.getData(dataId);
798     final RenameHandler renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(context);
799     assertNotNull("No handler for this context", renameHandler);
800
801     renameHandler.invoke(getProject(), myEditor, getFile(), context);
802   }
803
804   @Override
805   public void renameElement(@NotNull final PsiElement element, @NotNull final String newName) {
806     final boolean searchInComments = false;
807     final boolean searchTextOccurrences = false;
808     renameElement(element, newName, searchInComments, searchTextOccurrences);
809   }
810
811   @Override
812   public void renameElement(@NotNull final PsiElement element,
813                             @NotNull final String newName,
814                             final boolean searchInComments,
815                             final boolean searchTextOccurrences) {
816     final PsiElement substitution = RenamePsiElementProcessor.forElement(element).substituteElementToRename(element, myEditor);
817     if (substitution == null) return;
818     new RenameProcessor(getProject(), substitution, newName, searchInComments, searchTextOccurrences).run();
819   }
820
821   @Override
822   public <T extends PsiElement> T findElementByText(@NotNull String text, @NotNull Class<T> elementClass) {
823     return myEditorTestFixture.findElementByText(text, elementClass);
824   }
825
826   @Override
827   public void type(final char c) {
828     assertInitialized();
829     myEditorTestFixture.type(c);
830   }
831
832   @Override
833   public void type(@NotNull String s) {
834     myEditorTestFixture.type(s);
835   }
836
837   @Override
838   public void performEditorAction(@NotNull final String actionId) {
839     assertInitialized();
840     EdtTestUtil.runInEdtAndWait(() -> myEditorTestFixture.performEditorAction(actionId));
841   }
842
843   @NotNull
844   @Override
845   public Presentation testAction(@NotNull AnAction action) {
846     TestActionEvent e = new TestActionEvent(action);
847     action.beforeActionPerformedUpdate(e);
848     if (e.getPresentation().isEnabled() && e.getPresentation().isVisible()) {
849       action.actionPerformed(e);
850     }
851     return e.getPresentation();
852   }
853
854   @NotNull
855   @Override
856   public Collection<UsageInfo> testFindUsages(@NotNull final String... fileNames) {
857     assertInitialized();
858     configureByFiles(fileNames);
859     int flags = TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED;
860     PsiElement targetElement = TargetElementUtil.findTargetElement(getEditor(), flags);
861     assertNotNull("Cannot find referenced element", targetElement);
862     return findUsages(targetElement);
863   }
864
865   @NotNull
866   @Override
867   public Collection<Usage> testFindUsagesUsingAction(@NotNull String... fileNames) {
868     assertInitialized();
869     configureByFiles(fileNames);
870     EdtTestUtil.runInEdtAndWait(() -> {
871       UsageViewContentManager usageViewManager = UsageViewContentManager.getInstance(getProject());
872       Content selectedContent;
873       while ((selectedContent = usageViewManager.getSelectedContent()) != null) {
874         usageViewManager.closeContent(selectedContent);
875       }
876
877       myEditorTestFixture.performEditorAction(IdeActions.ACTION_FIND_USAGES);
878     });
879     return EdtTestUtil.runInEdtAndGet(() -> {
880       long startMillis = System.currentTimeMillis();
881       UsageView view;
882       boolean viewWasInitialized = false;
883       while ((view = UsageViewManager.getInstance(getProject()).getSelectedUsageView()) == null || view.isSearchInProgress()) {
884         IdeEventQueue.getInstance().flushQueue();
885         viewWasInitialized |= view != null;
886         if (!viewWasInitialized && System.currentTimeMillis() - startMillis > TimeUnit.SECONDS.toMillis(10)) {
887           fail("UsageView wasn't shown");
888           return Collections.emptyList();
889         }
890       }
891       return view.getUsages();
892     });
893   }
894
895   @NotNull
896   @Override
897   public Collection<UsageInfo> findUsages(@NotNull final PsiElement targetElement) {
898     return findUsages(targetElement, null);
899   }
900
901   @NotNull
902   @Override
903   public String getUsageViewTreeTextRepresentation(@NotNull final Collection<? extends UsageInfo> usages) {
904     UsageViewImpl usageView = (UsageViewImpl)UsageViewManager
905       .getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY,
906                                                  StreamEx.of(usages)
907                                                    .map(usageInfo -> new UsageInfo2UsageAdapter(usageInfo)).toArray(Usage.EMPTY_ARRAY),
908                                                  new UsageViewPresentation(),
909                                                  null);
910     return getUsageViewTreeTextRepresentation(usageView);
911   }
912
913
914   @NotNull
915   @Override
916   public String getUsageViewTreeTextRepresentation(@NotNull final PsiElement targetElement) {
917     final FindUsagesManager usagesManager = ((FindManagerImpl)FindManager.getInstance(getProject())).getFindUsagesManager();
918     final FindUsagesHandler handler = usagesManager.getFindUsagesHandler(targetElement, false);
919     assertNotNull("Cannot find handler for: " + targetElement, handler);
920     final UsageViewImpl usageView = (UsageViewImpl)usagesManager.doFindUsages(handler.getPrimaryElements(),
921                                                                               handler.getSecondaryElements(),
922                                                                               handler,
923                                                                               handler.getFindUsagesOptions(),
924                                                                               false);
925     return getUsageViewTreeTextRepresentation(usageView);
926
927   }
928
929   @NotNull
930   public Collection<UsageInfo> findUsages(@NotNull final PsiElement targetElement, @Nullable SearchScope scope) {
931     final Project project = getProject();
932     final FindUsagesHandler handler =
933       ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false);
934
935     final CommonProcessors.CollectProcessor<UsageInfo> processor =
936       new CommonProcessors.CollectProcessor<>(Collections.synchronizedList(new ArrayList<>()));
937     assertNotNull("Cannot find handler for: " + targetElement, handler);
938     final PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements());
939     final FindUsagesOptions options = handler.getFindUsagesOptions(null);
940     if (scope != null) options.searchScope = scope;
941     for (PsiElement psiElement : psiElements) {
942       handler.processElementUsages(psiElement, processor, options);
943     }
944     return processor.getResults();
945   }
946
947   @NotNull
948   @Override
949   public RangeHighlighter[] testHighlightUsages(@NotNull final String... files) {
950     configureByFiles(files);
951     testAction(new HighlightUsagesAction());
952     final Editor editor = getEditor();
953     //final Editor editor = com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext());
954     //assert editor != null;
955     //HighlightUsagesHandler.invoke(getProject(), editor, getFile());
956     return editor.getMarkupModel().getAllHighlighters();
957   }
958
959   @Override
960   public void moveFile(@NotNull final String filePath, @NotNull final String to, @TestDataFile @NotNull final String... additionalFiles) {
961     assertInitialized();
962     final Project project = getProject();
963     configureByFiles(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, filePath)));
964     final VirtualFile file = findFileInTempDir(to);
965     assertNotNull("Directory " + to + " not found", file);
966     assertTrue(to + " is not a directory", file.isDirectory());
967     final PsiDirectory directory = myPsiManager.findDirectory(file);
968     new MoveFilesOrDirectoriesProcessor(project, new PsiElement[]{getFile()}, directory, false, false, null, null).run();
969   }
970
971   @Override
972   @Nullable
973   public GutterMark findGutter(@NotNull final String filePath) {
974     configureByFilesInner(filePath);
975     CommonProcessors.FindFirstProcessor<GutterMark> processor = new CommonProcessors.FindFirstProcessor<>();
976     doHighlighting();
977     processGuttersAtCaret(myEditor, getProject(), processor);
978     return processor.getFoundValue();
979   }
980
981   @NotNull
982   @Override
983   public List<GutterMark> findGuttersAtCaret() {
984     CommonProcessors.CollectProcessor<GutterMark> processor = new CommonProcessors.CollectProcessor<>();
985     doHighlighting();
986     processGuttersAtCaret(myEditor, getProject(), processor);
987     return new ArrayList<>(processor.getResults());
988   }
989
990   public static boolean processGuttersAtCaret(Editor editor, Project project, @NotNull Processor<? super GutterMark> processor) {
991     int offset = editor.getCaretModel().getOffset();
992
993     RangeHighlighter[] highlighters = DocumentMarkupModel.forDocument(editor.getDocument(), project, true).getAllHighlighters();
994     for (RangeHighlighter highlighter : highlighters) {
995       GutterMark renderer = highlighter.getGutterIconRenderer();
996       if (renderer != null &&
997           editor.getDocument().getLineNumber(offset) == editor.getDocument().getLineNumber(highlighter.getStartOffset()) &&
998           !processor.process(renderer)) {
999         return false;
1000       }
1001     }
1002     return true;
1003   }
1004
1005   @Override
1006   @NotNull
1007   public List<GutterMark> findAllGutters(@NotNull final String filePath) {
1008     configureByFilesInner(filePath);
1009     return findAllGutters();
1010   }
1011
1012   @Override
1013   @NotNull
1014   public List<GutterMark> findAllGutters() {
1015     final Project project = getProject();
1016     final SortedMap<Integer, List<GutterMark>> result = new TreeMap<>();
1017
1018     List<HighlightInfo> infos = doHighlighting();
1019     for (HighlightInfo info : infos) {
1020       addGutterIconRenderer(info.getGutterIconRenderer(), info.startOffset, result);
1021     }
1022
1023     RangeHighlighter[] highlighters = DocumentMarkupModel.forDocument(myEditor.getDocument(), project, true).getAllHighlighters();
1024     for (final RangeHighlighter highlighter : highlighters) {
1025       if (!highlighter.isValid()) continue;
1026       addGutterIconRenderer(highlighter.getGutterIconRenderer(), highlighter.getStartOffset(), result);
1027     }
1028     return ContainerUtil.concat(result.values());
1029   }
1030
1031   @Override
1032   public PsiFile addFileToProject(@NotNull final String relativePath, @NotNull final String fileText) {
1033     assertInitialized();
1034     return addFileToProject(getTempDirPath(), relativePath, fileText);
1035   }
1036
1037   protected PsiFile addFileToProject(@NotNull final String rootPath, @NotNull final String relativePath, @NotNull final String fileText) {
1038     try {
1039       VirtualFile file = WriteCommandAction.runWriteCommandAction(getProject(), (ThrowableComputable<VirtualFile, IOException>)() -> {
1040         try {
1041           VirtualFile f;
1042           if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
1043             f = myTempDirFixture.createFile(relativePath, fileText);
1044           }
1045           else if (myProjectFixture instanceof HeavyIdeaTestFixture){
1046             f = ((HeavyIdeaTestFixture)myProjectFixture).addFileToProject(rootPath, relativePath, fileText).getViewProvider()
1047                                                            .getVirtualFile();
1048           }
1049           else {
1050             f = myTempDirFixture.createFile(relativePath, fileText);
1051           }
1052
1053           prepareVirtualFile(f);
1054
1055           return f;
1056         }
1057         catch (IOException e) {
1058           throw new RuntimeException(e);
1059         }
1060         finally {
1061           PsiManager.getInstance(getProject()).dropPsiCaches();
1062         }
1063       });
1064       return ReadAction.compute(() -> PsiManager.getInstance(getProject()).findFile(file));
1065     }
1066     catch (IOException e) {
1067       throw new RuntimeException(e);
1068     }
1069   }
1070
1071   public <T> void registerExtension(@NotNull ExtensionsArea area, @NotNull ExtensionPointName<T> epName, @NotNull T extension) {
1072     assertInitialized();
1073     area.getExtensionPoint(epName).registerExtension(extension, myProjectFixture.getTestRootDisposable());
1074   }
1075
1076   @NotNull
1077   @Override
1078   public PsiManager getPsiManager() {
1079     return myPsiManager;
1080   }
1081
1082   @Override
1083   public LookupElement[] complete(@NotNull CompletionType type) {
1084     return myEditorTestFixture.complete(type);
1085   }
1086
1087   @Override
1088   public LookupElement[] complete(@NotNull final CompletionType type, final int invocationCount) {
1089     assertInitialized();
1090     return myEditorTestFixture.complete(type, invocationCount);
1091   }
1092
1093   @Override
1094   @Nullable
1095   public LookupElement[] completeBasic() {
1096     return myEditorTestFixture.completeBasic();
1097   }
1098
1099   @Override
1100   @NotNull
1101   public final List<LookupElement> completeBasicAllCarets(@Nullable final Character charToTypeAfterCompletion) {
1102     return myEditorTestFixture.completeBasicAllCarets(charToTypeAfterCompletion);
1103   }
1104
1105   @Override
1106   public void saveText(@NotNull final VirtualFile file, @NotNull final String text) {
1107     try {
1108     WriteAction.runAndWait(() -> VfsUtil.saveText(file, text));
1109     }
1110     catch (IOException e) {
1111       throw new RuntimeException(e);
1112     }
1113   }
1114
1115   @Override
1116   @Nullable
1117   public LookupElement[] getLookupElements() {
1118     return myEditorTestFixture.getLookupElements();
1119   }
1120
1121   @Override
1122   public void checkResult(@NotNull String text) {
1123     checkResult(text, false);
1124   }
1125
1126   @Override
1127   public void checkResult(@NotNull String text, boolean stripTrailingSpaces) {
1128     IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
1129     if (policy != null) {
1130       policy.beforeCheckResult(getFile());
1131     }
1132     WriteCommandAction.runWriteCommandAction(getProject(), () -> {
1133       PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1134       EditorUtil.fillVirtualSpaceUntilCaret(getHostEditor());
1135       checkResult("TEXT", stripTrailingSpaces, SelectionAndCaretMarkupLoader.fromText(text), getHostFile().getText());
1136     });
1137   }
1138
1139   @Override
1140   public void checkResult(@NotNull String filePath, @NotNull String text, boolean stripTrailingSpaces) {
1141     IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
1142     if (policy != null) {
1143       policy.beforeCheckResult(getFile());
1144     }
1145     WriteCommandAction.runWriteCommandAction(getProject(), () -> {
1146       PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1147       PsiFile psiFile = getFileToCheck(filePath);
1148       checkResult("TEXT", stripTrailingSpaces, SelectionAndCaretMarkupLoader.fromText(text), psiFile.getText());
1149     });
1150   }
1151
1152   @Override
1153   public void checkResultByFile(@NotNull String expectedFile) {
1154     checkResultByFile(expectedFile, false);
1155   }
1156
1157   @Override
1158   public void checkResultByFile(@NotNull String expectedFile, boolean ignoreTrailingWhitespaces) {
1159     assertInitialized();
1160     ApplicationManager.getApplication().invokeAndWait(() -> checkResultByFile(expectedFile, getHostFile(), ignoreTrailingWhitespaces));
1161   }
1162
1163   @Override
1164   public void checkResultByFile(@NotNull String filePath, @NotNull String expectedFile, boolean ignoreTrailingWhitespaces) {
1165     assertInitialized();
1166     ApplicationManager.getApplication().invokeAndWait(() -> checkResultByFile(expectedFile, getFileToCheck(filePath), ignoreTrailingWhitespaces));
1167   }
1168
1169   private PsiFile getFileToCheck(String filePath) {
1170     String path = filePath.replace(File.separatorChar, '/');
1171     VirtualFile copy = findFileInTempDir(path);
1172     assertNotNull("could not find results file " + path, copy);
1173     PsiFile psiFile = myPsiManager.findFile(copy);
1174     assertNotNull(copy.getPath(), psiFile);
1175     return psiFile;
1176   }
1177
1178   @Override
1179   public void setUp() throws Exception {
1180     super.setUp();
1181
1182     TestApplicationManager.getInstance();
1183     EdtTestUtil.runInEdtAndWait(() -> {
1184       myProjectFixture.setUp();
1185       myTempDirFixture.setUp();
1186
1187       VirtualFile tempDir = myTempDirFixture.getFile("");
1188       assertNotNull(tempDir);
1189       HeavyPlatformTestCase.synchronizeTempDirVfs(tempDir);
1190
1191       myPsiManager = (PsiManagerImpl)PsiManager.getInstance(getProject());
1192       InspectionsKt.configureInspections(LocalInspectionTool.EMPTY_ARRAY, getProject(), myProjectFixture.getTestRootDisposable());
1193
1194       DaemonCodeAnalyzerImpl daemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1195       daemonCodeAnalyzer.prepareForTest();
1196
1197       DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false);
1198       ensureIndexesUpToDate(getProject());
1199       ((StartupManagerImpl)StartupManagerEx.getInstanceEx(getProject())).runPostStartupActivitiesRegisteredDynamically();
1200       CodeStyle.setTemporarySettings(getProject(), new CodeStyleSettings());
1201
1202       IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
1203       if (policy != null) {
1204         policy.setUp(getProject(), getTestRootDisposable(), getTestDataPath());
1205       }
1206     });
1207
1208     for (Module module : ModuleManager.getInstance(getProject()).getModules()) {
1209       ModuleRootManager.getInstance(module).orderEntries().getAllLibrariesAndSdkClassesRoots(); // instantiate all VFPs
1210     }
1211     if (shouldTrackVirtualFilePointers()) {
1212       myVirtualFilePointerTracker = new VirtualFilePointerTracker();
1213     }
1214   }
1215
1216   protected boolean shouldTrackVirtualFilePointers() {
1217     return true;
1218   }
1219
1220   @Override
1221   public void tearDown() throws Exception {
1222     // don't use method references here to make stack trace reading easier
1223     //noinspection Convert2MethodRef
1224     runAll(
1225       () -> {
1226         EdtTestUtil.runInEdtAndWait(() -> {
1227           if (ApplicationManager.getApplication() == null) {
1228             return;
1229           }
1230
1231           Project project;
1232           try {
1233             project = myProjectFixture.getProject();
1234           }
1235           catch (AssertionError ignore) {
1236             project = null;
1237           }
1238
1239           if (project != null) {
1240             CodeStyle.dropTemporarySettings(project);
1241             // clear "show param info" delayed requests leaking project
1242             AutoPopupController autoPopupController = project.getServiceIfCreated(AutoPopupController.class);
1243             if (autoPopupController != null) {
1244               autoPopupController.cancelAllRequests();
1245             }
1246           }
1247
1248           // return default value to avoid unnecessary save
1249           DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings = ServiceManager.getServiceIfCreated(DaemonCodeAnalyzerSettings.class);
1250           if (daemonCodeAnalyzerSettings != null) {
1251             daemonCodeAnalyzerSettings.setImportHintEnabled(true);
1252           }
1253
1254           if (project != null) {
1255             closeOpenFiles();
1256             ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project)).cleanupAfterTest();
1257             // needed for myVirtualFilePointerTracker check below
1258             ((ProjectRootManagerImpl)ProjectRootManager.getInstance(project)).clearScopesCachesForModules();
1259           }
1260         });
1261       },
1262       () -> {
1263         clearFileAndEditor();
1264         myPsiManager = null;
1265       },
1266       () -> disposeRootDisposable(),
1267       () -> EdtTestUtil.runInEdtAndWait(() -> myProjectFixture.tearDown()),
1268       () -> EdtTestUtil.runInEdtAndWait(() -> myTempDirFixture.tearDown()),
1269       () -> super.tearDown(),
1270       () -> {
1271         if (myVirtualFilePointerTracker != null) {
1272           myVirtualFilePointerTracker.assertPointersAreDisposed();
1273         }
1274       }
1275     );
1276   }
1277
1278   private void closeOpenFiles() {
1279     Project project = getProject();
1280     if (project == null) {
1281       return;
1282     }
1283
1284     LookupManager.hideActiveLookup(project);
1285     PsiDocumentManager.getInstance(project).commitAllDocuments();
1286     FileEditorManagerEx.getInstanceEx(project).closeAllFiles();
1287     EditorHistoryManager.getInstance(project).removeAllFiles();
1288   }
1289
1290   @NotNull
1291   private PsiFile[] configureByFilesInner(@NotNull String... filePaths) {
1292     assertInitialized();
1293     clearFileAndEditor();
1294     myAllPsiFiles = new PsiFile[filePaths.length];
1295     for (int i = filePaths.length - 1; i >= 0; i--) {
1296       myAllPsiFiles[i] = configureByFileInner(filePaths[i]);
1297     }
1298     return myAllPsiFiles;
1299   }
1300
1301   @Override
1302   public PsiFile configureByFile(@NotNull final String file) {
1303     configureByFilesInner(file);
1304     return getFile();
1305   }
1306
1307   @NotNull
1308   @Override
1309   public PsiFile[] configureByFiles(@NotNull final String... files) {
1310     return configureByFilesInner(files);
1311   }
1312
1313   @Override
1314   public PsiFile configureByText(@NotNull final FileType fileType, @NotNull final String text) {
1315     assertInitialized();
1316     final String extension = fileType.getDefaultExtension();
1317     final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
1318     if (fileTypeManager.getFileTypeByExtension(extension) != fileType) {
1319       WriteCommandAction.runWriteCommandAction(getProject(), () -> fileTypeManager.associateExtension(fileType, extension));
1320     }
1321     final String fileName = "aaa." + extension;
1322     return configureByText(fileName, text);
1323   }
1324
1325   @Override
1326   public PsiFile configureByText(@NotNull final String fileName, @NotNull final String text) {
1327     assertInitialized();
1328     VirtualFile vFile;
1329     try {
1330       vFile = WriteCommandAction.writeCommandAction(getProject()).compute(() -> {
1331         final VirtualFile file;
1332         if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
1333           final VirtualFile root = LightPlatformTestCase.getSourceRoot();
1334           root.refresh(false, false);
1335           file = root.findOrCreateChildData(this, fileName);
1336           assertNotNull(fileName + " not found in " + root.getPath(), file);
1337         }
1338         else if (myTempDirFixture instanceof TempDirTestFixtureImpl) {
1339           Path tempFile = ((TempDirTestFixtureImpl)myTempDirFixture).createTempFile(fileName);
1340           file = LocalFileSystem.getInstance().refreshAndFindFileByPath(FileUtil.toSystemIndependentName(tempFile.toString()));
1341           assertNotNull(tempFile + " not found", file);
1342         }
1343         else {
1344           file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(getTempDirPath(), fileName));
1345           assertNotNull(fileName + " not found in " + getTempDirPath(), file);
1346         }
1347
1348         prepareVirtualFile(file);
1349
1350         final Document document = FileDocumentManager.getInstance().getCachedDocument(file);
1351         if (document != null) {
1352           PsiDocumentManager.getInstance(getProject()).doPostponedOperationsAndUnblockDocument(document);
1353           FileDocumentManager.getInstance().saveDocument(document);
1354         }
1355
1356         VfsUtil.saveText(file, text);
1357         return file;
1358       });
1359     }
1360     catch (IOException e) {
1361       throw new RuntimeException(e);
1362     }
1363     configureInner(vFile, SelectionAndCaretMarkupLoader.fromFile(vFile));
1364     return getFile();
1365   }
1366
1367   @Override
1368   public Document getDocument(@NotNull final PsiFile file) {
1369     assertInitialized();
1370     return PsiDocumentManager.getInstance(getProject()).getDocument(file);
1371   }
1372
1373   private PsiFile configureByFileInner(@NotNull String filePath) {
1374     assertInitialized();
1375     VirtualFile file = copyFileToProject(filePath);
1376     return configureByFileInner(file);
1377   }
1378
1379   @Override
1380   public PsiFile configureFromTempProjectFile(@NotNull final String filePath) {
1381     final VirtualFile fileInTempDir = findFileInTempDir(filePath);
1382     if (fileInTempDir == null) {
1383       throw new IllegalArgumentException("Could not find file in temp dir: " + filePath);
1384     }
1385     return configureByFileInner(fileInTempDir);
1386   }
1387
1388   @Override
1389   public void configureFromExistingVirtualFile(@NotNull VirtualFile virtualFile) {
1390     configureByFileInner(virtualFile);
1391   }
1392
1393   private PsiFile configureByFileInner(@NotNull VirtualFile copy) {
1394     return configureInner(copy, SelectionAndCaretMarkupLoader.fromFile(copy));
1395   }
1396
1397   private PsiFile configureInner(@NotNull final VirtualFile copy, @NotNull final SelectionAndCaretMarkupLoader loader) {
1398     assertInitialized();
1399
1400     EdtTestUtilKt.runInEdtAndWait(() -> {
1401       if (!copy.getFileType().isBinary()) {
1402         try {
1403           WriteAction.run(() -> copy.setBinaryContent(loader.newFileText.getBytes(copy.getCharset())));
1404         }
1405         catch (IOException e) {
1406           throw new RuntimeException(e);
1407         }
1408       }
1409       setFileAndEditor(copy, createEditor(copy));
1410       if (myEditor == null) {
1411         fail("editor couldn't be created for: " + copy.getPath() + ", use copyFileToProject() instead of configureByFile()");
1412       }
1413
1414       EditorTestUtil.setCaretsAndSelection(myEditor, loader.caretState);
1415
1416       Module module = getModule();
1417       if (module != null) {
1418         for (Facet<?> facet : FacetManager.getInstance(module).getAllFacets()) {
1419           module.getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
1420         }
1421       }
1422       PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1423
1424       if (myCaresAboutInjection) {
1425         setupEditorForInjectedLanguage();
1426       }
1427
1428       IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
1429       if (policy != null) {
1430         policy.testFileConfigured(getFile());
1431       }
1432
1433       return null;
1434     });
1435
1436     return getFile();
1437   }
1438
1439   protected void prepareVirtualFile(@NotNull VirtualFile file) {
1440   }
1441
1442   private void setupEditorForInjectedLanguage() {
1443     Editor editor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(myEditor, getFile());
1444     if (editor instanceof EditorWindow) {
1445       setFileAndEditor(((EditorWindow)editor).getInjectedFile().getViewProvider().getVirtualFile(), editor);
1446     }
1447   }
1448
1449   @Override
1450   public VirtualFile findFileInTempDir(@NotNull final String filePath) {
1451     if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
1452       return myTempDirFixture.getFile(filePath);
1453     }
1454     String fullPath = getTempDirPath() + "/" + filePath;
1455
1456     final VirtualFile copy = LocalFileSystem.getInstance().refreshAndFindFileByPath(fullPath.replace(File.separatorChar, '/'));
1457     assertNotNull("file " + fullPath + " not found", copy);
1458     VfsTestUtil.assertFilePathEndsWithCaseSensitivePath(copy, filePath);
1459     return copy;
1460   }
1461
1462   @Nullable
1463   protected Editor createEditor(@NotNull VirtualFile file) {
1464     final Project project = getProject();
1465     final FileEditorManager instance = FileEditorManager.getInstance(project);
1466     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1467
1468     Editor editor = instance.openTextEditor(new OpenFileDescriptor(project, file), false);
1469     EditorTestUtil.waitForLoading(editor);
1470     if (editor != null) {
1471       DaemonCodeAnalyzer.getInstance(getProject()).restart();
1472     }
1473     return editor;
1474   }
1475
1476   private long collectAndCheckHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings) {
1477     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings, false);
1478   }
1479
1480   private long collectAndCheckHighlighting(boolean checkWarnings,
1481                                            boolean checkInfos,
1482                                            boolean checkWeakWarnings,
1483                                            boolean ignoreExtraHighlighting) {
1484     ExpectedHighlightingData data = new ExpectedHighlightingData(
1485       myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, ignoreExtraHighlighting, getHostFile(), myMessageBundles);
1486     data.init();
1487     return collectAndCheckHighlighting(data);
1488   }
1489
1490   private PsiFile getHostFile() {
1491     VirtualFile hostVFile = myFile instanceof VirtualFileWindow ? ((VirtualFileWindow)myFile).getDelegate() : myFile;
1492     return ReadAction.compute(() -> PsiManager.getInstance(getProject()).findFile(hostVFile));
1493   }
1494
1495   public long collectAndCheckHighlighting(@NotNull ExpectedHighlightingData data) {
1496     final Project project = getProject();
1497     EdtTestUtil.runInEdtAndWait(() -> PsiDocumentManager.getInstance(project).commitAllDocuments());
1498
1499     PsiFileImpl file = (PsiFileImpl)getHostFile();
1500     FileElement hardRefToFileElement = file.calcTreeElement();//to load text
1501
1502     // to load AST for changed files before it's prohibited by "fileTreeAccessFilter"
1503     ensureIndexesUpToDate(project);
1504
1505     final long start = System.currentTimeMillis();
1506     final VirtualFileFilter fileTreeAccessFilter = myVirtualFileFilter;
1507     Disposable disposable = Disposer.newDisposable();
1508     if (fileTreeAccessFilter != null) {
1509       PsiManagerEx.getInstanceEx(project).setAssertOnFileLoadingFilter(fileTreeAccessFilter, disposable);
1510     }
1511
1512     //    ProfilingUtil.startCPUProfiling();
1513     List<HighlightInfo> infos;
1514     try {
1515       infos = doHighlighting();
1516       removeDuplicatedRangesForInjected(infos);
1517     }
1518     finally {
1519       Disposer.dispose(disposable);
1520     }
1521     //    ProfilingUtil.captureCPUSnapshot("testing");
1522     final long elapsed = System.currentTimeMillis() - start;
1523
1524     data.checkResult(infos, file.getText());
1525     if (data.hasLineMarkers()) {
1526       Document document = getDocument(getFile());
1527       data.checkLineMarkers(DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject()), document.getText());
1528     }
1529     ObjectUtils.reachabilityFence(hardRefToFileElement);
1530     return elapsed;
1531   }
1532
1533   public void setVirtualFileFilter(@Nullable VirtualFileFilter filter) {
1534     myVirtualFileFilter = filter;
1535   }
1536
1537   public void setMessageBundles(@NotNull ResourceBundle... messageBundles) {
1538     myMessageBundles = messageBundles;
1539   }
1540
1541   @Override
1542   @NotNull
1543   public List<HighlightInfo> doHighlighting() {
1544     return myEditorTestFixture.doHighlighting(myAllowDirt);
1545   }
1546
1547   @NotNull
1548   @Override
1549   public List<HighlightInfo> doHighlighting(@NotNull final HighlightSeverity minimalSeverity) {
1550     return ContainerUtil.filter(doHighlighting(), info -> info.getSeverity().compareTo(minimalSeverity) >= 0);
1551   }
1552
1553   @NotNull
1554   @Override
1555   public String getTestDataPath() {
1556     return myTestDataPath;
1557   }
1558
1559   @Override
1560   public void setTestDataPath(@NotNull String dataPath) {
1561     myTestDataPath = dataPath;
1562   }
1563
1564   @Override
1565   public final Project getProject() {
1566     return myProjectFixture.getProject();
1567   }
1568
1569   @Override
1570   public Module getModule() {
1571     return myProjectFixture.getModule();
1572   }
1573
1574   @Override
1575   public Editor getEditor() {
1576     return myEditor;
1577   }
1578
1579   @Override
1580   public int getCaretOffset() {
1581     return myEditor.getCaretModel().getOffset();
1582   }
1583
1584   @Override
1585   @Nullable
1586   public PsiFile getFile() {
1587     return myFile != null ? ReadAction.compute(() -> PsiManager.getInstance(getProject()).findFile(myFile)) : null;
1588   }
1589
1590   @Override
1591   public void allowTreeAccessForFile(@NotNull final VirtualFile file) {
1592     assert myVirtualFileFilter instanceof FileTreeAccessFilter : "configured filter does not support this method";
1593     ((FileTreeAccessFilter)myVirtualFileFilter).allowTreeAccessForFile(file);
1594   }
1595
1596   @Override
1597   public void allowTreeAccessForAllFiles() {
1598     assert myVirtualFileFilter instanceof FileTreeAccessFilter : "configured filter does not support this method";
1599     ((FileTreeAccessFilter)myVirtualFileFilter).allowTreeAccessForAllFiles();
1600   }
1601
1602   private void checkResultByFile(@NotNull String expectedFile, @NotNull PsiFile originalFile, boolean stripTrailingSpaces) {
1603     IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
1604     if (policy != null) {
1605       policy.beforeCheckResult(getFile());
1606     }
1607     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1608     if (!stripTrailingSpaces) {
1609       EditorUtil.fillVirtualSpaceUntilCaret(getHostEditor());
1610     }
1611
1612     String fileText = originalFile.getText();
1613     String path = getTestDataPath() + "/" + expectedFile;
1614     String charset = Optional.ofNullable(originalFile.getVirtualFile()).map(f -> f.getCharset().name()).orElse(null);
1615     checkResult(expectedFile, stripTrailingSpaces, SelectionAndCaretMarkupLoader.fromFile(path, charset), fileText);
1616   }
1617
1618   private void checkResult(@NotNull String expectedFile,
1619                            boolean stripTrailingSpaces,
1620                            @NotNull SelectionAndCaretMarkupLoader loader,
1621                            @NotNull String actualText) {
1622     assertInitialized();
1623     Project project = getProject();
1624     Editor editor = getEditor();
1625     if (editor instanceof EditorWindow) {
1626       editor = ((EditorWindow)editor).getDelegate();
1627     }
1628
1629     UsefulTestCase.doPostponedFormatting(getProject());
1630     if (stripTrailingSpaces) {
1631       actualText = stripTrailingSpaces(actualText);
1632     }
1633
1634     PsiDocumentManager.getInstance(project).commitAllDocuments();
1635
1636     String expectedText = loader.newFileText;
1637     if (stripTrailingSpaces) {
1638       expectedText = stripTrailingSpaces(expectedText);
1639     }
1640
1641     actualText = StringUtil.convertLineSeparators(actualText);
1642
1643     if (!Comparing.equal(expectedText, actualText)) {
1644       if (loader.filePath != null) {
1645         if (loader.caretState.hasExplicitCaret()) {
1646           int offset = editor.getCaretModel().getOffset();
1647           if (offset > -1) {
1648             actualText = new StringBuilder(actualText).insert(offset, "<caret>").toString();
1649           }
1650           expectedText = loader.fileText;
1651           if (stripTrailingSpaces) {
1652             expectedText = stripTrailingSpaces(expectedText);
1653           }
1654         }
1655         throw new FileComparisonFailure(expectedFile, expectedText, actualText, loader.filePath);
1656       }
1657       else {
1658         throw new ComparisonFailure(expectedFile, expectedText, actualText);
1659       }
1660     }
1661
1662     EditorTestUtil.verifyCaretAndSelectionState(editor, loader.caretState, expectedFile);
1663   }
1664
1665   @NotNull
1666   private String stripTrailingSpaces(@NotNull String actualText) {
1667     final Document document = EditorFactory.getInstance().createDocument(actualText);
1668     ((DocumentImpl)document).stripTrailingSpaces(getProject());
1669     actualText = document.getText();
1670     return actualText;
1671   }
1672
1673   public void canChangeDocumentDuringHighlighting(boolean canI) {
1674     myAllowDirt = canI;
1675   }
1676
1677   @NotNull
1678   public String getFoldingDescription(boolean withCollapseStatus) {
1679     final Editor topEditor = getHostEditor();
1680     return EdtTestUtil.runInEdtAndGet(() -> {
1681       IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
1682       if (policy != null) {
1683         policy.waitForHighlighting(getProject(), topEditor);
1684       }
1685       CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(topEditor);
1686       return getFoldingData(topEditor, withCollapseStatus);
1687     });
1688   }
1689
1690   @NotNull
1691   public static String getFoldingData(Editor topEditor, boolean withCollapseStatus) {
1692     return getTagsFromSegments(topEditor.getDocument().getText(),
1693                                Arrays.asList(topEditor.getFoldingModel().getAllFoldRegions()),
1694                                FOLD,
1695                                foldRegion -> "text='" + foldRegion.getPlaceholderText() + "'"
1696                                              + (withCollapseStatus ? " expand='" + foldRegion.isExpanded() + "'" : ""));
1697   }
1698
1699   @NotNull
1700   public static <T extends Segment> String getTagsFromSegments(@NotNull String text,
1701                                                                @NotNull Collection<? extends T> segments,
1702                                                                @NotNull String tagName,
1703                                                                @Nullable Function<? super T, String> attrCalculator) {
1704     final List<Border> borders = new LinkedList<>();
1705     for (T region : segments) {
1706       String attr = attrCalculator == null ? null : attrCalculator.fun(region);
1707       borders.add(new CodeInsightTestFixtureImpl.Border(true, region.getStartOffset(), attr));
1708       borders.add(new CodeInsightTestFixtureImpl.Border(false, region.getEndOffset(), ""));
1709     }
1710     Collections.sort(borders);
1711
1712     StringBuilder result = new StringBuilder(text);
1713     for (CodeInsightTestFixtureImpl.Border border : borders) {
1714       StringBuilder info = new StringBuilder();
1715       info.append('<');
1716       if (border.isLeftBorder) {
1717         info.append(tagName);
1718         if (border.text != null) {
1719           info.append(' ').append(border.text);
1720         }
1721       }
1722       else {
1723         info.append('/').append(tagName);
1724       }
1725       info.append('>');
1726       result.insert(border.offset, info);
1727     }
1728     return result.toString();
1729   }
1730
1731   private void testFoldingRegions(@NotNull String verificationFileName,
1732                                   @Nullable String destinationFileName,
1733                                   boolean doCheckCollapseStatus) {
1734     String expectedContent;
1735     final File verificationFile;
1736     try {
1737       verificationFile = new File(verificationFileName);
1738       expectedContent = FileUtil.loadFile(verificationFile);
1739     }
1740     catch (IOException e) {
1741       throw new RuntimeException(e);
1742     }
1743     assertNotNull(expectedContent);
1744
1745     expectedContent = StringUtil.replace(expectedContent, "\r", "");
1746     final String cleanContent = removeFoldingMarkers(expectedContent);
1747     if (destinationFileName == null) {
1748       final String fileName = PathUtil.getFileName(verificationFileName);
1749       configureByText(fileName, cleanContent);
1750     }
1751     else {
1752       try {
1753         FileUtil.writeToFile(new File(destinationFileName), cleanContent);
1754         VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(destinationFileName);
1755         assertNotNull(file);
1756         configureFromExistingVirtualFile(file);
1757       }
1758       catch (IOException e) {
1759         throw new RuntimeException(e);
1760       }
1761     }
1762     final String actual = getFoldingDescription(doCheckCollapseStatus);
1763     if (!expectedContent.equals(actual)) {
1764       throw new FileComparisonFailure(verificationFile.getName(), expectedContent, actual, verificationFile.getPath());
1765     }
1766   }
1767
1768   @NotNull
1769   public static String removeFoldingMarkers(String expectedContent) {
1770     return expectedContent.replaceAll("<" + FOLD + "\\stext='[^']*'(\\sexpand='[^']*')*>", "")
1771       .replace("</" + FOLD + ">", "");
1772   }
1773
1774   @Override
1775   public void testFoldingWithCollapseStatus(@NotNull final String verificationFileName) {
1776     testFoldingRegions(verificationFileName, null, true);
1777   }
1778
1779   @Override
1780   public void testFoldingWithCollapseStatus(@NotNull final String verificationFileName, @Nullable String destinationFileName) {
1781     testFoldingRegions(verificationFileName, destinationFileName, true);
1782   }
1783
1784   @Override
1785   public void testFolding(@NotNull final String verificationFileName) {
1786     testFoldingRegions(verificationFileName, null, false);
1787   }
1788
1789   @Override
1790   public void testRainbow(@NotNull String fileName, @NotNull String text, boolean isRainbowOn, boolean withColor) {
1791     final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
1792     final boolean isRainbowOnInScheme = RainbowHighlighter.isRainbowEnabled(globalScheme, null);
1793     try {
1794       RainbowHighlighter.setRainbowEnabled(globalScheme, null, isRainbowOn);
1795       configureByText(fileName, text.replaceAll("<" + RAINBOW + "(\\scolor='[^']*')?>", "").replace("</" + RAINBOW + ">", ""));
1796
1797       List<HighlightInfo> highlighting = ContainerUtil.filter(doHighlighting(), info -> info.type == RainbowHighlighter.RAINBOW_ELEMENT);
1798       assertEquals(text, getTagsFromSegments(myEditor.getDocument().getText(), highlighting, RAINBOW, highlightInfo -> {
1799         if (!withColor) {
1800           return null;
1801         }
1802         TextAttributes attributes = highlightInfo.getTextAttributes(null, null);
1803         String color = attributes == null ? "null"
1804                                           : attributes.getForegroundColor() == null
1805                                             ? "null"
1806                                             : Integer.toHexString(attributes.getForegroundColor().getRGB());
1807         return "color='" + color + "'";
1808       }));
1809     }
1810     finally {
1811       RainbowHighlighter.setRainbowEnabled(globalScheme, null, isRainbowOnInScheme);
1812     }
1813   }
1814
1815   @Override
1816   public void testInlays() {
1817     InlayHintsChecker checker = new InlayHintsChecker(this);
1818     try {
1819       checker.setUp();
1820       checker.checkParameterHints();
1821     }
1822     finally {
1823       checker.tearDown();
1824     }
1825   }
1826
1827   @Override
1828   public void testInlays(java.util.function.Function<? super Inlay, String> inlayPresenter,
1829                          Predicate<? super Inlay> inlayFilter) {
1830     InlayHintsChecker checker = new InlayHintsChecker(this);
1831     try {
1832       checker.setUp();
1833       checker.checkInlays(inlayPresenter::apply, inlayFilter::test);
1834     }
1835     finally {
1836       checker.tearDown();
1837     }
1838   }
1839
1840   @Override
1841   public void checkResultWithInlays(String text) {
1842     Document checkDocument = new DocumentImpl(text);
1843     InlayHintsChecker checker = new InlayHintsChecker(this);
1844     CaretAndInlaysInfo inlaysAndCaretInfo = checker.extractInlaysAndCaretInfo(checkDocument);
1845     checkResult(checkDocument.getText());
1846     checker.verifyInlaysAndCaretInfo(inlaysAndCaretInfo, text);
1847   }
1848
1849   @Override
1850   public void assertPreferredCompletionItems(final int selected, @NotNull final String... expected) {
1851     myEditorTestFixture.assertPreferredCompletionItems(selected, expected);
1852   }
1853
1854   @Override
1855   public void testStructureView(@NotNull Consumer<? super StructureViewComponent> consumer) {
1856     assertNotNull("configure first", myFile);
1857
1858     final FileEditor fileEditor = FileEditorManager.getInstance(getProject()).getSelectedEditor(myFile);
1859     assertNotNull("editor not opened for " + myFile, myFile);
1860
1861     final StructureViewBuilder builder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(getFile());
1862     assertNotNull("no builder for " + myFile, builder);
1863
1864     StructureViewComponent component = null;
1865     try {
1866       component = (StructureViewComponent)builder.createStructureView(fileEditor, getProject());
1867       PlatformTestUtil.waitForPromise(component.rebuildAndUpdate());
1868       consumer.consume(component);
1869     }
1870     finally {
1871       if (component != null) Disposer.dispose(component);
1872     }
1873   }
1874
1875   @Override
1876   public void setCaresAboutInjection(boolean caresAboutInjection) {
1877     myCaresAboutInjection = caresAboutInjection;
1878   }
1879
1880   @Override
1881   public LookupImpl getLookup() {
1882     return myEditorTestFixture.getLookup();
1883   }
1884
1885   @NotNull
1886   @Override
1887   public List<Object> getGotoClassResults(@NotNull String pattern, boolean searchEverywhere, @Nullable PsiElement contextForSorting) {
1888     SearchEverywhereContributor<Object> contributor = createMockContributor(contextForSorting, searchEverywhere);
1889     final ArrayList<Object> results = new ArrayList<>();
1890     contributor.fetchElements(pattern, new MockProgressIndicator(), new CommonProcessors.CollectProcessor<>(results));
1891     return results;
1892   }
1893
1894   @NotNull
1895   @Override
1896   public List<Crumb> getBreadcrumbsAtCaret() {
1897     return myEditorTestFixture.getBreadcrumbsAtCaret();
1898   }
1899
1900   private SearchEverywhereContributor<Object> createMockContributor(@Nullable PsiElement contextForSorting, boolean everywhere) {
1901     final Project project = getProject();
1902     return new ClassSearchEverywhereContributor(project, contextForSorting) {{
1903       myScopeDescriptor = new ScopeDescriptor(FindSymbolParameters.searchScopeFor(myProject, everywhere));
1904     }};
1905   }
1906
1907   protected void bringRealEditorBack() {
1908     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1909     if (myEditor instanceof EditorWindow) {
1910       Document document = ((DocumentWindow)myEditor.getDocument()).getDelegate();
1911       setFileAndEditor(FileDocumentManager.getInstance().getFile(document), ((EditorWindow)myEditor).getDelegate());
1912     }
1913   }
1914
1915   public static boolean invokeIntention(@NotNull IntentionAction action, PsiFile file, Editor editor, String actionText) {
1916     // Test that action will automatically clear the read-only attribute if modification is necessary.
1917     // If your test fails due to this, make sure that your quick-fix/intention
1918     // overrides "getElementToMakeWritable" or has the following line:
1919     // if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
1920
1921     Project project = file.getProject();
1922     VirtualFile vFile = Objects.requireNonNull(InjectedLanguageManager.getInstance(project).getTopLevelFile(file)).getVirtualFile();
1923     AtomicBoolean result = new AtomicBoolean();
1924     withReadOnlyFile(vFile, project, () -> {
1925       try {
1926         ApplicationManager.getApplication().invokeLater(() -> {
1927           try {
1928             result.set(ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, action, actionText));
1929           }
1930           catch (StubTextInconsistencyException e) {
1931             PsiTestUtil.compareStubTexts(e);
1932           }
1933         });
1934         UIUtil.dispatchAllInvocationEvents();
1935         checkPsiTextConsistency(project, vFile);
1936       }
1937       catch (AssertionError e) {
1938         ExceptionUtil.rethrowUnchecked(ExceptionUtil.getRootCause(e));
1939         throw e;
1940       }
1941     });
1942     return result.get();
1943   }
1944
1945   /**
1946    * Make the given file read-only and execute the given action, afterwards make file writable again
1947    * @return whether the action has made the file writable itself
1948    */
1949   public static boolean withReadOnlyFile(VirtualFile vFile, Project project, Runnable action) {
1950     boolean writable;
1951     ReadonlyStatusHandlerImpl handler = (ReadonlyStatusHandlerImpl)ReadonlyStatusHandler.getInstance(project);
1952     setReadOnly(vFile, true);
1953     handler.setClearReadOnlyInTests(true);
1954     try {
1955       action.run();
1956     }
1957     finally {
1958       writable = vFile.isWritable();
1959       handler.setClearReadOnlyInTests(false);
1960       setReadOnly(vFile, false);
1961     }
1962     return writable;
1963   }
1964
1965   private static void checkPsiTextConsistency(Project project, VirtualFile vFile) {
1966     PsiFile topLevelPsi = vFile.isValid() ? PsiManager.getInstance(project).findFile(vFile) : null;
1967     if (topLevelPsi != null) {
1968       if (Registry.is("ide.check.structural.psi.text.consistency.in.tests")) {
1969         PsiTestUtil.checkPsiStructureWithCommit(topLevelPsi, PsiTestUtil::checkPsiMatchesTextIgnoringNonCode);
1970       } else {
1971         PsiTestUtil.checkStubsMatchText(topLevelPsi);
1972       }
1973     }
1974   }
1975
1976   private static void setReadOnly(VirtualFile vFile, boolean readOnlyStatus) {
1977     try {
1978       WriteAction.runAndWait(() -> ReadOnlyAttributeUtil.setReadOnlyAttribute(vFile, readOnlyStatus));
1979     }
1980     catch (IOException e) {
1981       throw new UncheckedIOException(e);
1982     }
1983   }
1984
1985   @NotNull
1986   private String getUsageViewTreeTextRepresentation(@NotNull final UsageViewImpl usageView) {
1987     Disposer.register(getTestRootDisposable(), usageView);
1988     usageView.expandAll();
1989     return TreeNodeTester.forNode(usageView.getRoot()).withPresenter(usageView::getNodeText).constructTextRepresentation();
1990   }
1991
1992   private static class SelectionAndCaretMarkupLoader {
1993     private final String fileText;
1994     private final String filePath;
1995     private final String newFileText;
1996     private final EditorTestUtil.CaretAndSelectionState caretState;
1997
1998     private SelectionAndCaretMarkupLoader(@NotNull String fileText, String filePath) {
1999       this.fileText = fileText;
2000       this.filePath = filePath;
2001       final Document document = EditorFactory.getInstance().createDocument(fileText);
2002       caretState = EditorTestUtil.extractCaretAndSelectionMarkers(document);
2003       newFileText = document.getText();
2004     }
2005
2006     @NotNull
2007     private static SelectionAndCaretMarkupLoader fromFile(@NotNull String path, String charset) {
2008       return fromIoSource(() -> FileUtil.loadFile(new File(path), charset), path);
2009     }
2010
2011     @NotNull
2012     private static SelectionAndCaretMarkupLoader fromFile(@NotNull VirtualFile file) {
2013       return fromIoSource(() -> VfsUtilCore.loadText(file), file.getPath());
2014     }
2015
2016     @NotNull
2017     private static SelectionAndCaretMarkupLoader fromIoSource(@NotNull ThrowableComputable<String, IOException> source, String path) {
2018       try {
2019         return new SelectionAndCaretMarkupLoader(StringUtil.convertLineSeparators(source.compute()), path);
2020       }
2021       catch (IOException e) {
2022         throw new RuntimeException(e);
2023       }
2024     }
2025
2026     @NotNull
2027     private static SelectionAndCaretMarkupLoader fromText(@NotNull String text) {
2028       return new SelectionAndCaretMarkupLoader(text, null);
2029     }
2030   }
2031
2032   private static class Border implements Comparable<Border> {
2033     private final boolean isLeftBorder;
2034     private final int offset;
2035     private final String text;
2036
2037     private Border(boolean isLeftBorder, int offset, String text) {
2038       this.isLeftBorder = isLeftBorder;
2039       this.offset = offset;
2040       this.text = text;
2041     }
2042
2043     @Override
2044     public int compareTo(@NotNull Border o) {
2045       return offset < o.offset ? 1 : -1;
2046     }
2047   }
2048
2049   @Override
2050   @NotNull
2051   public Disposable getProjectDisposable() {
2052     return myProjectFixture.getTestRootDisposable();
2053   }
2054 }