Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move error checking from render_exercise() to evaluate_exercise() #544

Merged
merged 25 commits into from Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4503f76
Move exercise error checking code into `evaluate_exercise()` from `re…
rossellhayes Jun 22, 2021
259a082
Remove breadcrumbs
rossellhayes Jun 22, 2021
ca144c9
Use more specific error class for render errors
rossellhayes Jun 24, 2021
c3c38ae
Simplify test
rossellhayes Jun 24, 2021
aa6abd7
Use `rlang::catch_cnd()` instead of `tryCatch()`
rossellhayes Jun 24, 2021
0cb3dfd
Run code checks before creating temp dir
rossellhayes Jun 24, 2021
bcd5dc3
Use `local_dir()` instead of `with_dir()` in `evaluate_exercise()`
rossellhayes Jun 24, 2021
18b0fa0
Simplify creation of tempdir
rossellhayes Jun 24, 2021
6e19e4a
Modify test to check `render_exercise()` and `evaluate_exercise()`
rossellhayes Jun 24, 2021
f04bdd4
Fix bug in returning error message
rossellhayes Jun 25, 2021
ea1675b
Update tests of `render_exercise()` and transfer tests to `evaluate_e…
rossellhayes Jun 25, 2021
14d058e
Fix merge conflicts
rossellhayes Jul 9, 2021
939eda6
Build docs (GitHub Actions)
rossellhayes Jul 9, 2021
c0ba8a1
Restore data directory feature
gadenbuie Jul 9, 2021
f56e82e
Fix merge conflict
rossellhayes Jul 9, 2021
cd6e8f7
Fix merge conflict
rossellhayes Jul 9, 2021
c0149cf
Restore withr::local_tempdir()
gadenbuie Jul 9, 2021
3365c8e
Fix comment about purpose of render_exercise()
gadenbuie Jul 9, 2021
c5e8a89
Rename error object in render_exercise error
gadenbuie Jul 9, 2021
ee19db2
Add test for exercises that exceed the timelimt
gadenbuie Jul 9, 2021
9dab33f
More specific tests for presence of error_check code
gadenbuie Jul 9, 2021
bd095ba
Standardize exercise checking-related code to length-1 string
gadenbuie Jul 9, 2021
a4030f0
Skip timelimit test on windows
gadenbuie Jul 9, 2021
be678f4
Factor out `standardize_code()`
gadenbuie Jul 9, 2021
287aca3
Add NEWS item
gadenbuie Jul 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 26 additions & 29 deletions R/exercise.R
Expand Up @@ -294,12 +294,6 @@ evaluate_exercise <- function(exercise, envir, evaluate_global_setup = FALSE) {
eval(parse(text = exercise$global_setup), envir = envir)
}


# Setup a temporary directory for rendering the exercise
exercise_dir <- tempfile(pattern = "lnr-ex")
dir.create(exercise_dir)
on.exit(unlink(exercise_dir), add = TRUE)

checker_feedback <- NULL
# Run the checker pre-evaluation _if_ there is code checking to do
if (length(exercise$code_check)) {
Expand All @@ -317,31 +311,34 @@ evaluate_exercise <- function(exercise, envir, evaluate_global_setup = FALSE) {
}
}

# Setup a temporary directory for rendering the exercise
exercise_dir <- tempfile(pattern = "lnr-ex")
dir.create(exercise_dir)
withr::local_dir(exercise_dir)
on.exit(unlink(exercise_dir), add = TRUE)
rossellhayes marked this conversation as resolved.
Show resolved Hide resolved

