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

Initial start on cpython interface testing #28

Merged
merged 2 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 46 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ on:
- main

jobs:
test:
name: Test
python_test:
name: Python tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand Down Expand Up @@ -42,30 +42,32 @@ jobs:
run: |
pytest

packaging:
name: Packaging
c_python_test:
name: CPython interface tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
- name: Install system dependencies
run: |
sudo apt install -y gcovr
- name: Install python dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build twine validate-pyproject[all]
- name: Check and install package
python -m pip install numpy pytest
- name: Build module with coverage
run: |
validate-pyproject pyproject.toml
python -m build
python -m twine check --strict dist/*
python -m pip install dist/*.whl
- name: Check vcztools CLI
# Build the extension module in-place so pytest can find it
CFLAGS="--coverage" python3 setup.py build_ext --inplace
- name: Run tests
run: |
vcztools --help
# Make sure we don't have ``vcztools`` in the CWD
cd tests
python -m vcztools --help
pytest -vs tests/test_cpython_interface.py
- name: Show coverage
run: |
gcovr --filter vcztools

c_test:
name: C tests
Expand All @@ -88,7 +90,33 @@ jobs:
run: |
ninja -C build coverage-text
cat build/meson-logs/coverage.txt
- name: Valgrind
- name: Valgrind
working-directory: ./lib
run: |
valgrind --leak-check=full --error-exitcode=1 ./build/tests

packaging:
name: Packaging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build twine validate-pyproject[all]
- name: Check and install package
run: |
validate-pyproject pyproject.toml
python -m build
python -m twine check --strict dist/*
python -m pip install dist/*.whl
- name: Check vcztools CLI
run: |
vcztools --help
# Make sure we don't have ``vcztools`` in the CWD
cd tests
python -m vcztools --help

53 changes: 53 additions & 0 deletions tests/test_cpython_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import pytest
import numpy as np

from vcztools import _vcztools

FIXED_FIELD_NAMES = ["chrom", "pos", "id", "qual", "ref", "alt", "filter"]


def example_fixed_data(num_variants, num_samples=0):
chrom = np.array(["X"] * num_variants, dtype="S")
pos = np.arange(num_variants, dtype=np.int32)
id = np.array(["."] * num_variants, dtype="S").reshape((num_variants, 1))
ref = np.array(["A"] * num_variants, dtype="S")
alt = np.array(["T"] * num_variants, dtype="S").reshape((num_variants, 1))
qual = np.arange(num_variants, dtype=np.float32)
filter_ = np.ones(num_variants, dtype=bool).reshape((num_variants, 1))
filter_id = np.array(["PASS"], dtype="S")
return {
"chrom": chrom,
"pos": pos,
"id": id,
"qual": qual,
"ref": ref,
"alt": alt,
"filter": filter_,
"filter_ids": filter_id,
}


class TestTypeChecking:
@pytest.mark.parametrize("name", FIXED_FIELD_NAMES)
def test_field_incorrect_length(self, name):
num_variants = 5
data = example_fixed_data(num_variants)
data[name] = data[name][1:]
with pytest.raises(ValueError, match=f"Array {name.upper()} must have "):
_vcztools.VcfEncoder(num_variants, 0, **data)

@pytest.mark.parametrize("name", FIXED_FIELD_NAMES)
def test_field_incorrect_dtype(self, name):
num_variants = 5
data = example_fixed_data(num_variants)
data[name] = np.zeros(data[name].shape, dtype=np.int64)
with pytest.raises(ValueError, match=f"Wrong dtype for {name.upper()}"):
_vcztools.VcfEncoder(num_variants, 0, **data)

@pytest.mark.parametrize("name", FIXED_FIELD_NAMES)
def test_field_incorrect_type(self, name):
num_variants = 5
data = example_fixed_data(num_variants)
data[name] = "A Python string"
with pytest.raises(TypeError, match=f"must be numpy.ndarray"):
_vcztools.VcfEncoder(num_variants, 0, **data)
6 changes: 3 additions & 3 deletions vcztools/_vcztoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ VcfEncoder_store_fixed_array(VcfEncoder *self, PyArrayObject *array, const char
goto out;
}
if (PyArray_DTYPE(array)->type_num != type) {
PyErr_Format(PyExc_ValueError, "Array %s is not of the correct type", name);
PyErr_Format(PyExc_ValueError, "Wrong dtype for %s", name);
goto out;
}

Expand Down Expand Up @@ -189,7 +189,7 @@ VcfEncoder_init(VcfEncoder *self, PyObject *args, PyObject *kwds)
goto out;
}

/* NOTE: we generalise this pattern for CHROM also to save a bit of time
/* NOTE: we could generalise this pattern for CHROM also to save a bit of time
* in building numpy String arrays */
assert(PyArray_CheckExact(filter_ids));
if (!PyArray_CHKFLAGS(filter_ids, NPY_ARRAY_IN_ARRAY)) {
Expand All @@ -209,7 +209,7 @@ VcfEncoder_init(VcfEncoder *self, PyObject *args, PyObject *kwds)
if (VcfEncoder_add_array(self, "IDS/", "FILTER", filter_ids) != 0) {
goto out;
}
if (VcfEncoder_store_fixed_array(self, filter, "filter", NPY_BOOL, 2, num_variants)
if (VcfEncoder_store_fixed_array(self, filter, "FILTER", NPY_BOOL, 2, num_variants)
!= 0) {
goto out;
}
Expand Down