1use std::borrow::Cow;
4use std::error::Error;
5use std::fmt;
6use std::process;
7use std::str;
8
9#[cfg(feature = "color")]
10use anstream::panic;
11use predicates::str::PredicateStrExt;
12use predicates_tree::CaseTreeExt;
13
14use crate::output::output_fmt;
15use crate::output::DebugBytes;
16
17pub trait OutputAssertExt {
34 fn assert(self) -> Assert;
51}
52
53impl OutputAssertExt for process::Output {
54 fn assert(self) -> Assert {
55 Assert::new(self)
56 }
57}
58
59impl OutputAssertExt for &mut process::Command {
60 fn assert(self) -> Assert {
61 let output = match self.output() {
62 Ok(output) => output,
63 Err(err) => {
64 panic!("Failed to spawn {self:?}: {err}");
65 }
66 };
67 Assert::new(output).append_context("command", format!("{self:?}"))
68 }
69}
70
71pub struct Assert {
90 output: process::Output,
91 context: Vec<(&'static str, Box<dyn fmt::Display + Send + Sync>)>,
92}
93
94impl Assert {
95 pub fn new(output: process::Output) -> Self {
99 Self {
100 output,
101 context: vec![],
102 }
103 }
104
105 fn into_error(self, reason: AssertReason) -> AssertError {
106 AssertError {
107 assert: self,
108 reason,
109 }
110 }
111
112 pub fn append_context<D>(mut self, name: &'static str, context: D) -> Self
128 where
129 D: fmt::Display + Send + Sync + 'static,
130 {
131 self.context.push((name, Box::new(context)));
132 self
133 }
134
135 pub fn get_output(&self) -> &process::Output {
139 &self.output
140 }
141
142 #[track_caller]
157 pub fn success(self) -> Self {
158 self.try_success().unwrap_or_else(AssertError::panic)
159 }
160
161 pub fn try_success(self) -> AssertResult {
163 if !self.output.status.success() {
164 let actual_code = self.output.status.code();
165 return Err(self.into_error(AssertReason::UnexpectedFailure { actual_code }));
166 }
167 Ok(self)
168 }
169
170 #[track_caller]
186 pub fn failure(self) -> Self {
187 self.try_failure().unwrap_or_else(AssertError::panic)
188 }
189
190 pub fn try_failure(self) -> AssertResult {
192 if self.output.status.success() {
193 return Err(self.into_error(AssertReason::UnexpectedSuccess));
194 }
195 Ok(self)
196 }
197
198 #[track_caller]
200 pub fn interrupted(self) -> Self {
201 self.try_interrupted().unwrap_or_else(AssertError::panic)
202 }
203
204 pub fn try_interrupted(self) -> AssertResult {
206 if self.output.status.code().is_some() {
207 return Err(self.into_error(AssertReason::UnexpectedCompletion));
208 }
209 Ok(self)
210 }
211
212 #[track_caller]
261 pub fn code<I, P>(self, pred: I) -> Self
262 where
263 I: IntoCodePredicate<P>,
264 P: predicates_core::Predicate<i32>,
265 {
266 self.try_code(pred).unwrap_or_else(AssertError::panic)
267 }
268
269 pub fn try_code<I, P>(self, pred: I) -> AssertResult
271 where
272 I: IntoCodePredicate<P>,
273 P: predicates_core::Predicate<i32>,
274 {
275 self.code_impl(&pred.into_code())
276 }
277
278 fn code_impl(self, pred: &dyn predicates_core::Predicate<i32>) -> AssertResult {
279 let actual_code = if let Some(actual_code) = self.output.status.code() {
280 actual_code
281 } else {
282 return Err(self.into_error(AssertReason::CommandInterrupted));
283 };
284 if let Some(case) = pred.find_case(false, &actual_code) {
285 return Err(self.into_error(AssertReason::UnexpectedReturnCode {
286 case_tree: CaseTree(case.tree()),
287 }));
288 }
289 Ok(self)
290 }
291
292 #[track_caller]
359 pub fn stdout<I, P>(self, pred: I) -> Self
360 where
361 I: IntoOutputPredicate<P>,
362 P: predicates_core::Predicate<[u8]>,
363 {
364 self.try_stdout(pred).unwrap_or_else(AssertError::panic)
365 }
366
367 pub fn try_stdout<I, P>(self, pred: I) -> AssertResult
369 where
370 I: IntoOutputPredicate<P>,
371 P: predicates_core::Predicate<[u8]>,
372 {
373 self.stdout_impl(&pred.into_output())
374 }
375
376 fn stdout_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult {
377 {
378 let actual = &self.output.stdout;
379 if let Some(case) = pred.find_case(false, actual) {
380 return Err(self.into_error(AssertReason::UnexpectedStdout {
381 case_tree: CaseTree(case.tree()),
382 }));
383 }
384 }
385 Ok(self)
386 }
387
388 #[track_caller]
455 pub fn stderr<I, P>(self, pred: I) -> Self
456 where
457 I: IntoOutputPredicate<P>,
458 P: predicates_core::Predicate<[u8]>,
459 {
460 self.try_stderr(pred).unwrap_or_else(AssertError::panic)
461 }
462
463 pub fn try_stderr<I, P>(self, pred: I) -> AssertResult
465 where
466 I: IntoOutputPredicate<P>,
467 P: predicates_core::Predicate<[u8]>,
468 {
469 self.stderr_impl(&pred.into_output())
470 }
471
472 fn stderr_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult {
473 {
474 let actual = &self.output.stderr;
475 if let Some(case) = pred.find_case(false, actual) {
476 return Err(self.into_error(AssertReason::UnexpectedStderr {
477 case_tree: CaseTree(case.tree()),
478 }));
479 }
480 }
481 Ok(self)
482 }
483}
484
485impl fmt::Display for Assert {
486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 let palette = crate::Palette::color();
488 for (name, context) in &self.context {
489 writeln!(f, "{:#}=`{:#}`", palette.key(name), palette.value(context))?;
490 }
491 output_fmt(&self.output, f)
492 }
493}
494
495impl fmt::Debug for Assert {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 f.debug_struct("Assert")
498 .field("output", &self.output)
499 .finish()
500 }
501}
502
503pub trait IntoCodePredicate<P>
528where
529 P: predicates_core::Predicate<i32>,
530{
531 type Predicate;
533
534 fn into_code(self) -> P;
536}
537
538impl<P> IntoCodePredicate<P> for P
539where
540 P: predicates_core::Predicate<i32>,
541{
542 type Predicate = P;
543
544 fn into_code(self) -> Self::Predicate {
545 self
546 }
547}
548
549#[derive(Debug)]
566pub struct EqCodePredicate(predicates::ord::EqPredicate<i32>);
567
568impl EqCodePredicate {
569 pub(crate) fn new(value: i32) -> Self {
570 let pred = predicates::ord::eq(value);
571 EqCodePredicate(pred)
572 }
573}
574
575impl predicates_core::reflection::PredicateReflection for EqCodePredicate {
576 fn parameters<'a>(
577 &'a self,
578 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
579 self.0.parameters()
580 }
581
582 fn children<'a>(
584 &'a self,
585 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
586 self.0.children()
587 }
588}
589
590impl predicates_core::Predicate<i32> for EqCodePredicate {
591 fn eval(&self, item: &i32) -> bool {
592 self.0.eval(item)
593 }
594
595 fn find_case<'a>(
596 &'a self,
597 expected: bool,
598 variable: &i32,
599 ) -> Option<predicates_core::reflection::Case<'a>> {
600 self.0.find_case(expected, variable)
601 }
602}
603
604impl fmt::Display for EqCodePredicate {
605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606 self.0.fmt(f)
607 }
608}
609
610impl IntoCodePredicate<EqCodePredicate> for i32 {
611 type Predicate = EqCodePredicate;
612
613 fn into_code(self) -> Self::Predicate {
614 Self::Predicate::new(self)
615 }
616}
617
618#[derive(Debug)]
635pub struct InCodePredicate(predicates::iter::InPredicate<i32>);
636
637impl InCodePredicate {
638 pub(crate) fn new<I: IntoIterator<Item = i32>>(value: I) -> Self {
639 let pred = predicates::iter::in_iter(value);
640 InCodePredicate(pred)
641 }
642}
643
644impl predicates_core::reflection::PredicateReflection for InCodePredicate {
645 fn parameters<'a>(
646 &'a self,
647 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
648 self.0.parameters()
649 }
650
651 fn children<'a>(
653 &'a self,
654 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
655 self.0.children()
656 }
657}
658
659impl predicates_core::Predicate<i32> for InCodePredicate {
660 fn eval(&self, item: &i32) -> bool {
661 self.0.eval(item)
662 }
663
664 fn find_case<'a>(
665 &'a self,
666 expected: bool,
667 variable: &i32,
668 ) -> Option<predicates_core::reflection::Case<'a>> {
669 self.0.find_case(expected, variable)
670 }
671}
672
673impl fmt::Display for InCodePredicate {
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675 self.0.fmt(f)
676 }
677}
678
679impl IntoCodePredicate<InCodePredicate> for Vec<i32> {
680 type Predicate = InCodePredicate;
681
682 fn into_code(self) -> Self::Predicate {
683 Self::Predicate::new(self)
684 }
685}
686
687impl IntoCodePredicate<InCodePredicate> for &'static [i32] {
688 type Predicate = InCodePredicate;
689
690 fn into_code(self) -> Self::Predicate {
691 Self::Predicate::new(self.iter().cloned())
692 }
693}
694
695pub trait IntoOutputPredicate<P>
722where
723 P: predicates_core::Predicate<[u8]>,
724{
725 type Predicate;
727
728 fn into_output(self) -> P;
730}
731
732impl<P> IntoOutputPredicate<P> for P
733where
734 P: predicates_core::Predicate<[u8]>,
735{
736 type Predicate = P;
737
738 fn into_output(self) -> Self::Predicate {
739 self
740 }
741}
742
743#[derive(Debug)]
761pub struct BytesContentOutputPredicate(Cow<'static, [u8]>);
762
763impl BytesContentOutputPredicate {
764 pub(crate) fn new(value: &'static [u8]) -> Self {
765 BytesContentOutputPredicate(Cow::from(value))
766 }
767
768 pub(crate) fn from_vec(value: Vec<u8>) -> Self {
769 BytesContentOutputPredicate(Cow::from(value))
770 }
771}
772
773impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate {}
774
775impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate {
776 fn eval(&self, item: &[u8]) -> bool {
777 self.0.as_ref() == item
778 }
779
780 fn find_case(
781 &self,
782 expected: bool,
783 variable: &[u8],
784 ) -> Option<predicates_core::reflection::Case<'_>> {
785 let actual = self.eval(variable);
786 if expected == actual {
787 Some(predicates_core::reflection::Case::new(Some(self), actual))
788 } else {
789 None
790 }
791 }
792}
793
794impl fmt::Display for BytesContentOutputPredicate {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 predicates::ord::eq(self.0.as_ref()).fmt(f)
797 }
798}
799
800impl IntoOutputPredicate<BytesContentOutputPredicate> for Vec<u8> {
801 type Predicate = BytesContentOutputPredicate;
802
803 fn into_output(self) -> Self::Predicate {
804 Self::Predicate::from_vec(self)
805 }
806}
807
808impl IntoOutputPredicate<BytesContentOutputPredicate> for &'static [u8] {
809 type Predicate = BytesContentOutputPredicate;
810
811 fn into_output(self) -> Self::Predicate {
812 Self::Predicate::new(self)
813 }
814}
815
816#[derive(Debug, Clone)]
836pub struct StrContentOutputPredicate(
837 predicates::str::Utf8Predicate<predicates::str::DifferencePredicate>,
838);
839
840impl StrContentOutputPredicate {
841 pub(crate) fn from_str(value: &'static str) -> Self {
842 let pred = predicates::str::diff(value).from_utf8();
843 StrContentOutputPredicate(pred)
844 }
845
846 pub(crate) fn from_string(value: String) -> Self {
847 let pred = predicates::str::diff(value).from_utf8();
848 StrContentOutputPredicate(pred)
849 }
850}
851
852impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate {
853 fn parameters<'a>(
854 &'a self,
855 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
856 self.0.parameters()
857 }
858
859 fn children<'a>(
861 &'a self,
862 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
863 self.0.children()
864 }
865}
866
867impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate {
868 fn eval(&self, item: &[u8]) -> bool {
869 self.0.eval(item)
870 }
871
872 fn find_case<'a>(
873 &'a self,
874 expected: bool,
875 variable: &[u8],
876 ) -> Option<predicates_core::reflection::Case<'a>> {
877 self.0.find_case(expected, variable)
878 }
879}
880
881impl fmt::Display for StrContentOutputPredicate {
882 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
883 self.0.fmt(f)
884 }
885}
886
887impl IntoOutputPredicate<StrContentOutputPredicate> for String {
888 type Predicate = StrContentOutputPredicate;
889
890 fn into_output(self) -> Self::Predicate {
891 Self::Predicate::from_string(self)
892 }
893}
894
895impl IntoOutputPredicate<StrContentOutputPredicate> for &'static str {
896 type Predicate = StrContentOutputPredicate;
897
898 fn into_output(self) -> Self::Predicate {
899 Self::Predicate::from_str(self)
900 }
901}
902
903#[derive(Debug, Clone)]
923pub struct StrOutputPredicate<P: predicates_core::Predicate<str>>(
924 predicates::str::Utf8Predicate<P>,
925);
926
927impl<P> StrOutputPredicate<P>
928where
929 P: predicates_core::Predicate<str>,
930{
931 pub(crate) fn new(pred: P) -> Self {
932 let pred = pred.from_utf8();
933 StrOutputPredicate(pred)
934 }
935}
936
937impl<P> predicates_core::reflection::PredicateReflection for StrOutputPredicate<P>
938where
939 P: predicates_core::Predicate<str>,
940{
941 fn parameters<'a>(
942 &'a self,
943 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
944 self.0.parameters()
945 }
946
947 fn children<'a>(
949 &'a self,
950 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
951 self.0.children()
952 }
953}
954
955impl<P> predicates_core::Predicate<[u8]> for StrOutputPredicate<P>
956where
957 P: predicates_core::Predicate<str>,
958{
959 fn eval(&self, item: &[u8]) -> bool {
960 self.0.eval(item)
961 }
962
963 fn find_case<'a>(
964 &'a self,
965 expected: bool,
966 variable: &[u8],
967 ) -> Option<predicates_core::reflection::Case<'a>> {
968 self.0.find_case(expected, variable)
969 }
970}
971
972impl<P> fmt::Display for StrOutputPredicate<P>
973where
974 P: predicates_core::Predicate<str>,
975{
976 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
977 self.0.fmt(f)
978 }
979}
980
981impl<P> IntoOutputPredicate<StrOutputPredicate<P>> for P
982where
983 P: predicates_core::Predicate<str>,
984{
985 type Predicate = StrOutputPredicate<P>;
986
987 fn into_output(self) -> Self::Predicate {
988 Self::Predicate::new(self)
989 }
990}
991
992pub type AssertResult = Result<Assert, AssertError>;
1011
1012#[derive(Debug)]
1014pub struct AssertError {
1015 assert: Assert,
1016 reason: AssertReason,
1017}
1018
1019#[derive(Debug)]
1020enum AssertReason {
1021 UnexpectedFailure { actual_code: Option<i32> },
1022 UnexpectedSuccess,
1023 UnexpectedCompletion,
1024 CommandInterrupted,
1025 UnexpectedReturnCode { case_tree: CaseTree },
1026 UnexpectedStdout { case_tree: CaseTree },
1027 UnexpectedStderr { case_tree: CaseTree },
1028}
1029
1030impl AssertError {
1031 #[track_caller]
1032 fn panic<T>(self) -> T {
1033 panic!("{}", self)
1034 }
1035
1036 pub fn assert(self) -> Assert {
1060 self.assert
1061 }
1062}
1063
1064impl Error for AssertError {}
1065
1066impl fmt::Display for AssertError {
1067 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1068 match &self.reason {
1069 AssertReason::UnexpectedFailure { actual_code } => writeln!(
1070 f,
1071 "Unexpected failure.\ncode={}\nstderr=```{}```",
1072 actual_code
1073 .map(|actual_code| actual_code.to_string())
1074 .unwrap_or_else(|| "<interrupted>".to_owned()),
1075 DebugBytes::new(&self.assert.output.stderr),
1076 ),
1077 AssertReason::UnexpectedSuccess => {
1078 writeln!(f, "Unexpected success")
1079 }
1080 AssertReason::UnexpectedCompletion => {
1081 writeln!(f, "Unexpected completion")
1082 }
1083 AssertReason::CommandInterrupted => {
1084 writeln!(f, "Command interrupted")
1085 }
1086 AssertReason::UnexpectedReturnCode { case_tree } => {
1087 writeln!(f, "Unexpected return code, failed {case_tree}")
1088 }
1089 AssertReason::UnexpectedStdout { case_tree } => {
1090 writeln!(f, "Unexpected stdout, failed {case_tree}")
1091 }
1092 AssertReason::UnexpectedStderr { case_tree } => {
1093 writeln!(f, "Unexpected stderr, failed {case_tree}")
1094 }
1095 }?;
1096 write!(f, "{}", self.assert)
1097 }
1098}
1099
1100struct CaseTree(predicates_tree::CaseTree);
1101
1102impl fmt::Display for CaseTree {
1103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1104 <predicates_tree::CaseTree as fmt::Display>::fmt(&self.0, f)
1105 }
1106}
1107
1108impl fmt::Debug for CaseTree {
1110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1111 <predicates_tree::CaseTree as fmt::Display>::fmt(&self.0, f)
1112 }
1113}
1114
1115#[cfg(test)]
1116mod test {
1117 use super::*;
1118
1119 use predicates::prelude::*;
1120
1121 fn convert_code<I, P>(pred: I) -> P
1124 where
1125 I: IntoCodePredicate<P>,
1126 P: Predicate<i32>,
1127 {
1128 pred.into_code()
1129 }
1130
1131 #[test]
1132 fn into_code_from_pred() {
1133 let pred = convert_code(predicate::eq(10));
1134 assert!(pred.eval(&10));
1135 }
1136
1137 #[test]
1138 fn into_code_from_i32() {
1139 let pred = convert_code(10);
1140 assert!(pred.eval(&10));
1141 }
1142
1143 #[test]
1144 fn into_code_from_vec() {
1145 let pred = convert_code(vec![3, 10]);
1146 assert!(pred.eval(&10));
1147 }
1148
1149 #[test]
1150 fn into_code_from_array() {
1151 let pred = convert_code(&[3, 10] as &[i32]);
1152 assert!(pred.eval(&10));
1153 }
1154
1155 fn convert_output<I, P>(pred: I) -> P
1158 where
1159 I: IntoOutputPredicate<P>,
1160 P: Predicate<[u8]>,
1161 {
1162 pred.into_output()
1163 }
1164
1165 #[test]
1166 fn into_output_from_pred() {
1167 let pred = convert_output(predicate::eq(b"Hello" as &[u8]));
1168 assert!(pred.eval(b"Hello" as &[u8]));
1169 }
1170
1171 #[test]
1172 fn into_output_from_bytes() {
1173 let pred = convert_output(b"Hello" as &[u8]);
1174 assert!(pred.eval(b"Hello" as &[u8]));
1175 }
1176
1177 #[test]
1178 fn into_output_from_vec() {
1179 let pred = convert_output(vec![b'H', b'e', b'l', b'l', b'o']);
1180 assert!(pred.eval(b"Hello" as &[u8]));
1181 }
1182
1183 #[test]
1184 fn into_output_from_str() {
1185 let pred = convert_output("Hello");
1186 assert!(pred.eval(b"Hello" as &[u8]));
1187 }
1188}