PY-2410: Support twisted.trial

Trial is test framework for Twisted much like unittests.
It is fully supported on Leonid side, we only need to create wrappers.

There were too many places with framework naming code making
it pretty hard to support new frameworks.

It is now unified. Any service (except framework-specific) should work
via PyTestFrameworkService to obtain list of frameworks and its
This commit is contained in:
Ilya.Kazakevich
2017-07-24 18:26:39 +03:00
parent 684449b5f0
commit e359fbe832
24 changed files with 1049 additions and 738 deletions

View File

@@ -0,0 +1,5 @@
Plugin to support twisted trial.
From: http://twistedmatrix.com/documents/13.1.0/core/howto/plugin.html
if a directory which has been added to sys.path (typically by adding it to the PYTHONPATH environment variable) contains a directory
named twisted/plugins/, each .py file in that directory will be loaded as a source of plugins.

View File

@@ -0,0 +1,37 @@
import sys
from teamcity.unittestpy import TeamcityTestResult
from twisted.trial.reporter import Reporter
from twisted.python.failure import Failure
from twisted.plugins.twisted_trial import _Reporter
class FailureWrapper(Failure):
def __getitem__(self, key):
return self.value[key]
class TeamcityReporter(TeamcityTestResult, Reporter):
def __init__(self,
stream=sys.stdout,
tbformat='default',
realtime=False,
publisher=None):
TeamcityTestResult.__init__(self)
Reporter.__init__(self,
stream=stream,
tbformat=tbformat,
realtime=realtime,
publisher=publisher)
def addError(self, test, failure, *k):
super(TeamcityReporter, self).addError(test, FailureWrapper(failure), *k)
Teamcity = _Reporter("Teamcity Reporter",
"twisted.plugins.teamcity_plugin",
description="teamcity messages",
longOpt="teamcity",
shortOpt="teamcity",
klass="TeamcityReporter")

View File

@@ -0,0 +1,28 @@
# coding=utf-8
import os
from pprint import pprint
from _jb_runner_tools import jb_start_tests, jb_doc_args
from twisted.scripts import trial
import sys
if __name__ == '__main__':
# This folder should be in sys.path because teamcity twisted plugin is there
sys.path.append(os.path.join(os.path.dirname(__file__), "__jb.for_twisted"))
path, targets, additional_args = jb_start_tests()
sys.path.append(os.getcwd()) # Current dir must be in sys.path according to trial docs
sys.argv.append("--reporter=teamcity")
if path:
assert os.path.exists(path), path + " does not exist"
# assert os.path.isfile(path), path + " is folder. Provide its name as python target (dot separated)"
sys.argv.append(os.path.normpath(path))
elif targets:
sys.argv += targets
sys.argv += additional_args
jb_doc_args("trial", sys.argv[1:])
trial.run()

View File

@@ -1,4 +1,7 @@
Test runners wraps python framework and provides teamcity protocol.
They are are distributed separately.
Copy latest version from https://github.com/JetBrains/teamcity-messages.git/teamcity
See https://confluence.jetbrains.com/display/~link/PyCharm+test+runners+protocol
See https://confluence.jetbrains.com/display/~link/PyCharm+test+runners+protocol
When updating, place "twisted.plugins" one level up because "twisted" should be in sys.path.
See twisted/plugins/README.txt

View File

@@ -210,6 +210,7 @@ public class PyNames {
public static final String NOSE_TEST = "nose";
public static final String PY_TEST = "pytest";
public static final String TRIAL_TEST = "trial";
public static final String TEST_CASE = "TestCase";

View File

@@ -12,8 +12,8 @@ envs {
packages = ["pip", "setuptools"]
_64Bits = true
conda "django19", "2.7", ["django==1.9", "tox", "nose", "pytest", "behave", "lettuce>=0.2.22", "unittest2", "teamcity-messages"], true
textfile "django19/tags.txt", "python2.7\ndjango\nnose\npytest\nbehave\nlettuce\npackaging\ntox\nunittest2"
conda "django19", "2.7", ["django==1.9", "tox", "nose", "pytest", "Twisted", "behave", "lettuce>=0.2.22", "unittest2", "teamcity-messages"], true
textfile "django19/tags.txt", "python2.7\ndjango\nnose\npytest\nbehave\nlettuce\npackaging\ntox\nunittest2\ntwisted"
conda "django110", "3.4", ["django==1.10", "django-nose"], false
textfile "django110/tags.txt", "python3.4\ndjango\nskeletons\ndjango-nose"

View File

@@ -817,6 +817,8 @@ runcfg.pytest.target=&Target:
runcfg.pytest.parameters=&Options:
runcfg.pytest.keywords=&Keywords:
### trial run conf
runcfg.trial.display_name=Twsited Trial
### test run configuration
runcfg.test.display_name=Python tests

View File

@@ -60,6 +60,7 @@ public enum PythonHelper implements HelperPackage {
UNITTEST("pycharm", "_jb_unittest_runner"),
PYTEST("pycharm", "_jb_pytest_runner"),
TRIAL("pycharm", "_jb_trialtest_runner"),
NOSE("pycharm", "_jb_nosetest_runner"),
BEHAVE("pycharm", "behave_runner"),

View File

@@ -38,8 +38,9 @@ public class PyTestFrameworkService implements PersistentStateComponent<PyTestFr
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};
private static final String[] FRAMEWORK_NAMES = {PyNames.PY_TEST, PyNames.NOSE_TEST, PyNames.TRIAL_TEST};
@Override
public PyTestFrameworkService getState() {
@@ -62,6 +63,17 @@ public class PyTestFrameworkService implements PersistentStateComponent<PyTestFr
return FRAMEWORK_NAMES.clone();
}
/**
* @return pypi package that contains this framework
*/
@NotNull
public static String getPackageByFramework(@NotNull final String frameworkName) {
if (frameworkName.equals(PyNames.TRIAL_TEST)) {
return "Twisted";
}
return frameworkName;
}
@NotNull
public static String getSdkReadableNameByFramework(@NotNull final String frameworkName) {
switch (frameworkName) {
@@ -71,6 +83,9 @@ public class PyTestFrameworkService implements PersistentStateComponent<PyTestFr
case PyNames.NOSE_TEST: {
return PyBundle.message("runcfg.nosetests.display_name");
}
case PyNames.TRIAL_TEST: {
return PyBundle.message("runcfg.trial.display_name");
}
}
throw new IllegalArgumentException("Unknown framework " + frameworkName);
}
@@ -84,6 +99,9 @@ public class PyTestFrameworkService implements PersistentStateComponent<PyTestFr
case PyNames.NOSE_TEST: {
return SDK_TO_NOSETEST;
}
case PyNames.TRIAL_TEST: {
return SDK_TO_TRIALTEST;
}
}
throw new IllegalArgumentException("Unknown framework " + frameworkName);
}

View File

