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

Refactor benchmarks' tests #32

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
86967fb
fix benchmarks to run with new workflow struct
sfmig Dec 8, 2023
0df06c4
edit asv config to use this branch
sfmig Dec 11, 2023
3eb0ede
change project name. remove dvcs
sfmig Dec 11, 2023
1603425
split tests with and without CLI inputs
sfmig Dec 20, 2023
5ffcbd0
pass all parameters to cellfinder_run
sfmig Dec 20, 2023
023f1a9
changed required, optional and internal fields. read_config passes. a…
sfmig Dec 20, 2023
e0502ed
add signal and background data from local
sfmig Dec 20, 2023
cbc1bb3
add missing data config fixtures
sfmig Dec 20, 2023
8fe1295
mark GIN download data test as slow
sfmig Dec 20, 2023
e278593
all tests passing with default config
sfmig Dec 20, 2023
d99cb02
all tests passing with default option
sfmig Dec 20, 2023
f83d4e7
make all paths input strings. move methods to class. make some fixtur…
sfmig Dec 20, 2023
2f284ba
refactor setup_workflow
sfmig Dec 21, 2023
bbc2274
cosmetic changes to fixtures
sfmig Dec 21, 2023
50c537e
remove spurious monkeypatched cwd from merge
sfmig Dec 21, 2023
1cc0de2
add skips that were removed in merge
sfmig Dec 21, 2023
d5f437c
move fixtures to code where they are used
sfmig Dec 21, 2023
c1cceea
bring back default_input_config_cellfinder fixture
sfmig Dec 21, 2023
7a015a9
finalise adding local config as parameter in test_read_cellfinder_config
sfmig Dec 21, 2023
6c2cf40
add local config to remaining unit tests
sfmig Dec 21, 2023
724ef94
add config for GIN as parameter in unit tests
sfmig Dec 21, 2023
3100457
add local config and GIN config to test main
sfmig Dec 21, 2023
48a8264
monkeypatched home in integration tests (only works in main)
sfmig Dec 21, 2023
fd3b110
skip tests that use subprocess for now
sfmig Dec 21, 2023
2ada202
monkeypatch pooch.retrieve when forcing download
sfmig Dec 21, 2023
826b55c
make config local fixture copy downloaded GIN data (rather than re-do…
sfmig Dec 21, 2023
d3ac64c
Merge branch 'smg/workflow-tests-caching' into smg/tests-refactor-ben…
sfmig Dec 21, 2023
50e1277
fix benchmarks dir name
sfmig Dec 21, 2023
c8545b5
fix benchmarks to run with workflow changes
sfmig Dec 21, 2023
90b5120
Merge branch 'main' into smg/tests-refactor-benchmarks
sfmig Jan 12, 2024
93bfbd7
add pointer to benchmark in asv monkeypatched config and make simpler…
sfmig Jan 12, 2024
1fcb6f2
add machine command to test_asv_run
sfmig Jan 16, 2024
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
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ exclude *.ini

recursive-include brainglobe_workflows *.py
recursive-include brainglobe_workflows/configs *.json

recursive-include benchmarks *.py
recursive-exclude benchmarks/results *
recursive-exclude .asv/ *
include asv.conf.json

recursive-exclude * __pycache__
Expand Down
14 changes: 7 additions & 7 deletions asv.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"version": 1,

// The name of the project being benchmarked
"project": "brainglobe_workflows",
"project": "brainglobe-workflows",

// The project's homepage
"project_url": "https://github.com/brainglobe/brainglobe-workflows",

// The URL or local path of the source code repository for the
// project being benchmarked
// "repo": ".",
"repo": "https://github.com/brainglobe/brainglobe-workflows",
"repo": "https://github.com/brainglobe/brainglobe-workflows.git",

// The Python project's subdirectory in your repo. If missing or
// the empty string, the project is assumed to be located at the root
Expand Down Expand Up @@ -40,14 +40,14 @@

// List of branches to benchmark. If not provided, defaults to "master"
// (for git) or "default" (for mercurial).
"branches": ["smg/tests-refactor"], // for git
"branches": ["smg/tests-refactor-benchmarks"], // for git
// "branches": ["default"], // for mercurial

// The DVCS being used. If not set, it will be automatically
// determined from "repo" by looking at the protocol in the URL
// (if remote), or by looking for special directories, such as
// ".git" (if local).
"dvcs": "git",
// "dvcs": "git",

// The tool to use to create environments. May be "conda",
// "virtualenv", "mamba" (above 3.8)
Expand Down Expand Up @@ -147,19 +147,19 @@

// The directory (relative to the current directory) that benchmarks are
// stored in. If not provided, defaults to "benchmarks"
"benchmark_dir": "brainglobe_benchmarks",
"benchmark_dir": "benchmarks",

// The directory (relative to the current directory) to cache the Python
// environments in. If not provided, defaults to "env"
"env_dir": ".asv/env",

// The directory (relative to the current directory) that raw benchmark
// results are stored in. If not provided, defaults to "results".
"results_dir": "brainglobe_benchmarks/results",
"results_dir": "benchmarks/results",

// The directory (relative to the current directory) that the html tree
// should be written to. If not provided, defaults to "html".
"html_dir": "brainglobe_benchmarks/html",
"html_dir": "benchmarks/html",

// The number of characters to retain in the commit hashes.
// "hash_length": 8,
Expand Down
99 changes: 71 additions & 28 deletions benchmarks/cellfinder_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import shutil
from pathlib import Path

import pooch
from brainglobe_utils.IO.cells import save_cells
from cellfinder.core.main import main as cellfinder_run
from cellfinder.core.tools.IO import read_with_dask
Expand Down Expand Up @@ -81,9 +80,10 @@
# Custom attributes
input_config_path = str(DEFAULT_JSON_CONFIG_PATH_CELLFINDER)

def setup_cache(
self,
):
def setup_cache(self):

Check warning on line 83 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L83

Added line #L83 was not covered by tests
# here ensure all the data for all possible configs we sweep
# thru is available?
# but in a benchmark directory? (can I monkeypatch home? :S)
"""
Download the input data from the GIN repository to the local
directory specified in the default_config.json
Expand All @@ -108,14 +108,14 @@
config_dict = json.load(cfg)
config = CellfinderConfig(**config_dict)

# Download data with pooch
_ = pooch.retrieve(
url=config.data_url,
known_hash=config.data_hash,
path=config._install_path,
progressbar=True,
processor=pooch.Unzip(extract_dir=config.data_dir_relative),
)
# # Download data with pooch
# _ = pooch.retrieve(
# url=config.data_url,
# known_hash=config.data_hash,
# path=config._install_path,
# progressbar=True,
# processor=pooch.Unzip(extract_dir=config.data_dir_relative),
# )

# Check paths to input data should now exist in config
assert Path(config._signal_dir_path).exists()
Expand All @@ -129,12 +129,7 @@
"""

# Run setup
cfg = setup_cellfinder_workflow(
[
"--config",
self.input_config_path,
]
)
cfg = setup_cellfinder_workflow(self.input_config_path)

Check warning on line 132 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L132

Added line #L132 was not covered by tests

# Save configuration as attribute
self.cfg = cfg
Expand Down Expand Up @@ -162,7 +157,7 @@
A base class for timing benchmarks for the cellfinder workflow.
"""

def time_workflow_from_cellfinder_run(self):
def time_workflow(self):

Check warning on line 160 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L160

Added line #L160 was not covered by tests
run_workflow_from_cellfinder_run(self.cfg)


Expand All @@ -177,10 +172,10 @@
"""

def time_read_signal_with_dask(self):
read_with_dask(self.cfg._signal_dir_path)
read_with_dask(str(self.cfg._signal_dir_path))

Check warning on line 175 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L175

Added line #L175 was not covered by tests

def time_read_background_with_dask(self):
read_with_dask(self.cfg._background_dir_path)
read_with_dask(str(self.cfg._background_dir_path))

Check warning on line 178 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L178

Added line #L178 was not covered by tests


class TimeDetectCells(TimeBenchmarkPrepGIN):
Expand All @@ -198,13 +193,37 @@
# basic setup
TimeBenchmarkPrepGIN.setup(self)

# add input data as arrays to config
self.signal_array = read_with_dask(self.cfg._signal_dir_path)
self.background_array = read_with_dask(self.cfg._background_dir_path)
# add input data as arrays to the config
self.signal_array = read_with_dask(str(self.cfg._signal_dir_path))
self.background_array = read_with_dask(

Check warning on line 198 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L197-L198

Added lines #L197 - L198 were not covered by tests
str(self.cfg._background_dir_path)
)

def time_cellfinder_run(self):
cellfinder_run(
self.signal_array, self.background_array, self.cfg.voxel_sizes
self.signal_array,
self.background_array,
self.cfg.voxel_sizes,
self.cfg.start_plane,
self.cfg.end_plane,
self.cfg.trained_model,
self.cfg.model_weights,
self.cfg.model,
self.cfg.batch_size,
self.cfg.n_free_cpus,
self.cfg.network_voxel_sizes,
self.cfg.soma_diameter,
self.cfg.ball_xy_size,
self.cfg.ball_z_size,
self.cfg.ball_overlap_fraction,
self.cfg.log_sigma_size,
self.cfg.n_sds_above_mean_thresh,
self.cfg.soma_spread_factor,
self.cfg.max_cluster_size,
self.cfg.cube_width,
self.cfg.cube_height,
self.cfg.cube_depth,
self.cfg.network_depth,
)


Expand All @@ -215,12 +234,36 @@
TimeBenchmarkPrepGIN.setup(self)

# add input data as arrays to config
self.signal_array = read_with_dask(self.cfg._signal_dir_path)
self.background_array = read_with_dask(self.cfg._background_dir_path)
self.signal_array = read_with_dask(str(self.cfg._signal_dir_path))
self.background_array = read_with_dask(

Check warning on line 238 in benchmarks/cellfinder_core.py

View check run for this annotation

Codecov / codecov/patch

benchmarks/cellfinder_core.py#L237-L238

Added lines #L237 - L238 were not covered by tests
str(self.cfg._background_dir_path)
)

# detect cells
self.detected_cells = cellfinder_run(
self.signal_array, self.background_array, self.cfg.voxel_sizes
self.signal_array,
self.background_array,
self.cfg.voxel_sizes,
self.cfg.start_plane,
self.cfg.end_plane,
self.cfg.trained_model,
self.cfg.model_weights,
self.cfg.model,
self.cfg.batch_size,
self.cfg.n_free_cpus,
self.cfg.network_voxel_sizes,
self.cfg.soma_diameter,
self.cfg.ball_xy_size,
self.cfg.ball_z_size,
self.cfg.ball_overlap_fraction,
self.cfg.log_sigma_size,
self.cfg.n_sds_above_mean_thresh,
self.cfg.soma_spread_factor,
self.cfg.max_cluster_size,
self.cfg.cube_width,
self.cfg.cube_height,
self.cfg.cube_depth,
self.cfg.network_depth,
)

def time_save_cells(self):
Expand Down
77 changes: 46 additions & 31 deletions tests/benchmarks/test_cellfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


@pytest.fixture()
def asv_config_monkeypatched_path(tmp_path: Path) -> str:
def asv_config_monkeypatched_path(tmp_path: Path) -> Path:
"""
Create a monkeypatched asv.conf.json file
in a Pytest-generated temporary directory
Expand All @@ -20,22 +20,27 @@ def asv_config_monkeypatched_path(tmp_path: Path) -> str:

Returns
-------
str
Path
Path to monkeypatched asv config file
"""
# read reference asv config
asv_original_path = Path(__file__).resolve().parents[3] / "asv.conf.json"
asv_original_path = Path(__file__).resolve().parents[2] / "asv.conf.json"
asv_monkeypatched_dict = util.load_json(
asv_original_path, js_comments=True
)

# change directories
# point to benchmarks directory in config
asv_monkeypatched_dict["benchmark_dir"] = str(
Path(__file__).resolve().parents[2] / "benchmarks"
)

# change env, results and html directories
for ky in ["env_dir", "results_dir", "html_dir"]:
asv_monkeypatched_dict[ky] = str(
Path(tmp_path) / asv_monkeypatched_dict[ky]
)

# change repo to URL rather than local
# ensure repo points to URL
asv_monkeypatched_dict[
"repo"
] = "https://github.com/brainglobe/brainglobe-workflows.git"
Expand All @@ -50,51 +55,61 @@ def asv_config_monkeypatched_path(tmp_path: Path) -> str:
# check json file exists
assert asv_monkeypatched_path.is_file()

return str(asv_monkeypatched_path)

return asv_monkeypatched_path

@pytest.mark.skip(reason="focus of PR32")
def test_run_benchmarks(asv_config_monkeypatched_path):
# --- ideally monkeypatch an asv config so that results are in tmp_dir?

# set up machine (env_dir, results_dir, html_dir)
def test_asv_run(asv_config_monkeypatched_path: Path):
asv_machine_output = subprocess.run(
[
"asv",
"machine",
"--yes",
"--config",
asv_config_monkeypatched_path,
str(asv_config_monkeypatched_path), # use monkeypatched config
]
)
assert asv_machine_output.returncode == 0

# run benchmarks
asv_benchmark_output = subprocess.run(
[
"asv",
"run",
"--quick", # each benchmark function is run only once
"--config",
asv_config_monkeypatched_path,
# "--dry-run"
# # Do not save any results to disk? not truly testing then
str(asv_config_monkeypatched_path),
],
cwd=str(
Path(asv_config_monkeypatched_path).parent
), # run from where asv config is
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
)
# STDOUT: "· Cloning project\n· Fetching recent changes\n·
# Creating environments\n· No __init__.py file in 'benchmarks'\n"

# check returncode
assert asv_benchmark_output.returncode == 0

# check logs?

# delete directories?
# check teardown after yield:
# https://docs.pytest.org/en/6.2.x/fixture.html#yield-fixtures-recommended
def test_asv_run_machine_specific(
asv_config_monkeypatched_path: Path,
):
# setup machine
asv_specific_machine_name = "CURRENT_MACHINE"
asv_machine_output = subprocess.run(
[
"asv",
"machine",
"--machine",
asv_specific_machine_name, # name of the current machine
"--yes",
"--config",
str(asv_config_monkeypatched_path), # use monkeypatched config
]
)
assert asv_machine_output.returncode == 0

# run benchmarks on machine
asv_benchmark_output = subprocess.run(
[
"asv",
"run",
"--quick", # each benchmark function is run only once
"--config",
str(asv_config_monkeypatched_path),
"--machine",
asv_specific_machine_name,
],
)
assert asv_benchmark_output.returncode == 0
Loading