1+ use std:: process:: { Child , ExitStatus } ;
2+ use std:: io;
3+
4+ pub struct Processo {
5+ processo : Child ,
6+ }
7+
8+ impl Processo {
9+ #[ cfg( windows) ]
10+ pub fn terminate ( & mut self ) -> io:: Result < ( ) > {
11+ // No Windows, .kill() já usa TerminateProcess, que é o comportamento
12+ // esperado para terminate() e kill() do Python nessa plataforma.
13+ self . processo . kill ( )
14+ }
15+
16+ #[ cfg( not( windows) ) ]
17+ pub fn terminate ( & mut self ) -> io:: Result < ( ) > {
18+ // Em sistemas POSIX (Linux, macOS), precisamos enviar SIGTERM manualmente.
19+ // O .kill() padrão do Rust enviaria SIGKILL, que é muito agressivo.
20+
21+ // 1. Pega o PID (Process ID) do processo filho.
22+ let pid = Pid :: from_raw ( self . processo . id ( ) as i32 ) ;
23+
24+ // 2. Envia o sinal SIGTERM para o PID.
25+ match signal:: kill ( pid, Signal :: SIGTERM ) {
26+ Ok ( _) => Ok ( ( ) ) ,
27+ Err ( e) => {
28+ // 3. Converte o erro da crate 'nix' para um erro padrão 'std::io::Error'.
29+ Err ( io:: Error :: new ( io:: ErrorKind :: Other , e) )
30+ }
31+ }
32+
33+ }
34+ #[ cfg( test) ]
35+ mod tests {
36+ use super :: * ; // Importa tudo do módulo pai (Processo, etc.)
37+ use std:: thread;
38+ use std:: time:: Duration ;
39+
40+ #[ test]
41+ fn test_process_creation_and_wait_success ( ) {
42+ // Testa o caso mais básico: criar um processo que termina com sucesso.
43+ // O comando `sleep 0.1` é rápido e deve sair com código 0.
44+ let mut processo = Processo :: new ( "sleep" , & [ "0.1" ] ) . expect ( "Falha ao criar processo 'sleep'" ) ;
45+
46+ let exit_code = processo. wait ( ) . expect ( "Falha ao esperar pelo processo" ) ;
47+
48+ // Assert: Verificamos se o código de saída é 0 (sucesso).
49+ assert_eq ! ( exit_code, 0 ) ;
50+ }
51+
52+ #[ test]
53+ fn test_process_creation_fails_for_invalid_command ( ) {
54+ // Testa se a criação falha quando o comando não existe.
55+ let resultado = Processo :: new ( "comando_que_definitivamente_nao_existe_123" , & [ ] ) ;
56+
57+ // Assert: Verificamos que o resultado é um erro.
58+ assert ! ( resultado. is_err( ) ) ;
59+ }
60+
61+ #[ test]
62+ fn test_terminate_long_running_process ( ) {
63+ // 1. SETUP: Inicia um processo que demoraria muito para terminar sozinho.
64+ let mut processo = Processo :: new ( "sleep" , & [ "30" ] ) . expect ( "Falha ao criar processo 'sleep 30'" ) ;
65+ // Dá um tempinho para o SO realmente iniciar o processo.
66+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
67+
68+ // 2. ACTION: Chama o método que queremos testar.
69+ processo. terminate ( ) . expect ( "Falha ao enviar sinal de terminate" ) ;
70+
71+ // 3. ASSERT: Verifica o resultado.
72+ let exit_code = processo. wait ( ) . expect ( "Falha ao esperar pelo processo terminado" ) ;
73+
74+ #[ cfg( not( windows) ) ]
75+ {
76+ // Em Linux/macOS, um processo terminado por sinal não tem um código de saída.
77+ // Nossa função wait() converte isso para -1. Esta é a verificação correta!
78+ assert_eq ! ( exit_code, -1 , "Em POSIX, o código de saída de um processo terminado por sinal deve ser -1 (na nossa implementação)" ) ;
79+ }
80+ #[ cfg( windows) ]
81+ {
82+ // No Windows, TerminateProcess força um código de saída, que geralmente é 1.
83+ assert_eq ! ( exit_code, 1 , "No Windows, o código de saída de um processo terminado geralmente é 1" ) ;
84+ }
85+ }
86+
87+ #[ test]
88+ fn test_kill_long_running_process ( ) {
89+ // O teste para kill() é quase idêntico ao de terminate(),
90+ // pois ambos resultam em um encerramento anormal.
91+
92+ // 1. SETUP
93+ let mut processo = Processo :: new ( "sleep" , & [ "30" ] ) . expect ( "Falha ao criar processo 'sleep 30'" ) ;
94+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
95+
96+ // 2. ACTION
97+ processo. kill ( ) . expect ( "Falha ao enviar sinal de kill" ) ;
98+
99+ // 3. ASSERT
100+ let exit_code = processo. wait ( ) . expect ( "Falha ao esperar pelo processo morto" ) ;
101+
102+ #[ cfg( not( windows) ) ]
103+ {
104+ // SIGKILL também resulta em um código de saída "None", que mapeamos para -1.
105+ assert_eq ! ( exit_code, -1 , "Em POSIX, o código de saída de um processo morto por sinal deve ser -1" ) ;
106+ }
107+ #[ cfg( windows) ]
108+ {
109+ // O comportamento é o mesmo de terminate() no Windows.
110+ assert_eq ! ( exit_code, 1 , "No Windows, o código de saída de um processo morto geralmente é 1" ) ;
111+ }
112+ }
113+ }
114+
115+ }
0 commit comments