Skip to content
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`pathway --method gsea` — the default — now works from a pip/source install.
- Pathway smoke tests (`pathway --method gsea` and `--method rra`, end to end)
and a small GMT test fixture.
- `tests/conftest.py`, which puts this repository's freshly built `RRA` and
`mageckGSEA` first on `PATH` when they exist. The suite invokes the helpers by
bare name, so under an editable install — which never runs the compile step —
it silently tested whichever build came first, including a MAGeCK v1 package's
identically named binaries. That reported four `mageckGSEA` failures against
fixes present in the checkout, with nothing in the output naming the binary as
the cause. The override applies in CI too, since `pip install .` leaves the
build outputs in the source tree alongside the copies `data_files` installs;
so the two tests that assert the helpers are *installed* now look at the PATH
as it stood before the override, keeping a broken installation from passing.
- `count` writes `<prefix>.pg_unmapped.txt` when `--unmapped-to-file` is given in
paired-guide mode, recording pairs that were counted but excluded from
`pg_count.txt` — because the second guide matched no entry in `--list-seq-2`,
Expand Down
52 changes: 52 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Test configuration for MAGeCK2.

The suite shells out to the bundled C++ helpers by bare name (``RRA``,
``mageckGSEA``), so it exercises whichever build happens to come first on PATH.
That is the correct binary after ``pip install .`` -- setup.py compiles the
helpers and installs them alongside the package -- but not during ordinary local
development: an editable install never runs that build step, and a MAGeCK v1
package in the same environment ships commands with the *same names*. The suite
then silently tests v1's binaries and reports failures against fixes that are
present in this checkout, which is confusing and points nowhere useful.

Put this repository's own freshly built helpers first on PATH when they exist,
so the tests always describe this checkout.

Note this override applies in CI too: ``pip install .`` runs ``make`` in the
source tree, so the build outputs are present there alongside the copies
``data_files`` installs. The installed copies would then never be exercised, and
a regression in helper installation could pass CI even though an installed
MAGeCK2 cannot find its helpers. To keep that coverage, the PATH as it stood
before this hook ran is published as ``MAGECK2_INSTALLED_PATH``, and the tests
asserting the helpers are installed look there rather than at the build tree.
"""

import os
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent

# (binary name, directory the Makefile writes it to)
HELPER_BIN_DIRS = [
("RRA", REPO_ROOT / "rra" / "bin"),
("mageckGSEA", REPO_ROOT / "gsea" / "bin"),
]


#: The PATH before this hook ran -- i.e. where an *installed* MAGeCK2 finds its
#: helpers. Published so tests can assert on the installation rather than on the
#: build tree that gets prepended below.
INSTALLED_PATH_ENV = "MAGECK2_INSTALLED_PATH"


def pytest_configure(config):
"""Prepend repo-built helper directories to PATH, most specific first."""
os.environ.setdefault(INSTALLED_PATH_ENV, os.environ.get("PATH", ""))

prefix = []
for name, bindir in HELPER_BIN_DIRS:
if (bindir / name).is_file():
prefix.append(str(bindir))

if prefix:
os.environ["PATH"] = os.pathsep.join(prefix + [os.environ.get("PATH", "")])
Comment on lines +51 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve installed-helper coverage in CI

In the CI workflow, pip install .[test] runs BuildWithHelpers, whose make calls leave both executables in these source-tree directories; this hook therefore always prepends those build outputs before pytest -v. Consequently the smoke tests no longer exercise the copies installed through data_files, so a regression in helper installation, destination, or permissions can pass CI even though an installed MAGeCK2 cannot find or execute its helpers. Limit this override to the editable/local-development case or retain a CI test that uses the unmodified installation PATH.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in 6aba9ff. You were right on both the mechanism and the consequence.

Verified the mechanism first — pip install .[test] into a fresh venv leaves the binaries in both places:

source tree:  rra/bin/RRA, gsea/bin/mageckGSEA   (make output)
venv:         .venv/bin/RRA, .venv/bin/mageckGSEA (data_files copy)

So the fallback my PR description claimed CI relies on never fires. That description was wrong; I've corrected it.

Then measured the coverage loss by simulating a data_files regression — installed helpers removed, source tree intact, venv built the way CI builds one:

with the conftest as submitted   ->  49 passed   (a broken release ships green)
with no conftest at all          ->   8 failed
after this fix                   ->   2 failed

The 49-passed row is exactly your point, and it would have masked a regression of the bug 0.2.0 fixed.

The fix keeps both coverages rather than dropping the override. conftest now publishes the pre-override PATH as MAGECK2_INSTALLED_PATH, and the two tests that assert the helpers are installedtest_rra_binary_on_path, test_mageckgsea_binary_on_path — resolve against it. The rest of the suite still tests the checkout's own build, which is the point of the override.

I preferred this to limiting the override to editable installs: "am I editable?" is awkward to detect reliably, and the installation contract deserves an explicit assertion rather than being implied by whichever binary happened to be found.

A side benefit — this reports the failure better than the pre-conftest baseline did. A broken install used to surface as eight GSEA assertion failures naming nothing relevant; it now says compiled RRA binary not installed onto PATH.

Verified across all three scenarios: good install 49 pass, broken install 2 fail (named), local editable install with v1 helpers in the environment 49 pass.

22 changes: 19 additions & 3 deletions tests/test_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
push in CI.
"""

import os
import shutil
import subprocess
from pathlib import Path
Expand All @@ -24,14 +25,29 @@ def test_package_imports():
assert __version__


def _installed_path():
"""The PATH as it stood before conftest prepended this repo's build tree.

The rest of the suite deliberately tests the checkout's own helpers. These
two tests are the exception: they check that an *installed* MAGeCK2 can find
its helpers, which is what `data_files` is responsible for and what a user
running `pip install mageck2` depends on. Looking at the build tree here
would let a broken installation pass CI, since `pip install .` leaves the
build outputs in the source tree as well.
"""
return os.environ.get("MAGECK2_INSTALLED_PATH", os.environ.get("PATH", ""))


def test_rra_binary_on_path():
assert shutil.which("RRA") is not None, "compiled RRA binary not found on PATH"
assert (
shutil.which("RRA", path=_installed_path()) is not None
), "compiled RRA binary not installed onto PATH"


def test_mageckgsea_binary_on_path():
assert (
shutil.which("mageckGSEA") is not None
), "compiled mageckGSEA binary not found on PATH"
shutil.which("mageckGSEA", path=_installed_path()) is not None
), "compiled mageckGSEA binary not installed onto PATH"


def test_mageck2_cli_runs():
Expand Down
Loading