1 2context("common") 3 4test_that("create self process", { 5 expect_error(ps_handle("foobar"), class = "invalid_argument") 6 expect_error(ps_handle(time = 123), class = "invalid_argument") 7 8 ps <- ps_handle() 9 expect_identical(ps_pid(ps), Sys.getpid()) 10}) 11 12test_that("format", { 13 ps <- ps_handle() 14 expect_match(format(ps), format_regexp()) 15}) 16 17test_that("print", { 18 ps <- ps_handle() 19 expect_output(print(ps), format_regexp()) 20}) 21 22test_that("pid", { 23 ## Argument check 24 expect_error(ps_pid(123), class = "invalid_argument") 25 26 ## Self 27 ps <- ps_handle() 28 expect_identical(ps_pid(ps), Sys.getpid()) 29 30 ## Child 31 p1 <- processx::process$new(px(), c("sleep", "10")) 32 on.exit(p1$kill(), add = TRUE) 33 ps <- ps_handle(p1$get_pid()) 34 expect_identical(ps_pid(ps), p1$get_pid()) 35 36 skip_if_no_processx() 37 38 ## Even if it has quit already 39 p2 <- processx::process$new(px(), c("sleep", "10")) 40 on.exit(p2$kill(), add = TRUE) 41 pid2 <- p2$get_pid() 42 ps <- ps_handle(pid2) 43 p2$kill() 44 45 expect_false(p2$is_alive()) 46 expect_identical(ps_pid(ps), pid2) 47}) 48 49test_that("create_time", { 50 ## Argument check 51 expect_error(ps_create_time(123), class = "invalid_argument") 52 53 skip_if_no_processx() 54 55 p1 <- processx::process$new(px(), c("sleep", "10")) 56 on.exit(p1$kill(), add = TRUE) 57 ps <- ps_handle(p1$get_pid()) 58 expect_identical(p1$get_start_time(), ps_create_time(ps)) 59}) 60 61test_that("is_running", { 62 ## Argument check 63 expect_error(ps_is_running(123), class = "invalid_argument") 64 65 p1 <- processx::process$new(px(), c("sleep", "10")) 66 on.exit(p1$kill(), add = TRUE) 67 ps <- ps_handle(p1$get_pid()) 68 expect_true(ps_is_running(ps)) 69 70 p1$kill() 71 timeout <- Sys.time() + 5 72 while (ps_is_running(ps) && Sys.time() < timeout) Sys.sleep(0.05) 73 expect_false(ps_is_running(ps)) 74}) 75 76test_that("parent", { 77 ## Argument check 78 expect_error(ps_parent(123), class = "invalid_argument") 79 80 p1 <- processx::process$new(px(), c("sleep", "10")) 81 on.exit(p1$kill(), add = TRUE) 82 ps <- ps_handle(p1$get_pid()) 83 expect_true(ps_is_running(ps)) 84 85 pp <- ps_parent(ps) 86 expect_equal(ps_pid(pp), Sys.getpid()) 87}) 88 89test_that("ppid", { 90 ## Argument check 91 expect_error(ps_ppid(123), class = "invalid_argument") 92 93 p1 <- processx::process$new(px(), c("sleep", "10")) 94 on.exit(p1$kill(), add = TRUE) 95 ps <- ps_handle(p1$get_pid()) 96 expect_true(ps_is_running(ps)) 97 98 expect_equal(ps_ppid(ps), Sys.getpid()) 99}) 100 101test_that("name", { 102 ## Argument check 103 expect_error(ps_name(123), class = "invalid_argument") 104 105 skip_if_no_processx() 106 107 p1 <- processx::process$new(px(), c("sleep", "10")) 108 on.exit(p1$kill(), add = TRUE) 109 ps <- ps_handle(p1$get_pid()) 110 expect_true(ps_is_running(ps)) 111 expect_true(ps_name(ps) %in% c("px", "px.exe")) 112 113 ## Long names are not truncated 114 file.copy( 115 px(), 116 tmp <- paste0(tempfile(pattern = "file1234567890123456"), ".bat")) 117 on.exit(unlink(tmp), add = TRUE) 118 Sys.chmod(tmp, "0755") 119 120 p2 <- processx::process$new(tmp, c("sleep", "10")) 121 on.exit(p2$kill(), add = TRUE) 122 ps <- ps_handle(p2$get_pid()) 123 expect_true(ps_is_running(ps)) 124 expect_equal(ps_name(ps), basename(tmp)) 125}) 126 127test_that("exe", { 128 ## Argument check 129 expect_error(ps_exe(123), class = "invalid_argument") 130 131 p1 <- processx::process$new(px(), c("sleep", "10")) 132 on.exit(p1$kill(), add = TRUE) 133 ps <- ps_handle(p1$get_pid()) 134 expect_true(ps_is_running(ps)) 135 expect_equal(ps_exe(ps), realpath(px())) 136}) 137 138test_that("cmdline", { 139 ## Argument check 140 expect_error(ps_cmdline(123), class = "invalid_argument") 141 142 p1 <- processx::process$new(px(), c("sleep", "10")) 143 on.exit(p1$kill(), add = TRUE) 144 ps <- ps_handle(p1$get_pid()) 145 expect_true(ps_is_running(ps)) 146 expect_equal(ps_cmdline(ps), c(px(), "sleep", "10")) 147}) 148 149test_that("cwd", { 150 ## Argument check 151 expect_error(ps_cwd(123), class = "invalid_argument") 152 153 p1 <- processx::process$new(px(), c("sleep", "10"), wd = tempdir()) 154 on.exit(p1$kill(), add = TRUE) 155 ps <- ps_handle(p1$get_pid()) 156 expect_true(ps_is_running(ps)) 157 158 expect_equal(normalizePath(ps_cwd(ps)), normalizePath(tempdir())) 159}) 160 161test_that("environ, environ_raw", { 162 ## Argument check 163 expect_error(ps_environ(123), class = "invalid_argument") 164 165 skip_if_no_processx() 166 167 rnd <- basename(tempfile()) 168 p1 <- processx::process$new(px(), c("sleep", "10"), env = c(FOO = rnd)) 169 on.exit(p1$kill(), add = TRUE) 170 ps <- ps_handle(p1$get_pid()) 171 expect_true(ps_is_running(ps)) 172 173 expect_equal(ps_environ(ps)[["FOO"]], rnd) 174 expect_true(paste0("FOO=", rnd) %in% ps_environ_raw(ps)) 175}) 176 177test_that("num_threads", { 178 ## Argument check 179 expect_error(ps_num_threads(123), class = "invalid_argument") 180 181 ## sleep should be single-threaded 182 p1 <- processx::process$new(px(), c("sleep", "10")) 183 on.exit(p1$kill(), add = TRUE) 184 ps <- ps_handle(p1$get_pid()) 185 expect_true(ps_is_running(ps)) 186 expect_equal(ps_num_threads(ps), 1) 187 ## TODO: more threads? 188}) 189 190test_that("suspend, resume", { 191 ## Argument check 192 expect_error(ps_suspend(123), class = "invalid_argument") 193 expect_error(ps_resume(123), class = "invalid_argument") 194 195 p1 <- processx::process$new(px(), c("sleep", "10")) 196 on.exit(p1$kill(), add = TRUE) 197 ps <- ps_handle(p1$get_pid()) 198 199 ps_suspend(ps) 200 timeout <- Sys.time() + 60 201 while (Sys.time() < timeout && ps_status(ps) != "stopped") Sys.sleep(0.05) 202 expect_equal(ps_status(ps), "stopped") 203 expect_true(p1$is_alive()) 204 expect_true(ps_is_running(ps)) 205 206 ps_resume(ps) 207 timeout <- Sys.time() + 60 208 while (Sys.time() < timeout && ps_status(ps) == "stopped") Sys.sleep(0.05) 209 expect_true(ps_status(ps) %in% c("running", "sleeping")) 210 expect_true(p1$is_alive()) 211 expect_true(ps_is_running(ps)) 212 ps_kill(ps) 213}) 214 215test_that("kill", { 216 ## Argument check 217 expect_error(ps_kill(123), class = "invalid_argument") 218 219 p1 <- processx::process$new(px(), c("sleep", "10")) 220 on.exit(p1$kill(), add = TRUE) 221 ps <- ps_handle(p1$get_pid()) 222 223 ps_kill(ps) 224 timeout <- Sys.time() + 5 225 while (Sys.time() < timeout && ps_is_running(ps)) Sys.sleep(0.05) 226 expect_false(p1$is_alive()) 227 expect_false(ps_is_running(ps)) 228 if (ps_os_type()[["POSIX"]]) { 229 expect_equal(p1$get_exit_status(), - signals()$SIGKILL) 230 } 231}) 232 233test_that("children", { 234 ## Argument check 235 expect_error(ps_children(123), class = "invalid_argument") 236 237 skip_if_no_processx() 238 239 p1 <- processx::process$new(px(), c("sleep", "10")) 240 on.exit(p1$kill(), add = TRUE) 241 p2 <- processx::process$new(px(), c("sleep", "10")) 242 on.exit(p2$kill(), add = TRUE) 243 244 ch <- ps_children(ps_handle()) 245 expect_true(length(ch) >= 2) 246 247 pids <- map_int(ch, ps_pid) 248 expect_true(p1$get_pid() %in% pids) 249 expect_true(p2$get_pid() %in% pids) 250 251 ## We don't do this on Windows, because the parent process might be 252 ## gone by now, and then it fails with no_such_process 253 if (ps_os_type()[["POSIX"]]) { 254 ch3 <- ps_children(ps_parent(ps_handle()), recursive = TRUE) 255 pids3 <- map_int(ch3, ps_pid) 256 expect_true(Sys.getpid() %in% pids3) 257 expect_true(p1$get_pid() %in% pids3) 258 expect_true(p2$get_pid() %in% pids3) 259 } 260}) 261 262test_that("num_fds", { 263 skip_in_rstudio() 264 skip_on_cran() 265 266 tmp <- tempfile() 267 on.exit(unlink(tmp), add = TRUE) 268 269 me <- ps_handle() 270 orig <- ps_num_fds(me) 271 272 f <- file(tmp, open = "w") 273 on.exit(close(f), add = TRUE) 274 275 expect_equal(ps_num_fds(me), orig + 1) 276}) 277 278test_that("open_files", { 279 skip_in_rstudio() 280 skip_on_cran() 281 282 tmp <- tempfile() 283 on.exit(unlink(tmp), add = TRUE) 284 285 f <- file(tmp, open = "w") 286 on.exit(try(close(f), silent = TRUE), add = TRUE) 287 288 files <- ps_open_files(ps_handle()) 289 expect_true(basename(tmp) %in% basename(files$path)) 290 291 close(f) 292 files <- ps_open_files(ps_handle()) 293 expect_false(basename(tmp) %in% basename(files$path)) 294}) 295 296test_that("interrupt", { 297 skip_on_cran() 298 px <- processx::process$new(px(), c("sleep", "10")) 299 on.exit(px$kill(), add = TRUE) 300 ps <- ps_handle(px$get_pid()) 301 302 expect_true(ps_is_running(ps)) 303 304 ps_interrupt(ps) 305 306 deadline <- Sys.time() + 3 307 while (ps_is_running(ps) && Sys.time() < deadline) Sys.sleep(0.05) 308 expect_true(Sys.time() < deadline) 309 expect_false(ps_is_running(ps)) 310 if (ps_os_type()[["POSIX"]]) expect_equal(px$get_exit_status(), -2) 311}) 312