diff --git a/python/helpers/pycharm/_jb_pytest_runner.py b/python/helpers/pycharm/_jb_pytest_runner.py index 6b4b0e05169c..891b54e62877 100644 --- a/python/helpers/pycharm/_jb_pytest_runner.py +++ b/python/helpers/pycharm/_jb_pytest_runner.py @@ -25,6 +25,8 @@ from teamcity import pytest_plugin import os _DOCTEST_MODULES_ARG = "--doctest-modules" +_JB_SKIP_PASSED_OUTPUT_DEFAULT_ARG = "--jb-skippassedoutput-default" +_JB_REPORT_LOGS_AS_TEST_LOG_ARG = "--jb-report-logs-as-test-log" def _add_module_to_target(module_name, python_parts): # Doctest: Find the fully qualified name of the target module by checking each @@ -42,6 +44,13 @@ def _add_module_to_target(module_name, python_parts): if __name__ == '__main__': + skip_passed_output_default = _JB_SKIP_PASSED_OUTPUT_DEFAULT_ARG in sys.argv + while _JB_SKIP_PASSED_OUTPUT_DEFAULT_ARG in sys.argv: + sys.argv.remove(_JB_SKIP_PASSED_OUTPUT_DEFAULT_ARG) + report_logs_as_test_log = _JB_REPORT_LOGS_AS_TEST_LOG_ARG in sys.argv + while _JB_REPORT_LOGS_AS_TEST_LOG_ARG in sys.argv: + sys.argv.remove(_JB_REPORT_LOGS_AS_TEST_LOG_ARG) + path, targets, additional_args = parse_arguments() sys.argv += additional_args @@ -65,6 +74,11 @@ if __name__ == '__main__': plugins_to_load.append(pytest_plugin) args = sys.argv[1:] + if skip_passed_output_default: + pytest_plugin.set_skip_passed_output_default(True) + if report_logs_as_test_log: + pytest_plugin.set_report_logs_as_test_log(True) + if "--jb-show-summary" in args: args.remove("--jb-show-summary") elif int(pytest.__version__.split('.')[0]) >= 6: diff --git a/python/src/com/jetbrains/python/testing/PyTest.kt b/python/src/com/jetbrains/python/testing/PyTest.kt index 0fa32e27e903..e0f4e31a3fcb 100644 --- a/python/src/com/jetbrains/python/testing/PyTest.kt +++ b/python/src/com/jetbrains/python/testing/PyTest.kt @@ -21,6 +21,7 @@ import com.intellij.util.execution.ParametersListUtil import com.jetbrains.python.PyBundle import com.jetbrains.python.PythonHelper import com.jetbrains.python.psi.resolve.PackageAvailabilitySpec +import com.jetbrains.python.run.PythonScriptExecution import com.jetbrains.python.run.target.HelpersAwareTargetEnvironmentRequest import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant import com.jetbrains.python.testing.PyTestSharedForm.create @@ -50,11 +51,22 @@ class PyPyTestExecutionEnvironment(configuration: PyTestConfiguration, environme } override fun customizePythonExecutionEnvironmentVars(helpersAwareTargetRequest: HelpersAwareTargetEnvironmentRequest, - envs: MutableMap>, - passParentEnvs: Boolean) { + envs: MutableMap>, + passParentEnvs: Boolean) { super.customizePythonExecutionEnvironmentVars(helpersAwareTargetRequest, envs, passParentEnvs) envs[PYTEST_RUN_CONFIG] = constant("True") } + + override fun addAfterParameters(cmd: GeneralCommandLine) { + cmd.parametersList.getParamsGroup(GROUP_SCRIPT)?.addParameter(PYTEST_SKIP_PASSED_OUTPUT_DEFAULT_ARG) + cmd.parametersList.getParamsGroup(GROUP_SCRIPT)?.addParameter(PYTEST_REPORT_LOGS_AS_TEST_LOG_ARG) + } + + override fun addAfterParameters(targetEnvironmentRequest: TargetEnvironmentRequest, + testScriptExecution: PythonScriptExecution) { + testScriptExecution.addParameter(constant(PYTEST_SKIP_PASSED_OUTPUT_DEFAULT_ARG)) + testScriptExecution.addParameter(constant(PYTEST_REPORT_LOGS_AS_TEST_LOG_ARG)) + } } @@ -150,3 +162,5 @@ class PyTestFactory(type: PythonTestConfigurationType) : PyAbstractTestFactory

