Skip to content

Commit

Permalink
Update unit tests
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mrclary committed Oct 15, 2024
1 parent 5dc6731 commit bcc1da5
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 88 deletions.
21 changes: 1 addition & 20 deletions spyder/app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
26 changes: 6 additions & 20 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -6384,13 +6383,13 @@ def test_switch_to_plugin(main_window, qtbot):
assert QApplication.focusWidget() is code_editor


@flaky(max_runs=5)
def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path,
restore_user_env):
# @flaky(max_runs=5)
def test_PYTHONPATH_in_consoles(main_window, qtbot, tmp_path):
"""
Test that PYTHONPATH is passed to IPython consoles under different
scenarios.
"""

# Wait until the window is fully up
ipyconsole = main_window.ipyconsole
shell = ipyconsole.get_current_shellwidget()
Expand All @@ -6400,37 +6399,24 @@ 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'
user_dir.mkdir()
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()
Expand All @@ -6443,7 +6429,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
Expand Down
95 changes: 55 additions & 40 deletions spyder/plugins/pythonpath/widgets/tests/test_pathmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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':
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -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.
Expand Down Expand Up @@ -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()
Expand Down
6 changes: 4 additions & 2 deletions spyder/utils/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
31 changes: 31 additions & 0 deletions spyder/utils/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -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)
14 changes: 8 additions & 6 deletions spyder/utils/tests/test_environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand Down

0 comments on commit bcc1da5

Please sign in to comment.