#[cfg(feature = "pathmap-backend")]
mod draft_lifecycle_tests {
use liblevenshtein::contextual::{Checkpoint, CheckpointStack, ContextTree, DraftBuffer};
#[test]
fn test_basic_draft_checkpoint_workflow() {
let mut buffer = DraftBuffer::new();
let mut checkpoints = CheckpointStack::new();
checkpoints.push_from_buffer(&buffer);
for ch in "hello".chars() {
buffer.insert(ch);
}
assert_eq!(buffer.as_str(), "hello");
checkpoints.push_from_buffer(&buffer);
for ch in " world".chars() {
buffer.insert(ch);
}
assert_eq!(buffer.as_str(), "hello world");
if let Some(checkpoint) = checkpoints.pop() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "hello");
if let Some(checkpoint) = checkpoints.pop() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "");
}
#[test]
fn test_checkpoint_at_specific_positions() {
let mut buffer = DraftBuffer::new();
for ch in "programming".chars() {
buffer.insert(ch);
}
let checkpoint_5 = Checkpoint::at(5); let checkpoint_7 = Checkpoint::at(7); let checkpoint_11 = Checkpoint::at(11);
checkpoint_7.restore(&mut buffer);
assert_eq!(buffer.as_str(), "program");
checkpoint_5.restore(&mut buffer);
assert_eq!(buffer.as_str(), "progr");
checkpoint_5.restore(&mut buffer);
assert_eq!(buffer.as_str(), "progr");
checkpoint_11.restore(&mut buffer);
assert_eq!(buffer.as_str(), "progr"); }
#[test]
fn test_context_tree_with_draft_lifecycle() {
let mut tree = ContextTree::new();
let mut buffers: std::collections::HashMap<u32, DraftBuffer> =
std::collections::HashMap::new();
let global_ctx = tree.create_root(0);
buffers.insert(global_ctx, DraftBuffer::from_string("global_var"));
let func_ctx = tree.create_child(1, global_ctx).unwrap();
buffers.insert(func_ctx, DraftBuffer::from_string("local_var"));
let block_ctx = tree.create_child(2, func_ctx).unwrap();
buffers.insert(block_ctx, DraftBuffer::from_string("block_var"));
assert_eq!(tree.depth(global_ctx), Some(0));
assert_eq!(tree.depth(func_ctx), Some(1));
assert_eq!(tree.depth(block_ctx), Some(2));
let visible = tree.visible_contexts(block_ctx);
assert_eq!(visible.len(), 3);
assert!(visible.contains(&block_ctx));
assert!(visible.contains(&func_ctx));
assert!(visible.contains(&global_ctx));
let mut visible_texts: Vec<String> = visible
.iter()
.filter_map(|ctx_id| buffers.get(ctx_id).map(|buf| buf.as_str()))
.collect();
visible_texts.sort();
assert_eq!(visible_texts, vec!["block_var", "global_var", "local_var"]);
}
#[test]
fn test_multi_context_draft_management() {
let mut tree = ContextTree::new();
let mut drafts: std::collections::HashMap<u32, (DraftBuffer, CheckpointStack)> =
std::collections::HashMap::new();
let root = tree.create_root(0);
let mut root_buffer = DraftBuffer::new();
let mut root_checkpoints = CheckpointStack::new();
root_checkpoints.push_from_buffer(&root_buffer);
for ch in "root".chars() {
root_buffer.insert(ch);
}
root_checkpoints.push_from_buffer(&root_buffer);
drafts.insert(root, (root_buffer, root_checkpoints));
let child1 = tree.create_child(1, root).unwrap();
let mut child1_buffer = DraftBuffer::new();
let mut child1_checkpoints = CheckpointStack::new();
child1_checkpoints.push_from_buffer(&child1_buffer);
for ch in "child1".chars() {
child1_buffer.insert(ch);
}
child1_checkpoints.push_from_buffer(&child1_buffer);
drafts.insert(child1, (child1_buffer, child1_checkpoints));
let child2 = tree.create_child(2, root).unwrap();
let mut child2_buffer = DraftBuffer::new();
let mut child2_checkpoints = CheckpointStack::new();
child2_checkpoints.push_from_buffer(&child2_buffer);
for ch in "child2".chars() {
child2_buffer.insert(ch);
}
child2_checkpoints.push_from_buffer(&child2_buffer);
drafts.insert(child2, (child2_buffer, child2_checkpoints));
assert_eq!(drafts.get(&root).unwrap().0.as_str(), "root");
assert_eq!(drafts.get(&child1).unwrap().0.as_str(), "child1");
assert_eq!(drafts.get(&child2).unwrap().0.as_str(), "child2");
if let Some((buffer, checkpoints)) = drafts.get_mut(&child1) {
checkpoints.pop(); if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(buffer); }
}
assert_eq!(drafts.get(&child1).unwrap().0.as_str(), "");
assert_eq!(drafts.get(&root).unwrap().0.as_str(), "root");
assert_eq!(drafts.get(&child2).unwrap().0.as_str(), "child2");
}
#[test]
fn test_draft_discard_on_context_removal() {
let mut tree = ContextTree::new();
let mut drafts: std::collections::HashMap<u32, DraftBuffer> =
std::collections::HashMap::new();
let root = tree.create_root(0);
drafts.insert(root, DraftBuffer::from_string("root"));
let func = tree.create_child(1, root).unwrap();
drafts.insert(func, DraftBuffer::from_string("func"));
let block = tree.create_child(2, func).unwrap();
drafts.insert(block, DraftBuffer::from_string("block"));
assert_eq!(tree.len(), 3);
assert_eq!(drafts.len(), 3);
assert!(tree.remove(func));
assert_eq!(tree.len(), 1);
assert_eq!(drafts.len(), 3);
drafts.retain(|ctx_id, _| tree.depth(*ctx_id).is_some());
assert_eq!(drafts.len(), 1);
assert!(drafts.contains_key(&root));
}
#[test]
fn test_checkpoint_stack_capacity() {
let mut buffer = DraftBuffer::new();
let mut checkpoints = CheckpointStack::with_capacity(10);
for i in 0..10 {
buffer.insert(char::from_digit(i, 10).unwrap());
checkpoints.push_from_buffer(&buffer);
}
assert_eq!(checkpoints.len(), 10);
assert_eq!(buffer.as_str(), "0123456789");
for expected_len in (1..=10).rev() {
let checkpoint = checkpoints.pop().unwrap();
checkpoint.restore(&mut buffer);
assert_eq!(buffer.len(), expected_len);
}
assert!(checkpoints.is_empty());
}
#[test]
fn test_draft_finalization() {
let mut buffer = DraftBuffer::new();
let mut checkpoints = CheckpointStack::new();
checkpoints.push_from_buffer(&buffer);
for ch in "draft_text".chars() {
buffer.insert(ch);
}
let finalized = buffer.as_str();
assert_eq!(finalized, "draft_text");
checkpoints.clear();
assert!(checkpoints.is_empty());
buffer.clear();
assert_eq!(buffer.as_str(), "");
}
#[test]
fn test_draft_rollback_to_multiple_checkpoints() {
let mut buffer = DraftBuffer::new();
let mut checkpoints = CheckpointStack::new();
checkpoints.push_from_buffer(&buffer);
buffer.insert('a');
checkpoints.push_from_buffer(&buffer);
buffer.insert('b');
checkpoints.push_from_buffer(&buffer);
buffer.insert('c');
checkpoints.push_from_buffer(&buffer);
buffer.insert('d');
checkpoints.push_from_buffer(&buffer);
assert_eq!(buffer.as_str(), "abcd");
assert_eq!(checkpoints.len(), 5);
checkpoints.pop();
if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "abc");
checkpoints.pop();
if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "ab");
checkpoints.pop();
if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "a");
checkpoints.pop();
if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "");
}
#[test]
fn test_context_visibility_with_sibling_contexts() {
let mut tree = ContextTree::new();
let root = tree.create_root(0);
let child1 = tree.create_child(1, root).unwrap();
let child2 = tree.create_child(2, root).unwrap();
let grandchild1 = tree.create_child(3, child1).unwrap();
let visible = tree.visible_contexts(grandchild1);
assert_eq!(visible.len(), 3); assert!(visible.contains(&grandchild1));
assert!(visible.contains(&child1));
assert!(visible.contains(&root));
assert!(!visible.contains(&child2));
let visible = tree.visible_contexts(child2);
assert_eq!(visible.len(), 2); assert!(visible.contains(&child2));
assert!(visible.contains(&root));
assert!(!visible.contains(&child1)); assert!(!visible.contains(&grandchild1)); }
#[test]
fn test_unicode_draft_with_checkpoints() {
let mut buffer = DraftBuffer::new();
let mut checkpoints = CheckpointStack::new();
checkpoints.push_from_buffer(&buffer);
for ch in "Hello 世界".chars() {
buffer.insert(ch);
}
assert_eq!(buffer.as_str(), "Hello 世界");
checkpoints.push_from_buffer(&buffer);
for ch in " 🌍".chars() {
buffer.insert(ch);
}
assert_eq!(buffer.as_str(), "Hello 世界 🌍");
checkpoints.push_from_buffer(&buffer);
checkpoints.pop(); if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "Hello 世界");
checkpoints.pop();
if let Some(checkpoint) = checkpoints.peek() {
checkpoint.restore(&mut buffer);
}
assert_eq!(buffer.as_str(), "");
}
}