Skip to content

Commit 7e866e4

Browse files
committed
Add tests to improve coverage
1 parent 8ad892b commit 7e866e4

5 files changed

Lines changed: 183 additions & 3 deletions

File tree

RcppTskit/R/RcppExports.R

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ rtsk_variant_iterator_next <- function(iterator) {
99
.Call(`_RcppTskit_rtsk_variant_iterator_next`, iterator)
1010
}
1111

12+
test_rtsk_variant_iterator_force_null_first_allele <- function(enabled) {
13+
invisible(.Call(`_RcppTskit_test_rtsk_variant_iterator_force_null_first_allele`, enabled))
14+
}
15+
16+
test_rtsk_variant_iterator_set_site_bounds <- function(iterator, next_site_id, stop_site_id) {
17+
invisible(.Call(`_RcppTskit_test_rtsk_variant_iterator_set_site_bounds`, iterator, next_site_id, stop_site_id))
18+
}
19+
20+
test_variant_site_index_range <- function(start, stop) {
21+
invisible(.Call(`_RcppTskit_test_variant_site_index_range`, start, stop))
22+
}
23+
1224
test_validate_options <- function(options, supported) {
1325
.Call(`_RcppTskit_test_validate_options`, options, supported)
1426
}

RcppTskit/src/RcppExports.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,39 @@ BEGIN_RCPP
3737
return rcpp_result_gen;
3838
END_RCPP
3939
}
40+
// test_rtsk_variant_iterator_force_null_first_allele
41+
void test_rtsk_variant_iterator_force_null_first_allele(const bool enabled);
42+
RcppExport SEXP _RcppTskit_test_rtsk_variant_iterator_force_null_first_allele(SEXP enabledSEXP) {
43+
BEGIN_RCPP
44+
Rcpp::RNGScope rcpp_rngScope_gen;
45+
Rcpp::traits::input_parameter< const bool >::type enabled(enabledSEXP);
46+
test_rtsk_variant_iterator_force_null_first_allele(enabled);
47+
return R_NilValue;
48+
END_RCPP
49+
}
50+
// test_rtsk_variant_iterator_set_site_bounds
51+
void test_rtsk_variant_iterator_set_site_bounds(const SEXP iterator, const int next_site_id, const int stop_site_id);
52+
RcppExport SEXP _RcppTskit_test_rtsk_variant_iterator_set_site_bounds(SEXP iteratorSEXP, SEXP next_site_idSEXP, SEXP stop_site_idSEXP) {
53+
BEGIN_RCPP
54+
Rcpp::RNGScope rcpp_rngScope_gen;
55+
Rcpp::traits::input_parameter< const SEXP >::type iterator(iteratorSEXP);
56+
Rcpp::traits::input_parameter< const int >::type next_site_id(next_site_idSEXP);
57+
Rcpp::traits::input_parameter< const int >::type stop_site_id(stop_site_idSEXP);
58+
test_rtsk_variant_iterator_set_site_bounds(iterator, next_site_id, stop_site_id);
59+
return R_NilValue;
60+
END_RCPP
61+
}
62+
// test_variant_site_index_range
63+
void test_variant_site_index_range(const std::string start, const std::string stop);
64+
RcppExport SEXP _RcppTskit_test_variant_site_index_range(SEXP startSEXP, SEXP stopSEXP) {
65+
BEGIN_RCPP
66+
Rcpp::RNGScope rcpp_rngScope_gen;
67+
Rcpp::traits::input_parameter< const std::string >::type start(startSEXP);
68+
Rcpp::traits::input_parameter< const std::string >::type stop(stopSEXP);
69+
test_variant_site_index_range(start, stop);
70+
return R_NilValue;
71+
END_RCPP
72+
}
4073
// test_validate_options
4174
int test_validate_options(const int options, const int supported);
4275
RcppExport SEXP _RcppTskit_test_validate_options(SEXP optionsSEXP, SEXP supportedSEXP) {
@@ -771,6 +804,9 @@ END_RCPP
771804
static const R_CallMethodDef CallEntries[] = {
772805
{"_RcppTskit_rtsk_variant_iterator_init", (DL_FUNC) &_RcppTskit_rtsk_variant_iterator_init, 6},
773806
{"_RcppTskit_rtsk_variant_iterator_next", (DL_FUNC) &_RcppTskit_rtsk_variant_iterator_next, 1},
807+
{"_RcppTskit_test_rtsk_variant_iterator_force_null_first_allele", (DL_FUNC) &_RcppTskit_test_rtsk_variant_iterator_force_null_first_allele, 1},
808+
{"_RcppTskit_test_rtsk_variant_iterator_set_site_bounds", (DL_FUNC) &_RcppTskit_test_rtsk_variant_iterator_set_site_bounds, 3},
809+
{"_RcppTskit_test_variant_site_index_range", (DL_FUNC) &_RcppTskit_test_variant_site_index_range, 2},
774810
{"_RcppTskit_test_validate_options", (DL_FUNC) &_RcppTskit_test_validate_options, 2},
775811
{"_RcppTskit_test_rtsk_wrap_tsk_size_t_as_integer64", (DL_FUNC) &_RcppTskit_test_rtsk_wrap_tsk_size_t_as_integer64, 2},
776812
{"_RcppTskit_kastore_version", (DL_FUNC) &_RcppTskit_kastore_version, 0},

RcppTskit/src/RcppTskit.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ using rtsk_variant_iterator_t =
172172
Rcpp::XPtr<rtsk_variant_iterator_state_t, Rcpp::PreserveStorage,
173173
rtsk_variant_iterator_free, true>;
174174

175+
bool g_test_force_null_first_allele = false;
176+
175177
// INTERNAL
176178
// @title Convert \code{Rcpp::Nullable} vector to empty-or-value vector
177179
// @param value nullable vector from \code{R}
@@ -205,6 +207,9 @@ std::vector<tsk_id_t> int_vector_to_tsk_id_vector(
205207
return out;
206208
}
207209

210+
void validate_variant_site_index_range(const tsk_size_t start,
211+
const tsk_size_t stop);
212+
208213
std::pair<tsk_id_t, tsk_id_t>
209214
compute_variant_iteration_bounds(rtsk_treeseq_t &ts_xptr, const double left,
210215
const double right) {
@@ -233,15 +238,19 @@ compute_variant_iteration_bounds(rtsk_treeseq_t &ts_xptr, const double left,
233238
static_cast<tsk_size_t>(std::lower_bound(begin, end, left) - begin);
234239
const tsk_size_t stop =
235240
static_cast<tsk_size_t>(std::lower_bound(begin, end, right) - begin);
241+
validate_variant_site_index_range(start, stop);
236242

243+
return std::make_pair(static_cast<tsk_id_t>(start),
244+
static_cast<tsk_id_t>(stop));
245+
}
246+
247+
void validate_variant_site_index_range(const tsk_size_t start,
248+
const tsk_size_t stop) {
237249
const tsk_size_t max_id =
238250
static_cast<tsk_size_t>(std::numeric_limits<tsk_id_t>::max());
239251
if (start > max_id || stop > max_id) {
240252
Rcpp::stop("Site index exceeds tsk_id_t range");
241253
}
242-
243-
return std::make_pair(static_cast<tsk_id_t>(start),
244-
static_cast<tsk_id_t>(stop));
245254
}
246255

247256
// INTERNAL
@@ -355,6 +364,10 @@ SEXP rtsk_variant_iterator_next(const SEXP iterator) {
355364
}
356365

357366
const tsk_variant_t &variant = iterator_xptr->variant;
367+
if (g_test_force_null_first_allele && variant.num_alleles > 0) {
368+
iterator_xptr->variant.alleles[0] = nullptr;
369+
g_test_force_null_first_allele = false;
370+
}
358371

359372
Rcpp::IntegerVector genotypes(variant.num_samples);
360373
for (tsk_size_t j = 0; j < variant.num_samples; ++j) {
@@ -377,6 +390,39 @@ SEXP rtsk_variant_iterator_next(const SEXP iterator) {
377390
Rcpp::_["has_missing_data"] = variant.has_missing_data);
378391
}
379392

393+
// TEST-ONLY
394+
// [[Rcpp::export]]
395+
void test_rtsk_variant_iterator_force_null_first_allele(const bool enabled) {
396+
g_test_force_null_first_allele = enabled;
397+
}
398+
399+
// TEST-ONLY
400+
// [[Rcpp::export]]
401+
void test_rtsk_variant_iterator_set_site_bounds(const SEXP iterator,
402+
const int next_site_id,
403+
const int stop_site_id) {
404+
rtsk_variant_iterator_t iterator_xptr(iterator);
405+
iterator_xptr->next_site_id = static_cast<tsk_id_t>(next_site_id);
406+
iterator_xptr->stop_site_id = static_cast<tsk_id_t>(stop_site_id);
407+
}
408+
409+
// TEST-ONLY
410+
// [[Rcpp::export]]
411+
void test_variant_site_index_range(const std::string start,
412+
const std::string stop) {
413+
unsigned long long start_parsed = 0;
414+
unsigned long long stop_parsed = 0;
415+
try {
416+
start_parsed = std::stoull(start);
417+
stop_parsed = std::stoull(stop);
418+
} catch (const std::exception &) {
419+
Rcpp::stop("start and stop must be valid base-10 unsigned integer strings");
420+
}
421+
const tsk_size_t start_size = static_cast<tsk_size_t>(start_parsed);
422+
const tsk_size_t stop_size = static_cast<tsk_size_t>(stop_parsed);
423+
validate_variant_site_index_range(start_size, stop_size);
424+
}
425+
380426
// TEST-ONLY
381427
// @title Test helper for validating tskit flags
382428
// @param options that will be validated

RcppTskit/tests/testthat/test_TreeSequence.R

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,17 @@ test_that("TreeSequence$variants() validates compatibility args", {
9292
ts_file <- system.file("examples/test.trees", package = "RcppTskit")
9393
ts <- ts_load(ts_file)
9494

95+
expect_error(ts$variants(copy = NA), "copy must be TRUE/FALSE")
96+
expect_error(ts$variants(copy = "yes"), "copy must be TRUE/FALSE")
9597
expect_error(ts$variants(copy = FALSE), "copy = FALSE is not supported yet")
98+
expect_error(
99+
ts$variants(impute_missing_data = NA),
100+
"impute_missing_data must be TRUE/FALSE or NULL"
101+
)
102+
expect_error(
103+
ts$variants(impute_missing_data = "yes"),
104+
"impute_missing_data must be TRUE/FALSE or NULL"
105+
)
96106
expect_warning(
97107
ts$variants(impute_missing_data = TRUE),
98108
"impute_missing_data is deprecated"

RcppTskit/tests/testthat/test_variant_iterator_low_level.R

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,79 @@ test_that("low-level variant iterator supports sample subsets", {
5151
expect_false(is.null(v))
5252
expect_length(v$genotypes, length(samples))
5353
})
54+
55+
test_that("low-level variant iterator validates bounds", {
56+
ts_file <- system.file("examples/test.trees", package = "RcppTskit")
57+
ts_xptr <- rtsk_treeseq_load(ts_file)
58+
seq_len <- rtsk_treeseq_get_sequence_length(ts_xptr)
59+
60+
expect_error(
61+
rtsk_variant_iterator_init(ts_xptr, left = NaN, right = seq_len),
62+
"left and right must be finite numbers"
63+
)
64+
expect_error(
65+
rtsk_variant_iterator_init(ts_xptr, left = -1, right = seq_len),
66+
"left and right must be >= 0"
67+
)
68+
expect_error(
69+
rtsk_variant_iterator_init(ts_xptr, left = seq_len + 1, right = seq_len),
70+
"left and right must be <= sequence length"
71+
)
72+
expect_error(
73+
rtsk_variant_iterator_init(ts_xptr, left = 1, right = 0.5),
74+
"left must be <= right"
75+
)
76+
})
77+
78+
test_that("low-level variant iterator validates site-index range helper", {
79+
expect_error(
80+
test_variant_site_index_range("2147483648", "0"),
81+
"Site index exceeds tsk_id_t range"
82+
)
83+
})
84+
85+
test_that("low-level variant iterator validates samples and alleles inputs", {
86+
ts_file <- system.file("examples/test.trees", package = "RcppTskit")
87+
ts_xptr <- rtsk_treeseq_load(ts_file)
88+
n_nodes <- as.integer(rtsk_treeseq_get_num_nodes(ts_xptr))
89+
90+
expect_error(
91+
rtsk_variant_iterator_init(ts_xptr, samples = c(n_nodes + 1L)),
92+
"Node out of bounds"
93+
)
94+
expect_error(
95+
rtsk_variant_iterator_init(ts_xptr, alleles = c("A", NA_character_)),
96+
"alleles cannot contain NA"
97+
)
98+
99+
it <- rtsk_variant_iterator_init(ts_xptr, alleles = c("A", "C", "G", "T"))
100+
v <- rtsk_variant_iterator_next(it)
101+
expect_false(is.null(v))
102+
})
103+
104+
test_that("low-level variant iterator decode error path can be triggered", {
105+
ts_file <- system.file("examples/test.trees", package = "RcppTskit")
106+
ts_xptr <- rtsk_treeseq_load(ts_file)
107+
n_sites <- as.integer(rtsk_treeseq_get_num_sites(ts_xptr))
108+
109+
it <- rtsk_variant_iterator_init(ts_xptr)
110+
test_rtsk_variant_iterator_set_site_bounds(
111+
it,
112+
next_site_id = n_sites,
113+
stop_site_id = n_sites + 1L
114+
)
115+
expect_error(
116+
rtsk_variant_iterator_next(it),
117+
regexp = "Site out of bounds|Bounds check"
118+
)
119+
})
120+
121+
test_that("low-level variant iterator maps null alleles to NA_character_", {
122+
ts_file <- system.file("examples/test.trees", package = "RcppTskit")
123+
ts_xptr <- rtsk_treeseq_load(ts_file)
124+
it <- rtsk_variant_iterator_init(ts_xptr)
125+
test_rtsk_variant_iterator_force_null_first_allele(TRUE)
126+
v <- rtsk_variant_iterator_next(it)
127+
expect_false(is.null(v))
128+
expect_true(anyNA(v$alleles))
129+
})

0 commit comments

Comments
 (0)