diff --git a/Cargo.lock b/Cargo.lock index af16a88596..3283d32a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,15 +102,19 @@ dependencies = [ "anyhow", "chrono", "collections", + "ctor", "editor", + "env_logger 0.9.3", "fs", "futures 0.3.28", "gpui", "indoc", "isahc", "language", + "log", "menu", "project", + "rand 0.8.5", "regex", "schemars", "search", diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index 5ef371e342..db8772bcb1 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -37,3 +37,8 @@ tiktoken-rs = "0.4" [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } + +ctor.workspace = true +env_logger.workspace = true +log.workspace = true +rand.workspace = true diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 52f31d2f56..bad153879f 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -283,3 +283,11 @@ pub async fn stream_completion( } } } + +#[cfg(test)] +#[ctor::ctor] +fn init_logger() { + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } +} diff --git a/crates/ai/src/diff.rs b/crates/ai/src/diff.rs index 1b5b4cbd20..355748ea3d 100644 --- a/crates/ai/src/diff.rs +++ b/crates/ai/src/diff.rs @@ -204,14 +204,67 @@ impl Diff { #[cfg(test)] mod tests { - use super::*; + use std::env; - #[test] - fn test_diff() { - let mut diff = Diff::new("hello world".to_string()); - dbg!(diff.push_new("hello")); - dbg!(diff.push_new(" ciaone")); - // dbg!(diff.push_new(" world")); - dbg!(diff.finish()); + use super::*; + use rand::prelude::*; + + #[gpui::test(iterations = 100)] + fn test_random_diffs(mut rng: StdRng) { + let old_text_len = env::var("OLD_TEXT_LEN") + .map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable")) + .unwrap_or(10); + let new_text_len = env::var("NEW_TEXT_LEN") + .map(|i| i.parse().expect("invalid `NEW_TEXT_LEN` variable")) + .unwrap_or(10); + + let old = util::RandomCharIter::new(&mut rng) + .take(old_text_len) + .collect::(); + log::info!("old text: {:?}", old); + + let mut diff = Diff::new(old.clone()); + let mut hunks = Vec::new(); + let mut new_len = 0; + let mut new = String::new(); + while new_len < new_text_len { + let new_chunk_len = rng.gen_range(1..=new_text_len - new_len); + let new_chunk = util::RandomCharIter::new(&mut rng) + .take(new_len) + .collect::(); + log::info!("new chunk: {:?}", new_chunk); + new_len += new_chunk_len; + new.push_str(&new_chunk); + let new_hunks = diff.push_new(&new_chunk); + log::info!("hunks: {:?}", new_hunks); + hunks.extend(new_hunks); + } + let final_hunks = diff.finish(); + log::info!("final hunks: {:?}", final_hunks); + hunks.extend(final_hunks); + + log::info!("new text: {:?}", new); + let mut old_ix = 0; + let mut new_ix = 0; + let mut patched = String::new(); + for hunk in hunks { + match hunk { + Hunk::Keep { len } => { + assert_eq!(&old[old_ix..old_ix + len], &new[new_ix..new_ix + len]); + patched.push_str(&old[old_ix..old_ix + len]); + old_ix += len; + new_ix += len; + } + Hunk::Remove { len } => { + old_ix += len; + } + Hunk::Insert { text } => { + assert_eq!(text, &new[new_ix..new_ix + text.len()]); + patched.push_str(&text); + new_ix += text.len(); + } + } + } + assert_eq!(patched, new); } } diff --git a/crates/ai/src/refactor.rs b/crates/ai/src/refactor.rs index 87f7495fcf..d68d8ce4ed 100644 --- a/crates/ai/src/refactor.rs +++ b/crates/ai/src/refactor.rs @@ -78,15 +78,14 @@ impl RefactoringAssistant { } let hunks = diff.push_new(&new_text); - hunks_tx.send((hunks, new_text)).await?; + hunks_tx.send(hunks).await?; } - - hunks_tx.send((diff.finish(), String::new())).await?; + hunks_tx.send(diff.finish()).await?; anyhow::Ok(()) }); - while let Some((hunks, new_text)) = hunks_rx.next().await { + while let Some(hunks) = hunks_rx.next().await { editor.update(&mut cx, |editor, cx| { editor.buffer().update(cx, |buffer, cx| { buffer.start_transaction(cx);