From 4ea900e8ebadeaac02c81be00a4d70613883679e Mon Sep 17 00:00:00 2001 From: Ryan Clary <9618975+mrclary@users.noreply.github.com> Date: Tue, 15 Oct 2024 00:52:53 -0700 Subject: [PATCH] Update unit tests * Test system PYTHONPATH import in test_pathmanager instead of test_mainwindow * Move restore_user_env fixture from app/tests/conftest.py to utils/tests/conftest.py * Ensure that the user environment script runs on posix while testing --- spyder/app/tests/conftest.py | 21 +--- spyder/app/tests/test_mainwindow.py | 23 +---- .../widgets/tests/test_pathmanager.py | 95 +++++++++++-------- spyder/utils/environ.py | 6 +- spyder/utils/tests/conftest.py | 31 ++++++ spyder/utils/tests/test_environ.py | 14 +-- 6 files changed, 103 insertions(+), 87 deletions(-) create mode 100644 spyder/utils/tests/conftest.py diff --git a/spyder/app/tests/conftest.py b/spyder/app/tests/conftest.py index f67a426208f..920662c141e 100755 --- a/spyder/app/tests/conftest.py +++ b/spyder/app/tests/conftest.py @@ -27,15 +27,13 @@ from spyder.api.plugin_registration.registry import PLUGIN_REGISTRY from spyder.api.plugins import Plugins from spyder.app import start -from spyder.config.base import get_home_dir, running_in_ci +from spyder.config.base import get_home_dir from spyder.config.manager import CONF from spyder.plugins.ipythonconsole.utils.kernelspec import SpyderKernelSpec from spyder.plugins.projects.api import EmptyProject from spyder.plugins.run.api import RunActions, StoredRunConfigurationExecutor from spyder.plugins.toolbar.api import ApplicationToolbars from spyder.utils import encoding -from spyder.utils.environ import (get_user_env, set_user_env, - amend_user_shell_init) # ============================================================================= # ---- Constants @@ -624,20 +622,3 @@ def threads_condition(): CONF.reset_manager() PLUGIN_REGISTRY.reset() raise - - -@pytest.fixture -def restore_user_env(): - """Set user environment variables and restore upon test exit""" - if not running_in_ci(): - pytest.skip("Skipped because not in CI.") - - if os.name == "nt": - orig_env = get_user_env() - - yield - - if os.name == "nt": - set_user_env(orig_env) - else: - amend_user_shell_init(restore=True) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index e34619b0440..2732b8dc556 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -85,7 +85,6 @@ RunExecutionParameters, ExtendedRunExecutionParameters, WorkingDirOpts, WorkingDirSource, RunContext) from spyder.py3compat import qbytearray_to_str, to_text_string -from spyder.utils.environ import set_user_env from spyder.utils.conda import get_list_conda_envs from spyder.utils.misc import remove_backslashes, rename_file from spyder.utils.clipboard_helper import CLIPBOARD_HELPER @@ -6384,8 +6383,7 @@ def test_switch_to_plugin(main_window, qtbot): @flaky(max_runs=5) -def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path, - restore_user_env): +def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path): """ Test that PYTHONPATH is passed to IPython consoles under different scenarios. @@ -6399,11 +6397,6 @@ def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path, # Main variables ppm = main_window.get_plugin(Plugins.PythonpathManager) - # Add a directory to PYTHONPATH - sys_dir = tmp_path / 'sys_dir' - sys_dir.mkdir() - set_user_env({"PYTHONPATH": str(sys_dir)}) - # Add a directory to the current list of paths to simulate a path added by # users user_dir = tmp_path / 'user_dir' @@ -6411,25 +6404,17 @@ def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path, user_paths = OrderedDict({str(user_dir): True}) if os.name != "nt": assert ppm.get_container()._spyder_pythonpath == [] - ppm.get_container()._save_paths(user_paths=user_paths) - - # Open Pythonpath dialog to detect sys_dir - ppm.show_path_manager() - qtbot.wait(500) - - # Check we're showing two headers - assert len(ppm.path_manager_dialog.headers) == 2 # Check the PPM emits the right signal after closing the dialog with qtbot.waitSignal(ppm.sig_pythonpath_changed, timeout=1000): - ppm.path_manager_dialog.close() + ppm.get_container()._save_paths(user_paths=user_paths) # Check directories were added to sys.path in the right order with qtbot.waitSignal(shell.executed, timeout=2000): shell.execute("import sys; sys_path = sys.path") sys_path = shell.get_value("sys_path") - assert sys_path[-2:] == [str(user_dir), str(sys_dir)] + assert sys_path[-1:] == [str(user_dir)] # Create new console ipyconsole.create_new_client() @@ -6442,7 +6427,7 @@ def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path, shell1.execute("import sys; sys_path = sys.path") sys_path = shell1.get_value("sys_path") - assert sys_path[-2:] == [str(user_dir), str(sys_dir)] + assert sys_path[-1:] == [str(user_dir)] # Check that disabling a path from the PPM removes it from sys.path in all # consoles diff --git a/spyder/plugins/pythonpath/widgets/tests/test_pathmanager.py b/spyder/plugins/pythonpath/widgets/tests/test_pathmanager.py index b3cfdb86084..c8010e091a2 100644 --- a/spyder/plugins/pythonpath/widgets/tests/test_pathmanager.py +++ b/spyder/plugins/pythonpath/widgets/tests/test_pathmanager.py @@ -18,7 +18,9 @@ from qtpy.QtWidgets import QMessageBox, QPushButton # Local imports +from spyder.utils.environ import get_user_env, set_user_env from spyder.utils.programs import is_module_installed +from spyder.utils.tests.conftest import restore_user_env from spyder.plugins.pythonpath.utils import check_path from spyder.plugins.pythonpath.widgets import pathmanager as pathmanager_mod @@ -41,19 +43,42 @@ def pathmanager(qtbot, request): @pytest.mark.parametrize( - 'pathmanager', - [(sys.path[:-10], sys.path[-10:], ())], - indirect=True + 'pathmanager', [(sys.path[:-10], sys.path[-10:], ())], indirect=True ) -def test_pathmanager(pathmanager, qtbot): +def test_pathmanager(qtbot, pathmanager): """Run PathManager test""" pathmanager.show() assert pathmanager -@pytest.mark.parametrize('pathmanager', - [(sys.path[:-10], sys.path[-10:], ())], - indirect=True) +@pytest.mark.parametrize('pathmanager', [((), (), ())], indirect=True) +def test_import_PYTHONPATH(qtbot, pathmanager, tmp_path, restore_user_env): + """ + Test that PYTHONPATH is imported. + """ + + # Add a directory to PYTHONPATH environment variable + sys_dir = tmp_path / 'sys_dir' + sys_dir.mkdir() + set_user_env({"PYTHONPATH": str(sys_dir)}) + + # Open Pythonpath dialog + pathmanager.show() + qtbot.wait(500) + + assert len(pathmanager.headers) == 0 + assert pathmanager.get_system_paths() == OrderedDict() + + # Import PYTHONPATH from environment + pathmanager.import_paths() + assert len(pathmanager.headers) == 1 + + assert pathmanager.get_system_paths() == OrderedDict({str(sys_dir): True}) + + +@pytest.mark.parametrize( + 'pathmanager', [(sys.path[:-10], sys.path[-10:], ())], indirect=True +) def test_check_uncheck_path(pathmanager): """ Test that checking and unchecking a path in the PathManager correctly @@ -66,20 +91,16 @@ def test_check_uncheck_path(pathmanager): assert item.checkState() == Qt.Checked -@pytest.mark.skipif(os.name != 'nt' or not is_module_installed('win32con'), - reason=("This feature is not applicable for Unix " - "systems and pywin32 is needed")) -@pytest.mark.parametrize('pathmanager', - [(['p1', 'p2', 'p3'], ['p4', 'p5', 'p6'], [])], - indirect=True) -def test_export_to_PYTHONPATH(pathmanager, mocker): - # Import here to prevent an ImportError when testing on unix systems - from spyder.utils.environ import (get_user_env, set_user_env, - listdict2envdict) - - # Store PYTHONPATH original state - env = get_user_env() - original_pathlist = env.get('PYTHONPATH', []) +@pytest.mark.skipif( + os.name != 'nt' or not is_module_installed('win32con'), + reason=("This feature is not applicable for Unix " + "systems and pywin32 is needed") +) +@pytest.mark.parametrize( + 'pathmanager', [(['p1', 'p2', 'p3'], ['p4', 'p5', 'p6'], [])], + indirect=True +) +def test_export_to_PYTHONPATH(pathmanager, mocker, restore_user_env): # Mock the dialog window and answer "Yes" to clear contents of PYTHONPATH # before adding Spyder's path list @@ -113,14 +134,10 @@ def test_export_to_PYTHONPATH(pathmanager, mocker): env = get_user_env() assert env['PYTHONPATH'] == expected_pathlist - # Restore PYTHONPATH to its original state - env['PYTHONPATH'] = original_pathlist - set_user_env(listdict2envdict(env)) - -@pytest.mark.parametrize('pathmanager', - [(sys.path[:-10], sys.path[-10:], ())], - indirect=True) +@pytest.mark.parametrize( + 'pathmanager', [(sys.path[:-10], sys.path[-10:], ())], indirect=True +) def test_invalid_directories(qtbot, pathmanager): """Check [site/dist]-packages are invalid paths.""" if os.name == 'nt': @@ -143,9 +160,9 @@ def interact_message_box(): pathmanager.add_path(path) -@pytest.mark.parametrize('pathmanager', - [(('/spam', '/bar'), ('/foo', ), ())], - indirect=True) +@pytest.mark.parametrize( + 'pathmanager', [(('/spam', '/bar'), ('/foo', ), ())], indirect=True +) def test_remove_item_and_reply_no(qtbot, pathmanager): """Check that the item is not removed after answering 'No'.""" pathmanager.show() @@ -169,9 +186,9 @@ def interact_message_box(): assert pathmanager.count() == count -@pytest.mark.parametrize('pathmanager', - [(('/spam', '/bar'), ('/foo', ), ())], - indirect=True) +@pytest.mark.parametrize( + 'pathmanager', [(('/spam', '/bar'), ('/foo', ), ())], indirect=True +) def test_remove_item_and_reply_yes(qtbot, pathmanager): """Check that the item is indeed removed after answering 'Yes'.""" pathmanager.show() @@ -196,9 +213,7 @@ def interact_message_box(): assert pathmanager.count() == (count - 1) -@pytest.mark.parametrize('pathmanager', - [((), (), ())], - indirect=True) +@pytest.mark.parametrize('pathmanager', [((), (), ())], indirect=True) def test_add_repeated_item(qtbot, pathmanager, tmpdir): """ Check behavior when an unchecked item that is already on the list is added. @@ -236,9 +251,9 @@ def interact_message_box(): assert all(pathmanager.get_user_paths().values()) -@pytest.mark.parametrize('pathmanager', - [(('/spam', '/bar'), ('/foo', ), ())], - indirect=True) +@pytest.mark.parametrize( + 'pathmanager', [(('/spam', '/bar'), ('/foo', ), ())], indirect=True +) def test_buttons_state(qtbot, pathmanager, tmpdir): """Check buttons are enabled/disabled based on items and position.""" pathmanager.show() diff --git a/spyder/utils/environ.py b/spyder/utils/environ.py index 5b1d36606ed..74c9ea3b388 100644 --- a/spyder/utils/environ.py +++ b/spyder/utils/environ.py @@ -27,7 +27,9 @@ from qtpy.QtWidgets import QMessageBox # Local imports -from spyder.config.base import _, running_in_ci, get_conf_path +from spyder.config.base import ( + _, running_in_ci, get_conf_path, running_under_pytest +) from spyder.widgets.collectionseditor import CollectionsEditor from spyder.utils.icon_manager import ima from spyder.utils.programs import run_shell_command @@ -111,7 +113,7 @@ def get_user_environment_variables(): # We only need to do this if Spyder was **not** launched from a # terminal. Otherwise, it'll inherit the env vars present in it. # Fixes spyder-ide/spyder#22415 - if not launched_from_terminal: + if not launched_from_terminal or running_under_pytest(): try: user_env_script = _get_user_env_script() proc = run_shell_command(user_env_script, env={}, text=True) diff --git a/spyder/utils/tests/conftest.py b/spyder/utils/tests/conftest.py new file mode 100644 index 00000000000..361d6442845 --- /dev/null +++ b/spyder/utils/tests/conftest.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Standard library imports +import os + +# Third-party imports +import pytest + +# Local imports +from spyder.config.base import running_in_ci +from spyder.utils.environ import ( + get_user_env, set_user_env, amend_user_shell_init +) + + +@pytest.fixture +def restore_user_env(): + """Set user environment variables and restore upon test exit""" + if not running_in_ci(): + pytest.skip("Skipped because not in CI.") + + if os.name == "nt": + orig_env = get_user_env() + + yield + + if os.name == "nt": + set_user_env(orig_env) + else: + amend_user_shell_init(restore=True) diff --git a/spyder/utils/tests/test_environ.py b/spyder/utils/tests/test_environ.py index 277f2418be6..d4f771d9848 100644 --- a/spyder/utils/tests/test_environ.py +++ b/spyder/utils/tests/test_environ.py @@ -18,15 +18,15 @@ from qtpy.QtCore import QTimer # Local imports -from spyder.utils.environ import (get_user_environment_variables, - UserEnvDialog, amend_user_shell_init) +from spyder.utils.environ import ( + get_user_environment_variables, UserEnvDialog, amend_user_shell_init +) from spyder.utils.test import close_message_box -from spyder.app.tests.conftest import restore_user_env @pytest.fixture def environ_dialog(qtbot): - "Setup the Environment variables Dialog." + """Setup the Environment variables Dialog.""" QTimer.singleShot(1000, lambda: close_message_box(qtbot)) dialog = UserEnvDialog() qtbot.addWidget(dialog) @@ -44,8 +44,10 @@ def test_get_user_environment_variables(): @pytest.mark.skipif(os.name == "nt", reason="Does not apply to Windows") def test_get_user_env_newline(restore_user_env): - # Test variable value with newline characters. - # Regression test for spyder-ide#20097 + """ + Test variable value with newline characters. + Regression test for spyder-ide#20097. + """ text = "myfunc() { echo hello;\n echo world\n}\nexport -f myfunc" amend_user_shell_init(text) user_env = get_user_environment_variables()