@@ -138,7 +138,7 @@ private fun VirtualFile.asPyFile(project: Project): PyFile? {
class PyTestLegacyConfigurationAdapter<in T : PyAbstractTestConfiguration>(newConfig: T)
: JDOMExternalizable {
private val configManager: LegacyConfigurationManager<*, *>
private val configManager: LegacyConfigurationManager<*, *>?
private val project = newConfig.project
/**
@@ -168,12 +168,17 @@ class PyTestLegacyConfigurationAdapter<in T : PyAbstractTestConfiguration>(newCo
configManager = LegacyConfigurationManagerUnit(newConfig)
}
else -> {
throw IllegalAccessException("Unknown config: $newConfig")
configManager = null
}
}
}
override fun readExternal(element: Element) {
val configManager = configManager
if (configManager == null) {
containsLegacyInformation = false
return
}
val legacyConfig = configManager.legacyConfig
if (legacyConfig is RunConfiguration) {
(legacyConfig as RunConfiguration).readExternal(element)
@@ -190,7 +195,7 @@ class PyTestLegacyConfigurationAdapter<in T : PyAbstractTestConfiguration>(newCo
override fun writeExternal(element: Element) {
if (containsLegacyInformation ?: return) {
val legacyConfig = configManager.legacyConfig
val legacyConfig = configManager?.legacyConfig
if (legacyConfig is RunConfiguration) {
(legacyConfig as RunConfiguration).writeExternal(element)
}
@@ -203,7 +208,7 @@ class PyTestLegacyConfigurationAdapter<in T : PyAbstractTestConfiguration>(newCo
fun copyFromLegacyIfNeeded() {
assert(project.isInitialized, { "Initialized project required" })
if (containsLegacyInformation ?: return && !(legacyInformationCopiedToNew ?: false) && SwingUtilities.isEventDispatchThread()) {
configManager.copyFromLegacy()
configManager?.copyFromLegacy()
legacyInformationCopiedToNew = true
}
}

View File

@@ -76,7 +76,8 @@ import com.jetbrains.reflection.getProperties
val factories: Array<PythonConfigurationFactoryBase> = arrayOf(
PyUnitTestFactory,
PyTestFactory,
PyNoseTestFactory)
PyNoseTestFactory,
PyTrialTestFactory)
internal fun getAdditionalArgumentsPropertyName() = com.jetbrains.python.testing.PyAbstractTestConfiguration::additionalArguments.name

View File

@@ -0,0 +1,60 @@
/*
* 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.execution.Executor
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.PythonHelper
/**
* Py.test runner
*/
class PyTrialTestSettingsEditor(configuration: PyAbstractTestConfiguration) :
PyAbstractTestSettingsEditor(PyTestSharedForm.create(configuration))
class PyTrialTestExecutionEnvironment(configuration: PyTrialTestConfiguration, environment: ExecutionEnvironment) :
PyTestExecutionEnvironment<PyTrialTestConfiguration>(configuration, environment) {
override fun getRunner() = PythonHelper.TRIAL
}
class PyTrialTestConfiguration(project: Project, factory: PyTrialTestFactory)
: PyAbstractTestConfiguration(project, factory, PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.TRIAL_TEST)) {
override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState? =
PyTrialTestExecutionEnvironment(this, environment)
override fun createConfigurationEditor(): SettingsEditor<PyAbstractTestConfiguration> =
PyTrialTestSettingsEditor(this)
override fun shouldSeparateTargetPath() = false
override fun isFrameworkInstalled() = VFSTestFrameworkListener.getInstance().isTestFrameworkInstalled(sdk, PyNames.TRIAL_TEST)
}
object PyTrialTestFactory : PyAbstractTestFactory<PyTrialTestConfiguration>() {
override fun createTemplateConfiguration(project: Project) = PyTrialTestConfiguration(project, this)
override fun getName(): String = PyTestFrameworkService.getSdkReadableNameByFramework(PyNames.TRIAL_TEST)
}

View File

@@ -135,12 +135,12 @@ public class VFSTestFrameworkListener {
* @return null if we can't be sure
*/
@Contract("null, _ -> null")
private Boolean checkTestFrameworkInstalled(@Nullable Sdk sdk, @NotNull String testPackageName) {
return checkTestFrameworksInstalled(sdk, testPackageName).get(testPackageName);
private Boolean checkTestFrameworkInstalled(@Nullable Sdk sdk, @NotNull String testFrameworkName) {
return checkTestFrameworksInstalled(sdk, testFrameworkName).get(testFrameworkName);
}
@NotNull
private Map<String, Boolean> checkTestFrameworksInstalled(@Nullable Sdk sdk, @NotNull String... testPackageNames) {
private Map<String, Boolean> checkTestFrameworksInstalled(@Nullable Sdk sdk, @NotNull String... testFrameworkNames) {
final Map<String, Boolean> result = new HashMap<>();
if (sdk == null || StringUtil.isEmptyOrSpaces(sdk.getHomePath())) {
LOG.info("Searching test runner in empty sdk");
@@ -151,8 +151,9 @@ public class VFSTestFrameworkListener {
if (refreshed) {
final List<PyPackage> packages = manager.getPackages();
if (packages != null) {
for (String name : testPackageNames) {
result.put(name, PyPackageUtil.findPackage(packages, name) != null);
for (final String frameworkName : testFrameworkNames) {
final String packageName = PyTestFrameworkService.getPackageByFramework(frameworkName);
result.put(frameworkName, PyPackageUtil.findPackage(packages, packageName) != null);
}
}
}

View File

@@ -1 +0,0 @@
__author__ = 'ktisha'

View File

@@ -0,0 +1,56 @@
/*
* 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.env.python.testing;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.openapi.application.ReadAction;
import com.intellij.util.ThrowableRunnable;
import com.jetbrains.env.PyProcessWithConsoleTestTask;
import com.jetbrains.env.ut.PyScriptTestProcessRunner;
import com.jetbrains.python.sdkTools.SdkCreationType;
import org.jetbrains.annotations.NotNull;
/**
* Checks each method by name
*/
abstract class PyTestsFunctionBasedRunner<T extends PyScriptTestProcessRunner<?>> extends PyProcessWithConsoleTestTask<T> {
@NotNull
protected final String[] myFunctionsToCheck;
protected PyTestsFunctionBasedRunner(@NotNull final String... functionsToCheck) {
super("/testRunner/env/testsInFolder", SdkCreationType.EMPTY_SDK);
assert functionsToCheck.length > 0 : "Provide functions";
myFunctionsToCheck = functionsToCheck.clone();
}
@Override
protected final void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
for (final String functionName : myFunctionsToCheck) {
ReadAction.run((ThrowableRunnable<AssertionError>)() -> {
final AbstractTestProxy method = runner.findTestByName(functionName);
checkMethod(method, functionName);
});
}
}
/**
* Called for each method
*/
protected abstract void checkMethod(@NotNull final AbstractTestProxy method, @NotNull final String functionName);
}

View File

@@ -0,0 +1,53 @@
/*
* 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.env.python.testing;
import com.intellij.execution.Location;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.jetbrains.env.ut.PyScriptTestProcessRunner;
import com.jetbrains.python.psi.PyFunction;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
/**
* Checks tests are resolved when launched from subfolder
*/
abstract class PyTestsInSubFolderRunner<T extends PyScriptTestProcessRunner<?>> extends PyTestsFunctionBasedRunner<T> {
/**
* @param functionsToCheck name of functions that should be found in test tree and resolved
*/
PyTestsInSubFolderRunner(@NotNull final String... functionsToCheck) {
super(functionsToCheck);
}
@Override
protected void checkMethod(@NotNull final AbstractTestProxy method, @NotNull final String functionName) {
final Location<?> methodLocation = method.getLocation(getProject(), GlobalSearchScope.moduleScope(myFixture.getModule()));
Assert.assertNotNull("Failed to resolve method location " + method, methodLocation);
final PsiElement methodPsiElement = methodLocation.getPsiElement();
Assert.assertNotNull("Failed to get PSI for method location", methodPsiElement);
Assert.assertThat("Wrong test returned", methodPsiElement, Matchers.instanceOf(PyFunction.class));
Assert.assertEquals("Wrong method name", functionName, ((PsiNamedElement)methodPsiElement).getName());
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.env.python.testing;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.sm.runner.ui.MockPrinter;
import com.jetbrains.env.ut.PyScriptTestProcessRunner;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
/**
* Checks test output is correct
*/
abstract class PyTestsOutputRunner<T extends PyScriptTestProcessRunner<?>> extends PyTestsFunctionBasedRunner<T> {
PyTestsOutputRunner(@NotNull final String... functionsToCheck) {
super(functionsToCheck);
}
@Override
protected void checkMethod(@NotNull final AbstractTestProxy method, @NotNull final String functionName) {
if (functionName.endsWith("test_metheggs")) {
Assert.assertThat("Method output is broken",
MockPrinter.fillPrinter(method).getStdOut().trim(), Matchers.containsString("I am method"));
}
else if (functionName.endsWith("test_funeggs")) {
Assert.assertThat("Function output is broken",
MockPrinter.fillPrinter(method).getStdOut().trim(), Matchers.containsString("I am function"));
}
else if (functionName.endsWith("test_first") || functionName.endsWith("test_second")) {
// No output expected
}
else {
throw new AssertionError("Unknown function " + functionName);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2000-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 com.jetbrains.env.python.testing
import com.google.common.collect.Sets
import com.jetbrains.env.PyProcessWithConsoleTestTask
import com.jetbrains.env.ut.PyScriptTestProcessRunner
import com.jetbrains.env.ut.PyUnitTestProcessRunner
import com.jetbrains.python.sdkTools.SdkCreationType
import java.util.function.Function
/**
* [PyProcessWithConsoleTestTask] to be used with python unittest and trial. It saves you from boilerplate
* by setting working folder and creating [PyUnitTestProcessRunner]
* @author Ilya.Kazakevich
*/
internal abstract class PyUnitTestLikeProcessWithConsoleTestTask<T :
PyScriptTestProcessRunner<*>> @JvmOverloads constructor(relativePathToTestData: String,
val myScriptName: String,
val myRerunFailedTests: Int = 0,
protected val processRunnerCreator: Function<TestRunnerConfig, T>) :
PyProcessWithConsoleTestTask<T>(relativePathToTestData, SdkCreationType.SDK_PACKAGES_ONLY) {
override fun getTagsToCover(): Set<String> = Sets.newHashSet("python2.6", "python2.7", "python3.5", "python3.6", "jython", "pypy",
"IronPython")
@Throws(Exception::class)
override fun createProcessRunner(): T =
processRunnerCreator.apply(TestRunnerConfig(myScriptName, myRerunFailedTests))
}
data class TestRunnerConfig(val scriptName: String, val rerunFailedTests: Int)

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2000-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 com.jetbrains.env.python.testing;
import com.google.common.collect.Sets;
import com.intellij.execution.Location;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.sm.runner.ui.MockPrinter;
import com.intellij.openapi.application.ReadAction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.ThrowableRunnable;
import com.jetbrains.env.PyProcessWithConsoleTestTask;
import com.jetbrains.env.ut.PyScriptTestProcessRunner;
import com.jetbrains.env.ut.PyUnitTestProcessRunner;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.sdkTools.SdkCreationType;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import java.util.Set;
/**
* {@link PyProcessWithConsoleTestTask} to be used with python unittest. It saves you from boilerplate
* by setting working folder and creating {@link PyUnitTestProcessRunner}
*
* @author Ilya.Kazakevich
*/
abstract class PyUnitTestProcessWithConsoleTestTask extends PyProcessWithConsoleTestTask<PyUnitTestProcessRunner> {
@NotNull
protected final String myScriptName;
private final int myRerunFailedTests;
PyUnitTestProcessWithConsoleTestTask(@NotNull final String relativePathToTestData, @NotNull final String scriptName) {
this(relativePathToTestData, scriptName, 0);
}
PyUnitTestProcessWithConsoleTestTask(@NotNull final String relativePathToTestData,
@NotNull final String scriptName,
final int rerunFailedTests) {
super(relativePathToTestData, SdkCreationType.SDK_PACKAGES_ONLY);
myScriptName = scriptName;
myRerunFailedTests= rerunFailedTests;
}
@Nullable
@Override
public Set<String> getTagsToCover() {
return Sets.newHashSet("python2.6", "python2.7", "python3.5", "python3.6", "jython", "pypy", "IronPython");
}
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(myScriptName, myRerunFailedTests);
}
/**
* Checks each method by name
*/
abstract static class PyTestsFunctionBasedRunner<T extends PyScriptTestProcessRunner<?>> extends PyProcessWithConsoleTestTask<T> {
@NotNull
protected final String[] myFunctionsToCheck;
protected PyTestsFunctionBasedRunner(@NotNull final String... functionsToCheck) {
super("/testRunner/env/testsInFolder", SdkCreationType.EMPTY_SDK);
assert functionsToCheck.length > 0 : "Provide functions";
myFunctionsToCheck = functionsToCheck.clone();
}
@Override
protected final void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
for (final String functionName : myFunctionsToCheck) {
ReadAction.run((ThrowableRunnable<AssertionError>)() -> {
final AbstractTestProxy method = runner.findTestByName(functionName);
checkMethod(method, functionName);
});
}
}
/**
* Called for each method
*/
protected abstract void checkMethod(@NotNull final AbstractTestProxy method, @NotNull final String functionName);
}
/**
* Checks tests are resolved when launched from subfolder
*/
abstract static class PyTestsInSubFolderRunner<T extends PyScriptTestProcessRunner<?>> extends PyTestsFunctionBasedRunner<T> {
/**
* @param functionsToCheck name of functions that should be found in test tree and resolved
*/
PyTestsInSubFolderRunner(@NotNull final String... functionsToCheck) {
super(functionsToCheck);
}
@Override
protected void checkMethod(@NotNull final AbstractTestProxy method, @NotNull final String functionName) {
final Location<?> methodLocation = method.getLocation(getProject(), GlobalSearchScope.moduleScope(myFixture.getModule()));
Assert.assertNotNull("Failed to resolve method location " + method, methodLocation);
final PsiElement methodPsiElement = methodLocation.getPsiElement();
Assert.assertNotNull("Failed to get PSI for method location", methodPsiElement);
Assert.assertThat("Wrong test returned", methodPsiElement, Matchers.instanceOf(PyFunction.class));
Assert.assertEquals("Wrong method name", functionName, ((PsiNamedElement)methodPsiElement).getName());
}
}
/**
* Checks test output is correct
*/
abstract static class PyTestsOutputRunner<T extends PyScriptTestProcessRunner<?>> extends PyTestsFunctionBasedRunner<T> {
PyTestsOutputRunner(@NotNull final String... functionsToCheck) {
super(functionsToCheck);
}
@Override
protected void checkMethod(@NotNull final AbstractTestProxy method, @NotNull final String functionName) {
if (functionName.endsWith("test_metheggs")) {
Assert.assertThat("Method output is broken",
MockPrinter.fillPrinter(method).getStdOut().trim(), Matchers.containsString("I am method"));
}
else if (functionName.endsWith("test_funeggs")) {
Assert.assertThat("Function output is broken",
MockPrinter.fillPrinter(method).getStdOut().trim(), Matchers.containsString("I am function"));
}
else if (functionName.endsWith("test_first") || functionName.endsWith("test_second")) {
// No output expected
}
else {
throw new AssertionError("Unknown function " + functionName);
}
}
}
}

View File

@@ -119,8 +119,8 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
@Test
public void testTestsInSubFolderResolvable() throws Exception {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask.PyTestsInSubFolderRunner<PyNoseTestProcessRunner>("test_metheggs", "test_funeggs",
"test_first") {
new PyTestsInSubFolderRunner<PyNoseTestProcessRunner>("test_metheggs", "test_funeggs",
"test_first") {
@NotNull
@Override
protected PyNoseTestProcessRunner createProcessRunner() throws Exception {
@@ -141,7 +141,7 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
@Test
public void testOutput() throws Exception {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask.PyTestsOutputRunner<PyNoseTestProcessRunner>("test_metheggs", "test_funeggs", "test_first") {
new PyTestsOutputRunner<PyNoseTestProcessRunner>("test_metheggs", "test_funeggs", "test_first") {
@NotNull
@Override
protected PyNoseTestProcessRunner createProcessRunner() throws Exception {

View File

@@ -292,8 +292,8 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testTestsInSubFolderResolvable() throws Exception {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask.PyTestsInSubFolderRunner<PyTestTestProcessRunner>("test_metheggs", "test_funeggs",
"test_first") {
new PyTestsInSubFolderRunner<PyTestTestProcessRunner>("test_metheggs", "test_funeggs",
"test_first") {
@NotNull
@Override
protected PyTestTestProcessRunner createProcessRunner() throws Exception {
@@ -314,7 +314,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
@Test
public void testOutput() throws Exception {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask.PyTestsOutputRunner<PyTestTestProcessRunner>("test_metheggs", "test_funeggs", "test_first") {
new PyTestsOutputRunner<PyTestTestProcessRunner>("test_metheggs", "test_funeggs", "test_first") {
@NotNull
@Override
protected PyTestTestProcessRunner createProcessRunner() throws Exception {

View File

@@ -0,0 +1,32 @@
/*
* 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.env.python.testing
import com.jetbrains.env.EnvTestTagsRequired
import com.jetbrains.env.ut.PyScriptTestProcessRunner
import com.jetbrains.python.testing.PyTrialTestConfiguration
import com.jetbrains.python.testing.PyTrialTestFactory
// Twisted trial test case
@EnvTestTagsRequired(tags = arrayOf("twisted"))
internal class PythonTrialTest : PythonUnitTestingLikeTest<PyTrialTestProcessRunner>() {
override fun createTestRunner(config: TestRunnerConfig) = PyTrialTestProcessRunner(config.scriptName, config.rerunFailedTests)
}
class PyTrialTestProcessRunner(scriptName: String,
timesToRerunFailedTests: Int) : PyScriptTestProcessRunner<PyTrialTestConfiguration>(
PyTrialTestFactory, PyTrialTestConfiguration::class.java, scriptName, timesToRerunFailedTests)

View File

@@ -0,0 +1,388 @@
/*
* 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.env.python.testing;
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.EdtTestUtil;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import com.jetbrains.env.PyEnvTestCase;
import com.jetbrains.env.ut.PyScriptTestProcessRunner;
import com.jetbrains.env.ut.PyUnitTestProcessRunner;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.LanguageLevel;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.List;
import static com.jetbrains.env.ut.PyScriptTestProcessRunner.TEST_TARGET_PREFIX;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
/**
* Parent of unittest and trial test.
* All tests here should run with trial and unit
*/
public abstract class PythonUnitTestingLikeTest<T extends PyScriptTestProcessRunner<?>> extends PyEnvTestCase {
/**
* Ensure "[" in test does not break output
*/
@Test
public void testEscaping() throws Exception {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "test_escaping.py", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(0, runner.getPassedTestsCount());
assertEquals(1, runner.getFailedTestsCount());
}
});
}
abstract T createTestRunner(@NotNull final TestRunnerConfig config);
/**
* Ensure that sys.path[0] is script folder, not helpers folder
*/
@Test
public void testSysPath() throws Exception {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("testRunner/env/unit/sysPath", "test_sample.py", this::createTestRunner) {
@NotNull
@Override
protected T createProcessRunner() throws Exception {
return getProcessRunnerCreator().apply(new TestRunnerConfig(toFullPath(getMyScriptName()), 0));
}
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
Assert.assertEquals(runner.getFormattedTestTree(), 1, runner.getAllTestsCount());
myFixture.getTempDirFixture().getFile("sysPath");
final VirtualFile folderWithScript = myFixture.getTempDirFixture().getFile(".");
assert folderWithScript != null : "No folder for script " + getMyScriptName();
Assert.assertThat("sys.path[0] should point to folder with test, while it does not", stdout,
Matchers.containsString(String.format("path[0]=%s", new File(folderWithScript.getPath()).getAbsolutePath())));
}
});
}
@Test
public void testUTRunner() {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "test1.py", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 2, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getPassedTestsCount());
runner.assertAllTestsPassed();
}
});
}
/**
* tests with docstrings are reported as "test.name (text)" by unittest.
*/
@Test
public void testWithDocString() throws Exception {
runPythonTest(
new PyUnitTestLikeProcessWithConsoleTestTask<T>("testRunner/env/unit/withDocString", "test_test.py", this::createTestRunner) {
@NotNull
@Override
protected T createProcessRunner() throws Exception {
return getProcessRunnerCreator().apply(new TestRunnerConfig(toFullPath(getMyScriptName()), 1));
}
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
if (runner.getCurrentRerunStep() == 0) {
assertEquals("test with docstring produced bad tree", "Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..SomeTestCase\n" +
"...testSomething (Only with docstring test is parsed with extra space)(+)\n" +
"...testSomethingBad (Fail)(-)\n", runner.getFormattedTestTree());
}
else {
assertEquals("test with docstring failed to rerun",
"Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..SomeTestCase\n" +
"...testSomethingBad (Fail)(-)\n", runner.getFormattedTestTree());
}
}
});
}
@Test
public void testClass() {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit",
TEST_TARGET_PREFIX + "test_file.GoodTest", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(1, runner.getPassedTestsCount());
}
});
}
@Test
public void testUTRunner2() {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "test2.py", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 3, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 1, runner.getPassedTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getFailedTestsCount());
}
});
}
/**
* Ensures file references are highlighted for python traceback
*/
@Test
public void testUnitTestFileReferences() {
final String fileName = "reference_tests.py";
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", fileName, this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
final List<String> fileNames = runner.getHighlightedStringsInConsole().getSecond();
Assert.assertTrue(String.format("Not enough highlighted entries(%s) in the following output: %s",
StringUtil.join(fileNames, ","),
runner.getAllConsoleText()),
fileNames.size() >= 3);
// UnitTest highlights file name
Assert.assertThat("Bad line highlighted", fileNames, hasItem(endsWith(fileName)));
}
});
}
/**
* Ensures that skipped and erroneous tests do not lead to suite ignorance
*/
@Test
public void testUTSkippedAndIgnored() {
runPythonTest(
new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "test_with_skips_and_errors.py", this::createTestRunner) {
@Override
public boolean isLanguageLevelSupported(@NotNull final LanguageLevel level) {
// This test requires unittest to have decorator "test" that does not exists in 2.6
return level.compareTo(LanguageLevel.PYTHON26) > 0;
}
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(4, runner.getAllTestsCount());
assertEquals(2, runner.getPassedTestsCount());
assertEquals(2, runner.getFailedTestsCount());
Assert.assertFalse("Suite is not finished", runner.getTestProxy().isInterrupted());
}
});
}
@Test
public void testDependent() {
runPythonTest(
new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "dependentTests/test_my_class.py", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(1, runner.getPassedTestsCount());
}
});
}
/**
* Ensures that python target pointing to module works correctly
*/
@Test
public void testRunModuleAsFile() throws Exception {
runPythonTest(new RunModuleAsFileTask<PyUnitTestProcessRunner>() {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(TARGET, 0);
}
});
}
/**
* Ensure rerun test works even if test is declared in parent
* See https://github.com/JetBrains/teamcity-messages/issues/117
*/
@Test
public void testRerunDerivedClass() throws Exception {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "rerun_derived.py", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
Assert.assertThat("Wrong number of failed tests", runner.getFailedTestsCount(), equalTo(1));
final int expectedNumberOfTests = (runner.getCurrentRerunStep() == 0 ? 2 : 1);
Assert.assertThat("Wrong number tests", runner.getAllTestsCount(), equalTo(expectedNumberOfTests));
if (runner.getCurrentRerunStep() == 1) {
// Make sure derived tests are launched, not abstract
Assert.assertEquals("Wrong tests after rerun",
"Test tree:\n" +
"[root]\n" +
".rerun_derived\n" +
"..TestDerived\n" +
"...test_a(-)\n", runner.getFormattedTestTree());
}
}
@NotNull
@Override
protected T createProcessRunner() throws Exception {
return getProcessRunnerCreator().apply(new TestRunnerConfig(getMyScriptName(), 2));
}
});
}
// Ensures setup/teardown does not break anything
@Test
public void testSetupTearDown() throws Exception {
runPythonTest(new SetupTearDownTestTask<PyUnitTestProcessRunner>() {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner("test_test.py", 1);
}
});
}
@Test
public void testFolder() {
runPythonTest(new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "test_folder/", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(5, runner.getAllTestsCount());
assertEquals(3, runner.getPassedTestsCount());
}
});
}
/**
* Run tests, delete file and click "rerun" should throw exception and display error since test ids do not point to correct PSI
* from that moment
*/
@Test
public void testCantRerun() throws Exception {
startMessagesCapture();
runPythonTest(
new PyUnitTestLikeProcessWithConsoleTestTask<T>("/testRunner/env/unit", "test_with_skips_and_errors.py", this::createTestRunner) {
@Override
protected void checkTestResults(@NotNull final T runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assert runner.getFailedTestsCount() > 0 : "We need failed tests to test broken rerun";
startMessagesCapture();
EdtTestUtil.runInEdtAndWait(() -> {
deleteAllTestFiles(myFixture);
runner.rerunFailedTests();
});
final List<Throwable> throwables = getCapturesMessages().first;
Assert.assertThat("Exception shall be thrown", throwables, not(emptyCollectionOf(Throwable.class)));
final Throwable exception = throwables.get(0);
Assert.assertThat("ExecutionException should be thrown", exception, instanceOf(ExecutionException.class));
Assert.assertThat("Wrong text", exception.getMessage(), equalTo(PyBundle.message("runcfg.tests.cant_rerun")));
Assert.assertThat("No messages displayed for exception", getCapturesMessages().second, not(emptyCollectionOf(String.class)));
stopMessageCapture();
}
});
}
/**
* Deletes all files in temp. folder
*/
private static void deleteAllTestFiles(@NotNull final CodeInsightTestFixture fixture) {
ApplicationManager.getApplication().runWriteAction(() -> {
final VirtualFile testRoot = fixture.getTempDirFixture().getFile(".");
assert testRoot != null : "No temp path?";
try {
for (final VirtualFile child : testRoot.getChildren()) {
child.delete(null);
}
}
catch (final IOException e) {
throw new AssertionError(String.format("Failed to delete files in %s : %s", testRoot, e));
}
});
}
}

