-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ADD: pytest tests and run in CI (#45)
* add pytest for tests * add tests and run in ci * add tifffile and imagecodecs to dev deps * patch resolution unit for py 3.7 * fix for python 3.7 tifffile * fix version_info check to be false for 3.7 * remove the --help cmds in tests * add stubs for tests of converters * add ci status badge * add tests of geojson conversion * use np.allclose instead of == for floats
- Loading branch information
Showing
4 changed files
with
147 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import json | ||
from pathlib import Path | ||
import sys | ||
|
||
from click.testing import CliRunner | ||
import numpy as np | ||
import pandas as pd | ||
import pytest | ||
import tifffile | ||
|
||
|
||
@pytest.fixture | ||
def tiff_image(tmp_path: Path) -> Path: | ||
x = np.empty((4096, 4096, 3), dtype="uint8") | ||
x[...] = [160, 32, 240] # rgb for purple | ||
path = Path(tmp_path / "images" / "purple.tif") | ||
path.parent.mkdir(exist_ok=True) | ||
|
||
if sys.version_info >= (3, 8): | ||
tifffile.imwrite( | ||
path, | ||
data=x, | ||
compression="zlib", | ||
tile=(256, 256), | ||
# 0.25 micrometers per pixel. | ||
resolution=(40000, 40000), | ||
resolutionunit=tifffile.RESUNIT.CENTIMETER, | ||
) | ||
else: | ||
# Earlier versions of tifffile do not have resolutionunit kwarg. | ||
tifffile.imwrite( | ||
path, | ||
data=x, | ||
compression="zlib", | ||
tile=(256, 256), | ||
# 0.25 micrometers per pixel. | ||
resolution=(40000, 40000, "CENTIMETER"), | ||
) | ||
|
||
return path | ||
|
||
|
||
def test_cli_list(): | ||
from wsinfer.cli.cli import cli | ||
|
||
runner = CliRunner() | ||
result = runner.invoke(cli, ["list"]) | ||
assert "resnet34" in result.output | ||
assert "TCGA-BRCA-v1" in result.output | ||
assert result.exit_code == 0 | ||
|
||
|
||
def test_cli_run_and_convert(tiff_image: Path, tmp_path: Path): | ||
from wsinfer.cli.cli import cli | ||
|
||
runner = CliRunner() | ||
results_dir = tmp_path / "inference" | ||
result = runner.invoke( | ||
cli, | ||
[ | ||
"run", | ||
"--wsi_dir", | ||
tiff_image.parent, | ||
"--model", | ||
"resnet34", | ||
"--weights", | ||
"TCGA-BRCA-v1", | ||
"--results_dir", | ||
results_dir, | ||
], | ||
) | ||
assert result.exit_code == 0 | ||
assert (results_dir / "model-outputs").exists() | ||
df = pd.read_csv(results_dir / "model-outputs" / "purple.csv") | ||
assert df.columns.tolist() == [ | ||
"slide", | ||
"minx", | ||
"miny", | ||
"width", | ||
"height", | ||
"prob_notumor", | ||
"prob_tumor", | ||
] | ||
assert (df.loc[:, "slide"] == str(tiff_image)).all() | ||
assert (df.loc[:, "width"] == 350).all() | ||
assert (df.loc[:, "height"] == 350).all() | ||
assert (df.loc[:, "width"] == 350).all() | ||
assert np.allclose(df.loc[:, "prob_notumor"], 0.9525967836380005) | ||
assert np.allclose(df.loc[:, "prob_tumor"], 0.04740329459309578) | ||
|
||
# Test conversion scripts. | ||
geojson_dir = results_dir / "geojson" | ||
result = runner.invoke(cli, ["togeojson", str(results_dir), str(geojson_dir)]) | ||
assert result.exit_code == 0 | ||
with open(geojson_dir / "purple.json") as f: | ||
d = json.load(f) | ||
assert len(d["features"]) == 144 | ||
|
||
for geojson_row in d["features"]: | ||
assert geojson_row["type"] == "Feature" | ||
assert geojson_row["id"] == "PathTileObject" | ||
assert geojson_row["geometry"]["type"] == "Polygon" | ||
|
||
# Check the probability values. | ||
assert all( | ||
np.allclose(dd["properties"]["measurements"][0]["value"], 0.9525967836380004) | ||
for dd in d["features"] | ||
) | ||
assert all( | ||
np.allclose(dd["properties"]["measurements"][1]["value"], 0.0474032945930957) | ||
for dd in d["features"] | ||
) | ||
|
||
# Check the names. | ||
assert all( | ||
dd["properties"]["measurements"][0]["name"] == "prob_notumor" | ||
for dd in d["features"] | ||
) | ||
assert all( | ||
dd["properties"]["measurements"][1]["name"] == "prob_tumor" | ||
for dd in d["features"] | ||
) | ||
|
||
# Check the coordinate values. | ||
for df_row, geojson_row in zip(df.itertuples(), d["features"]): | ||
maxx = df_row.minx + df_row.width | ||
maxy = df_row.miny + df_row.height | ||
df_coords = [ | ||
[maxx, df_row.miny], | ||
[maxx, maxy], | ||
[df_row.minx, maxy], | ||
[df_row.minx, df_row.miny], | ||
[maxx, df_row.miny], | ||
] | ||
assert [df_coords] == geojson_row["geometry"]["coordinates"] | ||
|
||
|
||
@pytest.mark.xfail | ||
def test_convert_to_sbu(): | ||
# TODO: create a synthetic output and then convert it. Check that it is valid. | ||
assert False |