diff --git a/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java b/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java index 5f384c27294e..148b9fd8f136 100644 --- a/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java +++ b/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java @@ -58,9 +58,11 @@ import java.util.List; */ @SuppressWarnings("HardCodedStringLiteral") public abstract class InspectionTestCase extends PsiTestCase { + private static final boolean MIGRATE_TEST = false; private static final Logger LOG = Logger.getInstance("#com.intellij.testFramework.InspectionTestCase"); private EntryPoint myUnusedCodeExtension; private VirtualFile ext_src; + private LightTestMigration myMigration; public static GlobalInspectionToolWrapper getUnusedDeclarationWrapper() { InspectionEP ep = new InspectionEP(); @@ -118,20 +120,24 @@ public abstract class InspectionTestCase extends PsiTestCase { boolean runDeadCodeFirst, @NotNull InspectionToolWrapper... additional) { final String testDir = getTestDataPath() + "/" + folderName; - GlobalInspectionContextImpl context = runTool(testDir, jdkName, runDeadCodeFirst, toolWrapper, additional); + final List> tools = getTools(runDeadCodeFirst, toolWrapper, additional); + GlobalInspectionContextImpl context = runTool(testDir, jdkName, toolWrapper, tools); InspectionTestUtil.compareToolResults(context, checkRange, testDir, ContainerUtil.append(Collections.singletonList(toolWrapper), additional)); + + if (MIGRATE_TEST) { + myMigration = new LightTestMigration(getClass(), testDir, tools, getTestProjectSdk()); + } } - protected void runTool(@NonNls @NotNull String testDir, @NonNls final String jdkName, @NotNull InspectionToolWrapper tool) { - runTool(testDir, jdkName, false, tool); + protected void runTool(@NonNls @NotNull String testDir, @NonNls final String jdkName, @NotNull InspectionToolWrapper tool) { + runTool(testDir, jdkName, tool, Collections.singletonList(tool)); } protected GlobalInspectionContextImpl runTool(@NotNull final String testDir, final String jdkName, - boolean runDeadCodeFirst, @NotNull InspectionToolWrapper toolWrapper, - @NotNull InspectionToolWrapper... additional) { + List> tools) { final VirtualFile[] sourceDir = new VirtualFile[1]; ApplicationManager.getApplication().runWriteAction(() -> { try { @@ -144,16 +150,23 @@ public abstract class InspectionTestCase extends PsiTestCase { VirtualFile projectDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(testDir)); AnalysisScope scope = createAnalysisScope(sourceDir[0].equals(projectDir) ? projectDir : sourceDir[0].getParent()); + GlobalInspectionContextForTests globalContext = InspectionsKt.createGlobalContextForTool(scope, getProject(), tools); + + InspectionTestUtil.runTool(toolWrapper, scope, globalContext); + return globalContext; + } + + @NotNull + private static List> getTools(boolean runDeadCodeFirst, + @NotNull InspectionToolWrapper toolWrapper, + @NotNull InspectionToolWrapper[] additional) { List> toolWrappers = new ArrayList<>(); if (runDeadCodeFirst) { toolWrappers.add(getUnusedDeclarationWrapper()); } toolWrappers.add(toolWrapper); ContainerUtil.addAll(toolWrappers, additional); - GlobalInspectionContextForTests globalContext = InspectionsKt.createGlobalContextForTool(scope, getProject(), toolWrappers); - - InspectionTestUtil.runTool(toolWrapper, scope, globalContext); - return globalContext; + return toolWrappers; } @NotNull @@ -241,6 +254,9 @@ public abstract class InspectionTestCase extends PsiTestCase { finally { super.tearDown(); } + if (myMigration != null) { + myMigration.tryMigrate(); + } } @Override diff --git a/java/testFramework/src/com/intellij/testFramework/LightTestMigration.java b/java/testFramework/src/com/intellij/testFramework/LightTestMigration.java new file mode 100644 index 000000000000..94c2c15869fa --- /dev/null +++ b/java/testFramework/src/com/intellij/testFramework/LightTestMigration.java @@ -0,0 +1,204 @@ +// Copyright 2000-2018 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. +package com.intellij.testFramework; + +import com.intellij.JavaTestUtil; +import com.intellij.codeHighlighting.HighlightDisplayLevel; +import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.openapi.application.ex.PathManagerEx; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.AnnotationOrderRootType; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.profile.codeInspection.ProjectInspectionProfileManager; +import com.intellij.testFramework.fixtures.*; +import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl; +import one.util.streamex.EntryStream; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.ComparisonFailure; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.time.Year; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A helper class to aid migration from {@link InspectionTestCase} to {@link LightCodeInsightFixtureTestCase} + */ +@SuppressWarnings("UseOfSystemOutOrSystemErr") +class LightTestMigration { + private final Class myTestClass; + private final Path myDir; + private final List> myTools; + private final Sdk mySdk; + + private static final Map JDK_MAP = EntryStream.of( + "java 1.7", "JAVA_1_7", + "java 1.8", "JAVA_8", + "java 9", "JAVA_9" + ).toMap(); + + LightTestMigration(Class testClass, + String dir, + List> tools, + Sdk sdk) { + myTestClass = testClass; + myDir = Paths.get(dir); + myTools = tools; + mySdk = sdk; + } + + void tryMigrate() throws Exception { + Path javaFile = getSoleJavaFile(myDir); + String fileText = new String(Files.readAllBytes(javaFile), StandardCharsets.UTF_8); + Path targetFile = myDir.resolve(javaFile.getFileName()); + IdeaTestFixtureFactory factory = IdeaTestFixtureFactory.getFixtureFactory(); + LightProjectDescriptor descriptor = new LightProjectDescriptor() { + @Nullable + @Override + public Sdk getSdk() { + return mySdk; + } + }; + TestFixtureBuilder fixtureBuilder = factory.createLightFixtureBuilder(descriptor); + IdeaProjectTestFixture fixture = fixtureBuilder.getFixture(); + JavaCodeInsightTestFixture javaFixture = + JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixture, new LightTempDirTestFixtureImpl(true)); + javaFixture.setUp(); + for (InspectionToolWrapper tool : myTools) { + javaFixture.enableInspections(tool.getTool()); + final Project project = javaFixture.getProject(); + final HighlightDisplayKey displayKey = HighlightDisplayKey.find(tool.getShortName()); + final InspectionProfileImpl currentProfile = ProjectInspectionProfileManager.getInstance(project).getCurrentProfile(); + final HighlightDisplayLevel errorLevel = currentProfile.getErrorLevel(displayKey, null); + if (errorLevel == HighlightDisplayLevel.DO_NOT_SHOW) { + currentProfile.setErrorLevel(displayKey, HighlightDisplayLevel.WARNING, project); + } + } + String expectedText = getExpectedText(javaFile, fileText, javaFixture); + FileUtil.delete(myDir.toFile()); + Files.createDirectories(myDir); + Files.write(targetFile, expectedText.getBytes(StandardCharsets.UTF_8)); + System.out.println("Written: " + targetFile); + generateClassTemplate(javaFile); + javaFixture.tearDown(); + } + + private void generateClassTemplate(Path javaFile) { + String pathSpec; + Path basePath = Paths.get(JavaTestUtil.getJavaTestDataPath()); + Set> importedClasses = + StreamEx.of(myTools.stream().>map(wrapper -> wrapper.getTool().getClass())) + .append(LightCodeInsightFixtureTestCase.class, LightProjectDescriptor.class, NotNull.class) + .toSet(); + if (myDir.startsWith(basePath)) { + Path relativePath = basePath.relativize(myDir); + importedClasses.add(JavaTestUtil.class); + pathSpec = "JavaTestUtil.getRelativeJavaTestDataPath() + \"/" + + StringUtil.escapeStringCharacters(relativePath.toString().replace('\\', '/')) + + '"'; + } + else { + basePath = Paths.get(PathManagerEx.getCommunityHomePath()); + if (myDir.startsWith(basePath)) { + Path relativePath = basePath.relativize(myDir); + pathSpec = "\"/" + StringUtil.escapeStringCharacters(relativePath.toString().replace('\\', '/')) + '"'; + } + else { + pathSpec = '"' + StringUtil.escapeStringCharacters(myDir.toString()) + '"'; + } + } + String imports = generateImports(importedClasses); + String testMethodName = "test" + javaFile.getFileName().toString().replaceFirst(".java$", ""); + String inspections = + myTools.stream().map(InspectionToolWrapper::getTool).map(InspectionProfileEntry::getClass).map(Class::getSimpleName) + .map(name -> "new " + name + "()").collect(Collectors.joining(", ")); + System.out.println("JDK version: " + mySdk.getVersionString()); + String guessedJdk = guessJdk(); + String year = Year.now().toString(); + String classTemplate = MessageFormat.format( + "// Copyright 2000-{7} 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.\n" + + "package {0};\n\n" + + "{1}" + + "\n" + + "public class {2} extends LightCodeInsightFixtureTestCase '{'\n" + + " @Override\n" + + " protected String getBasePath() '{'\n" + + " return {3};\n" + + " '}'\n" + + "\n" + + " @NotNull\n" + + " @Override\n" + + " protected LightProjectDescriptor getProjectDescriptor() '{'\n" + + " return {6};\n" + + " '}'\n" + + "\n" + + " private void doTest() '{'\n" + + " myFixture.enableInspections({4});\n" + + " myFixture.testHighlighting(getTestName(false) + \".java\");\n" + + " '}'\n" + + "\n" + + " public void {5}() '{'\n" + + " doTest();\n" + + " '}'\n" + + "'}'\n", + myTestClass.getPackage().getName(), imports, myTestClass.getSimpleName(), pathSpec, inspections, testMethodName, guessedJdk, year); + System.out.println("Class template: (" + myTestClass.getSimpleName() + ".java)"); + System.out.println("=============================="); + System.out.println(classTemplate); + System.out.println("=============================="); + } + + private String generateImports(Set> importedClasses) { + return importedClasses.stream() + .filter(cls -> !cls.getPackage().equals(myTestClass.getPackage())) + .map(Class::getName) + .sorted() + .map(name -> "import " + name + ";\n").collect(Collectors.joining()); + } + + private String guessJdk() { + String guessedJdk = JDK_MAP.getOrDefault(mySdk.getVersionString(), "JAVA_LATEST"); + if (mySdk.getRootProvider().getFiles(AnnotationOrderRootType.getInstance()).length != 0) { + guessedJdk += "_ANNOTATED"; + } + return guessedJdk; + } + + + private static String getExpectedText(Path javaFile, String fileText, JavaCodeInsightTestFixture javaFixture) { + javaFixture.configureByText(javaFile.getFileName().toString(), fileText); + try { + javaFixture.testHighlighting(true, false, false); + } + catch (ComparisonFailure e) { + // Seems that expected and actual are switched + return e.getActual(); + } + return fileText; + } + + private static Path getSoleJavaFile(Path dir) throws IOException { + List javaFiles = Files.walk(dir).filter(p -> p.toString().endsWith(".java")).filter(p -> Files.isRegularFile(p)) + .collect(Collectors.toList()); + if (javaFiles.isEmpty()) { + throw new RuntimeException("Unable to migrate: no Java files found in " + dir); + } + if (javaFiles.size() > 1) { + throw new RuntimeException("Unable to migrate: more than one Java file found in " + dir); + } + return javaFiles.get(0); + } +}