use gitbaby::command::context::GitPipeContext; struct MinimalCtx; impl GitPipeContext for MinimalCtx { fn cancel_pipe(&mut self) {} } struct StatefulCtx { cancelled: bool, current: Option, finished: Vec, progress: Vec<(usize, u64)>, } impl GitPipeContext for StatefulCtx { fn cancel_pipe(&mut self) { self.cancelled = true; } fn current_index(&self) -> Option { self.current } fn on_command_finished(&mut self, index: usize) { self.finished.push(index); } fn on_progress(&mut self, index: usize, bytes: u64) { self.progress.push((index, bytes)); } } #[test] fn default_current_index_is_none() { let ctx = MinimalCtx; assert_eq!(ctx.current_index(), None); } #[test] fn default_hooks_are_noops() { let mut ctx = MinimalCtx; ctx.on_command_finished(0); ctx.on_command_finished(7); ctx.on_progress(2, 1024); ctx.on_progress(5, 9999); assert_eq!(ctx.current_index(), None); } #[test] fn cancel_pipe_marks_cancelled_state() { let mut ctx = StatefulCtx { cancelled: false, current: Some(0), finished: vec![], progress: vec![], }; assert!(!ctx.cancelled); ctx.cancel_pipe(); assert!(ctx.cancelled); } #[test] fn on_command_finished_records_indices_in_order() { let mut ctx = StatefulCtx { cancelled: false, current: None, finished: vec![], progress: vec![], }; for i in [0_usize, 1, 2, 3] { ctx.on_command_finished(i); } assert_eq!(ctx.finished, vec![0, 1, 2, 3]); } #[test] fn on_progress_records_index_and_bytes_pairs() { let mut ctx = StatefulCtx { cancelled: false, current: None, finished: vec![], progress: vec![], }; ctx.on_progress(0, 0); ctx.on_progress(0, 1024); ctx.on_progress(1, 2048); assert_eq!(ctx.progress, vec![(0, 0), (0, 1024), (1, 2048)]); } #[test] fn current_index_returns_impl_specific_value() { let ctx = StatefulCtx { cancelled: false, current: Some(3), finished: vec![], progress: vec![], }; assert_eq!(ctx.current_index(), Some(3)); } #[test] fn current_index_can_be_none_even_when_active() { let ctx = StatefulCtx { cancelled: false, current: None, finished: vec![], progress: vec![], }; assert_eq!(ctx.current_index(), None); } #[test] fn trait_object_dispatch_invokes_impl() { let mut ctx = StatefulCtx { cancelled: false, current: Some(2), finished: vec![], progress: vec![], }; { let trait_obj: &mut dyn GitPipeContext = &mut ctx; trait_obj.cancel_pipe(); trait_obj.on_command_finished(1); trait_obj.on_progress(1, 42); assert_eq!(trait_obj.current_index(), Some(2)); } assert!(ctx.cancelled); assert_eq!(ctx.finished, vec![1]); assert_eq!(ctx.progress, vec![(1, 42)]); } #[test] fn multiple_impls_are_independent_via_trait_objects() { let mut a = StatefulCtx { cancelled: false, current: Some(0), finished: vec![], progress: vec![], }; let mut b = StatefulCtx { cancelled: false, current: Some(1), finished: vec![], progress: vec![], }; { let trait_obj: &mut dyn GitPipeContext = &mut a; trait_obj.cancel_pipe(); } { let trait_obj: &mut dyn GitPipeContext = &mut b; trait_obj.on_command_finished(7); } assert!(a.cancelled); assert!(!b.cancelled); assert_eq!(b.finished, vec![7]); assert_eq!(a.current_index(), Some(0)); assert_eq!(b.current_index(), Some(1)); }