-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathtest-exercise.R
More file actions
executable file
·114 lines (106 loc) · 4.27 KB
/
Copy pathtest-exercise.R
File metadata and controls
executable file
·114 lines (106 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#' Run all tests for an exercise
#'
#' Run all tests for an exercise and report the results (including feedback).
#' This function is run by R Backend and should not be used by course creators.
#'
#' @param sct Submission correctness tests as a character string.
#' @param ex_type Type of the exercise
#' @param pec pre-exercise-code
#' @param student_code character string representing the student code
#' @param solution_code character string representing the solution code
#' @param student_env environment containing the objects defined by the student.
#' @param solution_env environment containing the objects defined by solution code
#' @param output_list the output structure that is generated by RBackend
#' @param allow_errors whether or not errors are allowed by RBackend (FALSE by default)
#' @param force_diagnose whether diagnose tests have to pass even if the checks pass (FALSE by default)
#' @param seed random seed that is used for SCTs that run expressions (42 by default).
#'
#' @return A list with components \code{passed} that indicates whether all
#' tests were sucessful, and \code{feedback} that contains a feedback message.
#'
#' @export
test_exercise <- function(sct,
ex_type,
pec,
student_code,
solution_code,
student_env,
solution_env,
output_list,
allow_errors = FALSE,
force_diagnose = FALSE,
seed = 42) {
# backwards compatibility with older versions of RBackend
if (missing(student_env)) {
student_env <- globalenv()
}
# First check if parsing worked out
if (any(sapply(output_list, `[[`, "type") == "parse-error")) {
report <- tryCatch(do_parse(student_code),
error = function(e) {
list(message = parse_fallback_msg)
})
return(c(list(correct = FALSE), report))
} else {
# Store everything that's needed locally (initialize does a full reset)
tw$clear()
tw$set(success_msg = sample(c("Good Job!", "Well done!", "Great work!"), 1))
state <- RootState$new(pec = pec,
student_code = student_code,
student_pd = build_pd(student_code),
student_env = student_env,
solution_code = solution_code,
solution_pd = build_pd(solution_code),
solution_env = solution_env,
output_list = output_list,
test_env = new.env(parent = environment()),
force_diagnose = force_diagnose)
tw$set(state = state,
stack = TRUE,
seed = seed)
on.exit(tw$clear())
# Execute sct with the DataCamp reporter such that it collects test results
res <- run_until_fail(parse(text = sct))
# If the SCT passed, check whether there is an error as the last step
if (isTRUE(res$correct) && !allow_errors) {
res <- run_until_fail(ex() %>% check_error())
}
return(post_process(res, ex_type))
}
}
#' Run SCT until it fails
#'
#' @param code the SCT script to run as an expression
#' @param envir environment in which to execute the SCT script
#'
#' @export
run_until_fail <- function(code, envir = NULL) {
tryCatch({
if (is.null(envir)) {
envir <- tw$get("state")$get("test_env")
}
# Run the SCT
eval(code, envir = envir)
# If it got here, the SCT passed
return(list(correct = TRUE, message = tw$get("success_msg")))
}, sct_failure = function(e) {
return(list(correct = FALSE,
message = e$message,
feedback = attr(e, "feedback")))
})
}
post_process <- function(res, ex_type) {
# convert to HTML
res$message <- to_html(res$message)
# Only add line info if:
# - message is incorrect, and
# - exercise is not markdown / rcpp (post-processing of code chunks gives strange things)
if (!res$correct && !(ex_type %in% c("MarkdownExercise", "RCppExercise"))) {
line_info <- get_line_info(res$feedback)
if (!is.null(line_info)) {
res <- c(res, line_info)
}
}
res$feedback <- NULL
return(res)
}