test_that("empty columns create an empty file", {
  out <- tempfile()

  no_cols <- mtcars[, FALSE]
  no_rows_or_cols <- mtcars[FALSE, FALSE]

  expect_equal(vroom_write(no_cols, out), no_cols)
  expect_true(file.exists(out))
  unlink(out)

  expect_equal(vroom_write(no_rows_or_cols, out), no_rows_or_cols)
  expect_true(file.exists(out))
})

test_that("empty rows print the headers", {
  out <- tempfile()
  on.exit(unlink(out))

  no_rows <- mtcars[FALSE, ]

  expect_equal(vroom_write(no_rows, out), no_rows)

  expect_equal(strsplit(readLines(out), "\t")[[1]], colnames(mtcars))
})

test_that("strings are only quoted if needed", {
  x <- c("a", ',')

  csv <- vroom_format(data.frame(x), delim = ",", col_names = FALSE)
  expect_equal(csv, 'a\n\",\"\n')
  ssv <- vroom_format(data.frame(x), delim = " ", col_names = FALSE)
  expect_equal(ssv, 'a\n,\n')
})

test_that("read_delim/csv/tsv and write_delim round trip special chars", {
  x <- stats::setNames(
    list("a", '"', ",", "\n", "at\t"),
    paste0("V", seq_len(5))
  )

  output <- tibble::as_tibble(x)
  output_space <- vroom(
    I(vroom_format(output, delim = " ")),
    trim_ws = FALSE,
    progress = FALSE,
    col_types = list()
  )
  output_csv <- vroom(
    I(vroom_format(output, delim = ",")),
    trim_ws = FALSE,
    progress = FALSE,
    col_types = list()
  )
  output_tsv <- vroom(
    I(vroom_format(output, delim = "\t")),
    trim_ws = FALSE,
    progress = FALSE,
    col_types = list()
  )
  expect_equal(output_space, output)
  expect_equal(output_csv, output)
  expect_equal(output_tsv, output)
})

test_that("special floating point values translated to text", {
  df <- data.frame(x = c(NaN, NA, Inf, -Inf))
  expect_equal(vroom_format(df), "x\nNA\nNA\nInf\n-Inf\n")
})

test_that("NA integer values translated to text", {
  df <- data.frame(x = c(NA, 1L, 5L, 1234567890L))
  expect_equal(vroom_format(df), "x\nNA\n1\n5\n1234567890\n")
})

test_that("logical values give long names", {
  df <- data.frame(x = c(NA, FALSE, TRUE))
  expect_equal(vroom_format(df), "x\nNA\nFALSE\nTRUE\n")
})

test_that("roundtrip preserved floating point numbers", {
  input <- data.frame(x = runif(100))
  output <- vroom(
    I(vroom_format(input, delim = " ")),
    delim = " ",
    col_types = list()
  )

  expect_equal(input$x, output$x)
})

test_that("roundtrip preserves dates and datetimes", {
  x <- as.Date("2010-01-01") + 1:10
  y <- as.POSIXct(x)
  attr(y, "tzone") <- "UTC"

  input <- data.frame(x, y)
  output <- vroom(
    I(vroom_format(input, delim = " ")),
    delim = " ",
    col_types = list()
  )

  expect_equal(output$x, x)
  expect_equal(output$y, y)
})

test_that("fails to create file in non-existent directory", {
  expect_snapshot(
    vroom_write(mtcars, file.path(tempdir(), "x", "y"), "\t"),
    error = TRUE,
    transform = function(x) {
      # Scrub any path before x/y (works on all platforms)
      x <- gsub("\\\\", "/", x) # Normalize backslashes first
      x <- gsub("'[^']+/(?=x/y)", "'<temp>/", x, perl = TRUE)
      x
    }
  )
})

test_that("includes a byte order mark if desired", {
  tmp <- tempfile()
  on.exit(unlink(tmp))

  vroom_write(mtcars, tmp, bom = TRUE)

  output <- readBin(tmp, "raw", file.info(tmp)$size)

  # BOM is there
  expect_equal(output[1:3], charToRaw("\xEF\xBB\xBF"))

  # Rest of file also there
  expect_equal(output[4:6], charToRaw("mpg"))
})


