diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..45819c81 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: +# Run on creating or updating a PR + pull_request: + types: [opened, synchronize, reopened] + +# And pushing to the trunk or fix branches + push: + branches: + - trunk + - 'fix*' + +jobs: + build: + +# Run on ubuntu, mac and windows for python 3.8 (in AMS python stack) and 3.11 + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.8", "3.11"] + + steps: +# Checkout and copy into SCM namespace + - name: Checkout + uses: actions/checkout@v4 + + - name: Move Repo To SCM Namespace and Set PYTHONPATH + if: runner.os != 'Windows' + run: | + mkdir -p scm/plams + find . -mindepth 1 -maxdepth 1 -not -name 'scm' -not -name '.*' -exec mv {} scm/plams/ \; + echo "PYTHONPATH=$(pwd):$PYTHONPATH" >> $GITHUB_ENV + + - name: Move Repo To SCM Namespace and Set PYTHONPATH (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + New-Item -Path scm\plams -ItemType Directory -Force + + Get-ChildItem -Directory | Where-Object { $_.Name -ne 'scm' } | ForEach-Object { + Move-Item -Path $_.FullName -Destination scm\plams + } + + Get-ChildItem -File | ForEach-Object { + Move-Item -Path $_.FullName -Destination scm\plams + } + + echo "PYTHONPATH=$(pwd);$env:PYTHONPATH" >> $env:GITHUB_ENV + +# Set up python env + - name: Set Up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Display Python Version + run: python -c "import sys; print(sys.version)" + - name: Install Dependencies + working-directory: scm/plams + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest + pip install coverage + +# Run tests and calculate code coverage + - name: Run Unit Tests + working-directory: scm/plams + run: | + echo "PYTHONPATH is $PYTHONPATH" + pwd + coverage run -m pytest unit_tests + - name: Evaluate Coverage + working-directory: scm/plams + run: coverage report --omit="unit_tests/*" -i --fail-under=30 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..b442c7fb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +dill==0.3.6 +numpy==1.23.4 +scipy==1.9.3 +natsort==8.1.0 +ase==3.22.1 +rdkit==2024.03.1 \ No newline at end of file diff --git a/setup.py b/setup.py index eacc788f..9e357740 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,8 @@ ], keywords=["molecular modeling", "computational chemistry", "workflow", "python interface"], python_requires=">=3.6", - install_requires=["dill>=0.2.4", "numpy", "scipy", "natsort"], + install_requires=["dill>=0.2.4", "numpy<2", "scipy", "natsort"], + extras_require={"chem_tools": ["ase", "rdkit"]}, packages=packages, package_dir={"scm.plams": "."}, package_data={ diff --git a/unit_tests/test_helpers.py b/unit_tests/test_helpers.py index 9213c57f..c1b46636 100644 --- a/unit_tests/test_helpers.py +++ b/unit_tests/test_helpers.py @@ -1,4 +1,7 @@ import builtins +import pytest +import os + from scm.plams.core.settings import ( SafeRunSettings, LogSettings, @@ -62,3 +65,12 @@ def assert_config_as_expected( assert isinstance(config.job.runscript, RunScriptSettings) assert isinstance(config.jobmanager, JobManagerSettings) assert isinstance(config, ConfigSettings) + + +def skip_if_no_ams_installation(): + """ + Check whether the AMSBIN environment variable is set, and therefore if there is an AMS installation present. + If there is no installation, skip the test with a warning. + """ + if os.getenv("AMSBIN") is None: + pytest.skip("Skipping test as cannot find AMS installation. '$AMSBIN' environment variable is not set.") diff --git a/unit_tests/test_inputparser.py b/unit_tests/test_inputparser.py index 987e3d5a..c79c5c4b 100644 --- a/unit_tests/test_inputparser.py +++ b/unit_tests/test_inputparser.py @@ -3,7 +3,7 @@ from importlib import reload from scm.plams.core.settings import Settings -from scm.plams.unit_tests.test_helpers import get_mock_import_function +from scm.plams.unit_tests.test_helpers import get_mock_import_function, skip_if_no_ams_installation @pytest.fixture @@ -96,6 +96,9 @@ def test_to_dict_with_scmlibbase_succeeds(system_text_inputs): def get_monkeypatched_input_parser(monkeypatch): + # If there is no AMS installation the input parser will not run so skip test with a warning + skip_if_no_ams_installation() + # Mock scm.libbase import failing (even when present in the env) mock_import_function = get_mock_import_function("scm.libbase") monkeypatch.setattr(builtins, "__import__", mock_import_function) @@ -114,6 +117,9 @@ def get_monkeypatched_input_parser(monkeypatch): def get_input_parser_or_skip(): + # If there is no AMS installation the input parser will not run so skip test with a warning + skip_if_no_ams_installation() + from scm.plams.interfaces.adfsuite.inputparser import InputParserFacade, InputParser # Get an instance of the input parser facade using the scm.libbase parser diff --git a/unit_tests/test_interfaces.py b/unit_tests/test_interfaces.py index 76dd3ab8..430a8ad1 100644 --- a/unit_tests/test_interfaces.py +++ b/unit_tests/test_interfaces.py @@ -4,6 +4,7 @@ pass from scm.plams import AMSJob, Settings +from scm.plams.unit_tests.test_helpers import skip_if_no_ams_installation def test_hybrid_engine_input(): @@ -53,6 +54,7 @@ def test_hybrid_engine_input(): EndEngine """ + skip_if_no_ams_installation() job = AMSJob.from_input(AMSinput) assert job.get_input() == AMSinput @@ -105,6 +107,7 @@ def test_list_block_input(): EndEngine """ + skip_if_no_ams_installation() job = AMSJob.from_input(AMSinput) assert job.get_input() == AMSinput