[Test] Remove dependencies on IDEA classes from test modules

Some of IDEA services (like in `com.intellij/execution`) was copied,
  because they are used in tests but jars with them compiled with
  jdk 11 and we run our tests on jdk 8, so their bytecode can not
  be read
This commit is contained in:
Dmitriy Novozhilov
2021-07-02 17:34:14 +03:00
parent 11dfbd41ac
commit 11faf04a4c
25 changed files with 1660 additions and 672 deletions

View File

@@ -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", "")}")
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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)
}
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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.
* <p>
* {@code NONE} means a child process will receive an empty environment. <br/>
* {@code SYSTEM} will provide it with the same environment as an IDE. <br/>
* {@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<String, String> 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<Object, Object> myUserData;
public GeneralCommandLine() { }
public GeneralCommandLine(@NonNls String... command) {
this(Arrays.asList(command));
}
public GeneralCommandLine(@NonNls @NotNull List<String> 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<String, String> getEnvironment() {
return myEnvParams;
}
@NotNull
public GeneralCommandLine withEnvironment(@Nullable Map<String, String> 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<String, String> 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<String, String> getEffectiveEnvironment() {
Map<String, String> env = new MyMap();
setupEnvironment(env);
return env;
}
public void addParameters(@NonNls String... parameters) {
withParameters(parameters);
}
public void addParameters(@NotNull List<String> 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<String> 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.<br/>
* Warning: resulting string is not OS-dependent - <b>do not</b> 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.<br/>
* Warning: resulting string is not OS-dependent - <b>do not</b> 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<String> getCommandLineList(@Nullable String exeName) {
List<@NlsSafe String> commands = new ArrayList<>();
String exe = StringUtil.notNullize(exeName, StringUtil.notNullize(myExePath, "<null>"));
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<String> prepareCommandLine(@NotNull String command, @NotNull List<String> 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<String> 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<String> escapedCommands = validateAndPrepareCommandLine();
return toProcessBuilderInternal(escapedCommands);
}
@NotNull
private List<String> 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<String, String> 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:
* <p>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.
* <pre>
* [foo] [^] -> [foo] [^^]
* </pre>
* but:
* <pre>
* [foo] ["] [^] -> [foo] [\"] ["^"]
* </pre>
* Notice how the last parameter escaping changes after prepending another argument.</p>
* <p>If you need to alter the command line passed in, override the {@link #prepareCommandLine(String, List, Platform)} method instead.</p>
*/
@NotNull
protected Process startProcess(@NotNull List<String> 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<String>).
// Ideally, it should have been startProcess(ProcessBuilder), and the design would be more straightforward.
@NotNull
private ProcessBuilder toProcessBuilderInternal(@NotNull List<String> 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<String, String> 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<String, String> 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> T getUserData(@NotNull Key<T> key) {
if (myUserData == null) return null;
@SuppressWarnings("unchecked") T t = (T)myUserData.get(key);
return t;
}
@Override
public <T> void putUserData(@NotNull Key<T> 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<String, String> {
private MyMap() {
super(FastUtilHashingStrategies.getStringStrategy(!SystemInfoRt.isWindows));
}
@Override
public void putAll(Map<? extends String, ? extends String> map) {
if (map != null) {
super.putAll(map);
}
}
}
}

View File

@@ -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<String> myParameters = new ArrayList<>();
private final List<ParamsGroup> myGroups = new SmartList<>();
private final NotNullLazyValue<Map<String, String>> 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<String, String> getProperties() {
return getProperties("");
}
@NotNull
public Map<String, String> getProperties(@NonNls String valueIfMissing) {
Map<String, String> result = new LinkedHashMap<>();
JBIterable<Matcher> 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<String> getList() {
if (myGroups.isEmpty()) {
return Collections.unmodifiableList(myParameters);
}
List<String> 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<String> getParameters() {
return Collections.unmodifiableList(myParameters);
}
@NotNull
public List<ParamsGroup> 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 <propertyName>} 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 <propertyName>} 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<propertyName>} 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<propertyName>=<propertyValue>} 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<propertyName>=<propertyValue>} 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 <parameterPrefix>} with {@code <replacement>};
* otherwise appends {@code <replacement>} 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 <parameterPrefix>} with {@code <replacement>};
* otherwise prepends this list with {@code <replacement>}.
*/
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<String> 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<String> 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<String, String> 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<String, String> ourTestMacros;
@TestOnly
public static void setTestMacros(@Nullable @NonNls Map<String, String> testMacros) {
ourTestMacros = testMacros;
}
@NotNull
private static Map<String, String> 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<String, String> map = CollectionFactory.createCaseInsensitiveStringMap();
Map<String, String> pathMacros = PathMacros.getInstance().getUserMacros();
if (!pathMacros.isEmpty()) {
for (String name : pathMacros.keySet()) {
ContainerUtil.putIfNotNull(name, pathMacros.get(name), map);
}
}
Map<String, String> 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);
}
}

View File

@@ -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<String> parameters) {
for (String parameter : parameters) {
addParameter(parameter);
}
}
public void addParametersString(@NotNull String parametersString) {
addParameters(ParametersList.parse(parametersString));
}
public List<String> 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;
}
}

View File

