@@ -263,6 +263,8 @@ pub struct Execs {
263
263
expect_exit_code : Option < i32 > ,
264
264
expect_stdout_contains : Vec < String > ,
265
265
expect_stderr_contains : Vec < String > ,
266
+ expect_stdout_not_contains : Vec < String > ,
267
+ expect_stderr_not_contains : Vec < String > ,
266
268
expect_json : Option < Vec < Json > > ,
267
269
}
268
270
@@ -292,6 +294,16 @@ impl Execs {
292
294
self
293
295
}
294
296
297
+ pub fn with_stdout_does_not_contain < S : ToString > ( mut self , expected : S ) -> Execs {
298
+ self . expect_stdout_not_contains . push ( expected. to_string ( ) ) ;
299
+ self
300
+ }
301
+
302
+ pub fn with_stderr_does_not_contain < S : ToString > ( mut self , expected : S ) -> Execs {
303
+ self . expect_stderr_not_contains . push ( expected. to_string ( ) ) ;
304
+ self
305
+ }
306
+
295
307
pub fn with_json ( mut self , expected : & str ) -> Execs {
296
308
self . expect_json = Some ( expected. split ( "\n \n " ) . map ( |obj| {
297
309
Json :: from_str ( obj) . unwrap ( )
@@ -321,14 +333,22 @@ impl Execs {
321
333
322
334
fn match_stdout ( & self , actual : & Output ) -> ham:: MatchResult {
323
335
self . match_std ( self . expect_stdout . as_ref ( ) , & actual. stdout ,
324
- "stdout" , & actual. stderr , false ) ?;
336
+ "stdout" , & actual. stderr , MatchKind :: Exact ) ?;
325
337
for expect in self . expect_stdout_contains . iter ( ) {
326
338
self . match_std ( Some ( expect) , & actual. stdout , "stdout" ,
327
- & actual. stderr , true ) ?;
339
+ & actual. stderr , MatchKind :: Partial ) ?;
328
340
}
329
341
for expect in self . expect_stderr_contains . iter ( ) {
330
342
self . match_std ( Some ( expect) , & actual. stderr , "stderr" ,
331
- & actual. stdout , true ) ?;
343
+ & actual. stdout , MatchKind :: Partial ) ?;
344
+ }
345
+ for expect in self . expect_stdout_not_contains . iter ( ) {
346
+ self . match_std ( Some ( expect) , & actual. stdout , "stdout" ,
347
+ & actual. stderr , MatchKind :: NotPresent ) ?;
348
+ }
349
+ for expect in self . expect_stderr_not_contains . iter ( ) {
350
+ self . match_std ( Some ( expect) , & actual. stderr , "stderr" ,
351
+ & actual. stdout , MatchKind :: NotPresent ) ?;
332
352
}
333
353
334
354
if let Some ( ref objects) = self . expect_json {
@@ -349,12 +369,12 @@ impl Execs {
349
369
350
370
fn match_stderr ( & self , actual : & Output ) -> ham:: MatchResult {
351
371
self . match_std ( self . expect_stderr . as_ref ( ) , & actual. stderr ,
352
- "stderr" , & actual. stdout , false )
372
+ "stderr" , & actual. stdout , MatchKind :: Exact )
353
373
}
354
374
355
375
fn match_std ( & self , expected : Option < & String > , actual : & [ u8 ] ,
356
376
description : & str , extra : & [ u8 ] ,
357
- partial : bool ) -> ham:: MatchResult {
377
+ kind : MatchKind ) -> ham:: MatchResult {
358
378
let out = match expected {
359
379
Some ( out) => out,
360
380
None => return ham:: success ( ) ,
@@ -368,33 +388,46 @@ impl Execs {
368
388
let actual = actual. replace ( "\r " , "" ) ;
369
389
let actual = actual. replace ( "\t " , "<tab>" ) ;
370
390
371
- let mut a = actual. lines ( ) ;
372
- let e = out. lines ( ) ;
373
-
374
- if partial {
375
- let mut diffs = self . diff_lines ( a. clone ( ) , e. clone ( ) , partial) ;
376
- while let Some ( ..) = a. next ( ) {
377
- let a = self . diff_lines ( a. clone ( ) , e. clone ( ) , partial) ;
378
- if a. len ( ) < diffs. len ( ) {
379
- diffs = a;
391
+ match kind {
392
+ MatchKind :: Exact => {
393
+ let a = actual. lines ( ) ;
394
+ let e = out. lines ( ) ;
395
+
396
+ let diffs = self . diff_lines ( a, e, false ) ;
397
+ ham:: expect ( diffs. is_empty ( ) ,
398
+ format ! ( "differences:\n \
399
+ {}\n \n \
400
+ other output:\n \
401
+ `{}`", diffs. join( "\n " ) ,
402
+ String :: from_utf8_lossy( extra) ) )
403
+ }
404
+ MatchKind :: Partial => {
405
+ let mut a = actual. lines ( ) ;
406
+ let e = out. lines ( ) ;
407
+
408
+ let mut diffs = self . diff_lines ( a. clone ( ) , e. clone ( ) , true ) ;
409
+ while let Some ( ..) = a. next ( ) {
410
+ let a = self . diff_lines ( a. clone ( ) , e. clone ( ) , true ) ;
411
+ if a. len ( ) < diffs. len ( ) {
412
+ diffs = a;
413
+ }
380
414
}
415
+ ham:: expect ( diffs. is_empty ( ) ,
416
+ format ! ( "expected to find:\n \
417
+ {}\n \n \
418
+ did not find in output:\n \
419
+ {}", out,
420
+ actual) )
421
+ }
422
+ MatchKind :: NotPresent => {
423
+ ham:: expect ( !actual. contains ( out) ,
424
+ format ! ( "expected not to find:\n \
425
+ {}\n \n \
426
+ but found in output:\n \
427
+ {}", out,
428
+ actual) )
381
429
}
382
- ham:: expect ( diffs. is_empty ( ) ,
383
- format ! ( "expected to find:\n \
384
- {}\n \n \
385
- did not find in output:\n \
386
- {}", out,
387
- actual) )
388
- } else {
389
- let diffs = self . diff_lines ( a, e, partial) ;
390
- ham:: expect ( diffs. is_empty ( ) ,
391
- format ! ( "differences:\n \
392
- {}\n \n \
393
- other output:\n \
394
- `{}`", diffs. join( "\n " ) ,
395
- String :: from_utf8_lossy( extra) ) )
396
430
}
397
-
398
431
}
399
432
400
433
fn match_json ( & self , expected : & Json , line : & str ) -> ham:: MatchResult {
@@ -441,6 +474,13 @@ impl Execs {
441
474
}
442
475
}
443
476
477
+ #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
478
+ enum MatchKind {
479
+ Exact ,
480
+ Partial ,
481
+ NotPresent ,
482
+ }
483
+
444
484
pub fn lines_match ( expected : & str , mut actual : & str ) -> bool {
445
485
let expected = substitute_macros ( expected) ;
446
486
for ( i, part) in expected. split ( "[..]" ) . enumerate ( ) {
@@ -589,6 +629,8 @@ pub fn execs() -> Execs {
589
629
expect_exit_code : None ,
590
630
expect_stdout_contains : Vec :: new ( ) ,
591
631
expect_stderr_contains : Vec :: new ( ) ,
632
+ expect_stdout_not_contains : Vec :: new ( ) ,
633
+ expect_stderr_not_contains : Vec :: new ( ) ,
592
634
expect_json : None ,
593
635
}
594
636
}
0 commit comments