1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::ffi::OsStr; 15 use std::future::Future; 16 use std::io; 17 use std::path::Path; 18 use std::process::{Command as StdCommand, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio}; 19 20 use crate::process::child::{Child, ChildStderr, ChildStdin, ChildStdout}; 21 22 /// Async version of std::process::Command 23 #[derive(Debug)] 24 pub struct Command { 25 std: StdCommand, 26 kill: bool, 27 } 28 29 /// # Example 30 /// 31 /// ``` 32 /// use std::process::Command; 33 /// let command = Command::new("echo"); 34 /// let ylong_command = ylong_runtime::process::Command::new("hello"); 35 /// ``` 36 impl From<StdCommand> for Command { from(value: StdCommand) -> Self37 fn from(value: StdCommand) -> Self { 38 Self { 39 std: value, 40 kill: false, 41 } 42 } 43 } 44 45 impl Command { 46 /// Constructs a new Command for launching the program at path program, with 47 /// the following default configuration: 48 /// * No arguments to the program 49 /// * Inherit the current process's environment 50 /// * Inherit the current process's working directory 51 /// * Inherit stdin/stdout/stderr for spawn or status, but create pipes for 52 /// output 53 /// 54 /// Builder methods are provided to change these defaults and otherwise 55 /// configure the process. If program is not an absolute path, the PATH 56 /// will be searched in an OS-defined way. The search path to be used 57 /// may be controlled by setting the PATH environment variable on the 58 /// Command, but this has some implementation limitations on Windows (see 59 /// issue [#37519]). 60 /// 61 /// # Example 62 /// ``` 63 /// use ylong_runtime::process::Command; 64 /// let _command = Command::new("sh"); 65 /// ``` 66 /// 67 /// [#37519]: https://github.com/rust-lang/rust/issues/37519 new<S: AsRef<OsStr>>(program: S) -> Self68 pub fn new<S: AsRef<OsStr>>(program: S) -> Self { 69 Self { 70 std: StdCommand::new(program), 71 kill: false, 72 } 73 } 74 75 /// Gets std::process::Command from async `Command` 76 /// 77 /// # Example 78 /// 79 /// ``` 80 /// use ylong_runtime::process::Command; 81 /// let command = Command::new("echo"); 82 /// let std_command = command.as_std(); 83 /// ``` as_std(&self) -> &StdCommand84 pub fn as_std(&self) -> &StdCommand { 85 &self.std 86 } 87 88 /// Sets whether kill the child process when `Child` drop. 89 /// The default value is false, it's similar to the behavior of the std. kill_on_drop(&mut self, kill: bool) -> &mut Command90 pub fn kill_on_drop(&mut self, kill: bool) -> &mut Command { 91 self.kill = kill; 92 self 93 } 94 95 /// Adds a parameter to pass to the program. 96 /// 97 /// It's same as std. 98 /// 99 /// # Example 100 /// 101 /// ```no_run 102 /// use ylong_runtime::process::Command; 103 /// 104 /// Command::new("ls") 105 /// .arg("-l") 106 /// .arg("-a") 107 /// .spawn() 108 /// .expect("ls command failed to start"); 109 /// ``` arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command110 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command { 111 self.std.arg(arg); 112 self 113 } 114 115 /// Adds multiple parameters to pass to the program. 116 /// 117 /// It's same as std. 118 /// 119 /// # Example 120 /// 121 /// ```no_run 122 /// use ylong_runtime::process::Command; 123 /// 124 /// Command::new("ls") 125 /// .args(["-l", "-a"]) 126 /// .spawn() 127 /// .expect("ls command failed to start"); 128 /// ``` args<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(&mut self, args: I) -> &mut Command129 pub fn args<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(&mut self, args: I) -> &mut Command { 130 self.std.args(args); 131 self 132 } 133 134 /// Inserts or updates an environment variable mapping. 135 /// 136 /// It's same as std. 137 /// 138 /// # Example 139 /// 140 /// ```no_run 141 /// use ylong_runtime::process::Command; 142 /// 143 /// Command::new("ls") 144 /// .env("PATH", "/bin") 145 /// .spawn() 146 /// .expect("ls command failed to start"); 147 /// ``` env<K: AsRef<OsStr>, V: AsRef<OsStr>>(&mut self, key: K, val: V) -> &mut Command148 pub fn env<K: AsRef<OsStr>, V: AsRef<OsStr>>(&mut self, key: K, val: V) -> &mut Command { 149 self.std.env(key, val); 150 self 151 } 152 153 /// Adds or updates multiple environment variable mappings. 154 /// 155 /// It's same as std. 156 /// 157 /// # Example 158 /// 159 /// ```no_run 160 /// use std::collections::HashMap; 161 /// use std::env; 162 /// use std::process::Stdio; 163 /// 164 /// use ylong_runtime::process::Command; 165 /// 166 /// let filtered_env: HashMap<String, String> = env::vars() 167 /// .filter(|&(ref k, _)| k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH") 168 /// .collect(); 169 /// 170 /// Command::new("printenv") 171 /// .stdin(Stdio::null()) 172 /// .stdout(Stdio::inherit()) 173 /// .env_clear() 174 /// .envs(&filtered_env) 175 /// .spawn() 176 /// .expect("printenv failed to start"); 177 /// ``` envs<I, S, V>(&mut self, vars: I) -> &mut Command where I: IntoIterator<Item = (S, V)>, S: AsRef<OsStr>, V: AsRef<OsStr>,178 pub fn envs<I, S, V>(&mut self, vars: I) -> &mut Command 179 where 180 I: IntoIterator<Item = (S, V)>, 181 S: AsRef<OsStr>, 182 V: AsRef<OsStr>, 183 { 184 self.std.envs(vars); 185 self 186 } 187 188 /// Removes an environment variable mapping. 189 /// 190 /// It's same as std. 191 /// 192 /// # Example 193 /// 194 /// ```no_run 195 /// use ylong_runtime::process::Command; 196 /// 197 /// Command::new("ls") 198 /// .env_remove("PATH") 199 /// .spawn() 200 /// .expect("ls command failed to start"); 201 /// ``` env_remove<S: AsRef<OsStr>>(&mut self, key: S) -> &mut Command202 pub fn env_remove<S: AsRef<OsStr>>(&mut self, key: S) -> &mut Command { 203 self.std.env_remove(key); 204 self 205 } 206 207 /// Clears the entire environment map for the child process. 208 /// 209 /// It's same as std. 210 /// 211 /// # Example 212 /// 213 /// ```no_run 214 /// use ylong_runtime::process::Command; 215 /// 216 /// Command::new("ls") 217 /// .env_clear() 218 /// .spawn() 219 /// .expect("ls command failed to start"); 220 /// ``` env_clear(&mut self) -> &mut Command221 pub fn env_clear(&mut self) -> &mut Command { 222 self.std.env_clear(); 223 self 224 } 225 226 /// Sets the child process's working directory. 227 /// 228 /// It's same as std. 229 /// 230 /// # Example 231 /// 232 /// ```no_run 233 /// use ylong_runtime::process::Command; 234 /// 235 /// Command::new("ls") 236 /// .current_dir("/bin") 237 /// .spawn() 238 /// .expect("ls command failed to start"); 239 /// ``` current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command240 pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command { 241 self.std.current_dir(dir); 242 self 243 } 244 245 /// Configuration for the child process's standard input (stdin) handle. 246 /// Defaults to inherit when used with spawn or status, and defaults to 247 /// piped when used with output. 248 /// 249 /// It's same as std. 250 /// 251 /// # Example 252 /// 253 /// ```no_run 254 /// use std::process::Stdio; 255 /// 256 /// use ylong_runtime::process::Command; 257 /// 258 /// Command::new("ls") 259 /// .stdin(Stdio::null()) 260 /// .spawn() 261 /// .expect("ls command failed to start"); 262 /// ``` stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command263 pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command { 264 self.std.stdin(cfg); 265 self 266 } 267 268 /// Configuration for the child process's standard output (stdout) handle. 269 /// Defaults to inherit when used with spawn or status, and defaults to 270 /// piped when used with output. 271 /// 272 /// It's same as std. 273 /// 274 /// # Example 275 /// 276 /// ```no_run 277 /// use std::process::Stdio; 278 /// 279 /// use ylong_runtime::process::Command; 280 /// 281 /// Command::new("ls") 282 /// .stdout(Stdio::null()) 283 /// .spawn() 284 /// .expect("ls command failed to start"); 285 /// ``` stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command286 pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command { 287 self.std.stdout(cfg); 288 self 289 } 290 291 /// Configuration for the child process's standard error (stderr) handle. 292 /// Defaults to inherit when used with spawn or status, and defaults to 293 /// piped when used with output. 294 /// 295 /// It's same as std. 296 /// 297 /// # Example 298 /// 299 /// ```no_run 300 /// use std::process::Stdio; 301 /// 302 /// use ylong_runtime::process::Command; 303 /// 304 /// Command::new("ls") 305 /// .stderr(Stdio::null()) 306 /// .spawn() 307 /// .expect("ls command failed to start"); 308 /// ``` stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command309 pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command { 310 self.std.stderr(cfg); 311 self 312 } 313 314 /// Executes the command as a child process, returning a handle to it. 315 /// By default, stdin, stdout and stderr are inherited from the parent. 316 /// 317 /// This will spawn the child process synchronously and return a Future 318 /// handle of child process. 319 /// 320 /// # Example 321 /// 322 /// ```no_run 323 /// use ylong_runtime::process::Command; 324 /// 325 /// async fn command() -> std::process::ExitStatus { 326 /// let mut child = Command::new("ls") 327 /// .spawn() 328 /// .expect("ls command failed to start"); 329 /// child.wait().await.expect("ls command failed to run") 330 /// } 331 /// ``` spawn(&mut self) -> io::Result<Child>332 pub fn spawn(&mut self) -> io::Result<Child> { 333 let mut child = self.std.spawn()?; 334 let stdin = child 335 .stdin 336 .take() 337 .map(super::sys::stdio) 338 .transpose()? 339 .map(ChildStdin::new); 340 let stdout = child 341 .stdout 342 .take() 343 .map(super::sys::stdio) 344 .transpose()? 345 .map(ChildStdout::new); 346 let stderr = child 347 .stderr 348 .take() 349 .map(super::sys::stdio) 350 .transpose()? 351 .map(ChildStderr::new); 352 353 Child::new(child, self.kill, stdin, stdout, stderr) 354 } 355 356 /// Executes the command as a child process, waiting for it to finish and 357 /// collecting all of its output. By default, stdout and stderr are 358 /// captured (and used to provide the resulting output). Stdin is not 359 /// inherited from the parent and any attempt by the child process to read 360 /// from the stdin stream will result in the stream immediately closing. 361 /// 362 /// If set `kill_on_drop()`, the child will be killed when this method 363 /// return. 364 /// 365 /// # Example 366 /// 367 /// ```no_run 368 /// use ylong_runtime::process::Command; 369 /// 370 /// async fn command() { 371 /// let output = Command::new("ls") 372 /// .output() 373 /// .await 374 /// .expect("ls command failed to run"); 375 /// println!("stdout of ls: {:?}", output.stdout); 376 /// } 377 /// ``` output(&mut self) -> impl Future<Output = io::Result<Output>>378 pub fn output(&mut self) -> impl Future<Output = io::Result<Output>> { 379 self.stdout(Stdio::piped()); 380 self.stderr(Stdio::piped()); 381 382 let child = self.spawn(); 383 384 async { child?.output_wait().await } 385 } 386 387 /// Executes a command as a child process, waiting for it to finish and 388 /// collecting its status. By default, stdin, stdout and stderr are 389 /// inherited from the parent. 390 /// 391 /// If set `kill_on_drop()`, the child will be killed when this method 392 /// return. 393 /// 394 /// # Example 395 /// 396 /// ```no_run 397 /// use ylong_runtime::process::Command; 398 /// 399 /// async fn command() -> std::process::ExitStatus { 400 /// Command::new("ls") 401 /// .status() 402 /// .await 403 /// .expect("Command status failed!") 404 /// } 405 /// ``` 406 /// This fn can only obtain `ExitStatus`. To obtain the `Output`, please use 407 /// `output()` status(&mut self) -> impl Future<Output = io::Result<ExitStatus>>408 pub fn status(&mut self) -> impl Future<Output = io::Result<ExitStatus>> { 409 let child = self.spawn(); 410 411 async { 412 let mut child = child?; 413 414 drop(child.take_stdin()); 415 drop(child.take_stdout()); 416 drop(child.take_stderr()); 417 418 child.wait().await 419 } 420 } 421 422 /// Returns the path to the program that was given to Command::new. 423 /// 424 /// It's same as std. 425 /// 426 /// # Example 427 /// 428 /// ``` 429 /// use ylong_runtime::process::Command; 430 /// 431 /// let cmd = Command::new("echo"); 432 /// assert_eq!(cmd.get_program(), "echo"); 433 /// ``` 434 #[must_use] get_program(&self) -> &OsStr435 pub fn get_program(&self) -> &OsStr { 436 self.std.get_program() 437 } 438 439 /// Returns an iterator of the arguments that will be passed to the program. 440 /// 441 /// This does not include the path to the program as the first argument; 442 /// it only includes the arguments specified with Command::arg and 443 /// Command::args. 444 /// 445 /// It's same as std. 446 /// 447 /// # Example 448 /// 449 /// ``` 450 /// use std::ffi::OsStr; 451 /// 452 /// use ylong_runtime::process::Command; 453 /// 454 /// let mut cmd = Command::new("echo"); 455 /// cmd.arg("first").arg("second"); 456 /// let args: Vec<&OsStr> = cmd.get_args().collect(); 457 /// assert_eq!(args, &["first", "second"]); 458 /// ``` get_args(&self) -> CommandArgs<'_>459 pub fn get_args(&self) -> CommandArgs<'_> { 460 self.std.get_args() 461 } 462 463 /// Returns an iterator of the environment variables that will be set when 464 /// the process is spawned. 465 /// 466 /// It's same as std. 467 /// 468 /// # Example 469 /// 470 /// ``` 471 /// use std::ffi::OsStr; 472 /// 473 /// use ylong_runtime::process::Command; 474 /// 475 /// let mut cmd = Command::new("ls"); 476 /// cmd.env("TERM", "dumb").env_remove("TZ"); 477 /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect(); 478 /// assert_eq!( 479 /// envs, 480 /// &[ 481 /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))), 482 /// (OsStr::new("TZ"), None) 483 /// ] 484 /// ); 485 /// ``` get_envs(&self) -> CommandEnvs<'_>486 pub fn get_envs(&self) -> CommandEnvs<'_> { 487 self.std.get_envs() 488 } 489 490 /// Returns the working directory for the child process. 491 /// 492 /// This returns None if the working directory will not be changed. 493 /// 494 /// It's same as std. 495 /// 496 /// # Example 497 /// 498 /// ``` 499 /// use std::ffi::OsStr; 500 /// use std::path::Path; 501 /// 502 /// use ylong_runtime::process::Command; 503 /// 504 /// let mut cmd = Command::new("ls"); 505 /// assert_eq!(cmd.get_current_dir(), None); 506 /// cmd.current_dir("/bin"); 507 /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin"))) 508 /// ``` 509 #[must_use] get_current_dir(&self) -> Option<&Path>510 pub fn get_current_dir(&self) -> Option<&Path> { 511 self.std.get_current_dir() 512 } 513 } 514 515 #[cfg(unix)] 516 use std::os::unix::process::CommandExt; 517 518 #[cfg(unix)] 519 impl Command { 520 /// Sets the child process's user ID. This translates to a `setuid` call in 521 /// the child process. Failure in the `setuid` call will cause the spawn to 522 /// fail. 523 /// 524 /// It's same as std. uid(&mut self, id: u32) -> &mut Command525 pub fn uid(&mut self, id: u32) -> &mut Command { 526 self.std.uid(id); 527 self 528 } 529 530 /// Similar to `uid`, but sets the group ID of the child process. This has 531 /// the same semantics as the `uid` field. 532 /// 533 /// It's same as std. gid(&mut self, id: u32) -> &mut Command534 pub fn gid(&mut self, id: u32) -> &mut Command { 535 self.std.gid(id); 536 self 537 } 538 539 /// Sets executable argument 540 /// Sets the first process argument `argv[0]`, to something other than the 541 /// default executable path. 542 /// 543 /// It's same as std. arg0<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command544 pub fn arg0<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command { 545 self.std.arg0(arg); 546 self 547 } 548 549 /// Schedules a closure to be run just before the exec function is invoked. 550 /// The closure is allowed to return an I/O error whose OS error code will 551 /// be communicated back to the parent and returned as an error from when 552 /// the spawn was requested. Multiple closures can be registered and 553 /// they will be called in order of their registration. If a closure 554 /// returns Err then no further closures will be called and the spawn 555 /// operation will immediately return with a failure. 556 /// 557 /// It's same as std. 558 /// 559 /// # Safety 560 /// 561 /// This closure will be run in the context of the child process after a 562 /// `fork`. This primarily means that any modifications made to memory on 563 /// behalf of this closure will ***not*** be visible to the parent process. 564 /// This is often a very constrained environment where normal operations 565 /// like `malloc`, accessing environment variables through [`mod@std::env`] 566 /// or acquiring a mutex are not guaranteed to work (due to other 567 /// threads perhaps still running when the `fork` was run). pre_exec<F>(&mut self, f: F) -> &mut Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static,568 pub unsafe fn pre_exec<F>(&mut self, f: F) -> &mut Command 569 where 570 F: FnMut() -> io::Result<()> + Send + Sync + 'static, 571 { 572 self.std.pre_exec(f); 573 self 574 } 575 576 /// Sets the process group ID (PGID) of the child process. 577 /// Equivalent to a setpgid call in the child process, but may be more 578 /// efficient. Process groups determine which processes receive signals. 579 /// 580 /// It's same as std. process_group(&mut self, pgroup: i32) -> &mut Command581 pub fn process_group(&mut self, pgroup: i32) -> &mut Command { 582 self.std.process_group(pgroup); 583 self 584 } 585 } 586 587 #[cfg(test)] 588 mod test { 589 use std::io::IoSlice; 590 use std::process::Stdio; 591 592 use crate::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; 593 use crate::process::Command; 594 595 /// UT test cases for Command. 596 /// 597 /// # Brief 598 /// 1. Create a `Command`. 599 /// 2. Call `kill_on_drop()` set true and check command.kill. 600 /// 3. Call `kill_on_drop()` set false and check command.kill. 601 /// 4. Check command.std. 602 #[test] ut_process_basic_test()603 fn ut_process_basic_test() { 604 let mut command = Command::new("echo"); 605 assert!(!command.kill); 606 command.kill_on_drop(true); 607 assert!(command.kill); 608 command.kill_on_drop(false); 609 assert!(!command.kill); 610 assert_eq!(command.std.get_program(), "echo"); 611 } 612 613 /// UT test cases for `output()`. 614 /// 615 /// # Brief 616 /// 1. Create a `Command` with arg. 617 /// 2. Use `output()` waiting result. 618 #[test] ut_process_output_test()619 fn ut_process_output_test() { 620 let handle = crate::spawn(async { 621 let mut command = Command::new("echo"); 622 command.arg("Hello, world!"); 623 let output = command.output().await.unwrap(); 624 625 assert!(output.status.success()); 626 assert_eq!(output.stdout.as_slice(), b"Hello, world!\n"); 627 assert!(output.stderr.is_empty()); 628 }); 629 crate::block_on(handle).unwrap(); 630 } 631 632 /// UT test cases for `status()`. 633 /// 634 /// # Brief 635 /// 1. Create a `Command` with arg. 636 /// 2. Use `status()` waiting result. 637 #[test] ut_process_status_test()638 fn ut_process_status_test() { 639 let handle = crate::spawn(async { 640 let mut command = Command::new("echo"); 641 command.arg("Hello, world!"); 642 643 let status = command.status().await.unwrap(); 644 assert!(status.success()); 645 }); 646 crate::block_on(handle).unwrap(); 647 } 648 649 /// UT test cases for Command. 650 /// 651 /// # Brief 652 /// 1. Create a `Command` and `spawn()`. 653 /// 2. Take `child.stdin` and write something in it. 654 /// 3. Take `child.stdout` and read it, check the result. 655 /// 4. Check child's result. 656 #[test] ut_process_child_stdio_test()657 fn ut_process_child_stdio_test() { 658 let handle = crate::spawn(async { 659 let mut child = Command::new("rev") 660 .stdin(Stdio::piped()) 661 .stdout(Stdio::piped()) 662 .stderr(Stdio::piped()) 663 .spawn() 664 .expect("Failed to spawn child process"); 665 666 let mut stdin = child.take_stdin().expect("Failed to open stdin"); 667 let stdin_handle = crate::spawn(async move { 668 assert!(stdin.is_write_vectored()); 669 stdin 670 .write_vectored(&[IoSlice::new(b"Hello, world!")]) 671 .await 672 .unwrap(); 673 stdin.flush().await.unwrap(); 674 stdin.shutdown().await.unwrap(); 675 }); 676 677 let mut stdout = child.take_stdout().expect("Failed to open stdout"); 678 let stdout_handle = crate::spawn(async move { 679 let mut buf = Vec::new(); 680 stdout.read_to_end(&mut buf).await.unwrap(); 681 let str = "!dlrow ,olleH"; 682 assert!(String::from_utf8(buf).unwrap().contains(str)); 683 }); 684 685 let mut stderr = child.take_stderr().expect("Failed to open stderr"); 686 let stderr_handle = crate::spawn(async move { 687 let mut buf = Vec::new(); 688 stderr.read_to_end(&mut buf).await.unwrap(); 689 assert!(buf.is_empty()); 690 }); 691 692 let status = child.wait().await.unwrap(); 693 assert!(status.success()); 694 695 stdin_handle.await.unwrap(); 696 stdout_handle.await.unwrap(); 697 stderr_handle.await.unwrap(); 698 }); 699 crate::block_on(handle).unwrap(); 700 } 701 702 /// Ut test cases for `kill()`. 703 /// 704 /// # Brief 705 /// 1. Create a `Command` with arg. 706 /// 2. Use `spawn()` create a child handle 707 /// 3. Use `kill()` to kill the child handle. 708 #[test] ut_process_kill_test()709 fn ut_process_kill_test() { 710 let handle = crate::spawn(async { 711 let mut command = Command::new("echo"); 712 command.arg("Hello, world!"); 713 let mut child = command.spawn().unwrap(); 714 715 assert!(child.kill().await.is_ok()); 716 }); 717 crate::block_on(handle).unwrap(); 718 } 719 720 /// UT test cases for drop. 721 /// 722 /// # Brief 723 /// 1. Create a `Command` with kill_on_drop. 724 /// 2. Use `spawn()` create a child handle 725 /// 3. Use `drop()` to drop the child handle. 726 #[test] ut_process_drop_test()727 fn ut_process_drop_test() { 728 let handle = crate::spawn(async { 729 let mut command = Command::new("echo"); 730 command.arg("Hello, world!").kill_on_drop(true); 731 let child = command.spawn(); 732 assert!(child.is_ok()); 733 drop(child.unwrap()); 734 735 let mut command = Command::new("echo"); 736 command.arg("Hello, world!"); 737 let child = command.spawn(); 738 assert!(child.is_ok()); 739 drop(child.unwrap()); 740 }); 741 crate::block_on(handle).unwrap(); 742 } 743 744 /// UT test cases for command debug. 745 /// 746 /// # Brief 747 /// 1. Debug Command and Child. 748 /// 2. Check format is correct. 749 #[test] ut_process_debug_test()750 fn ut_process_debug_test() { 751 let handle = crate::spawn(async { 752 let mut command = Command::new("echo"); 753 assert_eq!( 754 format!("{command:?}"), 755 "Command { std: \"echo\", kill: false }" 756 ); 757 let mut child = command.spawn().unwrap(); 758 759 assert_eq!(format!("{child:?}"), "Child { state: Pending(Some(Child { stdin: None, stdout: None, stderr: None, .. })), kill_on_drop: false, stdin: None, stdout: None, stderr: None }"); 760 let status = child.wait().await.unwrap(); 761 assert!(status.success()); 762 }); 763 crate::block_on(handle).unwrap(); 764 } 765 } 766