1212PROCESS_DONE <- 200L
1313PROCESS_STARTED <- 201L
1414PROCESS_MSG <- 301L
15+ PROCESS_OUTPUT <- 302L
1516PROCESS_EXITED <- 500L
1617PROCESS_CRASHED <- 501L
1718PROCESS_CLOSED <- 502L
@@ -50,7 +51,9 @@ task_q <- R6::R6Class(
5051 state = " waiting" ,
5152 fun = I(list (fun )),
5253 args = I(list (args )),
53- worker = I(list (NULL ))
54+ worker = I(list (NULL )),
55+ path = args [[1 ]],
56+ startup = I(list (NULL ))
5457 )
5558 private $ schedule()
5659 invisible (id )
@@ -62,15 +65,49 @@ task_q <- R6::R6Class(
6265 if (x == Inf ) - 1 else as.integer(as.double(x , " secs" ) * 1000 )
6366 }
6467 repeat {
68+ pr <- vector(mode = " list" , nrow(private $ tasks ))
6569 topoll <- which(private $ tasks $ state == " running" )
66- conns <- lapply (
70+ pr [ topoll ] <- processx :: poll (
6771 private $ tasks $ worker [topoll ],
68- function ( x ) x $ get_poll_connection( )
72+ as_ms( timeout )
6973 )
70- pr <- processx :: poll(conns , as_ms(timeout ))
71- ready <- topoll [pr == " ready" ]
72- results <- lapply(ready , function (i ) {
73- msg <- private $ tasks $ worker [[i ]]$ read()
74+ results <- lapply(seq_along(pr ), function (i ) {
75+ # nothing from this worker?
76+ if (is.null(pr [[i ]]) || all(pr [[i ]] != " ready" )) {
77+ return ()
78+ }
79+
80+ # there is a testthat message?
81+ worker <- private $ tasks $ worker [[i ]]
82+ msg <- if (pr [[i ]][[" process" ]] == " ready" ) {
83+ worker $ read()
84+ }
85+
86+ # there is an output message?
87+ has_output <- pr [[i ]][[" output" ]] == " ready" ||
88+ pr [[i ]][[" error" ]] == " ready"
89+ outmsg <- NULL
90+ if (has_output ) {
91+ lns <- c(worker $ read_output_lines(), worker $ read_error_lines())
92+ inc <- paste0(worker $ read_output(), worker $ read_error())
93+ if (nchar(inc )) {
94+ lns <- c(lns , strsplit(inc , " \n " , fixed = TRUE )[[1 ]])
95+ }
96+ # startup message?
97+ if (is.na(private $ tasks $ path [i ])) {
98+ private $ tasks $ startup [[i ]] <- c(private $ tasks $ startup [[i ]], lns )
99+ } else {
100+ outmsg <- structure(
101+ list (
102+ code = PROCESS_OUTPUT ,
103+ message = lns ,
104+ path = private $ tasks $ path [i ]
105+ ),
106+ class = " testthat_message"
107+ )
108+ }
109+ }
110+
74111 # # TODO: why can this be NULL?
75112 if (is.null(msg ) || msg $ code == PROCESS_MSG ) {
76113 private $ tasks $ state [[i ]] <- " running"
@@ -100,9 +137,10 @@ task_q <- R6::R6Class(
100137 class = c(" testthat_process_error" , " testthat_error" )
101138 )
102139 }
103- msg
140+ compact( list ( msg , outmsg ))
104141 })
105- results <- results [! map_lgl(results , is.null )]
142+ # single list for all workers
143+ results <- compact(unlist(results , recursive = FALSE ))
106144
107145 private $ schedule()
108146 if (is.finite(timeout )) {
@@ -132,9 +170,11 @@ task_q <- R6::R6Class(
132170 state = " running" ,
133171 fun = nl ,
134172 args = nl ,
135- worker = nl
173+ worker = nl ,
174+ path = NA_character_ ,
175+ startup = nl
136176 )
137- rsopts <- callr :: r_session_options(... )
177+ rsopts <- callr :: r_session_options(stdout = " | " , stderr = " | " , ... )
138178 for (i in seq_len(concurrency )) {
139179 rs <- callr :: r_session $ new(rsopts , wait = FALSE )
140180 private $ tasks $ worker [[i ]] <- rs
@@ -176,7 +216,10 @@ task_q <- R6::R6Class(
176216 file <- private $ tasks $ args [[task_no ]][[1 ]]
177217 if (is.null(fun )) {
178218 msg $ error $ stdout <- msg $ stdout
179- msg $ error $ stderr <- msg $ stderr
219+ msg $ error $ stderr <- paste(
220+ c(private $ tasks $ startup [[task_no ]], msg $ stderr ),
221+ collapse = " \n "
222+ )
180223 abort(
181224 paste0(
182225 " testthat subprocess failed to start, stderr:\n " ,
0 commit comments