Get rid of PyTestFrameworkService

All information moved to factories.
It is now possible to check if package is installed using PyPackageService, no need to have separate service.

GitOrigin-RevId: f97d4e09cd93c6b2d46e36920b513b5b82d53dc6
This commit is contained in:
Ilya.Kazakevich
2021-05-28 23:17:29 +00:00
committed by intellij-monorepo-bot
parent 79877aff9e
commit 480de17a92
24 changed files with 149 additions and 506 deletions
@@ -222,10 +222,6 @@ public final class PyNames {
public static final String PASS = "pass";
public static final String NOSE_TEST = "nose";
public static final String PY_TEST = "pytest";
public static final String TRIAL_TEST = "Twisted";
public static final String TEST_CASE = "TestCase";
public static final String PYCACHE = "__pycache__";
@@ -94,8 +94,6 @@
<typedHandler implementation="com.jetbrains.python.codeInsight.PyKeywordTypedHandler" id="pyCommaAfterKwd"/>
<typedHandler implementation="com.jetbrains.python.codeInsight.PyMethodNameTypedHandler" id="pyMethodNameTypedHandler"/>
<typedHandler implementation="com.jetbrains.python.editor.PythonSpaceHandler"/>
<applicationService serviceImplementation="com.jetbrains.python.testing.VFSTestFrameworkListenerImpl"
serviceInterface="com.jetbrains.python.testing.VFSTestFrameworkListener"/>
<idIndexer filetype="Python" implementationClass="com.jetbrains.python.PyIdIndexer"/>
@@ -202,7 +200,6 @@
implementationClass="com.jetbrains.python.codeInsight.regexp.PythonVerboseRegexpSyntaxHighlighterFactory"/>
<regExpLanguageHost forClass="com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl" implementationClass="com.jetbrains.python.codeInsight.PythonRegExpHost"/>
<applicationService serviceImplementation="com.jetbrains.python.testing.PyTestFrameworkService"/>
<autoImportOptionsProvider instance="com.jetbrains.python.codeInsight.imports.PyAutoImportOptions"/>
<completion.ml.contextFeatures language="Python" implementationClass="com.jetbrains.python.codeInsight.mlcompletion.PyContextFeatureProvider"/>
@@ -681,8 +678,4 @@
<extensions defaultExtensionNs="com.jetbrains.python.debugger">
<numericContainerPopupCustomizer implementation="com.jetbrains.python.debugger.pydev.tables.PyNumericContainerPopupCustomizerDefault"/>
</extensions>
<applicationListeners>
<listener activeInTestMode="false" class="com.jetbrains.python.testing.VFSTestFrameworkListenerImpl$Listener" topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"/>
</applicationListeners>
</idea-plugin>
@@ -25,7 +25,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jetbrains.annotations.NotNull;
public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable {
@@ -50,14 +49,5 @@ public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurabl
@Override
public void apply() throws ConfigurationException {
super.apply();
// TODO[catherine] proper per-module caching of framework installed state
for (Module module : ModuleManager.getInstance(myProject).getModules()) {
final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
if (sdk != null) {
ApplicationManager.getApplication().executeOnPooledThread(() -> VFSTestFrameworkListener.getInstance().updateAllTestFrameworks(sdk));
break;
}
}
}
}
@@ -40,10 +40,9 @@ import com.jetbrains.python.packaging.PyPackageUtil;
import com.jetbrains.python.packaging.PyRequirementsKt;
import com.jetbrains.python.sdk.PythonSdkUtil;
import com.jetbrains.python.sdk.pipenv.PipenvKt;
import com.jetbrains.python.testing.PyTestFrameworkService;
import com.jetbrains.python.testing.PyTestsSharedKt;
import com.jetbrains.python.testing.PythonTestConfigurationsModel;
import com.jetbrains.python.testing.TestRunnerService;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import com.jetbrains.python.ui.PyUiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -139,13 +138,11 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
final Sdk sdk = PythonSdkUtil.findPythonSdk(myModule);
if (sdk != null) {
final Object selectedItem = myTestRunnerComboBox.getSelectedItem();
for (final String framework : PyTestFrameworkService.getFrameworkNamesArray()) {
if (PyTestFrameworkService.getSdkReadableNameByFramework(framework).equals(selectedItem)) {
if (!VFSTestFrameworkListener.getInstance().isTestFrameworkInstalled(sdk, framework)) {
return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", framework),
createQuickFix(sdk, facetErrorPanel, framework));
}
if (selectedItem instanceof String) {
var factory = PyTestsSharedKt.getFactoryById(selectedItem.toString());
if (factory != null && !factory.isFrameworkInstalled(sdk)) {
return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", factory.getId()),
createQuickFix(sdk, facetErrorPanel, factory.getPackageRequired()));
}
}
}
@@ -167,8 +164,6 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable {
@Override
public void finished(List<ExecutionException> exceptions) {
if (exceptions.isEmpty()) {
VFSTestFrameworkListener.getInstance().setTestFrameworkInstalled(true, sdk.getHomePath(),
name);
facetErrorPanel.getValidatorsManager().validate();
}
}
@@ -6,7 +6,7 @@ import com.intellij.execution.configurations.RunProfileState
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
import com.jetbrains.python.PyNames
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PythonHelper
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
@@ -26,7 +26,7 @@ class PyNoseTestExecutionEnvironment(configuration: PyNoseTestConfiguration, env
class PyNoseTestConfiguration(project: Project, factory: PyNoseTestFactory) :
PyAbstractTestConfiguration(project, factory, PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.NOSE_TEST)),
PyAbstractTestConfiguration(project, factory),
PyTestConfigurationWithCustomSymbol {
@ConfigField("runcfg.nosetests.config.regexPattern")
var regexPattern: String = ""
@@ -45,14 +45,20 @@ class PyNoseTestConfiguration(project: Project, factory: PyNoseTestFactory) :
override val fileSymbolSeparator get() = ":"
override val symbolSymbolSeparator get() = "."
override fun isFrameworkInstalled(): Boolean = VFSTestFrameworkListener.getInstance().isTestFrameworkInstalled(sdk, PyNames.NOSE_TEST)
}
class PyNoseTestFactory : PyAbstractTestFactory<PyNoseTestConfiguration>() {
companion object {
const val id = "Nosetests"
}
override fun createTemplateConfiguration(project: Project) = PyNoseTestConfiguration(project, this)
override fun getId(): String = "Nosetests"
override fun getId(): String = PyNoseTestFactory.id
override fun getName(): String = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.NOSE_TEST)
override fun getName(): String = PyBundle.message("runcfg.nosetests.display_name")
override val onlyClassesSupported: Boolean = false
override val packageRequired: String = "nose"
}
@@ -15,7 +15,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Pair
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.execution.ParametersListUtil
import com.jetbrains.python.PyNames
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PythonHelper
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
import com.jetbrains.python.testing.PyTestSharedForm.create
@@ -55,7 +55,7 @@ class PyPyTestExecutionEnvironment(configuration: PyTestConfiguration, environme
class PyTestConfiguration(project: Project, factory: PyTestFactory)
: PyAbstractTestConfiguration(project, factory, PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.PY_TEST)),
: PyAbstractTestConfiguration(project, factory),
PyTestConfigurationWithCustomSymbol {
@ConfigField("runcfg.pytest.config.keywords")
var keywords: String = ""
@@ -92,8 +92,6 @@ class PyTestConfiguration(project: Project, factory: PyTestFactory)
return super.getTestSpec()
}
override fun isFrameworkInstalled(): Boolean = VFSTestFrameworkListener.getInstance().isTestFrameworkInstalled(sdk, PyNames.PY_TEST)
override fun setMetaInfo(metaInfo: String) {
// Metainfo contains test name along with params.
parameters = getParamFromMetaInfo(metaInfo)
@@ -113,11 +111,19 @@ class PyTestConfiguration(project: Project, factory: PyTestFactory)
}
class PyTestFactory : PyAbstractTestFactory<PyTestConfiguration>() {
companion object {
const val id = "py.test" //Do not rename: used as ID for run configurations
}
override fun createTemplateConfiguration(project: Project): PyTestConfiguration = PyTestConfiguration(project, this)
override fun getName(): String = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.PY_TEST)
override fun getId() = PyTestFactory.id
override fun getId() = "py.test" //Do not rename: used as ID for run configurations
override fun getName(): String = PyBundle.message("runcfg.pytest.display_name")
override val onlyClassesSupported: Boolean = false
override val packageRequired: String = "pytest"
}
private const val PYTEST_RUN_CONFIG: String = "PYTEST_RUN_CONFIG"
@@ -1,83 +1,31 @@
// 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.python.testing;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.XmlSerializerUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @deprecated use {@link PyTestsSharedKt#getFactoryById(String)}
*/
@Deprecated
public final class PyTestFrameworkService {
private PyTestFrameworkService() {
@State(name = "PyTestFrameworkService", storages = @Storage("other.xml"))
public class PyTestFrameworkService implements PersistentStateComponent<PyTestFrameworkService> {
public static PyTestFrameworkService getInstance() {
return ApplicationManager.getApplication().getService(PyTestFrameworkService.class);
}
public Map<String, Boolean> SDK_TO_PYTEST = new HashMap<>();
public Map<String, Boolean> SDK_TO_NOSETEST = new HashMap<>();
public Map<String, Boolean> SDK_TO_TRIALTEST = new HashMap<>();
private static final String[] FRAMEWORK_NAMES = {PyNames.PY_TEST, PyNames.NOSE_TEST, PyNames.TRIAL_TEST};
@Override
public PyTestFrameworkService getState() {
return this;
}
@Override
public void loadState(@NotNull PyTestFrameworkService state) {
XmlSerializerUtil.copyBean(state, this);
}
/**
* @deprecated use {@link PyTestsSharedKt#getFactoryById(String)}
*/
@ApiStatus.ScheduledForRemoval(inVersion = "2021.3")
@Deprecated
@NotNull
public static Set<String> getFrameworkNamesSet() {
return ContainerUtil.newHashSet(FRAMEWORK_NAMES);
}
public static String @NotNull [] getFrameworkNamesArray() {
return FRAMEWORK_NAMES.clone();
}
@NotNull
public static @Nls String getSdkReadableNameByFramework(@NotNull final String frameworkName) {
switch (frameworkName) {
case PyNames.PY_TEST: {
return PyBundle.message("runcfg.pytest.display_name");
}
case PyNames.NOSE_TEST: {
return PyBundle.message("runcfg.nosetests.display_name");
}
case PyNames.TRIAL_TEST: {
return PyBundle.message("runcfg.trial.display_name");
}
public static @Nls String getSdkReadableNameByFramework(@NotNull final String frameworkId) {
var factory = PyTestsSharedKt.getFactoryById(frameworkId);
if (factory == null) {
throw new IllegalArgumentException("Unknown framework " + frameworkId);
}
throw new IllegalArgumentException("Unknown framework " + frameworkName);
}
@NotNull
Map<String, Boolean> getSdkToTestRunnerByName(@NotNull final String frameworkName) {
switch (frameworkName) {
case PyNames.PY_TEST: {
return SDK_TO_PYTEST;
}
case PyNames.NOSE_TEST: {
return SDK_TO_NOSETEST;
}
case PyNames.TRIAL_TEST: {
return SDK_TO_TRIALTEST;
}
}
throw new IllegalArgumentException("Unknown framework " + frameworkName);
return factory.getName();
}
}
@@ -34,7 +34,7 @@ import com.intellij.remote.RemoteSdkAdditionalData
import com.intellij.util.ThreeState
import com.jetbrains.extensions.*
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PyNames
import com.jetbrains.python.packaging.PyPackageManager
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyQualifiedNameOwner
@@ -58,12 +58,14 @@ import java.util.regex.Matcher
/**
* New configuration factories
*/
internal val pythonFactories
get() = arrayOf<PythonConfigurationFactoryBase>(
PyUnitTestFactory(),
internal val pythonFactories: Array<PyAbstractTestFactory<*>>
get() = arrayOf(
PyTestFactory(),
PyNoseTestFactory(),
PyTrialTestFactory())
PyTrialTestFactory(),
PyUnitTestFactory())
fun getFactoryById(id: String): PyAbstractTestFactory<*>? = pythonFactories.firstOrNull { it.id == id }
/**
* Accepts text that may be wrapped in TC message. Unwraps it and removes TC escape code.
@@ -80,12 +82,6 @@ fun processTCMessage(text: String): String {
internal fun getAdditionalArgumentsProperty() = PyAbstractTestConfiguration::additionalArguments
/**
* If runner name is here that means test runner only can run inheritors for TestCase
*/
val RunnersThatRequireTestCaseClass: Set<String> = setOf<String>(PythonTestConfigurationsModel.getPythonsUnittestName(),
PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.TRIAL_TEST))
/**
* Checks if element could be test target
* @param testCaseClassRequired see [PythonUnitTestDetectorsBasedOnSettings] docs
@@ -411,13 +407,12 @@ internal interface PyTestConfigurationWithCustomSymbol {
*
*/
abstract class PyAbstractTestConfiguration(project: Project,
configurationFactory: ConfigurationFactory,
private val runnerName: String)
: AbstractPythonTestRunConfiguration<PyAbstractTestConfiguration>(project, configurationFactory), PyRerunAwareConfiguration,
private val testFactory: PyAbstractTestFactory<*>)
: AbstractPythonTestRunConfiguration<PyAbstractTestConfiguration>(project, testFactory), PyRerunAwareConfiguration,
RefactoringListenerProvider, SMRunnerConsolePropertiesProvider {
override fun createTestConsoleProperties(executor: Executor): SMTRunnerConsoleProperties =
PythonTRunnerConsoleProperties(this, executor, true, PyTestsLocator).also {properties ->
PythonTRunnerConsoleProperties(this, executor, true, PyTestsLocator).also { properties ->
if (isIdTestBased) properties.makeIdTestBased()
}
@@ -433,12 +428,12 @@ abstract class PyAbstractTestConfiguration(project: Project,
@ConfigField("runcfg.python_tests.config.additionalArguments")
var additionalArguments: String = ""
val testFrameworkName: String = configurationFactory.name
val testFrameworkName: String = testFactory.name
/**
* @see [RunnersThatRequireTestCaseClass]
*/
fun isTestClassRequired(): ThreeState = if (RunnersThatRequireTestCaseClass.contains(runnerName)) {
fun isTestClassRequired(): ThreeState = if (testFactory.onlyClassesSupported) {
ThreeState.YES
}
else {
@@ -485,7 +480,11 @@ abstract class PyAbstractTestConfiguration(project: Project,
/**
* Check if framework is available on SDK
*/
abstract fun isFrameworkInstalled(): Boolean
open fun isFrameworkInstalled(): Boolean {
val sdk = sdk ?: return false // No SDK -- no tests
val requiredPackage = testFactory.packageRequired ?: return true // No package required
return PyPackageManager.getInstance(sdk).packages?.firstOrNull { it.name == requiredPackage } != null
}
override fun isIdTestBased(): Boolean = true
@@ -649,6 +648,21 @@ abstract class PyAbstractTestConfiguration(project: Project,
abstract class PyAbstractTestFactory<out CONF_T : PyAbstractTestConfiguration> : PythonConfigurationFactoryBase(
PythonTestConfigurationType.getInstance()) {
abstract override fun createTemplateConfiguration(project: Project): CONF_T
/**
* Only UnitTest inheritors are supported
*/
abstract val onlyClassesSupported: Boolean
/**
* Test framework needs package to be installed
*/
open val packageRequired: String? = null
open fun isFrameworkInstalled(sdk: Sdk): Boolean {
val requiredPackage = packageRequired ?: return true // No package required
return PyPackageManager.getInstance(sdk).packages?.firstOrNull { it.name == requiredPackage } != null
}
}
@@ -20,7 +20,7 @@ import com.intellij.execution.configurations.RunProfileState
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
import com.jetbrains.python.PyNames
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PythonHelper
/**
@@ -37,7 +37,7 @@ class PyTrialTestExecutionEnvironment(configuration: PyTrialTestConfiguration, e
class PyTrialTestConfiguration(project: Project, factory: PyTrialTestFactory)
: PyAbstractTestConfiguration(project, factory, PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.TRIAL_TEST)) {
: PyAbstractTestConfiguration(project, factory) {
override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState? =
@@ -47,16 +47,17 @@ class PyTrialTestConfiguration(project: Project, factory: PyTrialTestFactory)
PyTrialTestSettingsEditor(this)
override fun shouldSeparateTargetPath() = false
override fun isFrameworkInstalled(): Boolean = VFSTestFrameworkListener.getInstance().isTestFrameworkInstalled(sdk, PyNames.TRIAL_TEST)
}
class PyTrialTestFactory : PyAbstractTestFactory<PyTrialTestConfiguration>() {
override fun createTemplateConfiguration(project: Project): PyTrialTestConfiguration = PyTrialTestConfiguration(project, this)
override fun getName(): String = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.TRIAL_TEST)
override fun getName(): String = PyBundle.message("runcfg.trial.display_name")
override fun getId(): String = "Twisted Trial"
override val onlyClassesSupported: Boolean = true
override val packageRequired: String = "Twisted"
}
@@ -54,8 +54,7 @@ class PyUnitTestExecutionEnvironment(configuration: PyUnitTestConfiguration, env
class PyUnitTestConfiguration(project: Project, factory: PyUnitTestFactory) :
PyAbstractTestConfiguration(project, factory,
PythonTestConfigurationsModel.getPythonsUnittestName()) { // Bare functions not supported in unittest: classes only
PyAbstractTestConfiguration(project, factory) { // Bare functions not supported in unittest: classes only
@ConfigField("runcfg.unittest.config.pattern")
var pattern: String? = null
@@ -108,4 +107,6 @@ class PyUnitTestFactory : PyAbstractTestFactory<PyUnitTestConfiguration>() {
override fun getName(): String = PythonTestConfigurationsModel.getPythonsUnittestName()
override fun getId(): String = "Unittests"
override val onlyClassesSupported: Boolean = true
}
@@ -115,7 +115,9 @@ public final class PythonUnitTestDetectorsBasedOnSettings {
if (module == null) {
return true;
}
return PyTestsSharedKt.getRunnersThatRequireTestCaseClass().contains(TestRunnerService.getInstance(module).getProjectConfiguration());
var configuration = TestRunnerService.getInstance(module).getProjectConfiguration();
var factory = PyTestsSharedKt.getFactoryById(configuration);
return factory != null && factory.getOnlyClassesSupported();
}
private static boolean isTestCaseClassRequired(@NotNull final PsiElement anchor, @NotNull final ThreeState userProvidedValue) {
@@ -1,84 +0,0 @@
// 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.python.testing;
import com.intellij.execution.Location;
import com.intellij.execution.PsiLocation;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public final class PythonUnitTestUtil {
private PythonUnitTestUtil() {}
public static List<Location> findLocations(@NotNull final Project project,
@NotNull String fileName,
@Nullable String className,
@Nullable String methodName) {
if (fileName.contains("%")) {
fileName = fileName.substring(0, fileName.lastIndexOf("%"));
}
final List<Location> locations = new ArrayList<>();
if (methodName == null && className == null) {
final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
if (virtualFile == null) return locations;
final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
if (psiFile != null) {
locations.add(new PsiLocation<>(project, psiFile));
}
}
if (className != null) {
for (PyClass cls : PyClassNameIndex.find(className, project, false)) {
ProgressManager.checkCanceled();
final PsiFile containingFile = cls.getContainingFile();
final VirtualFile virtualFile = containingFile.getVirtualFile();
final String clsFileName = virtualFile == null ? containingFile.getName() : virtualFile.getPath();
final String clsFileNameWithoutExt = FileUtilRt.getNameWithoutExtension(clsFileName);
if (!clsFileNameWithoutExt.endsWith(fileName) && !fileName.equals(clsFileName)) {
continue;
}
if (methodName == null) {
locations.add(new PsiLocation<>(project, cls));
}
else {
final PyFunction method = cls.findMethodByName(methodName, true, null);
if (method == null) {
continue;
}
locations.add(new PyPsiLocationWithFixedClass(project, method, cls));
}
}
}
else if (methodName != null) {
for (PyFunction function : PyFunctionNameIndex.find(methodName, project)) {
ProgressManager.checkCanceled();
if (function.getContainingClass() == null) {
final PsiFile containingFile = function.getContainingFile();
final VirtualFile virtualFile = containingFile.getVirtualFile();
final String clsFileName = virtualFile == null ? containingFile.getName() : virtualFile.getPath();
final String clsFileNameWithoutExt = FileUtilRt.getNameWithoutExtension(clsFileName);
if (!clsFileNameWithoutExt.endsWith(fileName)) {
continue;
}
locations.add(new PsiLocation<>(project, function));
}
}
}
return locations;
}
}
@@ -12,13 +12,13 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.Function;
import com.jetbrains.python.PyPsiPackageUtil;
import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.PythonSdkUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
@@ -59,12 +59,10 @@ final class TestRunnerDetector implements Function<Pair<Module, Collection<Virtu
//check if installed in sdk
final Sdk sdk = PythonSdkUtil.findPythonSdk(module);
if (sdk != null && sdk.getSdkType() instanceof PythonSdkType) {
final List<PyPackage> packages = PyPackageUtil.refreshAndGetPackagesModally(sdk);
for (final String framework : PyTestFrameworkService.getFrameworkNamesArray()) {
if (PyPsiPackageUtil.findPackage(packages, framework) != null) {
testRunner = PyTestFrameworkService.getSdkReadableNameByFramework(framework);
break;
}
PyPackageUtil.refreshAndGetPackagesModally(sdk);
var factory = Arrays.stream(PyTestsSharedKt.getPythonFactories()).filter(o -> o.isFrameworkInstalled(sdk)).findFirst();
if (factory.isPresent()) {
testRunner = factory.get().getId();
}
}
if (!testRunner.isEmpty()) {
@@ -81,16 +79,27 @@ final class TestRunnerDetector implements Function<Pair<Module, Collection<Virtu
if (psiFile instanceof PyFile) {
final List<PyImportElement> importTargets = ((PyFile)psiFile).getImportTargets();
for (PyImportElement importElement : importTargets) {
for (final String framework : PyTestFrameworkService.getFrameworkNamesArray()) {
if (framework.equals(importElement.getVisibleName())) {
return PyTestFrameworkService.getSdkReadableNameByFramework(framework);
}
String name = importElement.getVisibleName();
if (name != null) {
@NonNls String runnerId = findSdkByPackage(name);
if (runnerId != null) return runnerId;
}
}
}
return "";
}
@Nullable
private static @NonNls String findSdkByPackage(@NotNull String packageToFind) {
for (var factory : PyTestsSharedKt.getPythonFactories()) {
var packageRequired = factory.getPackageRequired();
if (packageRequired != null && packageRequired.equals(packageToFind)) {
return factory.getId();
}
}
return null;
}
@NotNull
private static String detectTestRunnerFromSetupPy(@NotNull Module module) {
final PyCallExpression setupCall = PyPackageUtil.findSetupCall(module);
@@ -99,11 +108,8 @@ final class TestRunnerDetector implements Function<Pair<Module, Collection<Virtu
final PyExpression argumentValue = setupCall.getKeywordArgument(argumentName);
if (argumentValue instanceof PyStringLiteralExpression) {
final String stringValue = ((PyStringLiteralExpression)argumentValue).getStringValue();
for (final String framework : PyTestFrameworkService.getFrameworkNamesArray()) {
if (stringValue.contains(framework)) {
return PyTestFrameworkService.getSdkReadableNameByFramework(framework);
}
}
@NonNls String runnerId = findSdkByPackage(stringValue);
if (runnerId != null) return runnerId;
}
}
return "";
@@ -4,10 +4,11 @@ package com.jetbrains.python.testing;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.module.Module;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareService;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceModuleConfigurator;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareModuleConfiguratorImpl;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareService;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceClasses;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceModuleConfigurator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -21,14 +22,11 @@ abstract public class TestRunnerService
private static final PyDefaultProjectAwareServiceClasses<ServiceState, TestRunnerService, AppService, ModuleService>
SERVICE_CLASSES = new PyDefaultProjectAwareServiceClasses<>(AppService.class, ModuleService.class);
private static final TestRunnerDetector DETECTOR = new TestRunnerDetector();
private final List<String> myConfigurations = new ArrayList<>();
private final List<String> myConfigurations;
protected TestRunnerService() {
super(new ServiceState());
myConfigurations.add(PythonTestConfigurationsModel.getPythonsUnittestName());
for (final String framework : PyTestFrameworkService.getFrameworkNamesArray()) {
myConfigurations.add(PyTestFrameworkService.getSdkReadableNameByFramework(framework));
}
myConfigurations = new ArrayList<>(ContainerUtil.map(PyTestsSharedKt.getPythonFactories(), o -> o.getId()));
}
@NotNull
@@ -1,20 +0,0 @@
// 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.python.testing;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.projectRoots.Sdk;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface VFSTestFrameworkListener {
void updateAllTestFrameworks(@NotNull Sdk sdk);
boolean isTestFrameworkInstalled(@Nullable Sdk sdk, @NotNull String name);
void setTestFrameworkInstalled(boolean installed, @NotNull String sdkHome, @NotNull String name);
@NotNull
static VFSTestFrameworkListener getInstance() {
return ApplicationManager.getApplication().getService(VFSTestFrameworkListener.class);
}
}
@@ -1,185 +0,0 @@
/*
* Copyright 2000-2017 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 com.jetbrains.python.testing;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.Alarm;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyPsiPackageUtil;
import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyPackageUtil;
import com.jetbrains.python.sdk.PythonSdkUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
public final class VFSTestFrameworkListenerImpl implements Disposable, VFSTestFrameworkListener {
private static final Logger LOG = Logger.getInstance(VFSTestFrameworkListenerImpl.class);
private final AtomicBoolean myIsUpdating = new AtomicBoolean(false);
private final PyTestFrameworkService myService = PyTestFrameworkService.getInstance();
private final MergingUpdateQueue myQueue;
public VFSTestFrameworkListenerImpl() {
myQueue = new MergingUpdateQueue("TestFrameworkChecker", 5000, true, null, this, null, Alarm.ThreadToUse.POOLED_THREAD);
}
@Override
public void updateAllTestFrameworks(@NotNull Sdk sdk) {
final Map<String, Boolean> whichInstalled = checkTestFrameworksInstalled(sdk, PyTestFrameworkService.getFrameworkNamesArray());
ApplicationManager.getApplication().invokeLater(() -> {
for (Map.Entry<String, Boolean> entry : whichInstalled.entrySet()) {
final Boolean installed = entry.getValue();
if (installed != null) {
//noinspection ConstantConditions
setTestFrameworkInstalled(installed, sdk.getHomePath(), entry.getKey());
}
}
});
}
private void scheduleTestFrameworkCheck(@NotNull Sdk sdk, @NotNull String testPackageName) {
myQueue.queue(new Update(Pair.create(sdk, testPackageName)) {
@Override
public void run() {
checkFrameworkInstalledAndUpdateSettings(sdk, testPackageName);
}
});
}
private void checkFrameworkInstalledAndUpdateSettings(@Nullable Sdk sdk, @NotNull String testPackageName) {
final Boolean installed = checkTestFrameworkInstalled(sdk, testPackageName);
if (installed != null) {
//noinspection ConstantConditions
ApplicationManager.getApplication().invokeLater(() -> setTestFrameworkInstalled(installed, sdk.getHomePath(), testPackageName));
}
}
/**
* @return null if we can't be sure
*/
@Contract("null, _ -> null")
private Boolean checkTestFrameworkInstalled(@Nullable Sdk sdk, @NotNull String testFrameworkName) {
return checkTestFrameworksInstalled(sdk, testFrameworkName).get(testFrameworkName);
}
@NotNull
private Map<String, Boolean> checkTestFrameworksInstalled(@Nullable Sdk sdk, String @NotNull ... testFrameworkNames) {
final Map<String, Boolean> result = new HashMap<>();
if (sdk == null || StringUtil.isEmptyOrSpaces(sdk.getHomePath())) {
LOG.info("Searching test runner in empty sdk");
return result;
}
final PyPackageManager manager = PyPackageManager.getInstance(sdk);
final boolean refreshed = PyPackageUtil.updatePackagesSynchronouslyWithGuard(manager, myIsUpdating);
if (refreshed) {
final List<PyPackage> packages = manager.getPackages();
if (packages != null) {
for (final String frameworkName : testFrameworkNames) {
result.put(frameworkName, PyPsiPackageUtil.findPackage(packages, frameworkName) != null);
}
}
}
return result;
}
@Override
public boolean isTestFrameworkInstalled(@Nullable final Sdk sdk, @NotNull final String name) {
if (sdk == null) {
return false;
}
final Boolean isInstalled = myService.getSdkToTestRunnerByName(name).get(sdk.getHomePath());
if (isInstalled == null) {
scheduleTestFrameworkCheck(sdk, name);
return true;
}
return isInstalled;
}
@Override
public final void setTestFrameworkInstalled(boolean installed, @NotNull String sdkHome, @NotNull String name) {
myService.getSdkToTestRunnerByName(name).put(sdkHome, installed);
}
public static class Listener implements BulkFileListener {
@Override
public void after(@NotNull List<? extends @NotNull VFileEvent> events) {
for (VFileEvent event : events) {
if (!(event.getFileSystem() instanceof LocalFileSystem) || event instanceof VFileContentChangeEvent) {
continue;
}
final String path = event.getPath();
final Set<String> existingFrameworks = new HashSet<>();
for (final String framework : PyTestFrameworkService.getFrameworkNamesSet()) {
if (path.contains(framework)) {
existingFrameworks.add(framework);
}
if (path.contains("py-1")) {
existingFrameworks.add(PyNames.PY_TEST);
}
}
if (existingFrameworks.isEmpty()) {
continue;
}
for (Sdk sdk : PythonSdkUtil.getAllSdks()) {
if (PythonSdkUtil.isRemote(sdk)) {
continue;
}
for (VirtualFile virtualFile : sdk.getRootProvider().getFiles(OrderRootType.CLASSES)) {
final String root = virtualFile.getCanonicalPath();
if (root != null && path.contains(root)) {
final String framework = existingFrameworks.iterator().next();
var instance = ObjectUtils.tryCast(VFSTestFrameworkListener.getInstance(), VFSTestFrameworkListenerImpl.class);
if (instance != null) {
instance.scheduleTestFrameworkCheck(sdk, framework);
}
return;
}
}
}
}
}
}
@Override
public void dispose() {
// Needed to dispose MergingUpdateQueue
}
}
@@ -8,12 +8,11 @@ import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.Processor
import com.intellij.util.ThreeState
import com.jetbrains.python.PyNames
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.impl.PyEvaluator
import com.jetbrains.python.psi.stubs.PyDecoratorStubIndex
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.python.testing.PyTestFrameworkService
import com.jetbrains.python.testing.PyTestFactory
import com.jetbrains.python.testing.TestRunnerService
import com.jetbrains.python.testing.isTestElement
@@ -75,7 +74,7 @@ private fun createFixture(decorator: PyDecorator): PyTestFixture? {
}
}
private val pyTestName = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.PY_TEST)
private const val pyTestId = PyTestFactory.id
/**
* Gets list of fixtures suitable for certain function.
@@ -115,6 +114,6 @@ internal fun getFixtures(module: Module, forWhat: PyFunction, typeEvalContext: T
}
internal fun isPyTestEnabled(module: Module) =
TestRunnerService.getInstance(module).projectConfiguration == pyTestName
TestRunnerService.getInstance(module).projectConfiguration == pyTestId
-27
View File
@@ -1,27 +0,0 @@
// 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
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.replaceService
import com.jetbrains.python.testing.VFSTestFrameworkListener
/**
* Async/timer services are useless in tests and also may lead to dispose problems when project is disposed before FS changes are processed
*/
internal fun replaceServicesWithMocks(disposable: Disposable) {
ApplicationManager.getApplication().replaceService(VFSTestFrameworkListener::class.java, object : VFSTestFrameworkListener {
override fun updateAllTestFrameworks(sdk: Sdk) {
}
override fun isTestFrameworkInstalled(sdk: Sdk?, name: String): Boolean {
return true
}
override fun setTestFrameworkInstalled(installed: Boolean, sdkHome: String, name: String) {
}
}, disposable)
}
@@ -147,7 +147,6 @@ public abstract class PyExecutionFixtureTestTask extends PyTestTask {
myFixture = fixtureFactory.createCodeInsightFixture(fixtureBuilder.getFixture());
myFixture.setTestDataPath(getTestDataPath());
myFixture.setUp();
PyEnvTestToolsKt.replaceServicesWithMocks(myFixture.getProjectDisposable());
final Module module = myFixture.getModule();
assert module != null;
@@ -9,10 +9,8 @@ import com.jetbrains.env.PyEnvTestCase;
import com.jetbrains.env.PyProcessWithConsoleTestTask;
import com.jetbrains.env.python.testing.CreateConfigurationTestTask.PyConfigurationValidationTask;
import com.jetbrains.env.ut.PyNoseTestProcessRunner;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.testing.PyNoseTestConfiguration;
import com.jetbrains.python.testing.PyNoseTestFactory;
import com.jetbrains.python.testing.PyTestFrameworkService;
import com.jetbrains.python.tools.sdkTools.SdkCreationType;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
@@ -26,7 +24,6 @@ import static org.junit.Assert.assertEquals;
*/
@EnvTestTagsRequired(tags = "nose")
public final class PythonNoseTestingTest extends PyEnvTestCase {
private final String myFrameworkName = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.NOSE_TEST);
@Test
public void testNoseGenerators() {
@@ -111,7 +108,7 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
@Test
public void testMultipleCases() {
runPythonTest(
new CreateConfigurationMultipleCasesTask<>(PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.NOSE_TEST),
new CreateConfigurationMultipleCasesTask<>(new PyNoseTestFactory().getId(),
PyNoseTestConfiguration.class));
}
@@ -174,13 +171,13 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
@Test
public void testConfigurationProducer() {
runPythonTest(
new CreateConfigurationByFileTask<>(myFrameworkName, PyNoseTestConfiguration.class));
new CreateConfigurationByFileTask<>(getFrameworkId(), PyNoseTestConfiguration.class));
}
@Test
public void testConfigurationProducerOnDirectory() {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask<>(myFrameworkName,
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask<>(getFrameworkId(),
PyNoseTestConfiguration.class));
}
@@ -188,7 +185,7 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
public void testRenameClass() {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameClassTask<>(
myFrameworkName,
getFrameworkId(),
PyNoseTestConfiguration.class));
}
@@ -291,4 +288,9 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
runner.getFormattedTestTree());
}
}
@NotNull
private static String getFrameworkId() {
return PyNoseTestFactory.id;
}
}
@@ -21,7 +21,6 @@ import com.jetbrains.env.PyProcessWithConsoleTestTask;
import com.jetbrains.env.python.testing.CreateConfigurationTestTask.PyConfigurationValidationTask;
import com.jetbrains.env.ut.PyTestTestProcessRunner;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
@@ -55,8 +54,6 @@ import static org.junit.Assert.*;
@EnvTestTagsRequired(tags = "pytest")
public final class PythonPyTestingTest extends PyEnvTestCase {
private final String myFrameworkName = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.PY_TEST);
// Ensures setup/teardown does not break anything
@Test
@@ -514,7 +511,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testClosestSrcIsWorkDirOnNewConfig() {
runPythonTest(
new CreateConfigurationTestTask<>(myFrameworkName,
new CreateConfigurationTestTask<>(getFrameworkId(),
PyTestConfiguration.class) {
@NotNull
@Override
@@ -582,7 +579,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testConfigurationProducer() {
runPythonTest(
new CreateConfigurationByFileTask<>(myFrameworkName, PyTestConfiguration.class));
new CreateConfigurationByFileTask<>(getFrameworkId(), PyTestConfiguration.class));
}
@Test
@@ -614,7 +611,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testMultipleCases() {
runPythonTest(
new CreateConfigurationMultipleCasesTask<>(myFrameworkName, PyTestConfiguration.class));
new CreateConfigurationMultipleCasesTask<>(getFrameworkId(), PyTestConfiguration.class));
}
/**
@@ -624,7 +621,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testConfigurationByContext() {
runPythonTest(
new CreateConfigurationTestTask<>(myFrameworkName, PyTestConfiguration.class) {
new CreateConfigurationTestTask<>(getFrameworkId(), PyTestConfiguration.class) {
@NotNull
@@ -736,14 +733,14 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testConfigurationProducerOnDirectory() {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask<>(myFrameworkName,
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask<>(getFrameworkId(),
PyTestConfiguration.class));
}
@Test
public void testProduceConfigurationOnFile() {
runPythonTest(
new CreateConfigurationByFileTask<>(myFrameworkName,
new CreateConfigurationByFileTask<>(getFrameworkId(),
PyTestConfiguration.class, "spam.py") {
@NotNull
@Override
@@ -757,7 +754,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
public void testRenameClass() {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameClassTask<>(
myFrameworkName,
getFrameworkId(),
PyTestConfiguration.class));
}
@@ -1034,4 +1031,9 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
}
});
}
@NotNull
private static String getFrameworkId() {
return PyTestFactory.id;
}
}
@@ -8,7 +8,7 @@ import com.intellij.testFramework.VfsTestUtil
import com.jetbrains.python.PyNames
import com.jetbrains.python.fixtures.PyTestCase
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.testing.PyTestFrameworkService
import com.jetbrains.python.testing.PyTestFactory
import com.jetbrains.python.testing.PythonTestConfigurationsModel
import com.jetbrains.python.testing.TestRunnerService
@@ -45,7 +45,7 @@ class PyTestCreationModelTest : PyTestCase() {
}
fun testWithPyTest() {
service.projectConfiguration = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.PY_TEST)
service.projectConfiguration = PyTestFactory().id
val modelToTestClass = getModel()!!
assertEquals("test_create_tst.py", modelToTestClass.fileName)
assertEquals("", modelToTestClass.className)
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiFile;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.testing.PyTestFactory;
import com.jetbrains.python.testing.PythonTestConfigurationsModel;
import com.jetbrains.python.testing.TestRunnerService;
import org.jetbrains.annotations.NotNull;
@@ -46,7 +47,7 @@ public final class PyTestCreatorTest extends PyTestCase {
final PyTestCreationModel model = prepareAndCreateModel();
boolean p2k = LanguageLevel.forElement(myFixture.getFile()).isPython2();
TestRunnerService testRunnerService = TestRunnerService.getInstance(myFixture.getModule());
testRunnerService.setProjectConfiguration(PyNames.PY_TEST);
testRunnerService.setProjectConfiguration(PyTestFactory.id);
checkResult(model, (p2k ? "create_tst_class.expected_pytest_2k.py" : "create_tst_class.expected_pytest_3k.py"));
@@ -14,7 +14,7 @@ class PyTestFixtureAndParametrizedTest : PyTestCase() {
override fun setUp() {
super.setUp()
TestRunnerService.getInstance(myFixture.module).projectConfiguration =
PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.PY_TEST)
PyTestFactory().id
}
fun testInspection() {