Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: stubs CLI test #952

Merged
merged 13 commits into from
Oct 22, 2024
1 change: 1 addition & 0 deletions doc/changelog.d/952.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stubs CLI test
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ markers = [
"remote_session_launch: tests that launch Mechanical and work with gRPC server inside it",
"remote_session_connect: tests that connect to Mechanical and work with gRPC server inside it",
"minimum_version(num): tests that run if ansys-version is greater than or equal to the minimum version provided",
"version_range(min_revn,max_revn): tests that run if ansys-version is in the range of the minimum and maximum revision numbers inclusive.",
"windows_only: tests that run if the testing platform is on Windows",
"linux_only: tests that run if the testing platform is on Linux",
"cli: tests for the Command Line Interface",
Expand Down
62 changes: 55 additions & 7 deletions src/ansys/mechanical/core/ide_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,57 @@
import json
import os
from pathlib import Path
import re
import site
import sys
import sysconfig

import ansys.tools.path as atp
import click


def get_stubs_location():
"""Find the ansys-mechanical-stubs installation location in site-packages.

Returns
-------
pathlib.Path
The path to the ansys-mechanical-stubs installation in site-packages.
"""
site_packages = site.getsitepackages()
prefix_path = sys.prefix.replace("\\", "\\\\")
site_packages_regex = re.compile(f"{prefix_path}.*site-packages$")
site_packages_paths = list(filter(site_packages_regex.match, site_packages))

if len(site_packages_paths) == 1:
# Get the stubs location
stubs_location = Path(site_packages_paths[0]) / "ansys" / "mechanical" / "stubs"
return stubs_location

raise Exception("Could not retrieve the location of the ansys-mechanical-stubs package.")

Check warning on line 54 in src/ansys/mechanical/core/ide_config.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mechanical/core/ide_config.py#L54

Added line #L54 was not covered by tests


def get_stubs_versions(stubs_location: Path):
"""Retrieve the revision numbers in ansys-mechanical-stubs.

Parameters
----------
pathlib.Path
The path to the ansys-mechanical-stubs installation in site-packages.

Returns
-------
list
The list containing minimum and maximum versions in the ansys-mechanical-stubs package.
"""
# Get revision numbers in stubs folder
revns = [
int(revision[1:])
for revision in os.listdir(stubs_location)
if os.path.isdir(os.path.join(stubs_location, revision)) and revision.startswith("v")
]
return revns


def _vscode_impl(
target: str = "user",
revision: int = None,
Expand Down Expand Up @@ -65,9 +109,7 @@
settings_json = current_dir / ".vscode" / "settings.json"

# Location where the stubs are installed -> .venv/Lib/site-packages, for example
stubs_location = (
Path(sysconfig.get_paths()["purelib"]) / "ansys" / "mechanical" / "stubs" / f"v{revision}"
)
stubs_location = get_stubs_location() / f"v{revision}"

# The settings to add to settings.json for autocomplete to work
settings_json_data = {
Expand Down Expand Up @@ -105,11 +147,17 @@
The Mechanical revision number. For example, "242".
If unspecified, it finds the default Mechanical version from ansys-tools-path.
"""
# Get the ansys-mechanical-stubs install location
stubs_location = get_stubs_location()
# Get all revision numbers available in ansys-mechanical-stubs
revns = get_stubs_versions(stubs_location)
# Check the IDE and raise an exception if it's not VS Code
if ide == "vscode":
return _vscode_impl(target, revision)
else:
if revision < min(revns) or revision > max(revns):
raise Exception(f"PyMechanical Stubs are not available for {revision}")
elif ide != "vscode":
raise Exception(f"{ide} is not supported at the moment.")
else:
return _vscode_impl(target, revision)


@click.command()
Expand Down
13 changes: 13 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ def pytest_addoption(parser):
def pytest_collection_modifyitems(config, items):
"""Skips tests marked minimum_version if ansys-version is less than mark argument."""
for item in items:
# Skip tests that are less than the minimum version
if "minimum_version" in item.keywords:
revn = [mark.args[0] for mark in item.iter_markers(name="minimum_version")]
if int(config.getoption("--ansys-version")) < revn[0]:
Expand All @@ -393,6 +394,18 @@ def pytest_collection_modifyitems(config, items):
)
item.add_marker(skip_versions)

# Skip tests that are outside of the provided version range. For example,
# @pytest.mark.version_range(241,242)
if "version_range" in item.keywords:
revns = [mark.args for mark in item.iter_markers(name="version_range")][0]
ansys_version = int(config.getoption("--ansys-version"))

if (ansys_version < revns[0]) or (ansys_version > revns[1]):
skip_versions = pytest.mark.skip(
reason=f"Requires ansys-version in the range {revns[0]} to {revns[1]}."
)
item.add_marker(skip_versions)

# Skip on platforms other than Windows
if "windows_only" in item.keywords and sys.platform != "win32":
skip_except_windows = pytest.mark.skip(reason="Test requires Windows platform.")
Expand Down
103 changes: 63 additions & 40 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@
from pathlib import Path
import subprocess
import sys
import sysconfig

import pytest

from ansys.mechanical.core.ide_config import _cli_impl as ideconfig_cli_impl
from ansys.mechanical.core.ide_config import get_stubs_location, get_stubs_versions
from ansys.mechanical.core.run import _cli_impl

STUBS_LOC = get_stubs_location()
STUBS_REVNS = get_stubs_versions(STUBS_LOC)
MIN_STUBS_REVN = min(STUBS_REVNS)
MAX_STUBS_REVN = max(STUBS_REVNS)


@pytest.mark.cli
def test_cli_default(disable_cli):
Expand Down Expand Up @@ -252,40 +257,42 @@ def get_settings_location() -> str:
return settings_json


def get_stubs_location(revision: int) -> Path:
"""Get the ansys-mechanical-stubs location with specified revision.

Parameters
----------
revision: int
The Mechanical revision number. For example, "242".

Returns
-------
pathlib.Path
The path to the ansys-mechanical-stubs installation.
"""
return (
Path(sysconfig.get_paths()["purelib"]) / "ansys" / "mechanical" / "stubs" / f"v{revision}"
)


@pytest.mark.cli
def test_ideconfig_cli_ide_exception(capfd):
def test_ideconfig_cli_ide_exception(capfd, pytestconfig):
"""Test IDE configuration raises an exception for anything but vscode."""
revision = int(pytestconfig.getoption("ansys_version"))
with pytest.raises(Exception):
ideconfig_cli_impl(
ide="pycharm",
target="user",
revision=242,
revision=revision,
)


def test_ideconfig_cli_version_exception(pytestconfig):
"""Test the IDE configuration raises an exception when the version is out of bounds."""
revision = int(pytestconfig.getoption("ansys_version"))
stubs_location = get_stubs_location()
stubs_revns = get_stubs_versions(stubs_location)

# If revision number is greater than the maximum stubs revision number
# assert an exception is raised
if revision > max(stubs_revns):
with pytest.raises(Exception):
ideconfig_cli_impl(
ide="vscode",
target="user",
revision=revision,
)


@pytest.mark.cli
def test_ideconfig_cli_user_settings(capfd):
@pytest.mark.version_range(MIN_STUBS_REVN, MAX_STUBS_REVN)
def test_ideconfig_cli_user_settings(capfd, pytestconfig):
"""Test the IDE configuration prints correct information for user settings."""
# Set the revision number
revision = 242
# Get the revision number
revision = int(pytestconfig.getoption("ansys_version"))
stubs_location = get_stubs_location()

# Run the IDE configuration command for the user settings type
ideconfig_cli_impl(
Expand All @@ -300,17 +307,18 @@ def test_ideconfig_cli_user_settings(capfd):

# Get the path to the settings.json file based on operating system env vars
settings_json = get_settings_location()
stubs_location = get_stubs_location(revision)

assert f"Update {settings_json} with the following information" in out
assert str(stubs_location) in out


@pytest.mark.cli
def test_ideconfig_cli_workspace_settings(capfd):
@pytest.mark.version_range(MIN_STUBS_REVN, MAX_STUBS_REVN)
def test_ideconfig_cli_workspace_settings(capfd, pytestconfig):
"""Test the IDE configuration prints correct information for workplace settings."""
# Set the revision number
revision = 241
revision = int(pytestconfig.getoption("ansys_version"))
stubs_location = get_stubs_location()

# Run the IDE configuration command
ideconfig_cli_impl(
Expand All @@ -325,7 +333,6 @@ def test_ideconfig_cli_workspace_settings(capfd):

# Get the path to the settings.json file based on the current directory & .vscode folder
settings_json = Path.cwd() / ".vscode" / "settings.json"
stubs_location = get_stubs_location(revision)

# Assert the correct settings.json file and stubs location is in the output
assert f"Update {settings_json} with the following information" in out
Expand All @@ -335,10 +342,11 @@ def test_ideconfig_cli_workspace_settings(capfd):

@pytest.mark.cli
@pytest.mark.python_env
def test_ideconfig_venv(test_env, run_subprocess, rootdir):
@pytest.mark.version_range(MIN_STUBS_REVN, MAX_STUBS_REVN)
def test_ideconfig_cli_venv(test_env, run_subprocess, rootdir, pytestconfig):
"""Test the IDE configuration location when a virtual environment is active."""
# Set the revision number
revision = 242
revision = pytestconfig.getoption("ansys_version")

# Install pymechanical
subprocess.check_call(
Expand All @@ -347,8 +355,16 @@ def test_ideconfig_venv(test_env, run_subprocess, rootdir):
env=test_env.env,
)

# Get the virtual environment location
subprocess_output = run_subprocess(
[test_env.python, "-c", "'import sys; print(sys.prefix)'"],
env=test_env.env,
)
# Decode stdout and fix extra backslashes in paths
venv_loc = subprocess_output[1].decode().replace("\\\\", "\\")

# Run ansys-mechanical-ideconfig in the test virtual environment
process, stdout, stderr = run_subprocess(
subprocess_output_ideconfig = run_subprocess(
[
"ansys-mechanical-ideconfig",
"--ide",
Expand All @@ -361,15 +377,16 @@ def test_ideconfig_venv(test_env, run_subprocess, rootdir):
env=test_env.env,
)
# Decode stdout and fix extra backslashes in paths
stdout = stdout.decode().replace("\\\\", "\\")
stdout = subprocess_output_ideconfig[1].decode().replace("\\\\", "\\")

# Assert virtual environment is in the stdout
assert ".test_env" in stdout
assert venv_loc in stdout


@pytest.mark.cli
@pytest.mark.python_env
def test_ideconfig_default(test_env, run_subprocess, rootdir, pytestconfig):
@pytest.mark.version_range(MIN_STUBS_REVN, MAX_STUBS_REVN)
def test_ideconfig_cli_default(test_env, run_subprocess, rootdir, pytestconfig):
"""Test the IDE configuration location when no arguments are supplied."""
# Get the revision number
revision = pytestconfig.getoption("ansys_version")
Expand All @@ -383,16 +400,22 @@ def test_ideconfig_default(test_env, run_subprocess, rootdir, pytestconfig):
env=test_env.env,
)

# Get the virtual environment location
subprocess_output = run_subprocess(
[test_env.python, "-c", "'import sys; print(sys.prefix)'"],
env=test_env.env,
)
# Decode stdout and fix extra backslashes in paths
venv_loc = subprocess_output[1].decode().replace("\\\\", "\\")

# Run ansys-mechanical-ideconfig in the test virtual environment
process, stdout, stderr = run_subprocess(
[
"ansys-mechanical-ideconfig",
],
subprocess_output_ideconfig = run_subprocess(
["ansys-mechanical-ideconfig"],
env=test_env.env,
)
# Decode stdout and fix extra backslashes in paths
stdout = stdout.decode().replace("\\\\", "\\")
stdout = subprocess_output_ideconfig[1].decode().replace("\\\\", "\\")

assert revision in stdout
assert str(settings_json_fragment) in stdout
assert ".test_env" in stdout
assert venv_loc in stdout
Loading