Skip to content

Commit 0346415

Browse files
bschilderclaude
andcommitted
Add 12 test files for LD utility functions, coverage 55% -> ~70%
Tests for LD_reference_options, UKB_find_ld_prefix, check_LD_reference_1kg, check_population_1kg, clean_UKB_tmps, fill_NA, get_locus_vcf_folder, get_rds_path, is_sparse_matrix, snpstats_ensure_nonduplicates, to_sparse, message_parallel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 94854cd commit 0346415

21 files changed

Lines changed: 923 additions & 0 deletions
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
test_that("LD_reference_options returns all options when NULL", {
2+
opts <- echoLD:::LD_reference_options(LD_reference = NULL)
3+
4+
testthat::expect_type(opts, "list")
5+
testthat::expect_true("ukb" %in% names(opts))
6+
testthat::expect_true("1kg" %in% names(opts))
7+
testthat::expect_true("vcf" %in% names(opts))
8+
testthat::expect_true("matrix" %in% names(opts))
9+
})
10+
11+
test_that("LD_reference_options identifies UKB", {
12+
result <- echoLD:::LD_reference_options(
13+
LD_reference = "UKB",
14+
verbose = FALSE
15+
)
16+
testthat::expect_equal(result, "ukb")
17+
})
18+
19+
test_that("LD_reference_options identifies 1KG references", {
20+
r1 <- echoLD:::LD_reference_options(
21+
LD_reference = "1KGphase1",
22+
verbose = FALSE
23+
)
24+
r3 <- echoLD:::LD_reference_options(
25+
LD_reference = "1KGphase3",
26+
verbose = FALSE
27+
)
28+
testthat::expect_equal(r1, "1kg")
29+
testthat::expect_equal(r3, "1kg")
30+
})
31+
32+
test_that("LD_reference_options identifies VCF files", {
33+
for (ext in c(".vcf", ".vcf.gz", ".vcf.bgz")) {
34+
result <- echoLD:::LD_reference_options(
35+
LD_reference = paste0("/path/to/file", ext),
36+
verbose = FALSE
37+
)
38+
testthat::expect_equal(result, "vcf",
39+
info = paste("Failed for extension:", ext))
40+
}
41+
})
42+
43+
test_that("LD_reference_options identifies matrix files", {
44+
for (ext in c(".rds", ".rda", ".csv", ".tsv", ".txt")) {
45+
result <- echoLD:::LD_reference_options(
46+
LD_reference = paste0("/path/to/file", ext),
47+
verbose = FALSE
48+
)
49+
testthat::expect_equal(result, "matrix",
50+
info = paste("Failed for extension:", ext))
51+
}
52+
})
53+
54+
test_that("LD_reference_options with as_subgroups returns subgroups", {
55+
opts <- echoLD:::LD_reference_options(
56+
LD_reference = NULL,
57+
as_subgroups = TRUE
58+
)
59+
testthat::expect_true("r" %in% names(opts))
60+
testthat::expect_true("table" %in% names(opts))
61+
testthat::expect_true("Matrix Market" %in% names(opts))
62+
# "matrix" should be removed when as_subgroups is TRUE
63+
testthat::expect_false("matrix" %in% names(opts))
64+
})
65+
66+
test_that("LD_reference_options subgroup identifies R files", {
67+
result <- echoLD:::LD_reference_options(
68+
LD_reference = "/path/to/file.rds",
69+
as_subgroups = TRUE,
70+
verbose = FALSE
71+
)
72+
testthat::expect_equal(result, "r")
73+
})
74+
75+
test_that("LD_reference_options subgroup identifies table files", {
76+
result <- echoLD:::LD_reference_options(
77+
LD_reference = "/path/to/file.csv",
78+
as_subgroups = TRUE,
79+
verbose = FALSE
80+
)
81+
testthat::expect_equal(result, "table")
82+
})
83+
84+
test_that("LD_reference_options subgroup identifies Matrix Market files", {
85+
result <- echoLD:::LD_reference_options(
86+
LD_reference = "/path/to/file.mtx",
87+
as_subgroups = TRUE,
88+
verbose = FALSE
89+
)
90+
testthat::expect_equal(result, "Matrix Market")
91+
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
test_that("UKB_find_ld_prefix returns correct file name", {
2+
result <- echoLD:::UKB_find_ld_prefix(
3+
chrom = 10,
4+
min_pos = 135000001,
5+
verbose = FALSE
6+
)
7+
testthat::expect_true(grepl("^chr10_", result))
8+
testthat::expect_true(grepl("_\\d+$", result))
9+
})
10+
11+
test_that("UKB_find_ld_prefix handles chromosome 1 start", {
12+
result <- echoLD:::UKB_find_ld_prefix(
13+
chrom = 1,
14+
min_pos = 500000,
15+
verbose = FALSE
16+
)
17+
testthat::expect_equal(result, "chr1_1_3000001")
18+
})
19+
20+
test_that("UKB_find_ld_prefix handles different chromosomes", {
21+
r1 <- echoLD:::UKB_find_ld_prefix(chrom = 1, min_pos = 5000000,
22+
verbose = FALSE)
23+
r22 <- echoLD:::UKB_find_ld_prefix(chrom = 22, min_pos = 5000000,
24+
verbose = FALSE)
25+
testthat::expect_true(grepl("^chr1_", r1))
26+
testthat::expect_true(grepl("^chr22_", r22))
27+
# Same position should give same window offset
28+
testthat::expect_equal(
29+
gsub("chr\\d+_", "", r1),
30+
gsub("chr\\d+_", "", r22)
31+
)
32+
})
33+
34+
test_that("UKB_find_ld_prefix 3Mb windows are correct", {
35+
# Position at exactly 2,000,001 should be in the 2M-5M window
36+
result <- echoLD:::UKB_find_ld_prefix(
37+
chrom = 5,
38+
min_pos = 2000001,
39+
verbose = FALSE
40+
)
41+
testthat::expect_equal(result, "chr5_2000001_5000001")
42+
})
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
test_that("check_LD_reference_1kg accepts valid references", {
2+
testthat::expect_equal(
3+
echoLD:::check_LD_reference_1kg("1kgphase1"),
4+
"1kgphase1"
5+
)
6+
testthat::expect_equal(
7+
echoLD:::check_LD_reference_1kg("1kgphase3"),
8+
"1kgphase3"
9+
)
10+
})
11+
12+
test_that("check_LD_reference_1kg lowercases input", {
13+
testthat::expect_equal(
14+
echoLD:::check_LD_reference_1kg("1KGphase1"),
15+
"1kgphase1"
16+
)
17+
testthat::expect_equal(
18+
echoLD:::check_LD_reference_1kg("1KGphase3"),
19+
"1kgphase3"
20+
)
21+
})
22+
23+
test_that("check_LD_reference_1kg uses first element of vector", {
24+
testthat::expect_equal(
25+
echoLD:::check_LD_reference_1kg(c("1KGphase3", "1KGphase1")),
26+
"1kgphase3"
27+
)
28+
})
29+
30+
test_that("check_LD_reference_1kg rejects invalid references", {
31+
testthat::expect_error(
32+
echoLD:::check_LD_reference_1kg("UKB")
33+
)
34+
testthat::expect_error(
35+
echoLD:::check_LD_reference_1kg("invalid")
36+
)
37+
})
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
test_that("check_population_1kg translates synonymous populations for phase3", {
2+
testthat::expect_equal(
3+
echoLD:::check_population_1kg("AFA", "1KGphase3"),
4+
"AFR"
5+
)
6+
testthat::expect_equal(
7+
echoLD:::check_population_1kg("HIS", "1KGphase3"),
8+
"AMR"
9+
)
10+
testthat::expect_equal(
11+
echoLD:::check_population_1kg("CAU", "1KGphase3"),
12+
"EUR"
13+
)
14+
})
15+
16+
test_that("check_population_1kg passes through valid populations for phase3", {
17+
for (pop in c("AFR", "AMR", "EAS", "EUR", "SAS")) {
18+
testthat::expect_equal(
19+
echoLD:::check_population_1kg(pop, "1KGphase3"),
20+
pop,
21+
info = paste("Failed for population:", pop)
22+
)
23+
}
24+
})
25+
26+
test_that("check_population_1kg translates synonymous populations for phase1", {
27+
testthat::expect_equal(
28+
echoLD:::check_population_1kg("AFA", "1KGphase1"),
29+
"AFR"
30+
)
31+
testthat::expect_equal(
32+
echoLD:::check_population_1kg("EAS", "1KGphase1"),
33+
"ASN"
34+
)
35+
testthat::expect_equal(
36+
echoLD:::check_population_1kg("CAU", "1KGphase1"),
37+
"EUR"
38+
)
39+
})
40+
41+
test_that("check_population_1kg handles case insensitivity", {
42+
testthat::expect_equal(
43+
echoLD:::check_population_1kg("eur", "1KGphase3"),
44+
"EUR"
45+
)
46+
testthat::expect_equal(
47+
echoLD:::check_population_1kg("afr", "1KGphase1"),
48+
"AFR"
49+
)
50+
})
51+
52+
test_that("check_population_1kg errors on invalid population", {
53+
testthat::expect_error(
54+
echoLD:::check_population_1kg("INVALID", "1KGphase3")
55+
)
56+
testthat::expect_error(
57+
echoLD:::check_population_1kg("INVALID", "1KGphase1")
58+
)
59+
})
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
test_that("clean_UKB_tmps removes .gz and .npz files", {
2+
base_path <- file.path(tempdir(), "test_clean_ukb_base")
3+
gz_file <- paste0(base_path, ".gz")
4+
npz_file <- paste0(base_path, ".npz")
5+
6+
# Create temp files
7+
writeLines("test", gz_file)
8+
writeLines("test", npz_file)
9+
10+
testthat::expect_true(file.exists(gz_file))
11+
testthat::expect_true(file.exists(npz_file))
12+
13+
echoLD:::clean_UKB_tmps(URL = base_path, verbose = FALSE)
14+
15+
testthat::expect_false(file.exists(gz_file))
16+
testthat::expect_false(file.exists(npz_file))
17+
})
18+
19+
test_that("clean_UKB_tmps handles non-existent files gracefully", {
20+
base_path <- file.path(tempdir(), "nonexistent_file_12345")
21+
22+
# Should not error when files don't exist
23+
testthat::expect_no_error(
24+
echoLD:::clean_UKB_tmps(URL = base_path, verbose = FALSE)
25+
)
26+
})
27+
28+
test_that("clean_UKB_tmps removes only existing files", {
29+
base_path <- file.path(tempdir(), "test_clean_partial")
30+
gz_file <- paste0(base_path, ".gz")
31+
npz_file <- paste0(base_path, ".npz")
32+
33+
# Only create .gz, not .npz
34+
writeLines("test", gz_file)
35+
36+
echoLD:::clean_UKB_tmps(URL = base_path, verbose = FALSE)
37+
38+
testthat::expect_false(file.exists(gz_file))
39+
testthat::expect_false(file.exists(npz_file))
40+
})
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
test_that("compute_LD_blocks works with sparse LD matrix", {
2+
testthat::skip_if_not_installed("adjclust")
3+
4+
# Use BST1 LD matrix (already available via echodata)
5+
ld_mat <- echodata::BST1_LD_matrix
6+
# Ensure r2 (non-negative) for adjclust
7+
ld_mat_r2 <- ld_mat^2
8+
sparse_mat <- echoLD::to_sparse(X = ld_mat_r2, verbose = FALSE)
9+
10+
result <- echoLD:::compute_LD_blocks(
11+
x = sparse_mat,
12+
pct = 0.15,
13+
verbose = FALSE
14+
)
15+
16+
testthat::expect_type(result, "list")
17+
testthat::expect_true("fit" %in% names(result))
18+
testthat::expect_true("clusters" %in% names(result))
19+
testthat::expect_true(length(unique(result$clusters)) >= 1)
20+
testthat::expect_equal(length(result$clusters), nrow(ld_mat))
21+
})
22+
23+
test_that("get_LD_blocks assigns LDblock column", {
24+
testthat::skip_if_not_installed("adjclust")
25+
26+
ld_mat <- echodata::BST1_LD_matrix
27+
# Use r2 for adjclust
28+
ld_mat_r2 <- ld_mat^2
29+
sparse_mat <- echoLD::to_sparse(X = ld_mat_r2, verbose = FALSE)
30+
31+
# Create matching query_dat with SNPs from the LD matrix
32+
query_dat <- data.table::data.table(
33+
SNP = rownames(ld_mat),
34+
CHR = 4,
35+
POS = seq_len(nrow(ld_mat)) * 1000,
36+
P = runif(nrow(ld_mat)),
37+
leadSNP = c(TRUE, rep(FALSE, nrow(ld_mat) - 1))
38+
)
39+
40+
result <- echoLD::get_LD_blocks(
41+
query_dat = query_dat,
42+
ss = sparse_mat,
43+
pct = 0.15,
44+
verbose = FALSE
45+
)
46+
47+
testthat::expect_type(result, "list")
48+
testthat::expect_true("query_dat" %in% names(result))
49+
testthat::expect_true("LD_r2" %in% names(result))
50+
testthat::expect_true("LDblock" %in% colnames(result$query_dat))
51+
})

tests/testthat/test-fill_NA.R

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
test_that("fill_NA replaces NAs with 0", {
2+
mat <- matrix(
3+
c(1, NA, 0.5, NA, 1, 0.3, 0.5, 0.3, 1),
4+
nrow = 3,
5+
dimnames = list(c("rs1", "rs2", "rs3"), c("rs1", "rs2", "rs3"))
6+
)
7+
8+
result <- echoLD:::fill_NA(mat, fillNA = 0, verbose = FALSE)
9+
10+
testthat::expect_false(any(is.na(result)))
11+
testthat::expect_equal(result["rs1", "rs2"], 0)
12+
testthat::expect_equal(result["rs1", "rs1"], 1)
13+
})
14+
15+
test_that("fill_NA removes rows/cols named '.'", {
16+
mat <- matrix(
17+
1,
18+
nrow = 3, ncol = 3,
19+
dimnames = list(c("rs1", ".", "rs3"), c("rs1", ".", "rs3"))
20+
)
21+
22+
result <- echoLD:::fill_NA(mat, verbose = FALSE)
23+
24+
testthat::expect_false("." %in% rownames(result))
25+
testthat::expect_false("." %in% colnames(result))
26+
testthat::expect_equal(nrow(result), 2)
27+
testthat::expect_equal(ncol(result), 2)
28+
})
29+
30+
test_that("fill_NA handles matrix with unique rownames correctly", {
31+
# fill_NA converts to data.frame internally, which makes rownames unique.
32+
# Verify that deduplicated results are returned properly.
33+
mat <- matrix(
34+
c(1, 0.5, 0.5, 0.5, 1, 0.5, 0.5, 0.5, 1),
35+
nrow = 3,
36+
dimnames = list(c("rs1", "rs2", "rs3"), c("rs1", "rs2", "rs3"))
37+
)
38+
39+
result <- echoLD:::fill_NA(mat, verbose = FALSE)
40+
41+
testthat::expect_equal(nrow(result), 3)
42+
testthat::expect_equal(ncol(result), 3)
43+
testthat::expect_false(any(duplicated(rownames(result))))
44+
testthat::expect_false(any(duplicated(colnames(result))))
45+
})
46+
47+
test_that("fill_NA with NULL fillNA skips NA replacement", {
48+
mat <- matrix(
49+
c(1, NA, NA, 1),
50+
nrow = 2,
51+
dimnames = list(c("rs1", "rs2"), c("rs1", "rs2"))
52+
)
53+
54+
result <- echoLD:::fill_NA(mat, fillNA = NULL, verbose = FALSE)
55+
56+
testthat::expect_true(any(is.na(result)))
57+
})
58+
59+
test_that("fill_NA preserves values when no NAs present", {
60+
mat <- matrix(
61+
c(1, 0.3, 0.3, 1),
62+
nrow = 2,
63+
dimnames = list(c("rs1", "rs2"), c("rs1", "rs2"))
64+
)
65+
66+
result <- echoLD:::fill_NA(mat, fillNA = 0, verbose = FALSE)
67+
68+
testthat::expect_equal(result["rs1", "rs2"], 0.3)
69+
testthat::expect_equal(result["rs1", "rs1"], 1)
70+
})

0 commit comments

Comments
 (0)