R package bycatch (v0.10.4) — seabird bycatch risk assessment.
Maintainer: IslasGECI.
| Command | What it does |
|---|---|
make tests |
Run all tests (devtools::test) |
make tests_fast |
Run fast tests only (skips tests/testthat/slow/) |
make tests_slow |
Run slow tests in tests/testthat/slow/ only |
make tests_file file=<path> |
Run a single test file (no commit/restore) |
make format |
Auto-format R/ and tests/ with styler |
make check |
Check formatting only (no auto-fix) |
make clean |
Remove *.tar.gz, tests/testthat/_snaps, NAMESPACE |
make setup |
make clean → devtools::install → check → build → document |
make coverage |
Run tests + generate HTML coverage report |
make red/green/refactor |
TDD phase targets with auto-commit/restore |
devtoolsis only available inside Docker. Run tests viadocker exec bycatch_code_ci make tests. Each test file contains exactly onedescribe()block.make tests_filedoes not work for files inslow/; usetestthat::test_file()directly.Fast suite (~52s, 46 tests): 21 files. Slow suite (~6.5min, 2 tests): 2 files. Bottleneck:
slow/test_create_potential_kba.R(166s) andslow/test_compute_potential_kba.R(222s), both calltrack2KBA::findSite.TDD commits use emoji convention: 🛑🧪 for Red (failing tests), ✅🧪 for Green (passing implementation), ♻️ for refactor, 📝 for documentation.
devtools::test() loads code from R/ source directory — no rebuild needed. Only run make install to regenerate NAMESPACE, docs, or check CMD warnings.
- Red phase: format tests, run. If they fail → stage
tests/testthat/*.R→ commit with 🛑🧪. If pass →git restore . - Green phase: format R/, run. If pass → stage
R/*.R→ commit with ✅🧪. If fail →git restore . - Refactor phase: format both, run. If pass → stage all → commit with 📝. If fail →
git restore . - Single-file variants:
make red_file,green_file,refactor_file file=<path>
Three levels: Level 1 Pure (compute/plot, no I/O), Level 1 I/O (read/write/import/export, no computation), Level 2 Artifact (create/render, orchestrate). Docs: Arquitectura, Guía de estilo, Desacoplamiento.
| Prefix | Level | Role |
|---|---|---|
compute_* |
1 Pure | In-memory calculation, no I/O |
plot_* |
1 Pure | In-memory ggplot2, no I/O |
.name (dot) |
2 Helper | Private, called only by Level 2 |
create_* |
2 Artifact | Read → compute → write |
render_* |
2 Artifact | Read → plot → write |
import_* / export_* |
1 I/O | Disk I/O only, no computation |
- Level 1 Pure never reads/writes files. Level 1 I/O only reads/writes files, never computes.
- Level 2 composes Level 1 functions. Only Make orchestrates Level 2 — they never call each other.
- Every
track2KBAfunction appears in exactly onecompute_*(orplot_*). - Every
compute_*appears in exactly onecreate_*(orrender_*). Nocompute_*calls another — read intermediate results from disk instead. - Row filters (
[,dplyr::filter) belong incompute_*functions, never increate_*/render_*. Exception: row filtering inimport_*is permitted I/O adaptation.
Level 2 functions accept a single options list via get_domain_specific_options(). Every option key read by Level 2 must have a corresponding flag definition in R/get_domain_specific_options.R and be listed in test_get_domain_specific_options.R — otherwise CLI calls fail with "long flag is invalid".
compute_* functions are pure (return lists or data.frames). plot_* functions return ggplot2 objects. Disk I/O lives only in create_*/render_* functions.
R/— 5 files:cli.R(Level 2: create_, render_),compute.R(all compute_),plot.R(internal plot_),io.R(import_config, import_trips, import_trips_summary),get_domain_specific_options.R(parser flags).tests/testthat/— 21 fast + 2 slow inslow/. Onedescribe()per file. Paths hardcoded as/workdir/tests/data/…(Docker convention).tests/data/— CSV and RDS fixtures generated bytests/src/create_test_fixtures.R.track2kba/— Read-only reference clone ofBirdLifeInternational/track2kba(clean checkout, not a submodule).
-
Hash fragility: MD5 hashes of RDS output files (
tools::md5sum) are fragile — they break on R serialization format changes, package version bumps, and numerical drift. For regression tests that guard against a specific algorithmic change, prefer comparing a single numeric value from the output against a threshold at the midpoint of the two versions:- Run both versions (old and fixed) to obtain V1 and V2.
- Set threshold at midpoint
(V1+V2)/2, rounded to nearest 1,000. - Assert the output is on the correct side of the threshold.
Example:
sum(UDPolygons$area)ormax(UDPolygons$area)discriminate between two smoothing parameters (magvsscaleARS) because they produce materially different polygon areas (~44% difference). The midpoint provides a ~15% safety margin against numerical drift.
-
covr::package_coverage()runs only the fast suite —render_*bodies show as uncovered (exercised only by slow tests). -
Coverage script:
tests/testthat/coverage.R. HTML report →tests/coverage-report.html. -
Fixture regeneration:
docker exec bycatch_code_ci Rscript tests/src/create_test_fixtures.R. -
Fixture scripts access internal
compute_*viabycatch:::(they are never exported).
GitHub Actions: docker build → make check → make coverage → make mutants → push images to Docker Hub.
Each commit: Gitmoji + imperative verb + under-72-char summary. Blank line. Body explains motivation (why, not what). No Conventional Commits prefixes (feat:, fix:).
stylerformatting is mandatory.make checkenforces it in CI.- roxygen2 with
markdown = TRUE.R/*.Rtags are source of truth;NAMESPACEandman/are build artifacts removed bymake cleanand regenerated bydevtools::document(). They are intentionally not committed. - Bug-scope investigation: cross-reference ALL Level 2 functions against
get_domain_specific_options()— missing flags often affect more functions than reported. Usegrep('options\\[\\[', R/cli.R)to find all consumed keys. - Cache design: individual_kde.rds (KDE_surface, UDPolygons, tracks) and assessment.rds (assessment_summary, assessment_detail, KDE_surface).
- Colony is kept only in
compute_*calls totrack2KBAalgorithms (tripSplit,tripSummary).plot_*andrender_*never receive or use colony. sf_use_s2(FALSE)save/restore lives in everycompute_*andplot_*function that directly calls track2KBA (estSpaceUse, repAssess, findSite, mapSite, mapKDE). Uses save/restore pattern:s2_was_true <- sf::sf_use_s2(FALSE); on.exit(sf::sf_use_s2(s2_was_true)). Not needed increate_*/render_*— they delegate to the leaf functions. No test file should callsf_use_s2(FALSE)externally.--smoothing-method(-z) was removed in v0.10.4.create_individual_kdenow always usesscaleARS(Area-Restricted Search scale from First Passage Time analysis) viacompute_scale_parameters→track2KBA::findScale.create_processed_datawas removed in v0.10.0. Its role (composingcompute_individual_kde+compute_representative_assessment) is now embedded increate_representative_assessment.- Geometry validity contract:
compute_potential_kbaappliessf::st_make_validto the output oftrack2KBA::findSitebefore returning. This guarantees that KBA polygons written to GeoPackage bycreate_potential_kbaare valid, preventingTopologyExceptioncrashes inrender_potential_kba→track2KBA::mapSite→st_union. If the fix is needed elsewhere, it should live in acompute_*function (computation, not visualization). - CHANGELOG strategy: Document only exported (Level 2) functions (
create_*,render_*,get_domain_specific_options). Level 1 internal functions (compute_*,import_*,plot_*) are implementation details and excluded. This keeps the user-facing API changelog clean and separates public contract from internal refactoring. - Cross-repo validation: When updating CLI signatures or exported functions, audit all downstream consumers (e.g.,
../bycatch_thesis/Makefile) — verify everybycatch::call references an@exportfunction. Stale function calls can silently break dependency workflows. - Documentation sync: After renaming or changing signatures, verify all references in
DOCS.md,README.md, roxygen@paramtags, and test files. Stale docs in roxygen tags cause CLI failures ("long flag is invalid"). plot_*()delegation pattern:plot_potential_kba()andplot_individual_kde()delegate totrack2KBA::mapSite()andtrack2KBA::mapKDE()respectively (no custom ggplot2 code).plot_representative_assessment()clones track2KBA'srepAssess()plotting logic (confidence band formula: meanPred ± est_asym × sdInclude). Internal functions; changes require test fixture updates but do NOT appear in CHANGELOG (per documentation strategy).- Cache structure expectations:
render_representative_assessment()expects cache RDS with BOTH$assessment_detailAND$assessment_summary(includingest_asymfield). Old caches withoutest_asymwill cause failures. - License: AGPL-3.0-or-later.