Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
let mut tax_ids = self.tax_ids.clone();
tax_ids.sort_unstable();
let mut dupes = HashSet::new();
let mut last = tax_ids.get(0).unwrap();

Check warning on line 125 in src/base.rs

View workflow job for this annotation

GitHub Actions / clippy

accessing first element with `tax_ids.get(0)`

for i in 1..tax_ids.len() {
let cur = tax_ids.get(i).unwrap();
Expand Down Expand Up @@ -292,11 +292,14 @@
self.parent_distances.remove(idx);
self.ranks.remove(idx);
self.names.remove(idx);

// everything after `tax_id` in parents needs to get decremented by 1
// because we've changed the actual array size
for parent in self.parent_ids.iter_mut().skip(idx + 1) {
if *parent > 0 {
self.data.remove(idx);

// all parent indices pointing to positions after the removed node must be
// decremented by 1, because the array shifted. This applies to every element
// (not just those after idx), since an out-of-order taxonomy can have parents
// with larger indices than their children.
for parent in self.parent_ids.iter_mut() {
if *parent > idx {
*parent -= 1;
}
}
Expand Down Expand Up @@ -523,6 +526,30 @@
assert!(tax.remove("1").is_err());
}

#[test]
fn remove_then_add_then_prune_preserves_nodes() {
use crate::edit::prune_to;

// Reproduce DEV-9780: removing a node, adding it back under a new parent,
// then pruning should not lose unrelated nodes or panic.
let mut tax = create_test_taxonomy();

// Remove "2" (Bacteria), which is a child of root and parent of "562"
tax.remove("2").unwrap();

// Add "2" back directly under root
tax.add("1", "2").unwrap();

// Prune to keep "562" (E. coli). Before the fix, "562" was unreachable
// from root after the remove+add, so the pruned taxonomy was empty.
let pruned = prune_to(&tax, &["562"], false).unwrap();

// "562" must be present and reachable
assert!(pruned.to_internal_index("562").is_ok());
// Root must be present
assert_eq!(Taxonomy::<&str>::root(&pruned), "1");
}

#[test]
fn errors_on_taxonomy_with_cycle() {
let example = r#"{
Expand Down
Loading