test_that("can unzip all", {
  z <- make_a_zip()

  tmp2 <- test_temp_dir()
  zip::unzip(z$zip, exdir = tmp2)

  expect_true(file.exists(file.path(tmp2, basename(z$ex), "file1")))
  expect_true(file.exists(file.path(tmp2, basename(z$ex), "dir")))
  expect_true(file.exists(file.path(tmp2, basename(z$ex), "dir", "file2")))

  expect_equal(readLines(file.path(tmp2, basename(z$ex), "file1")), "file1")
  expect_equal(
    readLines(file.path(tmp2, basename(z$ex), "dir", "file2")),
    "file2"
  )
})

test_that("unzip creates exdir if needed", {
  z <- make_a_zip()

  tmp2 <- test_temp_dir(create = FALSE)
  expect_false(file.exists(tmp2))
  zip::unzip(z$zip, exdir = tmp2)

  expect_true(file.exists(tmp2))

  expect_true(file.exists(file.path(tmp2, basename(z$ex), "file1")))
  expect_true(file.exists(file.path(tmp2, basename(z$ex), "dir")))
  expect_true(file.exists(file.path(tmp2, basename(z$ex), "dir", "file2")))

  expect_equal(readLines(file.path(tmp2, basename(z$ex), "file1")), "file1")
  expect_equal(
    readLines(file.path(tmp2, basename(z$ex), "dir", "file2")),
    "file2"
  )
})

test_that("unzip certain files only", {
  z <- make_a_zip()

  ## No files
  tmp2 <- test_temp_dir()
  zip::unzip(z$zip, character(), exdir = tmp2)
  expect_true(file.exists(tmp2))
  expect_equal(dir(tmp2), character())

  ## File in directory
  tmp3 <- test_temp_dir()
  zip::unzip(z$zip, paste0(basename(z$ex), "/", "file1"), exdir = tmp3)
  expect_true(file.exists(tmp3))
  expect_true(file.exists(file.path(tmp3, basename(z$ex), "file1")))
  expect_false(file.exists(file.path(tmp3, basename(z$ex), "dir")))
  expect_equal(readLines(file.path(tmp3, basename(z$ex), "file1")), "file1")

  ## Only file(s) in root
  f <- test_temp_file()
  cat("foobar\n", file = f)
  zip <- test_temp_file(".zip")
  zipr(zip, f)

  tmp4 <- test_temp_dir()
  zip::unzip(zip, paste0(basename(f)), exdir = tmp4)
  expect_true(file.exists(tmp4))
  expect_equal(dir(tmp4), basename(f))
  expect_equal(readLines(file.path(tmp4, basename(f))), "foobar")

  ## Directory only
  tmp5 <- test_temp_dir()
  zip::unzip(z$zip, paste0(basename(z$ex), "/dir/"), exdir = tmp5)
  expect_true(file.exists(tmp5))
  expect_true(file.exists(file.path(tmp5, basename(z$ex), "dir")))

  ## Files and dirs
  tmp6 <- test_temp_dir()
  zip::unzip(
    z$zip,
    paste0(basename(z$ex), c("/dir/file2", "/file1")),
    exdir = tmp6
  )

  expect_true(file.exists(file.path(tmp6, basename(z$ex), "file1")))
  expect_true(file.exists(file.path(tmp6, basename(z$ex), "dir")))
  expect_true(file.exists(file.path(tmp6, basename(z$ex), "dir", "file2")))

  expect_equal(readLines(file.path(tmp6, basename(z$ex), "file1")), "file1")
  expect_equal(
    readLines(file.path(tmp6, basename(z$ex), "dir", "file2")),
    "file2"
  )
})

test_that("unzip sets mtime correctly", {
  ## ten minutes earlier
  mtime <- Sys.time() - 60 * 10
  z <- make_a_zip(mtime = mtime)

  ## Some Windows file systems have a 2-second precision
  three <- as.difftime(3, units = "secs")
  expect_true(all(abs(zip_list(z$zip)$timestamp - mtime) < 3))

  tmp2 <- test_temp_dir()
  zip::unzip(z$zip, exdir = tmp2)

  ok <- function(...) {
    t <- file.info(file.path(tmp2, basename(z$ex), ...))$mtime
    expect_true(abs(t - mtime) < 3)
  }

  ok("file1")
  ok("file11")
  ok("dir")
  ok("dir", "file2")
  ok("dir", "file3")
})

