gitbaby is an asynchronous Rust library wrapping git operations, built on gix + tokio + async-trait. Module layout: - archive, blame, blob, branch, cleanup, commit, compare, config, conflict, diff, merge, refs, remote, setup, share, submodule, tags, tree (feature modules) - command: command scheduler (cmd/env/pipe/context/error) - repo: RepositoryFacade trait (consumers implement, exposing a gix::Repository) Features: - async API on tokio 1.53 - order-preserving Env (Vec<(String, String)>) - hand-written BabyError / CmdError, not using thiserror derive - integration tests covering cmd_run / context / env / pipe (env keys prefixed for isolation) CI: none, standard cargo build / test / clippy / fmt only
161 lines
3.7 KiB
Rust
161 lines
3.7 KiB
Rust
use gitbaby::command::context::GitPipeContext;
|
|
|
|
struct MinimalCtx;
|
|
|
|
impl GitPipeContext for MinimalCtx {
|
|
fn cancel_pipe(&mut self) {}
|
|
}
|
|
|
|
struct StatefulCtx {
|
|
cancelled: bool,
|
|
current: Option<usize>,
|
|
finished: Vec<usize>,
|
|
progress: Vec<(usize, u64)>,
|
|
}
|
|
|
|
impl GitPipeContext for StatefulCtx {
|
|
fn cancel_pipe(&mut self) {
|
|
self.cancelled = true;
|
|
}
|
|
fn current_index(&self) -> Option<usize> {
|
|
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));
|
|
}
|