
## tests for digest, taken from the examples in the manual page

suppressMessages(library(digest))

# calculate sha1 fingerprints
x.numeric <- c(seq(0, 1, length = 4 ^ 3), -Inf, Inf, NA, NaN)
x.list <- list(letters, x.numeric)
x.dataframe <- data.frame(X = letters,
                          Y = x.numeric[2],
                          Z = factor(letters),
                          stringsAsFactors = FALSE)
x.matrix.num <- as.matrix(x.numeric)
x.matrix.letter <- as.matrix(letters)
x.dataframe.round <- x.dataframe
x.dataframe.round$Y <- signif(x.dataframe.round$Y, 14)
x.factor <- factor(letters)
x.array.num <- as.array(x.numeric)
x.formula <- a~b+c|d
x.paren_formula <- a~(b+c)
x.no_paren_formula <- a~b+c

# tests using detailed numbers
expect_false(identical(x.numeric, signif(x.numeric, 14)))
expect_false(identical(x.matrix.num, signif(x.matrix.num, 14)))

# returns the correct SHA1
expect_true(
    identical(
        sha1(x.numeric),
        {
            z <- digest:::num2hex(x.numeric)
            attr(z, "digest::sha1") <- list(
                class = class(x.numeric),
                digits = 14L,
                zapsmall = 7L
            )
            digest(z, algo = "sha1")
        }
    )
)
# Verify that all numeric values (especially +-Inf and NA/NaN) return unique
# SHA1 hashes
expect_false(
    any(duplicated(sapply(x.numeric, sha1)))
)
expect_true(
    identical(
        sha1(letters),
        {
            z <- letters
            attr(z, "digest::sha1") <- list(
                class = "character",
                digits = 14L,
                zapsmall = 7L
            )
            digest(z, algo = "sha1")
        }
    )
)
expect_true(
    identical(
        sha1(x.list),
        {
            z <- sapply(x.list, sha1)
            attr(z, "digest::sha1") <- list(
                class = "list",
                digits = 14L,
                zapsmall = 7L
            )
            digest(z, algo = "sha1")
        }
    )
)
options(sha1PackageVersion = "0.6.22.1")
expect_true(
    identical(
        sha1(x.dataframe),
        {
            z <- sapply(x.dataframe, sha1)
            attr(z, "digest::sha1") <- list(
                class = "data.frame",
                digits = 14L,
                zapsmall = 7L
            )
            digest(z, algo = "sha1")
        }
    )
)
## expect_true(
##     identical(
##         sha1(x.matrix.num),
##         {
##             z <- matrix(
##                 apply(x.matrix.num, 2, digest:::num2hex),
##                 ncol = ncol(x.matrix.num)
##             )
##             attr(z, "digest::sha1") <- list(
##                 class = "matrix",
##                 digits = 14L,
##                 zapsmall = 7L
##             )
##             digest(z, algo = "sha1")
##         }
##     )
## )
## expect_true(
##     identical(
##         sha1(x.matrix.letter),
##         {
##             z <- x.matrix.letter
##             attr(z, "digest::sha1") <- list(
##                 class = "matrix",
##                 digits = 14L,
##                 zapsmall = 7L
##             )
##             digest(z, algo = "sha1")
##         }
##     )
## )
stopifnot(
    identical(
        sha1(x.factor),
        {
            z <- x.factor
            attr(z, "digest::sha1") <- list(
                class = "factor",
                digits = 14L,
                zapsmall = 7L
            )
            digest(z, algo = "sha1")
        }
    )
)
# a matrix and a vector should have a different hash
expect_true(
    !identical(
        sha1(x.numeric),
        sha1(matrix(x.numeric, nrow = 1))
    )
)
expect_true(
    !identical(
        sha1(x.numeric),
        sha1(matrix(x.numeric, ncol = 1))
    )
)
expect_true(
    !identical(
        sha1(letters),
        sha1(matrix(letters, nrow = 1))
    )
)
expect_true(
    !identical(
        sha1(letters),
        sha1(matrix(letters, ncol = 1))
    )
)

# character(0) and numeric(0) should have a different hash
expect_true(!identical(sha1(character(0)), sha1(numeric(0))))

# a POSIXct and a POSIXlt should give a different hash
z <- as.POSIXct("2015-01-02 03:04:06.07", tz = "UTC")
expect_true(
    !identical(
        sha1(z),
        sha1(as.POSIXlt(z))
    )
)

lm.model.0 <- lm(weight ~ Time, data = ChickWeight)
lm.model.1 <- lm(weight ~ 1, data = ChickWeight)
glm.model.0 <- glm(weight ~ Time, data = ChickWeight, family = poisson)
glm.model.1 <- glm(weight ~ 1, data = ChickWeight, family = poisson)

anova.list <- list(
    lm = anova(lm.model.0, lm.model.1),
    glm = anova(glm.model.0, glm.model.1),
    glm.test = anova(glm.model.0, glm.model.1, test = "Chisq")
)

# works with lm anova"
expect_true(
    identical(
        sha1(anova.list[["lm"]]),
        {
            y <- apply(
                anova.list[["lm"]],
                1,
                digest:::num2hex,
                digits = 4,
                zapsmall = 7
            )
            attr(y, "digest::sha1") <- list(
                class = c("anova", "data.frame"),
                digits = 4L,
                zapsmall = 7L
            )
            digest(y, algo = "sha1")
        }
    )
)
# works with glm anova"
expect_true(
    identical(
        sha1(anova.list[["glm"]]),
        {
            y <- apply(
                anova.list[["glm"]],
                1,
                digest:::num2hex,
                digits = 4,
                zapsmall = 7
            )
            attr(y, "digest::sha1") <- list(
                class = c("anova", "data.frame"),
                digits = 4L,
                zapsmall = 7L
            )
            digest(y, algo = "sha1")
        }
    )
)
expect_true(
    identical(
        sha1(anova.list[["glm.test"]]),
        {
            y <- apply(
                anova.list[["glm.test"]],
                1,
                digest:::num2hex,
                digits = 4,
                zapsmall = 7
            )
            attr(y, "digest::sha1") <- list(
                class = c("anova", "data.frame"),
                digits = 4L,
                zapsmall = 7L
            )
            digest(y, algo = "sha1")
        }
    )
)
expect_true(
    identical(
        sha1(x.formula, environment=FALSE),
        {
            y <- sapply(
                X=x.formula,
                FUN=sha1,
                digits=14L,
                zapsmall=7L,
                ...=list(environment=FALSE),
                algo="sha1"
            )
            attr(y, "digest::sha1") <- list(
                class="formula",
                digits=14L,
                zapsmall=7L,
                environment=FALSE
            )
            digest(y, algo="sha1")
        }
    )
)
expect_true(
    identical(
        sha1(x.formula),
        {
            y <- c(
                sapply(
                    X=x.formula,
                    FUN=sha1,
                    digits=14L,
                    zapsmall=7L,
                    ...=list(environment=TRUE),
                    algo="sha1"
                ),
                digest(environment(x.formula), algo="sha1")
            )
            attr(y, "digest::sha1") <- list(
                class="formula",
                digits=14L,
                zapsmall=7L
            )
            digest(y, algo="sha1")
        }
    )
)
expect_true(
    sha1(x.paren_formula) != sha1(x.no_paren_formula)
)

test.element <- list(
    # NULL
    NULL,
    # empty classes
    logical(0), integer(0), numeric(0), character(0), list(), data.frame(),
    # scalar
    TRUE, FALSE, 1L, 1, "a",
    # date. Make sure to add the time zone. Otherwise the test might fail
    as.POSIXct("2015-01-02 03:04:06.07", tz = "UTC"),
    # vector
    c(TRUE, FALSE), 1:3, seq(0, 10, length = 4), letters[1:3],
    factor(letters[4:6]),
    as.POSIXct(c("2015-01-02 03:04:06.07", "1960-12-31 23:59:59"), tz = "UTC")
)
select.vector <- which(sapply(test.element, length) > 1)
test.element <- c(
    test.element,
    # add a data.frame
    list(expand.grid(test.element[select.vector])),
    # add a list
    list(test.element[select.vector]),
    # add matrices
    list(matrix(1:10)),
    list(matrix(seq(0, 10, length = 4))),
    list(matrix(letters))
)
# different values for digits or zapsmall gives different hashes
# expect for NULL
expect_true(
    identical(
        sha1(NULL, digits = 14),
        sha1(NULL, digits = 13)
    )
)
expect_true(
    identical(
        sha1(NULL, zapsmall = 14),
        sha1(NULL, zapsmall = 13)
    )
)
for (i in tail(seq_along(test.element), -1)) {
    expect_true(
        !identical(
            sha1(test.element[[i]], digits = 14),
            sha1(test.element[[i]], digits = 13)
        )
    )
    expect_true(
        !identical(
            sha1(test.element[[i]], zapsmall = 7),
            sha1(test.element[[i]], zapsmall = 6)
        )
    )
}
test.element <- c(test.element, anova.list)

#cat("\ncorrect <- c(\n")
#cat(
#    sprintf("    \"%s\"", sapply(test.element, sha1)),
#    sep = ",\n"
#)
#cat(")\n")

correct <- c(
    "8d9c05ec7ae28b219c4c56edbce6a721bd68af82",
    "d61eeea290dd09c5a3eba41c2b3174b6e4e2366d",
    "af23305d27f0409c91bdb86ba7c0cdb2e09a5dc6",
    "0c9ca70ce773deb0d9c0b0579c3b94856edf15cc",
    "095886422ad26e315c0960ef6b09842a1f9cc0ce",
    "6cc04c6c432bb91e210efe0b25c6ca809e6df2e3",
    "c1113ba008a349de64da2a7a724e501c1eb3929b",
    "6e12370bdc6fc457cc154f0525e22c6aa6439f4d",
    "1c1b5393c68a643bc79c277c6d7374d0b30cd985",
    "b48c17a2ac82601ff38df374f87d76005fb61cbd",
    "35280c99aa6a48bfc2810b72b763ccac0f632207",
    "f757cc017308d217f35ed8f0c001a57b97308fb7",
    "cfcf101b8449af67d34cdc1bcb0432fe9e4de08e",
    "a14384d1997440bad13b97b3ccfb3b8c0392e79a",
    "555f6bea49e58a2c2541060a21c2d4f9078c3086",
    "631d18dec342e2cb87614864ba525ebb9ad6a124",
    "b6c04f16b6fdacc794ea75c8c8dd210f99fafa65",
    "25485ba7e315956267b3fdc521b421bbb046325d",
    "6def3ca353dfc1a904bddd00e6a410d41ac7ab01",
    "cf220bcf84c3d0ab1b01f8f764396941d15ff20f",
    "2af8021b838f613aee7670bed19d0ddf1d6bc0c1",
    "270ed85d46524a59e3274d89a1bbf693521cb6af",
    "60e09482f12fda20f7d4a70e379c969c5a73f512",
    "10380001af2a541b5feefc7aab9f719b67330a42",
    "4580ff07f27eb8321421efac1676a80d9239572a",
    "d3022c5a223caaf77e9c564e024199e5d6f51bd5",
    "f54742ac61edd8c3980354620816c762b524dfc7"
)
# each object should yield a different hash
expect_true(!any(duplicated(correct)))
# returns the same SHA1 on both 32-bit and 64-bit OS"
## for (i in seq_along(test.element)) {
##     expect_true(
##         identical(
##             sha1(test.element[[i]]),
##             correct[i]
##         )
##     )
## }

# does work with empty lists and data.frames
expect_true(is.character(sha1(list())))
expect_true(is.character(sha1(data.frame())))
expect_true(is.character(sha1(list(a = 1, b = list(), c = data.frame()))))

# does work with complex type
expect_true(is.character(sha1(2 + 5i))) # single complex number
expect_true(is.character(sha1(1:10 + 5i))) # vector of complex numbers

# complex number with only the real part should be different from real number
expect_true(sha1(2) != sha1(2 + 0i))

# does work with Date type
expect_true(is.character(sha1(Sys.Date())))
expect_true(sha1(as.Date("1980-01-01")) != sha1(as.Date("1990-01-01")))

# different hashes for differently shaped arrays that contain the same data
data <- 1:8
a <- array(data, dim = c(2,2,2)) # cube 2x2x2
b <- array(data, dim = c(2,4,1)) # matrix 2x4
expect_true(sha1(a) != sha1(b))

# test error message
junk <- pi
class(junk) <- c("A", "B")
#error.message <- try(sha1(junk))
#expect_true(grepl("sha1\\(\\) has no method for the 'A', 'B' class", error.message))

junk <- function(
    x, y = list(...), test = TRUE, start = NULL, text = "abc", family = poisson,
    ...
){
    sha1(x)
}
#expect_true(sha1(junk) == "be194e8cdae926c13fd4e2c65bf6cb7a28dd0505")
expect_true(sha1(junk) == sha1(junk, environment = TRUE))
expect_true(sha1(junk) != sha1(junk, environment = FALSE))

#expect_true(sha1(matrix(integer(0))) == "e13485e1b995f3e36d43674dcbfedea08ce237bc")
expect_true(
    !identical(
        sha1(matrix(integer(0))),
        sha1(matrix(character(0)))
    )
)
expect_true(
    !identical(
        sha1(matrix(integer(0))),
        sha1(matrix(numeric(0)))
    )
)

## if (getRversion() < "3.5.0") {
##     expect_true(
##         identical(
##             sha1(serialize("e13485e1b995f3e36d43674dcbfedea08ce237bc", NULL)),
##             "93ab6a61f1a2ad50d4bf58396dc38cd3821b2eaf"
##         )
##     )
## }

x <- letters
for (algo in c("md5", "sha1", "crc32", "sha256", "sha512", "xxhash32",
               "xxhash64",  "murmur32")) {
    y <- x
    attr(y, "digest::sha1") <- digest:::attr_sha1(x, 14L, 7L, algo = algo)
    expect_true(
        identical(
            sha1(x, algo = algo),
            digest(y, algo = algo)
        )
    )

}

expect_true(is.character(sha1(sessionInfo())))

# check the effect of attributes from version 0.6.22.2
options(sha1PackageVersion = utils::packageVersion("digest"))
check_attribute_effect <- function(x) {
    y <- x
    attr(y, "test") <- "junk"
    expect_false(sha1(x) == sha1(y))
}
test.element <- list(2 + 5i, x.array.num, Sys.Date(), Sys.time(), y ~ x)
test.element <- c(test.element, list(x.dataframe), anova.list, function(x){x})
for (z in test.element) {
    check_attribute_effect(z)
}

# check that sha1() on contributed functions maintain there hash after storing
f <- tempfile(fileext = ".rds")
x <- digest::sha1
saveRDS(x, f)
y <- readRDS(f)
expect_identical(sha1(x), sha1(y))
expect_identical(sha1(x, environment = FALSE), sha1(y, environment = FALSE))
