eb705f1b24150b9786d159213b6bba6fb81ea796
[idea/community.git] / platform / testFramework / src / com / intellij / testFramework / fixtures / impl / CodeInsightTestFixtureImpl.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.testFramework.fixtures.impl;
17
18 import com.intellij.analysis.AnalysisScope;
19 import com.intellij.codeInsight.TargetElementUtil;
20 import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
21 import com.intellij.codeInsight.completion.CompletionProgressIndicator;
22 import com.intellij.codeInsight.completion.CompletionType;
23 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
24 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
25 import com.intellij.codeInsight.daemon.GutterMark;
26 import com.intellij.codeInsight.daemon.impl.*;
27 import com.intellij.codeInsight.folding.CodeFoldingManager;
28 import com.intellij.codeInsight.highlighting.actions.HighlightUsagesAction;
29 import com.intellij.codeInsight.intention.IntentionAction;
30 import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
31 import com.intellij.codeInsight.lookup.Lookup;
32 import com.intellij.codeInsight.lookup.LookupElement;
33 import com.intellij.codeInsight.lookup.LookupManager;
34 import com.intellij.codeInsight.lookup.impl.LookupImpl;
35 import com.intellij.codeInspection.InspectionManager;
36 import com.intellij.codeInspection.InspectionProfileEntry;
37 import com.intellij.codeInspection.InspectionToolProvider;
38 import com.intellij.codeInspection.LocalInspectionTool;
39 import com.intellij.codeInspection.ex.*;
40 import com.intellij.facet.Facet;
41 import com.intellij.facet.FacetManager;
42 import com.intellij.find.FindManager;
43 import com.intellij.find.findUsages.FindUsagesHandler;
44 import com.intellij.find.findUsages.FindUsagesOptions;
45 import com.intellij.find.impl.FindManagerImpl;
46 import com.intellij.ide.startup.StartupManagerEx;
47 import com.intellij.ide.startup.impl.StartupManagerImpl;
48 import com.intellij.ide.structureView.StructureViewBuilder;
49 import com.intellij.ide.structureView.newStructureView.StructureViewComponent;
50 import com.intellij.injected.editor.DocumentWindow;
51 import com.intellij.injected.editor.EditorWindow;
52 import com.intellij.internal.DumpLookupElementWeights;
53 import com.intellij.lang.LanguageStructureViewBuilder;
54 import com.intellij.lang.annotation.HighlightSeverity;
55 import com.intellij.openapi.Disposable;
56 import com.intellij.openapi.actionSystem.*;
57 import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
58 import com.intellij.openapi.application.AccessToken;
59 import com.intellij.openapi.application.ApplicationManager;
60 import com.intellij.openapi.application.Result;
61 import com.intellij.openapi.application.WriteAction;
62 import com.intellij.openapi.command.CommandProcessor;
63 import com.intellij.openapi.command.WriteCommandAction;
64 import com.intellij.openapi.editor.*;
65 import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
66 import com.intellij.openapi.editor.actionSystem.EditorActionManager;
67 import com.intellij.openapi.editor.ex.EditorEx;
68 import com.intellij.openapi.editor.ex.util.EditorUtil;
69 import com.intellij.openapi.editor.impl.DocumentImpl;
70 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
71 import com.intellij.openapi.editor.markup.RangeHighlighter;
72 import com.intellij.openapi.extensions.ExtensionPoint;
73 import com.intellij.openapi.extensions.ExtensionPointName;
74 import com.intellij.openapi.extensions.ExtensionsArea;
75 import com.intellij.openapi.fileEditor.*;
76 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
77 import com.intellij.openapi.fileTypes.FileType;
78 import com.intellij.openapi.fileTypes.FileTypeManager;
79 import com.intellij.openapi.module.Module;
80 import com.intellij.openapi.progress.ProcessCanceledException;
81 import com.intellij.openapi.project.DumbService;
82 import com.intellij.openapi.project.Project;
83 import com.intellij.openapi.util.*;
84 import com.intellij.openapi.util.io.FileUtil;
85 import com.intellij.openapi.util.text.StringUtil;
86 import com.intellij.openapi.vfs.*;
87 import com.intellij.profile.Profile;
88 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
89 import com.intellij.profile.codeInspection.ProjectInspectionProfileManagerImpl;
90 import com.intellij.psi.*;
91 import com.intellij.psi.impl.DebugUtil;
92 import com.intellij.psi.impl.PsiManagerEx;
93 import com.intellij.psi.impl.PsiManagerImpl;
94 import com.intellij.psi.impl.PsiModificationTrackerImpl;
95 import com.intellij.psi.impl.cache.CacheManager;
96 import com.intellij.psi.impl.cache.impl.todo.TodoIndex;
97 import com.intellij.psi.impl.source.PsiFileImpl;
98 import com.intellij.psi.impl.source.tree.FileElement;
99 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
100 import com.intellij.psi.search.GlobalSearchScope;
101 import com.intellij.psi.search.SearchScope;
102 import com.intellij.psi.search.UsageSearchContext;
103 import com.intellij.psi.stubs.StubUpdatingIndex;
104 import com.intellij.psi.util.PsiTreeUtil;
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.usageView.UsageInfo;
111 import com.intellij.util.*;
112 import com.intellij.util.containers.ContainerUtil;
113 import com.intellij.util.indexing.FileBasedIndex;
114 import com.intellij.util.ui.UIUtil;
115 import junit.framework.ComparisonFailure;
116 import org.jetbrains.annotations.NotNull;
117 import org.jetbrains.annotations.Nullable;
118 import org.jetbrains.annotations.TestOnly;
119 import org.junit.Assert;
120
121 import javax.swing.*;
122 import java.io.File;
123 import java.io.IOException;
124 import java.util.*;
125
126 /**
127  * @author Dmitry Avdeev
128  */
129 @SuppressWarnings({"TestMethodWithIncorrectSignature", "JUnitTestCaseWithNoTests", "JUnitTestClassNamingConvention", "TestOnlyProblems"})
130 public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsightTestFixture {
131   private static final Function<IntentionAction, String> INTENTION_NAME_FUN = intentionAction -> "\"" + intentionAction.getText() + "\"";
132
133   private static final String START_FOLD = "<fold\\stext=\'[^\']*\'(\\sexpand=\'[^\']*\')*>";
134   private static final String END_FOLD = "</fold>";
135
136   private final IdeaProjectTestFixture myProjectFixture;
137   private final TempDirTestFixture myTempDirFixture;
138   private PsiManagerImpl myPsiManager;
139   private VirtualFile myFile;
140   private Editor myEditor;
141   private String myTestDataPath;
142   private boolean myEmptyLookup;
143   private VirtualFileFilter myVirtualFileFilter = new FileTreeAccessFilter();
144   private boolean myAllowDirt;
145   private boolean myCaresAboutInjection = true;
146
147   @SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors")
148   public CodeInsightTestFixtureImpl(@NotNull IdeaProjectTestFixture projectFixture, @NotNull TempDirTestFixture tempDirTestFixture) {
149     myProjectFixture = projectFixture;
150     myTempDirFixture = tempDirTestFixture;
151   }
152
153   @NotNull
154   @TestOnly
155   public static GlobalInspectionContextForTests createGlobalContextForTool(@NotNull AnalysisScope scope,
156                                                                            @NotNull final Project project,
157                                                                            @NotNull InspectionManagerEx inspectionManager,
158                                                                            @NotNull final InspectionToolWrapper... toolWrappers) {
159     final InspectionProfileImpl profile = InspectionProfileImpl.createSimple("test", project, toolWrappers);
160     GlobalInspectionContextForTests context = new GlobalInspectionContextForTests(project, inspectionManager.getContentManager()) {
161       @NotNull
162       @Override
163       protected List<Tools> getUsedTools() {
164         return InspectionProfileImpl.initAndDo(() -> {
165           for (InspectionToolWrapper tool : toolWrappers) {
166             profile.enableTool(tool.getShortName(), project);
167           }
168           return profile.getAllEnabledInspectionTools(project);
169         });
170       }
171     };
172     context.setCurrentScope(scope);
173
174     return context;
175   }
176
177   private static void addGutterIconRenderer(final GutterMark renderer,
178                                             final int offset,
179                                             @NotNull SortedMap<Integer, List<GutterMark>> result) {
180     if (renderer == null) return;
181
182     List<GutterMark> renderers = result.get(offset);
183     if (renderers == null) {
184       result.put(offset, renderers = new SmartList<GutterMark>());
185     }
186     renderers.add(renderer);
187   }
188
189   public static void configureInspections(@NotNull InspectionProfileEntry[] tools,
190                                           @NotNull final Project project,
191                                           @NotNull final Collection<String> disabledInspections,
192                                           @NotNull Disposable parentDisposable) {
193     InspectionToolWrapper[] wrapped =
194       ContainerUtil.map2Array(tools, InspectionToolWrapper.class, new Function<InspectionProfileEntry, InspectionToolWrapper>() {
195         @Override
196         public InspectionToolWrapper fun(InspectionProfileEntry tool) {
197           return InspectionToolRegistrar.wrapTool(tool);
198         }
199       });
200
201     final InspectionProfileImpl profile = InspectionProfileImpl.createSimple(LightPlatformTestCase.PROFILE, project, wrapped);
202     profile.disableToolByDefault(new ArrayList<String>(disabledInspections), project);
203
204     final ApplicationInspectionProfileManagerImpl inspectionProfileManager = ApplicationInspectionProfileManagerImpl.getInstanceImpl();
205     final Profile oldRootProfile = inspectionProfileManager.getCurrentProfile();
206     inspectionProfileManager.addProfile(profile);
207     Disposer.register(parentDisposable, new Disposable() {
208       @Override
209       public void dispose() {
210         inspectionProfileManager.deleteProfile(profile.getName());
211         inspectionProfileManager.setRootProfile(oldRootProfile.getName());
212         clearAllToolsIn(InspectionProfileImpl.getDefaultProfile(), project);
213       }
214     });
215     inspectionProfileManager.setRootProfile(profile.getName());
216     InspectionProfileImpl.initAndDo((Computable)() -> {
217       InspectionProjectProfileManager.getInstance(project).updateProfile(profile);
218       ProjectInspectionProfileManagerImpl.getInstanceImpl(project).setRootProfile(profile.getName());
219       return null;
220     });
221   }
222
223   private static void clearAllToolsIn(@NotNull InspectionProfileImpl profile, @NotNull Project project) {
224     List<ScopeToolState> tools = profile.getAllTools(project);
225     for (ScopeToolState state : tools) {
226       InspectionToolWrapper wrapper = state.getTool();
227       if (wrapper.getExtension() != null) {
228         ReflectionUtil.resetField(wrapper, InspectionProfileEntry.class, "myTool"); // make it not initialized
229       }
230     }
231   }
232
233   private static void removeDuplicatedRangesForInjected(@NotNull List<HighlightInfo> infos) {
234     Collections.sort(infos, (o1, o2) -> {
235       final int i = o2.startOffset - o1.startOffset;
236       return i != 0 ? i : o1.getSeverity().myVal - o2.getSeverity().myVal;
237     });
238     HighlightInfo prevInfo = null;
239     for (Iterator<HighlightInfo> it = infos.iterator(); it.hasNext();) {
240       final HighlightInfo info = it.next();
241       if (prevInfo != null &&
242           info.getSeverity() == HighlightInfoType.SYMBOL_TYPE_SEVERITY &&
243           info.getDescription() == null &&
244           info.startOffset == prevInfo.startOffset &&
245           info.endOffset == prevInfo.endOffset) {
246         it.remove();
247       }
248       prevInfo = info.type == HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT ? info : null;
249     }
250   }
251
252   @NotNull
253   public static List<HighlightInfo> instantiateAndRun(@NotNull PsiFile file,
254                                                       @NotNull Editor editor,
255                                                       @NotNull int[] toIgnore,
256                                                       boolean canChangeDocument) {
257     Project project = file.getProject();
258     ensureIndexesUpToDate(project);
259     DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
260     TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
261     ProcessCanceledException exception = null;
262     for (int i = 0; i < 1000; i++) {
263       try {
264         List<HighlightInfo> infos = codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, toIgnore, canChangeDocument, null);
265         infos.addAll(DaemonCodeAnalyzerEx.getInstanceEx(project).getFileLevelHighlights(project, file));
266         return infos;
267       }
268       catch (ProcessCanceledException e) {
269         PsiDocumentManager.getInstance(project).commitAllDocuments();
270         UIUtil.dispatchAllInvocationEvents();
271         exception = e;
272       }
273     }
274     // unable to highlight after 100 retries
275     throw exception;
276   }
277
278   public static void ensureIndexesUpToDate(@NotNull Project project) {
279     if (!DumbService.isDumb(project)) {
280       FileBasedIndex.getInstance().ensureUpToDate(StubUpdatingIndex.INDEX_ID, project, null);
281       FileBasedIndex.getInstance().ensureUpToDate(TodoIndex.NAME, project, null);
282     }
283   }
284
285   @NotNull
286   public static List<IntentionAction> getAvailableIntentions(@NotNull final Editor editor, @NotNull final PsiFile file) {
287     return ApplicationManager.getApplication().runReadAction(new Computable<List<IntentionAction>>() {
288       @Override
289       public List<IntentionAction> compute() {
290         return doGetAvailableIntentions(editor, file);
291       }
292     });
293   }
294
295   @NotNull
296   private static List<IntentionAction> doGetAvailableIntentions(@NotNull Editor editor, @NotNull PsiFile file) {
297     ShowIntentionsPass.IntentionsInfo intentions = new ShowIntentionsPass.IntentionsInfo();
298     ShowIntentionsPass.getActionsToShow(editor, file, intentions, -1);
299     List<HighlightInfo.IntentionActionDescriptor> descriptors = new ArrayList<HighlightInfo.IntentionActionDescriptor>();
300     descriptors.addAll(intentions.intentionsToShow);
301     descriptors.addAll(intentions.errorFixesToShow);
302     descriptors.addAll(intentions.inspectionFixesToShow);
303     descriptors.addAll(intentions.guttersToShow);
304
305     final int fileOffset = editor.getCaretModel().getOffset();
306     PsiElement hostElement = file.getViewProvider().findElementAt(fileOffset, file.getLanguage());
307     PsiElement injectedElement = InjectedLanguageUtil.findElementAtNoCommit(file, fileOffset);
308
309     PsiFile injectedFile = injectedElement != null ? injectedElement.getContainingFile() : null;
310     Editor injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(editor, injectedFile);
311
312     List<IntentionAction> result = new ArrayList<IntentionAction>();
313
314     List<HighlightInfo> infos = DaemonCodeAnalyzerEx.getInstanceEx(file.getProject()).getFileLevelHighlights(file.getProject(), file);
315     for (HighlightInfo info : infos) {
316       for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) {
317         HighlightInfo.IntentionActionDescriptor actionInGroup = pair.first;
318         final IntentionAction action = actionInGroup.getAction();
319
320         if (ShowIntentionActionsHandler.availableFor(file, editor, action)
321             ||
322             injectedElement != null && hostElement != injectedElement && ShowIntentionActionsHandler.availableFor(injectedFile, injectedEditor, action)) {
323           descriptors.add(actionInGroup);
324         }
325       }
326     }
327
328     // add all intention options for simplicity
329     for (HighlightInfo.IntentionActionDescriptor descriptor : descriptors) {
330       result.add(descriptor.getAction());
331
332       if (injectedElement != null && injectedElement != hostElement) {
333         List<IntentionAction> options = descriptor.getOptions(injectedElement, injectedEditor);
334         if (options != null) {
335           for (IntentionAction option : options) {
336             if (ShowIntentionActionsHandler.availableFor(injectedFile, injectedEditor, option)) {
337               result.add(option);
338             }
339           }
340         }
341       }
342
343       if (hostElement != null) {
344         List<IntentionAction> options = descriptor.getOptions(hostElement, editor);
345         if (options != null) {
346           for (IntentionAction option : options) {
347             if (ShowIntentionActionsHandler.availableFor(file, editor, option)) {
348               result.add(option);
349             }
350           }
351         }
352       }
353     }
354     return result;
355   }
356
357   @NotNull
358   @Override
359   public String getTempDirPath() {
360     return myTempDirFixture.getTempDirPath();
361   }
362
363   @NotNull
364   @Override
365   public TempDirTestFixture getTempDirFixture() {
366     return myTempDirFixture;
367   }
368
369   @NotNull
370   @Override
371   public VirtualFile copyFileToProject(@NotNull String sourcePath) {
372     return copyFileToProject(sourcePath, sourcePath);
373   }
374
375   @NotNull
376   @Override
377   public VirtualFile copyFileToProject(@NotNull String sourcePath, @NotNull String targetPath) {
378     String testDataPath = getTestDataPath();
379     File sourceFile = FileUtil.findFirstThatExist(testDataPath + '/' + sourcePath, sourcePath);
380     VirtualFile targetFile = myTempDirFixture.getFile(targetPath);
381
382     if (sourceFile == null && targetFile != null && targetPath.equals(sourcePath)) {
383       return targetFile;
384     }
385
386     Assert.assertNotNull("Cannot find source file: " + sourcePath + "; test data path: " + testDataPath, sourceFile);
387     Assert.assertTrue("Not a file: " + sourceFile, sourceFile.isFile());
388
389     if (targetFile == null) {
390       targetFile = myTempDirFixture.createFile(targetPath);
391       VfsTestUtil.assertFilePathEndsWithCaseSensitivePath(targetFile, sourcePath);
392       targetFile.putUserData(VfsTestUtil.TEST_DATA_FILE_PATH, sourceFile.getAbsolutePath());
393     }
394
395     final File _source = sourceFile;
396     final VirtualFile _target = targetFile;
397     new WriteAction() {
398       @Override
399       protected void run(@NotNull Result result) throws IOException {
400         _target.setBinaryContent(FileUtil.loadFileBytes(_source));
401       }
402     }.execute();
403
404     return targetFile;
405   }
406
407   @NotNull
408   @Override
409   public VirtualFile copyDirectoryToProject(@NotNull String sourcePath, @NotNull String targetPath) {
410     final String testDataPath = getTestDataPath();
411
412     final File fromFile = new File(testDataPath + "/" + sourcePath);
413     if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
414       return myTempDirFixture.copyAll(fromFile.getPath(), targetPath);
415     }
416     final File targetFile = new File(getTempDirPath() + "/" + targetPath);
417     try {
418       FileUtil.copyDir(fromFile, targetFile);
419     }
420     catch (IOException e) {
421       throw new RuntimeException(e);
422     }
423
424     final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(targetFile);
425     Assert.assertNotNull(file);
426     file.refresh(false, true);
427     return file;
428   }
429
430   @Override
431   public void enableInspections(@NotNull InspectionProfileEntry... inspections) {
432     assertInitialized();
433     for (InspectionProfileEntry inspection : inspections) {
434       LightPlatformTestCase.enableInspectionTool(getProject(), InspectionToolRegistrar.wrapTool(inspection));
435     }
436   }
437
438   @Override
439   public void enableInspections(@NotNull final Class<? extends LocalInspectionTool>... inspections) {
440     enableInspections(Arrays.asList(inspections));
441   }
442
443   @Override
444   public void enableInspections(@NotNull final Collection<Class<? extends LocalInspectionTool>> inspections) {
445     List<LocalInspectionTool> tools = new ArrayList<LocalInspectionTool>();
446     for (Class<? extends LocalInspectionTool> clazz : inspections) {
447       try {
448         LocalInspectionTool inspection = clazz.getConstructor().newInstance();
449         tools.add(inspection);
450       }
451       catch (Exception e) {
452         throw new RuntimeException("Cannot instantiate " + clazz);
453       }
454     }
455     enableInspections(tools.toArray(new LocalInspectionTool[tools.size()]));
456   }
457
458   @Override
459   public void disableInspections(@NotNull InspectionProfileEntry... inspections) {
460     InspectionProfileImpl profile = (InspectionProfileImpl)InspectionProjectProfileManager.getInstance(getProject()).getCurrentProfile();
461     for (InspectionProfileEntry inspection : inspections) {
462       profile.disableTool(inspection.getShortName(), getProject());
463     }
464   }
465
466   @Override
467   public void enableInspections(@NotNull InspectionToolProvider... providers) {
468     List<LocalInspectionTool> tools = new ArrayList<LocalInspectionTool>();
469     for (InspectionToolProvider provider : providers) {
470       for (Class<?> clazz : provider.getInspectionClasses()) {
471         try {
472           Object o = clazz.getConstructor().newInstance();
473           if (o instanceof LocalInspectionTool) {
474             LocalInspectionTool inspection = (LocalInspectionTool)o;
475             tools.add(inspection);
476           }
477         }
478         catch (Exception e) {
479           throw new RuntimeException("Cannot instantiate " + clazz, e);
480         }
481       }
482     }
483     enableInspections(tools.toArray(new LocalInspectionTool[tools.size()]));
484   }
485
486   @Override
487   public long testHighlighting(final boolean checkWarnings,
488                                final boolean checkInfos,
489                                final boolean checkWeakWarnings,
490                                @NotNull final String... filePaths) {
491     if (filePaths.length > 0) {
492       configureByFilesInner(filePaths);
493     }
494     try {
495       return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings);
496     }
497     catch (Exception e) {
498       throw new RuntimeException(e);
499     }
500   }
501
502   @Override
503   public long testHighlightingAllFiles(final boolean checkWarnings,
504                                        final boolean checkInfos,
505                                        final boolean checkWeakWarnings,
506                                        @NotNull final String... filePaths) {
507     final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
508     for (String path : filePaths) {
509       files.add(copyFileToProject(path));
510     }
511     return testHighlightingAllFiles(checkWarnings, checkInfos, checkWeakWarnings, VfsUtilCore.toVirtualFileArray(files));
512   }
513
514   @Override
515   public long testHighlightingAllFiles(final boolean checkWarnings,
516                                        final boolean checkInfos,
517                                        final boolean checkWeakWarnings,
518                                        @NotNull final VirtualFile... files) {
519     return collectAndCheckHighlightings(checkWarnings, checkInfos, checkWeakWarnings, files);
520   }
521
522   private long collectAndCheckHighlightings(final boolean checkWarnings,
523                                             final boolean checkInfos,
524                                             final boolean checkWeakWarnings,
525                                             @NotNull VirtualFile[] files) {
526     final List<Trinity<PsiFile, Editor, ExpectedHighlightingData>> datas =
527       ContainerUtil.map2List(files, file -> {
528         final PsiFile psiFile = myPsiManager.findFile(file);
529         Assert.assertNotNull(psiFile);
530         final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(psiFile);
531         Assert.assertNotNull(document);
532         ExpectedHighlightingData data = new ExpectedHighlightingData(document, checkWarnings, checkWeakWarnings, checkInfos, psiFile);
533         data.init();
534         return Trinity.create(psiFile, createEditor(file), data);
535       });
536     long elapsed = 0;
537     for (Trinity<PsiFile, Editor, ExpectedHighlightingData> trinity : datas) {
538       myEditor = trinity.second;
539       myFile = trinity.first.getVirtualFile();
540       elapsed += collectAndCheckHighlighting(trinity.third);
541     }
542     return elapsed;
543   }
544
545   @Override
546   public long checkHighlighting(final boolean checkWarnings, final boolean checkInfos, final boolean checkWeakWarnings) {
547     return checkHighlighting(checkWarnings, checkInfos, checkWeakWarnings, false);
548   }
549
550   @Override
551   public long checkHighlighting(final boolean checkWarnings, final boolean checkInfos, final boolean checkWeakWarnings, boolean ignoreExtraHighlighting) {
552     try {
553       return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings, ignoreExtraHighlighting);
554     }
555     catch (Exception e) {
556       throw new RuntimeException(e);
557     }
558   }
559
560   @Override
561   public long checkHighlighting() {
562     return checkHighlighting(true, false, true);
563   }
564
565   @Override
566   public long testHighlighting(@NotNull final String... filePaths) {
567     return testHighlighting(true, false, true, filePaths);
568   }
569
570   @Override
571   public long testHighlighting(final boolean checkWarnings,
572                                final boolean checkInfos,
573                                final boolean checkWeakWarnings,
574                                @NotNull final VirtualFile file) {
575     openFileInEditor(file);
576     try {
577       return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings);
578     }
579     catch (Exception e) {
580       throw new RuntimeException(e);
581     }
582   }
583
584   @NotNull
585   @Override
586   public HighlightTestInfo testFile(@NotNull String... filePath) {
587     return new HighlightTestInfo(getTestRootDisposable(), filePath) {
588       @Override
589       public HighlightTestInfo doTest() {
590         configureByFiles(filePaths);
591         ExpectedHighlightingData data = new ExpectedHighlightingData(myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, getFile());
592         if (checkSymbolNames) data.checkSymbolNames();
593         data.init();
594         collectAndCheckHighlighting(data);
595         return this;
596       }
597     };
598   }
599
600   @Override
601   public void openFileInEditor(@NotNull final VirtualFile file) {
602     myFile = file;
603     myEditor = createEditor(file);
604   }
605
606   @Override
607   public void testInspection(@NotNull String testDir, @NotNull InspectionToolWrapper toolWrapper) {
608     VirtualFile sourceDir = copyDirectoryToProject(new File(testDir, "src").getPath(), "src");
609     PsiDirectory psiDirectory = getPsiManager().findDirectory(sourceDir);
610     Assert.assertNotNull(psiDirectory);
611
612     AnalysisScope scope = new AnalysisScope(psiDirectory);
613     scope.invalidate();
614
615     InspectionManagerEx inspectionManager = (InspectionManagerEx)InspectionManager.getInstance(getProject());
616     GlobalInspectionContextForTests globalContext = createGlobalContextForTool(scope, getProject(), inspectionManager, toolWrapper);
617
618     InspectionTestUtil.runTool(toolWrapper, scope, globalContext);
619     InspectionTestUtil.compareToolResults(globalContext, toolWrapper, false, new File(getTestDataPath(), testDir).getPath());
620   }
621
622   @Override
623   @Nullable
624   public PsiReference getReferenceAtCaretPosition(@NotNull final String... filePaths) {
625     if (filePaths.length > 0) {
626       configureByFilesInner(filePaths);
627     }
628     return getFile().findReferenceAt(myEditor.getCaretModel().getOffset());
629   }
630
631   @Override
632   @NotNull
633   public PsiReference getReferenceAtCaretPositionWithAssertion(@NotNull final String... filePaths) {
634     final PsiReference reference = getReferenceAtCaretPosition(filePaths);
635     Assert.assertNotNull("no reference found at " + myEditor.getCaretModel().getLogicalPosition(), reference);
636     return reference;
637   }
638
639   @Override
640   @NotNull
641   public List<IntentionAction> getAvailableIntentions(@NotNull final String... filePaths) {
642     if (filePaths.length > 0) {
643       configureByFilesInner(filePaths);
644     }
645     return getAvailableIntentions();
646   }
647
648   @Override
649   @NotNull
650   public List<IntentionAction> getAllQuickFixes(@NotNull final String... filePaths) {
651     if (filePaths.length != 0) {
652       configureByFilesInner(filePaths);
653     }
654     List<HighlightInfo> infos = doHighlighting();
655     ArrayList<IntentionAction> actions = new ArrayList<IntentionAction>();
656     for (HighlightInfo info : infos) {
657       if (info.quickFixActionRanges != null) {
658         for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) {
659           actions.add(pair.getFirst().getAction());
660         }
661       }
662     }
663     return actions;
664   }
665
666   @Override
667   @NotNull
668   public List<IntentionAction> getAvailableIntentions() {
669     doHighlighting();
670     PsiFile file = getFile();
671     Editor editor = getEditor();
672     if (editor instanceof EditorWindow) {
673       editor = ((EditorWindow)editor).getDelegate();
674       file = InjectedLanguageUtil.getTopLevelFile(file);
675     }
676     Assert.assertNotNull(file);
677     return getAvailableIntentions(editor, file);
678   }
679
680   @NotNull
681   @Override
682   public List<IntentionAction> filterAvailableIntentions(@NotNull final String hint) {
683     final List<IntentionAction> availableIntentions = getAvailableIntentions();
684     return ContainerUtil.findAll(availableIntentions, intentionAction -> intentionAction.getText().startsWith(hint));
685   }
686
687   @Override
688   public IntentionAction findSingleIntention(@NotNull final String hint) {
689     final List<IntentionAction> list = filterAvailableIntentions(hint);
690     if (list.isEmpty()) {
691       Assert.fail("\"" + hint + "\" not in [" + StringUtil.join(getAvailableIntentions(), INTENTION_NAME_FUN, ", ") + "]");
692     }
693     else if (list.size() > 1) {
694       Assert.fail("Too many intentions found for \"" + hint + "\": [" + StringUtil.join(list, INTENTION_NAME_FUN, ", ") + "]");
695     }
696     return UsefulTestCase.assertOneElement(list);
697   }
698
699   @Override
700   public IntentionAction getAvailableIntention(@NotNull final String intentionName, @NotNull final String... filePaths) {
701     List<IntentionAction> intentions = getAvailableIntentions(filePaths);
702     IntentionAction action = CodeInsightTestUtil.findIntentionByText(intentions, intentionName);
703     if (action == null) {
704       System.out.println(intentionName + " not found among " + StringUtil.join(intentions, action1 -> action1.getText(), ","));
705     }
706     return action;
707   }
708
709   @Override
710   public void launchAction(@NotNull final IntentionAction action) {
711     ShowIntentionActionsHandler.chooseActionAndInvoke(getFile(), getEditor(), action, action.getText());
712     UIUtil.dispatchAllInvocationEvents();
713   }
714
715   @Override
716   public void testCompletion(@NotNull String[] filesBefore, @NotNull @TestDataFile String fileAfter) {
717     testCompletionTyping(filesBefore, "", fileAfter);
718   }
719
720   @Override
721   public void testCompletionTyping(@NotNull final String[] filesBefore, @NotNull String toType, @NotNull final String fileAfter) {
722     assertInitialized();
723     configureByFiles(filesBefore);
724     complete(CompletionType.BASIC);
725     type(toType);
726     try {
727       checkResultByFile(fileAfter);
728     }
729     catch (RuntimeException e) {
730       System.out.println("LookupElementStrings = " + getLookupElementStrings());
731       throw e;
732     }
733   }
734
735   protected void assertInitialized() {
736     Assert.assertNotNull("setUp() hasn't been called", myPsiManager);
737   }
738
739   @Override
740   public void testCompletion(@NotNull String fileBefore, @NotNull String fileAfter, @NotNull final String... additionalFiles) {
741     testCompletionTyping(fileBefore, "", fileAfter, additionalFiles);
742   }
743
744   @Override
745   public void testCompletionTyping(@NotNull @TestDataFile String fileBefore,
746                                    @NotNull String toType,
747                                    @NotNull @TestDataFile String fileAfter,
748                                    @NotNull String... additionalFiles) {
749     testCompletionTyping(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, fileBefore)), toType, fileAfter);
750   }
751
752   @Override
753   public void testCompletionVariants(@NotNull final String fileBefore, @NotNull final String... expectedItems) {
754     assertInitialized();
755     final List<String> result = getCompletionVariants(fileBefore);
756     Assert.assertNotNull(result);
757     UsefulTestCase.assertSameElements(result, expectedItems);
758   }
759
760   @Override
761   public List<String> getCompletionVariants(@NotNull final String... filesBefore) {
762     assertInitialized();
763     configureByFiles(filesBefore);
764     final LookupElement[] items = complete(CompletionType.BASIC);
765     Assert.assertNotNull("No lookup was shown, probably there was only one lookup element that was inserted automatically", items);
766     return getLookupElementStrings();
767   }
768
769   @Override
770   @Nullable
771   public List<String> getLookupElementStrings() {
772     assertInitialized();
773     final LookupElement[] elements = getLookupElements();
774     if (elements == null) return null;
775
776     return ContainerUtil.map(elements, lookupItem -> lookupItem.getLookupString());
777   }
778
779   @Override
780   public void finishLookup(final char completionChar) {
781     CommandProcessor.getInstance().executeCommand(getProject(),
782                                                   () -> ((LookupImpl)LookupManager.getActiveLookup(getEditor())).finishLookup(completionChar), null, null);
783   }
784
785   @Override
786   public void testRename(@NotNull final String fileBefore, @NotNull final String fileAfter, @NotNull final String newName, @NotNull final String... additionalFiles) {
787     assertInitialized();
788     configureByFiles(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, fileBefore)));
789     testRename(fileAfter, newName);
790   }
791
792   @Override
793   public void testRename(@NotNull final String fileAfter, @NotNull final String newName) {
794     renameElementAtCaret(newName);
795     checkResultByFile(fileAfter);
796   }
797
798   @Override
799   @NotNull
800   public PsiElement getElementAtCaret() {
801     assertInitialized();
802     Editor editor = getCompletionEditor();
803     int findTargetFlags = TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED | TargetElementUtil.ELEMENT_NAME_ACCEPTED;
804     PsiElement element = TargetElementUtil.findTargetElement(editor, findTargetFlags);
805
806     // if no references found in injected fragment, try outer document
807     if (element == null && editor instanceof EditorWindow) {
808       element = TargetElementUtil.findTargetElement(((EditorWindow)editor).getDelegate(), findTargetFlags);
809     }
810
811     if (element == null) {
812       Assert.fail("element not found in file " + myFile.getName() +
813                   " at caret position offset " + myEditor.getCaretModel().getOffset() +  "," +
814                   " psi structure:\n" + DebugUtil.psiToString(getFile(), true, true));
815     }
816     return element;
817   }
818
819   @Override
820   public void renameElementAtCaret(@NotNull final String newName) {
821     renameElement(getElementAtCaret(), newName);
822   }
823
824   @Override
825   public void renameElementAtCaretUsingHandler(@NotNull final String newName) {
826       final DataContext editorContext = ((EditorEx)myEditor).getDataContext();
827       final DataContext context = new DataContext() {
828         @Override
829         public Object getData(final String dataId) {
830           return PsiElementRenameHandler.DEFAULT_NAME.getName().equals(dataId)
831                  ? newName
832                  : editorContext.getData(dataId);
833         }
834       };
835       final RenameHandler renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(context);
836       Assert.assertNotNull("No handler for this context", renameHandler);
837
838       renameHandler.invoke(getProject(), myEditor, getFile(), context);
839   }
840
841   @Override
842   public void renameElement(@NotNull final PsiElement element, @NotNull final String newName) {
843     final boolean searchInComments = false;
844     final boolean searchTextOccurrences = false;
845     renameElement(element, newName, searchInComments, searchTextOccurrences);
846   }
847
848   @Override
849   public void renameElement(@NotNull final PsiElement element,
850                             @NotNull final String newName,
851                             final boolean searchInComments,
852                             final boolean searchTextOccurrences) {
853     final PsiElement substitution = RenamePsiElementProcessor.forElement(element).substituteElementToRename(element, myEditor);
854     if (substitution == null) return;
855     new RenameProcessor(getProject(), substitution, newName, searchInComments, searchTextOccurrences).run();
856   }
857
858   @Override
859   public <T extends PsiElement> T findElementByText(@NotNull String text, @NotNull Class<T> elementClass) {
860     int pos = PsiDocumentManager.getInstance(getProject()).getDocument(getFile()).getText().indexOf(text);
861     Assert.assertTrue(text, pos >= 0);
862     return PsiTreeUtil.getParentOfType(getFile().findElementAt(pos), elementClass);
863   }
864
865   @Override
866   public void type(final char c) {
867     assertInitialized();
868     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
869       @Override
870       public void run() {
871         final EditorActionManager actionManager = EditorActionManager.getInstance();
872         if (c == '\b') {
873           performEditorAction(IdeActions.ACTION_EDITOR_BACKSPACE);
874           return;
875         }
876         if (c == '\n') {
877           if (_performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM)) {
878             return;
879           }
880           if (_performEditorAction(IdeActions.ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE)) {
881             return;
882           }
883
884           performEditorAction(IdeActions.ACTION_EDITOR_ENTER);
885           return;
886         }
887         if (c == '\t') {
888           if (_performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_REPLACE)) {
889             return;
890           }
891           if (_performEditorAction(IdeActions.ACTION_EXPAND_LIVE_TEMPLATE_BY_TAB)) {
892             return;
893           }
894           if (_performEditorAction(IdeActions.ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE)) {
895             return;
896           }
897           if (_performEditorAction(IdeActions.ACTION_EDITOR_TAB)) {
898             return;
899           }
900         }
901         if (c == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) {
902           if (_performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_COMPLETE_STATEMENT)) {
903             return;
904           }
905         }
906
907         CommandProcessor.getInstance().executeCommand(getProject(), () -> {
908           CommandProcessor.getInstance().setCurrentCommandGroupId(myEditor.getDocument());
909           ActionManagerEx.getInstanceEx().fireBeforeEditorTyping(c, getEditorDataContext());
910           actionManager.getTypedAction().actionPerformed(getEditor(), c, getEditorDataContext());
911         }, null, DocCommandGroupId.noneGroupId(myEditor.getDocument()));
912       }
913     });
914   }
915
916   @NotNull
917   private DataContext getEditorDataContext() {
918     return ((EditorEx)myEditor).getDataContext();
919   }
920
921   @Override
922   public void type(@NotNull String s) {
923     for (int i = 0; i < s.length(); i++) {
924       type(s.charAt(i));
925     }
926   }
927
928   @Override
929   public void performEditorAction(@NotNull final String actionId) {
930     assertInitialized();
931     _performEditorAction(actionId);
932   }
933
934   private boolean _performEditorAction(@NotNull String actionId) {
935     final DataContext dataContext = getEditorDataContext();
936
937     final ActionManagerEx managerEx = ActionManagerEx.getInstanceEx();
938     final AnAction action = managerEx.getAction(actionId);
939     final AnActionEvent event = new AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, new Presentation(), managerEx, 0);
940
941     return WriteCommandAction.runWriteCommandAction(getProject(), new Computable<Boolean>() {
942       @Override
943       public Boolean compute() {
944         action.update(event);
945
946         if (!event.getPresentation().isEnabled()) {
947           return false;
948         }
949
950         managerEx.fireBeforeActionPerformed(action, dataContext, event);
951
952         action.actionPerformed(event);
953
954         managerEx.fireAfterActionPerformed(action, dataContext, event);
955         return true;
956       }
957     });
958   }
959
960   @NotNull
961   @Override
962   public Presentation testAction(@NotNull AnAction action) {
963     TestActionEvent e = new TestActionEvent(action);
964     action.beforeActionPerformedUpdate(e);
965     if (e.getPresentation().isEnabled() && e.getPresentation().isVisible()) {
966       action.actionPerformed(e);
967     }
968     return e.getPresentation();
969   }
970
971   @NotNull
972   @Override
973   public Collection<UsageInfo> testFindUsages(@NotNull final String... fileNames) {
974     assertInitialized();
975     configureByFiles(fileNames);
976     final PsiElement targetElement = TargetElementUtil
977       .findTargetElement(getEditor(), TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED);
978     Assert.assertNotNull("Cannot find referenced element", targetElement);
979     return findUsages(targetElement);
980   }
981
982   @NotNull
983   @Override
984   public Collection<UsageInfo> findUsages(@NotNull final PsiElement targetElement) {
985     return findUsages(targetElement, null);
986   }
987
988   @NotNull
989   public Collection<UsageInfo> findUsages(@NotNull final PsiElement targetElement, @Nullable SearchScope scope) {
990     final Project project = getProject();
991     final FindUsagesHandler handler =
992       ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false);
993
994     final CommonProcessors.CollectProcessor<UsageInfo> processor = new CommonProcessors.CollectProcessor<UsageInfo>();
995     Assert.assertNotNull("Cannot find handler for: " + targetElement, handler);
996     final PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements());
997     final FindUsagesOptions options = handler.getFindUsagesOptions(null);
998     if (scope != null) options.searchScope = scope;
999     for (PsiElement psiElement : psiElements) {
1000       handler.processElementUsages(psiElement, processor, options);
1001     }
1002     return processor.getResults();
1003   }
1004
1005   @NotNull
1006   @Override
1007   public RangeHighlighter[] testHighlightUsages(@NotNull final String... files) {
1008     configureByFiles(files);
1009     testAction(new HighlightUsagesAction());
1010     final Editor editor = getEditor();
1011     //final Editor editor = com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext());
1012     //assert editor != null;
1013     //HighlightUsagesHandler.invoke(getProject(), editor, getFile());
1014     return editor.getMarkupModel().getAllHighlighters();
1015   }
1016
1017   @Override
1018   public void moveFile(@NotNull final String filePath, @NotNull final String to, @NotNull final String... additionalFiles) {
1019     assertInitialized();
1020     final Project project = getProject();
1021     configureByFiles(ArrayUtil.reverseArray(ArrayUtil.append(additionalFiles, filePath)));
1022     final VirtualFile file = findFileInTempDir(to);
1023     Assert.assertNotNull("Directory " + to + " not found", file);
1024     Assert.assertTrue(to + " is not a directory", file.isDirectory());
1025     final PsiDirectory directory = myPsiManager.findDirectory(file);
1026     new MoveFilesOrDirectoriesProcessor(project, new PsiElement[]{getFile()}, directory,
1027                                         false, false, null, null).run();
1028   }
1029
1030   @Override
1031   @Nullable
1032   public GutterMark findGutter(@NotNull final String filePath) {
1033     configureByFilesInner(filePath);
1034     CommonProcessors.FindFirstProcessor<GutterMark> processor = new CommonProcessors.FindFirstProcessor<GutterMark>();
1035     findGutters(processor);
1036     return processor.getFoundValue();
1037   }
1038
1039   @NotNull
1040   @Override
1041   public List<GutterMark> findGuttersAtCaret() {
1042     CommonProcessors.CollectProcessor<GutterMark> processor = new CommonProcessors.CollectProcessor<GutterMark>();
1043     findGutters(processor);
1044     return new ArrayList<GutterMark>(processor.getResults());
1045   }
1046
1047   private void findGutters(Processor<GutterMark> processor) {
1048     int offset = myEditor.getCaretModel().getOffset();
1049
1050     final Collection<HighlightInfo> infos = doHighlighting();
1051     for (HighlightInfo info : infos) {
1052       if (info.endOffset >= offset && info.startOffset <= offset) {
1053         final GutterMark renderer = info.getGutterIconRenderer();
1054         if (renderer != null && !processor.process(renderer)) {
1055           return;
1056         }
1057       }
1058     }
1059     RangeHighlighter[] highlighters = DocumentMarkupModel.forDocument(myEditor.getDocument(), getProject(), true).getAllHighlighters();
1060     for (RangeHighlighter highlighter : highlighters) {
1061       if (highlighter.getEndOffset() >= offset && highlighter.getStartOffset() <= offset) {
1062         GutterMark renderer = highlighter.getGutterIconRenderer();
1063         if (renderer != null && !processor.process(renderer)) {
1064           return;
1065         }
1066       }
1067     }
1068   }
1069
1070   @Override
1071   @NotNull
1072   public List<GutterMark> findAllGutters(@NotNull final String filePath) {
1073     configureByFilesInner(filePath);
1074     return findAllGutters();
1075   }
1076
1077   @Override
1078   @NotNull
1079   public List<GutterMark> findAllGutters() {
1080     final Project project = getProject();
1081     final SortedMap<Integer, List<GutterMark>> result = new TreeMap<Integer, List<GutterMark>>();
1082
1083     List<HighlightInfo> infos = doHighlighting();
1084     for (HighlightInfo info : infos) {
1085       addGutterIconRenderer(info.getGutterIconRenderer(), info.startOffset, result);
1086     }
1087
1088     RangeHighlighter[] highlighters = DocumentMarkupModel.forDocument(myEditor.getDocument(), project, true).getAllHighlighters();
1089     for (final RangeHighlighter highlighter : highlighters) {
1090       if (!highlighter.isValid()) continue;
1091       addGutterIconRenderer(highlighter.getGutterIconRenderer(), highlighter.getStartOffset(), result);
1092     }
1093     return ContainerUtil.concat(result.values());
1094   }
1095
1096   @Override
1097   public PsiFile addFileToProject(@NotNull final String relativePath, @NotNull final String fileText) {
1098     assertInitialized();
1099     return addFileToProject(getTempDirPath(), relativePath, fileText);
1100   }
1101
1102   protected PsiFile addFileToProject(@NotNull final String rootPath, @NotNull final String relativePath, @NotNull final String fileText) {
1103     return new WriteCommandAction<PsiFile>(getProject()) {
1104       @Override
1105       protected void run(@NotNull Result<PsiFile> result) throws Throwable {
1106         try {
1107           if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
1108             final VirtualFile file = myTempDirFixture.createFile(relativePath, fileText);
1109             result.setResult(PsiManager.getInstance(getProject()).findFile(file));
1110           }
1111           else {
1112             result.setResult(((HeavyIdeaTestFixture)myProjectFixture).addFileToProject(rootPath, relativePath, fileText));
1113           }
1114         }
1115         catch (IOException e) {
1116           throw new RuntimeException(e);
1117         }
1118         finally {
1119           ((PsiModificationTrackerImpl)PsiManager.getInstance(getProject()).getModificationTracker()).incCounter();
1120         }
1121       }
1122     }.execute().getResultObject();
1123   }
1124
1125   public <T> void registerExtension(final ExtensionsArea area, final ExtensionPointName<T> epName, final T extension) {
1126     assertInitialized();
1127     final ExtensionPoint<T> extensionPoint = area.getExtensionPoint(epName);
1128     extensionPoint.registerExtension(extension);
1129     Disposer.register(getTestRootDisposable(), new Disposable() {
1130       @Override
1131       public void dispose() {
1132         extensionPoint.unregisterExtension(extension);
1133       }
1134     });
1135   }
1136
1137   @NotNull
1138   @Override
1139   public PsiManager getPsiManager() {
1140     return myPsiManager;
1141   }
1142
1143   @Override
1144   public LookupElement[] complete(@NotNull CompletionType type) {
1145     return complete(type, 1);
1146   }
1147
1148   @Override
1149   public LookupElement[] complete(@NotNull final CompletionType type, final int invocationCount) {
1150     assertInitialized();
1151     myEmptyLookup = false;
1152     return UIUtil.invokeAndWaitIfNeeded(new Computable<LookupElement[]>() {
1153       @Override
1154       public LookupElement[] compute() {
1155         CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
1156           @Override
1157           public void run() {
1158             final CodeCompletionHandlerBase handler = new CodeCompletionHandlerBase(type) {
1159
1160               @Override
1161               protected void completionFinished(CompletionProgressIndicator indicator, boolean hasModifiers) {
1162                 myEmptyLookup = indicator.getLookup().getItems().isEmpty();
1163                 super.completionFinished(indicator, hasModifiers);
1164               }
1165             };
1166             Editor editor = getCompletionEditor();
1167             handler.invokeCompletion(getProject(), editor, invocationCount);
1168             PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); // to compare with file text
1169           }
1170         }, null, null);
1171         return getLookupElements();
1172       }
1173     });
1174   }
1175
1176   @Nullable
1177   protected Editor getCompletionEditor() {
1178     return InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(myEditor, getFile());
1179   }
1180
1181   @Override
1182   @Nullable
1183   public LookupElement[] completeBasic() {
1184     return complete(CompletionType.BASIC);
1185   }
1186
1187   @Override
1188   @NotNull
1189   public final List<LookupElement> completeBasicAllCarets(@Nullable final Character charToTypeAfterCompletion) {
1190     final CaretModel caretModel = myEditor.getCaretModel();
1191     final List<Caret> carets = caretModel.getAllCarets();
1192
1193     final List<Integer> originalOffsets = new ArrayList<Integer>(carets.size());
1194
1195     for (final Caret caret : carets) {
1196       originalOffsets.add(caret.getOffset());
1197     }
1198     caretModel.removeSecondaryCarets();
1199
1200     // We do it in reverse order because completions would affect offsets
1201     // i.e.: when you complete "spa" to "spam", next caret offset increased by 1
1202     Collections.reverse(originalOffsets);
1203     final List<LookupElement> result = new ArrayList<LookupElement>();
1204     for (final int originalOffset : originalOffsets) {
1205       caretModel.moveToOffset(originalOffset);
1206       final LookupElement[] lookupElements = completeBasic();
1207       if (charToTypeAfterCompletion != null) {
1208         type(charToTypeAfterCompletion);
1209       }
1210       if (lookupElements != null) {
1211         result.addAll(Arrays.asList(lookupElements));
1212       }
1213     }
1214     return result;
1215   }
1216
1217   @Override
1218   public void saveText(@NotNull final VirtualFile file, @NotNull final String text) {
1219     new WriteAction() {
1220       @Override
1221       protected void run(@NotNull Result result) throws Throwable {
1222         VfsUtil.saveText(file, text);
1223       }
1224     }.execute().throwException();
1225
1226   }
1227
1228   @Override
1229   @Nullable
1230   public LookupElement[] getLookupElements() {
1231     LookupImpl lookup = getLookup();
1232     if (lookup == null) {
1233       return myEmptyLookup ? LookupElement.EMPTY_ARRAY : null;
1234     }
1235     else {
1236       final List<LookupElement> list = lookup.getItems();
1237       return list.toArray(new LookupElement[list.size()]);
1238     }
1239   }
1240
1241   @Override
1242   public void checkResult(@NotNull final String text) {
1243     checkResult(text, false);
1244   }
1245
1246   @Override
1247   public void checkResult(@NotNull final String text, final boolean stripTrailingSpaces) {
1248     new WriteCommandAction(getProject()) {
1249       @Override
1250       protected void run(@NotNull Result result) throws Throwable {
1251         PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1252         EditorUtil.fillVirtualSpaceUntilCaret(myEditor);
1253         checkResult("TEXT", stripTrailingSpaces, SelectionAndCaretMarkupLoader.fromText(text), getHostFile().getText());
1254       }
1255     }.execute();
1256   }
1257
1258   @Override
1259   public void checkResultByFile(@NotNull final String expectedFile) {
1260     checkResultByFile(expectedFile, false);
1261   }
1262
1263   @Override
1264   public void checkResultByFile(@NotNull final String expectedFile, final boolean ignoreTrailingWhitespaces) {
1265     assertInitialized();
1266     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
1267       @Override
1268       public void run() {
1269         try {
1270           checkResultByFile(expectedFile, getHostFile(), ignoreTrailingWhitespaces);
1271         }
1272         catch (IOException e) {
1273           throw new RuntimeException(e);
1274         }
1275       }
1276     });
1277   }
1278
1279   @Override
1280   public void checkResultByFile(@NotNull final String filePath, @NotNull final String expectedFile, final boolean ignoreTrailingWhitespaces) {
1281     assertInitialized();
1282
1283     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
1284       @Override
1285       public void run() {
1286         final String path = filePath.replace(File.separatorChar, '/');
1287         final VirtualFile copy = findFileInTempDir(path);
1288         if (copy == null) {
1289           throw new IllegalArgumentException("could not find results file " + path);
1290         }
1291         final PsiFile psiFile = myPsiManager.findFile(copy);
1292         Assert.assertNotNull(copy.getPath(), psiFile);
1293         try {
1294           checkResultByFile(expectedFile, psiFile, ignoreTrailingWhitespaces);
1295         }
1296         catch (IOException e) {
1297           throw new RuntimeException(e);
1298         }
1299       }
1300     });
1301   }
1302
1303   @Override
1304   public void setUp() throws Exception {
1305     super.setUp();
1306
1307     TestRunnerUtil.replaceIdeEventQueueSafely();
1308     EdtTestUtil.runInEdtAndWait(new ThrowableRunnable<Throwable>() {
1309       @Override
1310       public void run() throws Throwable {
1311         myProjectFixture.setUp();
1312         myTempDirFixture.setUp();
1313
1314         VirtualFile tempDir = myTempDirFixture.getFile("");
1315         PlatformTestCase.synchronizeTempDirVfs(tempDir);
1316
1317         myPsiManager = (PsiManagerImpl)PsiManager.getInstance(getProject());
1318         configureInspections(LocalInspectionTool.EMPTY_ARRAY, getProject(), Collections.<String>emptyList(), getTestRootDisposable());
1319
1320         DaemonCodeAnalyzerImpl daemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1321         daemonCodeAnalyzer.prepareForTest();
1322
1323         DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false);
1324         ensureIndexesUpToDate(getProject());
1325         ((StartupManagerImpl)StartupManagerEx.getInstanceEx(getProject())).runPostStartupActivities();
1326       }
1327     });
1328   }
1329
1330   @Override
1331   public void tearDown() throws Exception {
1332     try {
1333       EdtTestUtil.runInEdtAndWait(new ThrowableRunnable<Throwable>() {
1334         @Override
1335         public void run() throws Throwable {
1336           try {
1337             DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); // return default value to avoid unnecessary save
1338             FileEditorManager editorManager = FileEditorManager.getInstance(getProject());
1339             PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1340             VirtualFile[] openFiles = editorManager.getOpenFiles();
1341             for (VirtualFile openFile : openFiles) {
1342               editorManager.closeFile(openFile);
1343             }
1344           }
1345           finally {
1346             myEditor = null;
1347             myFile = null;
1348             myPsiManager = null;
1349
1350             try {
1351               myProjectFixture.tearDown();
1352             }
1353             finally {
1354               myTempDirFixture.tearDown();
1355             }
1356           }
1357         }
1358       });
1359     }
1360     finally {
1361       super.tearDown();
1362     }
1363   }
1364
1365   @NotNull
1366   private PsiFile[] configureByFilesInner(@NotNull String... filePaths) {
1367     assertInitialized();
1368     myFile = null;
1369     myEditor = null;
1370     PsiFile[] psiFiles = new PsiFile[filePaths.length];
1371     for (int i = filePaths.length - 1; i >= 0; i--) {
1372       psiFiles[i] = configureByFileInner(filePaths[i]);
1373     }
1374     return psiFiles;
1375   }
1376
1377   @Override
1378   public PsiFile configureByFile(@NotNull final String file) {
1379     configureByFilesInner(file);
1380     return getFile();
1381   }
1382
1383   @NotNull
1384   @Override
1385   public PsiFile[] configureByFiles(@NotNull final String... files) {
1386     return configureByFilesInner(files);
1387   }
1388
1389   @Override
1390   public PsiFile configureByText(@NotNull final FileType fileType, @NotNull final String text) {
1391     assertInitialized();
1392     final String extension = fileType.getDefaultExtension();
1393     final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
1394     if (fileTypeManager.getFileTypeByExtension(extension) != fileType) {
1395       new WriteCommandAction(getProject()) {
1396         @Override
1397         protected void run(@NotNull Result result) throws Exception {
1398           fileTypeManager.associateExtension(fileType, extension);
1399         }
1400       }.execute();
1401     }
1402     final String fileName = "aaa." + extension;
1403     return configureByText(fileName, text);
1404   }
1405
1406   @Override
1407   public PsiFile configureByText(@NotNull final String fileName, @NotNull final String text) {
1408     assertInitialized();
1409     return new WriteCommandAction<PsiFile>(getProject()) {
1410       @Override
1411       protected void run(@NotNull Result<PsiFile> result) throws Throwable {
1412         final VirtualFile vFile;
1413         if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
1414           final VirtualFile root = LightPlatformTestCase.getSourceRoot();
1415           root.refresh(false, false);
1416           vFile = root.findOrCreateChildData(this, fileName);
1417           Assert.assertNotNull(fileName + " not found in " + root.getPath(), vFile);
1418         }
1419         else if (myTempDirFixture instanceof TempDirTestFixtureImpl) {
1420           final File tempFile = ((TempDirTestFixtureImpl)myTempDirFixture).createTempFile(fileName);
1421           vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempFile);
1422           Assert.assertNotNull(tempFile + " not found", vFile);
1423         }
1424         else {
1425           vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(getTempDirPath(), fileName));
1426           Assert.assertNotNull(fileName + " not found in " + getTempDirPath(), vFile);
1427         }
1428
1429         prepareVirtualFile(vFile);
1430
1431         final Document document = FileDocumentManager.getInstance().getCachedDocument(vFile);
1432         if (document != null) {
1433           PsiDocumentManager.getInstance(getProject()).doPostponedOperationsAndUnblockDocument(document);
1434           FileDocumentManager.getInstance().saveDocument(document);
1435         }
1436
1437         VfsUtil.saveText(vFile, text);
1438         configureInner(vFile, SelectionAndCaretMarkupLoader.fromFile(vFile));
1439         result.setResult(getFile());
1440       }
1441     }.execute().getResultObject();
1442   }
1443
1444   @Override
1445   public Document getDocument(@NotNull final PsiFile file) {
1446     assertInitialized();
1447     return PsiDocumentManager.getInstance(getProject()).getDocument(file);
1448   }
1449
1450   private PsiFile configureByFileInner(@NotNull String filePath) {
1451     assertInitialized();
1452     final VirtualFile file = copyFileToProject(filePath);
1453     return configureByFileInner(file);
1454   }
1455
1456   @Override
1457   public PsiFile configureFromTempProjectFile(@NotNull final String filePath) {
1458     final VirtualFile fileInTempDir = findFileInTempDir(filePath);
1459     if (fileInTempDir == null) {
1460       throw new IllegalArgumentException("Could not find file in temp dir: " + filePath);
1461     }
1462     return configureByFileInner(fileInTempDir);
1463   }
1464
1465   @Override
1466   public void configureFromExistingVirtualFile(@NotNull VirtualFile virtualFile) {
1467     configureByFileInner(virtualFile);
1468   }
1469
1470   private PsiFile configureByFileInner(@NotNull VirtualFile copy) {
1471     return configureInner(copy, SelectionAndCaretMarkupLoader.fromFile(copy));
1472   }
1473
1474   private PsiFile configureInner(@NotNull final VirtualFile copy, @NotNull final SelectionAndCaretMarkupLoader loader) {
1475     assertInitialized();
1476
1477     EdtTestUtil.runInEdtAndWait(new ThrowableRunnable<Throwable>() {
1478       @Override
1479       public void run() {
1480         if (!copy.getFileType().isBinary()) {
1481           AccessToken token = WriteAction.start();
1482           try {
1483             copy.setBinaryContent(loader.newFileText.getBytes(copy.getCharset()));
1484           }
1485           catch (IOException e) {
1486             throw new RuntimeException(e);
1487           }
1488           finally {
1489             token.finish();
1490           }
1491         }
1492         myFile = copy;
1493         myEditor = createEditor(copy);
1494         if (myEditor == null) {
1495           Assert.fail("editor couldn't be created for: " + copy.getPath() + ", use copyFileToProject() instead of configureByFile()");
1496         }
1497
1498         EditorTestUtil.setCaretsAndSelection(myEditor, loader.caretState);
1499
1500         Module module = getModule();
1501         if (module != null) {
1502           for (Facet facet : FacetManager.getInstance(module).getAllFacets()) {
1503             module.getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
1504           }
1505         }
1506         PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1507
1508         if (myCaresAboutInjection) {
1509           setupEditorForInjectedLanguage();
1510         }
1511       }
1512     });
1513
1514     return getFile();
1515   }
1516
1517   protected void prepareVirtualFile(@NotNull VirtualFile file) {
1518   }
1519
1520   private void setupEditorForInjectedLanguage() {
1521     Editor editor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(myEditor, getFile());
1522     if (editor instanceof EditorWindow) {
1523       myFile = ((EditorWindow)editor).getInjectedFile().getViewProvider().getVirtualFile();
1524       myEditor = editor;
1525     }
1526   }
1527
1528   @Override
1529   public VirtualFile findFileInTempDir(@NotNull final String filePath) {
1530     if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
1531       return myTempDirFixture.getFile(filePath);
1532     }
1533     String fullPath = getTempDirPath() + "/" + filePath;
1534
1535     final VirtualFile copy = LocalFileSystem.getInstance().refreshAndFindFileByPath(fullPath.replace(File.separatorChar, '/'));
1536     Assert.assertNotNull("file " + fullPath + " not found", copy);
1537     VfsTestUtil.assertFilePathEndsWithCaseSensitivePath(copy, filePath);
1538     return copy;
1539   }
1540
1541   @Nullable
1542   protected Editor createEditor(@NotNull VirtualFile file) {
1543     final Project project = getProject();
1544     final FileEditorManager instance = FileEditorManager.getInstance(project);
1545     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1546
1547     Editor editor = instance.openTextEditor(new OpenFileDescriptor(project, file), false);
1548     if (editor != null) {
1549       DaemonCodeAnalyzer.getInstance(getProject()).restart();
1550     }
1551     return editor;
1552   }
1553
1554   private long collectAndCheckHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings) throws Exception {
1555     return collectAndCheckHighlighting(checkWarnings, checkInfos, checkWeakWarnings, false);
1556   }
1557
1558   private long collectAndCheckHighlighting(boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings,
1559                                            boolean ignoreExtraHighlighting) throws Exception {
1560     ExpectedHighlightingData data = new ExpectedHighlightingData(myEditor.getDocument(),
1561                                                                  checkWarnings, checkWeakWarnings, checkInfos, ignoreExtraHighlighting, getHostFile());
1562     data.init();
1563     return collectAndCheckHighlighting(data);
1564   }
1565
1566   private PsiFile getHostFile() {
1567     return InjectedLanguageUtil.getTopLevelFile(getFile());
1568   }
1569
1570   private long collectAndCheckHighlighting(@NotNull ExpectedHighlightingData data) {
1571     final Project project = getProject();
1572     PsiDocumentManager.getInstance(project).commitAllDocuments();
1573
1574     PsiFileImpl file = (PsiFileImpl)getHostFile();
1575     FileElement hardRefToFileElement = file.calcTreeElement();//to load text
1576
1577     //to initialize caches
1578     if (!DumbService.isDumb(project)) {
1579       CacheManager.SERVICE.getInstance(project).getFilesWithWord("XXX", UsageSearchContext.IN_COMMENTS, GlobalSearchScope.allScope(project), true);
1580     }
1581
1582     final long start = System.currentTimeMillis();
1583     final VirtualFileFilter fileTreeAccessFilter = myVirtualFileFilter;
1584     Disposable disposable = Disposer.newDisposable();
1585     if (fileTreeAccessFilter != null) {
1586       PsiManagerEx.getInstanceEx(project).setAssertOnFileLoadingFilter(fileTreeAccessFilter, disposable);
1587     }
1588
1589     //    ProfilingUtil.startCPUProfiling();
1590     List<HighlightInfo> infos;
1591     try {
1592       infos = doHighlighting();
1593       removeDuplicatedRangesForInjected(infos);
1594     }
1595     finally {
1596       Disposer.dispose(disposable);
1597     }
1598     //    ProfilingUtil.captureCPUSnapshot("testing");
1599     final long elapsed = System.currentTimeMillis() - start;
1600
1601     data.checkResult(infos, file.getText());
1602     hardRefToFileElement.hashCode(); // use it so gc won't collect it
1603     return elapsed;
1604   }
1605
1606   public void setVirtualFileFilter(@Nullable VirtualFileFilter filter) {
1607     myVirtualFileFilter = filter;
1608   }
1609
1610   @Override
1611   @NotNull
1612   public List<HighlightInfo> doHighlighting() {
1613     final Project project = getProject();
1614     PsiDocumentManager.getInstance(project).commitAllDocuments();
1615
1616     PsiFile file = getFile();
1617     Editor editor = getEditor();
1618     if (editor instanceof EditorWindow) {
1619       editor = ((EditorWindow)editor).getDelegate();
1620       file = InjectedLanguageUtil.getTopLevelFile(file);
1621     }
1622     return instantiateAndRun(file, editor, ArrayUtil.EMPTY_INT_ARRAY, myAllowDirt);
1623   }
1624
1625   @NotNull
1626   @Override
1627   public List<HighlightInfo> doHighlighting(@NotNull final HighlightSeverity minimalSeverity) {
1628     return ContainerUtil.filter(doHighlighting(), info -> info.getSeverity().compareTo(minimalSeverity) >= 0);
1629   }
1630
1631   @NotNull
1632   @Override
1633   public String getTestDataPath() {
1634     return myTestDataPath;
1635   }
1636
1637   @Override
1638   public void setTestDataPath(@NotNull String dataPath) {
1639     myTestDataPath = dataPath;
1640   }
1641
1642   @Override
1643   public Project getProject() {
1644     return myProjectFixture.getProject();
1645   }
1646
1647   @Override
1648   public Module getModule() {
1649     return myProjectFixture.getModule();
1650   }
1651
1652   @Override
1653   public Editor getEditor() {
1654     return myEditor;
1655   }
1656
1657   @Override
1658   public int getCaretOffset() {
1659     return myEditor.getCaretModel().getOffset();
1660   }
1661
1662   @Override
1663   public PsiFile getFile() {
1664     return myFile == null ? null : ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
1665       @Override
1666       public PsiFile compute() {
1667         return PsiManager.getInstance(getProject()).findFile(myFile);
1668       }
1669     });
1670   }
1671
1672   @Override
1673   public void allowTreeAccessForFile(@NotNull final VirtualFile file) {
1674     assert myVirtualFileFilter instanceof FileTreeAccessFilter : "configured filter does not support this method";
1675     ((FileTreeAccessFilter)myVirtualFileFilter).allowTreeAccessForFile(file);
1676   }
1677
1678   @Override
1679   public void allowTreeAccessForAllFiles() {
1680     assert myVirtualFileFilter instanceof FileTreeAccessFilter : "configured filter does not support this method";
1681     ((FileTreeAccessFilter)myVirtualFileFilter).allowTreeAccessForAllFiles();
1682   }
1683
1684   private void checkResultByFile(@NotNull String expectedFile,
1685                                  @NotNull PsiFile originalFile,
1686                                  boolean stripTrailingSpaces) throws IOException {
1687     if (!stripTrailingSpaces) {
1688       EditorUtil.fillVirtualSpaceUntilCaret(myEditor);
1689     }
1690     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1691
1692     final String fileText = originalFile.getText();
1693     final String path = getTestDataPath() + "/" + expectedFile;
1694
1695     /*final VirtualFile result = LocalFileSystem.getInstance().findFileByPath(path);
1696     final int caret = myEditor.getCaretModel().getOffset();
1697     final String newText = myFile == originalFile ? fileText.substring(0, caret) + "<caret>" + fileText.substring(caret) : fileText;
1698     VfsUtil.saveText(result, newText);*/
1699
1700     VirtualFile virtualFile = originalFile.getVirtualFile();
1701     String charset = virtualFile == null? null : virtualFile.getCharset().name();
1702     checkResult(expectedFile, stripTrailingSpaces, SelectionAndCaretMarkupLoader.fromFile(path, charset), fileText);
1703
1704   }
1705
1706   private void checkResult(@NotNull String expectedFile,
1707                            final boolean stripTrailingSpaces,
1708                            @NotNull SelectionAndCaretMarkupLoader loader,
1709                            @NotNull String actualText) {
1710     assertInitialized();
1711     Project project = getProject();
1712     Editor editor = getEditor();
1713     if (editor instanceof EditorWindow) {
1714       editor = ((EditorWindow)editor).getDelegate();
1715     }
1716
1717     UsefulTestCase.doPostponedFormatting(getProject());
1718     if (stripTrailingSpaces) {
1719       actualText = stripTrailingSpaces(actualText);
1720     }
1721
1722     PsiDocumentManager.getInstance(project).commitAllDocuments();
1723
1724     String newFileText1 = loader.newFileText;
1725     if (stripTrailingSpaces) {
1726       newFileText1 = stripTrailingSpaces(newFileText1);
1727     }
1728
1729     actualText = StringUtil.convertLineSeparators(actualText);
1730
1731     if (!Comparing.equal(newFileText1, actualText)) {
1732       if (loader.filePath != null) {
1733         throw new FileComparisonFailure(expectedFile, newFileText1, actualText, loader.filePath);
1734       }
1735       else {
1736         throw new ComparisonFailure(expectedFile, newFileText1, actualText);
1737       }
1738     }
1739
1740     EditorTestUtil.verifyCaretAndSelectionState(editor, loader.caretState, expectedFile);
1741   }
1742
1743   @NotNull
1744   private String stripTrailingSpaces(@NotNull String actualText) {
1745     final Document document = EditorFactory.getInstance().createDocument(actualText);
1746     ((DocumentImpl)document).stripTrailingSpaces(getProject());
1747     actualText = document.getText();
1748     return actualText;
1749   }
1750
1751   public void canChangeDocumentDuringHighlighting(boolean canI) {
1752     myAllowDirt = canI;
1753   }
1754
1755   @NotNull
1756   public String getFoldingDescription(boolean withCollapseStatus) {
1757     CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myEditor);
1758
1759     final FoldingModel model = myEditor.getFoldingModel();
1760     final FoldRegion[] foldingRegions = model.getAllFoldRegions();
1761     final List<Border> borders = new LinkedList<Border>();
1762
1763     for (FoldRegion region : foldingRegions) {
1764       borders.add(new Border(Border.LEFT, region.getStartOffset(), region.getPlaceholderText(), region.isExpanded()));
1765       borders.add(new Border(Border.RIGHT, region.getEndOffset(), "", region.isExpanded()));
1766     }
1767     Collections.sort(borders);
1768
1769     StringBuilder result = new StringBuilder(myEditor.getDocument().getText());
1770     for (Border border : borders) {
1771       result.insert(border.getOffset(), border.isSide() == Border.LEFT ? "<fold text=\'" + border.getText() + "\'" +
1772                                                                          (withCollapseStatus ? " expand=\'" +
1773                                                                                                     border.isExpanded() +
1774                                                                                                     "\'" : "") +
1775                                                                           ">" : END_FOLD);
1776     }
1777
1778     return result.toString();
1779   }
1780
1781   private void testFoldingRegions(@NotNull String verificationFileName, @Nullable String destinationFileName, boolean doCheckCollapseStatus) {
1782     String expectedContent;
1783     try {
1784       expectedContent = FileUtil.loadFile(new File(verificationFileName));
1785     }
1786     catch (IOException e) {
1787       throw new RuntimeException(e);
1788     }
1789     Assert.assertNotNull(expectedContent);
1790
1791     expectedContent = StringUtil.replace(expectedContent, "\r", "");
1792     final String cleanContent = expectedContent.replaceAll(START_FOLD, "").replaceAll(END_FOLD, "");
1793     if (destinationFileName == null) {
1794       configureByText(FileTypeManager.getInstance().getFileTypeByFileName(verificationFileName), cleanContent);
1795     }
1796     else {
1797       try {
1798         FileUtil.writeToFile(new File(destinationFileName), cleanContent);
1799         configureFromExistingVirtualFile(LocalFileSystem.getInstance().refreshAndFindFileByPath(destinationFileName));
1800       }
1801       catch (IOException e) {
1802         throw new RuntimeException(e);
1803       }
1804     }
1805     final String actual = getFoldingDescription(doCheckCollapseStatus);
1806     Assert.assertEquals(expectedContent, actual);
1807   }
1808
1809   @Override
1810   public void testFoldingWithCollapseStatus(@NotNull final String verificationFileName) {
1811     testFoldingRegions(verificationFileName, null, true);
1812   }
1813
1814   @Override
1815   public void testFoldingWithCollapseStatus(@NotNull final String verificationFileName, @Nullable String destinationFileName) {
1816     testFoldingRegions(verificationFileName, destinationFileName, true);
1817   }
1818
1819   @Override
1820   public void testFolding(@NotNull final String verificationFileName) {
1821     testFoldingRegions(verificationFileName, null, false);
1822   }
1823
1824   @Override
1825   public void assertPreferredCompletionItems(final int selected, @NotNull final String... expected) {
1826     final LookupImpl lookup = getLookup();
1827     Assert.assertNotNull("No lookup is shown", lookup);
1828
1829     final JList list = lookup.getList();
1830     List<String> strings = getLookupElementStrings();
1831     Assert.assertNotNull(strings);
1832     final List<String> actual = strings.subList(0, Math.min(expected.length, strings.size()));
1833     if (!actual.equals(Arrays.asList(expected))) {
1834       UsefulTestCase.assertOrderedEquals(DumpLookupElementWeights.getLookupElementWeights(lookup, false), expected);
1835     }
1836     if (selected != list.getSelectedIndex()) {
1837       System.out.println(DumpLookupElementWeights.getLookupElementWeights(lookup, false));
1838     }
1839     Assert.assertEquals(selected, list.getSelectedIndex());
1840   }
1841
1842   @Override
1843   public void testStructureView(@NotNull Consumer<StructureViewComponent> consumer) {
1844     Assert.assertNotNull("configure first", myFile);
1845
1846     final FileEditor fileEditor = FileEditorManager.getInstance(getProject()).getSelectedEditor(myFile);
1847     Assert.assertNotNull("editor not opened for " + myFile, myFile);
1848
1849     final StructureViewBuilder builder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(getFile());
1850     Assert.assertNotNull("no builder for " + myFile, myFile);
1851
1852     StructureViewComponent component = null;
1853     try {
1854       component = (StructureViewComponent)builder.createStructureView(fileEditor, myProjectFixture.getProject());
1855       consumer.consume(component);
1856     }
1857     finally {
1858       if (component != null) Disposer.dispose(component);
1859     }
1860   }
1861
1862   @Override
1863   public void setCaresAboutInjection(boolean caresAboutInjection) {
1864     myCaresAboutInjection = caresAboutInjection;
1865   }
1866
1867   @Override
1868   public LookupImpl getLookup() {
1869     return (LookupImpl)LookupManager.getActiveLookup(myEditor);
1870   }
1871
1872   protected void bringRealEditorBack() {
1873     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1874     if (myEditor instanceof EditorWindow) {
1875       Document document = ((DocumentWindow)myEditor.getDocument()).getDelegate();
1876       myFile = FileDocumentManager.getInstance().getFile(document);
1877       myEditor = ((EditorWindow)myEditor).getDelegate();
1878     }
1879   }
1880
1881   private static class SelectionAndCaretMarkupLoader {
1882     private final String filePath;
1883     private final String newFileText;
1884     private final EditorTestUtil.CaretAndSelectionState caretState;
1885
1886     private SelectionAndCaretMarkupLoader(@NotNull String fileText, String filePath) {
1887       this.filePath = filePath;
1888       final Document document = EditorFactory.getInstance().createDocument(fileText);
1889       caretState = EditorTestUtil.extractCaretAndSelectionMarkers(document);
1890       newFileText = document.getText();
1891     }
1892
1893     @NotNull
1894     private static SelectionAndCaretMarkupLoader fromFile(@NotNull String path, String charset) throws IOException {
1895       return new SelectionAndCaretMarkupLoader(StringUtil.convertLineSeparators(FileUtil.loadFile(new File(path), charset)), path);
1896     }
1897
1898     @NotNull
1899     static SelectionAndCaretMarkupLoader fromFile(@NotNull VirtualFile file) {
1900       final String text;
1901       try {
1902         text = VfsUtilCore.loadText(file);
1903       }
1904       catch (IOException e) {
1905         throw new RuntimeException(e);
1906       }
1907       return new SelectionAndCaretMarkupLoader(StringUtil.convertLineSeparators(text), file.getPath());
1908     }
1909
1910     @NotNull
1911     static SelectionAndCaretMarkupLoader fromText(@NotNull String text) {
1912       return new SelectionAndCaretMarkupLoader(text, null);
1913     }
1914   }
1915
1916   private static class Border implements Comparable<Border> {
1917     private static final boolean LEFT = true;
1918     private static final boolean RIGHT = false;
1919     private final boolean mySide;
1920     private final int myOffset;
1921     private final String myText;
1922     private final boolean myIsExpanded;
1923
1924     private Border(boolean side, int offset, String text, boolean isExpanded) {
1925       mySide = side;
1926       myOffset = offset;
1927       myText = text;
1928       myIsExpanded = isExpanded;
1929     }
1930
1931     public boolean isExpanded() {
1932       return myIsExpanded;
1933     }
1934
1935     public boolean isSide() {
1936       return mySide;
1937     }
1938
1939     public int getOffset() {
1940       return myOffset;
1941     }
1942
1943     public String getText() {
1944       return myText;
1945     }
1946
1947     @Override
1948     public int compareTo(@NotNull Border o) {
1949       return getOffset() < o.getOffset() ? 1 : -1;
1950     }
1951   }
1952 }