View File

@@ -15,19 +15,11 @@
*/
package com.jetbrains.env.python.testing;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RuntimeConfigurationWarning;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.EdtTestUtil;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import com.jetbrains.env.EnvTestTagsRequired;
import com.jetbrains.env.PyEnvTestCase;
import com.jetbrains.env.ut.PyUnitTestProcessRunner;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.sdk.InvalidSdkException;
@@ -35,26 +27,57 @@ import com.jetbrains.python.testing.PyUnitTestConfiguration;
import com.jetbrains.python.testing.PyUnitTestFactory;
import com.jetbrains.python.testing.PythonTestConfigurationsModel;
import com.jetbrains.python.testing.TestTargetType;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import static com.jetbrains.env.ut.PyScriptTestProcessRunner.TEST_TARGET_PREFIX;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.junit.Assert.assertEquals;
/**
* @author traff
*/
public final class PythonUnitTestingTest extends PyEnvTestCase {
public final class PythonUnitTestingTest extends PythonUnitTestingLikeTest<PyUnitTestProcessRunner> {
@Override
PyUnitTestProcessRunner createTestRunner(@NotNull final TestRunnerConfig config) {
return new PyUnitTestProcessRunner(config.getScriptName(), config.getRerunFailedTests());
}
@Test
public void testRenameClass() throws Exception {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameClassTask<>(
PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class));
}
@Test(expected = RuntimeConfigurationWarning.class)
public void testValidation() throws Exception {
final CreateConfigurationTestTask.PyConfigurationCreationTask<PyUnitTestConfiguration> task =
new CreateConfigurationTestTask.PyConfigurationCreationTask<PyUnitTestConfiguration>() {
@NotNull
@Override
protected PyUnitTestFactory createFactory() {
return PyUnitTestFactory.INSTANCE;
}
};
runPythonTest(task);
final PyUnitTestConfiguration configuration = task.getConfiguration();
configuration.setPattern("foo");
configuration.getTarget().setTargetType(TestTargetType.PATH);
configuration.getTarget().setTarget("foo.py");
configuration.checkConfiguration();
}
/**
* tests failfast as example of argument
*/
@@ -66,7 +89,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 1) {
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 1) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull final PyUnitTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
@@ -92,7 +115,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
/**
* check non-ascii (127+) chars are supported in skip messaged
* check non-ascii (127+) chars are supported in skip messaged
*/
@Test
public void testNonAsciiMessage() throws Exception {
@@ -109,56 +132,16 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
runner.getFormattedTestTree();
assertEquals("Skipped test with non-ascii message broke tree",
"Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..TestCase\n" +
"...test(~)\n", runner.getFormattedTestTree());
"[root]\n" +
".test_test\n" +
"..TestCase\n" +
"...test(~)\n", runner.getFormattedTestTree());
Assert.assertThat("non-ascii char broken in output", stdout, containsString("ошибка"));
}
});
}
/**
* tests with docstrings are reported as "test.name (text)" by unittest.
*/
@Test
public void testWithDocString() throws Exception {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("testRunner/env/unit/withDocString", "test_test.py") {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 1);
}
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
if (runner.getCurrentRerunStep() == 0) {
assertEquals("test with docstring produced bad tree", "Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..SomeTestCase\n" +
"...testSomething (Only with docstring test is parsed with extra space)(+)\n" +
"...testSomethingBad (Fail)(-)\n", runner.getFormattedTestTree());
}
else {
assertEquals("test with docstring failed to rerun",
"Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..SomeTestCase\n" +
"...testSomethingBad (Fail)(-)\n", runner.getFormattedTestTree());
}
}
});
}
// Ensure failed and error subtests work
@Test
@EnvTestTagsRequired(tags = "python3")
@@ -168,7 +151,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 1);
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 1);
}
@Override
@@ -198,7 +181,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 1);
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 1);
}
@Override
@@ -233,7 +216,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 1);
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 1);
}
@Override
@@ -261,18 +244,6 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
});
}
// Ensures setup/teardown does not break anything
@Test
public void testSetupTearDown() throws Exception {
runPythonTest(new SetupTearDownTestTask<PyUnitTestProcessRunner>() {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner("test_test.py", 1);
}
});
}
/**
* Raising SkipTest on class setup should not lead to KeyError
@@ -287,43 +258,14 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
Assert.assertEquals("Output tree broken for skipped exception thrown in setup method" ,"Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..TestSimple\n" +
"...setUpClass(~)\n" +
"..TestSubSimple\n" +
"...setUpClass(~)\n", runner.getFormattedTestTree());
}
});
}
/**
* Ensure that sys.path[0] is script folder, not helpers folder
*/
@Test
public void testSysPath() throws Exception {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("testRunner/env/unit/sysPath", "test_sample.py") {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 0);
}
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
Assert.assertEquals(runner.getFormattedTestTree(), 1, runner.getAllTestsCount());
myFixture.getTempDirFixture().getFile("sysPath");
final VirtualFile folderWithScript = myFixture.getTempDirFixture().getFile(".");
assert folderWithScript != null : "No folder for script " + myScriptName;
Assert.assertThat("sys.path[0] should point to folder with test, while it does not", stdout,
Matchers.containsString(String.format("path[0]=%s", new File(folderWithScript.getPath()).getAbsolutePath())));
Assert.assertEquals("Output tree broken for skipped exception thrown in setup method", "Test tree:\n" +
"[root]\n" +
".test_test\n" +
"..TestSimple\n" +
"...setUpClass(~)\n" +
"..TestSubSimple\n" +
"...setUpClass(~)\n",
runner.getFormattedTestTree());
}
});
}
@@ -340,7 +282,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 1);
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 1);
}
@Override
@@ -374,7 +316,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
// Full pass is required because it is folder
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 2) {
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 2) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull final PyUnitTestConfiguration configuration)
throws IOException {
@@ -399,20 +341,6 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
});
}
/**
* Ensures that python target pointing to module works correctly
*/
@Test
public void testRunModuleAsFile() throws Exception {
runPythonTest(new RunModuleAsFileTask<PyUnitTestProcessRunner>() {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(TARGET, 0);
}
});
}
@Test
public void testRerunSubfolder() throws Exception {
runPythonTest(new RerunSubfolderTask<PyUnitTestProcessRunner>(1) {
@@ -442,7 +370,7 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
// Full pass is required because it is folder
return new PyUnitTestProcessRunner(toFullPath(myScriptName), 2) {
return new PyUnitTestProcessRunner(toFullPath(getMyScriptName()), 2) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUnitTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
@@ -510,20 +438,181 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
});
}
// PY-24407
@Test
public void testMultipleCases() throws Exception {
public void testWorkingDirectoryDependsOnRelativeImport() throws Exception {
runPythonTest(new CreateConfigurationTestTask<PyUnitTestConfiguration>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class) {
@NotNull
@Override
protected List<PsiElement> getPsiElementsToRightClickOn() {
myFixture.configureByFile("testRelativeImport/src/tests/test_no_relative.py");
final PyFunction noRelativeImportFun = myFixture.findElementByText("test_no_relative", PyFunction.class);
assert noRelativeImportFun != null;
myFixture.configureByFile("testRelativeImport/src/tests/test_relative.py");
final PyFunction relativeImportFun = myFixture.findElementByText("test_relative", PyFunction.class);
assert relativeImportFun != null;
return Arrays.asList(relativeImportFun, noRelativeImportFun);
}
@Override
protected void checkConfiguration(@NotNull PyUnitTestConfiguration configuration, @NotNull PsiElement elementToRightClickOn) {
super.checkConfiguration(configuration, elementToRightClickOn);
configuration.getWorkingDirectorySafe();
final PyFunction function = (PyFunction)elementToRightClickOn;
if (function.getName().equals("test_relative")) {
Assert.assertThat("Wrong dir for relative import", configuration.getWorkingDirectory(), endsWith("testRelativeImport"));
assertEquals("Bad target", "src.tests.test_relative.ModuleTest.test_relative", configuration.getTarget().getTarget());
}
else if (function.getName().equals("test_no_relative")) {
Assert
.assertThat("Wrong dir for non relative import", configuration.getWorkingDirectory(), endsWith("testRelativeImport/src/tests"));
assertEquals("Bad target", "test_no_relative.ModuleTest.test_no_relative", configuration.getTarget().getTarget());
}
else {
throw new AssertionError("Unexpected function " + function.getName());
}
}
});
}
/**
* Checks tests are resolved when launched from subfolder
*/
@Test
public void testTestsInSubFolderResolvable() throws Exception {
runPythonTest(
new CreateConfigurationMultipleCasesTask<PyUnitTestConfiguration>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class){
new PyTestsInSubFolderRunner<PyUnitTestProcessRunner>("test_metheggs", "test_first") {
@NotNull
@Override
protected boolean configurationShouldBeProducedForElement(@NotNull final PsiElement element) {
// test_functions.py does not conttain any TestCase and can't be launched with unittest
final PsiFile file = element.getContainingFile();
return file == null || ! file.getName().endsWith("test_functions.py");
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath("tests"), 0) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUnitTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
configuration.setWorkingDirectory(getWorkingFolderForScript());
}
};
}
});
}
/**
* Ensures test output works
*/
@Test
public void testOutput() throws Exception {
runPythonTest(
new PyTestsOutputRunner<PyUnitTestProcessRunner>("test_metheggs", "test_first") {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath("tests"), 0) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUnitTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
configuration.setWorkingDirectory(getWorkingFolderForScript());
}
};
}
});
}
/**
* Checks <a href="https://docs.python.org/2/library/unittest.html#load-tests-protocol">Load test protocol</a>
*/
@Test
public void testLoadProtocol() throws Exception {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test_load_protocol.py") {
@Override
public boolean isLanguageLevelSupported(@NotNull final LanguageLevel level) {
// "load_protocol" does not exist before 2.7
return level.compareTo(LanguageLevel.PYTHON26) > 0;
}
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals("bad num of passed tests: unittest load protocol failed to find tests?", 3, runner.getPassedTestsCount());
runner.assertAllTestsPassed();
}
});
}
/**
* Ensures pattern is supported
*/
@Test
public void testUTRunnerByPattern() {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", PyUnitTestProcessRunner.TEST_PATTERN_PREFIX + "*pattern.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 4, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getPassedTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getFailedTestsCount());
}
});
}
@Test
public void testMethod() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit",
TEST_TARGET_PREFIX +
"test_file.GoodTest.test_passes") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(1, runner.getPassedTestsCount());
}
});
}
@Test
public void testRelativeImports() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit/relativeImports",
PyUnitTestProcessRunner.TEST_PATTERN_PREFIX + "test_imps.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 1, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 1, runner.getPassedTestsCount());
}
});
}
@Test
public void testConfigurationProducerOnDirectory() throws Exception {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask<>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class));
}
@Test
public void testConfigurationProducer() throws Exception {
runPythonTest(new CreateConfigurationByFileTask<>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME, PyUnitTestConfiguration.class));
}
/**
* Ensures newly created configuration inherits working dir from default if set
*/
@@ -554,438 +643,31 @@ public final class PythonUnitTestingTest extends PyEnvTestCase {
);
}
// PY-24407
@Test
public void testWorkingDirectoryDependsOnRelativeImport() throws Exception {
runPythonTest(new CreateConfigurationTestTask<PyUnitTestConfiguration>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME, PyUnitTestConfiguration.class){
@NotNull
@Override
protected List<PsiElement> getPsiElementsToRightClickOn() {
myFixture.configureByFile("testRelativeImport/src/tests/test_no_relative.py");
final PyFunction noRelativeImportFun = myFixture.findElementByText("test_no_relative", PyFunction.class);
assert noRelativeImportFun != null;
myFixture.configureByFile("testRelativeImport/src/tests/test_relative.py");
final PyFunction relativeImportFun = myFixture.findElementByText("test_relative", PyFunction.class);
assert relativeImportFun != null;
return Arrays.asList(relativeImportFun, noRelativeImportFun);
}
@Override
protected void checkConfiguration(@NotNull PyUnitTestConfiguration configuration, @NotNull PsiElement elementToRightClickOn) {
super.checkConfiguration(configuration, elementToRightClickOn);
configuration.getWorkingDirectorySafe();
final PyFunction function = (PyFunction)elementToRightClickOn;
if (function.getName().equals("test_relative")) {
Assert.assertThat("Wrong dir for relative import", configuration.getWorkingDirectory(), endsWith("testRelativeImport"));
assertEquals("Bad target", "src.tests.test_relative.ModuleTest.test_relative", configuration.getTarget().getTarget());
} else if (function.getName().equals("test_no_relative")) {
Assert.assertThat("Wrong dir for non relative import", configuration.getWorkingDirectory(), endsWith("testRelativeImport/src/tests"));
assertEquals("Bad target", "test_no_relative.ModuleTest.test_no_relative", configuration.getTarget().getTarget());
} else {
throw new AssertionError("Unexpected function " + function.getName());
}
}
});
}
@Test
public void testConfigurationProducer() throws Exception {
runPythonTest(new CreateConfigurationByFileTask<>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME, PyUnitTestConfiguration.class));
}
/**
* Checks tests are resolved when launched from subfolder
*/
@Test
public void testTestsInSubFolderResolvable() throws Exception {
public void testMultipleCases() throws Exception {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask.PyTestsInSubFolderRunner<PyUnitTestProcessRunner>("test_metheggs", "test_first") {
@NotNull
new CreateConfigurationMultipleCasesTask<PyUnitTestConfiguration>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class) {
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath("tests"), 0) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUnitTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
configuration.setWorkingDirectory(getWorkingFolderForScript());
}
};
}
});
}
/**
* Ensures test output works
*/
@Test
public void testOutput() throws Exception {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask.PyTestsOutputRunner<PyUnitTestProcessRunner>("test_metheggs", "test_first") {
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(toFullPath("tests"), 0) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUnitTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
configuration.setWorkingDirectory(getWorkingFolderForScript());
}
};
protected boolean configurationShouldBeProducedForElement(@NotNull final PsiElement element) {
// test_functions.py does not conttain any TestCase and can't be launched with unittest
final PsiFile file = element.getContainingFile();
return file == null || !file.getName().endsWith("test_functions.py");
}
});
}
@Test(expected = RuntimeConfigurationWarning.class)
public void testValidation() throws Exception {
private abstract class PyUnitTestProcessWithConsoleTestTask extends PyUnitTestLikeProcessWithConsoleTestTask<PyUnitTestProcessRunner> {
public PyUnitTestProcessWithConsoleTestTask(@NotNull String relativePathToTestData,
@NotNull String scriptName) {
super(relativePathToTestData, scriptName, PythonUnitTestingTest.this::createTestRunner);
}
final CreateConfigurationTestTask.PyConfigurationCreationTask<PyUnitTestConfiguration> task =
new CreateConfigurationTestTask.PyConfigurationCreationTask<PyUnitTestConfiguration>() {
@NotNull
@Override
protected PyUnitTestFactory createFactory() {
return PyUnitTestFactory.INSTANCE;
}
};
runPythonTest(task);
final PyUnitTestConfiguration configuration = task.getConfiguration();
configuration.setPattern("foo");
configuration.getTarget().setTargetType(TestTargetType.PATH);
configuration.getTarget().setTarget("foo.py");
configuration.checkConfiguration();
}
@Test
public void testConfigurationProducerOnDirectory() throws Exception {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask<>(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class));
}
@Test
public void testRenameClass() throws Exception {
runPythonTest(
new CreateConfigurationByFileTask.CreateConfigurationTestAndRenameClassTask<>(
PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME,
PyUnitTestConfiguration.class));
}
@Test
public void testUTRunner() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test1.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 2, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getPassedTestsCount());
runner.assertAllTestsPassed();
}
});
}
/**
* Checks <a href="https://docs.python.org/2/library/unittest.html#load-tests-protocol">Load test protocol</a>
*/
@Test
public void testLoadProtocol() throws Exception {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test_load_protocol.py") {
@Override
public boolean isLanguageLevelSupported(@NotNull final LanguageLevel level) {
// "load_protocol" does not exist before 2.7
return level.compareTo(LanguageLevel.PYTHON26) > 0;
}
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals("bad num of passed tests: unittest load protocol failed to find tests?", 3, runner.getPassedTestsCount());
runner.assertAllTestsPassed();
}
});
}
/**
* Ensure rerun test works even if test is declared in parent
* See https://github.com/JetBrains/teamcity-messages/issues/117
*/
@Test
public void testRerunDerivedClass() throws Exception {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "rerun_derived.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
Assert.assertThat("Wrong number of failed tests", runner.getFailedTestsCount(), equalTo(1));
final int expectedNumberOfTests = (runner.getCurrentRerunStep() == 0 ? 2 : 1);
Assert.assertThat("Wrong number tests", runner.getAllTestsCount(), equalTo(expectedNumberOfTests));
if (runner.getCurrentRerunStep() == 1) {
// Make sure derived tests are launched, not abstract
Assert.assertEquals("Wrong tests after rerun",
"Test tree:\n" +
"[root]\n" +
".rerun_derived\n" +
"..TestDerived\n" +
"...test_a(-)\n", runner.getFormattedTestTree());
}
}
@NotNull
@Override
protected PyUnitTestProcessRunner createProcessRunner() throws Exception {
return new PyUnitTestProcessRunner(myScriptName, 2);
}
});
}
/**
* Run tests, delete file and click "rerun" should throw exception and display error since test ids do not point to correct PSI
* from that moment
*/
@Test
public void testCantRerun() throws Exception {
startMessagesCapture();
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test_with_skips_and_errors.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assert runner.getFailedTestsCount() > 0 : "We need failed tests to test broken rerun";
startMessagesCapture();
EdtTestUtil.runInEdtAndWait(() -> {
deleteAllTestFiles(myFixture);
runner.rerunFailedTests();
});
final List<Throwable> throwables = getCapturesMessages().first;
Assert.assertThat("Exception shall be thrown", throwables, not(emptyCollectionOf(Throwable.class)));
final Throwable exception = throwables.get(0);
Assert.assertThat("ExecutionException should be thrown", exception, instanceOf(ExecutionException.class));
Assert.assertThat("Wrong text", exception.getMessage(), equalTo(PyBundle.message("runcfg.tests.cant_rerun")));
Assert.assertThat("No messages displayed for exception", getCapturesMessages().second, not(emptyCollectionOf(String.class)));
stopMessageCapture();
}
});
}
/**
* Ensures that skipped and erroneous tests do not lead to suite ignorance
*/
@Test
public void testUTSkippedAndIgnored() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test_with_skips_and_errors.py") {
@Override
public boolean isLanguageLevelSupported(@NotNull final LanguageLevel level) {
// This test requires unittest to have decorator "test" that does not exists in 2.6
return level.compareTo(LanguageLevel.PYTHON26) > 0;
}
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(4, runner.getAllTestsCount());
assertEquals(2, runner.getPassedTestsCount());
assertEquals(2, runner.getFailedTestsCount());
Assert.assertFalse("Suite is not finished", runner.getTestProxy().isInterrupted());
}
});
}
@Test
public void testUTRunner2() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test2.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 3, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 1, runner.getPassedTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getFailedTestsCount());
}
});
}
/**
* Ensures pattern is supported
*/
@Test
public void testUTRunnerByPattern() {
runPythonTest(
new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", PyUnitTestProcessRunner.TEST_PATTERN_PREFIX + "*pattern.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 4, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getPassedTestsCount());
assertEquals(runner.getFormattedTestTree(), 2, runner.getFailedTestsCount());
}
});
}
/**
* Ensure "[" in test does not break output
*/
@Test
public void testEscaping() throws Exception {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test_escaping.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(0, runner.getPassedTestsCount());
assertEquals(1, runner.getFailedTestsCount());
}
});
}
@Test
public void testClass() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit",
TEST_TARGET_PREFIX + "test_file.GoodTest") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(1, runner.getPassedTestsCount());
}
});
}
@Test
public void testMethod() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit",
TEST_TARGET_PREFIX +
"test_file.GoodTest.test_passes") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(1, runner.getPassedTestsCount());
}
});
}
@Test
public void testFolder() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "test_folder/") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(5, runner.getAllTestsCount());
assertEquals(3, runner.getPassedTestsCount());
}
});
}
/**
* Ensures file references are highlighted for python traceback
*/
@Test
public void testUnitTestFileReferences() {
final String fileName = "reference_tests.py";
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", fileName) {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
final List<String> fileNames = runner.getHighlightedStringsInConsole().getSecond();
Assert.assertTrue(String.format("Not enough highlighted entries(%s) in the following output: %s",
StringUtil.join(fileNames, ","),
runner.getAllConsoleText()),
fileNames.size() >= 3);
// UnitTest highlights file name
Assert.assertThat("Bad line highlighted", fileNames, hasItem(endsWith(fileName)));
}
});
}
@Test
public void testDependent() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit", "dependentTests/test_my_class.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(1, runner.getAllTestsCount());
assertEquals(1, runner.getPassedTestsCount());
}
});
}
@Test
public void testRelativeImports() {
runPythonTest(new PyUnitTestProcessWithConsoleTestTask("/testRunner/env/unit/relativeImports",
PyUnitTestProcessRunner.TEST_PATTERN_PREFIX + "test_imps.py") {
@Override
protected void checkTestResults(@NotNull final PyUnitTestProcessRunner runner,
@NotNull final String stdout,
@NotNull final String stderr,
@NotNull final String all) {
assertEquals(runner.getFormattedTestTree(), 1, runner.getAllTestsCount());
assertEquals(runner.getFormattedTestTree(), 1, runner.getPassedTestsCount());
}
});
}
/**
* Deletes all files in temp. folder
*/
private static void deleteAllTestFiles(@NotNull final CodeInsightTestFixture fixture) {
ApplicationManager.getApplication().runWriteAction(() -> {
final VirtualFile testRoot = fixture.getTempDirFixture().getFile(".");
assert testRoot != null : "No temp path?";
try {
for (final VirtualFile child : testRoot.getChildren()) {
child.delete(null);
}
}
catch (final IOException e) {
throw new AssertionError(String.format("Failed to delete files in %s : %s", testRoot, e));
}
});
public PyUnitTestProcessWithConsoleTestTask(@NotNull String relativePathToTestData,
@NotNull String scriptName,
int rerunFailedTests) {
super(relativePathToTestData, scriptName, rerunFailedTests, PythonUnitTestingTest.this::createTestRunner);
}
}
}