diff --git a/compiler/android-tests/build.gradle.kts b/compiler/android-tests/build.gradle.kts
index ff2ebe34d13..407a9b4dd7d 100644
--- a/compiler/android-tests/build.gradle.kts
+++ b/compiler/android-tests/build.gradle.kts
@@ -34,6 +34,7 @@ dependencies {
testCompile(jpsBuildTest())
testRuntimeOnly(compile(intellijCoreDep()) { includeJars("intellij-core") })
+ testRuntimeOnly(compile(intellijDep()) { includeJars("jna", rootProject = rootProject) })
testCompile("org.junit.platform:junit-platform-launcher:${commonVer("org.junit.platform", "")}")
}
diff --git a/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt b/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt
index 19e68c18ff0..b4db07d9a75 100644
--- a/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt
+++ b/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt
@@ -17,7 +17,7 @@ import java.nio.file.Paths
abstract class AbstractLightTree2FirConverterTestCase : AbstractRawFirBuilderTestCase() {
- override fun doTest(filePath: String) {
+ fun doTest(filePath: String) {
val firFile = LightTree2Fir(
session = FirSessionFactory.createEmptySession(),
scopeProvider = StubFirScopeProvider,
diff --git a/compiler/incremental-compilation-impl/build.gradle.kts b/compiler/incremental-compilation-impl/build.gradle.kts
index c95e68dbf6a..1b32bd34e76 100644
--- a/compiler/incremental-compilation-impl/build.gradle.kts
+++ b/compiler/incremental-compilation-impl/build.gradle.kts
@@ -26,7 +26,7 @@ dependencies {
testCompile(intellijDep()) { includeJars("log4j", "jdom") }
testRuntime(project(":kotlin-reflect"))
testRuntime(project(":core:descriptors.runtime"))
- testRuntime(intellijDep()) { includeJars("lz4-java", rootProject = rootProject) }
+ testRuntime(intellijDep()) { includeJars("lz4-java", "jna", rootProject = rootProject) }
}
sourceSets {
diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/parsing/ClassesFqNamesTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/parsing/ClassesFqNamesTest.kt
index 99036a5efe4..2a65a2baec5 100644
--- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/parsing/ClassesFqNamesTest.kt
+++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/parsing/ClassesFqNamesTest.kt
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.incremental.parsing
import com.intellij.openapi.util.io.FileUtil
-import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.junit.After
import org.junit.Before
@@ -138,6 +137,6 @@ class ClassesFqNamesTest : KtUsefulTestCase() {
val expected = expectedClasses.sorted().joinToString("\n")
val actual = classesFqNames(setOf(testKt)).sorted().joinToString("\n")
- UsefulTestCase.assertEquals(expected, actual)
+ assertEquals(expected, actual)
}
-}
\ No newline at end of file
+}
diff --git a/compiler/tests-common/build.gradle.kts b/compiler/tests-common/build.gradle.kts
index ca417c987e9..10adacd5170 100644
--- a/compiler/tests-common/build.gradle.kts
+++ b/compiler/tests-common/build.gradle.kts
@@ -57,13 +57,6 @@ dependencies {
testCompileOnly(project(":kotlin-reflect-api"))
testCompileOnly(toolsJar())
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
- testCompile(intellijDep()) {
- includeJars(
- "testFramework", // needed for parsing tests
- "platform-ide-util-io", // needed for CLI process utils in KotlinIntegrationTestBase
- rootProject = rootProject
- )
- }
Platform[202] {
testCompile(intellijDep()) { includeJars("intellij-deps-fastutil-8.3.1-1") }
}
@@ -77,6 +70,7 @@ dependencies {
"asm-all",
"log4j",
"jdom",
+ "jna",
rootProject = rootProject
)
isTransitive = false
diff --git a/compiler/tests-common/tests/com/intellij/execution/ExecutionException.java b/compiler/tests-common/tests/com/intellij/execution/ExecutionException.java
new file mode 100644
index 00000000000..e405761d5d9
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/ExecutionException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution;
+
+import com.intellij.openapi.util.NlsContexts;
+
+import java.io.IOException;
+
+public class ExecutionException extends Exception {
+ public ExecutionException(final @NlsContexts.DialogMessage String s) {
+ super(s);
+ }
+
+ public ExecutionException(final Throwable cause) {
+ super(cause == null ? null : cause.getMessage(), cause);
+ }
+
+ public ExecutionException(final @NlsContexts.DialogMessage String s, Throwable cause) {
+ super(s, cause);
+ }
+
+ public IOException toIOException() {
+ final Throwable cause = getCause();
+ return cause instanceof IOException ? (IOException)cause : new IOException(this);
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/IllegalEnvVarException.java b/compiler/tests-common/tests/com/intellij/execution/IllegalEnvVarException.java
new file mode 100644
index 00000000000..e4aa4d84faf
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/IllegalEnvVarException.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution;
+
+import com.intellij.openapi.util.NlsContexts;
+
+public class IllegalEnvVarException extends ExecutionException {
+ public IllegalEnvVarException(String message) {
+ super(message);
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/configurations/GeneralCommandLine.java b/compiler/tests-common/tests/com/intellij/execution/configurations/GeneralCommandLine.java
new file mode 100644
index 00000000000..7f3accf3574
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/configurations/GeneralCommandLine.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution.configurations;
+
+import com.intellij.diagnostic.LoadingState;
+import com.intellij.execution.CommandLineUtil;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.IllegalEnvVarException;
+import com.intellij.execution.Platform;
+import com.intellij.execution.process.ProcessNotCreatedException;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.NlsSafe;
+import com.intellij.openapi.util.SystemInfoRt;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.encoding.EncodingManager;
+import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.containers.CollectionFactory;
+import com.intellij.util.containers.FastUtilHashingStrategies;
+import com.intellij.util.execution.ParametersListUtil;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.*;
+
+public class GeneralCommandLine implements UserDataHolder {
+ private static final Logger LOG = Logger.getInstance(GeneralCommandLine.class);
+
+ /**
+ * Determines the scope of a parent environment passed to a child process.
+ *
+ * {@code NONE} means a child process will receive an empty environment.
+ * {@code SYSTEM} will provide it with the same environment as an IDE.
+ * {@code CONSOLE} provides the child with a similar environment as if it was launched from, well, a console.
+ * On OS X, a console environment is simulated (see {@link EnvironmentUtil#getEnvironmentMap()} for reasons it's needed
+ * and details on how it works). On Windows and Unix hosts, this option is no different from {@code SYSTEM}
+ * since there is no drastic distinction in environment between GUI and console apps.
+ */
+ public enum ParentEnvironmentType {NONE, SYSTEM, CONSOLE}
+
+ private String myExePath;
+ private File myWorkDirectory;
+ private final Map myEnvParams = new MyMap();
+ private ParentEnvironmentType myParentEnvironmentType = ParentEnvironmentType.CONSOLE;
+ private final ParametersList myProgramParams = new ParametersList();
+ private Charset myCharset = defaultCharset();
+ private boolean myRedirectErrorStream;
+ private File myInputFile;
+ private Map myUserData;
+
+ public GeneralCommandLine() { }
+
+ public GeneralCommandLine(@NonNls String... command) {
+ this(Arrays.asList(command));
+ }
+
+ public GeneralCommandLine(@NonNls @NotNull List command) {
+ int size = command.size();
+ if (size > 0) {
+ setExePath(command.get(0));
+ if (size > 1) {
+ addParameters(command.subList(1, size));
+ }
+ }
+ }
+
+ protected GeneralCommandLine(@NotNull GeneralCommandLine original) {
+ myExePath = original.myExePath;
+ myWorkDirectory = original.myWorkDirectory;
+ myEnvParams.putAll(original.myEnvParams);
+ myParentEnvironmentType = original.myParentEnvironmentType;
+ original.myProgramParams.copyTo(myProgramParams);
+ myCharset = original.myCharset;
+ myRedirectErrorStream = original.myRedirectErrorStream;
+ myInputFile = original.myInputFile;
+ myUserData = null; // user data should not be copied over
+ }
+
+ private static Charset defaultCharset() {
+ return LoadingState.COMPONENTS_LOADED.isOccurred() ? EncodingManager.getInstance().getDefaultConsoleEncoding() : Charset.defaultCharset();
+ }
+
+ public @NotNull @NlsSafe String getExePath() {
+ return myExePath;
+ }
+
+ public @NotNull GeneralCommandLine withExePath(@NotNull @NlsSafe String exePath) {
+ myExePath = exePath.trim();
+ return this;
+ }
+
+ public void setExePath(@NotNull @NlsSafe String exePath) {
+ withExePath(exePath);
+ }
+
+ public File getWorkDirectory() {
+ return myWorkDirectory;
+ }
+
+ @NotNull
+ public GeneralCommandLine withWorkDirectory(@Nullable String path) {
+ return withWorkDirectory(path != null ? new File(path) : null);
+ }
+
+ @NotNull
+ public GeneralCommandLine withWorkDirectory(@Nullable File workDirectory) {
+ myWorkDirectory = workDirectory;
+ return this;
+ }
+
+ public void setWorkDirectory(@Nullable String path) {
+ withWorkDirectory(path);
+ }
+
+ public void setWorkDirectory(@Nullable File workDirectory) {
+ withWorkDirectory(workDirectory);
+ }
+
+ /**
+ * Note: the map returned is forgiving to passing null values into putAll().
+ */
+ @NotNull
+ public Map getEnvironment() {
+ return myEnvParams;
+ }
+
+ @NotNull
+ public GeneralCommandLine withEnvironment(@Nullable Map environment) {
+ if (environment != null) {
+ getEnvironment().putAll(environment);
+ }
+ return this;
+ }
+
+ @NotNull
+ public GeneralCommandLine withEnvironment(@NonNls @NotNull String key, @NonNls @NotNull String value) {
+ getEnvironment().put(key, value);
+ return this;
+ }
+
+ public boolean isPassParentEnvironment() {
+ return myParentEnvironmentType != ParentEnvironmentType.NONE;
+ }
+
+ /** @deprecated use {@link #withParentEnvironmentType(ParentEnvironmentType)} */
+ @Deprecated
+ public void setPassParentEnvironment(boolean passParentEnvironment) {
+ withParentEnvironmentType(passParentEnvironment ? ParentEnvironmentType.CONSOLE : ParentEnvironmentType.NONE);
+ }
+
+ @NotNull
+ public ParentEnvironmentType getParentEnvironmentType() {
+ return myParentEnvironmentType;
+ }
+
+ @NotNull
+ public GeneralCommandLine withParentEnvironmentType(@NotNull ParentEnvironmentType type) {
+ myParentEnvironmentType = type;
+ return this;
+ }
+
+ /**
+ * Returns an environment that will be inherited by a child process.
+ * @see #getEffectiveEnvironment()
+ */
+ @NotNull
+ public Map getParentEnvironment() {
+ switch (myParentEnvironmentType) {
+ case SYSTEM:
+ return System.getenv();
+ case CONSOLE:
+ return EnvironmentUtil.getEnvironmentMap();
+ default:
+ return Collections.emptyMap();
+ }
+ }
+
+ /**
+ * Returns an environment as seen by a child process,
+ * that is the {@link #getEnvironment() environment} merged with the {@link #getParentEnvironment() parent} one.
+ */
+ @NotNull
+ public Map getEffectiveEnvironment() {
+ Map env = new MyMap();
+ setupEnvironment(env);
+ return env;
+ }
+
+ public void addParameters(@NonNls String... parameters) {
+ withParameters(parameters);
+ }
+
+ public void addParameters(@NotNull List parameters) {
+ withParameters(parameters);
+ }
+
+ @NotNull
+ public GeneralCommandLine withParameters(@NotNull @NonNls String... parameters) {
+ for (String parameter : parameters) addParameter(parameter);
+ return this;
+ }
+
+ @NotNull
+ public GeneralCommandLine withParameters(@NotNull List parameters) {
+ for (String parameter : parameters) addParameter(parameter);
+ return this;
+ }
+
+ public void addParameter(@NonNls @NotNull String parameter) {
+ myProgramParams.add(parameter);
+ }
+
+ @NotNull
+ public ParametersList getParametersList() {
+ return myProgramParams;
+ }
+
+ @NotNull
+ public Charset getCharset() {
+ return myCharset;
+ }
+
+ @NotNull
+ public GeneralCommandLine withCharset(@NotNull Charset charset) {
+ myCharset = charset;
+ return this;
+ }
+
+ public void setCharset(@NotNull Charset charset) {
+ withCharset(charset);
+ }
+
+ public boolean isRedirectErrorStream() {
+ return myRedirectErrorStream;
+ }
+
+ @NotNull
+ public GeneralCommandLine withRedirectErrorStream(boolean redirectErrorStream) {
+ myRedirectErrorStream = redirectErrorStream;
+ return this;
+ }
+
+ public void setRedirectErrorStream(boolean redirectErrorStream) {
+ withRedirectErrorStream(redirectErrorStream);
+ }
+
+ public File getInputFile() {
+ return myInputFile;
+ }
+
+ @NotNull
+ public GeneralCommandLine withInput(@Nullable File file) {
+ myInputFile = file;
+ return this;
+ }
+
+ /**
+ * Returns string representation of this command line.
+ * Warning: resulting string is not OS-dependent - do not use it for executing this command line.
+ *
+ * @return single-string representation of this command line.
+ */
+ @NlsSafe
+ @NotNull
+ public String getCommandLineString() {
+ return getCommandLineString(null);
+ }
+
+ /**
+ * Returns string representation of this command line.
+ * Warning: resulting string is not OS-dependent - do not use it for executing this command line.
+ *
+ * @param exeName use this executable name instead of given by {@link #setExePath(String)}
+ * @return single-string representation of this command line.
+ */
+ @NotNull
+ public String getCommandLineString(@Nullable String exeName) {
+ return ParametersListUtil.join(getCommandLineList(exeName));
+ }
+
+ @NotNull
+ public List getCommandLineList(@Nullable String exeName) {
+ List<@NlsSafe String> commands = new ArrayList<>();
+ String exe = StringUtil.notNullize(exeName, StringUtil.notNullize(myExePath, ""));
+ commands.add(exe);
+
+ commands.addAll(myProgramParams.getList());
+ return commands;
+ }
+
+ /**
+ * Prepares command (quotes and escapes all arguments) and returns it as a newline-separated list.
+ *
+ * @return command as a newline-separated list.
+ * @see #getPreparedCommandLine(Platform)
+ */
+ @NotNull
+ public String getPreparedCommandLine() {
+ return getPreparedCommandLine(Platform.current());
+ }
+
+ /**
+ * Prepares command (quotes and escapes all arguments) and returns it as a newline-separated list
+ * (suitable e.g. for passing in an environment variable).
+ *
+ * @param platform a target platform
+ * @return command as a newline-separated list.
+ */
+ @NotNull
+ public String getPreparedCommandLine(@NotNull Platform platform) {
+ String exePath = myExePath != null ? myExePath : "";
+ return StringUtil.join(prepareCommandLine(exePath, myProgramParams.getList(), platform), "\n");
+ }
+
+ @NotNull
+ protected List prepareCommandLine(@NotNull String command, @NotNull List parameters, @NotNull Platform platform) {
+ return CommandLineUtil.toCommandLine(command, parameters, platform);
+ }
+
+ @NotNull
+ public Process createProcess() throws ExecutionException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Executing [" + getCommandLineString() + "]");
+ LOG.debug(" environment: " + myEnvParams + " (+" + myParentEnvironmentType + ")");
+ LOG.debug(" charset: " + myCharset);
+ }
+
+ List commands = validateAndPrepareCommandLine();
+ try {
+ return startProcess(commands);
+ }
+ catch (IOException e) {
+ LOG.debug(e);
+ throw new ProcessNotCreatedException(e.getMessage(), e, this);
+ }
+ }
+
+ public @NotNull ProcessBuilder toProcessBuilder() throws ExecutionException {
+ List escapedCommands = validateAndPrepareCommandLine();
+ return toProcessBuilderInternal(escapedCommands);
+ }
+
+ @NotNull
+ private List validateAndPrepareCommandLine() throws ExecutionException {
+ try {
+ if (myWorkDirectory != null) {
+ if (!myWorkDirectory.exists()) {
+ throw new ExecutionException(myWorkDirectory + " does not exist");
+ }
+ if (!myWorkDirectory.isDirectory()) {
+ throw new ExecutionException(myWorkDirectory + " is not a directory");
+ }
+ }
+
+ if (StringUtil.isEmptyOrSpaces(myExePath)) {
+ throw new ExecutionException("executable not specified");
+ }
+ }
+ catch (ExecutionException e) {
+ LOG.debug(e);
+ throw e;
+ }
+
+ for (Map.Entry entry : myEnvParams.entrySet()) {
+ String name = entry.getKey();
+ String value = entry.getValue();
+ if (!EnvironmentUtil.isValidName(name)) throw new IllegalEnvVarException("run.configuration.invalid.env.name: " + name);
+ if (!EnvironmentUtil.isValidValue(value)) throw new IllegalEnvVarException("run.configuration.invalid.env.value: " + name + ", " + value);
+ }
+
+ String exePath = myExePath;
+ if (SystemInfoRt.isMac && myParentEnvironmentType == ParentEnvironmentType.CONSOLE && exePath.indexOf(File.separatorChar) == -1) {
+ String systemPath = System.getenv("PATH");
+ String shellPath = EnvironmentUtil.getValue("PATH");
+ if (!Objects.equals(systemPath, shellPath)) {
+ File exeFile = PathEnvironmentVariableUtil.findInPath(myExePath, shellPath, null);
+ if (exeFile != null) {
+ LOG.debug(exePath + " => " + exeFile);
+ exePath = exeFile.getPath();
+ }
+ }
+ }
+
+ return prepareCommandLine(exePath, myProgramParams.getList(), Platform.current());
+ }
+
+ /**
+ * @implNote for subclasses:
+ * On Windows the escapedCommands argument must never be modified or augmented in any way.
+ * Windows command line handling is extremely fragile and vague, and the exact escaping of a particular argument may vary
+ * depending on values of the preceding arguments.
+ *
+ * [foo] [^] -> [foo] [^^]
+ *
+ * but:
+ *
+ * [foo] ["] [^] -> [foo] [\"] ["^"]
+ *
+ * Notice how the last parameter escaping changes after prepending another argument.
+ * If you need to alter the command line passed in, override the {@link #prepareCommandLine(String, List, Platform)} method instead.
+ */
+ @NotNull
+ protected Process startProcess(@NotNull List escapedCommands) throws IOException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Building process with commands: " + escapedCommands);
+ }
+ return toProcessBuilderInternal(escapedCommands).start();
+ }
+
+ // This is caused by the fact there are external usages overriding startProcess(List).
+ // Ideally, it should have been startProcess(ProcessBuilder), and the design would be more straightforward.
+ @NotNull
+ private ProcessBuilder toProcessBuilderInternal(@NotNull List escapedCommands) {
+ ProcessBuilder builder = new ProcessBuilder(escapedCommands);
+ setupEnvironment(builder.environment());
+ builder.directory(myWorkDirectory);
+ builder.redirectErrorStream(myRedirectErrorStream);
+ if (myInputFile != null) {
+ builder.redirectInput(ProcessBuilder.Redirect.from(myInputFile));
+ }
+ return buildProcess(builder);
+ }
+
+ /**
+ * Executed with pre-filled ProcessBuilder as the param and
+ * gives the last chance to configure starting process
+ * parameters before a process is started
+ * @param builder filed ProcessBuilder
+ */
+ @NotNull
+ protected ProcessBuilder buildProcess(@NotNull ProcessBuilder builder) {
+ return builder;
+ }
+
+ protected void setupEnvironment(@NotNull Map environment) {
+ environment.clear();
+
+ if (myParentEnvironmentType != ParentEnvironmentType.NONE) {
+ environment.putAll(getParentEnvironment());
+ }
+
+ if (SystemInfoRt.isUnix) {
+ File workDirectory = getWorkDirectory();
+ if (workDirectory != null) {
+ environment.put("PWD", FileUtil.toSystemDependentName(workDirectory.getAbsolutePath()));
+ }
+ }
+
+ if (!myEnvParams.isEmpty()) {
+ if (SystemInfoRt.isWindows) {
+ Map envVars = CollectionFactory.createCaseInsensitiveStringMap();
+ envVars.putAll(environment);
+ envVars.putAll(myEnvParams);
+ environment.clear();
+ environment.putAll(envVars);
+ }
+ else {
+ environment.putAll(myEnvParams);
+ }
+ }
+ }
+
+ /**
+ * Normally, double quotes in parameters are escaped, so they arrive to a called program as-is.
+ * But some commands (e.g. {@code 'cmd /c start "title" ...'}) should get their quotes non-escaped.
+ * Wrapping a parameter by this method (instead of using quotes) will do exactly this.
+ *
+ * @see com.intellij.execution.util.ExecUtil#getTerminalCommand(String, String)
+ */
+ @NotNull
+ public static String inescapableQuote(@NotNull String parameter) {
+ return CommandLineUtil.specialQuote(parameter);
+ }
+
+ @Override
+ public String toString() {
+ return myExePath + " " + myProgramParams;
+ }
+
+ @Nullable
+ @Override
+ public T getUserData(@NotNull Key key) {
+ if (myUserData == null) return null;
+ @SuppressWarnings("unchecked") T t = (T)myUserData.get(key);
+ return t;
+ }
+
+ @Override
+ public void putUserData(@NotNull Key key, @Nullable T value) {
+ if (myUserData == null) {
+ if (value == null) return;
+ myUserData = new HashMap<>();
+ }
+ myUserData.put(key, value);
+ }
+
+ private static final class MyMap extends Object2ObjectOpenCustomHashMap {
+ private MyMap() {
+ super(FastUtilHashingStrategies.getStringStrategy(!SystemInfoRt.isWindows));
+ }
+
+ @Override
+ public void putAll(Map extends String, ? extends String> map) {
+ if (map != null) {
+ super.putAll(map);
+ }
+ }
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/configurations/ParametersList.java b/compiler/tests-common/tests/com/intellij/execution/configurations/ParametersList.java
new file mode 100644
index 00000000000..631b31268b4
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/configurations/ParametersList.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution.configurations;
+
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathMacros;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.NlsSafe;
+import com.intellij.openapi.util.NotNullLazyValue;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ArrayUtilRt;
+import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.CollectionFactory;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.JBIterable;
+import com.intellij.util.execution.ParametersListUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class ParametersList implements Cloneable {
+ private static final Pattern PROPERTY_PATTERN = Pattern.compile("-D(\\S+?)(?:=(.+))?");
+ private static final Pattern MACRO_PATTERN = Pattern.compile("\\$\\{([^}]+)}");
+
+ private final List myParameters = new ArrayList<>();
+ private final List myGroups = new SmartList<>();
+ private final NotNullLazyValue> myMacroMap = NotNullLazyValue.createValue(ParametersList::computeMacroMap);
+
+ public boolean hasParameter(@NotNull @NonNls String parameter) {
+ return myParameters.contains(parameter);
+ }
+
+ public boolean hasProperty(@NotNull @NonNls String propertyName) {
+ return getPropertyValue(propertyName) != null;
+ }
+
+ @Nullable
+ public String getPropertyValue(@NotNull @NonNls String propertyName) {
+ String exact = "-D" + propertyName;
+ String prefix = "-D" + propertyName + "=";
+ int index = indexOfParameter(o -> o.equals(exact) || o.startsWith(prefix));
+ if (index < 0) return null;
+ String str = myParameters.get(index);
+ return str.length() == exact.length() ? "" : str.substring(prefix.length());
+ }
+
+ @NotNull
+ public Map getProperties() {
+ return getProperties("");
+ }
+
+ @NotNull
+ public Map getProperties(@NonNls String valueIfMissing) {
+ Map result = new LinkedHashMap<>();
+ JBIterable matchers = JBIterable.from(myParameters).map(PROPERTY_PATTERN::matcher).filter(Matcher::matches);
+ for (Matcher matcher : matchers) {
+ result.put(matcher.group(1), StringUtil.notNullize(matcher.group(2), valueIfMissing));
+ }
+ return result;
+ }
+
+ @NotNull
+ public String getParametersString() {
+ return join(getList());
+ }
+
+ public String [] getArray() {
+ return ArrayUtilRt.toStringArray(getList());
+ }
+
+ @NotNull
+ public List getList() {
+ if (myGroups.isEmpty()) {
+ return Collections.unmodifiableList(myParameters);
+ }
+
+ List params = new ArrayList<>(myParameters);
+ for (ParamsGroup group : myGroups) {
+ params.addAll(group.getParameters());
+ }
+ return Collections.unmodifiableList(params);
+ }
+
+ public void clearAll() {
+ myParameters.clear();
+ myGroups.clear();
+ }
+
+ public void prepend(@NotNull @NonNls String parameter) {
+ addAt(0, parameter);
+ }
+
+ public void prependAll(@NonNls String ... parameter) {
+ addAll(parameter);
+ Collections.rotate(myParameters, parameter.length);
+ }
+
+ public void addParametersString(@Nullable @NonNls String parameters) {
+ if (StringUtil.isEmptyOrSpaces(parameters)) return;
+ for (String param : parse(parameters)) {
+ add(param);
+ }
+ }
+
+ public void add(@Nullable @NonNls String parameter) {
+ if (parameter == null) return;
+ myParameters.add(expandMacros(parameter));
+ }
+
+ @NotNull
+ public ParamsGroup addParamsGroup(@NotNull @NonNls String groupId) {
+ return addParamsGroup(new ParamsGroup(groupId));
+ }
+
+ @NotNull
+ public ParamsGroup addParamsGroup(@NotNull ParamsGroup group) {
+ myGroups.add(group);
+ return group;
+ }
+
+ @NotNull
+ public ParamsGroup addParamsGroupAt(int index, @NotNull ParamsGroup group) {
+ myGroups.add(index, group);
+ return group;
+ }
+
+ @NotNull
+ public ParamsGroup addParamsGroupAt(int index, @NotNull @NonNls String groupId) {
+ ParamsGroup group = new ParamsGroup(groupId);
+ myGroups.add(index, group);
+ return group;
+ }
+
+ public int getParamsGroupsCount() {
+ return myGroups.size();
+ }
+
+ @NotNull
+ public List getParameters() {
+ return Collections.unmodifiableList(myParameters);
+ }
+
+ @NotNull
+ public List getParamsGroups() {
+ return Collections.unmodifiableList(myGroups);
+ }
+
+ @NotNull
+ public ParamsGroup getParamsGroupAt(int index) {
+ return myGroups.get(index);
+ }
+
+ @Nullable
+ public ParamsGroup getParamsGroup(@NotNull @NonNls String name) {
+ for (ParamsGroup group : myGroups) {
+ if (name.equals(group.getId())) return group;
+ }
+ return null;
+ }
+
+ @Nullable
+ public ParamsGroup removeParamsGroup(int index) {
+ return myGroups.remove(index);
+ }
+
+ public void addAt(int index, @NotNull @NonNls String parameter) {
+ myParameters.add(index, expandMacros(parameter));
+ }
+
+ /**
+ * Keeps the {@code } property if defined; or defines it with {@code System.getProperty()} as a value if present.
+ */
+ public void defineSystemProperty(@NotNull @NonNls String propertyName) {
+ defineProperty(propertyName, System.getProperty(propertyName));
+ }
+
+ /**
+ * Keeps the {@code } property if defined; otherwise appends the new one ignoring null values.
+ */
+ public void defineProperty(@NotNull @NonNls String propertyName, @Nullable @NonNls String propertyValue) {
+ if (propertyValue == null) return;
+ @NlsSafe String exact = "-D" + propertyName;
+ @NlsSafe String prefix = "-D" + propertyName + "=";
+ int index = indexOfParameter(o -> o.equals(exact) || o.startsWith(prefix));
+ if (index > -1) return;
+ String value = propertyValue.isEmpty() ? exact : prefix + expandMacros(propertyValue);
+ myParameters.add(value);
+ }
+
+ /**
+ * Adds {@code -D} to the list; replaces the value of the last property if defined.
+ */
+ public void addProperty(@NotNull @NonNls String propertyName) {
+ @NlsSafe String exact = "-D" + propertyName;
+ @NlsSafe String prefix = "-D" + propertyName + "=";
+ replaceOrAddAt(exact, myParameters.size(), o -> o.equals(exact) || o.startsWith(prefix));
+ }
+
+ /**
+ * Adds {@code -D=} to the list ignoring null values;
+ * replaces the value of the last property if defined.
+ */
+ public void addProperty(@NotNull @NonNls String propertyName, @Nullable @NonNls String propertyValue) {
+ if (propertyValue == null) return;
+ @NlsSafe String exact = "-D" + propertyName;
+ @NlsSafe String prefix = "-D" + propertyName + "=";
+ String value = propertyValue.isEmpty() ? exact : prefix + expandMacros(propertyValue);
+ replaceOrAddAt(value, myParameters.size(), o -> o.equals(exact) || o.startsWith(prefix));
+ }
+
+ /**
+ * Adds {@code -D=} to the list ignoring null, empty and spaces-only values;
+ * replaces the value of the last property if defined.
+ */
+ public void addNotEmptyProperty(@NotNull @NonNls String propertyName, @Nullable @NonNls String propertyValue) {
+ if (StringUtil.isEmptyOrSpaces(propertyValue)) return;
+ addProperty(propertyName, propertyValue);
+ }
+
+ /**
+ * Replaces the last parameter that starts with the {@code } with {@code };
+ * otherwise appends {@code } to the list.
+ */
+ public void replaceOrAppend(@NotNull @NonNls String parameterPrefix, @NotNull @NonNls String replacement) {
+ replaceOrAddAt(expandMacros(replacement), myParameters.size(), o -> o.startsWith(parameterPrefix));
+ }
+
+ /**
+ * Replaces the last parameter that starts with the {@code } with {@code };
+ * otherwise prepends this list with {@code }.
+ */
+ public void replaceOrPrepend(@NotNull @NonNls String parameterPrefix, @NotNull @NonNls String replacement) {
+ replaceOrAddAt(expandMacros(replacement), 0, o -> o.startsWith(parameterPrefix));
+ }
+
+ private void replaceOrAddAt(@NotNull String replacement,
+ int position,
+ @NotNull Condition super String> existingCondition) {
+ int index = indexOfParameter(existingCondition);
+ boolean setNewValue = StringUtil.isNotEmpty(replacement);
+ if (index > -1 && setNewValue) {
+ myParameters.set(index, replacement);
+ }
+ else if (index > -1) {
+ myParameters.remove(index);
+ }
+ else if (setNewValue) {
+ myParameters.add(position, replacement);
+ }
+ }
+
+ private int indexOfParameter(@NotNull @NonNls Condition super String> condition) {
+ return ContainerUtil.lastIndexOf(myParameters, condition);
+ }
+
+ public void set(int ind, @NotNull @NonNls String value) {
+ myParameters.set(ind, value);
+ }
+
+ public String get(int ind) {
+ return myParameters.get(ind);
+ }
+
+ @Nullable
+ public String getLast() {
+ return myParameters.size() > 0 ? myParameters.get(myParameters.size() - 1) : null;
+ }
+
+ public void add(@NotNull @NonNls String name, @NotNull @NonNls String value) {
+ myParameters.add(name); // do not expand macros in parameter name
+ add(value);
+ }
+
+ public void addAll(@NonNls String ... parameters) {
+ addAll(Arrays.asList(parameters));
+ }
+
+ public void addAll(@NotNull @NonNls List parameters) {
+ // Don't use myParameters.addAll(parameters) , it does not call expandMacros(parameter)
+ for (String parameter : parameters) {
+ add(parameter);
+ }
+ }
+
+ /** @noinspection MethodDoesntCallSuperMethod*/
+ @Override
+ public ParametersList clone() {
+ return copyTo(new ParametersList());
+ }
+
+ @NotNull
+ public ParametersList copyTo(@NotNull ParametersList target) {
+ target.myParameters.addAll(myParameters);
+ for (ParamsGroup group : myGroups) {
+ target.myGroups.add(group.clone());
+ }
+ return target;
+ }
+
+ /**
+ * @see ParametersListUtil#join(List)
+ */
+ @NotNull
+ public static String join(@NotNull @NonNls List parameters) {
+ return ParametersListUtil.join(parameters);
+ }
+
+ /**
+ * @see ParametersListUtil#join(List)
+ */
+ @NotNull
+ public static String join(@NonNls String ... parameters) {
+ return ParametersListUtil.join(parameters);
+ }
+
+ /**
+ * @see ParametersListUtil#parseToArray(String)
+ */
+ public static String [] parse(@NotNull @NonNls String string) {
+ return ParametersListUtil.parseToArray(string);
+ }
+
+ @NotNull
+ public String expandMacros(@NotNull @NonNls String text) {
+ int start = text.indexOf("${");
+ if (start < 0) return text;
+ Map macroMap = myMacroMap.getValue();
+ Matcher matcher = MACRO_PATTERN.matcher(text);
+ StringBuilder sb = null;
+ while (matcher.find(start)) {
+ String value = macroMap.get(matcher.group(1));
+ if (value != null) {
+ if (sb == null) sb = new StringBuilder(2 * text.length()).append(text, 0, matcher.start());
+ else sb.append(text, start, matcher.start());
+ sb.append(value);
+ start = matcher.end();
+ }
+ else {
+ if (sb != null) sb.append(text, start, matcher.start() + 2);
+ start = matcher.start() + 2;
+ }
+ }
+ return sb == null ? text : sb.append(text, start, text.length()).toString();
+ }
+
+ private static Map ourTestMacros;
+
+ @TestOnly
+ public static void setTestMacros(@Nullable @NonNls Map testMacros) {
+ ourTestMacros = testMacros;
+ }
+
+ @NotNull
+ private static Map computeMacroMap() {
+ // ApplicationManager.getApplication() will return null if executed in ParameterListTest
+ Application application = ApplicationManager.getApplication();
+ if (application == null || application.isUnitTestMode() && ourTestMacros != null) {
+ return ObjectUtils.notNull(ourTestMacros, Collections.emptyMap());
+ }
+
+ Map map = CollectionFactory.createCaseInsensitiveStringMap();
+ Map pathMacros = PathMacros.getInstance().getUserMacros();
+ if (!pathMacros.isEmpty()) {
+ for (String name : pathMacros.keySet()) {
+ ContainerUtil.putIfNotNull(name, pathMacros.get(name), map);
+ }
+ }
+ Map env = EnvironmentUtil.getEnvironmentMap();
+ for (String name : env.keySet()) {
+ ContainerUtil.putIfAbsent(name, env.get(name), map);
+ }
+ return map;
+ }
+
+ @NonNls
+ @Override
+ public String toString() {
+ return myParameters + (myGroups.isEmpty() ? "" : " and " + myGroups);
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/configurations/ParamsGroup.java b/compiler/tests-common/tests/com/intellij/execution/configurations/ParamsGroup.java
new file mode 100644
index 00000000000..474cfe1050d
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/configurations/ParamsGroup.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution.configurations;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public final class ParamsGroup implements Cloneable {
+
+ private final String myGroupId;
+ private final ParametersList myParamList;
+
+ public ParamsGroup(@NotNull String groupId) {
+ this(groupId, new ParametersList());
+ }
+
+ private ParamsGroup(@NotNull String groupId, @NotNull ParametersList paramList) {
+ myGroupId = groupId;
+ myParamList = paramList;
+ }
+
+ @NotNull
+ public String getId() {
+ return myGroupId;
+ }
+
+ public void addParameter(@NotNull String parameter) {
+ myParamList.add(parameter);
+ }
+
+ public void addParameterAt(int index, @NotNull String parameter) {
+ myParamList.addAt(index, parameter);
+ }
+
+ public void addParameters(@NotNull String ... parameters) {
+ for (String parameter : parameters) {
+ addParameter(parameter);
+ }
+ }
+
+ public void addParameters(@NotNull List parameters) {
+ for (String parameter : parameters) {
+ addParameter(parameter);
+ }
+ }
+
+ public void addParametersString(@NotNull String parametersString) {
+ addParameters(ParametersList.parse(parametersString));
+ }
+
+ public List getParameters() {
+ return myParamList.getList();
+ }
+
+ public ParametersList getParametersList() {
+ return myParamList;
+ }
+
+ /** @noinspection MethodDoesntCallSuperMethod*/
+ @Override
+ public ParamsGroup clone() {
+ return new ParamsGroup(myGroupId, myParamList.clone());
+ }
+
+ @Override
+ public String toString() {
+ return myGroupId + ":" + myParamList;
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java b/compiler/tests-common/tests/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java
new file mode 100644
index 00000000000..63a5bfebb5b
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution.configurations;
+
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public final class PathEnvironmentVariableUtil {
+
+ private static final String PATH = "PATH";
+
+ private PathEnvironmentVariableUtil() { }
+
+ /**
+ * Finds an executable file with the specified base name, that is located in a directory
+ * listed in PATH environment variable.
+ *
+ * @param fileBaseName file base name
+ * @return {@link File} instance or null if not found
+ */
+ @Nullable
+ public static File findInPath(@NotNull @NonNls String fileBaseName) {
+ return findInPath(fileBaseName, null);
+ }
+
+ /**
+ * Finds an executable file with the specified base name, that is located in a directory
+ * listed in PATH environment variable and is accepted by filter.
+ *
+ * @param fileBaseName file base name
+ * @param filter exe file filter
+ * @return {@link File} instance or null if not found
+ */
+ @Nullable
+ public static File findInPath(@NotNull String fileBaseName, @Nullable FileFilter filter) {
+ return findInPath(fileBaseName, getPathVariableValue(), filter);
+ }
+
+ /**
+ * Finds an executable file with the specified base name, that is located in a directory
+ * listed in the passed PATH environment variable value and is accepted by filter.
+ *
+ * @param fileBaseName file base name
+ * @param pathVariableValue value of PATH environment variable
+ * @param filter exe file filter
+ * @return {@link File} instance or null if not found
+ */
+ @Nullable
+ public static File findInPath(@NotNull String fileBaseName, @Nullable String pathVariableValue, @Nullable FileFilter filter) {
+ List exeFiles = findExeFilesInPath(true, filter, pathVariableValue, fileBaseName);
+ return ContainerUtil.getFirstItem(exeFiles);
+ }
+
+ /**
+ * Finds all executable files with the specified base name, that are located in directories
+ * from PATH environment variable.
+ *
+ * @param fileBaseName file base name
+ * @return file list
+ */
+ @NotNull
+ public static List findAllExeFilesInPath(@NotNull String fileBaseName) {
+ return findAllExeFilesInPath(fileBaseName, null);
+ }
+
+ @NotNull
+ public static List findAllExeFilesInPath(@NotNull String fileBaseName, @Nullable FileFilter filter) {
+ return findExeFilesInPath(false, filter, getPathVariableValue(), fileBaseName);
+ }
+
+ @NotNull
+ private static List findExeFilesInPath(boolean stopAfterFirstMatch,
+ @Nullable FileFilter filter,
+ @Nullable String pathEnvVarValue,
+ String... fileBaseNames) {
+ if (pathEnvVarValue == null) {
+ return Collections.emptyList();
+ }
+ List result = new SmartList<>();
+ List dirPaths = getPathDirs(pathEnvVarValue);
+ for (String dirPath : dirPaths) {
+ File dir = new File(dirPath);
+ if (dir.isAbsolute() && dir.isDirectory()) {
+ for (String fileBaseName : fileBaseNames) {
+ File exeFile = new File(dir, fileBaseName);
+ if (exeFile.isFile() && exeFile.canExecute()) {
+ if (filter == null || filter.accept(exeFile)) {
+ result.add(exeFile);
+ if (stopAfterFirstMatch) {
+ return result;
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @NotNull
+ public static List getPathDirs(@NotNull String pathEnvVarValue) {
+ return StringUtil.split(pathEnvVarValue, File.pathSeparator, true, true);
+ }
+
+ @NotNull
+ public static List getWindowsExecutableFileExtensions() {
+ if (SystemInfo.isWindows) {
+ String allExtensions = System.getenv("PATHEXT");
+ if (allExtensions != null) {
+ Collection extensions = StringUtil.split(allExtensions, ";", true, true);
+ extensions = ContainerUtil.filter(extensions, s -> !StringUtil.isEmpty(s) && s.startsWith("."));
+ return ContainerUtil.map2List(extensions, s -> StringUtil.toLowerCase(s));
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @NotNull
+ public static String findExecutableInWindowsPath(@NotNull String exePath) {
+ return findExecutableInWindowsPath(exePath, exePath);
+ }
+
+ @Contract("_, !null -> !null")
+ public static String findExecutableInWindowsPath(@NotNull String exePath, @Nullable String defaultPath) {
+ if (SystemInfo.isWindows) {
+ if (!StringUtil.containsChar(exePath, '/') && !StringUtil.containsChar(exePath, '\\')) {
+ List executableFileExtensions = getWindowsExecutableFileExtensions();
+
+ String[] baseNames = ContainerUtil.map2Array(executableFileExtensions, String.class, s -> exePath + s);
+ List exeFiles = findExeFilesInPath(true, null, getPathVariableValue(), baseNames);
+ File foundFile = ContainerUtil.getFirstItem(exeFiles);
+ if (foundFile != null) {
+ return foundFile.getAbsolutePath();
+ }
+ }
+ }
+ return defaultPath;
+ }
+
+ /**
+ * Retrieves the value of PATH environment variable
+ */
+ @Nullable
+ public static String getPathVariableValue() {
+ return EnvironmentUtil.getValue(PATH);
+ }
+
+ @Nullable
+ public static File findExecutableInPathOnAnyOS(@NotNull @NonNls String fileBaseName) {
+ if (SystemInfo.isWindows) {
+ String[] fileNames = ContainerUtil.map2Array(getWindowsExecutableFileExtensions(), String.class,
+ (String extension) -> fileBaseName + extension);
+ List exeFiles = findExeFilesInPath(true, null, getPathVariableValue(), fileNames);
+ return ContainerUtil.getFirstItem(exeFiles);
+ }
+ else {
+ return findInPath(fileBaseName);
+ }
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/process/OSProcessHandler.java b/compiler/tests-common/tests/com/intellij/execution/process/OSProcessHandler.java
new file mode 100644
index 00000000000..bed5cecb423
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/process/OSProcessHandler.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution.process;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.EmptyProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.ExceptionUtil;
+import com.intellij.util.io.BaseDataReader;
+import com.intellij.util.io.BaseOutputReader;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class OSProcessHandler extends BaseOSProcessHandler {
+ private static final Logger LOG = Logger.getInstance(OSProcessHandler.class);
+ private static final Set REPORTED_EXECUTIONS = Collections.newSetFromMap(new ConcurrentHashMap<>());
+ private static final long ALLOWED_TIMEOUT_THRESHOLD = 10;
+
+ private static final Key> DELETE_FILES_ON_TERMINATION = Key.create("OSProcessHandler.FileToDelete");
+
+ private final boolean myHasErrorStream;
+ @NotNull
+ private final ModalityState myModality;
+ private boolean myHasPty;
+ private final Set extends File> myFilesToDelete;
+
+ public OSProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
+ super(startProcess(commandLine), commandLine.getCommandLineString(), commandLine.getCharset());
+
+ setHasPty(isPtyProcess(getProcess()));
+ myHasErrorStream = !commandLine.isRedirectErrorStream();
+ myFilesToDelete = commandLine.getUserData(DELETE_FILES_ON_TERMINATION);
+ myModality = getDefaultModality();
+ }
+
+ @NotNull
+ public static ModalityState getDefaultModality() {
+ Application app = ApplicationManager.getApplication();
+ return app == null ? ModalityState.NON_MODAL : app.getDefaultModalityState();
+ }
+
+ /**
+ * {@code commandLine} must not be empty (for correct thread attribution in the stacktrace)
+ */
+ public OSProcessHandler(@NotNull Process process, /*@NotNull*/ String commandLine, @Nullable Charset charset) {
+ this(process, commandLine, charset, null);
+ }
+
+ /**
+ * {@code commandLine} must not be empty (for correct thread attribution in the stacktrace)
+ */
+ private OSProcessHandler(
+ @NotNull Process process, /*@NotNull*/
+ String commandLine,
+ @Nullable Charset charset,
+ @Nullable Set extends File> filesToDelete
+ ) {
+ super(process, commandLine, charset);
+ setHasPty(isPtyProcess(process));
+ myFilesToDelete = filesToDelete;
+ myHasErrorStream = true;
+ myModality = getDefaultModality();
+ }
+
+ @NotNull
+ private static Process startProcess(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
+ try {
+ return commandLine.createProcess();
+ }
+ catch (Throwable e) {
+ deleteTempFiles(commandLine.getUserData(DELETE_FILES_ON_TERMINATION));
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean waitFor() {
+ checkEdtAndReadAction(this);
+ return super.waitFor();
+ }
+
+ @Override
+ public boolean waitFor(long timeoutInMilliseconds) {
+ if (timeoutInMilliseconds > ALLOWED_TIMEOUT_THRESHOLD) {
+ checkEdtAndReadAction(this);
+ }
+ return super.waitFor(timeoutInMilliseconds);
+ }
+
+ /**
+ * Checks if we are going to wait for {@code processHandler} to finish on EDT or under ReadAction. Logs error if we do so.
+ *
+ * HOW-TO fix an error from this method:
+ *
+ * You are on the pooled thread under {@link com.intellij.openapi.application.ReadAction ReadAction}:
+ *
+ * Synchronous (you need to return execution result or derived information to the caller) - get rid the ReadAction or synchronicity.
+ * * Move execution part out of the code executed under ReadAction, or make your execution asynchronous - execute on
+ * * {@link Task.Backgroundable other thread} and invoke a callback.
+ * Non-synchronous (you don't need to return something) - execute on another thread. E.g. using {@link Task.Backgroundable}
+ *
+ *
+ *
+ * You are on EDT:
+ *
+ *
+ * Outside of {@link com.intellij.openapi.application.WriteAction WriteAction}:
+ *
+ * Synchronous (you need to return execution result or derived information to the caller) - execute under
+ * {@link ProgressManager#runProcessWithProgressSynchronously(Runnable, String, boolean, com.intellij.openapi.project.Project) modal progress}.
+ * Non-synchronous (you don't need to return something) - execute on the pooled thread. E.g. using {@link Task.Backgroundable}
+ *
+ *
+ *
+ * Under {@link com.intellij.openapi.application.WriteAction WriteAction}
+ *
+ * Synchronous (you need to return execution result or derived information to the caller) - get rid the WriteAction or synchronicity.
+ * Move execution part out of the code executed under WriteAction, or make your execution asynchronous - execute on
+ * {@link Task.Backgroundable other thread} and invoke a callback.
+ * Non-synchronous (you don't need to return something) - execute on the pooled thread. E.g. using {@link Task.Backgroundable}
+ *
+ *
+ *
+ *
+ * @apiNote works only in the internal non-headless mode. Reports once per running session per stacktrace per cause.
+ */
+ private static void checkEdtAndReadAction(@NotNull ProcessHandler processHandler) {
+ Application application = ApplicationManager.getApplication();
+ if (application == null || !application.isInternal() || application.isHeadlessEnvironment()) {
+ return;
+ }
+ @NonNls String message = null;
+ if (application.isDispatchThread()) {
+ message = "Synchronous execution on EDT: ";
+ }
+ else if (application.isReadAccessAllowed()) {
+ message = "Synchronous execution under ReadAction: ";
+ }
+ if (message != null && REPORTED_EXECUTIONS.add(ExceptionUtil.currentStackTrace())) {
+ LOG.error(message + processHandler + ", see com.intellij.execution.process.OSProcessHandler#checkEdtAndReadAction() Javadoc for resolutions");
+ }
+ }
+
+ private static void deleteTempFiles(Set extends File> tempFiles) {
+ if (tempFiles != null) {
+ try {
+ for (File file : tempFiles) {
+ FileUtil.delete(file);
+ }
+ }
+ catch (Throwable t) {
+ LOG.error("failed to delete temp. files", t);
+ }
+ }
+ }
+
+ private static boolean isPtyProcess(Process process) {
+ Class> c = process.getClass();
+ while (c != null) {
+ if ("com.pty4j.unix.UnixPtyProcess".equals(c.getName()) || "com.pty4j.windows.WinPtyProcess".equals(c.getName())) {
+ return true;
+ }
+ c = c.getSuperclass();
+ }
+ return false;
+ }
+
+ @Override
+ protected void onOSProcessTerminated(int exitCode) {
+ if (myModality != ModalityState.NON_MODAL) {
+ ProgressManager.getInstance().runProcess(() -> super.onOSProcessTerminated(exitCode), new EmptyProgressIndicator(myModality));
+ } else {
+ super.onOSProcessTerminated(exitCode);
+ }
+ deleteTempFiles(myFilesToDelete);
+ }
+
+ @Override
+ protected boolean processHasSeparateErrorStream() {
+ return myHasErrorStream;
+ }
+
+ @Override
+ protected void doDestroyProcess() {
+ getProcess().destroy();
+ }
+
+ /**
+ * In case of PTY this process handler will use blocking read because {@link InputStream#available()} doesn't work for Pty4j, and there
+ * is no reason to "disconnect" leaving PTY alive. See {@link BaseDataReader.SleepingPolicy} for more info.
+ *
+ * The value should be set before {@link #startNotify()} invocation.
+ * It is set by default in case of using GeneralCommandLine based constructor.
+ *
+ * @param hasPty {@code true} if process is PTY-based.
+ */
+ private void setHasPty(boolean hasPty) {
+ myHasPty = hasPty;
+ }
+
+ /**
+ * Rule of thumb: use {@link BaseOutputReader.Options#BLOCKING} for short-living process that you never want to "disconnect" from.
+ * See {@link BaseDataReader.SleepingPolicy} for the whole story.
+ */
+ @NotNull
+ @Override
+ protected BaseOutputReader.Options readerOptions() {
+ return myHasPty ? BaseOutputReader.Options.BLOCKING : super.readerOptions(); // blocking read in case of PTY-based process
+ }
+
+ /**
+ * Registers a file to delete after the given command line finishes.
+ * In order to have an effect, the command line has to be executed with {@link #OSProcessHandler(GeneralCommandLine)}.
+ */
+ public static void deleteFileOnTermination(@NotNull GeneralCommandLine commandLine, @NotNull File fileToDelete) {
+ Set set = commandLine.getUserData(DELETE_FILES_ON_TERMINATION);
+ if (set == null) {
+ set = new HashSet<>();
+ commandLine.putUserData(DELETE_FILES_ON_TERMINATION, set);
+ }
+ set.add(fileToDelete);
+ }
+
+ public static class Silent extends OSProcessHandler {
+ public Silent(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
+ super(commandLine);
+ }
+
+ @NotNull
+ @Override
+ protected BaseOutputReader.Options readerOptions() {
+ return BaseOutputReader.Options.forMostlySilentProcess();
+ }
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/execution/process/ProcessNotCreatedException.java b/compiler/tests-common/tests/com/intellij/execution/process/ProcessNotCreatedException.java
new file mode 100644
index 00000000000..17a4070c85b
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/execution/process/ProcessNotCreatedException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.execution.process;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.openapi.util.NlsContexts;
+
+public class ProcessNotCreatedException extends ExecutionException {
+ private final GeneralCommandLine myCommandLine;
+
+ public ProcessNotCreatedException(final @NlsContexts.DialogMessage String message, final GeneralCommandLine commandLine) {
+ super(message);
+ myCommandLine = commandLine;
+ }
+
+ public ProcessNotCreatedException(final @NlsContexts.DialogMessage String message, final Throwable cause, final GeneralCommandLine commandLine) {
+ super(message, cause);
+ myCommandLine = commandLine;
+ }
+
+ public GeneralCommandLine getCommandLine() {
+ return myCommandLine;
+ }
+}
diff --git a/compiler/tests-common/tests/com/intellij/testFramework/TestDataPath.java b/compiler/tests-common/tests/com/intellij/testFramework/TestDataPath.java
new file mode 100644
index 00000000000..e3a20a97f46
--- /dev/null
+++ b/compiler/tests-common/tests/com/intellij/testFramework/TestDataPath.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.testFramework;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface TestDataPath {
+ String value();
+}
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/integration/KotlinIntegrationTestBase.java b/compiler/tests-common/tests/org/jetbrains/kotlin/integration/KotlinIntegrationTestBase.java
index 4711def56eb..d52cc3fe357 100644
--- a/compiler/tests-common/tests/org/jetbrains/kotlin/integration/KotlinIntegrationTestBase.java
+++ b/compiler/tests-common/tests/org/jetbrains/kotlin/integration/KotlinIntegrationTestBase.java
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.utils.KotlinPaths;
import org.jetbrains.kotlin.utils.PathUtil;
import java.io.File;
-import java.io.IOException;
import java.util.regex.Pattern;
@WithMutedInDatabaseRunTest
@@ -97,7 +96,7 @@ public abstract class KotlinIntegrationTestBase extends TestCaseWithTmpdir {
return content;
}
- private void check(String testDataDir, String baseName, String content) throws IOException {
+ private void check(String testDataDir, String baseName, String content) {
File expectedFile = new File(testDataDir, baseName + ".expected");
String normalizedContent = normalizeOutput(new File(testDataDir), content);
@@ -162,7 +161,7 @@ public abstract class KotlinIntegrationTestBase extends TestCaseWithTmpdir {
}
@Override
- public void onTextAvailable(ProcessEvent event, Key outputType) {
+ public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
if (outputType == ProcessOutputTypes.STDERR) {
err.append(event.getText());
}
@@ -175,6 +174,6 @@ public abstract class KotlinIntegrationTestBase extends TestCaseWithTmpdir {
}
@Override
- public void processTerminated(ProcessEvent event) {}
+ public void processTerminated(@NotNull ProcessEvent event) {}
}
}
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java
index 2ceeca4ab3a..dd21ca107a5 100644
--- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java
+++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java
@@ -7,11 +7,7 @@ package org.jetbrains.kotlin.test;
import com.google.common.collect.Lists;
import com.intellij.openapi.Disposable;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -130,16 +126,6 @@ public class KotlinTestUtils {
CompilerConfiguration configuration = new CompilerConfiguration();
configuration.put(CommonConfigurationKeys.MODULE_NAME, TEST_MODULE_NAME);
- if ("true".equals(System.getProperty("kotlin.ni"))) {
- // Enable new inference for tests which do not declare their own language version settings
- CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, new CompilerTestLanguageVersionSettings(
- Collections.emptyMap(),
- LanguageVersionSettingsImpl.DEFAULT.getApiVersion(),
- LanguageVersionSettingsImpl.DEFAULT.getLanguageVersion(),
- Collections.emptyMap()
- ));
- }
-
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, new MessageCollector() {
@Override
public void clear() {
@@ -250,29 +236,6 @@ public class KotlinTestUtils {
JvmResolveUtil.analyze(ktFiles, environment);
}
- public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull Editor editor) {
- assertEqualsToFile(expectedFile, editor, true);
- }
-
- public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull Editor editor, Boolean enableSelectionTags) {
- Caret caret = editor.getCaretModel().getCurrentCaret();
- List tags = Lists.newArrayList(
- new TagsTestDataUtil.TagInfo<>(caret.getOffset(), true, "caret")
- );
-
- if (enableSelectionTags) {
- int selectionStart = caret.getSelectionStart();
- int selectionEnd = caret.getSelectionEnd();
-
- tags.add(new TagsTestDataUtil.TagInfo<>(selectionStart, true, "selection"));
- tags.add(new TagsTestDataUtil.TagInfo<>(selectionEnd, false, "selection"));
- }
-
- String afterText = TagsTestDataUtil.insertTagsInText(tags, editor.getDocument().getText(), (TagsTestDataUtil.TagInfo t) -> null);
-
- assertEqualsToFile(expectedFile, afterText);
- }
-
public static void assertEqualsToFile(@NotNull Path expectedFile, @NotNull String actual) {
assertEqualsToFile(expectedFile.toFile(), actual);
}
@@ -389,31 +352,6 @@ public class KotlinTestUtils {
return files;
}
- public static String getLastCommentedLines(@NotNull Document document) {
- List resultLines = new ArrayList<>();
- for (int i = document.getLineCount() - 1; i >= 0; i--) {
- int lineStart = document.getLineStartOffset(i);
- int lineEnd = document.getLineEndOffset(i);
- if (document.getCharsSequence().subSequence(lineStart, lineEnd).toString().trim().isEmpty()) {
- continue;
- }
-
- if ("//".equals(document.getCharsSequence().subSequence(lineStart, lineStart + 2).toString())) {
- resultLines.add(document.getCharsSequence().subSequence(lineStart + 2, lineEnd));
- }
- else {
- break;
- }
- }
- Collections.reverse(resultLines);
- StringBuilder result = new StringBuilder();
- for (CharSequence line : resultLines) {
- result.append(line).append("\n");
- }
- result.delete(result.length() - 1, result.length());
- return result.toString();
- }
-
public enum CommentType {
ALL,
LINE_COMMENT,
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/TagsTestDataUtil.java b/compiler/tests-common/tests/org/jetbrains/kotlin/test/TagsTestDataUtil.java
deleted file mode 100644
index 8ddb73f60e3..00000000000
--- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/TagsTestDataUtil.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright 2010-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.kotlin.test;
-
-import com.google.common.collect.Lists;
-import com.intellij.codeInsight.daemon.LineMarkerInfo;
-import com.intellij.codeInsight.daemon.impl.HighlightInfo;
-import com.intellij.lang.annotation.HighlightSeverity;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.util.text.StringUtil;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-public class TagsTestDataUtil {
- public static String insertInfoTags(List lineMarkers, boolean withDescription, String text) {
- List lineMarkerPoints = toLineMarkerTagPoints(lineMarkers, withDescription);
-
- return insertTagsInText(lineMarkerPoints, text, (TagInfo t) -> null);
- }
-
- public static String insertInfoTags(List highlights, String text) {
- List highlightPoints = toHighlightTagPoints(highlights);
-
- return insertTagsInText(highlightPoints, text, (TagInfo t) -> null);
- }
-
- public static String generateTextWithCaretAndSelection(@NotNull Editor editor) {
- List points = Lists.newArrayList();
- points.add(new TagInfo(editor.getCaretModel().getOffset(), true, "caret"));
- if (editor.getSelectionModel().hasSelection()) {
- points.add(new TagInfo(editor.getSelectionModel().getSelectionStart(), true, "selection"));
- points.add(new TagInfo(editor.getSelectionModel().getSelectionEnd(), false, "selection"));
- }
-
- return insertTagsInText(points, editor.getDocument().getText(), (TagInfo t) -> null);
- }
-
- public static String insertTagsInText(List extends TagInfo> tags, String text, Function computeExtraAttributes) {
- StringBuilder builder = new StringBuilder(text);
-
- // Need to sort tags for inserting them in reverse order to have valid offsets for yet not inserted tags.
- // Can't just sort in reverse order but do sort plus reverse instead to preserve final order for tags with the same offset.
- List extends TagInfo> sortedTagPoints = Lists.reverse(tags.stream().sorted().collect(Collectors.toList()));
-
- // Insert tags into text starting from the end for preventing invalidating previous tags offsets
- for (TagInfo point : sortedTagPoints) {
- String tagText;
- if (point.isStart) {
- String attributesString = point.getAttributesString();
- String extraAttributes = computeExtraAttributes.apply(point);
-
- String allAttributes;
- if (extraAttributes != null) {
- allAttributes = attributesString + " " + extraAttributes;
- } else {
- allAttributes = attributesString;
- }
-
- String closeSuffix = point.isClosed ? "/" : "";
- if (attributesString.isEmpty()) {
- tagText = String.format("<%s%s>", point.getName(), closeSuffix);
- }
- else {
- tagText = String.format("<%s %s%s>", point.getName(), allAttributes, closeSuffix);
- }
- }
- else {
- tagText = String.format("%s>", point.getName());
- }
-
- builder.insert(point.offset, tagText);
- }
-
- return builder.toString();
- }
-
- @NotNull
- public static List toLineMarkerTagPoints(
- Collection lineMarkers,
- boolean withDescription
- ) {
- List lineMarkerPoints = Lists.newArrayList();
- for (LineMarkerInfo markerInfo : lineMarkers) {
- lineMarkerPoints.add(new LineMarkerTagPoint(markerInfo.startOffset, true, markerInfo, withDescription));
- lineMarkerPoints.add(new LineMarkerTagPoint(markerInfo.endOffset, false, markerInfo, withDescription));
- }
- return lineMarkerPoints;
- }
-
- @NotNull
- public static List toHighlightTagPoints(Collection highlights) {
- List highlightPoints = Lists.newArrayList();
- for (HighlightInfo highlight : highlights) {
- highlightPoints.add(new HighlightTagPoint(highlight.startOffset, true, highlight));
- highlightPoints.add(new HighlightTagPoint(highlight.endOffset, false, highlight));
- }
- return highlightPoints;
- }
-
- public static class TagInfo implements Comparable> {
- protected final int offset;
- protected final boolean isStart;
- protected final boolean isClosed;
- protected final boolean isFixed;
- public final Data data;
-
- public TagInfo(int offset, boolean start, Data data) {
- this(offset, start, false, false, data);
- }
-
- public TagInfo(int offset, boolean isStart, boolean isClosed, boolean isFixed, Data data) {
- if (isClosed && !isStart) {
- throw new IllegalArgumentException("isClosed should be true only for start tags");
- }
-
- this.offset = offset;
- this.isStart = isStart;
- this.isClosed = isClosed;
- this.isFixed = isFixed;
- this.data = data;
- }
-
- @Override
- public int compareTo(@NotNull TagInfo> other) {
- if (offset != other.offset) {
- return ((Integer) offset).compareTo(other.offset);
- }
-
- if (isStart != other.isStart) {
- // All "starts" should go after "ends" for same offset
- return isStart ? -1 : 1;
- }
-
- if (isFixed || other.isFixed) {
- return 0;
- }
-
- String thisTag = this.getName();
- String otherTag = other.getName();
-
- // Invert order for end tags
- return thisTag.compareTo(otherTag) * (isStart ? -1 : 1);
- }
-
- @NotNull
- public String getName() {
- return data.toString();
- }
-
- @NotNull
- public String getAttributesString() {
- return "";
- }
- }
-
- public static class HighlightTagPoint extends TagInfo {
- private final HighlightInfo highlightInfo;
-
- private HighlightTagPoint(int offset, boolean start, HighlightInfo info) {
- super(offset, start, info);
- highlightInfo = info;
- }
-
- @NotNull
- @Override
- public String getName() {
- return highlightInfo.getSeverity().equals(HighlightSeverity.INFORMATION)
- ? "info"
- : highlightInfo.getSeverity().toString().toLowerCase();
- }
-
- @NotNull
- @Override
- public String getAttributesString() {
- if (isStart) {
- if (highlightInfo.getDescription() != null) {
- return String.format("descr=\"%s\" textAttributesKey=\"%s\"",
- sanitizeLineBreaks(highlightInfo.getDescription()),
- highlightInfo.forcedTextAttributesKey
- );
- }
- else {
- return String.format("textAttributesKey=\"%s\"", highlightInfo.forcedTextAttributesKey);
- }
- }
- else {
- return "";
- }
- }
- }
-
- public static class LineMarkerTagPoint extends TagInfo {
- private final boolean withDescription;
-
- public LineMarkerTagPoint(int offset, boolean start, LineMarkerInfo info, boolean withDescription) {
- super(offset, start, info);
- this.withDescription = withDescription;
- }
-
- @NotNull
- @Override
- public String getName() {
- return "lineMarker";
- }
-
- @NotNull
- @Override
- public String getAttributesString() {
- return withDescription ? String.format("descr=\"%s\"", sanitizeLineMarkerTooltip(data.getLineMarkerTooltip())) : "descr=\"*\"";
- }
- }
-
- private static @NotNull String sanitizeLineMarkerTooltip(@Nullable String originalText) {
- if (originalText == null) return "null";
- String noHtmlTags = StringUtil.removeHtmlTags(originalText);
- return sanitizeLineBreaks(noHtmlTags);
- }
-
- private static String sanitizeLineBreaks(String originalText) {
- return StringUtil.replace(originalText, "\n", " ");
- }
-}
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java
index bdb834a0195..53008ebf6e2 100644
--- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java
+++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java
@@ -16,63 +16,35 @@
package org.jetbrains.kotlin.test.testFramework;
-import com.intellij.ide.util.AppPropertiesComponentImpl;
-import com.intellij.ide.util.PropertiesComponent;
-import com.intellij.lang.*;
-import com.intellij.lang.impl.PsiBuilderFactoryImpl;
-import com.intellij.mock.*;
-import com.intellij.openapi.Disposable;
+import com.intellij.lang.ParserDefinition;
+import com.intellij.mock.MockProject;
import com.intellij.openapi.application.PathManager;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.EditorFactory;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
-import com.intellij.openapi.fileTypes.FileTypeManager;
-import com.intellij.openapi.fileTypes.FileTypeRegistry;
-import com.intellij.openapi.options.SchemeManagerFactory;
-import com.intellij.openapi.progress.EmptyProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.progress.impl.CoreProgressManager;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
-import com.intellij.pom.PomModel;
-import com.intellij.pom.core.impl.PomModelImpl;
-import com.intellij.pom.tree.TreeAspect;
-import com.intellij.psi.*;
-import com.intellij.psi.impl.*;
-import com.intellij.psi.util.CachedValuesManager;
-import com.intellij.testFramework.LightVirtualFile;
-import com.intellij.testFramework.MockSchemeManagerFactory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.impl.DebugUtil;
import com.intellij.testFramework.TestDataFile;
-import com.intellij.util.CachedValuesManagerImpl;
-import com.intellij.util.Function;
-import junit.framework.TestCase;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.kotlin.idea.KotlinFileType;
-import org.picocontainer.ComponentAdapter;
-import org.picocontainer.MutablePicoContainer;
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
+import org.jetbrains.kotlin.config.CompilerConfiguration;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
import java.io.File;
import java.io.IOException;
-import java.util.Set;
@SuppressWarnings("ALL")
public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
- public static final Key HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY");
- protected String myFilePrefix = "";
protected String myFileExt;
protected final String myFullDataPath;
protected PsiFile myFile;
- private MockPsiManager myPsiManager;
- private PsiFileFactoryImpl myFileFactory;
- protected Language myLanguage;
private final ParserDefinition[] myDefinitions;
private final boolean myLowercaseFirstLetter;
+ private KotlinCoreEnvironment myEnvironment;
+
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) {
this(dataPath, fileExt, false, definitions);
}
@@ -87,88 +59,9 @@ public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
@Override
protected void setUp() throws Exception {
super.setUp();
- initApplication();
- ComponentAdapter component = getApplication().getPicoContainer().getComponentAdapter(ProgressManager.class.getName());
-
- myProject = new MockProjectEx(getTestRootDisposable());
- myPsiManager = new MockPsiManager(myProject);
- myFileFactory = new PsiFileFactoryImpl(myPsiManager);
- MutablePicoContainer appContainer = getApplication().getPicoContainer();
- final MockEditorFactory editorFactory = new MockEditorFactory();
- MockFileTypeManager mockFileTypeManager = new MockFileTypeManager(KotlinFileType.INSTANCE);
- MockFileDocumentManagerImpl mockFileDocumentManager = new MockFileDocumentManagerImpl(new Function() {
- @Override
- public Document fun(CharSequence charSequence) {
- return editorFactory.createDocument(charSequence);
- }
- }, HARD_REF_TO_DOCUMENT_KEY);
-
- registerApplicationService(PropertiesComponent.class, new AppPropertiesComponentImpl());
- registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
- registerApplicationService(DefaultASTFactory.class, new DefaultASTFactoryImpl());
- registerApplicationService(SchemeManagerFactory.class, new MockSchemeManagerFactory());
- registerApplicationService(FileTypeManager.class, mockFileTypeManager);
- registerApplicationService(FileDocumentManager.class, mockFileDocumentManager);
-
- registerApplicationService(ProgressManager.class, new CoreProgressManager());
-
- registerComponentInstance(appContainer, FileTypeRegistry.class, mockFileTypeManager);
- registerComponentInstance(appContainer, FileTypeManager.class, mockFileTypeManager);
- registerComponentInstance(appContainer, EditorFactory.class, editorFactory);
- registerComponentInstance(appContainer, FileDocumentManager.class, mockFileDocumentManager);
- registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
-
- myProject.registerService(PsiManager.class, myPsiManager);
- myProject.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(myProject, new PsiCachedValuesFactory(myProject)));
- myProject.registerService(TreeAspect.class, new TreeAspect());
-
- for (ParserDefinition definition : myDefinitions) {
- addExplicitExtension(LanguageParserDefinitions.INSTANCE, definition.getFileNodeType().getLanguage(), definition);
- }
- if (myDefinitions.length > 0) {
- configureFromParserDefinition(myDefinitions[0], myFileExt);
- }
-
- // That's for reparse routines
- final PomModelImpl pomModel = new PomModelImpl(myProject);
- myProject.registerService(PomModel.class, pomModel);
- }
-
- public void configureFromParserDefinition(ParserDefinition definition, String extension) {
- myLanguage = definition.getFileNodeType().getLanguage();
- myFileExt = extension;
- addExplicitExtension(LanguageParserDefinitions.INSTANCE, this.myLanguage, definition);
- registerComponentInstance(
- getApplication().getPicoContainer(), FileTypeManager.class,
- new MockFileTypeManager(new MockLanguageFileType(myLanguage, myFileExt)));
- }
-
- protected void addExplicitExtension(final LanguageExtension instance, final Language language, final T object) {
- instance.addExplicitExtension(language, object);
- Disposer.register(myProject, new Disposable() {
- @Override
- public void dispose() {
- instance.removeExplicitExtension(language, object);
- }
- });
- }
-
- protected void registerApplicationService(final Class aClass, T object) {
- getApplication().registerService(aClass, object);
- Disposer.register(myProject, new Disposable() {
- @Override
- public void dispose() {
- getApplication().getPicoContainer().unregisterComponent(aClass.getName());
- }
- });
- }
-
- public MockProjectEx getProject() {
- return myProject;
- }
-
- public MockPsiManager getPsiManager() {
- return myPsiManager;
+ myEnvironment = KotlinCoreEnvironment.createForTests(getTestRootDisposable(), CompilerConfiguration.EMPTY,
+ EnvironmentConfigFiles.JVM_CONFIG_FILES);
+ myProject = (MockProject) myEnvironment.getProject();
}
@Override
@@ -176,7 +69,7 @@ public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
super.tearDown();
myFile = null;
myProject = null;
- myPsiManager = null;
+ myEnvironment = null;
}
protected String getTestDataPath() {
@@ -200,92 +93,12 @@ public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
return true;
}
- protected void doTest(boolean checkResult) {
- String name = getTestName();
- try {
- String text = loadFile(name + "." + myFileExt);
- myFile = createPsiFile(name, text);
- ensureParsed(myFile);
- assertEquals("light virtual file text mismatch", text, ((LightVirtualFile)myFile.getVirtualFile()).getContent().toString());
- assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
- assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
- assertEquals("psi text mismatch", text, myFile.getText());
- ensureCorrectReparse(myFile);
- if (checkResult){
- checkResult(name, myFile);
- }
- else{
- toParseTreeText(myFile, skipSpaces(), includeRanges());
- }
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- protected void doTest(String suffix) throws IOException {
- String name = getTestName();
- String text = loadFile(name + "." + myFileExt);
- myFile = createPsiFile(name, text);
- ensureParsed(myFile);
- assertEquals(text, myFile.getText());
- checkResult(name + suffix, myFile);
- }
-
- protected void doCodeTest(String code) throws IOException {
- String name = getTestName();
- myFile = createPsiFile("a", code);
- ensureParsed(myFile);
- assertEquals(code, myFile.getText());
- checkResult(myFilePrefix + name, myFile);
- }
-
protected PsiFile createPsiFile(String name, String text) {
return createFile(name + "." + myFileExt, text);
}
protected PsiFile createFile(@NonNls String name, String text) {
- LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
- virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
- return createFile(virtualFile);
- }
-
- protected PsiFile createFile(LightVirtualFile virtualFile) {
- return myFileFactory.trySetupPsiForFile(virtualFile, myLanguage, true, false);
- }
-
- protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
- doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
- }
-
- public static void doCheckResult(String testDataDir,
- PsiFile file,
- boolean checkAllPsiRoots,
- String targetDataName,
- boolean skipSpaces,
- boolean printRanges) throws IOException {
- FileViewProvider provider = file.getViewProvider();
- Set languages = provider.getLanguages();
-
- if (!checkAllPsiRoots || languages.size() == 1) {
- doCheckResult(testDataDir, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
- return;
- }
-
- for (Language language : languages) {
- PsiFile root = provider.getPsi(language);
- String expectedName = targetDataName + "." + language.getID() + ".txt";
- doCheckResult(testDataDir, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
- }
- }
-
- protected void checkResult(String actual) throws IOException {
- String name = getTestName();
- doCheckResult(myFullDataPath, myFilePrefix + name + ".txt", actual);
- }
-
- protected void checkResult(@TestDataFile @NonNls String targetDataName, String actual) throws IOException {
- doCheckResult(myFullDataPath, targetDataName, actual);
+ return KtTestUtil.createFile(name, text, myProject);
}
public static void doCheckResult(String fullPath, String targetDataName, String actual) throws IOException {
@@ -304,23 +117,4 @@ public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
public static String loadFileDefault(String dir, String name) throws IOException {
return FileUtil.loadFile(new File(dir, name), CharsetToolkit.UTF8, true).trim();
}
-
- public static void ensureParsed(PsiFile file) {
- file.accept(new PsiElementVisitor() {
- @Override
- public void visitElement(PsiElement element) {
- element.acceptChildren(this);
- }
- });
- }
-
- public static void ensureCorrectReparse(@NotNull PsiFile file) {
- String psiToStringDefault = DebugUtil.psiToString(file, false, false);
- String fileText = file.getText();
- DiffLog diffLog = (new BlockSupportImpl()).reparseRange(
- file, file.getNode(), TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
- diffLog.performActualPsiChange(file);
-
- TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false));
- }
}
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java
index 344dc591e27..e99fc4d9d72 100644
--- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java
+++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java
@@ -16,25 +16,10 @@
package org.jetbrains.kotlin.test.testFramework;
-import com.intellij.core.CoreEncodingProjectManager;
-import com.intellij.mock.MockApplication;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.fileTypes.FileTypeManager;
-import com.intellij.openapi.vfs.encoding.EncodingManager;
-import org.picocontainer.MutablePicoContainer;
+import com.intellij.mock.MockProject;
public abstract class KtPlatformLiteFixture extends KtUsefulTestCase {
- protected MockProjectEx myProject;
-
- public static MockApplication getApplication() {
- return (MockApplication) ApplicationManager.getApplication();
- }
-
- public void initApplication() {
- MockApplication instance = new MockApplication(getTestRootDisposable());
- ApplicationManager.setApplication(instance, FileTypeManager::getInstance, getTestRootDisposable());
- getApplication().registerService(EncodingManager.class, CoreEncodingProjectManager.class);
- }
+ protected MockProject myProject;
@Override
protected void tearDown() throws Exception {
@@ -42,12 +27,4 @@ public abstract class KtPlatformLiteFixture extends KtUsefulTestCase {
clearFields(this);
myProject = null;
}
-
- @SuppressWarnings("unchecked")
- public static T registerComponentInstance(MutablePicoContainer container, Class key, T implementation) {
- Object old = container.getComponentInstance(key);
- container.unregisterComponent(key);
- container.registerComponentInstance(key, implementation);
- return (T)old;
- }
}
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtUsefulTestCase.java b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtUsefulTestCase.java
index 657ee289ec8..0f6835751b1 100644
--- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtUsefulTestCase.java
+++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtUsefulTestCase.java
@@ -5,38 +5,28 @@
package org.jetbrains.kotlin.test.testFramework;
-import com.intellij.codeInsight.CodeInsightSettings;
-import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory;
-import com.intellij.diagnostic.PerformanceWatcher;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.PsiDocumentManager;
-import com.intellij.psi.codeStyle.CodeStyleSettings;
-import com.intellij.psi.impl.DocumentCommitProcessor;
-import com.intellij.psi.impl.DocumentCommitThread;
-import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.rt.execution.junit.FileComparisonFailure;
-import com.intellij.testFramework.*;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.PeekableIterator;
import com.intellij.util.containers.PeekableIteratorWrapper;
-import com.intellij.util.indexing.FileBasedIndex;
-import com.intellij.util.indexing.FileBasedIndexImpl;
import com.intellij.util.lang.CompoundRuntimeException;
import gnu.trove.Equality;
import gnu.trove.THashSet;
@@ -63,7 +53,6 @@ import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
-import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@SuppressWarnings("ALL")
@@ -79,9 +68,6 @@ public abstract class KtUsefulTestCase extends TestCase {
private Application application;
- static {
- IdeaForkJoinWorkerThreadFactory.setupPoisonFactory();
- }
protected static final Logger LOG = Logger.getInstance(KtUsefulTestCase.class);
@NotNull
@@ -93,14 +79,12 @@ public abstract class KtUsefulTestCase extends TestCase {
private String myTempDir;
private static final String DEFAULT_SETTINGS_EXTERNALIZED;
- private static final CodeInsightSettings defaultSettings = new CodeInsightSettings();
static {
// Radar #5755208: Command line Java applications need a way to launch without a Dock icon.
System.setProperty("apple.awt.UIElement", "true");
try {
Element oldS = new Element("temp");
- defaultSettings.writeExternal(oldS);
DEFAULT_SETTINGS_EXTERNALIZED = JDOMUtil.writeElement(oldS);
}
catch (Exception e) {
@@ -176,7 +160,7 @@ public abstract class KtUsefulTestCase extends TestCase {
boolean isStressTest = isStressTest();
ApplicationInfoImpl.setInStressTest(isStressTest);
-
+ Registry.getInstance().markAsLoaded();
// turn off Disposer debugging for performance tests
Disposer.setDebugMode(!isStressTest);
}
@@ -287,39 +271,6 @@ public abstract class KtUsefulTestCase extends TestCase {
containerMap.clear();
}
- static void doCheckForSettingsDamage(@NotNull CodeStyleSettings oldCodeStyleSettings, @NotNull CodeStyleSettings currentCodeStyleSettings) {
- final CodeInsightSettings settings = CodeInsightSettings.getInstance();
- // don't use method references here to make stack trace reading easier
- //noinspection Convert2MethodRef
- new RunAll()
- .append(() -> {
- try {
- checkCodeInsightSettingsEqual(defaultSettings, settings);
- }
- catch (AssertionError error) {
- CodeInsightSettings clean = new CodeInsightSettings();
- for (Field field : clean.getClass().getFields()) {
- try {
- ReflectionUtil.copyFieldValue(clean, settings, field);
- }
- catch (Exception ignored) {
- }
- }
- throw error;
- }
- })
- .append(() -> {
- currentCodeStyleSettings.getIndentOptions(StdFileTypes.JAVA);
- try {
- checkCodeStyleSettingsEqual(oldCodeStyleSettings, currentCodeStyleSettings);
- }
- finally {
- currentCodeStyleSettings.clearCodeStyleSettings();
- }
- })
- .run();
- }
-
@NotNull
public Disposable getTestRootDisposable() {
return myTestRootDisposable;
@@ -725,10 +676,6 @@ public abstract class KtUsefulTestCase extends TestCase {
Assert.fail(value + " should be equal to one of " + Arrays.toString(values));
}
- public static void printThreadDump() {
- PerformanceWatcher.dumpThreadsToConsole("Thread dump:");
- }
-
public static void assertEmpty(@NotNull Object[] array) {
assertOrderedEquals(array);
}
@@ -885,27 +832,6 @@ public abstract class KtUsefulTestCase extends TestCase {
}
}
- private static void checkCodeStyleSettingsEqual(@NotNull CodeStyleSettings expected, @NotNull CodeStyleSettings settings) {
- if (!expected.equals(settings)) {
- Element oldS = new Element("temp");
- expected.writeExternal(oldS);
- Element newS = new Element("temp");
- settings.writeExternal(newS);
-
- String newString = JDOMUtil.writeElement(newS);
- String oldString = JDOMUtil.writeElement(oldS);
- Assert.assertEquals("Code style settings damaged", oldString, newString);
- }
- }
-
- private static void checkCodeInsightSettingsEqual(@NotNull CodeInsightSettings oldSettings, @NotNull CodeInsightSettings settings) {
- if (!oldSettings.equals(settings)) {
- Element newS = new Element("temp");
- settings.writeExternal(newS);
- Assert.assertEquals("Code insight settings damaged", DEFAULT_SETTINGS_EXTERNALIZED, JDOMUtil.writeElement(newS));
- }
- }
-
/**
* @return true for a test which performs A LOT of computations.
* Such test should typically avoid performing expensive checks, e.g. data structure consistency complex validations.
@@ -925,13 +851,6 @@ public abstract class KtUsefulTestCase extends TestCase {
return name != null && (name.contains("Stress") || name.contains("Slow"));
}
- public static void doPostponedFormatting(@NotNull Project project) {
- DocumentUtil.writeInRunUndoTransparentAction(() -> {
- PsiDocumentManager.getInstance(project).commitAllDocuments();
- PostprocessReformattingAspect.getInstance(project).doPostponedFormatting();
- });
- }
-
protected void assertNoThrowable(@NotNull Runnable closure) {
String throwableName = null;
try {
diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java b/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java
index 0ae37e545d7..3202ac98e2a 100644
--- a/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java
+++ b/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java
@@ -16,9 +16,6 @@
package org.jetbrains.kotlin.parsing;
-import com.intellij.openapi.fileTypes.FileTypeManager;
-import com.intellij.openapi.fileTypes.FileTypeRegistry;
-import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.psi.PsiErrorElement;
@@ -136,21 +133,4 @@ public abstract class AbstractParsingTest extends KtParsingTestCase {
return createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(filePath)), fileContent);
}
}
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
- // Temp workaround for setting FileTypeRegistry.ourInstanceGetter to CoreFileTypeRegistry forever
- // Reproducible with pattern test kind:
- // org.jetbrains.kotlin.parsing.JetParsingTestGenerated$Psi||org.jetbrains.kotlin.codegen.TestlibTest||org.jetbrains.kotlin.completion.MultiFileJvmBasicCompletionTestGenerated
-
- //noinspection AssignmentToStaticFieldFromInstanceMethod
- FileTypeRegistry.ourInstanceGetter = new Getter() {
- @Override
- public FileTypeRegistry get() {
- return FileTypeManager.getInstance();
- }
- };
- }
}
diff --git a/js/js.tests/test/com/intellij/mock/MockVirtualFileSystem.java b/js/js.tests/test/com/intellij/mock/MockVirtualFileSystem.java
new file mode 100644
index 00000000000..3e33e55a491
--- /dev/null
+++ b/js/js.tests/test/com/intellij/mock/MockVirtualFileSystem.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package com.intellij.mock;
+
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.testFramework.LightVirtualFile;
+import com.intellij.util.containers.CollectionFactory;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Map;
+
+public final class MockVirtualFileSystem extends DeprecatedVirtualFileSystem {
+ private static final String PROTOCOL = "mock";
+
+ private final MyVirtualFile myRoot = new MyVirtualFile("", null) {
+ @NotNull
+ @Override
+ public VirtualFileSystem getFileSystem() {
+ return MockVirtualFileSystem.this;
+ }
+ };
+
+ @NotNull
+ @Override
+ public MyVirtualFile findFileByPath(@NotNull String path) {
+ String normalized = path.replace(File.separatorChar, '/').replace('/', ':');
+ if (StringUtil.startsWithChar(normalized, ':')) normalized = normalized.substring(1);
+ MyVirtualFile file = myRoot;
+ for (String component : StringUtil.split(normalized, ":")) {
+ file = file.getOrCreate(component);
+ }
+ return file;
+ }
+
+ @NotNull
+ public MockVirtualFileSystem file(@NotNull String path, @NotNull String data) {
+ MyVirtualFile file = findFileByPath(path);
+ file.setContent(null, data, false);
+ return this;
+ }
+
+ @NotNull
+ public VirtualFile getRoot() {
+ return myRoot;
+ }
+
+ @NotNull
+ @Override
+ public String getProtocol() {
+ return PROTOCOL;
+ }
+
+ @Override
+ public void refresh(boolean asynchronous) { }
+
+ @Override
+ public VirtualFile refreshAndFindFileByPath(@NotNull String path) {
+ return findFileByPath(path);
+ }
+
+ private static class MyVirtualFile extends LightVirtualFile {
+ private final MyVirtualFile myParent;
+ private Map myChildren;
+
+ private MyVirtualFile(String name, MyVirtualFile parent) {
+ super(name);
+ myParent = parent;
+ }
+
+ @NotNull
+ @Override
+ public VirtualFileSystem getFileSystem() {
+ return myParent.getFileSystem();
+ }
+
+ @NotNull
+ public MyVirtualFile getOrCreate(@NotNull String name) {
+ MyVirtualFile file = findChild(name);
+ if (file == null) {
+ if (myChildren == null) {
+ myChildren = CollectionFactory.createSmallMemoryFootprintMap();
+ }
+ file = new MyVirtualFile(name, this);
+ myChildren.put(name, file);
+ }
+ return file;
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return myParent == null || (myChildren != null && !myChildren.isEmpty());
+ }
+
+ @NotNull
+ @Override
+ public String getPath() {
+ MockVirtualFileSystem.MyVirtualFile parent = getParent();
+ return parent == null ? getName() : parent.getPath() + "/" + getName();
+ }
+
+ @Override
+ public MyVirtualFile getParent() {
+ return myParent;
+ }
+
+ @Override
+ public VirtualFile[] getChildren() {
+ return myChildren == null ? EMPTY_ARRAY : VfsUtilCore.toVirtualFileArray(myChildren.values());
+ }
+
+ @Nullable
+ @Override
+ public MyVirtualFile findChild(@NotNull String name) {
+ return myChildren == null ? null : myChildren.get(name);
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-statistics/build.gradle.kts b/libraries/tools/kotlin-gradle-statistics/build.gradle.kts
index 306d012ebcb..e8a3f67020c 100644
--- a/libraries/tools/kotlin-gradle-statistics/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-statistics/build.gradle.kts
@@ -16,7 +16,9 @@ sourceSets {
if (kotlinBuildProperties.isJpsBuildEnabled || kotlinBuildProperties.useFir) {
none()
} else {
- projectDefault()
+ none()
+
+// projectDefault()
}
}
}
diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractCommonizationFromSourcesTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractCommonizationFromSourcesTest.kt
index 333bf1221c2..bd3e66da989 100644
--- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractCommonizationFromSourcesTest.kt
+++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractCommonizationFromSourcesTest.kt
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.commonizer
import com.intellij.openapi.Disposable
-import com.intellij.testFramework.PlatformTestUtil.lowercaseFirstLetter
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
diff --git a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt
index 806f0bf11a1..ad114886b1d 100755
--- a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt
+++ b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.android.synthetic.test
import com.intellij.mock.MockProject
-import com.intellij.testFramework.registerServiceInstance
import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
import org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor