tests/testthat/test-util.R

context("util")


test_that("camel <-> snake", {
  expect_equal(camel_to_snake("fooBar"), "foo_bar")
  expect_equal(camel_to_snake("foo_bar"), "foo_bar")
  expect_equal(snake_to_camel("fooBar"), "fooBar")
  expect_equal(snake_to_camel("foo_bar"), "fooBar")

  expect_equal(camel_to_snake(c("fooBar", "fizzBuzz")),
               c("foo_bar", "fizz_buzz"))
  expect_equal(snake_to_camel(c("foo_bar", "fizz_buzz")),
               c("fooBar", "fizzBuzz"))
})


test_that("case convert with consecutive capitals", {
  expect_equal(pascal_to_snake("NanoCPUs"), "nano_cpus")
  ## The conversion is lossy though:
  expect_equal(snake_to_pascal("nano_cpus"), "NanoCpus")
})


test_that("case convert: reference check", {
  nms <- read.csv(stevedore_file("spec/names.csv"), stringsAsFactors = FALSE)
  ex <- read.csv(stevedore_file("spec/names_override.csv"),
                 stringsAsFactors = FALSE)
  i <- !(nms$from %in% ex$from)
  expect_equal(nms$to[i], pascal_to_snake(nms$from[i]))
})


test_that("case convert: caching", {
  pascal_to_snake_cache_reset()
  expect_equal(pascal_to_snake("CACert"),
               pascal_to_snake_cached("CACert"))

  from <- "AbraCadabra"
  to <- pascal_to_snake(from)
  expect_false(from %in% .stevedore$names[, "from"])
  expect_false(to %in% .stevedore$names[, "to"])
  expect_equal(pascal_to_snake_cached(from), to)
  expect_true(from %in% .stevedore$names[, "from"])
  expect_true(to %in% .stevedore$names[, "to"])
})


test_that("case convert: special cases", {
  ## See notes in design.md
  expect_equal(pascal_to_snake("CACert"), "ca_cert")
  expect_equal(pascal_to_snake("CAConfig"), "ca_config")
  expect_equal(pascal_to_snake("CPUSet"), "cpu_set")
  expect_equal(pascal_to_snake("CPUShares"), "cpu_shares")
  expect_equal(pascal_to_snake("DNSConfig"), "dns_config")
  expect_equal(pascal_to_snake("GID"), "gid")
  expect_equal(pascal_to_snake("ID"), "id")
  expect_equal(pascal_to_snake("IOMaximumBandwidth"), "io_maximum_bandwidth")
  expect_equal(pascal_to_snake("IOMaximumIOps"), "io_maximum_iops")
  expect_equal(pascal_to_snake("IP"), "ip")
  expect_equal(pascal_to_snake("IPAddress"), "ip_address")
  expect_equal(pascal_to_snake("IPAM"), "ipam")
  expect_equal(pascal_to_snake("IPAMConfig"), "ipam_config")
  expect_equal(pascal_to_snake("IPPrefixLen"), "ip_prefix_len")
  expect_equal(pascal_to_snake("IPv4Address"), "ipv4_address")
  expect_equal(pascal_to_snake("IPv4Forwarding"), "ipv4_forwarding")
  expect_equal(pascal_to_snake("IPv6Address"), "ipv6_address")
  expect_equal(pascal_to_snake("IPv6Gateway"), "ipv6_gateway")
  expect_equal(pascal_to_snake("NCPU"), "n_cpu")
  expect_equal(pascal_to_snake("NEventsListener"), "n_events_listener")
  expect_equal(pascal_to_snake("NFd"), "n_fd")
  expect_equal(pascal_to_snake("NGoroutines"), "n_goroutines")
  expect_equal(pascal_to_snake("OOMKilled"), "oom_killed")
  expect_equal(pascal_to_snake("OS"), "os")
  expect_equal(pascal_to_snake("OSFeatures"), "os_features")
  expect_equal(pascal_to_snake("OSType"), "os_type")
  expect_equal(pascal_to_snake("OSVersion"), "os_version")
  expect_equal(pascal_to_snake("PID"), "pid")
  expect_equal(pascal_to_snake("RW"), "rw")
  expect_equal(pascal_to_snake("SELinuxContext"), "selinux_context")
  expect_equal(pascal_to_snake("TLSInfo"), "tls_info")
  expect_equal(pascal_to_snake("TTY"), "tty")
  expect_equal(pascal_to_snake("UID"), "uid")
  expect_equal(pascal_to_snake("URL"), "url")
  expect_equal(pascal_to_snake("URLs"), "urls")
  expect_equal(pascal_to_snake("UTSMode"), "uts_mode")
})


test_that("case convert: exceptions", {
  expect_equal(pascal_to_snake("MinAPIVersion"), "min_apiversion")
  expect_equal(pascal_to_snake_cached("MinAPIVersion"), "min_api_version")
})


test_that("is_error", {
  expect_false(is_error(NULL))

  cond <- list(message = "foo", code = 404L, endpoint = "pull")
  class(cond) <- c("docker_error", "error", "condition")
  expect_true(is_error(cond))
})


test_that("split command", {
  expect_identical(split_command("hello"), "hello")
  expect_identical(split_command("hello world"), c("hello", "world"))

  ## These we don't handle yet:
  expect_error(split_command("'foo bar'"), "A proper command splitter")
  expect_error(split_command("foo 'bar'"), "A proper command splitter")
  expect_error(split_command('foo "bar"'), "A proper command splitter")
  expect_error(split_command('"foo bar"'), "A proper command splitter")
})


test_that("check command", {
  expect_identical(validate_command("hello world"), "hello world")
  expect_identical(validate_command(I("hello world")), c("hello", "world"))
  expect_null(validate_command(NULL))
  expect_identical(validate_command(letters), letters)
  expect_identical(validate_command(I(letters)), I(letters))
})


## The new yaml package introduces integer overflow with warnings.
test_that("yaml overflow", {
  str <- "Resources:\n  NanoCPUs: 4000000000"
  expect_identical(yaml_load(str),
                   list(Resources = list(NanoCPUs = 4e9)))
})


test_that("stream filtering", {
  s <- rep(1:2, length.out = 10)
  x <- paste0(letters[1:10], "\n")
  obj <- docker_stream(x, s)

  expect_equal(format(obj, style = "plain", filter = "stdout"), x[s == 1])
  expect_equal(format(obj, style = "plain", filter = "stderr"), x[s == 2])
  expect_equal(format(obj, style = "plain", filter = c("stdout", "stderr")), x)
  expect_equal(format(obj, style = "plain", filter = c("stdin")), character(0))
  expect_equal(format(obj, style = "plain", filter = NULL), x)
})


test_that("stream truncating", {
  s <- rep(1:2, length.out = 10)
  x <- paste0(letters[1:10], "\n")
  obj <- docker_stream(x, s)

  expect_equal(
    format(obj, style = "prefix", strip_newline = TRUE, max_lines = 3),
    c("O> a", "-- [...truncated 8 lines...]", "E> j"))
  expect_equal(
    format(obj, style = "prefix", strip_newline = TRUE, max_lines = 4),
    c("O> a", "-- [...truncated 7 lines...]", "O> i", "E> j"))
  expect_equal(
    format(obj, style = "prefix", strip_newline = TRUE, max_lines = 10),
    format(obj, style = "prefix", strip_newline = TRUE))
})


test_that("stream printing", {
  x <- sprintf("Reticulating spline %d...\n", 1:10)
  s <- rep(1, length.out = length(x))
  logs <- docker_stream(x, s)

  expect_match(format(logs, style = "plain"),
               "^Reticulating spline \\d+...\n$")
  expect_match(format(logs, style = "prefix"),
               "^O> Reticulating spline \\d+...\n")
  expect_equal(all(crayon::has_style(format(logs, style = "colour"))),
               crayon::has_color())
  expect_match(capture.output(print(logs, style = "plain")),
               "^Reticulating spline \\d+...$")
  expect_match(capture.output(print(logs, style = "prefix")),
               "^O> Reticulating spline \\d+...$")
  expect_match(capture_output_no_crayon(print(logs)),
               "Reticulating spline \\d+...")
})


test_that("container output", {
  x <- list(id = function() "aaa", name = function() "bbb")
  class(x) <- "docker_container"
  expect_equal(format(x),
               c("<docker_container>",
                 "  id: aaa",
                 "  name: bbb"))
})


test_that("container output print", {
  x <- list(container = "a", logs = "b")
  class(x) <- "docker_run_output"
  str <- c("<docker_run_output>",
           "  $container:",
           "    a",
           "",
           "  $logs:",
           "    b")
  expect_equal(format(x), str)
  expect_equal(capture.output(res <- withVisible(print(x))), str)
  expect_identical(res, list(value = x, visible = FALSE))
})


test_that("integer apply", {
  int <- integer(1)
  twice <- function(x) {
    x * (if (is.integer(x)) 2L else 2.0)
  }
  ## Basic mode: this is all ok
  x <- list(a = 100L, b = 200L)
  expect_identical(viapply(x, twice), c(a = 200L, b = 400L))
  expect_identical(viapply(x, twice, USE.NAMES = FALSE), c(200L, 400L))

  expect_identical(vapply2(x, twice, int), c(a = 200L, b = 400L))
  expect_identical(vapply2(x, twice, int, USE.NAMES = FALSE), c(200L, 400L))


  ## How about integer-as-numeric?
  x <- lapply(x, as.numeric)
  expect_identical(viapply(x, twice), c(a = 200L, b = 400L))
  expect_identical(viapply(x, twice, USE.NAMES = FALSE), c(200L, 400L))
  expect_identical(vapply2(x, twice, int), c(a = 200L, b = 400L))
  expect_identical(vapply2(x, twice, int, USE.NAMES = FALSE), c(200L, 400L))

  ## Very large numbers:
  large <- .Machine$integer.max + 1.0
  x <- list(a = 100L, b = large)
  expect_identical(viapply(x, twice), c(a = 200.0, b = large * 2))
  expect_identical(viapply(x, twice, USE.NAMES = FALSE),
                   c(200.0, large * 2))
  expect_identical(vapply2(x, twice, int), c(a = 200.0, b = large * 2))
  expect_identical(vapply2(x, twice, int, USE.NAMES = FALSE),
                   c(200.0, large * 2))

  ## Error case:
  expect_error(viapply(x, function(x) x + 0.1),
               "Result not integer-like")

  ## non-integer
  expect_identical(
    vapply2(letters, strrep, character(1), 2, USE.NAMES = FALSE),
    strrep(letters, 2))
})


test_that("integer apply/json serialisation", {
  fmt <- '[{"a": %s}, {"a": %s}]'
  s1 <- sprintf(fmt, "100", "200")
  s2 <- sprintf(fmt, "10000000000", "20000000000")
  expect_identical(viapply(from_json(s1), "[[", "a"), c(100L, 200L))
  expect_identical(viapply(from_json(s2), "[[", "a"), (1:2) * 10^10)
})


test_that("sys_which passes for found program", {
  if (!nzchar(Sys.which("ping"))) {
    skip("ping not on path")
  }
  expect_silent(p <- sys_which("ping"))
  expect_is(p, "character")
  expect_identical(p, unname(Sys.which("ping")))
})


test_that("sys_which throws for non existant program", {
  expect_error(sys_which("unknown-program-never-exists"),
               "Did not find program 'unknown-program-never-exists'")
})


test_that("reset_line", {
  tmp <- tempfile_test()
  con <- file(tmp, "w+")
  on.exit(close(con))

  cat("hello", file = con)
  reset_line(con, 10, is_tty = TRUE)
  cat("goodbye", file = con)
  close(con)
  on.exit()

  bytes <- readBin(tmp, raw(), file.size(tmp))
  expect_equal(rawToChar(bytes),
               sprintf("hello\r%s\rgoodbye", strrep(" ", 10)))
  unlink(tmp)

  tmp <- tempfile_test()
  con <- file(tmp, "w+")
  on.exit(close(con))
  cat("hello", file = con)
  reset_line(con, 10, is_tty = FALSE, newline_if_not_tty = TRUE)
  cat("good", file = con)
  reset_line(con, 10, is_tty = FALSE, newline_if_not_tty = FALSE)
  cat("bye", file = con)
  close(con)
  on.exit()

  bytes <- readBin(tmp, raw(), file.size(tmp))
  expect_match(rawToChar(bytes), "hello(\n|\r\n)goodbye")
  unlink(tmp)
})


test_that("download_file", {
  d <- test_docker_client()
  x <- d$container$run("nginx", detach = TRUE, ports = TRUE, rm = TRUE)
  on.exit(x$stop(0))

  url <- sprintf("http://localhost:%s/index.html", x$ports()$host_port[[1]])

  f <- function() {
    res <- curl::curl_fetch_memory(url)
    res$status_code == 200
  }
  wait_until_ready(f)

  p <- tempfile_test()

  expect_silent(cmp <- download_file(url, p, quiet = TRUE))
  expect_identical(cmp, p)

  txt <- readLines(p)
  expect_match(txt, "Welcome to nginx", fixed = TRUE, all = FALSE)
  writeLines("", p)

  expect_silent(cmp <- download_file(url, p, quiet = TRUE))
  expect_equal(readLines(p), "")

  unlink(p)
})


test_that("pretty_bytes", {
  expect_equal(pretty_bytes(0), "0 B")
  expect_equal(pretty_bytes(1), "1 B")
  expect_equal(pretty_bytes(11), "11 B")
  expect_equal(pretty_bytes(111), "111 B")
  expect_equal(pretty_bytes(1111), "1.11 kB")
  expect_equal(pretty_bytes(11111), "11.11 kB")
  expect_equal(pretty_bytes(111111), "111.11 kB")
  expect_equal(pretty_bytes(1111111), "1.11 MB")
  expect_equal(pretty_bytes(11111111), "11.11 MB")
  expect_equal(pretty_bytes(111111111), "111.11 MB")
  expect_equal(pretty_bytes(1111111111), "1.11 GB")
  expect_equal(pretty_bytes(11111111111), "11.11 GB")
  expect_equal(pretty_bytes(111111111111), "111.11 GB")
})


test_that("set_attributes", {
  expect_identical(set_attributes(1L, NULL), 1L)
  expect_identical(set_attributes(1L, list(a = 2L)),
                   structure(1L, a = 2L))
})


test_that("has_colour", {
  expect_equal(has_colour(NULL), crayon::has_color())

  path <- tempfile_test()
  con <- file(path, "w")
  on.exit(close(con))
  expect_false(has_colour(con))
  close(con)
  on.exit()
  unlink(path)
})


test_that("data_frame", {
  a <- 1:10
  b <- sample(letters, 10)
  expect_equal(data_frame(a, b), data.frame(a, b, stringsAsFactors = FALSE))
})


test_that("nothing", {
  expect_equal(withVisible(nothing()),
               list(value = NULL, visible = FALSE))
})


test_that("read_binary", {
  p <- tempfile_test()
  on.exit(unlink(p))
  bytes <- as.raw(sample(0:255, 10000, replace = TRUE))
  writeBin(bytes, p)
  expect_identical(read_binary(p), bytes)
})


test_that("indent", {
  expect_equal(indent("a", 4), "    a")
  expect_equal(indent(letters, 2), paste0("  ", letters))
})


test_that("base64encode", {
  str <- "hello world"
  res <- "aGVsbG8gd29ybGQ="
  expect_identical(base64encode(str), res)
  expect_identical(base64decode(res), str)
})


test_that("base64encode - urlsafe", {
  d <- as.raw(c(0xf9, 0x9f, 0xe3, 0xb7, 0x93, 0x17, 0xcb, 0xc4, 0x53, 0x03))
  s1 <- "+Z/jt5MXy8RTAw=="
  s2 <- "-Z_jt5MXy8RTAw=="
  expect_equal(base64encode(d), s1)
  expect_equal(base64encode(d, TRUE), s2)

  expect_equal(base64decode(s1), rawToChar(d))
  expect_equal(base64decode(s2, TRUE), rawToChar(d))
})


test_that("sprintfn", {
  expect_equal(sprintfn("mystring", character(0)), "mystring")
  expect_equal(sprintfn("xx %s yy", "aa"), "xx aa yy")
  expect_equal(sprintfn("xx %s %s yy", c("aa", "bb")), "xx aa bb yy")
  expect_error(sprintfn("xx %s %s %s yy", c("aa", "bb", "cc")),
               "Not implemented [stevedore bug]", fixed = TRUE)
})


