From b9a3dd9dfb780b9691f8a1859fd67cf9eadbd2d0 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Wed, 4 Feb 2015 00:59:40 -0800 Subject: Skip some tests that require a subinterpreter launched with -E or -I when the interpreter under test is being run in an environment that requires the use of environment variables such as PYTHONHOME in order to function at all. Adds a private test.script_helper._interpreter_requires_environment() function to be used with @unittest.skipIf on stdlib test methods requiring this. --- Lib/test/script_helper.py | 35 +++++++++++++++++++++++++++++++++++ Lib/test/test_cmd_line.py | 4 +++- Lib/test/test_script_helper.py | 41 +++++++++++++++++++++++++++++++++++++++++ Lib/test/test_tracemalloc.py | 4 +++- 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py index 87a781e..6a8bea6 100644 --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -15,6 +15,41 @@ import zipfile from importlib.util import source_from_cache from test.support import make_legacy_pyc, strip_python_stderr, temp_dir + +# Cached result of the expensive test performed in the function below. +__cached_interp_requires_environment = None + +def _interpreter_requires_environment(): + """ + Returns True if our sys.executable interpreter requires environment + variables in order to be able to run at all. + + This is designed to be used with @unittest.skipIf() to annotate tests + that need to use an assert_python*() function to launch an isolated + mode (-I) or no environment mode (-E) sub-interpreter process. + + A normal build & test does not run into this situation but it can happen + when trying to run the standard library test suite from an interpreter that + doesn't have an obvious home with Python's current home finding logic. + + Setting PYTHONHOME is one way to get most of the testsuite to run in that + situation. PYTHONPATH or PYTHONUSERSITE are other common envirnonment + variables that might impact whether or not the interpreter can start. + """ + global __cached_interp_requires_environment + if __cached_interp_requires_environment is None: + # Try running an interpreter with -E to see if it works or not. + try: + subprocess.check_call([sys.executable, '-E', + '-c', 'import sys; sys.exit(0)']) + except subprocess.CalledProcessError: + __cached_interp_requires_environment = True + else: + __cached_interp_requires_environment = False + + return __cached_interp_requires_environment + + # Executing the interpreter in a subprocess def _assert_python(expected_success, *args, **env_vars): if '__isolated' in env_vars: diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 327c145..3683a48 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -8,6 +8,7 @@ import shutil import sys import subprocess import tempfile +from test import script_helper from test.script_helper import (spawn_python, kill_python, assert_python_ok, assert_python_failure) @@ -439,7 +440,8 @@ class CmdLineTest(unittest.TestCase): self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1) self.assertEqual(b'', out) - + @unittest.skipIf(script_helper._interpreter_requires_environment(), + 'Cannot run -I tests when PYTHON env vars are required.') def test_isolatedmode(self): self.verify_valid_flag('-I') self.verify_valid_flag('-IEs') diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py index ea73fd8..da61265 100755 --- a/Lib/test/test_script_helper.py +++ b/Lib/test/test_script_helper.py @@ -1,7 +1,10 @@ """Unittests for test.script_helper. Who tests the test helper?""" +import subprocess +import sys from test import script_helper import unittest +from unittest import mock class TestScriptHelper(unittest.TestCase): @@ -31,5 +34,43 @@ class TestScriptHelper(unittest.TestCase): msg='unexpected command line.') +class TestScriptHelperEnvironment(unittest.TestCase): + """Code coverage for _interpreter_requires_environment().""" + + def setUp(self): + self.assertTrue( + hasattr(script_helper, '__cached_interp_requires_environment')) + # Reset the private cached state. + script_helper.__dict__['__cached_interp_requires_environment'] = None + + def tearDown(self): + # Reset the private cached state. + script_helper.__dict__['__cached_interp_requires_environment'] = None + + @mock.patch('subprocess.check_call') + def test_interpreter_requires_environment_true(self, mock_check_call): + mock_check_call.side_effect = subprocess.CalledProcessError('', '') + self.assertTrue(script_helper._interpreter_requires_environment()) + self.assertTrue(script_helper._interpreter_requires_environment()) + self.assertEqual(1, mock_check_call.call_count) + + @mock.patch('subprocess.check_call') + def test_interpreter_requires_environment_false(self, mock_check_call): + # The mocked subprocess.check_call fakes a no-error process. + script_helper._interpreter_requires_environment() + self.assertFalse(script_helper._interpreter_requires_environment()) + self.assertEqual(1, mock_check_call.call_count) + + @mock.patch('subprocess.check_call') + def test_interpreter_requires_environment_details(self, mock_check_call): + script_helper._interpreter_requires_environment() + self.assertFalse(script_helper._interpreter_requires_environment()) + self.assertFalse(script_helper._interpreter_requires_environment()) + self.assertEqual(1, mock_check_call.call_count) + check_call_command = mock_check_call.call_args[0][0] + self.assertEqual(sys.executable, check_call_command[0]) + self.assertIn('-E', check_call_command) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index bc22450..48ccab2 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -5,7 +5,7 @@ import tracemalloc import unittest from unittest.mock import patch from test.script_helper import assert_python_ok, assert_python_failure -from test import support +from test import script_helper, support try: import threading except ImportError: @@ -755,6 +755,8 @@ class TestCommandLine(unittest.TestCase): stdout = stdout.rstrip() self.assertEqual(stdout, b'False') + @unittest.skipIf(script_helper._interpreter_requires_environment(), + 'Cannot run -E tests when PYTHON env vars are required.') def test_env_var_ignored_with_E(self): """PYTHON* environment variables must be ignored when -E is present.""" code = 'import tracemalloc; print(tracemalloc.is_tracing())' -- cgit v0.12