test_that("overwrite is FALSE", {
  z <- make_a_zip()
  tmp <- test_temp_dir()
  zip::unzip(z$zip, exdir = tmp)
  zip::unzip(z$zip, exdir = tmp)
  expect_snapshot(
    error = TRUE,
    zip::unzip(z$zip, overwrite = FALSE, exdir = tmp),
    transform = function(x) {
      x <- transform_tempdir(x)
      x <- sub("test-dir-[^/]+/", "test-dir-<random>/", x)
      x <- sub("test-file-[^.]+[.]", "test-file-<random>.", x)
      x <- sub("\\", "/", x, fixed = TRUE)
      x
    }
  )
})

test_that("junkpaths is TRUE", {
  z <- make_a_zip()
  tmp <- test_temp_dir()
  zip::unzip(z$zip, exdir = tmp, junkpaths = TRUE)

  expect_true(file.exists(file.path(tmp, "file1")))
  expect_true(file.exists(file.path(tmp, "file2")))
  expect_true(file.exists(file.path(tmp, "file11")))
  expect_true(file.exists(file.path(tmp, "file3")))

  expect_false(file.exists(file.path(tmp, "dir")))

  expect_equal(readLines(file.path(tmp, "file1")), "file1")
  expect_equal(readLines(file.path(tmp, "file2")), "file2")
})

test_that("permissions as kept on Unix", {
  skip_on_os("windows")

  tmp <- test_temp_dir()
  Sys.chmod(tmp, "0777", FALSE)

  cat("foobar\n", file = f <- file.path(tmp, "file1"))
  Sys.chmod(f, "0400", FALSE)

  dir.create(f <- file.path(tmp, "dir"))
  Sys.chmod(f, "0700", FALSE)

  cat("foobar2\n", file = f <- file.path(tmp, "dir", "file2"))
  Sys.chmod(f, "0755", FALSE)

  cat("foobar3\n", file = f <- file.path(tmp, "dir", "file3"))
  Sys.chmod(f, "0777", FALSE)

  zip <- test_temp_file(".zip", create = FALSE)
  zipr(zip, tmp)

  tmp2 <- test_temp_dir()
  zip::unzip(zip, exdir = tmp2)

  check_perm <- function(mode, ...) {
    f <- file.path(tmp2, ...)
    expect_equal(file.info(f)$mode, as.octmode(mode))
  }

  check_perm("0777", basename(tmp))
  check_perm("0400", basename(tmp), "file1")
  check_perm("0700", basename(tmp), "dir")
  check_perm("0755", basename(tmp), "dir", "file2")
  check_perm("0777", basename(tmp), "dir", "file3")
})

test_that("umask if no permissions", {
  msdos <- test_path("fixtures/msdos.zip")
  dir.create(tmp <- tempfile("zip-test-noperm"))
  on.exit(unlink(tmp, recursive = TRUE), add = TRUE)

  zip::unzip(msdos, exdir = tmp)
  dsc <- file.path(tmp, "DESCRIPT")
  expect_true(file.exists(dsc))

  if (.Platform$OS.type == "unix") {
    mode <- file.mode(dsc)
    umask <- system("umask", intern = TRUE)
    expect_equal(as.integer(format(mode)) + as.integer(umask), 666)
  } else {
    expect_true(TRUE)
  }
})

test_that("symlinks on Unix", {
  skip_on_os("windows")
  symlink <- test_path("fixtures/symlink.zip")
  dir.create(tmp <- tempfile("zip-test-symlink"))
  on.exit(unlink(tmp, recursive = TRUE), add = TRUE)

  zip::unzip(symlink, exdir = tmp)
  expect_true(file.exists(file.path(tmp, "a")))
  expect_true(file.exists(file.path(tmp, "a", "foo")))
  expect_true(file.exists(file.path(tmp, "a", "bar")))
  expect_equal(
    Sys.readlink(file.path(tmp, "a", "bar")),
    "foo"
  )
})

test_that("CP437-encoded filename is decoded on extraction", {
  cp437 <- test_path("fixtures/cp437.zip")
  tmp <- test_temp_dir()

  zip::unzip(cp437, exdir = tmp)
  extracted <- list.files(tmp)
  expect_equal(extracted, "catal\u00f1n.txt")
  expect_equal(
    readLines(file.path(tmp, "catal\u00f1n.txt")),
    "CP437 filename test"
  )
})

test_that("unzip works on files with STORED comp_size=0 quirk", {
  zf <- test_path("fixtures/stored-zero-compsize.zip")
  tmp <- test_temp_dir()
  zip::unzip(zf, exdir = tmp)
  expect_equal(
    readLines(file.path(tmp, "subdir", "hello.txt")),
    "Hello from a quirky zip file!"
  )
})

