## These are tests that are not part of the upstream ringbuf library
## but are added for the R version. I'll switch the organisation
## around later, perhaps.
context("ring_buffer_bytes (r)")
test_that("tail offset", {
bytes <- as.raw(0:255)
buf <- ring_buffer_bytes(length(bytes))
expect_equal(buf$push(bytes), length(bytes))
expect_true(buf$is_full())
n <- 20
cmp <- as.raw(seq_len(n) - 1L)
expect_equal(buf$read(n), cmp)
tmp <- vapply(seq_len(n) - 1L,
function(x) buf$tail_offset(x), raw(1))
expect_equal(tmp, cmp)
expect_equal(buf$take(n), cmp)
cmp <- as.raw(as.integer(cmp) + n)
expect_equal(buf$read(n), cmp)
tmp <- vapply(seq_len(n) - 1L, buf$tail_offset, raw(1))
expect_equal(tmp, cmp)
expect_error(buf$tail_offset(300), "Buffer underflow")
expect_equal(buf$tail_offset(255 - n), as.raw(255))
expect_error(buf$tail_offset(255 - n + 1),
"Buffer underflow")
## Add a bunch more bytes in so that we wrap the tail:
cmp <- as.raw(rev(seq_len(n) - 1L))
buf$push(cmp)
expect_true(buf$is_full())
})
## This one duplicates the simple checks used in the environment based
## ring buffer:
test_that("head offset (1)", {
n <- 10
buf <- ring_buffer_bytes(n)
m <- 4
data <- as.raw(1:m)
buf$push(data)
expect_equal(buf$head_offset(0), data[m])
expect_equal(buf$head_offset(1), data[m - 1])
expect_equal(buf$head_offset(m - 1), data[1])
expect_error(buf$head_offset(m), "Buffer underflow")
})
## This one is more involved.
test_that("head offset", {
bytes <- as.raw(0:255)
buf <- ring_buffer_bytes(length(bytes))
expect_equal(buf$push(bytes), length(bytes))
expect_true(buf$is_full())
n <- 20
cmp <- rev(bytes)[seq_len(n)]
## expect_equal(buf$read_head(n), cmp)
tmp <- vapply(seq_len(n) - 1L,
function(x) buf$head_offset(x), raw(1))
expect_equal(tmp, cmp)
buf$take(n)
expect_error(buf$head_offset(300), "Buffer underflow")
expect_equal(buf$head_offset(255 - n), bytes[n + 1])
expect_error(buf$head_offset(255 - n + 1),
"Buffer underflow")
## Add a bunch more bytes in so that we wrap the tail:
cmp <- as.raw(rev(seq_len(n) - 1L))
buf$push(cmp)
expect_true(buf$is_full())
tmp1 <- vapply(seq_len(256) - 1L,
function(x) buf$head_offset(x), raw(1))
tmp2 <- vapply(seq_len(256) - 1L,
function(x) buf$tail_offset(x), raw(1))
expect_equal(tmp1, rev(tmp2))
})
test_that("impossible sizes", {
expect_error(ring_buffer_bytes(0),
"Can't create ring buffer with size 0")
expect_error(ring_buffer_bytes(10, 0),
"Can't create ring buffer with stride 0")
})
test_that("input validation", {
expect_error(ring_buffer_bytes(pi)$size(),
"Expected an integer value")
expect_error(ring_buffer_bytes(-1),
"Expected a nonnegative value")
expect_error(ring_buffer_bytes(-1L),
"Expected a nonnegative value")
expect_error(ring_buffer_bytes(Inf),
"Expected a nonnegative value")
expect_error(ring_buffer_bytes(NA_real_),
"Expected a nonnegative value")
expect_error(ring_buffer_bytes(NA_integer_),
"Expected a nonnegative value")
})
## unusual direction:
test_that("take_head", {
bytes <- as.raw(0:255)
n <- length(bytes)
buf <- ring_buffer_bytes(n)
expect_equal(buf$push(bytes), n)
expect_true(buf$is_full())
expect_equal(buf$read_head(0), raw(0))
expect_equal(buf$read_head(1), tail(bytes, 1))
expect_equal(buf$read_head(2), rev(tail(bytes, 2)))
expect_equal(buf$read_head(n), rev(bytes))
expect_error(buf$read_head(n + 1L), "Buffer underflow")
m <- 15
buf$take(m)
b2 <- sample(bytes, m)
buf$push(b2)
expect_equal(buf$read_head(0), raw(0))
expect_equal(buf$read_head(1), tail(b2, 1))
expect_equal(buf$read_head(m), rev(b2))
cmp <- c(rev(b2), rev(bytes[-seq_len(m)]))
expect_equal(buf$read_head(n), cmp)
expect_equal(buf$take_head(0), raw(0))
expect_equal(buf$read_head(n), cmp)
expect_equal(buf$take_head(1), cmp[1])
expect_equal(buf$read_head(n - 1), cmp[-1])
expect_equal(buf$take_head(m - 1), cmp[2:m])
expect_equal(buf$read_head(n - m), cmp[-seq_len(m)])
expect_error(buf$take_head(buf$used() + 1),
"Buffer underflow")
})
test_that("head() behaviour", {
b <- ring_buffer_bytes(10)
expect_error(b$head(), "empty")
expect_error(b$tail(), "empty")
b$push(as.raw(1:4))
expect_equal(b$tail(), as.raw(1))
expect_equal(b$tail_offset(0), as.raw(1))
## In contrast with the C API this returns the *most recently added
## element*, not the memory that will be written to next.
expect_equal(b$head(), as.raw(4))
expect_equal(b$head_offset(0), as.raw(4))
})
test_that("duplicate", {
n <- 10
buf <- ring_buffer_bytes(10)
buf$push(as.raw(1:12))
buf$take(3)
expect_equal(buf$head_pos(), 1) # NOTE: different to env!
expect_equal(buf$tail_pos(), 5)
expect_equal(buf$used(), 7)
expect_equal(buf$size(), n)
expect_equal(buf$read(buf$used()), as.raw(6:12))
cpy <- buf$duplicate()
## Source unchanged:
for (x in list(buf, cpy)) {
expect_equal(x$head_pos(), 1)
expect_equal(x$tail_pos(), 5)
expect_equal(x$used(), 7)
expect_equal(x$size(), n)
expect_equal(x$read(x$used()), as.raw(6:12))
}
## But we can move the two buffers independently.
expect_equal(cpy$take(2), as.raw(6:7))
cpy$push(as.raw(13))
expect_equal(buf$head_pos(), 1)
expect_equal(buf$tail_pos(), 5)
expect_equal(buf$used(), 7)
expect_equal(buf$size(), n)
expect_equal(buf$read(buf$used()), as.raw(6:12))
expect_equal(cpy$head_pos(), 2)
expect_equal(cpy$tail_pos(), 7)
expect_equal(cpy$used(), 6)
expect_equal(cpy$size(), n)
expect_equal(cpy$read(cpy$used()), as.raw(8:13))
})
test_that("head_advance", {
n <- 5
s <- 16
b <- ring_buffer_bytes(n, s)
## Some bytes to stuff into the buffer; enough to wrap the buffer
tmp <- lapply(seq_len(n + 2), function(...) random_bytes(s))
for (i in seq_along(tmp)) {
ok <- test_advance_head(b, tmp[[i]])
expect_true(ok)
expect_identical(b$read_head(1), tmp[[i]])
j <- max(1, i - b$used() + 1):i
expect_identical(b$read(b$used()), unlist(tmp[j]))
}
})
## Overflow functions are added to:
##
## * set
## * set_stride
## * push
## * copy
## * head_advance
## Of the different overflow actions, error is the easiest:
test_that("overflow error; set", {
n <- 10
s <- 6
b <- ring_buffer_bytes(n, s, "error")
pat <- random_bytes(1)
expect_error(b$set(pat, n + 1),
"Buffer overflow (adding 11 elements, but 10 available)",
fixed = TRUE)
expect_true(b$is_empty())
b$set(pat, n)
expect_true(b$is_full())
expect_error(b$set(pat, 1),
"Buffer overflow (adding 1 elements, but 0 available)",
fixed = TRUE)
expect_equal(b$take(1), rep(pat, s))
})
test_that("overflow error; set_stride", {
n <- 10
s <- 6
b <- ring_buffer_bytes(n, s, "error")
pat <- random_bytes(s)
expect_error(b$set(pat, n + 1),
"Buffer overflow (adding 11 elements, but 10 available)",
fixed = TRUE)
expect_true(b$is_empty())
b$set(pat, n)
expect_true(b$is_full())
expect_error(b$set(pat, 1),
"Buffer overflow (adding 1 elements, but 0 available)",
fixed = TRUE)
expect_equal(b$take(1), pat)
})
test_that("overflow error; push", {
n <- 10
s <- 6
b <- ring_buffer_bytes(n, s, "error")
expect_error(b$push(random_bytes((n + 1) * s)),
"Buffer overflow (adding 11 elements, but 10 available)",
fixed = TRUE)
expect_true(b$is_empty())
})
test_that("overflow error; copy", {
n <- 10
s <- 6
b1 <- ring_buffer_bytes(n + 1, s)
b2 <- ring_buffer_bytes(n, s, "error")
b1$push(random_bytes((n + 1) * s))
expect_error(b1$copy(b2, n + 1),
"Buffer overflow (adding 11 elements, but 10 available)",
fixed = TRUE)
expect_true(b2$is_empty())
expect_true(b1$is_full())
})
test_that("incompatible stride on copy", {
n <- 10
s <- 6
b1 <- ring_buffer_bytes(n, s)
b1$push(random_bytes((n + 1) * s))
b2 <- ring_buffer_bytes(n, s + 1)
expect_error(b1$copy(b2, 1),
"Can't copy as buffers differ in their stride (6 vs 7)",
fixed = TRUE)
})
test_that("can't self copy", {
n <- 10
s <- 6
b1 <- ring_buffer_bytes(n, s)
expect_error(b1$copy(b1, 1),
"Can't copy a buffer into itself",
fixed = TRUE)
})
test_that("grow - exact", {
## First, try manually growing a buffer under various states. This
## has slightly fewer moving parts than doing this reactively based
## on overflow.
##
## There are three scenarios to try here: empty, partially full and
## totally full.
n <- 10
s <- 6
e <- 3
## (1) empty
buf <- ring_buffer_bytes(n, s)
expect_null(buf$grow(e, TRUE))
expect_equal(buf$size(), n + e)
expect_equal(buf$used(), 0)
b <- random_bytes(buf$size() * s)
buf$push(b)
expect_equal(buf$read(buf$size()), b)
## (2) partially full
buf <- ring_buffer_bytes(n, s)
b1 <- random_bytes(e * s)
buf$push(b1)
expect_equal(buf$data(), pad(b1, (n + 1) * s))
buf$grow(e, TRUE)
expect_equal(buf$size(), n + e)
expect_equal(buf$used(), e)
expect_equal(buf$data(), pad(b1, (n + e + 1) * s))
b2 <- random_bytes(n * s)
buf$push(b2)
expect_equal(buf$read(n + e), c(b1, b2))
## (e) completely full
buf <- ring_buffer_bytes(n, s)
b1 <- random_bytes(n * s)
buf$push(b1)
buf$grow(e, TRUE)
expect_equal(buf$size(), n + e)
expect_equal(buf$used(), n)
b2 <- random_bytes(e * s)
buf$push(b2)
expect_equal(buf$read(n + 3), c(b1, b2))
})
test_that("zero growth", {
n <- 10
s <- 6
buf <- ring_buffer_bytes(n, s)
b <- random_bytes(n * s)
buf$push(b)
for (exact in c(TRUE, FALSE)) {
buf$grow(0, exact)
expect_equal(buf$size(), n)
expect_equal(buf$read(n), b)
expect_equal(buf$head_pos(), n)
expect_equal(buf$tail_pos(), 0)
}
})
## Then growth, for which we also have to test where we get to, which
## is a bit more of a faff. However, if the error is thrown correctly
## for all of the above tests, we should be able to get away with just
## running this for one and checking carefully it does the right thing.
test_that("overflow grow; set", {
PHI <- 1.6180339887
n <- 10
s <- 6
b <- ring_buffer_bytes(n, s, "grow")
pat <- random_bytes(1)
newlen <- ceiling(n * PHI)
expect_equal(b$size(), n)
expect_equal(b$set(pat, n + 1), n + 1)
expect_equal(b$size(), newlen)
expect_equal(b$size(TRUE), newlen * s)
expect_equal(b$bytes_data(), (newlen + 1) * s)
expect_equal(b$head_pos(), n + 1)
expect_equal(b$tail_pos(), 0)
expect_equal(b$used(), n + 1)
expect_equal(b$free(), newlen - (n + 1))
expect_equal(b$read(n + 1), rep(pat, (n + 1) * s))
b$set(pat, 1)
expect_equal(b$size(), newlen)
## Now, try and grow this a *lot* more and make sure that we
## increase size the right number of times.
m <- 30 * n
ceiling(newlen * PHI * ceiling(log((m + n + 2) / newlen, PHI)))
newlen2 <- ceiling(newlen * PHI ^ ceiling(log((m + n + 2) / newlen, PHI)))
b$set(pat, 30 * n)
expect_equal(b$size(), newlen2)
expect_equal(b$used(), m + n + 2)
pat2 <- random_bytes(1)
b2 <- ring_buffer_bytes(n, s, "grow")
b2$set(pat, n)
expect_equal(b2$take(n - 2), rep(pat, (n - 2) * s))
b2$set(pat2, n - 2)
expect_equal(b2$size(), n)
expect_true(b2$is_full())
})
test_that("invalid overflow option", {
expect_error(ring_buffer_bytes(10, 10, "g"),
"Invalid value for 'on_overflow'")
expect_error(ring_buffer_bytes(10, 10, NA),
"Invalid value for 'on_overflow'")
expect_error(ring_buffer_bytes(10, 10, "magic"),
"Invalid value for 'on_overflow'")
expect_error(ring_buffer_bytes(10, 10, 1),
"on_overflow must be a character")
expect_error(ring_buffer_bytes(10, 10, character(0)),
"on_overflow must be a scalar")
})
test_that("grow geometrically by zero", {
b <- ring_buffer_bytes(10, 6)
b$grow(5)
expect_equal(b$size(), 10)
b$grow(10)
expect_equal(b$size(), 10)
})
test_that("mirror", {
size <- 128
stride <- 8
rb1 <- ring_buffer_bytes(size, stride)
rb2 <- ring_buffer_bytes(size, stride)
bb1 <- random_bytes(floor(size / 2) * stride)
bb2 <- random_bytes(floor(size / 3) * stride)
rb1$push(bb1)
rb2$push(bb2)
expect_error(rb1$mirror(rb1),
"Can't mirror a buffer into itself")
expect_equal(rb1$read(rb1$used()), bb1)
expect_equal(rb2$read(rb2$used()), bb2)
rb1$mirror(rb2)
expect_equal(rb1$read(rb1$used()), bb1)
expect_equal(rb1$read(rb1$used()), bb1)
expect_equal(rb1$head_pos(), rb2$head_pos())
expect_equal(rb1$tail_pos(), rb2$tail_pos())
expect_equal(rb1$used(), rb2$used())
## Now rotate the buffer and try again:
rb2$push(random_bytes((size + 5) * stride))
rb2$take(4)
bb3 <- rb2$read(rb2$used())
rb2$mirror(rb1)
expect_equal(rb1$read(rb1$used()), bb3)
expect_equal(rb2$read(rb2$used()), bb3)
expect_equal(rb1$head_pos(), rb2$head_pos())
expect_equal(rb1$tail_pos(), rb2$tail_pos())
expect_equal(rb1$used(), rb2$used())
## Try a couple of incompatible cases:
expect_error(rb1$mirror(ring_buffer_bytes(size - 1, stride)),
"Can't mirror as buffers differ in their size (128 vs 127)",
fixed = TRUE)
expect_error(rb1$mirror(ring_buffer_bytes(size, stride - 1)),
"Can't mirror as buffers differ in their stride (8 vs 7)",
fixed = TRUE)
## check even when bytes used would be about the same:
expect_error(rb1$mirror(ring_buffer_bytes(size / 2, stride * 2)),
"Can't mirror as buffers differ",
fixed = TRUE)
})
test_that("reset", {
rb <- ring_buffer_bytes(10)
rb$push(as.raw(1:3))
rb$push(as.raw(seq_len(rb$size())))
rb$reset()
expect_equal(rb$used(), 0)
expect_true(rb$is_empty())
expect_equal(rb$head_pos(), 0)
expect_equal(rb$tail_pos(), 0)
expect_equal(rb$data(), as.raw(c(9:10, 3, 1:8)))
rb$reset(TRUE)
expect_equal(rb$data(), rep(as.raw(0), rb$bytes_data()))
})
test_that("head set/advance", {
stride <- 4
n <- 10
rb <- ring_buffer_bytes(n, stride)
## expect_equal(rb$head_data(), rep(as.raw(0), stride))
d <- random_bytes(stride)
rb$head_set(d)
expect_equal(rb$head_data(), d)
expect_equal(rb$data(), pad(d, (n + 1) * stride))
expect_true(rb$is_empty())
rb$head_advance()
expect_false(rb$is_empty())
expect_equal(rb$used(), 1)
expect_equal(rb$tail(), d)
expect_equal(rb$head_data(), rep(as.raw(0), stride))
expect_error(rb$head_set(random_bytes(stride - 1)),
"expected exactly 4 bytes")
expect_error(rb$head_set(random_bytes(stride + 1)),
"expected exactly 4 bytes")
expect_equal(rb$head_data(), rep(as.raw(0), stride))
})
test_that("non-raw input", {
stride <- 4
n <- 10
rb <- ring_buffer_bytes(n, stride)
expect_error(rb$push(runif(stride)),
"Expected a raw vector 'data'", fixed = TRUE)
})
test_that("invalid copy", {
bytes <- as.raw(0:255)
buf <- ring_buffer_bytes(length(bytes))
expect_error(buf$copy(list(), 0),
"'dest' must be a 'ring_buffer_bytes'", fixed = TRUE)
expect_error(buf$copy(buf, 0),
"Can't copy a buffer into itself", fixed = TRUE)
})
test_that("invalid mirror", {
bytes <- as.raw(0:255)
buf <- ring_buffer_bytes(length(bytes))
expect_error(buf$mirror(list()),
"'dest' must be a 'ring_buffer_bytes'", fixed = TRUE)
expect_error(buf$mirror(buf),
"Can't mirror a buffer into itself", fixed = TRUE)
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.