@@ -151,13 +151,126 @@ fn test_pipe() {
151151 let wc_cmd = "wc" ;
152152 assert ! ( run_cmd!( ls | $wc_cmd) . is_ok( ) ) ;
153153
154- // test pipefail
155- assert ! ( run_cmd!( false | true ) . is_err( ) ) ;
156- assert ! ( run_fun!( false | true ) . is_err( ) ) ;
157- assert ! ( run_fun!( ignore false | true ) . is_ok( ) ) ;
158- set_pipefail ( false ) ;
159- assert ! ( run_fun!( false | true ) . is_ok( ) ) ;
160- set_pipefail ( true ) ;
154+ // test `ignore` command and pipefail mode
155+ // FIXME: make set_pipefail() thread safe, then move this to a separate test_ignore_and_pipefail()
156+ struct TestCase {
157+ /// Run the test case, returning whether the result `.is_ok()`.
158+ code : fn ( ) -> bool ,
159+ /// Stringified version of `code`, for identifying assertion failures.
160+ code_str : & ' static str ,
161+ /// Do we expect `.is_ok()` when pipefail is on?
162+ expected_ok_pipefail_on : bool ,
163+ /// Do we expect `.is_ok()` when pipefail is off?
164+ expected_ok_pipefail_off : bool ,
165+ }
166+ /// Make a function for [TestCase::code].
167+ ///
168+ /// Usage: `code!((macro!(command)).extra)`
169+ /// - `(macro!(command)).extra` is an expression of type CmdResult
170+ macro_rules! code {
171+ ( ( $macro: tt $bang: tt ( $( $command: tt) +) ) $( $after: tt) * ) => {
172+ || $macro$bang( $( $command) +) $( $after) * . is_ok( )
173+ } ;
174+ }
175+ /// Make a string for [TestCase::code_str].
176+ ///
177+ /// Usage: `code_str!((macro!(command)).extra)`
178+ /// - `(macro!(command)).extra` is an expression of type CmdResult
179+ macro_rules! code_str {
180+ ( ( $macro: tt $bang: tt ( $( $command: tt) +) ) $( $after: tt) * ) => {
181+ stringify!( $macro$bang( $( $command) +) $( $after) * . is_ok( ) )
182+ } ;
183+ }
184+ /// Make a [TestCase].
185+ /// Usage: `test_case!(true/false, true/false, (macro!(command)).extra)`
186+ /// - the first `true/false` is TestCase::expected_ok_pipefail_on
187+ /// - the second `true/false` is TestCase::expected_ok_pipefail_off
188+ /// - `(macro!(command)).extra` is an expression of type CmdResult
189+ macro_rules! test_case {
190+ ( $expected_ok_pipefail_on: expr, $expected_ok_pipefail_off: expr, ( $macro: tt $bang: tt ( $( $command: tt) +) ) $( $after: tt) * ) => {
191+ TestCase {
192+ code: code!( ( $macro $bang ( $( $command) +) ) $( $after) * ) ,
193+ code_str: code_str!( ( $macro $bang ( $( $command) +) ) $( $after) * ) ,
194+ expected_ok_pipefail_on: $expected_ok_pipefail_on,
195+ expected_ok_pipefail_off: $expected_ok_pipefail_off,
196+ }
197+ } ;
198+ }
199+ /// Generate test cases for the given entry point.
200+ /// For each test case, every entry point should yield the same results.
201+ macro_rules! test_cases_for_entry_point {
202+ ( ( $macro: tt $bang: tt ( ...) ) $( $after: tt) * ) => {
203+ & [
204+ // Use result of last command in pipeline, if all others exit successfully.
205+ test_case!( true , true , ( $macro $bang ( true ) ) $( $after) * ) ,
206+ test_case!( false , false , ( $macro $bang ( false ) ) $( $after) * ) ,
207+ test_case!( true , true , ( $macro $bang ( true | true ) ) $( $after) * ) ,
208+ test_case!( false , false , ( $macro $bang ( true | false ) ) $( $after) * ) ,
209+ // Use failure of other commands, if pipefail is on.
210+ test_case!( false , true , ( $macro $bang ( false | true ) ) $( $after) * ) ,
211+ // Use failure of last command in pipeline.
212+ test_case!( false , false , ( $macro $bang ( false | false ) ) $( $after) * ) ,
213+ // Ignore all failures, when using `ignore` command.
214+ test_case!( true , true , ( $macro $bang ( ignore true ) ) $( $after) * ) ,
215+ test_case!( true , true , ( $macro $bang ( ignore false ) ) $( $after) * ) ,
216+ test_case!( true , true , ( $macro $bang ( ignore true | true ) ) $( $after) * ) ,
217+ test_case!( true , true , ( $macro $bang ( ignore true | false ) ) $( $after) * ) ,
218+ test_case!( true , true , ( $macro $bang ( ignore false | true ) ) $( $after) * ) ,
219+ test_case!( true , true , ( $macro $bang ( ignore false | false ) ) $( $after) * ) ,
220+ ]
221+ } ;
222+ }
223+
224+ let test_cases: & [ & [ TestCase ] ] = & [
225+ test_cases_for_entry_point ! ( ( run_cmd!( ...) ) ) ,
226+ test_cases_for_entry_point ! ( ( run_fun!( ...) ) . map( |_stdout| ( ) ) ) ,
227+ test_cases_for_entry_point ! ( ( spawn!( ...) ) . unwrap( ) . wait( ) ) ,
228+ test_cases_for_entry_point ! ( ( spawn_with_output!( ...) ) . unwrap( ) . wait_with_all( ) . 0 ) ,
229+ test_cases_for_entry_point ! ( ( spawn_with_output!( ...) )
230+ . unwrap( )
231+ . wait_with_output( )
232+ . map( |_stdout| ( ) ) ) ,
233+ test_cases_for_entry_point ! ( ( spawn_with_output!( ...) )
234+ . unwrap( )
235+ . wait_with_raw_output( & mut vec![ ] ) ) ,
236+ // FIXME: wait_with_pipe() is currently busted
237+ // test_cases_for_entry_point!((spawn_with_output!(...))
238+ // .unwrap()
239+ // .wait_with_pipe(&mut |_stdout| {})),
240+ ] ;
241+
242+ macro_rules! check_eq {
243+ ( $left: expr, $right: expr, $( $rest: tt) +) => { {
244+ let left = $left;
245+ let right = $right;
246+ if left != right {
247+ eprintln!( "assertion failed ({} != {}): {}" , left, right, format!( $( $rest) +) ) ;
248+ false
249+ } else {
250+ true
251+ }
252+ } } ;
253+ }
254+
255+ let mut ok = true ;
256+ for case in test_cases. iter ( ) . flat_map ( |items| items. iter ( ) ) {
257+ ok &= check_eq ! (
258+ ( case. code) ( ) ,
259+ case. expected_ok_pipefail_on,
260+ "{} when pipefail is on" ,
261+ case. code_str
262+ ) ;
263+ set_pipefail ( false ) ;
264+ ok &= check_eq ! (
265+ ( case. code) ( ) ,
266+ case. expected_ok_pipefail_off,
267+ "{} when pipefail is off" ,
268+ case. code_str
269+ ) ;
270+ set_pipefail ( true ) ;
271+ }
272+
273+ assert ! ( ok) ;
161274}
162275
163276#[ test]
0 commit comments