test_that("does not writes a trailing .0 for whole number doubles", {
  expect_equal(vroom_format(tibble::tibble(x = 1)), "x\n1\n")

  expect_equal(vroom_format(tibble::tibble(x = 0)), "x\n0\n")

  expect_equal(vroom_format(tibble::tibble(x = -1)), "x\n-1\n")

  expect_equal(vroom_format(tibble::tibble(x = 999)), "x\n999\n")

  expect_equal(vroom_format(tibble::tibble(x = -999)), "x\n-999\n")

  expect_equal(vroom_format(tibble::tibble(x = 123456789)), "x\n123456789\n")

  expect_equal(vroom_format(tibble::tibble(x = -123456789)), "x\n-123456789\n")
})

test_that("can write windows eol characters if desired (#263)", {
  expect_equal(vroom_format(tibble::tibble(x = 1), eol = "\r\n"), "x\r\n1\r\n")
})

test_that("write_csv can write to compressed files", {
  mt <- vroom(vroom_example("mtcars.csv"), col_types = list())

  filename <- file.path(tempdir(), "mtcars.csv.bz2")
  on.exit(unlink(filename))
  vroom_write(mt, filename)

  is_bz2_file <- function(x) {
    # Magic number for bz2 is "BZh" in ASCII
    # https://en.wikipedia.org/wiki/Bzip2#File_format
    identical(charToRaw("BZh"), readBin(x, n = 3, what = "raw"))
  }

  expect_true(is_bz2_file(filename))

  expect_equal(vroom(filename, col_types = list()), mt)
})

test_that("write_csv writes large integers without scientific notation #671", {
  x <- data.frame(a = c(60150001022000, 60150001022001))
  expect_equal(vroom_format(x), "a\n60150001022000\n60150001022001\n")
})

test_that("write_csv writes large integers without scientific notation up to 1E15 #671", {
  x <- data.frame(a = c(1E13, 1E14, 1E15, 1E16))
  expect_equal(
    vroom_format(x),
    "a\n10000000000000\n100000000000000\n1e15\n1e16\n"
  )
})

#test_that("write_csv2 and format_csv2 writes ; sep and , decimal mark", {
#df <- tibble::tibble(x = c(0.5, 2, 1.2), y = c("a", "b", "c"))
#expect_equal(format_csv2(df), "x;y\n0,5;a\n2,0;b\n1,2;c\n")

#filename <- tempfile(pattern = "readr", fileext = ".csv")
#on.exit(unlink(filename))
#write_csv2(df, filename)

#expect_equivalent(df, suppressMessages(read_csv2(filename)))
#})

#test_that("write_csv2 and format_csv2 writes NA appropriately", {
#df <- tibble::tibble(x = c(0.5, NA, 1.2), y = c("a", "b", NA))
#expect_equal(format_csv2(df), "x;y\n0,5;a\nNA;b\n1,2;NA\n")
#})

test_that("Can change the escape behavior for quotes", {
  df <- data.frame(x = c("a", '"', ",", "\n"))

  expect_snapshot(error = TRUE, {
    vroom_format(df, "\t", escape = "invalid")
  })

  expect_equal(vroom_format(df, "\t"), 'x\na\n""""\n,\n"\n"\n')
  expect_equal(
    vroom_format(df, "\t", escape = "double"),
    "x\na\n\"\"\"\"\n,\n\"\n\"\n"
  )
  expect_equal(
    vroom_format(df, "\t", escape = "backslash"),
    "x\na\n\"\\\"\"\n,\n\"\n\"\n"
  )
  expect_equal(
    vroom_format(df, "\t", escape = "none"),
    "x\na\n\"\"\"\n,\n\"\n\"\n"
  )
})

test_that("hms NAs are written without padding (#930)", {
  df <- data.frame(x = hms::as_hms(c(NA, 34.234)))
  expect_equal(vroom_format(df), "x\nNA\n00:00:34.234\n")
})

test_that("vroom_write equals the same thing as vroom_format", {
  df <- gen_tbl(100, 8, col_types = c("dilfcDtT"), missing = .1)
  tf <- tempfile()
  on.exit(unlink(tf))

  # Temporarily run with 2 lines per buffer, to test the multithreading
  withr::with_envvar(
    c("VROOM_WRITE_BUFFER_LINES" = "2"),
    vroom_write(df, tf, "\t")
  )

  expect_equal(readChar(tf, file.info(tf)$size), vroom_format(df))
})

