context("tdiff class") # ################################################################### # supported units of time units <- c("y", "q", "m", "w", "d", "h", "min", "s") # test sample size NN <- 99L # test samples yy <- sample(1990L:2020L, NN, replace = TRUE) qq <- .validate_yq(yy, sample.int(4L, size = NN, replace = TRUE)) mm <- .validate_ym(yy, sample.int(12L, size = NN, replace = TRUE)) mm <- .validate_ym(yy, sample.int(12L, size = NN, replace = TRUE)) ww <- pmin(sample.int(53L, size = NN, replace = TRUE), .weeks_in_year(yy)) ww <- .validate_yw(yy, ww) dd <- pmin(sample.int(31L, size = NN, replace = TRUE), .days_in_month(mm)) dd <- .validate_ymd(.m2y(mm), .m2mnth(mm), dd) yy <- sample(0L:9999L, NN, replace = TRUE) tt0 <- round(as.numeric(Sys.time()) + runif(NN, -3e7, 3e7), digits = 3) tt1 <- round(tt0) tt2 <- round(tt0 / 60) * 60 tt3 <- round(tt0 / 3600) * 3600 test_that("'.validate_tdiff' works correctly", { rng <- c(y = 9999, q = .validate_yq(9999, 4) - .validate_yq(0, 1), m = .validate_ym(9999, 12) - .validate_ym(0, 1), w = .validate_yw(9999, 52) - .validate_yw(0, 1), d = .validate_ymd(9999, 12, 31) - .validate_ymd(0, 1, 1), t = 253402246800 - -62167165200) for (un in setdiff(names(rng), "t")) { rt <- rng[un] x <- c(-rt - 1, -rt, rt + 0:2) vx <- .validate_tdiff(x, un) expect_equal(vx[!is.na(vx)], x[2L:4L]) } rt <- rng["t"] x <- c(-rt - 1, -rt, rt + 1) vx <- .validate_tdiff(x, "t") expect_equal(vx[!is.na(vx)], x[2L]) }) test_that("'as.tdiff' works correctly", { # default, NULL, numeric err <- ".*unit.* missing" expect_error(as.tdiff(1), err) expect_error(as.tdiff(NULL), err) un <- sample(units, 1L) expect_identical(as.tdiff(NULL, un), as.tdiff(numeric(), un)) err <- paste0(sQuote("as.tdiff"), " method not defined") expect_error(as.tdiff(today()), err, fixed = TRUE) warn <- "NAs introduced" expect_warning(res <- as.tdiff(9999:10001, "y"), warn, fixed = TRUE) expect_identical(res, as.tdiff(c(9999:10000, NA), "y")) # character, factor expect_identical(as.tdiff("1d"), as.tdiff(1, "d")) expect_identical(as.tdiff("d"), as.tdiff(1, "d")) expect_identical(as.tdiff(as.factor(paste0(1:3, "d"))), as.tdiff(1:3, "d")) err <- "parse error / could not recognise format" expect_error(as.tdiff("dd"), err, fixed = TRUE) # difftime xx <- sample(-100:100, NN, replace = TRUE) map <- c(s = "secs", min = "mins", h = "hours", d = "days", w = "weeks") for (un in names(map)) { expect_identical(as.tdiff(as.difftime(xx, units = map[un])), as.tdiff(xx, un)) } warn <- "NAs introduced" dtw <- as.difftime(51:53 * 10000, units = "weeks") expect_warning(tdw <- as.tdiff(dtw), warn, fixed = TRUE) expect_identical(tdw, as.tdiff(c(51:52 * 10000, NA), "w")) }) test_that("'years', ..., 'secs' work correctly", { funcs <- c("years", "qrtrs", "mnths", "weeks", "days", "hours", "mins", "secs") for (f in funcs) { u <- if (f == "mins") "min" else substr(f, 1L, 1L) xx <- sample(-100:100, NN, replace = TRUE) expect_identical(do.call(f, list(xx)), as.tdiff(xx, u)) } }) test_that("'as.character.tdiff' and 'format.tdiff' work correctly", { skip_on_cran() # in case of corner cases, this is also slow... # basic R implementations .as.character.tdiff0 <- function(x) { nms <- names(x) type <- .get_tdiff_type(x) if (anyna <- anyNA(x)) { nna <- !is.na(x) res <- rep(NA_character_, length(x)) names(res) <- nms if (!any(nna)) { names(res) <- nms; return (res) } x <- x[nna] } x <- as.vector(x) if (type == "t") { ax <- abs(x) nx <- (x < 0) s <- round(ax %% 60, 6) m <- as.integer((ax %% 3600) %/% 60) h <- as.integer((ax %% 86400) %/% 3600) d <- as.integer(ax %/% 86400) if (all(x == 0)) { sflag <- TRUE mflag <- FALSE } else { sflag <- any(s != 0) mflag <- any(m) || sflag && (any(h) || any(d)) } xf <- rep("", length(x)) xf[nx] <- paste0("-", xf[nx]) # days iid <- as.logical(d) xf[iid] <- paste0(xf[iid], d[iid], "d") # hours xf[iid] <- paste0(xf[iid], formatC(h[iid], width = 2L, format = "d", flag = "0")) iih <- !iid & (as.logical(h) | as.logical(m) | !mflag && !sflag | mflag && !sflag) xf[iih] <- paste0(xf[iih], h[iih]) iih <- iid | iih xf[iih] <- paste0(xf[iih], "h") # minutes if (mflag) { iim <- !iih & (as.logical(m) | !sflag) xf[iim] <- paste0(xf[iim], m[iim]) xf[iih] <- paste0(xf[iih], formatC(m[iih], width = 2L, format = "d", flag = "0")) iim <- iim | iih xf[iim] <- paste0(xf[iim], "m") } else iim <- rep(FALSE, length(x)) # seconds if (sflag) { sf <- paste0(s %/% 10, format(round(s %% 10, 6))) xf[iim] <- paste0(xf[iim], sf[iim]) xf[!iim] <- paste0(xf[!iim], gsub("^0([0-9])", "\\1", sf[!iim])) xf <- paste0(xf, "s") } } else { xf <- paste0(x, type) } if (anyna) { res[nna] <- xf; return (res) } names(xf) <- nms return (xf) } .format.tdiff0 <- function(x) { xf <- format(.as.character.tdiff0(x), justify = "right") type <- .get_tdiff_type(x) nms <- names(x) if (type == "y") { names(xf) <- nms; return (xf) } attributes(x) <- NULL xfaux <- rep("", length(x)) if (type == "t") { x <- round(x / 86400); type <- "d" } if ((type == "q") && (aux <- .anyTRUE(addi <- (abs(x) >= 4L)))) { addi[is.na(addi)] <- FALSE xfaux[addi] <- paste0("(", round(x[addi] / 4, 2), "y)") } if ((type == "m") && (aux <- .anyTRUE(addi <- (abs(x) >= 12L)))) { addi[is.na(addi)] <- FALSE xfaux[addi] <- paste0("(", round(x[addi] / 12, 2), "y)") } if ((type == "w") && (aux <- .anyTRUE(addi <- (abs(x) >= 52L)))) { addi[is.na(addi)] <- FALSE xfaux[addi] <- paste0("(~", round(x[addi] / 52.1775, 1), "y)") } if ((type == "d") && (aux <- .anyTRUE(addi <- (abs(x) >= 30L)))) { addi[is.na(addi)] <- FALSE addi <- which(addi) mm <- round(x[addi] / 30.44, 1) iy <- (abs(mm) >= 12) xfaux[addi[!iy]] <- paste0("(~", mm[!iy], "m)") xfaux[addi[iy]] <- paste0("(~", round(x[addi[iy]] / 365.2425, 1), "y)") } if (aux) xf <- paste(xf, format(xfaux, justify = "left")) names(xf) <- nms return (xf) } # actual tests for (un in c("y", "q", "m", "w", "d")) { xx <- get(paste0(un, un)) dxx <- as.tdiff(xx - rev(xx), un) expect_identical(as.character(dxx), .as.character.tdiff0(dxx)) expect_identical(format(dxx), .format.tdiff0(dxx)) expect_true(all(diff(nchar(format(dxx))) == 0L)) dxx <- dxx[1L:4L] names(dxx) <- letters[1L:4L] expect_identical(as.character(dxx), .as.character.tdiff0(dxx)) expect_identical(format(dxx), .format.tdiff0(dxx)) } for (ttv in 0:3) { xx <- get(paste0("tt", ttv)) dxx <- as.tdiff(xx - rev(xx), "s") expect_identical(as.character(dxx), .as.character.tdiff0(dxx)) expect_true(all(diff(nchar(format(dxx))) == 0L)) expect_identical(format(dxx), .format.tdiff0(dxx)) dxx <- dxx[1L:4L] names(dxx) <- letters[1L:4L] expect_identical(as.character(dxx), .as.character.tdiff0(dxx)) expect_identical(format(dxx), .format.tdiff0(dxx)) } # empty un <- sample(units, 1L) xx <- as.tdiff(numeric(), un) expect_identical(as.character(xx), character()) expect_identical(format(xx), character()) }) test_that("'as.integer.tdiff' and 'as.double.tdiff' work correctly", { un <- sample(head(units, -3L), 1L) td <- as.tdiff(-1:1, un) expect_identical(as.integer(td), -1L:1L) expect_identical(as.double(td), as.double(-1:1)) td <- hours(-1:1) expect_identical(as.integer(td), -1L:1L) expect_identical(as.double(td), 3600. * (-1:1)) td <- mins(-1:1) expect_identical(as.integer(td), -1L:1L) expect_identical(as.double(td), 60. * (-1:1)) td <- secs(-1:1) expect_identical(as.integer(td), -1L:1L) expect_identical(as.double(td), as.double(-1:1)) }) test_that("'as.list.tdiff' works correctly", { xx <- sample(-100:100, NN, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) expect_identical(as.list(td), lapply(xx, function(x) as.tdiff(x, un))) xx10 <- xx[1L:10L] names(xx10) <- letters[1L:10L] lx10 <- as.list(xx10) dx10 <- as.tdiff(xx10, un) expect_identical(as.list(dx10), lapply(lx10, function(x) as.tdiff(x, un))) }) test_that("'as.data.frame.tdiff' works correctly", { xx <- sample(-100:100, NN, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) df <- as.data.frame(td) expect_true(is.data.frame(df)) expect_identical(nrow(df), length(td)) expect_identical(ncol(df), 1L) expect_identical(df[[1L]], td) }) test_that("tdiff '[', '[[', '[<-', and '[[<-' methods work correctly", { xx <- sample(-100:100, NN, replace = FALSE) un <- sample(units, 1L) dx <- as.tdiff(xx, un) ii <- sample(1L:NN, 10) expect_identical(dx[ii], as.tdiff(xx[ii], un)) ii <- sample(1L:NN, 10L) jj <- sample(1L:NN, 10L) dx[ii] <- dx[jj] xx[ii] <- xx[jj] expect_identical(dx, as.tdiff(xx, un)) dx[] <- 2000 expect_true(all(dx == 2000)) xx <- sample(-100:100, NN, replace = FALSE) dx <- days(xx) expect_silent(dx[NN + (-2L:0L)] <- NA) expect_identical(is.na(dx), c(rep(FALSE, NN - 3L), rep(TRUE, 3L))) expect_silent(dx[] <- "-1d") expect_true(all(dx == -1)) expect_silent(dx[1L] <- "2d") expect_true(dx[1L] == 2) expect_true(all(dx[-1L] == -1)) expect_silent(dx[1L:2L] <- as.difftime(3, units = "days")) expect_true(dx[1L] == dx[2L]) expect_true(dx[2L] == dx[[2L]]) expect_true(dx[[1L]] == dx[[2L]]) expect_silent(dx[[NN - 1L]] <- as.difftime(-5, units = "days")) expect_silent(dx[[NN]] <- "-4d") expect_true(all(dx[NN + -1:0] == -5:-4)) expect_silent(dx[[NN]] <- NA) expect_true(is.na(dx[NN])) err <- paste0("time unit mismatch in ", sQuote("[<-.tdiff"), ": ", .t_unit2char("d"), ", ", .t_unit2char("w")) expect_error(dx[1L:3L] <- weeks(1:3), err, fixed = TRUE) err <- paste0("time unit mismatch in ", sQuote("[[<-.tdiff"), ": ", .t_unit2char("d"), ", ", .t_unit2char("m")) expect_error(dx[[3L]] <- mnths(7), err, fixed = TRUE) }) test_that("'length<-' works correctly", { NN <- sample((NN %/% 2):NN, 1L) MM <- sample(2L:5L, 1L) xx <- sample(-100:100, NN, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) length(xx) <- NN - MM length(td) <- NN - MM expect_identical(as.tdiff(xx, un), rep(td)) NN <- sample((NN %/% 2):NN, 1L) MM <- sample(2L:5L, 1L) xx <- sample(-100:100, NN, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) length(xx) <- NN + MM length(td) <- NN + MM expect_identical(as.tdiff(xx, un), rep(td)) }) test_that("'rep' works correctly", { NN <- sample((NN %/% 2):NN, 1L) MM <- sample(2L:5L, 1L) xx <- sample(-100:100, NN, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) expect_identical(as.tdiff(rep(xx, MM), un), rep(td, MM)) }) test_that("'c.tdiff' works correctly", { xx <- sample(-100:100, NN, replace = FALSE) un <- sample(units, 1L) dx <- as.tdiff(xx, un) nn <- sample.int(NN - 1L, 1L) x1 <- head(xx, nn) x2 <- tail(xx, -nn) d1 <- as.tdiff(x1, un) d2 <- as.tdiff(x2, un) expect_identical(dx, c(d1, d2)) if (!(un %in% c("h", "min"))) expect_identical(dx, c(d1, x2)) expect_identical(c(days(7), "8d"), days(7:8)) expect_identical(c(days(7), 8), days(7:8)) err <- paste("time unit mismatch in", sQuote("c.tdiff")) un2 <- if (un %in% tail(units, 3L)) sample(head(units, -3L), 1L) else sample(setdiff(units, un), 1L) expect_error(c(d1, as.tdiff(x2, un2)), err, fixed = TRUE) expect_error(c(as.tdiff(x1, un2), d2), err, fixed = TRUE) }) test_that("'Math.tind', 'Summary.tind', and 'Complex.tind' methods work correctly", { xx <- sample(-100:100, NN, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) # math w/0 sign for (f in c("abs", "cummin", "cummax")) { expect_identical(do.call(f, list(td)), as.tdiff(do.call(f, list(xx)), un)) } # summary + sign for (f in c("all", "any", "sign")) { expect_identical(do.call(f, list(td)), do.call(f, list(xx))) } for (f in c("min", "max", "range", "sum")) { expect_identical(do.call(f, list(td)), as.tdiff(do.call(f, list(xx)), un)) expect_identical(do.call(f, list(td)), do.call(f, list(td[1L:10L], td[11L:NN]))) } # math2 xx <- round(runif(NN, -1, 10), digits = 3) un <- "s" td <- as.tdiff(xx, un) expect_identical(signif(td, 1), as.tdiff(signif(xx, 1), un)) expect_identical(round(td, 1), as.tdiff(round(xx, 1), un)) # errors err <- paste0(" method not defined for class ", dQuote("tdiff")) expect_error(prod(td), paste0(sQuote("prod"), err), fixed = TRUE) expect_error(cos(td), paste0(sQuote("cos"), err), fixed = TRUE) expect_error(Arg(td), paste0(sQuote("Arg"), err), fixed = TRUE) }) test_that("'is.unsorted', 'sort', and 'order' tind methods work correctly", { xx <- sample(-1:1, 10, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) expect_identical(is.unsorted(td), is.unsorted(xx)) expect_false(is.unsorted(sort(td))) expect_identical(sort(td), as.tdiff(sort(xx), un)) }) test_that("'unique', 'duplicated', and 'anyDuplicated' tdiff methods work correctly", { xx <- sample(-1:1, 10, replace = TRUE) un <- sample(units, 1L) td <- as.tdiff(xx, un) expect_identical(unique(td), as.tdiff(unique(xx), un)) expect_identical(unique(as.tdiff(sort(xx), un)), as.tdiff(sort(unique(xx)), un)) expect_identical(duplicated(td), duplicated(xx)) expect_identical(duplicated(td, fromLast = TRUE), duplicated(xx, fromLast = TRUE)) expect_identical(anyDuplicated(td), anyDuplicated(xx)) expect_identical(anyDuplicated(unique(td)), 0L) expect_identical(anyDuplicated(td, fromLast = TRUE), anyDuplicated(xx, fromLast = TRUE)) })