diff --git a/.github/workflows/ci-code.yml b/.github/workflows/ci-code.yml index cb77c2f137..4539956e86 100644 --- a/.github/workflows/ci-code.yml +++ b/.github/workflows/ci-code.yml @@ -111,9 +111,10 @@ jobs: - name: Run test suite env: + AIIDA_TEST_PROFILE: test_aiida AIIDA_WARN_v3: 1 run: - .github/workflows/tests.sh + pytest --cov aiida --verbose tests -m 'not nightly' - name: Upload coverage report if: matrix.python-version == 3.9 && github.repository == 'aiidateam/aiida-core' diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index e3d17ae742..a4e3c30de7 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -252,9 +252,10 @@ jobs: - name: Run test suite env: + AIIDA_TEST_PROFILE: test_aiida AIIDA_WARN_v3: 1 run: - .github/workflows/tests.sh + pytest --cov aiida --verbose tests -m 'not nightly' - name: Freeze test environment run: pip freeze | sed '1d' | tee requirements-py-${{ matrix.python-version }}.txt diff --git a/.github/workflows/tests.sh b/.github/workflows/tests.sh deleted file mode 100755 index fb15a28997..0000000000 --- a/.github/workflows/tests.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -ev - -# Make sure the folder containing the workchains is in the python path before the daemon is started -SYSTEM_TESTS="${GITHUB_WORKSPACE}/.github/system_tests" - -# Until the `${SYSTEM_TESTS}/pytest` tests are moved within `tests` we have to run them separately and pass in the path to the -# `conftest.py` explicitly, because otherwise it won't be able to find the fixtures it provides -AIIDA_TEST_PROFILE=test_aiida pytest --cov aiida --verbose tests/conftest.py ${SYSTEM_TESTS}/pytest - -# main aiida-core tests -AIIDA_TEST_PROFILE=test_aiida pytest --cov aiida --verbose tests -m 'not nightly' diff --git a/.github/system_tests/pytest/test_memory_leaks.py b/tests/engine/test_memory_leaks.py similarity index 62% rename from .github/system_tests/pytest/test_memory_leaks.py rename to tests/engine/test_memory_leaks.py index cdd7a536ab..2a0540aef7 100644 --- a/.github/system_tests/pytest/test_memory_leaks.py +++ b/tests/engine/test_memory_leaks.py @@ -24,48 +24,57 @@ def run_finished_ok(*args, **kwargs): """Convenience function to check that run worked fine.""" _, node = run_get_node(*args, **kwargs) - assert node.is_finished_ok, (node.exit_status, node.exit_message) + assert node.is_finished_ok, (node.process_state, node.exit_code) + + +@pytest.fixture +def check_memory_leaks(): + """Check that no processes remain in memory after the test finishes. + + The fixture checks the current processes in memory before running the test which are ignored in the check after the + test. This is to prevent the test failing because some other test in the suite failed to properly cleanup a process. + """ + starting_processes = get_instances(processes.Process, delay=0.2) + + yield + + # Check that no reference to the process is left in memory. Some delay is necessary in order to allow for all + # callbacks to finish. + process_instances = set(get_instances(processes.Process, delay=0.2)).difference(set(starting_processes)) + assert not process_instances, f'Memory leak: process instances remain in memory: {process_instances}' @pytest.mark.skipif(sys.version_info >= (3, 12), reason='Garbage collecting hangs on Python 3.12') +@pytest.mark.usefixtures('aiida_profile', 'check_memory_leaks') def test_leak_run_process(): """Test whether running a dummy process leaks memory.""" inputs = {'a': orm.Int(2), 'b': orm.Str('test')} run_finished_ok(test_processes.DummyProcess, **inputs) - # check that no reference to the process is left in memory - # some delay is necessary in order to allow for all callbacks to finish - process_instances = get_instances(processes.Process, delay=0.2) - assert not process_instances, f'Memory leak: process instances remain in memory: {process_instances}' - @pytest.mark.skipif(sys.version_info >= (3, 12), reason='Garbage collecting hangs on Python 3.12') +@pytest.mark.usefixtures('aiida_profile', 'check_memory_leaks') def test_leak_local_calcjob(aiida_local_code_factory): """Test whether running a local CalcJob leaks memory.""" inputs = {'x': orm.Int(1), 'y': orm.Int(2), 'code': aiida_local_code_factory('core.arithmetic.add', '/bin/bash')} run_finished_ok(ArithmeticAddCalculation, **inputs) - # check that no reference to the process is left in memory - # some delay is necessary in order to allow for all callbacks to finish - process_instances = get_instances(processes.Process, delay=0.2) - assert not process_instances, f'Memory leak: process instances remain in memory: {process_instances}' - @pytest.mark.skipif(sys.version_info >= (3, 12), reason='Garbage collecting hangs on Python 3.12') -def test_leak_ssh_calcjob(): +@pytest.mark.usefixtures('aiida_profile', 'check_memory_leaks') +def test_leak_ssh_calcjob(aiida_computer_ssh): """Test whether running a CalcJob over SSH leaks memory. - Note: This relies on the 'slurm-ssh' computer being set up. + Note: This relies on the localhost configuring an SSH server and allowing to connect to it from localhost. """ + computer = aiida_computer_ssh() + + # Ensure a connection can be opened before launching the calcjob or it will get stuck in the EBM. + with computer.get_transport() as transport: + assert transport.whoami() + code = orm.InstalledCode( - default_calc_job_plugin='core.arithmetic.add', - computer=orm.load_computer('slurm-ssh'), - filepath_executable='/bin/bash' + default_calc_job_plugin='core.arithmetic.add', computer=computer, filepath_executable='/bin/bash' ) inputs = {'x': orm.Int(1), 'y': orm.Int(2), 'code': code} run_finished_ok(ArithmeticAddCalculation, **inputs) - - # check that no reference to the process is left in memory - # some delay is necessary in order to allow for all callbacks to finish - process_instances = get_instances(processes.Process, delay=0.2) - assert not process_instances, f'Memory leak: process instances remain in memory: {process_instances}'