test_that("unzip reads Info-ZIP forced ZIP64 (`zip -fz`)", {
  # See tools/extra/make-zip64-infozip.sh. Exercises the zero-length STORED
  # directory entries that the bundled miniz reader used to reject.
  zf <- test_path("fixtures/zip64.zip")
  tmp <- test_temp_dir()
  zip::unzip(zf, exdir = tmp)
  expect_true(dir.exists(file.path(tmp, "src", "dir")))
  expect_equal(readLines(file.path(tmp, "src", "file1")), "file1")
  expect_equal(readLines(file.path(tmp, "src", "dir", "file2")), "file2")
})

test_that("unzip() shows progress bar when zip_progress = TRUE", {
  skip_if_not_installed("cli")
  withr::local_options(zip_progress = FALSE)

  z <- make_a_zip()
  exdir <- test_temp_dir()

  withr::local_options(zip_progress = TRUE, cli.progress_show_after = -1)
  output <- capture.output(
    asNamespace("cli")$cli_with_ticks(
      zip::unzip(z$zip, exdir = exdir)
    ),
    type = "message"
  )

  expect_true(file.exists(file.path(exdir, basename(z$ex), "file1")))
  expect_match(output, "Unzipping", all = FALSE)
})

test_that("unzip() with vector zipfile extracts all archives (threaded path)", {
  z1 <- make_a_zip()
  z2 <- make_a_zip()
  exdir <- test_temp_dir()

  zip::unzip(c(z1$zip, z2$zip), exdir = exdir)

  expect_true(file.exists(file.path(exdir, basename(z1$ex), "file1")))
  expect_true(file.exists(file.path(exdir, basename(z2$ex), "file1")))
  expect_equal(readLines(file.path(exdir, basename(z1$ex), "file1")), "file1")
  expect_equal(readLines(file.path(exdir, basename(z2$ex), "file1")), "file1")
})

test_that("unzip() with vector zipfile returns combined data frame", {
  z1 <- make_a_zip()
  z2 <- make_a_zip()
  exdir <- test_temp_dir()

  result <- zip::unzip(c(z1$zip, z2$zip), exdir = exdir)

  expect_s3_class(result, "data.frame")
  expect_true(nrow(result) > 0)
  expect_true(any(grepl(basename(z1$ex), result$path)))
  expect_true(any(grepl(basename(z2$ex), result$path)))
})

test_that("unzip() with vector zipfile falls back to sequential when files= is set", {
  # Build two zips with a common internal path so files= can filter both
  f1 <- test_temp_file()
  cat("zip1", file = f1)
  f1b <- test_temp_file()
  cat("extra1", file = f1b)
  z1 <- test_temp_file(".zip", create = FALSE)
  zip(z1, c(f1, f1b), keys = c("data.txt", "extra1.txt"))

  f2 <- test_temp_file()
  cat("zip2", file = f2)
  f2b <- test_temp_file()
  cat("extra2", file = f2b)
  z2 <- test_temp_file(".zip", create = FALSE)
  zip(z2, c(f2, f2b), keys = c("data.txt", "extra2.txt"))

  exdir <- test_temp_dir()
  zip::unzip(c(z1, z2), files = "data.txt", exdir = exdir)

  expect_true(file.exists(file.path(exdir, "data.txt")))
  expect_false(file.exists(file.path(exdir, "extra1.txt")))
  expect_false(file.exists(file.path(exdir, "extra2.txt")))
})

test_that("unzip() with vector zipfile falls back to sequential when junkpaths = TRUE", {
  z1 <- make_a_zip()
  z2 <- make_a_zip()
  exdir <- test_temp_dir()

  zip::unzip(c(z1$zip, z2$zip), junkpaths = TRUE, exdir = exdir)

  expect_true(file.exists(file.path(exdir, "file1")))
  expect_true(file.exists(file.path(exdir, "file2")))
  expect_false(file.exists(file.path(exdir, basename(z1$ex))))
})

test_that("unzip() with vector zipfile falls back to sequential when encoding is set", {
  z1 <- make_a_zip()
  z2 <- make_a_zip()
  exdir <- test_temp_dir()

  zip::unzip(c(z1$zip, z2$zip), encoding = "UTF-8", exdir = exdir)

  expect_true(file.exists(file.path(exdir, basename(z1$ex), "file1")))
  expect_true(file.exists(file.path(exdir, basename(z2$ex), "file1")))
})
