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::DebugBytes;
15use crate::output::output_fmt;
16
17pub trait OutputAssertExt {
34 #[must_use]
51 fn assert(self) -> Assert;
52}
53
54impl OutputAssertExt for process::Output {
55 fn assert(self) -> Assert {
56 Assert::new(self)
57 }
58}
59
60impl OutputAssertExt for &mut process::Command {
61 #[track_caller]
62 fn assert(self) -> Assert {
63 let output = match self.output() {
64 Ok(output) => output,
65 Err(err) => {
66 panic!("Failed to spawn {self:?}: {err}");
67 }
68 };
69 Assert::new(output).append_context("command", format!("{self:?}"))
70 }
71}
72
73pub struct Assert {
92 output: process::Output,
93 context: Vec<(&'static str, Box<dyn fmt::Display + Send + Sync>)>,
94}
95
96impl Assert {
97 #[must_use]
101 pub fn new(output: process::Output) -> Self {
102 Self {
103 output,
104 context: vec![],
105 }
106 }
107
108 fn into_error(self, reason: AssertReason) -> AssertError {
109 AssertError {
110 assert: self,
111 reason,
112 }
113 }
114
115 #[must_use]
131 pub fn append_context<D>(mut self, name: &'static str, context: D) -> Self
132 where
133 D: fmt::Display + Send + Sync + 'static,
134 {
135 self.context.push((name, Box::new(context)));
136 self
137 }
138
139 pub fn get_output(&self) -> &process::Output {
143 &self.output
144 }
145
146 #[track_caller]
161 pub fn success(self) -> Self {
162 match self.try_success() {
163 Ok(v) => v,
164 Err(e) => e.panic(),
166 }
167 }
168
169 pub fn try_success(self) -> AssertResult {
171 if !self.output.status.success() {
172 let actual_code = self.output.status.code();
173 return Err(self.into_error(AssertReason::UnexpectedFailure { actual_code }));
174 }
175 Ok(self)
176 }
177
178 #[track_caller]
194 pub fn failure(self) -> Self {
195 match self.try_failure() {
196 Ok(v) => v,
197 Err(e) => e.panic(),
199 }
200 }
201
202 pub fn try_failure(self) -> AssertResult {
204 if self.output.status.success() {
205 return Err(self.into_error(AssertReason::UnexpectedSuccess));
206 }
207 Ok(self)
208 }
209
210 #[track_caller]
212 pub fn interrupted(self) -> Self {
213 match self.try_interrupted() {
214 Ok(v) => v,
215 Err(e) => e.panic(),
217 }
218 }
219
220 pub fn try_interrupted(self) -> AssertResult {
222 if self.output.status.code().is_some() {
223 return Err(self.into_error(AssertReason::UnexpectedCompletion));
224 }
225 Ok(self)
226 }
227
228 #[track_caller]
277 pub fn code<I, P>(self, pred: I) -> Self
278 where
279 I: IntoCodePredicate<P>,
280 P: predicates_core::Predicate<i32>,
281 {
282 match self.try_code(pred) {
283 Ok(v) => v,
284 Err(e) => e.panic(),
286 }
287 }
288
289 pub fn try_code<I, P>(self, pred: I) -> AssertResult
291 where
292 I: IntoCodePredicate<P>,
293 P: predicates_core::Predicate<i32>,
294 {
295 self.code_impl(&pred.into_code())
296 }
297
298 fn code_impl(self, pred: &dyn predicates_core::Predicate<i32>) -> AssertResult {
299 let actual_code = if let Some(actual_code) = self.output.status.code() {
300 actual_code
301 } else {
302 return Err(self.into_error(AssertReason::CommandInterrupted));
303 };
304 if let Some(case) = pred.find_case(false, &actual_code) {
305 return Err(self.into_error(AssertReason::UnexpectedReturnCode {
306 case_tree: CaseTree(case.tree()),
307 }));
308 }
309 Ok(self)
310 }
311
312 #[track_caller]
379 pub fn stdout<I, P>(self, pred: I) -> Self
380 where
381 I: IntoOutputPredicate<P>,
382 P: predicates_core::Predicate<[u8]>,
383 {
384 match self.try_stdout(pred) {
385 Ok(v) => v,
386 Err(e) => e.panic(),
388 }
389 }
390
391 pub fn try_stdout<I, P>(self, pred: I) -> AssertResult
393 where
394 I: IntoOutputPredicate<P>,
395 P: predicates_core::Predicate<[u8]>,
396 {
397 self.stdout_impl(&pred.into_output())
398 }
399
400 fn stdout_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult {
401 {
402 let actual = &self.output.stdout;
403 if let Some(case) = pred.find_case(false, actual) {
404 return Err(self.into_error(AssertReason::UnexpectedStdout {
405 case_tree: CaseTree(case.tree()),
406 }));
407 }
408 }
409 Ok(self)
410 }
411
412 #[track_caller]
479 pub fn stderr<I, P>(self, pred: I) -> Self
480 where
481 I: IntoOutputPredicate<P>,
482 P: predicates_core::Predicate<[u8]>,
483 {
484 match self.try_stderr(pred) {
485 Ok(v) => v,
486 Err(e) => e.panic(),
488 }
489 }
490
491 pub fn try_stderr<I, P>(self, pred: I) -> AssertResult
493 where
494 I: IntoOutputPredicate<P>,
495 P: predicates_core::Predicate<[u8]>,
496 {
497 self.stderr_impl(&pred.into_output())
498 }
499
500 fn stderr_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult {
501 {
502 let actual = &self.output.stderr;
503 if let Some(case) = pred.find_case(false, actual) {
504 return Err(self.into_error(AssertReason::UnexpectedStderr {
505 case_tree: CaseTree(case.tree()),
506 }));
507 }
508 }
509 Ok(self)
510 }
511}
512
513impl fmt::Display for Assert {
514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515 let palette = crate::Palette::color();
516 for (name, context) in &self.context {
517 writeln!(f, "{:#}=`{:#}`", palette.key(name), palette.value(context))?;
518 }
519 output_fmt(&self.output, f)
520 }
521}
522
523impl fmt::Debug for Assert {
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525 f.debug_struct("Assert")
526 .field("output", &self.output)
527 .finish()
528 }
529}
530
531pub trait IntoCodePredicate<P>
556where
557 P: predicates_core::Predicate<i32>,
558{
559 type Predicate;
561
562 fn into_code(self) -> P;
564}
565
566impl<P> IntoCodePredicate<P> for P
567where
568 P: predicates_core::Predicate<i32>,
569{
570 type Predicate = P;
571
572 fn into_code(self) -> Self::Predicate {
573 self
574 }
575}
576
577#[derive(Debug)]
594pub struct EqCodePredicate(predicates::ord::EqPredicate<i32>);
595
596impl EqCodePredicate {
597 pub(crate) fn new(value: i32) -> Self {
598 let pred = predicates::ord::eq(value);
599 Self(pred)
600 }
601}
602
603impl predicates_core::reflection::PredicateReflection for EqCodePredicate {
604 fn parameters<'a>(
605 &'a self,
606 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
607 self.0.parameters()
608 }
609
610 fn children<'a>(
612 &'a self,
613 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
614 self.0.children()
615 }
616}
617
618impl predicates_core::Predicate<i32> for EqCodePredicate {
619 fn eval(&self, item: &i32) -> bool {
620 self.0.eval(item)
621 }
622
623 fn find_case<'a>(
624 &'a self,
625 expected: bool,
626 variable: &i32,
627 ) -> Option<predicates_core::reflection::Case<'a>> {
628 self.0.find_case(expected, variable)
629 }
630}
631
632impl fmt::Display for EqCodePredicate {
633 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634 self.0.fmt(f)
635 }
636}
637
638impl IntoCodePredicate<EqCodePredicate> for i32 {
639 type Predicate = EqCodePredicate;
640
641 fn into_code(self) -> Self::Predicate {
642 Self::Predicate::new(self)
643 }
644}
645
646#[derive(Debug)]
663pub struct InCodePredicate(predicates::iter::InPredicate<i32>);
664
665impl InCodePredicate {
666 pub(crate) fn new<I: IntoIterator<Item = i32>>(value: I) -> Self {
667 let pred = predicates::iter::in_iter(value);
668 Self(pred)
669 }
670}
671
672impl predicates_core::reflection::PredicateReflection for InCodePredicate {
673 fn parameters<'a>(
674 &'a self,
675 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
676 self.0.parameters()
677 }
678
679 fn children<'a>(
681 &'a self,
682 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
683 self.0.children()
684 }
685}
686
687impl predicates_core::Predicate<i32> for InCodePredicate {
688 fn eval(&self, item: &i32) -> bool {
689 self.0.eval(item)
690 }
691
692 fn find_case<'a>(
693 &'a self,
694 expected: bool,
695 variable: &i32,
696 ) -> Option<predicates_core::reflection::Case<'a>> {
697 self.0.find_case(expected, variable)
698 }
699}
700
701impl fmt::Display for InCodePredicate {
702 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703 self.0.fmt(f)
704 }
705}
706
707impl IntoCodePredicate<InCodePredicate> for Vec<i32> {
708 type Predicate = InCodePredicate;
709
710 fn into_code(self) -> Self::Predicate {
711 Self::Predicate::new(self)
712 }
713}
714
715impl IntoCodePredicate<InCodePredicate> for &'static [i32] {
716 type Predicate = InCodePredicate;
717
718 fn into_code(self) -> Self::Predicate {
719 Self::Predicate::new(self.iter().cloned())
720 }
721}
722
723pub trait IntoOutputPredicate<P>
750where
751 P: predicates_core::Predicate<[u8]>,
752{
753 type Predicate;
755
756 fn into_output(self) -> P;
758}
759
760impl<P> IntoOutputPredicate<P> for P
761where
762 P: predicates_core::Predicate<[u8]>,
763{
764 type Predicate = P;
765
766 fn into_output(self) -> Self::Predicate {
767 self
768 }
769}
770
771#[derive(Debug)]
789pub struct BytesContentOutputPredicate(Cow<'static, [u8]>);
790
791impl BytesContentOutputPredicate {
792 pub(crate) fn new(value: &'static [u8]) -> Self {
793 Self(Cow::from(value))
794 }
795
796 pub(crate) fn from_vec(value: Vec<u8>) -> Self {
797 Self(Cow::from(value))
798 }
799}
800
801impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate {}
802
803impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate {
804 fn eval(&self, item: &[u8]) -> bool {
805 self.0.as_ref() == item
806 }
807
808 fn find_case(
809 &self,
810 expected: bool,
811 variable: &[u8],
812 ) -> Option<predicates_core::reflection::Case<'_>> {
813 let actual = self.eval(variable);
814 if expected == actual {
815 Some(predicates_core::reflection::Case::new(Some(self), actual))
816 } else {
817 None
818 }
819 }
820}
821
822impl fmt::Display for BytesContentOutputPredicate {
823 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
824 predicates::ord::eq(self.0.as_ref()).fmt(f)
825 }
826}
827
828impl IntoOutputPredicate<BytesContentOutputPredicate> for Vec<u8> {
829 type Predicate = BytesContentOutputPredicate;
830
831 fn into_output(self) -> Self::Predicate {
832 Self::Predicate::from_vec(self)
833 }
834}
835
836impl IntoOutputPredicate<BytesContentOutputPredicate> for &'static [u8] {
837 type Predicate = BytesContentOutputPredicate;
838
839 fn into_output(self) -> Self::Predicate {
840 Self::Predicate::new(self)
841 }
842}
843
844#[derive(Debug, Clone)]
864pub struct StrContentOutputPredicate(
865 predicates::str::Utf8Predicate<predicates::str::DifferencePredicate>,
866);
867
868impl StrContentOutputPredicate {
869 pub(crate) fn from_str(value: &'static str) -> Self {
870 let pred = predicates::str::diff(value).from_utf8();
871 Self(pred)
872 }
873
874 pub(crate) fn from_string(value: String) -> Self {
875 let pred = predicates::str::diff(value).from_utf8();
876 Self(pred)
877 }
878}
879
880impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate {
881 fn parameters<'a>(
882 &'a self,
883 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
884 self.0.parameters()
885 }
886
887 fn children<'a>(
889 &'a self,
890 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
891 self.0.children()
892 }
893}
894
895impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate {
896 fn eval(&self, item: &[u8]) -> bool {
897 self.0.eval(item)
898 }
899
900 fn find_case<'a>(
901 &'a self,
902 expected: bool,
903 variable: &[u8],
904 ) -> Option<predicates_core::reflection::Case<'a>> {
905 self.0.find_case(expected, variable)
906 }
907}
908
909impl fmt::Display for StrContentOutputPredicate {
910 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
911 self.0.fmt(f)
912 }
913}
914
915impl IntoOutputPredicate<StrContentOutputPredicate> for String {
916 type Predicate = StrContentOutputPredicate;
917
918 fn into_output(self) -> Self::Predicate {
919 Self::Predicate::from_string(self)
920 }
921}
922
923impl IntoOutputPredicate<StrContentOutputPredicate> for &'static str {
924 type Predicate = StrContentOutputPredicate;
925
926 fn into_output(self) -> Self::Predicate {
927 Self::Predicate::from_str(self)
928 }
929}
930
931#[derive(Debug, Clone)]
951pub struct StrOutputPredicate<P: predicates_core::Predicate<str>>(
952 predicates::str::Utf8Predicate<P>,
953);
954
955impl<P> StrOutputPredicate<P>
956where
957 P: predicates_core::Predicate<str>,
958{
959 pub(crate) fn new(pred: P) -> Self {
960 let pred = pred.from_utf8();
961 Self(pred)
962 }
963}
964
965impl<P> predicates_core::reflection::PredicateReflection for StrOutputPredicate<P>
966where
967 P: predicates_core::Predicate<str>,
968{
969 fn parameters<'a>(
970 &'a self,
971 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
972 self.0.parameters()
973 }
974
975 fn children<'a>(
977 &'a self,
978 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
979 self.0.children()
980 }
981}
982
983impl<P> predicates_core::Predicate<[u8]> for StrOutputPredicate<P>
984where
985 P: predicates_core::Predicate<str>,
986{
987 fn eval(&self, item: &[u8]) -> bool {
988 self.0.eval(item)
989 }
990
991 fn find_case<'a>(
992 &'a self,
993 expected: bool,
994 variable: &[u8],
995 ) -> Option<predicates_core::reflection::Case<'a>> {
996 self.0.find_case(expected, variable)
997 }
998}
999
1000impl<P> fmt::Display for StrOutputPredicate<P>
1001where
1002 P: predicates_core::Predicate<str>,
1003{
1004 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1005 self.0.fmt(f)
1006 }
1007}
1008
1009impl<P> IntoOutputPredicate<StrOutputPredicate<P>> for P
1010where
1011 P: predicates_core::Predicate<str>,
1012{
1013 type Predicate = StrOutputPredicate<P>;
1014
1015 fn into_output(self) -> Self::Predicate {
1016 Self::Predicate::new(self)
1017 }
1018}
1019
1020pub type AssertResult = Result<Assert, AssertError>;
1039
1040#[derive(Debug)]
1042pub struct AssertError {
1043 assert: Assert,
1044 reason: AssertReason,
1045}
1046
1047#[derive(Debug)]
1048enum AssertReason {
1049 UnexpectedFailure { actual_code: Option<i32> },
1050 UnexpectedSuccess,
1051 UnexpectedCompletion,
1052 CommandInterrupted,
1053 UnexpectedReturnCode { case_tree: CaseTree },
1054 UnexpectedStdout { case_tree: CaseTree },
1055 UnexpectedStderr { case_tree: CaseTree },
1056}
1057
1058impl AssertError {
1059 #[track_caller]
1060 fn panic<T>(self) -> T {
1061 panic!("{}", self)
1062 }
1063
1064 pub fn assert(self) -> Assert {
1088 self.assert
1089 }
1090}
1091
1092impl Error for AssertError {}
1093
1094impl fmt::Display for AssertError {
1095 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1096 match &self.reason {
1097 AssertReason::UnexpectedFailure { actual_code } => writeln!(
1098 f,
1099 "Unexpected failure.\ncode={}\nstderr=```{}```",
1100 actual_code
1101 .map(|actual_code| actual_code.to_string())
1102 .unwrap_or_else(|| "<interrupted>".to_owned()),
1103 DebugBytes::new(&self.assert.output.stderr),
1104 ),
1105 AssertReason::UnexpectedSuccess => {
1106 writeln!(f, "Unexpected success")
1107 }
1108 AssertReason::UnexpectedCompletion => {
1109 writeln!(f, "Unexpected completion")
1110 }
1111 AssertReason::CommandInterrupted => {
1112 writeln!(f, "Command interrupted")
1113 }
1114 AssertReason::UnexpectedReturnCode { case_tree } => {
1115 writeln!(f, "Unexpected return code, failed {case_tree}")
1116 }
1117 AssertReason::UnexpectedStdout { case_tree } => {
1118 writeln!(f, "Unexpected stdout, failed {case_tree}")
1119 }
1120 AssertReason::UnexpectedStderr { case_tree } => {
1121 writeln!(f, "Unexpected stderr, failed {case_tree}")
1122 }
1123 }?;
1124 write!(f, "{}", self.assert)
1125 }
1126}
1127
1128struct CaseTree(predicates_tree::CaseTree);
1129
1130impl fmt::Display for CaseTree {
1131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1132 <predicates_tree::CaseTree as fmt::Display>::fmt(&self.0, f)
1133 }
1134}
1135
1136impl fmt::Debug for CaseTree {
1138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1139 <predicates_tree::CaseTree as fmt::Display>::fmt(&self.0, f)
1140 }
1141}
1142
1143#[cfg(test)]
1144mod test {
1145 use super::*;
1146
1147 use predicates::prelude::*;
1148
1149 fn convert_code<I, P>(pred: I) -> P
1152 where
1153 I: IntoCodePredicate<P>,
1154 P: Predicate<i32>,
1155 {
1156 pred.into_code()
1157 }
1158
1159 #[test]
1160 fn into_code_from_pred() {
1161 let pred = convert_code(predicate::eq(10));
1162 assert!(pred.eval(&10));
1163 }
1164
1165 #[test]
1166 fn into_code_from_i32() {
1167 let pred = convert_code(10);
1168 assert!(pred.eval(&10));
1169 }
1170
1171 #[test]
1172 fn into_code_from_vec() {
1173 let pred = convert_code(vec![3, 10]);
1174 assert!(pred.eval(&10));
1175 }
1176
1177 #[test]
1178 fn into_code_from_array() {
1179 let pred = convert_code(&[3, 10] as &[i32]);
1180 assert!(pred.eval(&10));
1181 }
1182
1183 fn convert_output<I, P>(pred: I) -> P
1186 where
1187 I: IntoOutputPredicate<P>,
1188 P: Predicate<[u8]>,
1189 {
1190 pred.into_output()
1191 }
1192
1193 #[test]
1194 fn into_output_from_pred() {
1195 let pred = convert_output(predicate::eq(b"Hello" as &[u8]));
1196 assert!(pred.eval(b"Hello" as &[u8]));
1197 }
1198
1199 #[test]
1200 fn into_output_from_bytes() {
1201 let pred = convert_output(b"Hello" as &[u8]);
1202 assert!(pred.eval(b"Hello" as &[u8]));
1203 }
1204
1205 #[test]
1206 fn into_output_from_vec() {
1207 let pred = convert_output(vec![b'H', b'e', b'l', b'l', b'o']);
1208 assert!(pred.eval(b"Hello" as &[u8]));
1209 }
1210
1211 #[test]
1212 fn into_output_from_str() {
1213 let pred = convert_output("Hello");
1214 assert!(pred.eval(b"Hello" as &[u8]));
1215 }
1216}