("/testRunner/env/pytest/capture_logging", SdkCreationType.EMPTY_SDK) { + + @NotNull + @Override + protected PyTestTestProcessRunner createProcessRunner() { + return new PyTestTestProcessRunner("test_logging_fail.py", 0); + } + + @Override + protected void checkTestResults(@NotNull final PyTestTestProcessRunner runner, + @NotNull final String stdout, + @NotNull final String stderr, + @NotNull final String all, + int exitCode) { + var consoleText = runner.getAllConsoleText(); + Assertions.assertThat(consoleText.split("\n|(\r\n)")) + .containsSubsequence( + "test_logging_fail.py::test_fail_with_logging FAILED [100%]", + "WARNING test_logging_fail:test_logging_fail.py:6 warning_from_failing_test" + ); + MatcherAssert.assertThat("Captured logs should use a dedicated service message", + all, containsString("[testLog")); + MatcherAssert.assertThat("Captured logs should not be reported as stdout", + all, not(containsString("[testStdOut"))); + } + }); + } + + @Test + public void testLoggingSkippedForPassingTest() { + runPythonTest(new PyProcessWithConsoleTestTask("/testRunner/env/pytest/capture_logging_default", SdkCreationType.EMPTY_SDK) { + + @NotNull + @Override + protected PyTestTestProcessRunner createProcessRunner() { + return new PyTestTestProcessRunner("test_logging_pass.py", 0); + } + + @Override + protected void checkTestResults(@NotNull final PyTestTestProcessRunner runner, + @NotNull final String stdout, + @NotNull final String stderr, + @NotNull final String all, + int exitCode) { + var consoleText = runner.getAllConsoleText(); + Assertions.assertThat(consoleText) + .doesNotContain("warning_from_passing_test"); + MatcherAssert.assertThat("Captured logs from passing tests should not leak into the process output", + all, not(containsString("warning_from_passing_test"))); + } + }); + } + + @Test + public void testLoggingCapturedForPassingTestWhenConfigured() { + runPythonTest(new PyProcessWithConsoleTestTask("/testRunner/env/pytest/capture_logging", SdkCreationType.EMPTY_SDK) { + + @NotNull + @Override + protected PyTestTestProcessRunner createProcessRunner() { + return new PyTestTestProcessRunner("test_logging_pass.py", 0) { + @Override + protected void configurationCreatedAndWillLaunch(@NotNull PyTestConfiguration configuration) throws IOException { + super.configurationCreatedAndWillLaunch(configuration); + configuration.setAdditionalArguments("-c pytest.ini"); + } + }; + } + + @Override + protected void checkTestResults(@NotNull final PyTestTestProcessRunner runner, + @NotNull final String stdout, + @NotNull final String stderr, + @NotNull final String all, + int exitCode) { + var consoleText = runner.getAllConsoleText(); + Assertions.assertThat(consoleText.split("\n|(\r\n)")) + .containsSubsequence( + "test_logging_pass.py::test_pass_with_logging PASSED [100%]", + "WARNING test_logging_pass:test_logging_pass.py:6 warning_from_passing_test" + ); + MatcherAssert.assertThat("Captured logs should use a dedicated service message", + all, containsString("[testLog")); + MatcherAssert.assertThat("Captured logs should not be reported as stdout", + all, not(containsString("[testStdOut"))); + } + }); + } + @NotNull private static String getFrameworkId() { return PyTestFactory.id;