Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

name: test-coverage.yaml

permissions: read-all

jobs:
test-coverage:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

steps:
- uses: actions/checkout@v6

- uses: r-lib/actions/setup-r@v2

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::covr, any::xml2
needs: coverage

- name: Test coverage
run: |
cov <- covr::package_coverage(
quiet = FALSE,
clean = FALSE,
install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
)
covr::to_cobertura(cov)
shell: Rscript {0}

- uses: codecov/codecov-action@v7
with:
fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }}
files: ./cobertura.xml
plugins: noop
disable_search: true

- name: Show testthat output
if: always()
run: |
find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
shell: bash

- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
with:
name: coverage-test-failures
path: ${{ runner.temp }}/package
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: codriver
Title: Context-Aware AI Assistant for 'RStudio'
Version: 1.0.0
Version: 1.0.0.9000
Authors@R: person("S.A.", "van der Wulp", , "vdwulp@gmail.com", role = c("aut", "cre", "cph"))
Description: A context-aware AI assistant for 'RStudio' that works directly in
the source editor without switching context or opening a separate chat
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# codriver (development version)

* Increased test coverage to 100%.

# codriver 1.0.0

* Initial CRAN submission.
4 changes: 4 additions & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@

#' @noRd
.onLoad <- function(libname, pkgname) {
# nocov start - no test coverage for this function

# Create a private environment each time the package is loaded
.codriver_env <<- new.env(parent = emptyenv())

# Initialize config slot
.codriver_env$config <- read_config()

# nocov end
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![CRAN status](https://www.r-pkg.org/badges/version/codriver)](https://CRAN.R-project.org/package=codriver)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/vdwulp/codriver/blob/master/LICENSE.md)
[![R-CMD-check](https://github.com/vdwulp/codriver/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/vdwulp/codriver/actions/workflows/R-CMD-check.yaml)
[![Codecov test coverage](https://codecov.io/gh/vdwulp/codriver/branch/main/graph/badge.svg)](https://app.codecov.io/gh/vdwulp/codriver/tree/main)
<!-- badges: end -->

*A context-aware AI assistant for RStudio that is literally at your fingertips.*
Expand Down
27 changes: 27 additions & 0 deletions tests/testthat/test-build_prompt.R
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,33 @@ test_that("build_prompt - preamble reports no library calls when none present",
})



# ── Line truncation ───────────────────────────────────────────────────────────

test_that("build_prompt - before: truncates to n_lines from end when context exceeds limit", {
# 35 lines > 30-line limit; only last 30 should appear in Code window
ctx <- make_context(c(paste0("line", seq_len(35L)), ""))
action <- make_action("continue", position = pos(36L, 1L))
bp_mocks({
result <- bp(ctx, action)
expect_match(result$user, "line35")
expect_false(grepl("line1\\b", result$user))
})
})

test_that("build_prompt - after: truncates to n_lines from start when context exceeds limit", {
# position at line 1; 15 lines after > 10-line limit for after()
ctx <- make_context(c("x <- 1", paste0("after", seq_len(15L))))
action <- make_action("edit", text = "x <- 1", position = pos(1L, 7L),
range = rng(1L, 1L, 1L, 7L))
bp_mocks({
result <- bp(ctx, action)
expect_match(result$user, "after1")
expect_false(grepl("after15", result$user))
})
})


# ── Block formatting ──────────────────────────────────────────────────────────

test_that("build_prompt - blocks use START/END delimiters", {
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-build_prompt_workspace.R
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ test_that("describe_columns - detects suffix pattern when columns share a last t

test_that("describe_columns - suffix owned by a single prefix group is discarded", {
# All _usd columns start with "amt" -> single prefix owner -> discard
result <- dc(c("amt_usd_a", "amt_usd_b", "other_eur"))
result <- dc(c("amt_a_usd", "amt_b_usd", "other_eur"))
expect_false(grepl("\\*_usd", result))
})

Expand Down
23 changes: 23 additions & 0 deletions tests/testthat/test-configure.R
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,26 @@ test_that("is_shortcut_registered - returns empty string when codriver::codriver
local_mocked_bindings(rstudio_config_path = function(...) tmp, .package = "codriver")
expect_equal(is_shortcut_registered(), "")
})


# ── register_shortcut ─────────────────────────────────────────────────────────

test_that("register_shortcut - returns TRUE when shortcut is registered after call", {
with_mocked_bindings(
use_rstudio_keyboard_shortcut = function(...) invisible(NULL),
is_shortcut_registered = function() "Ctrl+Shift+P",
{
expect_true(register_shortcut("Ctrl+Shift+P"))
}
)
})

test_that("register_shortcut - returns FALSE when shortcut is not registered after call", {
with_mocked_bindings(
use_rstudio_keyboard_shortcut = function(...) invisible(NULL),
is_shortcut_registered = function() NULL,
{
expect_false(register_shortcut("Ctrl+Shift+P"))
}
)
})
45 changes: 45 additions & 0 deletions tests/testthat/test-resolve_action.R
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ test_that("scan_lines - ignores # inside raw string with padding r\"-(...)- \"",
expect_equal(scan_col(c('x <- r"-(# not a comment)-"'), 1L), -1L)
})

test_that("scan_lines - ignores # inside raw string with multi-dash padding", {
expect_equal(scan_col(c('x <- r"---(# not a comment)---"'), 1L), -1L)
})

test_that("scan_lines - detects comment after closed raw string", {
expect_equal(scan_col(c('x <- r"(text)" # comment'), 1L), 16L)
})
Expand Down Expand Up @@ -345,13 +349,28 @@ test_that("resolve_action - multi-line selection ending at col 1 trims trailing
expect_false(endsWith(result$text, "\n"))
})

test_that("resolve_action - multi-line code selection where anchor is start row with inline comment -> comment", {
ctx <- make_selection_context(c("x <- 1 # old", " "), 1L, 8L, 2L, 2L)
result <- ra(ctx)
expect_equal(result$mode, "comment")
})

test_that("resolve_action - multi-line comment selection -> generate from contiguous block", {
ctx <- make_selection_context(c("# line one", "# line two", "x <- 1"), 1L, 1L, 2L, 11L)
result <- ra(ctx)
expect_equal(result$mode, "generate")
expect_equal(result$text, "line one\nline two")
})

test_that("resolve_action - multi-line comment selection with non-comment row above anchor stops block at code row", {
# row 1 is code, rows 2-3 are comments; selection covers all three
# anchor = row 3 (comment), walk up hits row 1 (code) -> break -> block is rows 2-3 only
ctx <- make_selection_context(c("x <- 1", "# line one", "# line two"), 1L, 1L, 3L, 11L)
result <- ra(ctx)
expect_equal(result$mode, "generate")
expect_equal(result$text, "line one\nline two")
})

test_that("resolve_action - multi-line selection with only whitespace -> continue", {
ctx <- make_selection_context(c("", ""), 1L, 1L, 2L, 1L)
result <- ra(ctx)
Expand All @@ -362,6 +381,32 @@ test_that("resolve_action - multi-line whitespace selection with comment start l
ctx <- make_selection_context(c("# instruction", " ", " "), 1L, 1L, 3L, 1L)
result <- ra(ctx)
expect_equal(result$mode, "generate")
expect_equal(result$text, "instruction")
})

test_that("resolve_action - multi-line whitespace selection with comment start line, anchor in whitespace -> generate", {
ctx <- make_selection_context(c("# instruction ", " "), 1L, 15L, 2L, 2L)
result <- ra(ctx)
expect_equal(result$mode, "generate")
expect_equal(result$text, "instruction")
})

test_that("resolve_action - multi-line whitespace selection with code start line, anchor at comment column -> comment", {
ctx <- make_selection_context(c("x <- 1 # note", " "), 1L, 8L, 2L, 2L)
result <- ra(ctx)
expect_equal(result$mode, "comment")
})

test_that("resolve_action - multi-line whitespace selection with code start line, anchor past inline comment -> comment", {
ctx <- make_selection_context(c("x <- 1 # note ", " "), 1L, 15L, 2L, 2L)
result <- ra(ctx)
expect_equal(result$mode, "comment")
})

test_that("resolve_action - multi-line whitespace selection with code start line -> complete", {
ctx <- make_selection_context(c("x <- 1 ", " "), 1L, 8L, 2L, 2L)
result <- ra(ctx)
expect_equal(result$mode, "complete")
})


Expand Down