From 4539d73f5c0133efe42f02f4680ead580f2fd9f3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 31 Oct 2026 06:34:39 +0000
Subject: [PATCH 1/3] Add testthat coverage and contributor guidance
Co-authored-by: asgr <5617132+asgr@users.noreply.github.com>
---
.github/workflows/main.yml | 2 +-
AGENTS.md | 24 ++++++++++++++++
DESCRIPTION | 3 +-
tests/testthat.R | 4 +++
tests/testthat/test-coordinate-conversions.R | 28 +++++++++++++++++++
tests/testthat/test-coordinate-matching.R | 29 ++++++++++++++++++++
tests/testthat/test-cosmology.R | 14 ++++++++++
tests/testthat/test-sky-area.R | 18 ++++++++++++
tests/testthat/test-spherical-coordinates.R | 13 +++++++++
9 files changed, 133 insertions(+), 2 deletions(-)
create mode 100644 AGENTS.md
create mode 100644 tests/testthat.R
create mode 100644 tests/testthat/test-coordinate-conversions.R
create mode 100644 tests/testthat/test-coordinate-matching.R
create mode 100644 tests/testthat/test-cosmology.R
create mode 100644 tests/testthat/test-sky-area.R
create mode 100644 tests/testthat/test-spherical-coordinates.R
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index beab5bf..af0c77d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -43,7 +43,7 @@ jobs:
- uses: r-lib/actions/setup-r-dependencies@v2
with:
- extra-packages: any::rcmdcheck
+ extra-packages: any::rcmdcheck, any::testthat
- uses: r-lib/actions/check-r-package@v2
with:
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..4f32b6b
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,24 @@
+# Working on `celestial`
+
+`celestial` is an R package of astronomical utilities. Most functionality is in
+`R/`, package metadata is in `DESCRIPTION` and `NAMESPACE`, and user-facing
+function documentation is in `man/`. The `src/` directory contains the
+Rcpp-backed implementation and generated Rcpp bindings.
+
+## Tests and checks
+
+- Add package tests under `tests/testthat/`, with descriptive
+ `test-.R` filenames. Use `test_that()` and `expect_*()` from testthat.
+- Run the focused suite with `Rscript -e 'devtools::test()'` and the package
+ check with `R CMD check .` when the required R dependencies are available.
+- Keep tests deterministic; avoid network access and uncontrolled randomness.
+- Keep `Suggests` and CI test dependencies in sync when adding test tools.
+
+## Changes
+
+- Preserve existing public function names, arguments, units, and return
+ structures unless a change explicitly calls for an API change.
+- Update the relevant Rd documentation when changing a public function.
+- Do not hand-edit `R/RcppExports.R` or `src/RcppExports.cpp`; regenerate these
+ bindings with Rcpp tooling when the C++ interface changes.
+- Keep changes focused, and add tests for changed behavior and boundary cases.
diff --git a/DESCRIPTION b/DESCRIPTION
index 09883c6..4da723c 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -11,6 +11,7 @@ Description: Contains a number of common astronomy utility functions for cosmolo
License: LGPL-3
LazyData: TRUE
Depends: R (>= 3.00), RANN, NISTunits, pracma
-Suggests: plotrix, igraph
+Suggests: plotrix, igraph, testthat (>= 3.0.0)
LinkingTo: Rcpp
NeedsCompilation: yes
+Config/testthat/edition: 3
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..520be89
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,4 @@
+library(testthat)
+library(celestial)
+
+test_check("celestial")
diff --git a/tests/testthat/test-coordinate-conversions.R b/tests/testthat/test-coordinate-conversions.R
new file mode 100644
index 0000000..66d701f
--- /dev/null
+++ b/tests/testthat/test-coordinate-conversions.R
@@ -0,0 +1,28 @@
+test_that("decimal degrees convert to formatted DMS and HMS", {
+ expect_equal(
+ deg2dms(c(-27.345, 12.5), type = "cat", sep = ":"),
+ c("-27:20:42.00", "+12:30:00.00")
+ )
+ expect_equal(
+ deg2hms(c(182.5, 0), type = "cat", sep = ":"),
+ c("12:10:00.00", "00:00:00.00")
+ )
+})
+
+test_that("DMS values and strings convert back to decimal degrees", {
+ expect_equal(
+ dms2deg(d = c(27, 12), m = c(20, 30), s = c(42, 0), sign = c(-1, 1)),
+ c(-27.345, 12.5)
+ )
+ expect_equal(
+ dms2deg(c("-27:20:42", "+12:30:00")),
+ c(-27.345, 12.5)
+ )
+ expect_equal(hms2deg(c("12:10:00", "00:00:00")), c(182.5, 0))
+})
+
+test_that("coordinate conversions reject values outside their domains", {
+ expect_error(deg2dms(91), "All deg values")
+ expect_error(dms2deg(d = 12, m = 60), "All m values")
+ expect_error(hms2deg(h = 25), "All h values")
+})
diff --git a/tests/testthat/test-coordinate-matching.R b/tests/testthat/test-coordinate-matching.R
new file mode 100644
index 0000000..14649e4
--- /dev/null
+++ b/tests/testthat/test-coordinate-matching.R
@@ -0,0 +1,29 @@
+test_that("coordinate matching returns matches and separations in requested units", {
+ reference <- rbind(c(10, 0), c(30, 0))
+ comparison <- rbind(c(10 + 1 / 3600, 0), c(80, 0))
+
+ matches <- coordmatch(reference, comparison, rad = 2, radunit = "asec")
+
+ expect_equal(matches$Nmatch, c(1, 0))
+ expect_equal(matches$bestmatch$refID, 1)
+ expect_equal(matches$bestmatch$compareID, 1)
+ expect_equal(matches$bestmatch$sep, 1, tolerance = 1e-5)
+})
+
+test_that("single-coordinate matching chooses the nearest in-radius source", {
+ matches <- coordmatchsing(
+ RAref = 0, Decref = 0,
+ coordcompare = rbind(c(1, 0), c(0, 0)),
+ rad = 2, radunit = "deg"
+ )
+
+ expect_equal(matches$ID, c(2, 1))
+ expect_equal(matches$bestmatch["compareID"], 2)
+ expect_equal(matches$bestmatch["sep"], 0, tolerance = 1e-10)
+})
+
+test_that("linked coordinate groups combine connected objects", {
+ groups <- group_links(rbind(c(10, 20), c(20, 30), c(40, 50)))
+
+ expect_equal(groups, list(c(10, 20, 30), c(40, 50)))
+})
diff --git a/tests/testthat/test-cosmology.R b/tests/testthat/test-cosmology.R
new file mode 100644
index 0000000..14539e1
--- /dev/null
+++ b/tests/testthat/test-cosmology.R
@@ -0,0 +1,14 @@
+test_that("cosmological distances have expected identities and ordering", {
+ distances <- cosdist(z = c(0, 0.5, 1), H0 = 70, OmegaM = 0.3)
+
+ expect_equal(distances$z, c(0, 0.5, 1))
+ expect_equal(distances$CoDist[1], 0)
+ expect_true(all(diff(distances$CoDist) > 0))
+ expect_equal(distances$LumDist, (1 + distances$z) * distances$CoDist)
+ expect_equal(distances$AngDist, distances$CoDist / (1 + distances$z))
+})
+
+test_that("cosmological distance functions validate redshifts", {
+ expect_error(cosdist(z = -1), "z must be > -1")
+ expect_error(cosdist(z = Inf), "z must be finite")
+})
diff --git a/tests/testthat/test-sky-area.R b/tests/testthat/test-sky-area.R
new file mode 100644
index 0000000..6c2d336
--- /dev/null
+++ b/tests/testthat/test-sky-area.R
@@ -0,0 +1,18 @@
+test_that("sky area handles full-sky and unit conversions", {
+ full_sky <- skyarea(long = c(0, 360), lat = c(-90, 90), outunit = "sr")
+
+ expect_equal(unname(full_sky["area"]), 4 * pi, tolerance = 1e-10)
+ expect_equal(unname(full_sky["areafrac"]), 1)
+
+ degrees <- skyarea(long = c(0, 10), lat = c(0, 10), outunit = "deg2")
+ steradians <- skyarea(long = c(0, 10), lat = c(0, 10), outunit = "sr")
+ expect_equal(unname(steradians["area"]),
+ unname(degrees["area"]) * (pi / 180)^2,
+ tolerance = 1e-10)
+})
+
+test_that("sky area rejects unsupported units and non-physical regions", {
+ expect_error(skyarea(inunit = "hour"), "inunit must be")
+ expect_error(skyarea(outunit = "degree"), "outunit must be")
+ expect_error(skyarea(long = c(10, 0)), "non-physical")
+})
diff --git a/tests/testthat/test-spherical-coordinates.R b/tests/testthat/test-spherical-coordinates.R
new file mode 100644
index 0000000..4c0c05c
--- /dev/null
+++ b/tests/testthat/test-spherical-coordinates.R
@@ -0,0 +1,13 @@
+test_that("spherical and Cartesian coordinates round-trip", {
+ spherical <- cbind(long = c(0, 90, 180), lat = c(0, 30, -45),
+ radius = c(1, 2, 3))
+
+ cartesian <- sph2car(spherical, deg = TRUE)
+ expect_equal(car2sph(cartesian, deg = TRUE), spherical, tolerance = 1e-10)
+})
+
+test_that("equatorial and galactic coordinates round-trip", {
+ equatorial <- cbind(RA = c(0, 123.4, 359), Dec = c(-30, 0, 70))
+
+ expect_equal(gal2eq(eq2gal(equatorial)), equatorial, tolerance = 1e-10)
+})
From ea6f2855eeb003439bde50f6178d07d187c37ed3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 31 Oct 2026 06:35:39 +0000
Subject: [PATCH 2/3] Ensure CI resolves testthat dependencies
Co-authored-by: asgr <5617132+asgr@users.noreply.github.com>
---
.github/workflows/main.yml | 1 +
AGENTS.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index af0c77d..78d2fd1 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -44,6 +44,7 @@ jobs:
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck, any::testthat
+ needs: check
- uses: r-lib/actions/check-r-package@v2
with:
diff --git a/AGENTS.md b/AGENTS.md
index 4f32b6b..da6d859 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -9,7 +9,7 @@ Rcpp-backed implementation and generated Rcpp bindings.
- Add package tests under `tests/testthat/`, with descriptive
`test-.R` filenames. Use `test_that()` and `expect_*()` from testthat.
-- Run the focused suite with `Rscript -e 'devtools::test()'` and the package
+- Run the focused suite with `Rscript -e 'testthat::test_local()'` and the package
check with `R CMD check .` when the required R dependencies are available.
- Keep tests deterministic; avoid network access and uncontrolled randomness.
- Keep `Suggests` and CI test dependencies in sync when adding test tools.
From ac93fd37e202edc6642fb796c333e55a633ffd97 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 6 Nov 2026 04:14:32 +0000
Subject: [PATCH 3/3] Ignore names in coordinate match tests
Co-authored-by: asgr <5617132+asgr@users.noreply.github.com>
---
tests/testthat/test-coordinate-matching.R | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/testthat/test-coordinate-matching.R b/tests/testthat/test-coordinate-matching.R
index 14649e4..5075e2a 100644
--- a/tests/testthat/test-coordinate-matching.R
+++ b/tests/testthat/test-coordinate-matching.R
@@ -18,8 +18,8 @@ test_that("single-coordinate matching chooses the nearest in-radius source", {
)
expect_equal(matches$ID, c(2, 1))
- expect_equal(matches$bestmatch["compareID"], 2)
- expect_equal(matches$bestmatch["sep"], 0, tolerance = 1e-10)
+ expect_equal(unname(matches$bestmatch["compareID"]), 2)
+ expect_equal(unname(matches$bestmatch["sep"]), 0, tolerance = 1e-10)
})
test_that("linked coordinate groups combine connected objects", {