diff --git a/python/helpers/pycharm/_jb_unittest_runner.py b/python/helpers/pycharm/_jb_unittest_runner.py index 4caf925c03d9..a1821dc7b52e 100644 --- a/python/helpers/pycharm/_jb_unittest_runner.py +++ b/python/helpers/pycharm/_jb_unittest_runner.py @@ -1,43 +1,87 @@ -# coding=utf-8 +import errno import os import sys -from unittest import main +from unittest import main as unittest_main -from _jb_runner_tools import jb_start_tests, jb_doc_args, JB_DISABLE_BUFFERING, \ -JB_VERBOSE, PROJECT_DIR, jb_finish_tests +from _jb_runner_tools import JB_DISABLE_BUFFERING +from _jb_runner_tools import JB_VERBOSE +from _jb_runner_tools import PROJECT_DIR +from _jb_runner_tools import jb_doc_args +from _jb_runner_tools import jb_finish_tests +from _jb_runner_tools import jb_start_tests from teamcity import unittestpy -if __name__ == '__main__': - path, targets, additional_args = jb_start_tests() - args = ["python -m unittest", "--quiet"] - if JB_VERBOSE: - args.append("--verbose") # additional args may override this (unittest argparser works left-to-right) +def build_unittest_args( + path, + targets, + additional_args, + project_dir=PROJECT_DIR, + verbose=JB_VERBOSE, +): + args = ["python -m unittest"] + subcommand_args = [] + if path: - assert os.path.exists(path), "{0}: No such file or directory".format(path) - if sys.version_info > (3, 0) and os.path.isfile(path): - # In Py3 it is possible to run script directly which is much more stable than discovery machinery - # For example it supports hyphens in file names PY-23549 - additional_args = [path] + additional_args + if not os.path.exists(path): + raise OSError(errno.ENOENT, "No such file or directory", path) + + if sys.version_info >= (3, 0) and os.path.isfile(path): + # in Py3 it is possible to run script directly + # which is much more stable than discovery machinery + # for example it supports hyphens in file names PY-23549 + subcommand_args = [path] else: - discovery_args = ["discover", "-s"] - # Unittest in py2 does not support running script directly (and folders in py2 and py3), - # but it can use "discover" to find all tests in some folder (optionally filtering by script) + subcommand_args = ["discover", "-s"] + + # unittest in py2 does not support running script directly + # (and folders in py2 and py3), + # but it can use "discover" to find all tests in some folder + # (optionally filtering by script) if os.path.isfile(path): - discovery_args += [os.path.dirname(path), "-p", os.path.basename(path)] + subcommand_args += [ + os.path.dirname(path), + "-p", + os.path.basename(path) + ] else: - discovery_args.append(path) - discovery_args += ["-t", - PROJECT_DIR] # To force unit calculate path relative to this folder - additional_args = discovery_args + additional_args + subcommand_args.append(path) + + # to force unittest to calculate the path relative to this folder + subcommand_args += ["-t", project_dir] elif targets: - additional_args += targets + subcommand_args = list(targets) + + args += subcommand_args + + if verbose: + args.append("--verbose") + else: + args.append("--quiet") + args += additional_args + return args + + +def main(): + path, targets, additional_args = jb_start_tests() + args = build_unittest_args(path, targets, additional_args) jb_doc_args("unittests", args) - # Working dir should be on path, that is how unittest work when launched from command line + + # working dir should be on path + # that is how unittest work when launched from command line sys.path.insert(0, PROJECT_DIR) + try: - sys.exit(main(argv=args, module=None, testRunner=unittestpy.TeamcityTestRunner, - buffer=not JB_DISABLE_BUFFERING)) + sys.exit(unittest_main( + argv=args, + module=None, + testRunner=unittestpy.TeamcityTestRunner, + buffer=not JB_DISABLE_BUFFERING + )) finally: jb_finish_tests() + + +if __name__ == "__main__": + main() diff --git a/python/helpersTests/tests/pycharm/test_jb_unittest_runner.py b/python/helpersTests/tests/pycharm/test_jb_unittest_runner.py new file mode 100644 index 000000000000..8c34a3632745 --- /dev/null +++ b/python/helpersTests/tests/pycharm/test_jb_unittest_runner.py @@ -0,0 +1,174 @@ +import sys + +import pytest + +from pycharm._jb_unittest_runner import build_unittest_args + + +def test_path_doesnt_exist(): + pattern = r"No such file or directory: 'test_foo.py'" + with pytest.raises(OSError, match=pattern): + build_unittest_args("test_foo.py", [], []) + + + +def test_targets(): + assert build_unittest_args( + path=None, + targets=["test_some_func.SomeFuncTestCase"], + additional_args=[], + verbose=False, + project_dir="/project_dir", + ) == [ + "python -m unittest", + "test_some_func.SomeFuncTestCase", + "--quiet", + ] + + +def test_quiet_discover(tmp_path): + assert build_unittest_args( + path=str(tmp_path), + targets=None, + additional_args=[], + verbose=False, + project_dir="/project_dir", + ) == [ + "python -m unittest", + "discover", + "-s", + str(tmp_path), + "-t", + "/project_dir", + "--quiet", + ] + + +def test_verbose_discover(tmp_path): + assert build_unittest_args( + path=str(tmp_path), + targets=None, + additional_args=[], + verbose=True, + project_dir="/project_dir", + ) == [ + "python -m unittest", + "discover", + "-s", + str(tmp_path), + "-t", + "/project_dir", + "--verbose", + ] + + +@pytest.mark.skipif(sys.version_info >= (3, 0), reason="Python 2 is required") +def test_python2(tmp_path): + assert build_unittest_args( + path=str(tmp_path), + targets=None, + additional_args=[], + verbose=True, + project_dir="/project_dir", + python_version=(2, 7), + ) == [ + "python -m unittest", + "discover", + "-s", + str(tmp_path), + "-t", + "/project_dir", + "--verbose", + ] + + +@pytest.mark.skipif(sys.version_info >= (3, 0), reason="Python 2 is required") +def test_python2_and_path_is_file(tmp_path): + tmp_file = tmp_path / "test_sample.py" + tmp_file.touch() + + assert build_unittest_args( + path=str(tmp_file), + targets=None, + additional_args=[], + verbose=True, + project_dir="/project_dir", + python_version=(2, 7), + ) == [ + "python -m unittest", + "discover", + "-s", + str(tmp_path), + "-p", + tmp_file.name, + "-t", + "/project_dir", + "--verbose", + ] + + +@pytest.mark.skipif(sys.version_info < (3, 0), reason="Python 3 is required") +def test_python3_and_path_is_file(tmp_path): + tmp_file = tmp_path / "test_sample.py" + tmp_file.touch() + + assert build_unittest_args( + path=str(tmp_file), + targets=None, + additional_args=[], + verbose=True, + project_dir="/project_dir", + ) == [ + "python -m unittest", + str(tmp_file), + "--verbose", + ] + + +def test_user_args(): + assert build_unittest_args( + path=None, + targets=["test_some_func.SomeFuncTestCase"], + additional_args=["--locals", "-f"], + verbose=True, + project_dir="/project_dir", + ) == [ + "python -m unittest", + "test_some_func.SomeFuncTestCase", + "--verbose", + "--locals", + "-f", + ] + + +def test_user_overrides_verbosity(): + assert build_unittest_args( + path=None, + targets=["test_some_func.SomeFuncTestCase"], + additional_args=["--quiet"], + verbose=True, + project_dir="/project_dir", + ) == [ + "python -m unittest", + "test_some_func.SomeFuncTestCase", + "--verbose", + "--quiet", + ] + + +def test_rerun_failed_tests(): + assert build_unittest_args( + path=None, + targets=[ + "test_some_func.SomeFuncTestCase.test_false_1", + "test_some_func.SomeFuncTestCase.test_false_2", + ], + additional_args=[], + verbose=False, + project_dir="/project_dir", + ) == [ + "python -m unittest", + "test_some_func.SomeFuncTestCase.test_false_1", + "test_some_func.SomeFuncTestCase.test_false_2", + "--quiet", + ]