@@ -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<File> 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<File> findAllExeFilesInPath(@NotNull String fileBaseName) {
return findAllExeFilesInPath(fileBaseName, null);
}
@NotNull
public static List<File> findAllExeFilesInPath(@NotNull String fileBaseName, @Nullable FileFilter filter) {
return findExeFilesInPath(false, filter, getPathVariableValue(), fileBaseName);
}
@NotNull
private static List<File> findExeFilesInPath(boolean stopAfterFirstMatch,
@Nullable FileFilter filter,
@Nullable String pathEnvVarValue,
String... fileBaseNames) {
if (pathEnvVarValue == null) {
return Collections.emptyList();
}
List<File> result = new SmartList<>();
List<String> 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<String> getPathDirs(@NotNull String pathEnvVarValue) {
return StringUtil.split(pathEnvVarValue, File.pathSeparator, true, true);
}
@NotNull
public static List<String> getWindowsExecutableFileExtensions() {
if (SystemInfo.isWindows) {
String allExtensions = System.getenv("PATHEXT");
if (allExtensions != null) {
Collection<String> 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<String> executableFileExtensions = getWindowsExecutableFileExtensions();
String[] baseNames = ContainerUtil.map2Array(executableFileExtensions, String.class, s -> exePath + s);
List<File> 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<File> exeFiles = findExeFilesInPath(true, null, getPathVariableValue(), fileNames);
return ContainerUtil.getFirstItem(exeFiles);
}
else {
return findInPath(fileBaseName);
}
}
}

View File

@@ -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<String> REPORTED_EXECUTIONS = Collections.newSetFromMap(new ConcurrentHashMap<>());
private static final long ALLOWED_TIMEOUT_THRESHOLD = 10;
private static final Key<Set<File>> 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.
* <br/><br/>
* HOW-TO fix an error from this method:
* <ul>
* <li>You are on the pooled thread under {@link com.intellij.openapi.application.ReadAction ReadAction}:
* <ul>
* <li>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.</li>
* <li>Non-synchronous (you don't need to return something) - execute on another thread. E.g. using {@link Task.Backgroundable}</li>
* </ul>
* </li>
*
* <li>You are on EDT:
* <ul>
*
* <li>Outside of {@link com.intellij.openapi.application.WriteAction WriteAction}:
* <ul>
* <li>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}.</li>
* <li>Non-synchronous (you don't need to return something) - execute on the pooled thread. E.g. using {@link Task.Backgroundable}</li>
* </ul>
* </li>
*
* <li>Under {@link com.intellij.openapi.application.WriteAction WriteAction}
* <ul>
* <li>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.</li>
* <li>Non-synchronous (you don't need to return something) - execute on the pooled thread. E.g. using {@link Task.Backgroundable}</li>
* </ul>
* </li>
* </ul></li></ul>
*
* @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();
}
/**
* <p>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.</p>
*
* <p>The value should be set before {@link #startNotify()} invocation.
* It is set by default in case of using GeneralCommandLine based constructor.</p>
*
* @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<File> 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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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) {}
}
}

View File

@@ -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<TagsTestDataUtil.TagInfo> 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<CharSequence> 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,

View File

@@ -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<LineMarkerInfo> lineMarkers, boolean withDescription, String text) {
List<LineMarkerTagPoint> lineMarkerPoints = toLineMarkerTagPoints(lineMarkers, withDescription);
return insertTagsInText(lineMarkerPoints, text, (TagInfo t) -> null);
}
public static String insertInfoTags(List<HighlightInfo> highlights, String text) {
List<HighlightTagPoint> highlightPoints = toHighlightTagPoints(highlights);
return insertTagsInText(highlightPoints, text, (TagInfo t) -> null);
}
public static String generateTextWithCaretAndSelection(@NotNull Editor editor) {
List<TagInfo> points = Lists.newArrayList();
points.add(new TagInfo<String>(editor.getCaretModel().getOffset(), true, "caret"));
if (editor.getSelectionModel().hasSelection()) {
points.add(new TagInfo<String>(editor.getSelectionModel().getSelectionStart(), true, "selection"));
points.add(new TagInfo<String>(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<TagInfo, String> 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<LineMarkerTagPoint> toLineMarkerTagPoints(
Collection<LineMarkerInfo> lineMarkers,
boolean withDescription
) {
List<LineMarkerTagPoint> 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<HighlightTagPoint> toHighlightTagPoints(Collection<HighlightInfo> highlights) {
List<HighlightTagPoint> 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<Data> implements Comparable<TagInfo<?>> {
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<HighlightInfo> {
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<LineMarkerInfo> {
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", " ");
}
}

View File

@@ -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<Document> 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<CharSequence, Document>() {
@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 <T> void addExplicitExtension(final LanguageExtension<T> 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 <T> void registerApplicationService(final Class<T> 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<Language> 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));
}
}

View File

@@ -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> T registerComponentInstance(MutablePicoContainer container, Class<T> key, T implementation) {
Object old = container.getComponentInstance(key);
container.unregisterComponent(key);
container.registerComponentInstance(key, implementation);
return (T)old;
}
}

View File

@@ -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 {

View File

@@ -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<FileTypeRegistry>() {
@Override
public FileTypeRegistry get() {
return FileTypeManager.getInstance();
}
};
}
}

View File

@@ -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<String, MyVirtualFile> 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);
}
}
}

View File

@@ -16,7 +16,9 @@ sourceSets {
if (kotlinBuildProperties.isJpsBuildEnabled || kotlinBuildProperties.useFir) {
none()
} else {
projectDefault()
none()
// projectDefault()
}
}
}

View File

@@ -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

View File

@@ -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