test_that("download_file", {
  skip_if_no_internet()
  dest <- download_file("https://google.com", tempfile_test(), TRUE)
  expect_true(file.exists(dest))
  writeLines("testing", dest)
  expect_identical(download_file("https://google.com", dest, TRUE), dest)
  expect_silent(download_file("https://google.com", dest, FALSE))
  expect_equal(readLines(dest), "testing")
  unlink(dest)
})


test_that("capture_args", {
  expect_identical(capture_args(function() 1, "foo"), "    foo()")
  expect_identical(capture_args(function() 1, "foo", 2), "  foo()")

  expect_identical(capture_args(function(a = 1) 1, "foo"), "    foo(a = 1)")
  expect_identical(capture_args(function(a = 1) 1, "foo", 2), "  foo(a = 1)")
  expect_identical(capture_args(function(a = 1L) 1, "foo"), "    foo(a = 1L)")

  expect_identical(capture_args(function(a) 1, "foo"), "    foo(a)")

  expect_identical(capture_args(
    function(very_long_first_arg = very_long_default_value) 1, "foo", 4, 20),
    "    foo(very_long_first_arg = very_long_default_value)")
  expect_identical(capture_args(
    function(very_long_first_arg = very_long_default_value, b = 1) 1, "foo",
    4, 20),
    "    foo(very_long_first_arg = very_long_default_value,\n        b = 1)")
  expect_identical(capture_args(
    function(very_long_first_arg = very_long_default_value, b = 1) 1, "foo",
    4, 1000),
    "    foo(very_long_first_arg = very_long_default_value, b = 1)")
})


test_that("join_text_list", {
  expect_identical(join_text_list(character()), character())
  expect_identical(join_text_list("a"), "a")
  expect_identical(join_text_list(c("a", "b")), "a and b")
  expect_identical(join_text_list(c("a", "b", "c")), "a, b and c")
})


test_that("pass_through", {
  expect_identical(pass_through(iris), iris)
})


test_that("parse_timestamp", {
  t <- parse_timestamp("2018-03-22T11:51:26.854401Z")
  expect_false(is.na(t))
  expect_is(t, "POSIXt")
  expect_is(t, "POSIXlt")
  cmp <- as.POSIXlt(ISOdatetime(2018, 03, 22, 11, 51, 26.854401, tz = "GMT"))
  expect_equal(t, cmp)
})


test_that("time_ago", {
  timestamp <- "2018-03-22T11:51:26.854401Z"
  t <- parse_timestamp(timestamp)

  expect_equal(time_ago(timestamp, t), "0 secs ago")
  expect_equal(time_ago(timestamp, t + 20), "20 secs ago")
  expect_equal(time_ago(timestamp, t + 20 * 60), "20 mins ago")
  expect_equal(time_ago(timestamp, t + 60 * 60 * 3), "3 hours ago")
  expect_equal(time_ago(timestamp, t + 60 * 60 * 24 * 2), "2 days ago")
})


test_that("cat2", {
  expect_output(cat2("hello", file = stdout()), "hello")
  expect_silent(cat2("hello", file = NULL))
})


test_that("Sys_which", {
  expect_error(Sys_which(rand_str(32)),
               "Command '.{32}' not found on PATH")
})


test_that("system3", {
  skip_on_windows()

  expect_silent(res <- system3("id", "-adsfa"))
  expect_false(res$success)
  expect_error(system3("id", "-adsfa", check = TRUE),
               paste(res$output, collapse = "\n"), fixed = TRUE)
})


## These ones are are just to ensure that some assertions work as expected
test_that("swagger_get_type corner cases", {
  expect_error(swagger_get_type(list(allOf = list())),
               "Should not happen [stevedore bug]", fixed = TRUE)
  expect_error(swagger_get_type(list()),
               "Could not determine type [stevedore bug]", fixed = TRUE)
})


test_that("swagger_arg_collect corner case", {
  expect_error(swagger_arg_collect(list("in" = "other")),
               "assertion error [stevedore bug]", fixed = TRUE)
})


test_that("swagger_arg_collect_query corner cases", {
  expect_error(swagger_arg_collect_query(list(type = "array")),
               "Unknown array query type [stevedore bug]", fixed = TRUE)
  expect_error(swagger_arg_collect_query(list(type = "other")),
               "Unknown query type [stevedore bug]", fixed = TRUE)
})
richfitz/stevedore documentation built on July 22, 2023, 1:13 p.m.