mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
PY-71926 Refine test detection criteria
Add a new setting Python Integrated Tools: Detect tests in Jupyter Notebooks. Exclude Jupyter Notebook files from the scope for test detection by default. Add tests Merge-request: IJ-MR-134248 Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com> GitOrigin-RevId: 0bee082bde4fa608cb1907b8fbd64b97bb9755a0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
571ab91be6
commit
994775243f
@@ -741,6 +741,14 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
|
||||
<extensionPoint qualifiedName="Pythonid.customProcessHandlerProvider"
|
||||
interface="com.jetbrains.python.run.PyCustomProcessHandlerProvider"
|
||||
dynamic="true"/>
|
||||
|
||||
<extensionPoint qualifiedName="com.jetbrains.python.testing.pyTestLineMarkerContributorCustomizer"
|
||||
interface="com.jetbrains.python.testing.PyTestLineMarkerContributorCustomizer"
|
||||
dynamic="true"/>
|
||||
|
||||
<extensionPoint qualifiedName="com.jetbrains.python.configuration.pyIntegratedToolsTestPanelCustomizer"
|
||||
interface="com.jetbrains.python.configuration.PyIntegratedToolsTestPanelCustomizer"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="Pythonid">
|
||||
|
||||
@@ -121,28 +121,23 @@
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="edcb2" binding="myTestsPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<grid id="edcb2" binding="myTestsPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="68c44" class="javax.swing.JComboBox" binding="myTestRunnerComboBox">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="3f484" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<constraints border-constraint="West"/>
|
||||
<properties>
|
||||
<text resource-bundle="messages/PyBundle" key="form.integrated.tools.default.test.runner"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="68c44" class="javax.swing.JComboBox" binding="myTestRunnerComboBox">
|
||||
<constraints border-constraint="Center"/>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="9d84d" binding="myPipEnvPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.project.DefaultProjectFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.ui.DialogPanel;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -30,6 +31,7 @@ import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.util.FileContentUtil;
|
||||
import com.intellij.util.FileContentUtilCore;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
@@ -77,6 +79,8 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
|
||||
private JPanel myTestsPanel;
|
||||
private TextFieldWithBrowseButton myPipEnvPathField;
|
||||
private JPanel myPipEnvPanel;
|
||||
@NotNull
|
||||
private final Collection<@NotNull DialogPanel> myCustomizePanels = PyIntegratedToolsTestPanelCustomizer.Companion.createPanels();
|
||||
|
||||
|
||||
public PyIntegratedToolsConfigurable() {
|
||||
@@ -184,6 +188,9 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
|
||||
public JComponent createComponent() {
|
||||
myModel = PyTestRunConfigurationsModel.Companion.create(myModule);
|
||||
myTestRunnerComboBox.setRenderer(new PyTestRunConfigurationRenderer(PythonSdkUtil.findPythonSdk(myModule)));
|
||||
for (@NotNull DialogPanel panel : myCustomizePanels) {
|
||||
myTestsPanel.add(BorderLayout.AFTER_LAST_LINE, panel);
|
||||
}
|
||||
|
||||
updateConfigurations();
|
||||
initErrorValidation();
|
||||
@@ -224,7 +231,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
|
||||
if (!myPipEnvPathField.getText().equals(StringUtil.notNullize(PipenvKt.getPipEnvPath(PropertiesComponent.getInstance())))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ContainerUtil.exists(myCustomizePanels, panel -> panel.isModified());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -256,6 +263,10 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
|
||||
|
||||
DaemonCodeAnalyzer.getInstance(myProject).restart();
|
||||
PipenvKt.setPipEnvPath(PropertiesComponent.getInstance(), StringUtil.nullize(myPipEnvPathField.getText()));
|
||||
|
||||
for (@NotNull DialogPanel panel : myCustomizePanels) {
|
||||
panel.apply();
|
||||
}
|
||||
}
|
||||
|
||||
public void reparseFiles(final List<String> extensions) {
|
||||
@@ -298,6 +309,10 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (@NotNull DialogPanel panel : myCustomizePanels) {
|
||||
panel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.configuration
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.ui.DialogPanel
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
|
||||
interface PyIntegratedToolsTestPanelCustomizer {
|
||||
companion object {
|
||||
private val EP_NAME: ExtensionPointName<PyIntegratedToolsTestPanelCustomizer> =
|
||||
ExtensionPointName.create("com.jetbrains.python.configuration.pyIntegratedToolsTestPanelCustomizer")
|
||||
|
||||
@RequiresEdt
|
||||
@JvmStatic
|
||||
fun createPanels(): List<DialogPanel> = EP_NAME.extensionList.map { it.createPanel() }
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
fun createPanel(): DialogPanel
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.jetbrains.python.testing
|
||||
|
||||
import com.intellij.execution.lineMarker.RunLineMarkerContributor
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
@@ -18,6 +19,9 @@ class PyTestLineMarkerContributor : RunLineMarkerContributor(), DumbAware {
|
||||
return null
|
||||
}
|
||||
val testElement = element.parent ?: return null
|
||||
if (!PyTestLineMarkerContributorCustomizer.shouldProcessElement(testElement)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val typeEvalContext = TypeEvalContext.codeAnalysis(element.project, element.containingFile)
|
||||
if ((testElement is PyClass || testElement is PyFunction)
|
||||
@@ -27,3 +31,15 @@ class PyTestLineMarkerContributor : RunLineMarkerContributor(), DumbAware {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
interface PyTestLineMarkerContributorCustomizer {
|
||||
companion object {
|
||||
private val EP_NAME: ExtensionPointName<PyTestLineMarkerContributorCustomizer> =
|
||||
ExtensionPointName.create("com.jetbrains.python.testing.pyTestLineMarkerContributorCustomizer")
|
||||
|
||||
@JvmStatic
|
||||
fun shouldProcessElement(testElement: PsiElement): Boolean = EP_NAME.extensionList.all { it.isTestableElement(testElement) }
|
||||
}
|
||||
|
||||
fun isTestableElement(testElement: PsiElement): Boolean = true
|
||||
}
|
||||
1
python/testData/pyTestLineMarker/pytest/__init__.py
Normal file
1
python/testData/pyTestLineMarker/pytest/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from _pytest.fixtures import fixture
|
||||
9
python/testData/pyTestLineMarker/pythonFile.py
Normal file
9
python/testData/pyTestLineMarker/pythonFile.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def foo(x):
|
||||
return x + 1
|
||||
|
||||
|
||||
def te<caret>st_foo():
|
||||
assert foo(1) == 2
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.testing
|
||||
|
||||
import com.intellij.execution.lineMarker.RunLineMarkerContributor
|
||||
import com.intellij.execution.lineMarker.RunLineMarkerContributor.Info
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.jetbrains.python.fixtures.PyTestCase
|
||||
import junit.framework.TestCase
|
||||
|
||||
open class PyTestRunLineMarkerTest : PyTestCase() {
|
||||
companion object {
|
||||
const val TESTS_DIR = "/pyTestLineMarker/"
|
||||
const val PYTHON_FILE = "pythonFile.py"
|
||||
}
|
||||
|
||||
override fun getTestDataPath(): String = super.getTestDataPath() + TESTS_DIR
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
TestRunnerService.getInstance(myFixture.module).selectedFactory =
|
||||
PythonTestConfigurationType.getInstance().pyTestFactory
|
||||
myFixture.copyDirectoryToProject("", "")
|
||||
}
|
||||
|
||||
protected fun getCaretElement(fileName: String): PsiElement? {
|
||||
val psiFile = configureByFile(fileName)
|
||||
TestCase.assertNotNull("Can't find test file", psiFile)
|
||||
val element = psiFile?.findElementAt(myFixture.caretOffset)
|
||||
TestCase.assertNotNull("Can't find caret element", element)
|
||||
return element
|
||||
}
|
||||
|
||||
protected open fun configureByFile(fileName: String): PsiFile? = myFixture.configureByFile(fileName)
|
||||
|
||||
private fun getInfo(element: PsiElement, lineMarkerContributor: RunLineMarkerContributor): Info? = lineMarkerContributor.getInfo(element)
|
||||
|
||||
protected fun assertInfoFound(element: PsiElement, lineMarkerContributor: RunLineMarkerContributor) {
|
||||
val info = getInfo(element, lineMarkerContributor)
|
||||
TestCase.assertNotNull("Info is not found", info)
|
||||
if (info != null) {
|
||||
TestCase.assertNotNull("Run icon is not found", info.icon)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun assertInfoNotFound(element: PsiElement, lineMarkerContributor: RunLineMarkerContributor) {
|
||||
TestCase.assertNull("Info is found", getInfo(element, lineMarkerContributor))
|
||||
}
|
||||
|
||||
fun testPythonFile() {
|
||||
val lineMarkerContributor = PyTestLineMarkerContributor()
|
||||
val element = getCaretElement(PYTHON_FILE)
|
||||
if (element != null) {
|
||||
assertInfoFound(element, lineMarkerContributor)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user