test_that("vroom_format handles empty data frames", {
  df <- data.frame()
  expect_equal(vroom_format(df), "")

  df <- data.frame(a = 1:2, b = 2:3)
  df <- df[0, ]
  expect_equal(vroom_format(df), "a\tb\n")
})

test_that("vroom_write() / vroom_read() roundtrips an empty data frame", {
  df <- tibble::tibble()
  t <- tempfile(fileext = ".csv")
  on.exit(unlink(t))

  vroom_write(df, t)
  expect_equal(vroom(t, show_col_types = FALSE), df)
})

test_that("vroom_write(append = TRUE) works with R connections", {
  df <- data.frame(x = 1, y = 2)
  f <- tempfile(fileext = ".tsv.gz")
  on.exit(unlink(f))

  vroom::vroom_write(df, f)
  vroom::vroom_write(df, f, append = TRUE)

  expect_equal(vroom_lines(f), c("x\ty", "1\t2", "1\t2"))
})

test_that("vroom_write() works with an empty delimiter", {
  df <- data.frame(x = "foo", y = "bar")

  f <- tempfile(fileext = ".tsv.gz")
  on.exit(unlink(f))

  vroom::vroom_write(df, f, delim = "")
  expect_equal(vroom_lines(f), c("xy", "foobar"))
})

test_that("vroom_write_lines() works with empty", {
  f <- tempfile(fileext = ".txt")
  on.exit(unlink(f))

  vroom::vroom_write_lines(character(), f)
  expect_equal(vroom_lines(f), character())
})

test_that("vroom_write_lines() works with normal input", {
  f <- tempfile(fileext = ".txt")
  on.exit(unlink(f))

  vroom::vroom_write_lines(c("foo", "bar"), f)
  expect_equal(vroom_lines(f), c("foo", "bar"))
})

test_that("vroom_write_lines() does not escape or quote lines", {
  f <- tempfile(fileext = ".txt")
  on.exit(unlink(f))

  vroom::vroom_write_lines(c('"foo"', "bar"), f)
  expect_equal(vroom_lines(f), c('"foo"', "bar"))
})

test_that("vroom_write() always outputs in UTF-8", {
  x <- iconv(c("ao\u00FBt", "\u00E9l\u00E8ve", "\u00E7a va"), "UTF-8", "latin1")

  data <- tibble::tibble(X = x)
  names(data) <- iconv("fran\u00E7aise", "UTF-8", "latin1")

  f <- tempfile()
  vroom_write(data, f, delim = ",")

  expected_data <- charToRaw(
    paste0(
      collapse = "\n",
      c(
        enc2utf8(names(data)),
        enc2utf8(data[[1]]),
        ""
      )
    )
  )

  actual_data <- readBin(f, "raw", file.size(f))

  expect_equal(actual_data, expected_data)
})

test_that("vroom_format() does not quote strings that start with the `na` string (#426)", {
  names_df <- tibble::tibble(x = c(NA, "NA", "NATHAN", "JONAH"))

  simple_output <- vroom_format(names_df)
  expect_equal(
    simple_output,
    "x\nNA\nNA\nNATHAN\nJONAH\n"
  )

  output_unique_NA <- vroom_format(names_df, na = "JON")
  expect_equal(
    output_unique_NA,
    "x\nJON\nNA\nNATHAN\nJONAH\n"
  )
})

test_that("na argument modifies how missing values are written", {
  df <- data.frame(x = c(NA, "x"), y = c(1, 2))
  expect_equal(vroom_format(df, ",", na = "None"), "x,y\nNone,1\nx,2\n")
})

test_that("vroom_write() does not overwrite file when appending empty data frame", {
  tf <- withr::local_tempfile()
  data <- tibble::tibble(
    a = "1",
    b = "2",
    c = "3"
  )

  vroom_write(data, file = tf, delim = ",")
  vroom_write(data.frame(), file = tf, append = TRUE, delim = ",")

  expect_equal(vroom_lines(tf, altrep = FALSE), c("a,b,c", "1,2,3"))
})