# Resolve knitr options for the exercise and setup chunks
rmd_results <- withr::with_dir(
exercise_dir,
tryCatch(
render_exercise(exercise, envir),
error = function(e) {
if (length(exercise$error_check)) {
# Run the condition through an error checker
# (the exercise could be to throw an error!)
checker_feedback <- try_checker(
exercise, "exercise.checker",
check_code = exercise$error_check,
envir_result = e$envir_result,
evaluate_result = e$evaluate_result,
envir_prep = e$envir_prep,
last_value = e$e,
engine = exercise$engine
)
if (is_exercise_result(checker_feedback)) {
return(checker_feedback)
}
rmd_results <- tryCatch(
render_exercise(exercise, envir),
error = function(e) {
if (length(exercise$error_check)) {
# Run the condition through an error checker
# (the exercise could be to throw an error!)
checker_feedback <- try_checker(
exercise, "exercise.checker",
check_code = exercise$error_check,
envir_result = e$envir_result,
evaluate_result = e$evaluate_result,
envir_prep = e$envir_prep,
last_value = e$e,
engine = exercise$engine
)
if (is_exercise_result(checker_feedback)) {
return(checker_feedback)
}
exercise_result_error(e$msg)
}
)
exercise_result_error(e$msg)
}
)

if (is_exercise_result(rmd_results)) {
Expand Down Expand Up @@ -570,7 +567,7 @@ render_exercise <- function(exercise, envir) {
return(exercise_result_timeout())
}
rlang::abort(
class = "learnr_exercise_result",
class = "learnr_render_exercise_error",
envir_result = envir_result,
evaluate_result = evaluate_result,
envir_prep = envir_prep,
Expand Down
40 changes: 19 additions & 21 deletions tests/testthat/test-exercise.R
Expand Up @@ -141,7 +141,9 @@ test_that("render_exercise() returns identical envir_prep and envir_result if an
)

exercise_result <- withr::with_tempdir(
tryCatch(render_exercise(exercise, new.env()), error = identity)
rlang::catch_cnd(
render_exercise(exercise, new.env()), "learnr_render_exercise_error"
)
)

expect_s3_class(exercise_result$last_value, "simpleError")
Expand All @@ -161,7 +163,9 @@ test_that("render_exercise() returns envir_result up to error", {
)

exercise_result <- withr::with_tempdir(
tryCatch(render_exercise(exercise, new.env()), error = identity)
rlang::catch_cnd(
render_exercise(exercise, new.env()), "learnr_render_exercise_error"
)
)

expect_s3_class(exercise_result$last_value, "simpleError")
Expand All @@ -181,17 +185,21 @@ test_that("render_exercise() with errors and no checker returns exercise result
)

exercise_result <- withr::with_tempdir(
tryCatch(render_exercise(exercise, new.env()), error = identity)
rlang::catch_cnd(
render_exercise(exercise, new.env()), "learnr_render_exercise_error"
)
)
expect_s3_class(exercise_result, "learnr_exercise_result")
expect_s3_class(exercise_result, "learnr_render_exercise_error")
expect_identical(exercise_result$error_message, "setup")
expect_null(exercise_result$feedback)

exercise <- mock_exercise(user_code = "stop('user')")
exercise_result <- withr::with_tempdir(
tryCatch(render_exercise(exercise, new.env()), error = identity)
rlang::catch_cnd(
render_exercise(exercise, new.env()), "learnr_render_exercise_error"
)
)
expect_s3_class(exercise_result, "learnr_exercise_result")
expect_s3_class(exercise_result, "learnr_render_exercise_error")
expect_identical(exercise_result$error_message, "user")
expect_null(exercise_result$feedback)
})
Expand Down Expand Up @@ -229,22 +237,14 @@ test_that("render_exercise() cleans up exercise_prep files even when setup fails
files <- expect_message(
withr::with_tempdir({
before <- dir()
e <- tryCatch(render_exercise(exercise, new.env()), error = identity)
res <- try_checker(
exercise, "exercise.checker",
check_code = exercise$error_check,
envir_result = e$envir_result,
evaluate_result = e$evaluate_result,
envir_prep = e$envir_prep,
last_value = e$e,
engine = exercise$engine
e <- rlang::catch_cnd(
render_exercise(exercise, new.env()), "learnr_render_exercise_error"
)

list(
before = before,
before_error = get("dir_setup", res$feedback$checker_args$envir_prep),
during = res$feedback$checker_result,
after = dir()
during = get("dir_setup", e$envir_prep),
after = dir()
)
}),
"exercise_prep.Rmd"
Expand All @@ -253,9 +253,7 @@ test_that("render_exercise() cleans up exercise_prep files even when setup fails
# start with nothing
expect_identical(files$before, character(0))
# prep file is present while evaluating prep
expect_identical(files$before_error, "exercise_prep.Rmd")
# prep files are cleaned up after error
expect_identical(files$during, character(0))
expect_identical(files$during, "exercise_prep.Rmd")
# nothing in directory after render_exercise() because user code didn't evaluate
expect_identical(files$after, character(0))
})
Expand Down