tests/testthat/test-contextFind.R

# Test contextFind function

# Helper function to create temporary test files
setup_test_files <- function() {
  temp_dir <- tempdir()
  test_dir <- file.path(temp_dir, "contextFind_test")

  # Clean up if exists
  if (dir.exists(test_dir)) {
    unlink(test_dir, recursive = TRUE)
  }

  dir.create(test_dir, showWarnings = FALSE)
  dir.create(file.path(test_dir, "subdir"), showWarnings = FALSE)

  # Create test R files
  writeLines(c(
    "# Line 1",
    "# Line 2",
    "test_function <- function() {",
    "  x <- 1",
    "  y <- 2",
    "  return(x + y)",
    "}",
    "# Line 8"
  ), file.path(test_dir, "test1.R"))

  writeLines(c(
    "another_function <- function() {",
    "  test_value <- 42",
    "  return(test_value)",
    "}"
  ), file.path(test_dir, "test2.R"))

  # Create test Rmd file
  writeLines(c(
    "---",
    "title: Test",
    "---",
    "",
    "```{r}",
    "test_chunk <- TRUE",
    "```"
  ), file.path(test_dir, "test.Rmd"))

  # Create a file in subdirectory
  writeLines(c(
    "sub_function <- function() {",
    "  test_nested <- 'nested'",
    "}"
  ), file.path(test_dir, "subdir", "nested.R"))

  # Create a non-R file (should be ignored)
  writeLines(c(
    "test_value in txt file"
  ), file.path(test_dir, "ignore.txt"))

  return(test_dir)
}

test_that("contextFind finds basic matches", {
  test_dir <- setup_test_files()

  results <- contextFind("test_function", path = test_dir, contextLines = 0)

  expect_type(results, "list")
  expect_length(results, 1)
  expect_equal(results[[1]]$match_line, 3)
  expect_true(grepl("test1.R$", results[[1]]$file))
})

test_that("contextFind extracts correct context lines", {
  test_dir <- setup_test_files()

  results <- contextFind("x <- 1", path = test_dir, contextLines = 2)

  expect_length(results, 1)
  context <- results[[1]]$context
  expect_length(context, 5)  # 2 before + match + 2 after
  expect_equal(names(context), c("2", "3", "4", "5", "6"))
  expect_equal(as.character(context["4"]), "  x <- 1")
})

test_that("contextFind handles matches at file start", {
  test_dir <- setup_test_files()

  results <- contextFind("# Line 1", path = test_dir, contextLines = 2)

  expect_length(results, 1)
  context <- results[[1]]$context
  # Should only have match + 2 after (no lines before)
  expect_length(context, 3)
  expect_equal(names(context)[1], "1")
})

test_that("contextFind handles matches at file end", {
  test_dir <- setup_test_files()

  results <- contextFind("# Line 8", path = test_dir, contextLines = 2)

  expect_length(results, 1)
  context <- results[[1]]$context
  # Should only have 2 before + match (no lines after)
  expect_length(context, 3)
  expect_equal(names(context)[3], "8")
})

test_that("contextFind returns NULL with message when no matches found", {
  test_dir <- setup_test_files()

  expect_message(
    results <- contextFind("nonexistent_string", path = test_dir),
    "No matches found"
  )

  expect_null(results)
})

test_that("contextFind searches both .R and .Rmd files", {
  test_dir <- setup_test_files()

  # Search for pattern that appears in both types
  results <- contextFind("test", path = test_dir, contextLines = 0)

  expect_true(length(results) > 0)

  # Check that we have results from both file types
  file_extensions <- sapply(results, function(r) {
    tools::file_ext(r$file)
  })

  expect_true("R" %in% file_extensions)
  expect_true("Rmd" %in% file_extensions)
})

test_that("contextFind respects recursive parameter", {
  test_dir <- setup_test_files()

  # Non-recursive search should not find nested file
  results_non_recursive <- contextFind("test_nested", path = test_dir,
                                       recursive = FALSE, contextLines = 0)
  expect_null(results_non_recursive)

  # Recursive search should find nested file
  results_recursive <- contextFind("test_nested", path = test_dir,
                                   recursive = TRUE, contextLines = 0)
  expect_length(results_recursive, 1)
  expect_true(grepl("subdir", results_recursive[[1]]$file))
})

