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