1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
34 static int close_cb_called;
35 static int exit_cb_called;
36 static uv_process_t process;
37 static uv_timer_t timer;
38 static uv_process_options_t options;
39 static char exepath[1024];
40 static size_t exepath_size = 1024;
42 static int no_term_signal;
44 #define OUTPUT_SIZE 1024
45 static char output[OUTPUT_SIZE];
46 static int output_used;
49 static void close_cb(uv_handle_t* handle) {
54 static void exit_cb(uv_process_t* process,
59 ASSERT(exit_status == 1);
60 ASSERT(term_signal == 0);
61 uv_close((uv_handle_t*)process, close_cb);
65 static void fail_cb(uv_process_t* process,
68 ASSERT(0 && "fail_cb called");
72 static void kill_cb(uv_process_t* process,
80 ASSERT(exit_status == 1);
82 ASSERT(exit_status == 0);
84 ASSERT(no_term_signal || term_signal == 15);
85 uv_close((uv_handle_t*)process, close_cb);
88 * Sending signum == 0 should check if the
89 * child process is still alive, not kill it.
90 * This process should be dead.
92 err = uv_kill(process->pid, 0);
93 ASSERT(err == UV_ESRCH);
96 static void detach_failure_cb(uv_process_t* process,
99 printf("detach_cb\n");
103 static void on_alloc(uv_handle_t* handle,
104 size_t suggested_size,
106 buf->base = output + output_used;
107 buf->len = OUTPUT_SIZE - output_used;
111 static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {
113 output_used += nread;
114 } else if (nread < 0) {
115 ASSERT(nread == UV_EOF);
116 uv_close((uv_handle_t*)tcp, close_cb);
121 static void write_cb(uv_write_t* req, int status) {
123 uv_close((uv_handle_t*)req->handle, close_cb);
127 static void init_process_options(char* test, uv_exit_cb exit_cb) {
128 /* Note spawn_helper1 defined in test/run-tests.c */
129 int r = uv_exepath(exepath, &exepath_size);
131 exepath[exepath_size] = '\0';
135 options.file = exepath;
137 options.exit_cb = exit_cb;
142 static void timer_cb(uv_timer_t* handle, int status) {
143 uv_process_kill(&process, /* SIGTERM */ 15);
144 uv_close((uv_handle_t*)handle, close_cb);
148 TEST_IMPL(spawn_fails) {
149 init_process_options("", fail_cb);
150 options.file = options.args[0] = "program-that-had-better-not-exist";
152 ASSERT(UV_ENOENT == uv_spawn(uv_default_loop(), &process, &options));
153 ASSERT(0 == uv_is_active((uv_handle_t*) &process));
154 ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
156 MAKE_VALGRIND_HAPPY();
161 TEST_IMPL(spawn_exit_code) {
164 init_process_options("spawn_helper1", exit_cb);
166 r = uv_spawn(uv_default_loop(), &process, &options);
169 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
172 ASSERT(exit_cb_called == 1);
173 ASSERT(close_cb_called == 1);
175 MAKE_VALGRIND_HAPPY();
180 TEST_IMPL(spawn_stdout) {
183 uv_stdio_container_t stdio[2];
185 init_process_options("spawn_helper2", exit_cb);
187 uv_pipe_init(uv_default_loop(), &out, 0);
188 options.stdio = stdio;
189 options.stdio[0].flags = UV_IGNORE;
190 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
191 options.stdio[1].data.stream = (uv_stream_t*)&out;
192 options.stdio_count = 2;
194 r = uv_spawn(uv_default_loop(), &process, &options);
197 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
200 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
203 ASSERT(exit_cb_called == 1);
204 ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
205 printf("output is: %s", output);
206 ASSERT(strcmp("hello world\n", output) == 0);
208 MAKE_VALGRIND_HAPPY();
213 TEST_IMPL(spawn_stdout_to_file) {
217 uv_stdio_container_t stdio[2];
220 unlink("stdout_file");
222 init_process_options("spawn_helper2", exit_cb);
224 r = uv_fs_open(uv_default_loop(), &fs_req, "stdout_file", O_CREAT | O_RDWR,
225 S_IRUSR | S_IWUSR, NULL);
227 uv_fs_req_cleanup(&fs_req);
231 options.stdio = stdio;
232 options.stdio[0].flags = UV_IGNORE;
233 options.stdio[1].flags = UV_INHERIT_FD;
234 options.stdio[1].data.fd = file;
235 options.stdio_count = 2;
237 r = uv_spawn(uv_default_loop(), &process, &options);
240 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
243 ASSERT(exit_cb_called == 1);
244 ASSERT(close_cb_called == 1);
246 r = uv_fs_read(uv_default_loop(), &fs_req, file, output, sizeof(output),
249 uv_fs_req_cleanup(&fs_req);
251 r = uv_fs_close(uv_default_loop(), &fs_req, file, NULL);
253 uv_fs_req_cleanup(&fs_req);
255 printf("output is: %s", output);
256 ASSERT(strcmp("hello world\n", output) == 0);
259 unlink("stdout_file");
261 MAKE_VALGRIND_HAPPY();
266 TEST_IMPL(spawn_stdin) {
270 uv_write_t write_req;
272 uv_stdio_container_t stdio[2];
273 char buffer[] = "hello-from-spawn_stdin";
275 init_process_options("spawn_helper3", exit_cb);
277 uv_pipe_init(uv_default_loop(), &out, 0);
278 uv_pipe_init(uv_default_loop(), &in, 0);
279 options.stdio = stdio;
280 options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
281 options.stdio[0].data.stream = (uv_stream_t*)∈
282 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
283 options.stdio[1].data.stream = (uv_stream_t*)&out;
284 options.stdio_count = 2;
286 r = uv_spawn(uv_default_loop(), &process, &options);
290 buf.len = sizeof(buffer);
291 r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb);
294 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
297 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
300 ASSERT(exit_cb_called == 1);
301 ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */
302 ASSERT(strcmp(buffer, output) == 0);
304 MAKE_VALGRIND_HAPPY();
309 TEST_IMPL(spawn_stdio_greater_than_3) {
312 uv_stdio_container_t stdio[4];
314 init_process_options("spawn_helper5", exit_cb);
316 uv_pipe_init(uv_default_loop(), &pipe, 0);
317 options.stdio = stdio;
318 options.stdio[0].flags = UV_IGNORE;
319 options.stdio[1].flags = UV_IGNORE;
320 options.stdio[2].flags = UV_IGNORE;
321 options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
322 options.stdio[3].data.stream = (uv_stream_t*)&pipe;
323 options.stdio_count = 4;
325 r = uv_spawn(uv_default_loop(), &process, &options);
328 r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read);
331 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
334 ASSERT(exit_cb_called == 1);
335 ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
336 printf("output from stdio[3] is: %s", output);
337 ASSERT(strcmp("fourth stdio!\n", output) == 0);
339 MAKE_VALGRIND_HAPPY();
344 TEST_IMPL(spawn_ignored_stdio) {
347 init_process_options("spawn_helper6", exit_cb);
349 options.stdio = NULL;
350 options.stdio_count = 0;
352 r = uv_spawn(uv_default_loop(), &process, &options);
355 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
358 ASSERT(exit_cb_called == 1);
359 ASSERT(close_cb_called == 1);
361 MAKE_VALGRIND_HAPPY();
366 TEST_IMPL(spawn_and_kill) {
369 init_process_options("spawn_helper4", kill_cb);
371 r = uv_spawn(uv_default_loop(), &process, &options);
374 r = uv_timer_init(uv_default_loop(), &timer);
377 r = uv_timer_start(&timer, timer_cb, 500, 0);
380 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
383 ASSERT(exit_cb_called == 1);
384 ASSERT(close_cb_called == 2); /* Once for process and once for timer. */
386 MAKE_VALGRIND_HAPPY();
391 TEST_IMPL(spawn_preserve_env) {
394 uv_stdio_container_t stdio[2];
396 init_process_options("spawn_helper7", exit_cb);
398 uv_pipe_init(uv_default_loop(), &out, 0);
399 options.stdio = stdio;
400 options.stdio[0].flags = UV_IGNORE;
401 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
402 options.stdio[1].data.stream = (uv_stream_t*) &out;
403 options.stdio_count = 2;
405 r = putenv("ENV_TEST=testval");
408 /* Explicitly set options.env to NULL to test for env clobbering. */
411 r = uv_spawn(uv_default_loop(), &process, &options);
414 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
417 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
420 ASSERT(exit_cb_called == 1);
421 ASSERT(close_cb_called == 2);
423 printf("output is: %s", output);
424 ASSERT(strcmp("testval", output) == 0);
426 MAKE_VALGRIND_HAPPY();
431 TEST_IMPL(spawn_detached) {
434 init_process_options("spawn_helper4", detach_failure_cb);
436 options.flags |= UV_PROCESS_DETACHED;
438 r = uv_spawn(uv_default_loop(), &process, &options);
441 uv_unref((uv_handle_t*)&process);
443 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
446 ASSERT(exit_cb_called == 0);
448 r = uv_kill(process.pid, 0);
451 r = uv_kill(process.pid, 15);
454 MAKE_VALGRIND_HAPPY();
458 TEST_IMPL(spawn_and_kill_with_std) {
460 uv_pipe_t in, out, err;
462 char message[] = "Nancy's joining me because the message this evening is "
463 "not my message but ours.";
465 uv_stdio_container_t stdio[3];
467 init_process_options("spawn_helper4", kill_cb);
469 r = uv_pipe_init(uv_default_loop(), &in, 0);
472 r = uv_pipe_init(uv_default_loop(), &out, 0);
475 r = uv_pipe_init(uv_default_loop(), &err, 0);
478 options.stdio = stdio;
479 options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
480 options.stdio[0].data.stream = (uv_stream_t*)∈
481 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
482 options.stdio[1].data.stream = (uv_stream_t*)&out;
483 options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
484 options.stdio[2].data.stream = (uv_stream_t*)&err;
485 options.stdio_count = 3;
487 r = uv_spawn(uv_default_loop(), &process, &options);
490 buf = uv_buf_init(message, sizeof message);
491 r = uv_write(&write, (uv_stream_t*) &in, &buf, 1, write_cb);
494 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
497 r = uv_read_start((uv_stream_t*) &err, on_alloc, on_read);
500 r = uv_timer_init(uv_default_loop(), &timer);
503 r = uv_timer_start(&timer, timer_cb, 500, 0);
506 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
509 ASSERT(exit_cb_called == 1);
510 ASSERT(close_cb_called == 5); /* process x 1, timer x 1, stdio x 3. */
512 MAKE_VALGRIND_HAPPY();
517 TEST_IMPL(spawn_and_ping) {
518 uv_write_t write_req;
521 uv_stdio_container_t stdio[2];
524 init_process_options("spawn_helper3", exit_cb);
525 buf = uv_buf_init("TEST", 4);
527 uv_pipe_init(uv_default_loop(), &out, 0);
528 uv_pipe_init(uv_default_loop(), &in, 0);
529 options.stdio = stdio;
530 options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
531 options.stdio[0].data.stream = (uv_stream_t*)∈
532 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
533 options.stdio[1].data.stream = (uv_stream_t*)&out;
534 options.stdio_count = 2;
536 r = uv_spawn(uv_default_loop(), &process, &options);
539 /* Sending signum == 0 should check if the
540 * child process is still alive, not kill it.
542 r = uv_process_kill(&process, 0);
545 r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb);
548 r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read);
551 ASSERT(exit_cb_called == 0);
553 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
556 ASSERT(exit_cb_called == 1);
557 ASSERT(strcmp(output, "TEST") == 0);
559 MAKE_VALGRIND_HAPPY();
571 init_process_options("spawn_helper4", kill_cb);
573 r = uv_spawn(uv_default_loop(), &process, &options);
576 /* Sending signum == 0 should check if the
577 * child process is still alive, not kill it.
579 r = uv_kill(process.pid, 0);
582 /* Kill the process. */
583 r = uv_kill(process.pid, /* SIGTERM */ 15);
586 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
589 ASSERT(exit_cb_called == 1);
590 ASSERT(close_cb_called == 1);
592 MAKE_VALGRIND_HAPPY();
598 TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) {
603 uv_stdio_container_t stdio[2];
605 init_process_options("spawn_helper2", exit_cb);
607 uv_pipe_init(uv_default_loop(), &out, 0);
608 options.stdio = stdio;
609 options.stdio[0].flags = UV_IGNORE;
610 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
611 options.stdio[1].data.stream = (uv_stream_t*)&out;
612 options.stdio_count = 2;
614 /* Create a pipe that'll cause a collision. */
617 "\\\\.\\pipe\\uv\\%p-%d",
619 GetCurrentProcessId());
620 pipe_handle = CreateNamedPipeA(name,
621 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
622 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
628 ASSERT(pipe_handle != INVALID_HANDLE_VALUE);
630 r = uv_spawn(uv_default_loop(), &process, &options);
633 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
636 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
639 ASSERT(exit_cb_called == 1);
640 ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
641 printf("output is: %s", output);
642 ASSERT(strcmp("hello world\n", output) == 0);
644 MAKE_VALGRIND_HAPPY();
649 int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr);
650 WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target);
652 TEST_IMPL(argument_escaping) {
653 const WCHAR* test_str[] = {
662 L"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\""
664 const int count = sizeof(test_str) / sizeof(*test_str);
668 size_t total_size = 0;
676 "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"",
679 WCHAR* verbatim_output;
680 WCHAR* non_verbatim_output;
682 test_output = calloc(count, sizeof(WCHAR*));
683 for (i = 0; i < count; ++i) {
684 test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(WCHAR));
685 quote_cmd_arg(test_str[i], test_output[i]);
686 wprintf(L"input : %s\n", test_str[i]);
687 wprintf(L"output: %s\n", test_output[i]);
688 total_size += wcslen(test_output[i]) + 1;
690 command_line = calloc(total_size + 1, sizeof(WCHAR));
691 for (i = 0; i < count; ++i) {
692 wcscat(command_line, test_output[i]);
693 wcscat(command_line, L" ");
695 command_line[total_size - 1] = L'\0';
697 wprintf(L"command_line: %s\n", command_line);
699 cracked = CommandLineToArgvW(command_line, &num_args);
700 for (i = 0; i < num_args; ++i) {
701 wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]);
702 ASSERT(wcscmp(test_str[i], cracked[i]) == 0);
706 for (i = 0; i < count; ++i) {
707 free(test_output[i]);
710 result = make_program_args(verbatim, 1, &verbatim_output);
712 result = make_program_args(verbatim, 0, &non_verbatim_output);
715 wprintf(L" verbatim_output: %s\n", verbatim_output);
716 wprintf(L"non_verbatim_output: %s\n", non_verbatim_output);
718 ASSERT(wcscmp(verbatim_output,
719 L"cmd.exe /c c:\\path\\to\\node.exe --eval "
720 L"\"require('c:\\\\path\\\\to\\\\test.js')\"") == 0);
721 ASSERT(wcscmp(non_verbatim_output,
722 L"cmd.exe /c \"c:\\path\\to\\node.exe --eval "
723 L"\\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0);
725 free(verbatim_output);
726 free(non_verbatim_output);
731 int make_program_env(char** env_block, WCHAR** dst_ptr);
733 TEST_IMPL(environment_creation) {
735 char* environment[] = {
737 "SYSTEM=ROOT", /* substring of a supplied var name */
738 "SYSTEMROOTED=OMG", /* supplied var name is a substring */
745 WCHAR* ptr = expected;
750 for (i = 0; i < sizeof(environment) / sizeof(environment[0]) - 1; i++) {
751 ptr += uv_utf8_to_utf16(environment[i],
753 expected + sizeof(expected) - ptr);
756 memcpy(ptr, L"SYSTEMROOT=", sizeof(L"SYSTEMROOT="));
757 ptr += sizeof(L"SYSTEMROOT=")/sizeof(WCHAR) - 1;
758 ptr += GetEnvironmentVariableW(L"SYSTEMROOT",
760 expected + sizeof(expected) - ptr);
763 memcpy(ptr, L"SYSTEMDRIVE=", sizeof(L"SYSTEMDRIVE="));
764 ptr += sizeof(L"SYSTEMDRIVE=")/sizeof(WCHAR) - 1;
765 ptr += GetEnvironmentVariableW(L"SYSTEMDRIVE",
767 expected + sizeof(expected) - ptr);
771 result = make_program_env(environment, &env);
774 for (str = env; *str; str += wcslen(str) + 1) {
775 wprintf(L"%s\n", str);
778 ASSERT(wcscmp(expected, env) == 0);
785 TEST_IMPL(spawn_setuid_setgid) {
788 /* if not root, then this will fail. */
789 uv_uid_t uid = getuid();
791 fprintf(stderr, "spawn_setuid_setgid skipped: not root\n");
795 init_process_options("spawn_helper1", exit_cb);
797 /* become the "nobody" user. */
799 pw = getpwnam("nobody");
801 options.uid = pw->pw_uid;
802 options.gid = pw->pw_gid;
803 options.flags = UV_PROCESS_SETUID | UV_PROCESS_SETGID;
805 r = uv_spawn(uv_default_loop(), &process, &options);
808 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
811 ASSERT(exit_cb_called == 1);
812 ASSERT(close_cb_called == 1);
814 MAKE_VALGRIND_HAPPY();
821 TEST_IMPL(spawn_setuid_fails) {
824 /* if root, become nobody. */
825 uv_uid_t uid = getuid();
828 pw = getpwnam("nobody");
830 ASSERT(0 == setgid(pw->pw_gid));
831 ASSERT(0 == setuid(pw->pw_uid));
834 init_process_options("spawn_helper1", fail_cb);
836 options.flags |= UV_PROCESS_SETUID;
839 r = uv_spawn(uv_default_loop(), &process, &options);
840 ASSERT(r == UV_EPERM);
842 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
845 ASSERT(close_cb_called == 0);
847 MAKE_VALGRIND_HAPPY();
852 TEST_IMPL(spawn_setgid_fails) {
855 /* if root, become nobody. */
856 uv_uid_t uid = getuid();
859 pw = getpwnam("nobody");
861 ASSERT(0 == setgid(pw->pw_gid));
862 ASSERT(0 == setuid(pw->pw_uid));
865 init_process_options("spawn_helper1", fail_cb);
867 options.flags |= UV_PROCESS_SETGID;
870 r = uv_spawn(uv_default_loop(), &process, &options);
871 ASSERT(r == UV_EPERM);
873 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
876 ASSERT(close_cb_called == 0);
878 MAKE_VALGRIND_HAPPY();
886 static void exit_cb_unexpected(uv_process_t* process,
889 ASSERT(0 && "should not have been called");
893 TEST_IMPL(spawn_setuid_fails) {
896 init_process_options("spawn_helper1", exit_cb_unexpected);
898 options.flags |= UV_PROCESS_SETUID;
899 options.uid = (uv_uid_t) -42424242;
901 r = uv_spawn(uv_default_loop(), &process, &options);
902 ASSERT(r == UV_ENOTSUP);
904 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
907 ASSERT(close_cb_called == 0);
909 MAKE_VALGRIND_HAPPY();
914 TEST_IMPL(spawn_setgid_fails) {
917 init_process_options("spawn_helper1", exit_cb_unexpected);
919 options.flags |= UV_PROCESS_SETGID;
920 options.gid = (uv_gid_t) -42424242;
922 r = uv_spawn(uv_default_loop(), &process, &options);
923 ASSERT(r == UV_ENOTSUP);
925 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
928 ASSERT(close_cb_called == 0);
930 MAKE_VALGRIND_HAPPY();
936 TEST_IMPL(spawn_auto_unref) {
937 init_process_options("spawn_helper1", NULL);
938 ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options));
939 ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
940 ASSERT(0 == uv_is_closing((uv_handle_t*) &process));
941 uv_close((uv_handle_t*) &process, NULL);
942 ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
943 ASSERT(1 == uv_is_closing((uv_handle_t*) &process));
944 MAKE_VALGRIND_HAPPY();