1- use std:: process:: { Child , ExitStatus } ;
21use std:: io;
2+ use std:: process:: { Child , ExitStatus } ;
3+ #[ cfg( not( windows) ) ]
4+ use nix:: {
5+ sys:: signal:: { self , Signal } ,
6+ unistd:: Pid ,
7+ } ;
38
49pub struct Processo {
5- processo : Child ,
10+ pub processo : Child ,
611}
712
813impl Processo {
14+
915 #[ cfg( windows) ]
1016 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.
1317 self . processo . kill ( )
1418 }
1519
1620 #[ cfg( not( windows) ) ]
1721 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.
2222 let pid = Pid :: from_raw ( self . processo . id ( ) as i32 ) ;
23-
24- // 2. Envia o sinal SIGTERM para o PID.
2523 match signal:: kill ( pid, Signal :: SIGTERM ) {
2624 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- }
25+ Err ( e) => Err ( io:: Error :: new ( io:: ErrorKind :: Other , e) ) ,
3126 }
32-
3327 }
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- }
5128
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- }
29+ pub fn kill ( & mut self ) -> io:: Result < ( ) > {
30+ self . processo . kill ( )
31+ }
32+
33+ }
34+ mod tests {
35+ use super :: * ;
36+ use std:: process:: Command ;
37+ use std:: thread;
38+ use std:: time:: Duration ;
6039
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- }
40+ fn create_long_running_command ( ) -> Command {
41+ if cfg ! ( windows) {
42+ let mut cmd = Command :: new ( "timeout" ) ;
43+ cmd. arg ( "/T" ) . arg ( "30" ) ;
44+ cmd
45+ } else {
46+ let mut cmd = Command :: new ( "sleep" ) ;
47+ cmd. arg ( "30" ) ;
48+ cmd
8549 }
50+ }
8651
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-
52+ #[ test]
53+ fn test_terminate_a_running_process ( ) {
54+ let child = create_long_running_command ( )
55+ . spawn ( )
56+ . expect ( "Falha ao iniciar processo para o teste de terminate" ) ;
57+
58+ let mut processo = Processo { processo : child } ;
59+
60+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
61+
62+ processo. terminate ( ) . expect ( "Falha ao chamar terminate" ) ;
63+
64+ let exit_code = processo. wait ( ) . expect ( "Falha ao esperar pelo processo terminado" ) ;
65+
66+ let expected_code = if cfg ! ( windows) { 1 } else { -1 } ;
67+ assert_eq ! ( exit_code, expected_code, "O código de saída após terminate não foi o esperado." ) ;
68+ }
69+
70+ #[ test]
71+ fn test_kill_a_running_process ( ) {
72+ let child = create_long_running_command ( )
73+ . spawn ( )
74+ . expect ( "Falha ao iniciar processo para o teste de kill" ) ;
75+
76+
77+ let mut processo = Processo { processo : child } ;
78+
79+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
80+
81+ processo. kill ( ) . expect ( "Falha ao chamar kill" ) ;
82+
83+
84+ let exit_code = processo. wait ( ) . expect ( "Falha ao esperar pelo processo morto" ) ;
85+
86+ let expected_code = if cfg ! ( windows) { 1 } else { -1 } ;
87+ assert_eq ! ( exit_code, expected_code, "O código de saída após kill não foi o esperado." ) ;
88+ }
89+
90+ #[ test]
91+ fn test_wait_on_a_process_that_finishes_normally ( ) {
92+ let mut command = if cfg ! ( windows) {
93+ let mut cmd = Command :: new ( "timeout" ) ;
94+ cmd. arg ( "/T" ) . arg ( "1" ) ;
95+ cmd
96+ } else {
97+ let mut cmd = Command :: new ( "sleep" ) ;
98+ cmd. arg ( "1" ) ;
99+ cmd
100+ } ;
101+
102+ let child = command. spawn ( ) . expect ( "Falha ao iniciar processo curto" ) ;
103+ let mut processo = Processo { processo : child } ;
104+
105+ let exit_code = processo. wait ( ) . expect ( "Falha ao esperar pelo processo" ) ;
106+ assert_eq ! ( exit_code, 0 ) ;
107+ }
115108}
0 commit comments