Skip to main content

assert_cmd/
assert.rs

1//! [`std::process::Output`] assertions.
2
3use 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
17/// Assert the state of an [`Output`].
18///
19/// # Examples
20///
21/// ```rust,no_run
22/// use assert_cmd::prelude::*;
23///
24/// use std::process::Command;
25///
26/// let mut cmd = Command::cargo_bin("bin_fixture")
27///     .unwrap();
28/// cmd.assert()
29///     .success();
30/// ```
31///
32/// [`Output`]: std::process::Output
33pub trait OutputAssertExt {
34    /// Wrap with an interface for that provides assertions on the [`Output`].
35    ///
36    /// # Examples
37    ///
38    /// ```rust,no_run
39    /// use assert_cmd::prelude::*;
40    ///
41    /// use std::process::Command;
42    ///
43    /// let mut cmd = Command::cargo_bin("bin_fixture")
44    ///     .unwrap();
45    /// cmd.assert()
46    ///     .success();
47    /// ```
48    ///
49    /// [`Output`]: std::process::Output
50    #[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    fn assert(self) -> Assert {
62        let output = match self.output() {
63            Ok(output) => output,
64            Err(err) => {
65                panic!("Failed to spawn {self:?}: {err}");
66            }
67        };
68        Assert::new(output).append_context("command", format!("{self:?}"))
69    }
70}
71
72/// Assert the state of an [`Output`].
73///
74/// Create an `Assert` through the [`OutputAssertExt`] trait.
75///
76/// # Examples
77///
78/// ```rust,no_run
79/// use assert_cmd::prelude::*;
80///
81/// use std::process::Command;
82///
83/// let mut cmd = Command::cargo_bin("bin_fixture")
84///     .unwrap();
85/// cmd.assert()
86///     .success();
87/// ```
88///
89/// [`Output`]: std::process::Output
90pub struct Assert {
91    output: process::Output,
92    context: Vec<(&'static str, Box<dyn fmt::Display + Send + Sync>)>,
93}
94
95impl Assert {
96    /// Create an `Assert` for a given [`Output`].
97    ///
98    /// [`Output`]: std::process::Output
99    #[must_use]
100    pub fn new(output: process::Output) -> Self {
101        Self {
102            output,
103            context: vec![],
104        }
105    }
106
107    fn into_error(self, reason: AssertReason) -> AssertError {
108        AssertError {
109            assert: self,
110            reason,
111        }
112    }
113
114    /// Clarify failures with additional context.
115    ///
116    /// # Examples
117    ///
118    /// ```rust,no_run
119    /// use assert_cmd::prelude::*;
120    ///
121    /// use std::process::Command;
122    ///
123    /// Command::cargo_bin("bin_fixture")
124    ///     .unwrap()
125    ///     .assert()
126    ///     .append_context("main", "no args")
127    ///     .success();
128    /// ```
129    #[must_use]
130    pub fn append_context<D>(mut self, name: &'static str, context: D) -> Self
131    where
132        D: fmt::Display + Send + Sync + 'static,
133    {
134        self.context.push((name, Box::new(context)));
135        self
136    }
137
138    /// Access the contained [`Output`].
139    ///
140    /// [`Output`]: std::process::Output
141    pub fn get_output(&self) -> &process::Output {
142        &self.output
143    }
144
145    /// Ensure the command succeeded.
146    ///
147    /// # Examples
148    ///
149    /// ```rust,no_run
150    /// use assert_cmd::prelude::*;
151    ///
152    /// use std::process::Command;
153    ///
154    /// Command::cargo_bin("bin_fixture")
155    ///     .unwrap()
156    ///     .assert()
157    ///     .success();
158    /// ```
159    #[track_caller]
160    pub fn success(self) -> Self {
161        self.try_success().unwrap_or_else(AssertError::panic)
162    }
163
164    /// `try_` variant of [`Assert::success`].
165    pub fn try_success(self) -> AssertResult {
166        if !self.output.status.success() {
167            let actual_code = self.output.status.code();
168            return Err(self.into_error(AssertReason::UnexpectedFailure { actual_code }));
169        }
170        Ok(self)
171    }
172
173    /// Ensure the command failed.
174    ///
175    /// # Examples
176    ///
177    /// ```rust,no_run
178    /// use assert_cmd::prelude::*;
179    ///
180    /// use std::process::Command;
181    ///
182    /// Command::cargo_bin("bin_fixture")
183    ///     .unwrap()
184    ///     .env("exit", "1")
185    ///     .assert()
186    ///     .failure();
187    /// ```
188    #[track_caller]
189    pub fn failure(self) -> Self {
190        self.try_failure().unwrap_or_else(AssertError::panic)
191    }
192
193    /// Variant of [`Assert::failure`] that returns an [`AssertResult`].
194    pub fn try_failure(self) -> AssertResult {
195        if self.output.status.success() {
196            return Err(self.into_error(AssertReason::UnexpectedSuccess));
197        }
198        Ok(self)
199    }
200
201    /// Ensure the command aborted before returning a code.
202    #[track_caller]
203    pub fn interrupted(self) -> Self {
204        self.try_interrupted().unwrap_or_else(AssertError::panic)
205    }
206
207    /// Variant of [`Assert::interrupted`] that returns an [`AssertResult`].
208    pub fn try_interrupted(self) -> AssertResult {
209        if self.output.status.code().is_some() {
210            return Err(self.into_error(AssertReason::UnexpectedCompletion));
211        }
212        Ok(self)
213    }
214
215    /// Ensure the command returned the expected code.
216    ///
217    /// This uses [`IntoCodePredicate`] to provide short-hands for common cases.
218    ///
219    /// See [`predicates`] for more predicates.
220    ///
221    /// # Examples
222    ///
223    /// Accepting a predicate:
224    /// ```rust,no_run
225    /// use assert_cmd::prelude::*;
226    ///
227    /// use std::process::Command;
228    /// use predicates::prelude::*;
229    ///
230    /// Command::cargo_bin("bin_fixture")
231    ///     .unwrap()
232    ///     .env("exit", "42")
233    ///     .assert()
234    ///     .code(predicate::eq(42));
235    /// ```
236    ///
237    /// Accepting an exit code:
238    /// ```rust,no_run
239    /// use assert_cmd::prelude::*;
240    ///
241    /// use std::process::Command;
242    ///
243    /// Command::cargo_bin("bin_fixture")
244    ///     .unwrap()
245    ///     .env("exit", "42")
246    ///     .assert()
247    ///     .code(42);
248    /// ```
249    ///
250    /// Accepting multiple exit codes:
251    /// ```rust,no_run
252    /// use assert_cmd::prelude::*;
253    ///
254    /// use std::process::Command;
255    ///
256    /// Command::cargo_bin("bin_fixture")
257    ///     .unwrap()
258    ///     .env("exit", "42")
259    ///     .assert()
260    ///     .code(&[2, 42] as &[i32]);
261    /// ```
262    ///
263    #[track_caller]
264    pub fn code<I, P>(self, pred: I) -> Self
265    where
266        I: IntoCodePredicate<P>,
267        P: predicates_core::Predicate<i32>,
268    {
269        self.try_code(pred).unwrap_or_else(AssertError::panic)
270    }
271
272    /// Variant of [`Assert::code`] that returns an [`AssertResult`].
273    pub fn try_code<I, P>(self, pred: I) -> AssertResult
274    where
275        I: IntoCodePredicate<P>,
276        P: predicates_core::Predicate<i32>,
277    {
278        self.code_impl(&pred.into_code())
279    }
280
281    fn code_impl(self, pred: &dyn predicates_core::Predicate<i32>) -> AssertResult {
282        let actual_code = if let Some(actual_code) = self.output.status.code() {
283            actual_code
284        } else {
285            return Err(self.into_error(AssertReason::CommandInterrupted));
286        };
287        if let Some(case) = pred.find_case(false, &actual_code) {
288            return Err(self.into_error(AssertReason::UnexpectedReturnCode {
289                case_tree: CaseTree(case.tree()),
290            }));
291        }
292        Ok(self)
293    }
294
295    /// Ensure the command wrote the expected data to `stdout`.
296    ///
297    /// This uses [`IntoOutputPredicate`] to provide short-hands for common cases.
298    ///
299    /// See [`predicates`] for more predicates.
300    ///
301    /// # Examples
302    ///
303    /// Accepting a bytes predicate:
304    /// ```rust,no_run
305    /// use assert_cmd::prelude::*;
306    ///
307    /// use std::process::Command;
308    /// use predicates::prelude::*;
309    ///
310    /// Command::cargo_bin("bin_fixture")
311    ///     .unwrap()
312    ///     .env("stdout", "hello")
313    ///     .env("stderr", "world")
314    ///     .assert()
315    ///     .stdout(predicate::eq(b"hello\n" as &[u8]));
316    /// ```
317    ///
318    /// Accepting a `str` predicate:
319    /// ```rust,no_run
320    /// use assert_cmd::prelude::*;
321    ///
322    /// use std::process::Command;
323    /// use predicates::prelude::*;
324    ///
325    /// Command::cargo_bin("bin_fixture")
326    ///     .unwrap()
327    ///     .env("stdout", "hello")
328    ///     .env("stderr", "world")
329    ///     .assert()
330    ///     .stdout(predicate::str::diff("hello\n"));
331    /// ```
332    ///
333    /// Accepting bytes:
334    /// ```rust,no_run
335    /// use assert_cmd::prelude::*;
336    ///
337    /// use std::process::Command;
338    ///
339    /// Command::cargo_bin("bin_fixture")
340    ///     .unwrap()
341    ///     .env("stdout", "hello")
342    ///     .env("stderr", "world")
343    ///     .assert()
344    ///     .stdout(b"hello\n" as &[u8]);
345    /// ```
346    ///
347    /// Accepting a `str`:
348    /// ```rust,no_run
349    /// use assert_cmd::prelude::*;
350    ///
351    /// use std::process::Command;
352    ///
353    /// Command::cargo_bin("bin_fixture")
354    ///     .unwrap()
355    ///     .env("stdout", "hello")
356    ///     .env("stderr", "world")
357    ///     .assert()
358    ///     .stdout("hello\n");
359    /// ```
360    ///
361    #[track_caller]
362    pub fn stdout<I, P>(self, pred: I) -> Self
363    where
364        I: IntoOutputPredicate<P>,
365        P: predicates_core::Predicate<[u8]>,
366    {
367        self.try_stdout(pred).unwrap_or_else(AssertError::panic)
368    }
369
370    /// Variant of [`Assert::stdout`] that returns an [`AssertResult`].
371    pub fn try_stdout<I, P>(self, pred: I) -> AssertResult
372    where
373        I: IntoOutputPredicate<P>,
374        P: predicates_core::Predicate<[u8]>,
375    {
376        self.stdout_impl(&pred.into_output())
377    }
378
379    fn stdout_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult {
380        {
381            let actual = &self.output.stdout;
382            if let Some(case) = pred.find_case(false, actual) {
383                return Err(self.into_error(AssertReason::UnexpectedStdout {
384                    case_tree: CaseTree(case.tree()),
385                }));
386            }
387        }
388        Ok(self)
389    }
390
391    /// Ensure the command wrote the expected data to `stderr`.
392    ///
393    /// This uses [`IntoOutputPredicate`] to provide short-hands for common cases.
394    ///
395    /// See [`predicates`] for more predicates.
396    ///
397    /// # Examples
398    ///
399    /// Accepting a bytes predicate:
400    /// ```rust,no_run
401    /// use assert_cmd::prelude::*;
402    ///
403    /// use std::process::Command;
404    /// use predicates::prelude::*;
405    ///
406    /// Command::cargo_bin("bin_fixture")
407    ///     .unwrap()
408    ///     .env("stdout", "hello")
409    ///     .env("stderr", "world")
410    ///     .assert()
411    ///     .stderr(predicate::eq(b"world\n" as &[u8]));
412    /// ```
413    ///
414    /// Accepting a `str` predicate:
415    /// ```rust,no_run
416    /// use assert_cmd::prelude::*;
417    ///
418    /// use std::process::Command;
419    /// use predicates::prelude::*;
420    ///
421    /// Command::cargo_bin("bin_fixture")
422    ///     .unwrap()
423    ///     .env("stdout", "hello")
424    ///     .env("stderr", "world")
425    ///     .assert()
426    ///     .stderr(predicate::str::diff("world\n"));
427    /// ```
428    ///
429    /// Accepting bytes:
430    /// ```rust,no_run
431    /// use assert_cmd::prelude::*;
432    ///
433    /// use std::process::Command;
434    ///
435    /// Command::cargo_bin("bin_fixture")
436    ///     .unwrap()
437    ///     .env("stdout", "hello")
438    ///     .env("stderr", "world")
439    ///     .assert()
440    ///     .stderr(b"world\n" as &[u8]);
441    /// ```
442    ///
443    /// Accepting a `str`:
444    /// ```rust,no_run
445    /// use assert_cmd::prelude::*;
446    ///
447    /// use std::process::Command;
448    ///
449    /// Command::cargo_bin("bin_fixture")
450    ///     .unwrap()
451    ///     .env("stdout", "hello")
452    ///     .env("stderr", "world")
453    ///     .assert()
454    ///     .stderr("world\n");
455    /// ```
456    ///
457    #[track_caller]
458    pub fn stderr<I, P>(self, pred: I) -> Self
459    where
460        I: IntoOutputPredicate<P>,
461        P: predicates_core::Predicate<[u8]>,
462    {
463        self.try_stderr(pred).unwrap_or_else(AssertError::panic)
464    }
465
466    /// Variant of [`Assert::stderr`] that returns an [`AssertResult`].
467    pub fn try_stderr<I, P>(self, pred: I) -> AssertResult
468    where
469        I: IntoOutputPredicate<P>,
470        P: predicates_core::Predicate<[u8]>,
471    {
472        self.stderr_impl(&pred.into_output())
473    }
474
475    fn stderr_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult {
476        {
477            let actual = &self.output.stderr;
478            if let Some(case) = pred.find_case(false, actual) {
479                return Err(self.into_error(AssertReason::UnexpectedStderr {
480                    case_tree: CaseTree(case.tree()),
481                }));
482            }
483        }
484        Ok(self)
485    }
486}
487
488impl fmt::Display for Assert {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        let palette = crate::Palette::color();
491        for (name, context) in &self.context {
492            writeln!(f, "{:#}=`{:#}`", palette.key(name), palette.value(context))?;
493        }
494        output_fmt(&self.output, f)
495    }
496}
497
498impl fmt::Debug for Assert {
499    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500        f.debug_struct("Assert")
501            .field("output", &self.output)
502            .finish()
503    }
504}
505
506/// Used by [`Assert::code`] to convert `Self` into the needed
507/// [`predicates_core::Predicate<i32>`].
508///
509/// # Examples
510///
511/// ```rust,no_run
512/// use assert_cmd::prelude::*;
513///
514/// use std::process::Command;
515/// use predicates::prelude::*;
516///
517/// Command::cargo_bin("bin_fixture")
518///     .unwrap()
519///     .env("exit", "42")
520///     .assert()
521///     .code(predicate::eq(42));
522///
523/// // which can be shortened to:
524/// Command::cargo_bin("bin_fixture")
525///     .unwrap()
526///     .env("exit", "42")
527///     .assert()
528///     .code(42);
529/// ```
530pub trait IntoCodePredicate<P>
531where
532    P: predicates_core::Predicate<i32>,
533{
534    /// The type of the predicate being returned.
535    type Predicate;
536
537    /// Convert to a predicate for testing a program's exit code.
538    fn into_code(self) -> P;
539}
540
541impl<P> IntoCodePredicate<P> for P
542where
543    P: predicates_core::Predicate<i32>,
544{
545    type Predicate = P;
546
547    fn into_code(self) -> Self::Predicate {
548        self
549    }
550}
551
552/// Keep `predicates` concrete Predicates out of our public API.
553/// [`predicates_core::Predicate`] used by [`IntoCodePredicate`] for code.
554///
555/// # Example
556///
557/// ```rust,no_run
558/// use assert_cmd::prelude::*;
559///
560/// use std::process::Command;
561///
562/// Command::cargo_bin("bin_fixture")
563///     .unwrap()
564///     .env("exit", "42")
565///     .assert()
566///     .code(42);
567/// ```
568#[derive(Debug)]
569pub struct EqCodePredicate(predicates::ord::EqPredicate<i32>);
570
571impl EqCodePredicate {
572    pub(crate) fn new(value: i32) -> Self {
573        let pred = predicates::ord::eq(value);
574        EqCodePredicate(pred)
575    }
576}
577
578impl predicates_core::reflection::PredicateReflection for EqCodePredicate {
579    fn parameters<'a>(
580        &'a self,
581    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
582        self.0.parameters()
583    }
584
585    /// Nested `Predicate`s of the current `Predicate`.
586    fn children<'a>(
587        &'a self,
588    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
589        self.0.children()
590    }
591}
592
593impl predicates_core::Predicate<i32> for EqCodePredicate {
594    fn eval(&self, item: &i32) -> bool {
595        self.0.eval(item)
596    }
597
598    fn find_case<'a>(
599        &'a self,
600        expected: bool,
601        variable: &i32,
602    ) -> Option<predicates_core::reflection::Case<'a>> {
603        self.0.find_case(expected, variable)
604    }
605}
606
607impl fmt::Display for EqCodePredicate {
608    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609        self.0.fmt(f)
610    }
611}
612
613impl IntoCodePredicate<EqCodePredicate> for i32 {
614    type Predicate = EqCodePredicate;
615
616    fn into_code(self) -> Self::Predicate {
617        Self::Predicate::new(self)
618    }
619}
620
621/// Keep `predicates` concrete Predicates out of our public API.
622/// [`predicates_core::Predicate`] used by [`IntoCodePredicate`] for iterables of codes.
623///
624/// # Example
625///
626/// ```rust,no_run
627/// use assert_cmd::prelude::*;
628///
629/// use std::process::Command;
630///
631/// Command::cargo_bin("bin_fixture")
632///     .unwrap()
633///     .env("exit", "42")
634///     .assert()
635///     .code(&[2, 42] as &[i32]);
636/// ```
637#[derive(Debug)]
638pub struct InCodePredicate(predicates::iter::InPredicate<i32>);
639
640impl InCodePredicate {
641    pub(crate) fn new<I: IntoIterator<Item = i32>>(value: I) -> Self {
642        let pred = predicates::iter::in_iter(value);
643        InCodePredicate(pred)
644    }
645}
646
647impl predicates_core::reflection::PredicateReflection for InCodePredicate {
648    fn parameters<'a>(
649        &'a self,
650    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
651        self.0.parameters()
652    }
653
654    /// Nested `Predicate`s of the current `Predicate`.
655    fn children<'a>(
656        &'a self,
657    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
658        self.0.children()
659    }
660}
661
662impl predicates_core::Predicate<i32> for InCodePredicate {
663    fn eval(&self, item: &i32) -> bool {
664        self.0.eval(item)
665    }
666
667    fn find_case<'a>(
668        &'a self,
669        expected: bool,
670        variable: &i32,
671    ) -> Option<predicates_core::reflection::Case<'a>> {
672        self.0.find_case(expected, variable)
673    }
674}
675
676impl fmt::Display for InCodePredicate {
677    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
678        self.0.fmt(f)
679    }
680}
681
682impl IntoCodePredicate<InCodePredicate> for Vec<i32> {
683    type Predicate = InCodePredicate;
684
685    fn into_code(self) -> Self::Predicate {
686        Self::Predicate::new(self)
687    }
688}
689
690impl IntoCodePredicate<InCodePredicate> for &'static [i32] {
691    type Predicate = InCodePredicate;
692
693    fn into_code(self) -> Self::Predicate {
694        Self::Predicate::new(self.iter().cloned())
695    }
696}
697
698/// Used by [`Assert::stdout`] and [`Assert::stderr`] to convert Self
699/// into the needed [`predicates_core::Predicate<[u8]>`].
700///
701/// # Examples
702///
703/// ```rust,no_run
704/// use assert_cmd::prelude::*;
705///
706/// use std::process::Command;
707/// use predicates::prelude::*;
708///
709/// Command::cargo_bin("bin_fixture")
710///     .unwrap()
711///     .env("stdout", "hello")
712///     .env("stderr", "world")
713///     .assert()
714///     .stdout(predicate::str::diff("hello\n").from_utf8());
715///
716/// // which can be shortened to:
717/// Command::cargo_bin("bin_fixture")
718///     .unwrap()
719///     .env("stdout", "hello")
720///     .env("stderr", "world")
721///     .assert()
722///     .stdout("hello\n");
723/// ```
724pub trait IntoOutputPredicate<P>
725where
726    P: predicates_core::Predicate<[u8]>,
727{
728    /// The type of the predicate being returned.
729    type Predicate;
730
731    /// Convert to a predicate for testing a path.
732    fn into_output(self) -> P;
733}
734
735impl<P> IntoOutputPredicate<P> for P
736where
737    P: predicates_core::Predicate<[u8]>,
738{
739    type Predicate = P;
740
741    fn into_output(self) -> Self::Predicate {
742        self
743    }
744}
745
746/// Keep `predicates` concrete Predicates out of our public API.
747/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for bytes.
748///
749/// # Example
750///
751/// ```rust,no_run
752/// use assert_cmd::prelude::*;
753///
754/// use std::process::Command;
755///
756/// Command::cargo_bin("bin_fixture")
757///     .unwrap()
758///     .env("stdout", "hello")
759///     .env("stderr", "world")
760///     .assert()
761///     .stderr(b"world\n" as &[u8]);
762/// ```
763#[derive(Debug)]
764pub struct BytesContentOutputPredicate(Cow<'static, [u8]>);
765
766impl BytesContentOutputPredicate {
767    pub(crate) fn new(value: &'static [u8]) -> Self {
768        BytesContentOutputPredicate(Cow::from(value))
769    }
770
771    pub(crate) fn from_vec(value: Vec<u8>) -> Self {
772        BytesContentOutputPredicate(Cow::from(value))
773    }
774}
775
776impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate {}
777
778impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate {
779    fn eval(&self, item: &[u8]) -> bool {
780        self.0.as_ref() == item
781    }
782
783    fn find_case(
784        &self,
785        expected: bool,
786        variable: &[u8],
787    ) -> Option<predicates_core::reflection::Case<'_>> {
788        let actual = self.eval(variable);
789        if expected == actual {
790            Some(predicates_core::reflection::Case::new(Some(self), actual))
791        } else {
792            None
793        }
794    }
795}
796
797impl fmt::Display for BytesContentOutputPredicate {
798    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799        predicates::ord::eq(self.0.as_ref()).fmt(f)
800    }
801}
802
803impl IntoOutputPredicate<BytesContentOutputPredicate> for Vec<u8> {
804    type Predicate = BytesContentOutputPredicate;
805
806    fn into_output(self) -> Self::Predicate {
807        Self::Predicate::from_vec(self)
808    }
809}
810
811impl IntoOutputPredicate<BytesContentOutputPredicate> for &'static [u8] {
812    type Predicate = BytesContentOutputPredicate;
813
814    fn into_output(self) -> Self::Predicate {
815        Self::Predicate::new(self)
816    }
817}
818
819/// Keep `predicates` concrete Predicates out of our public API.
820/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for [`str`].
821///
822/// # Example
823///
824/// ```rust,no_run
825/// use assert_cmd::prelude::*;
826///
827/// use std::process::Command;
828///
829/// Command::cargo_bin("bin_fixture")
830///     .unwrap()
831///     .env("stdout", "hello")
832///     .env("stderr", "world")
833///     .assert()
834///     .stderr("world\n");
835/// ```
836///
837/// [`str`]: https://doc.rust-lang.org/std/primitive.str.html
838#[derive(Debug, Clone)]
839pub struct StrContentOutputPredicate(
840    predicates::str::Utf8Predicate<predicates::str::DifferencePredicate>,
841);
842
843impl StrContentOutputPredicate {
844    pub(crate) fn from_str(value: &'static str) -> Self {
845        let pred = predicates::str::diff(value).from_utf8();
846        StrContentOutputPredicate(pred)
847    }
848
849    pub(crate) fn from_string(value: String) -> Self {
850        let pred = predicates::str::diff(value).from_utf8();
851        StrContentOutputPredicate(pred)
852    }
853}
854
855impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate {
856    fn parameters<'a>(
857        &'a self,
858    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
859        self.0.parameters()
860    }
861
862    /// Nested `Predicate`s of the current `Predicate`.
863    fn children<'a>(
864        &'a self,
865    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
866        self.0.children()
867    }
868}
869
870impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate {
871    fn eval(&self, item: &[u8]) -> bool {
872        self.0.eval(item)
873    }
874
875    fn find_case<'a>(
876        &'a self,
877        expected: bool,
878        variable: &[u8],
879    ) -> Option<predicates_core::reflection::Case<'a>> {
880        self.0.find_case(expected, variable)
881    }
882}
883
884impl fmt::Display for StrContentOutputPredicate {
885    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
886        self.0.fmt(f)
887    }
888}
889
890impl IntoOutputPredicate<StrContentOutputPredicate> for String {
891    type Predicate = StrContentOutputPredicate;
892
893    fn into_output(self) -> Self::Predicate {
894        Self::Predicate::from_string(self)
895    }
896}
897
898impl IntoOutputPredicate<StrContentOutputPredicate> for &'static str {
899    type Predicate = StrContentOutputPredicate;
900
901    fn into_output(self) -> Self::Predicate {
902        Self::Predicate::from_str(self)
903    }
904}
905
906// Keep `predicates` concrete Predicates out of our public API.
907/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for
908/// [`Predicate<str>`][predicates_core::Predicate].
909///
910/// # Example
911///
912/// ```rust,no_run
913/// use assert_cmd::prelude::*;
914///
915/// use std::process::Command;
916/// use predicates::prelude::*;
917///
918/// Command::cargo_bin("bin_fixture")
919///     .unwrap()
920///     .env("stdout", "hello")
921///     .env("stderr", "world")
922///     .assert()
923///     .stderr(predicate::str::diff("world\n"));
924/// ```
925#[derive(Debug, Clone)]
926pub struct StrOutputPredicate<P: predicates_core::Predicate<str>>(
927    predicates::str::Utf8Predicate<P>,
928);
929
930impl<P> StrOutputPredicate<P>
931where
932    P: predicates_core::Predicate<str>,
933{
934    pub(crate) fn new(pred: P) -> Self {
935        let pred = pred.from_utf8();
936        StrOutputPredicate(pred)
937    }
938}
939
940impl<P> predicates_core::reflection::PredicateReflection for StrOutputPredicate<P>
941where
942    P: predicates_core::Predicate<str>,
943{
944    fn parameters<'a>(
945        &'a self,
946    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
947        self.0.parameters()
948    }
949
950    /// Nested `Predicate`s of the current `Predicate`.
951    fn children<'a>(
952        &'a self,
953    ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
954        self.0.children()
955    }
956}
957
958impl<P> predicates_core::Predicate<[u8]> for StrOutputPredicate<P>
959where
960    P: predicates_core::Predicate<str>,
961{
962    fn eval(&self, item: &[u8]) -> bool {
963        self.0.eval(item)
964    }
965
966    fn find_case<'a>(
967        &'a self,
968        expected: bool,
969        variable: &[u8],
970    ) -> Option<predicates_core::reflection::Case<'a>> {
971        self.0.find_case(expected, variable)
972    }
973}
974
975impl<P> fmt::Display for StrOutputPredicate<P>
976where
977    P: predicates_core::Predicate<str>,
978{
979    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
980        self.0.fmt(f)
981    }
982}
983
984impl<P> IntoOutputPredicate<StrOutputPredicate<P>> for P
985where
986    P: predicates_core::Predicate<str>,
987{
988    type Predicate = StrOutputPredicate<P>;
989
990    fn into_output(self) -> Self::Predicate {
991        Self::Predicate::new(self)
992    }
993}
994
995/// [`Assert`] represented as a [`Result`].
996///
997/// Produced by the `try_` variants the [`Assert`] methods.
998///
999/// # Example
1000///
1001/// ```rust
1002/// use assert_cmd::prelude::*;
1003///
1004/// use std::process::Command;
1005///
1006/// let result = Command::new("echo")
1007///     .assert()
1008///     .try_success();
1009/// assert!(result.is_ok());
1010/// ```
1011///
1012/// [`Result`]: std::result::Result
1013pub type AssertResult = Result<Assert, AssertError>;
1014
1015/// [`Assert`] error (see [`AssertResult`]).
1016#[derive(Debug)]
1017pub struct AssertError {
1018    assert: Assert,
1019    reason: AssertReason,
1020}
1021
1022#[derive(Debug)]
1023enum AssertReason {
1024    UnexpectedFailure { actual_code: Option<i32> },
1025    UnexpectedSuccess,
1026    UnexpectedCompletion,
1027    CommandInterrupted,
1028    UnexpectedReturnCode { case_tree: CaseTree },
1029    UnexpectedStdout { case_tree: CaseTree },
1030    UnexpectedStderr { case_tree: CaseTree },
1031}
1032
1033impl AssertError {
1034    #[track_caller]
1035    fn panic<T>(self) -> T {
1036        panic!("{}", self)
1037    }
1038
1039    /// Returns the [`Assert`] wrapped into the [`Result`] produced by
1040    /// the `try_` variants of the [`Assert`] methods.
1041    ///
1042    /// # Examples
1043    ///
1044    /// ```rust,no_run
1045    /// use assert_cmd::prelude::*;
1046    ///
1047    /// use std::process::Command;
1048    /// use predicates::prelude::*;
1049    ///
1050    /// let result = Command::new("echo")
1051    ///     .assert();
1052    ///
1053    /// match result.try_success() {
1054    ///         Ok(assert) => {
1055    ///             assert.stdout(predicate::eq(b"Success\n" as &[u8]));
1056    ///         }
1057    ///         Err(err) => {
1058    ///            err.assert().stdout(predicate::eq(b"Err but some specific output you might want to check\n" as &[u8]));
1059    ///         }
1060    ///     }
1061    /// ```
1062    pub fn assert(self) -> Assert {
1063        self.assert
1064    }
1065}
1066
1067impl Error for AssertError {}
1068
1069impl fmt::Display for AssertError {
1070    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1071        match &self.reason {
1072            AssertReason::UnexpectedFailure { actual_code } => writeln!(
1073                f,
1074                "Unexpected failure.\ncode={}\nstderr=```{}```",
1075                actual_code
1076                    .map(|actual_code| actual_code.to_string())
1077                    .unwrap_or_else(|| "<interrupted>".to_owned()),
1078                DebugBytes::new(&self.assert.output.stderr),
1079            ),
1080            AssertReason::UnexpectedSuccess => {
1081                writeln!(f, "Unexpected success")
1082            }
1083            AssertReason::UnexpectedCompletion => {
1084                writeln!(f, "Unexpected completion")
1085            }
1086            AssertReason::CommandInterrupted => {
1087                writeln!(f, "Command interrupted")
1088            }
1089            AssertReason::UnexpectedReturnCode { case_tree } => {
1090                writeln!(f, "Unexpected return code, failed {case_tree}")
1091            }
1092            AssertReason::UnexpectedStdout { case_tree } => {
1093                writeln!(f, "Unexpected stdout, failed {case_tree}")
1094            }
1095            AssertReason::UnexpectedStderr { case_tree } => {
1096                writeln!(f, "Unexpected stderr, failed {case_tree}")
1097            }
1098        }?;
1099        write!(f, "{}", self.assert)
1100    }
1101}
1102
1103struct CaseTree(predicates_tree::CaseTree);
1104
1105impl fmt::Display for CaseTree {
1106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1107        <predicates_tree::CaseTree as fmt::Display>::fmt(&self.0, f)
1108    }
1109}
1110
1111// Work around `Debug` not being implemented for `predicates_tree::CaseTree`.
1112impl fmt::Debug for CaseTree {
1113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1114        <predicates_tree::CaseTree as fmt::Display>::fmt(&self.0, f)
1115    }
1116}
1117
1118#[cfg(test)]
1119mod test {
1120    use super::*;
1121
1122    use predicates::prelude::*;
1123
1124    // Since IntoCodePredicate exists solely for conversion, test it under that scenario to ensure
1125    // it works as expected.
1126    fn convert_code<I, P>(pred: I) -> P
1127    where
1128        I: IntoCodePredicate<P>,
1129        P: Predicate<i32>,
1130    {
1131        pred.into_code()
1132    }
1133
1134    #[test]
1135    fn into_code_from_pred() {
1136        let pred = convert_code(predicate::eq(10));
1137        assert!(pred.eval(&10));
1138    }
1139
1140    #[test]
1141    fn into_code_from_i32() {
1142        let pred = convert_code(10);
1143        assert!(pred.eval(&10));
1144    }
1145
1146    #[test]
1147    fn into_code_from_vec() {
1148        let pred = convert_code(vec![3, 10]);
1149        assert!(pred.eval(&10));
1150    }
1151
1152    #[test]
1153    fn into_code_from_array() {
1154        let pred = convert_code(&[3, 10] as &[i32]);
1155        assert!(pred.eval(&10));
1156    }
1157
1158    // Since IntoOutputPredicate exists solely for conversion, test it under that scenario to ensure
1159    // it works as expected.
1160    fn convert_output<I, P>(pred: I) -> P
1161    where
1162        I: IntoOutputPredicate<P>,
1163        P: Predicate<[u8]>,
1164    {
1165        pred.into_output()
1166    }
1167
1168    #[test]
1169    fn into_output_from_pred() {
1170        let pred = convert_output(predicate::eq(b"Hello" as &[u8]));
1171        assert!(pred.eval(b"Hello" as &[u8]));
1172    }
1173
1174    #[test]
1175    fn into_output_from_bytes() {
1176        let pred = convert_output(b"Hello" as &[u8]);
1177        assert!(pred.eval(b"Hello" as &[u8]));
1178    }
1179
1180    #[test]
1181    fn into_output_from_vec() {
1182        let pred = convert_output(vec![b'H', b'e', b'l', b'l', b'o']);
1183        assert!(pred.eval(b"Hello" as &[u8]));
1184    }
1185
1186    #[test]
1187    fn into_output_from_str() {
1188        let pred = convert_output("Hello");
1189        assert!(pred.eval(b"Hello" as &[u8]));
1190    }
1191}