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