@@ -21,7 +21,9 @@ use std::{
21
21
22
22
use fm:: FMBuilder ;
23
23
use getopts:: Options ;
24
- use libc:: { fcntl, poll, pollfd, F_GETFL , F_SETFL , O_NONBLOCK , POLLERR , POLLHUP , POLLIN } ;
24
+ use libc:: {
25
+ close, fcntl, poll, pollfd, F_GETFL , F_SETFL , O_NONBLOCK , POLLERR , POLLHUP , POLLIN , POLLOUT ,
26
+ } ;
25
27
use termcolor:: { Color , ColorChoice , ColorSpec , StandardStream , WriteColor } ;
26
28
use threadpool:: ThreadPool ;
27
29
use walkdir:: WalkDir ;
@@ -369,6 +371,12 @@ impl LangTester {
369
371
if let Some ( ref status) = test. status {
370
372
eprintln ! ( "\n ---- lang_tests::{} status ----\n {}" , test_fname, status) ;
371
373
}
374
+ if test. stdin_remaining != 0 {
375
+ eprintln ! (
376
+ "\n ---- lang_tests::{} stdin ----\n {} bytes of stdin were not consumed" ,
377
+ test_fname, test. stdin_remaining
378
+ ) ;
379
+ }
372
380
if let Some ( ref stderr) = test. stderr {
373
381
eprintln ! (
374
382
"\n ---- lang_tests::{} stderr ----\n {}\n " ,
@@ -425,6 +433,7 @@ pub(crate) enum Status {
425
433
#[ derive( Clone , Debug ) ]
426
434
pub ( crate ) struct TestCmd < ' a > {
427
435
pub status : Status ,
436
+ pub stdin : Option < String > ,
428
437
pub stderr : Vec < & ' a str > ,
429
438
pub stdout : Vec < & ' a str > ,
430
439
/// A list of custom command line arguments which should be passed when
@@ -436,6 +445,7 @@ impl<'a> TestCmd<'a> {
436
445
pub fn default ( ) -> Self {
437
446
Self {
438
447
status : Status :: Success ,
448
+ stdin : None ,
439
449
stderr : vec ! [ "..." ] ,
440
450
stdout : vec ! [ "..." ] ,
441
451
args : Vec :: new ( ) ,
@@ -454,6 +464,7 @@ pub(crate) struct Tests<'a> {
454
464
#[ derive( Debug , PartialEq ) ]
455
465
struct TestFailure {
456
466
status : Option < String > ,
467
+ stdin_remaining : usize ,
457
468
stderr : Option < String > ,
458
469
stdout : Option < String > ,
459
470
}
@@ -597,14 +608,16 @@ fn run_tests<'a>(
597
608
598
609
let mut failure = TestFailure {
599
610
status : None ,
611
+ stdin_remaining : 0 ,
600
612
stderr : None ,
601
613
stdout : None ,
602
614
} ;
603
615
for ( cmd_name, mut cmd) in cmd_pairs {
604
616
let default_test = TestCmd :: default ( ) ;
605
617
let test = tests. get ( & cmd_name) . unwrap_or ( & default_test) ;
606
618
cmd. args ( & test. args ) ;
607
- let ( status, stderr, stdout) = run_cmd ( inner. clone ( ) , & test_fname, cmd) ;
619
+ let ( status, stdin_remaining, stderr, stdout) =
620
+ run_cmd ( inner. clone ( ) , & test_fname, cmd, & test) ;
608
621
609
622
let mut meant_to_error = false ;
610
623
@@ -635,7 +648,7 @@ fn run_tests<'a>(
635
648
// successfully (i.e. if the stderr test failed, print that out; but, equally, if
636
649
// stderr wasn't specified as a test, print it out, because the user can't
637
650
// otherwise know what it contains).
638
- if !( pass_status && pass_stderr && pass_stdout) {
651
+ if !( pass_status && stdin_remaining == 0 && pass_stderr && pass_stdout) {
639
652
if !pass_status || failure. status . is_none ( ) {
640
653
match test. status {
641
654
Status :: Success | Status :: Error => {
@@ -670,6 +683,8 @@ fn run_tests<'a>(
670
683
failure. stdout = Some ( stdout) ;
671
684
}
672
685
686
+ failure. stdin_remaining = stdin_remaining;
687
+
673
688
// If a sub-test failed, bail out immediately, otherwise subsequent sub-tests
674
689
// will overwrite the failure output!
675
690
break ;
@@ -694,6 +709,7 @@ fn run_tests<'a>(
694
709
if failure
695
710
!= ( TestFailure {
696
711
status : None ,
712
+ stdin_remaining : 0 ,
697
713
stderr : None ,
698
714
stdout : None ,
699
715
} )
@@ -721,34 +737,43 @@ fn run_cmd(
721
737
inner : Arc < LangTesterPooler > ,
722
738
test_fname : & str ,
723
739
mut cmd : Command ,
724
- ) -> ( ExitStatus , String , String ) {
740
+ test : & TestCmd ,
741
+ ) -> ( ExitStatus , usize , String , String ) {
725
742
// The basic sequence here is:
726
743
// 1) Spawn the command
727
744
// 2) Read everything from stderr & stdout until they are both disconnected
728
745
// 3) wait() for the command to finish
729
746
730
747
let mut child = cmd
748
+ . stdin ( process:: Stdio :: piped ( ) )
731
749
. stderr ( process:: Stdio :: piped ( ) )
732
750
. stdout ( process:: Stdio :: piped ( ) )
733
- . stdin ( process:: Stdio :: null ( ) )
734
751
. spawn ( )
735
752
. unwrap_or_else ( |_| fatal ( & format ! ( "Couldn't run command {:?}." , cmd) ) ) ;
736
753
754
+ let stdin = child. stdin . as_mut ( ) . unwrap ( ) ;
737
755
let stderr = child. stderr . as_mut ( ) . unwrap ( ) ;
738
756
let stdout = child. stdout . as_mut ( ) . unwrap ( ) ;
739
757
758
+ let stdin_fd = stdin. as_raw_fd ( ) ;
740
759
let stderr_fd = stderr. as_raw_fd ( ) ;
741
760
let stdout_fd = stdout. as_raw_fd ( ) ;
742
- if set_nonblock ( stderr_fd)
761
+ if set_nonblock ( stdin_fd)
762
+ . and_then ( |_| set_nonblock ( stderr_fd) )
743
763
. and_then ( |_| set_nonblock ( stdout_fd) )
744
764
. is_err ( )
745
765
{
746
- fatal ( "Couldn't set stderr and/or stdout to be non-blocking" ) ;
766
+ fatal ( "Couldn't set stdin and/or stderr and/or stdout to be non-blocking" ) ;
747
767
}
748
768
749
769
let mut cap_stderr = String :: new ( ) ;
750
770
let mut cap_stdout = String :: new ( ) ;
751
771
let mut pollfds = [
772
+ pollfd {
773
+ fd : stdin_fd,
774
+ events : 0 ,
775
+ revents : 0 ,
776
+ } ,
752
777
pollfd {
753
778
fd : stderr_fd,
754
779
events : POLLERR | POLLIN | POLLHUP ,
@@ -760,6 +785,14 @@ fn run_cmd(
760
785
revents : 0 ,
761
786
} ,
762
787
] ;
788
+ let mut stdin_off = 0 ;
789
+ let mut stdin_finished;
790
+ if test. stdin . is_none ( ) {
791
+ stdin_finished = true ;
792
+ } else {
793
+ stdin_finished = false ;
794
+ pollfds[ 0 ] . events = POLLERR | POLLOUT | POLLHUP ;
795
+ }
763
796
let mut buf = [ 0 ; READBUF ] ;
764
797
let start = Instant :: now ( ) ;
765
798
let mut last_warning = Instant :: now ( ) ;
@@ -774,8 +807,28 @@ fn run_cmd(
774
807
. unwrap_or ( 1000 ) ,
775
808
)
776
809
. unwrap_or ( 1000 ) ;
777
- if unsafe { poll ( ( & mut pollfds) as * mut _ as * mut pollfd , 2 , timeout) } != -1 {
778
- if pollfds[ 0 ] . revents & POLLIN == POLLIN {
810
+ if unsafe { poll ( ( & mut pollfds) as * mut _ as * mut pollfd , 3 , timeout) } != -1 {
811
+ if pollfds[ 0 ] . revents & POLLOUT == POLLOUT {
812
+ // This unwrap() is safe as long as POLLOUT is removed from stdin's events when
813
+ // stdin is closed.
814
+ let stdin_str = test. stdin . as_ref ( ) . unwrap ( ) ;
815
+ if let Ok ( i) = stdin. write ( & stdin_str. as_bytes ( ) [ stdin_off..] ) {
816
+ stdin_off += i;
817
+ }
818
+ if stdin_off == stdin_str. len ( ) {
819
+ stdin_finished = true ;
820
+ // This is a bit icky, because the `stdin` variable will later close stdin when the
821
+ // variable is dropped. However, some programs expect stdin to be closed before
822
+ // they'll continue, so this is the least worst option.
823
+ unsafe {
824
+ close ( stdin_fd) ;
825
+ }
826
+ // Remove POLLOUT from events so that we don't try reading anything again.
827
+ pollfds[ 0 ] . events = POLLERR | POLLHUP ;
828
+ }
829
+ }
830
+
831
+ if pollfds[ 1 ] . revents & POLLIN == POLLIN {
779
832
if let Ok ( i) = stderr. read ( & mut buf) {
780
833
if i > 0 {
781
834
let utf8 = str:: from_utf8 ( & buf[ ..i] ) . unwrap_or_else ( |_| {
@@ -789,7 +842,7 @@ fn run_cmd(
789
842
}
790
843
}
791
844
792
- if pollfds[ 1 ] . revents & POLLIN == POLLIN {
845
+ if pollfds[ 2 ] . revents & POLLIN == POLLIN {
793
846
if let Ok ( i) = stdout. read ( & mut buf) {
794
847
if i > 0 {
795
848
let utf8 = str:: from_utf8 ( & buf[ ..i] ) . unwrap_or_else ( |_| {
@@ -803,7 +856,10 @@ fn run_cmd(
803
856
}
804
857
}
805
858
806
- if pollfds[ 0 ] . revents & POLLHUP == POLLHUP && pollfds[ 1 ] . revents & POLLHUP == POLLHUP {
859
+ if ( stdin_finished || pollfds[ 0 ] . revents & POLLHUP == POLLHUP )
860
+ && pollfds[ 1 ] . revents & POLLHUP == POLLHUP
861
+ && pollfds[ 2 ] . revents & POLLHUP == POLLHUP
862
+ {
807
863
break ;
808
864
}
809
865
}
@@ -861,7 +917,14 @@ fn run_cmd(
861
917
}
862
918
}
863
919
} ;
864
- ( status, cap_stderr, cap_stdout)
920
+
921
+ let stdin_remaining = if stdin_finished {
922
+ 0
923
+ } else {
924
+ let stdin_str = test. stdin . as_ref ( ) . unwrap ( ) ;
925
+ stdin_str. len ( ) - stdin_off
926
+ } ;
927
+ ( status, stdin_remaining, cap_stderr, cap_stdout)
865
928
}
866
929
867
930
fn set_nonblock ( fd : c_int ) -> Result < ( ) , io:: Error > {
0 commit comments