test_that("contextFind ignores non-R files", {
  test_dir <- setup_test_files()

  # Search for pattern that only exists in .txt file
  results <- contextFind("txt file", path = test_dir, contextLines = 0)

  expect_null(results)
})

test_that("contextFind handles multiple matches in same file", {
  test_dir <- setup_test_files()

  results <- contextFind("test", path = test_dir, contextLines = 0)

  expect_true(length(results) > 1)

  # Check that matches are ordered by line number within same file
  test1_results <- Filter(function(r) grepl("test1.R$", r$file), results)
  if (length(test1_results) > 1) {
    line_nums <- sapply(test1_results, function(r) r$match_line)
    expect_equal(line_nums, sort(line_nums))
  }
})

test_that("contextFind result structure is correct", {
  test_dir <- setup_test_files()

  results <- contextFind("test_function", path = test_dir, contextLines = 1)

  expect_type(results, "list")
  expect_length(results, 1)

  result <- results[[1]]
  expect_named(result, c("file", "match_line", "mtime", "context"))
  expect_type(result$file, "character")
  expect_type(result$match_line, "integer")
  expect_s3_class(result$mtime, "POSIXct")
  expect_type(result$context, "character")
  expect_true(all(names(result$context) != ""))  # Context should be named
})

test_that("contextFind throws error when no files found", {
  temp_dir <- tempfile()
  dir.create(temp_dir)

  expect_error(
    contextFind("anything", path = temp_dir),
    "No files found in path"
  )

  unlink(temp_dir, recursive = TRUE)
})

test_that("contextFind handles files with read errors gracefully", {
  test_dir <- setup_test_files()

  # This should not throw an error even if a file can't be read
  # (the function uses tryCatch)
  expect_error(
    contextFind("test", path = test_dir),
    NA
  )
})

test_that("contextFind with contextLines = 0 shows only match line", {
  test_dir <- setup_test_files()

  results <- contextFind("x <- 1", path = test_dir, contextLines = 0)

  expect_length(results, 1)
  context <- results[[1]]$context
  expect_length(context, 1)
  expect_equal(names(context), "4")
})

test_that("contextFind is case-sensitive", {
  test_dir <- setup_test_files()

  # Should find lowercase
  results_lower <- contextFind("test_function", path = test_dir, contextLines = 0)
  expect_length(results_lower, 1)

  # Should not find uppercase (function uses fixed = TRUE)
  results_upper <- contextFind("TEST_FUNCTION", path = test_dir, contextLines = 0)
  expect_null(results_upper)
})

test_that("contextFind results are sorted correctly", {
  test_dir <- setup_test_files()

  # Create files with different modification times
  Sys.sleep(0.1)
  writeLines("old_test <- 1", file.path(test_dir, "old.R"))
  Sys.sleep(0.1)
  writeLines("new_test <- 1", file.path(test_dir, "new.R"))

  results <- contextFind("test", path = test_dir, contextLines = 0)

  expect_true(length(results) > 1)

  # Results should be sorted by mtime (oldest first in internal order,
  # but displayed newest last)
  mtimes <- sapply(results, function(r) r$mtime)
  expect_true(all(diff(mtimes) >= 0))
})

test_that("contextFind handles special regex characters as literal", {
  test_dir <- setup_test_files()

  # Add a file with special characters
  writeLines(c(
    "pattern <- '.*test.*'",
    "x <- 1"
  ), file.path(test_dir, "special.R"))

  # Should find literal string (fixed = TRUE means no regex)
  results <- contextFind(".*test.*", path = test_dir, contextLines = 0)

  expect_length(results, 1)
  expect_true(grepl("special.R$", results[[1]]$file))
})

Try the contextFind package in your browser

Any scripts or data that you put into this service are public.

contextFind documentation built on Nov. 5, 2025, 6:04 p.m.