@@ -663,16 +663,16 @@ impl<State> Harness<'_, State> {
663663 /// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.
664664 ///
665665 /// # Panics
666- /// Panics if the image does not match the snapshot, if there was an error reading or writing the
666+ /// The result is added to the [`Harness`]'s internal [`SnapshotResults`].
667+ ///
668+ /// The harness will panic when dropped if there were any snapshot errors.
669+ ///
670+ /// Errors happen if the image does not match the snapshot, if there was an error reading or writing the
667671 /// snapshot, if the rendering fails or if no default renderer is available.
668672 #[ track_caller]
669673 pub fn snapshot_options ( & mut self , name : impl Into < String > , options : & SnapshotOptions ) {
670- match self . try_snapshot_options ( name, options) {
671- Ok ( _) => { }
672- Err ( err) => {
673- panic ! ( "{err}" ) ;
674- }
675- }
674+ let result = self . try_snapshot_options ( name, options) ;
675+ self . snapshot_results . add ( result) ;
676676 }
677677
678678 /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot.
@@ -688,12 +688,8 @@ impl<State> Harness<'_, State> {
688688 /// snapshot, if the rendering fails or if no default renderer is available.
689689 #[ track_caller]
690690 pub fn snapshot ( & mut self , name : impl Into < String > ) {
691- match self . try_snapshot ( name) {
692- Ok ( _) => { }
693- Err ( err) => {
694- panic ! ( "{err}" ) ;
695- }
696- }
691+ let result = self . try_snapshot ( name) ;
692+ self . snapshot_results . add ( result) ;
697693 }
698694
699695 /// Render a snapshot, save it to a temp file and open it in the default image viewer.
@@ -739,6 +735,12 @@ impl<State> Harness<'_, State> {
739735 }
740736 }
741737 }
738+
739+ /// This removes the snapshot results from the harness. Useful if you e.g. want to merge it
740+ /// with the results from another harness (using [`SnapshotResults::add`]).
741+ pub fn take_snapshot_results ( & mut self ) -> SnapshotResults {
742+ std:: mem:: take ( & mut self . snapshot_results )
743+ }
742744}
743745
744746/// Utility to collect snapshot errors and display them at the end of the test.
@@ -765,9 +767,22 @@ impl<State> Harness<'_, State> {
765767/// Panics if there are any errors when dropped (this way it is impossible to forget to call `unwrap`).
766768/// If you don't want to panic, you can use [`SnapshotResults::into_result`] or [`SnapshotResults::into_inner`].
767769/// If you want to panic early, you can use [`SnapshotResults::unwrap`].
768- #[ derive( Debug , Default ) ]
770+ #[ derive( Debug ) ]
769771pub struct SnapshotResults {
770772 errors : Vec < SnapshotError > ,
773+ handled : bool ,
774+ location : std:: panic:: Location < ' static > ,
775+ }
776+
777+ impl Default for SnapshotResults {
778+ #[ track_caller]
779+ fn default ( ) -> Self {
780+ Self {
781+ errors : Vec :: new ( ) ,
782+ handled : true , // If no snapshots were added, we should consider this handled.
783+ location : * std:: panic:: Location :: caller ( ) ,
784+ }
785+ }
771786}
772787
773788impl Display for SnapshotResults {
@@ -785,17 +800,30 @@ impl Display for SnapshotResults {
785800}
786801
787802impl SnapshotResults {
803+ #[ track_caller]
788804 pub fn new ( ) -> Self {
789805 Default :: default ( )
790806 }
791807
792808 /// Check if the result is an error and add it to the list of errors.
793809 pub fn add ( & mut self , result : SnapshotResult ) {
810+ self . handled = false ;
794811 if let Err ( err) = result {
795812 self . errors . push ( err) ;
796813 }
797814 }
798815
816+ /// Add all errors from another `SnapshotResults`.
817+ pub fn extend ( & mut self , other : Self ) {
818+ self . handled = false ;
819+ self . errors . extend ( other. into_inner ( ) ) ;
820+ }
821+
822+ /// Add all errors from a [`Harness`].
823+ pub fn extend_harness < T > ( & mut self , harness : & mut Harness < ' _ , T > ) {
824+ self . extend ( harness. take_snapshot_results ( ) ) ;
825+ }
826+
799827 /// Check if there are any errors.
800828 pub fn has_errors ( & self ) -> bool {
801829 !self . errors . is_empty ( )
@@ -807,13 +835,14 @@ impl SnapshotResults {
807835 if self . has_errors ( ) { Err ( self ) } else { Ok ( ( ) ) }
808836 }
809837
838+ /// Consume this and return the list of errors.
810839 pub fn into_inner ( mut self ) -> Vec < SnapshotError > {
840+ self . handled = true ;
811841 std:: mem:: take ( & mut self . errors )
812842 }
813843
814844 /// Panics if there are any errors, displaying each.
815845 #[ expect( clippy:: unused_self) ]
816- #[ track_caller]
817846 pub fn unwrap ( self ) {
818847 // Panic is handled in drop
819848 }
@@ -826,7 +855,6 @@ impl From<SnapshotResults> for Vec<SnapshotError> {
826855}
827856
828857impl Drop for SnapshotResults {
829- #[ track_caller]
830858 fn drop ( & mut self ) {
831859 // Don't panic if we are already panicking (the test probably failed for another reason)
832860 if std:: thread:: panicking ( ) {
@@ -836,5 +864,32 @@ impl Drop for SnapshotResults {
836864 if self . has_errors ( ) {
837865 panic ! ( "{}" , self ) ;
838866 }
867+
868+ thread_local ! {
869+ static UNHANDLED_SNAPSHOT_RESULTS_COUNTER : std:: cell:: RefCell <usize > = const { std:: cell:: RefCell :: new( 0 ) } ;
870+ }
871+
872+ if !self . handled {
873+ let count = UNHANDLED_SNAPSHOT_RESULTS_COUNTER . with ( |counter| {
874+ let mut count = counter. borrow_mut ( ) ;
875+ * count += 1 ;
876+ * count
877+ } ) ;
878+
879+ #[ expect( clippy:: manual_assert) ]
880+ if count >= 2 {
881+ panic ! (
882+ r#"
883+ Multiple SnapshotResults were dropped without being handled.
884+
885+ In order to allow consistent snapshot updates, all snapshot results within a test should be merged in a single SnapshotResults instance.
886+ Usually this is handled internally in a harness. If you have multiple harnesses, you can merge the results using `Harness::take_snapshot_results` and `SnapshotResults::extend`.
887+
888+ The SnapshotResult was constructed at {}
889+ "# ,
890+ self . location
891+ ) ;
892+ }
893+ }
839894 }
840895}
0 commit comments