mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 13:39:36 +07:00
Delete IPythonConsoleTest#testParsing: duplicates PythonConsoleParsingTest#testQuestionEnd. Fails because a virtual file is not marked as IPython. PythonConsoleTest#testCompletionDoNotEvaluateProperty: rewrite to static. DebugConsoleTest: delete deprecated python function. Merge-request: IJ-MR-145576 Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com> GitOrigin-RevId: 3708defece0a957708073b524995c21e7d095224
489 lines
15 KiB
Java
489 lines
15 KiB
Java
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
|
package com.jetbrains.env.debug;
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.Sets;
|
|
import com.intellij.execution.console.ConsoleHistoryController;
|
|
import com.intellij.openapi.actionSystem.AnAction;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.application.ReadAction;
|
|
import com.intellij.openapi.command.WriteCommandAction;
|
|
import com.intellij.openapi.editor.Caret;
|
|
import com.intellij.openapi.editor.ex.EditorEx;
|
|
import com.intellij.testFramework.EditorTestUtil;
|
|
import com.intellij.testFramework.EdtTestUtil;
|
|
import com.intellij.testFramework.TestActionEvent;
|
|
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
|
|
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
|
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
|
|
import com.jetbrains.env.PyEnvTestCase;
|
|
import com.jetbrains.python.console.PyConsoleOptions;
|
|
import com.jetbrains.python.console.PyConsoleOptionsConfigurable;
|
|
import com.jetbrains.python.console.PythonConsoleView;
|
|
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
|
|
import com.jetbrains.python.debugger.PyDebugValue;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.junit.Assert;
|
|
import org.junit.Test;
|
|
|
|
import javax.swing.tree.TreeNode;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.function.Predicate;
|
|
|
|
import static com.intellij.testFramework.UsefulTestCase.assertContainsElements;
|
|
import static com.jetbrains.env.debug.PyBaseDebuggerTask.findCompletionVariantByName;
|
|
import static com.jetbrains.env.debug.PyBaseDebuggerTask.findDebugValueByName;
|
|
import static com.jetbrains.python.PyParameterInfoTest.checkParameters;
|
|
import static org.junit.Assert.*;
|
|
|
|
public class PythonConsoleTest extends PyEnvTestCase {
|
|
private static @Nullable List<String> getStaticCompletion(CodeInsightTestFixture fixture, PythonConsoleView consoleView) {
|
|
fixture.configureFromExistingVirtualFile(consoleView.getVirtualFile());
|
|
ApplicationManager.getApplication().invokeAndWait(() -> {
|
|
addCaretsToConsoleEditor(consoleView.getCurrentEditor());
|
|
});
|
|
|
|
fixture.completeBasic();
|
|
|
|
return fixture.getLookupElementStrings();
|
|
}
|
|
|
|
@RequiresEdt
|
|
private static void addCaretsToConsoleEditor(EditorEx consoleEditor) {
|
|
var state = EditorTestUtil.extractCaretAndSelectionMarkers(consoleEditor.getDocument());
|
|
AtomicBoolean primary = new AtomicBoolean(true);
|
|
ReadAction.run(() -> {
|
|
var model = consoleEditor.getCaretModel();
|
|
List<Caret> oldCarets = List.copyOf(model.getAllCarets());
|
|
|
|
for (EditorTestUtil.CaretInfo caret : state.carets()) {
|
|
if (caret.position != null) {
|
|
model.addCaret(caret.position, primary.get());
|
|
primary.set(false);
|
|
}
|
|
}
|
|
|
|
oldCarets.forEach(model::removeCaret);
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testConsolePrint() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("x = 96");
|
|
exec("x = x + 1");
|
|
exec("print(x)");
|
|
waitForOutput("97");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testExecuteMultiline() { //PY-4329
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("""
|
|
if True:
|
|
x = 1
|
|
y = x + 100
|
|
for i in range(1):
|
|
print(y)
|
|
""");
|
|
waitForOutput("101");
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Set<String> getTags() {
|
|
return Sets.newHashSet("-jython"); //jython doesn't support multiline execution: http://bugs.jython.org/issue2106
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testInterruptAsync() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("import time");
|
|
execNoWait("""
|
|
for i in range(10000):
|
|
print(i)
|
|
time.sleep(0.1)""");
|
|
waitForOutput("3\n4\n5");
|
|
Assert.assertFalse(canExecuteNow());
|
|
interrupt();
|
|
waitForFinish();
|
|
waitForReady();
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Set<String> getTags() {
|
|
return Sets.newHashSet("-iron", "-jython");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testLineByLineInput() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("x = 96");
|
|
exec("x = x + 1");
|
|
exec("""
|
|
if True:
|
|
print(x)
|
|
""");
|
|
waitForOutput("97");
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testVariablesView() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("x = 1");
|
|
exec("print(x)");
|
|
waitForOutput("1");
|
|
|
|
assertTrue("Variable has wrong value",
|
|
hasValue("x", "1"));
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCompoundVariable() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("x = [1, 2, 3]");
|
|
exec("print(x)");
|
|
waitForOutput("[1, 2, 3]");
|
|
|
|
List<String> values = getCompoundValueChildren(getValue("x"));
|
|
Collections.sort(values);
|
|
assertContainsElements(values, "1", "2", "3", "3");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testChangeVariable() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("x = 1");
|
|
exec("print(x)");
|
|
waitForOutput("1");
|
|
|
|
setValue("x", "2");
|
|
|
|
exec("print(x)");
|
|
|
|
waitForOutput("2");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testConsoleActionsReflectedInVariableView() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
|
|
private static final int TIMEOUT = 3000;
|
|
private static final int NUMBER_OF_ATTEMPTS = 10;
|
|
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("foo = 'bar'");
|
|
waitForConditionIsTrue("`foo` is not in the console variable tree", (root) -> {
|
|
for (TreeNode child : root.getChildren()) {
|
|
XDebuggerTreeNode node = (XDebuggerTreeNode)child;
|
|
if (node.toString().equals("foo")) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
|
|
exec("foo = 'baz'");
|
|
waitForConditionIsTrue("`foo` value hasn't changed to 'baz'", (root) -> {
|
|
for (TreeNode child : root.getChildren()) {
|
|
XDebuggerTreeNode node = (XDebuggerTreeNode)child;
|
|
if (node.toString().equals("foo") && node.getText().toString().contains("'baz'")) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
|
|
exec("del foo");
|
|
waitForConditionIsTrue("`foo` is still in the console variable tree", (root) -> {
|
|
for (TreeNode child : root.getChildren()) {
|
|
XDebuggerTreeNode node = (XDebuggerTreeNode)child;
|
|
if (node.toString().equals("foo")) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
private void waitForConditionIsTrue(String message, @NotNull Predicate<XDebuggerTreeNode> pred) throws InterruptedException {
|
|
final long startedAt = System.currentTimeMillis();
|
|
do {
|
|
if (pred.test(getConsoleView().getDebuggerTreeRootNode())) return;
|
|
Thread.sleep(TIMEOUT / NUMBER_OF_ATTEMPTS);
|
|
}
|
|
while (startedAt + TIMEOUT > System.currentTimeMillis());
|
|
Assert.fail(message);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCollectionsShapes() {
|
|
runPythonTest(new PyConsoleTask("/debug") {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("from test_shapes import *");
|
|
waitForOutput("Executed");
|
|
|
|
final List<PyDebugValue> frameVariables = loadFrame();
|
|
PyDebugValue var = findDebugValueByName(frameVariables, "list1");
|
|
assertEquals("120", var.getShape());
|
|
var = findDebugValueByName(frameVariables, "dict1");
|
|
assertEquals("2", var.getShape());
|
|
var = findDebugValueByName(frameVariables, "custom");
|
|
assertEquals("5", var.getShape());
|
|
var = findDebugValueByName(frameVariables, "df1");
|
|
assertEquals("(3, 6)", var.getShape());
|
|
var = findDebugValueByName(frameVariables, "n_array");
|
|
assertEquals("(3, 2)", var.getShape());
|
|
var = findDebugValueByName(frameVariables, "series");
|
|
assertEquals("(5,)", var.getShape());
|
|
|
|
var = findDebugValueByName(frameVariables, "custom_shape");
|
|
assertEquals("(3,)", var.getShape());
|
|
var = findDebugValueByName(frameVariables, "custom_shape2");
|
|
assertEquals("(2, 3)", var.getShape());
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Set<String> getTags() {
|
|
return ImmutableSet.of("pandas");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCompletionMethods() {
|
|
runPythonTest(new PyConsoleTask("/debug") {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("a = \"aaa\"");
|
|
exec("a");
|
|
waitForOutput("aaa");
|
|
|
|
List<PydevCompletionVariant> completions = getCompletions("a.");
|
|
assertFalse("Completion variants list is empty", completions.isEmpty());
|
|
PydevCompletionVariant compVariant = findCompletionVariantByName(completions, "center");
|
|
assertNotNull("Completion variant `center` is missing", compVariant);
|
|
|
|
assertEquals(2, compVariant.getType());
|
|
assertTrue("Missing completion argument `width` in `str.center()`", compVariant.getArgs().contains("width"));
|
|
assertFalse("Documentation is empty for `str.center()`", compVariant.getDescription().isEmpty());
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCompletionDoNotEvaluateProperty() {
|
|
runPythonTest(new PyConsoleTask("/debug") {
|
|
@Override
|
|
public void before() {
|
|
PyConsoleOptions.getInstance(getProject()).setCodeCompletionOption(PyConsoleOptionsConfigurable.CodeCompletionOption.STATIC);
|
|
}
|
|
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("""
|
|
class Bar:
|
|
@property
|
|
def prop(self):
|
|
x = 2389952
|
|
print(x + 1)
|
|
return "bar"
|
|
\s
|
|
""");
|
|
exec("bar = Bar()");
|
|
exec("print(\"Hey\")");
|
|
waitForOutput("Hey");
|
|
|
|
addTextToEditor("bar.<caret>");
|
|
getStaticCompletion(myFixture, getConsoleView());
|
|
assertTrue(getConsoleView().getCurrentEditor().getDocument().getText().endsWith("prop"));
|
|
|
|
// Just to make sure that output is updated
|
|
exec("print('Hello')");
|
|
waitForOutput("Hello");
|
|
|
|
String currentOutput = output();
|
|
assertFalse("Property was called for completion", currentOutput.contains("2389953"));
|
|
}
|
|
|
|
@Override
|
|
public @NotNull Set<String> getTags() {
|
|
return ImmutableSet.of("-python3.8", "-python3.9", "-python3.10", "-python3.11", "-python3.12");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testParameterInfo() {
|
|
runPythonTest(new PyConsoleTask("/debug") {
|
|
@Override
|
|
public void before() {
|
|
PyConsoleOptions.getInstance(getProject()).setCodeCompletionOption(PyConsoleOptionsConfigurable.CodeCompletionOption.RUNTIME);
|
|
}
|
|
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("from os import getenv");
|
|
exec("print(\"Hi\")");
|
|
waitForOutput("Hi");
|
|
addTextToEditor("getenv()");
|
|
ApplicationManager.getApplication().runReadAction(() -> {
|
|
checkParameters(7, getConsoleFile(), "key, default=None", new String[]{"key, "}, myFixture.getEditor());
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testCheckForThreadLeaks() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("x = 42");
|
|
exec("print(x)");
|
|
waitForOutput("42");
|
|
}
|
|
|
|
@Override
|
|
public boolean reportThreadLeaks() {
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testStaticCodeInside() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void before() {
|
|
PyConsoleOptions.getInstance(getProject()).setCodeCompletionOption(PyConsoleOptionsConfigurable.CodeCompletionOption.STATIC);
|
|
}
|
|
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("def foo():\n" +
|
|
" pass");
|
|
exec("x = 42");
|
|
exec("s = 'str'");
|
|
|
|
assertContainsElements(getStaticCompletion(myFixture, getConsoleView()), "x", "foo", "s");
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testConsoleHistoryNavigation() {
|
|
runPythonTest(new PyConsoleTask("/debug") {
|
|
@Override
|
|
public void testing() {
|
|
addToHistory("a = 1");
|
|
addToHistory("""
|
|
def foo():
|
|
x = 1
|
|
y = 2
|
|
return x + y""");
|
|
|
|
runHistoryAction(true);
|
|
checkCaretPosition(true);
|
|
|
|
runHistoryAction(true);
|
|
checkCaretPosition(true);
|
|
|
|
runHistoryAction(false);
|
|
checkCaretPosition(false);
|
|
}
|
|
|
|
private void addToHistory(String command) {
|
|
WriteCommandAction.runWriteCommandAction(getProject(), () -> {
|
|
ConsoleHistoryController.getController(getConsoleView()).addToHistory(command);
|
|
});
|
|
}
|
|
|
|
private void runHistoryAction(boolean isNext) {
|
|
ConsoleHistoryController controller = ConsoleHistoryController.getController(getConsoleView());
|
|
EdtTestUtil.runInEdtAndWait(() -> {
|
|
AnAction action = isNext ? controller.getHistoryNext() : controller.getHistoryPrev();
|
|
action.actionPerformed(TestActionEvent.createTestEvent());
|
|
});
|
|
}
|
|
|
|
private void checkCaretPosition(boolean isNext) {
|
|
EditorEx consoleEditor = getConsoleView().getConsoleEditor();
|
|
|
|
ApplicationManager.getApplication().runReadAction(() -> {
|
|
int offset = consoleEditor.getCaretModel().getOffset();
|
|
int caretLine = consoleEditor.getDocument().getLineNumber(offset);
|
|
int linesCount = consoleEditor.getDocument().getLineCount();
|
|
if (linesCount > 1) {
|
|
if (isNext) {
|
|
assertEquals(0, caretLine);
|
|
}
|
|
else {
|
|
assertEquals(linesCount - 1, caretLine);
|
|
}
|
|
}
|
|
else {
|
|
assertEquals(0, caretLine);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testPromptsPrinting() {
|
|
runPythonTest(new PyConsoleTask() {
|
|
@Override
|
|
public void testing() throws Exception {
|
|
exec("print('>?')");
|
|
waitForOutput(">?");
|
|
|
|
exec("print('...')");
|
|
waitForOutput("...");
|
|
|
|
exec("print('>>>')");
|
|
waitForOutput(">>>");
|
|
}
|
|
});
|
|
}
|
|
}
|