From 6a5577fbd2d08d91c897099fa11a31f64cea3776 Mon Sep 17 00:00:00 2001 From: Eachan Johnson Date: Thu, 3 Oct 2024 09:36:20 +0100 Subject: [PATCH] Update testing suite to pytest (#655) * Change tester from nose to pytest Remove python3.12 test * Remove nose Remove nose * Remove assert parentheses Switch script tests to pytest * Add python3.12 support --------- Co-authored-by: eaoljo <111305346+eaoljo@users.noreply.github.com> --- .../workflows/{nosetests.yml => tests.yml} | 8 +- tests/test_style.py | 4 +- tests/test_umi_tools.py | 206 +++++++++--------- 3 files changed, 110 insertions(+), 108 deletions(-) rename .github/workflows/{nosetests.yml => tests.yml} (84%) diff --git a/.github/workflows/nosetests.yml b/.github/workflows/tests.yml similarity index 84% rename from .github/workflows/nosetests.yml rename to .github/workflows/tests.yml index 643a7051..9851d52d 100644 --- a/.github/workflows/nosetests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: ["3.7", "3.8", "3.9"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] os: [ubuntu-latest] steps: @@ -20,7 +20,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install flake8 nose pep8 pyyaml + pip install flake8 pytest pep8 pyyaml if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with flake8 run: | @@ -31,6 +31,6 @@ jobs: - name: Install run: | python setup.py install - - name: Test with nosetests + - name: Test with PyTest run: | - nosetests -v tests/test_umi_tools.py + pytest -v tests/test_umi_tools.py diff --git a/tests/test_style.py b/tests/test_style.py index b7ef7256..ece94e8e 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -16,7 +16,6 @@ import pep8 import glob import os -from nose.tools import ok_ # DIRECTORIES to examine for python modules/scripts EXPRESSIONS = ( @@ -57,8 +56,7 @@ def check_style(filename): found = ['%s:%i' % (x, y) for x, y in report.counters.items() if x not in IGNORE] total = sum(take) - ok_(total == 0, - 'pep8 style violations in %s: %s' % (filename, ','.join(found))) + assert total == 0, 'pep8 style violations in %s: %s' % (filename, ','.join(found)) def test_style(): diff --git a/tests/test_umi_tools.py b/tests/test_umi_tools.py index 5fe4620b..f76fe254 100644 --- a/tests/test_umi_tools.py +++ b/tests/test_umi_tools.py @@ -22,21 +22,112 @@ import hashlib import sys import platform - -from nose.tools import ok_ +import pytest PYTHON_VERSION = platform.python_version() IS_PY3 = sys.version_info.major >= 3 - - SUBDIRS = ("gpipe", "optic") # Setup logging LOGFILE = open("test_scripts.log", "a") DEBUG = os.environ.get("CGAT_DEBUG", False) +def compute_checksum(filename): + '''return md5 checksum of file.''' + return hashlib.md5(open(filename, 'rb').read()).hexdigest() + + +def get_tests_directory(): + """discover and return the absolute path of the root directory""" + testdir = os.getcwd() + if not testdir.endswith("tests"): + testdir = os.path.join(testdir, "tests") + if not os.path.exists(testdir): + raise ValueError("can not find test directory") + + return testdir + + +def _read(fn): + if fn.endswith(".gz"): + with gzip.open(fn) as inf: + data = inf.read() + else: + with open(fn, "rb") as inf: + data = inf.read() + + if IS_PY3: + try: + data = data.decode("ascii") + except UnicodeDecodeError: + return data + + data = [x for x in data.splitlines() + if not x.startswith("#")] + + return data + + +def get_scripts(): + # directory location of tests + testing_dir = get_tests_directory() + + # directory location of scripts + tool_dir = os.path.join(os.path.dirname(testing_dir), "umi_tools") + + tool = "tests/umi_tools.py" + tool_name = os.path.basename(tool) + + return [os.path.abspath(os.path.join(tool_dir, tool_name))] + -def check_main(script): + +def get_script_parameters(): + '''yield list of scripts to test.''' + # the current directory + current_dir = os.getcwd() + + # directory location of tests + testing_dir = get_tests_directory() + + # directory location of scripts + tool_dir = os.path.join(os.path.dirname(testing_dir), "umi_tools") + + tool = "tests/umi_tools.py" + tool_name = os.path.basename(tool) + + fn = 'tests/tests.yaml' + assert os.path.exists(fn), "tests.yaml does not exist!" + + tool_tests = yaml.safe_load(open(fn)) + + parameters = [] + for test, values in sorted(list(tool_tests.items())): + if "skip_python" in values: + versions = [x.strip() for x in + str(values["skip_python"]).split(",")] + versions = [x for x in versions + if PYTHON_VERSION.startswith(x)] + if len(versions) > 0: + continue + parameters.append(( + test, + values.get('stdin', None), + values['options'], + values['outputs'], + values['references'], + current_dir, + values.get('sort', False) + )) + return parameters + +######################################### +# List of tests to perform. +######################################### +# The fields are: + +@pytest.mark.parametrize("script", get_scripts()) +def test_script_has_main(script): '''test is if a script can be imported and has a main function. ''' @@ -58,26 +149,16 @@ def check_main(script): script = re.sub("%s_" % s, "%s/" % s, script) # check for text match - ok_([x for x in open(script) if x.startswith("def main(")], - "no main function") - - -def compute_checksum(filename): - '''return md5 checksum of file.''' - return hashlib.md5(open(filename, 'rb').read()).hexdigest() - -######################################### -# List of tests to perform. -######################################### -# The fields are: + assert [x for x in open(script) if x.startswith("def main(")], "no main function" -def check_script(test_name, - stdin, - options, outputs, - references, - current_dir, - sort=False): +@pytest.mark.parametrize("test_name,stdin,options,outputs,references,current_dir,sort", get_script_parameters()) +def test_script(test_name, + stdin, + options, outputs, + references, + current_dir, + sort): '''check script. # 1. Name of the script # 2. Filename to use as stdin @@ -204,81 +285,4 @@ def check_script(test_name, if not DEBUG: shutil.rmtree(tmpdir) - ok_(not fail, msg) - - -def get_tests_directory(): - """discover and return the absolute path of the root directory""" - testdir = os.getcwd() - if not testdir.endswith("tests"): - testdir = os.path.join(testdir, "tests") - if not os.path.exists(testdir): - raise ValueError("can not find test directory") - - return testdir - - -def test_tool(): - '''yield list of scripts to test.''' - # the current directory - current_dir = os.getcwd() - - # directory location of tests - testing_dir = get_tests_directory() - - # directory location of scripts - tool_dir = os.path.join(os.path.dirname(testing_dir), "umi_tools") - - #for test_script in test_dirs: - - check_main.description = os.path.join(tool_dir, "def_main") - - tool = "tests/umi_tools.py" - tool_name = os.path.basename(tool) - - yield (check_main, - os.path.abspath(os.path.join(tool_dir, tool_name))) - - fn = 'tests/tests.yaml' - assert os.path.exists(fn), "tests.yaml does not exist!" - - tool_tests = yaml.safe_load(open(fn)) - - for test, values in sorted(list(tool_tests.items())): - check_script.description = os.path.join(tool_name, test) - if "skip_python" in values: - versions = [x.strip() for x in - str(values["skip_python"]).split(",")] - versions = [x for x in versions - if PYTHON_VERSION.startswith(x)] - if len(versions) > 0: - continue - - yield(check_script, - test, - values.get('stdin', None), - values['options'], - values['outputs'], - values['references'], - current_dir, - values.get('sort', False)) - - -def _read(fn): - if fn.endswith(".gz"): - with gzip.open(fn) as inf: - data = inf.read() - else: - with open(fn, "rb") as inf: - data = inf.read() - - if IS_PY3: - try: - data = data.decode("ascii") - except UnicodeDecodeError: - return data - - data = [x for x in data.splitlines() - if not x.startswith("#")] - - return data + assert not fail, msg