diff --git a/src/provenance.rs b/src/provenance.rs index e7e8cc159..d64a246b3 100644 --- a/src/provenance.rs +++ b/src/provenance.rs @@ -192,13 +192,12 @@ impl OwnedProvenanceTable { #[cfg(test)] mod test_provenances { use super::*; - use crate::test_fixtures::make_empty_table_collection; use crate::TableAccess; #[test] fn test_empty_record_string() { // check for tables... - let mut tables = make_empty_table_collection(1.0); + let mut tables = crate::TableCollection::new(10.).unwrap(); let s = String::from(""); let row_id = tables.add_provenance(&s).unwrap(); let _ = tables.provenances().row(row_id).unwrap(); @@ -215,7 +214,7 @@ mod test_provenances { #[test] fn test_add_rows() { let records = vec!["banana".to_string(), "split".to_string()]; - let mut tables = make_empty_table_collection(1.); + let mut tables = crate::TableCollection::new(10.).unwrap(); for (i, r) in records.iter().enumerate() { let row_id = tables.add_provenance(r).unwrap(); assert!(row_id == ProvenanceId(i as crate::tsk_id_t)); diff --git a/src/table_collection.rs b/src/table_collection.rs index 086ccd148..19b146a8e 100644 --- a/src/table_collection.rs +++ b/src/table_collection.rs @@ -1245,1069 +1245,3 @@ impl TableAccess for TableCollection { } impl crate::traits::NodeListGenerator for TableCollection {} - -#[cfg(test)] -mod test { - use super::*; - use crate::metadata::*; - use crate::prelude::*; - use crate::NodeFlags; - - fn make_small_table_collection() -> TableCollection { - let mut tables = TableCollection::new(1000.).unwrap(); - tables - .add_node(0, 1.0, PopulationId::NULL, IndividualId::NULL) - .unwrap(); - tables - .add_node(0, 0.0, PopulationId::NULL, IndividualId::NULL) - .unwrap(); - tables - .add_node(0, 0.0, PopulationId::NULL, IndividualId::NULL) - .unwrap(); - tables.add_edge(0., 1000., 0, 1).unwrap(); - tables.add_edge(0., 1000., 0, 2).unwrap(); - tables.build_index().unwrap(); - tables - } - - #[test] - fn test_sequence_length() { - let tables = TableCollection::new(1000.).unwrap(); - assert!(close_enough(tables.sequence_length(), 1000.)); - } - - #[test] - #[should_panic] - fn test_zero_sequence_length() { - let _ = TableCollection::new(0.).unwrap(); - } - - #[test] - #[should_panic] - fn test_negative_sequence_length() { - let _ = TableCollection::new(-1.).unwrap(); - } - - #[test] - fn test_add_edges() { - let mut tables = TableCollection::new(1000.).unwrap(); - for i in 0..5 { - let _ = tables.add_edge(0., 1000., i, 2 * i).unwrap(); - } - let edges = tables.edges(); - for i in 0..5 { - assert_eq!(edges.parent(i).unwrap(), i); - assert_eq!(edges.child(i).unwrap(), 2 * i); - } - } - - #[test] - fn test_mutable_node_access() { - let tables = TableCollection::new(1000.).unwrap(); - let mut nodes = tables.nodes(); - let f = nodes.flags_array_mut(); - for i in f { - *i = NodeFlags::from(11); - } - - for t in nodes.time_array_mut() { - *t = Time::from(-33.0); - } - - for i in tables.nodes_iter() { - assert_eq!(i.flags.bits(), 11); - assert_eq!(f64::from(i.time) as i64, -33); - } - } - - #[test] - fn test_node_iteration() { - let tables = make_small_table_collection(); - for (i, row) in tables.nodes().iter().enumerate() { - assert!(close_enough( - tables.nodes().time(i as tsk_id_t).unwrap(), - row.time - )); - assert_eq!(tables.nodes().flags(i as tsk_id_t).unwrap(), row.flags); - assert_eq!( - tables.nodes().population(i as tsk_id_t).unwrap(), - row.population - ); - assert_eq!( - tables.nodes().individual(i as tsk_id_t).unwrap(), - row.individual - ); - assert!(row.metadata.is_none()); - } - - for row in tables.nodes_iter() { - assert!(close_enough(tables.nodes().time(row.id).unwrap(), row.time)); - assert_eq!(tables.nodes().flags(row.id).unwrap(), row.flags); - assert_eq!(tables.nodes().population(row.id).unwrap(), row.population); - assert_eq!(tables.nodes().individual(row.id).unwrap(), row.individual); - assert!(row.metadata.is_none()); - } - } - - #[test] - fn test_edge_iteration() { - let tables = make_small_table_collection(); - for (i, row) in tables.edges().iter().enumerate() { - assert!(close_enough( - tables.edges().left(i as tsk_id_t).unwrap(), - row.left - )); - assert!(close_enough( - tables.edges().right(i as tsk_id_t).unwrap(), - row.right - )); - assert_eq!(tables.edges().parent(i as tsk_id_t).unwrap(), row.parent); - assert_eq!(tables.edges().child(i as tsk_id_t).unwrap(), row.child); - assert!(row.metadata.is_none()); - } - for row in tables.edges_iter() { - assert!(close_enough(tables.edges().left(row.id).unwrap(), row.left)); - assert!(close_enough( - tables.edges().right(row.id).unwrap(), - row.right - )); - assert_eq!(tables.edges().parent(row.id).unwrap(), row.parent); - assert_eq!(tables.edges().child(row.id).unwrap(), row.child); - assert!(row.metadata.is_none()); - } - } - - #[test] - fn test_edge_index_access() { - let tables = make_small_table_collection(); - assert!(tables.is_indexed()); - assert_eq!( - tables.edge_insertion_order().unwrap().len(), - tables.edges().num_rows().try_into().unwrap() - ); - assert_eq!( - tables.edge_removal_order().unwrap().len(), - tables.edges().num_rows().try_into().unwrap() - ); - - for i in tables.edge_insertion_order().unwrap() { - assert!(*i >= 0); - assert!(*i < tables.edges().num_rows()); - } - - for i in tables.edge_removal_order().unwrap() { - assert!(*i >= 0); - assert!(*i < tables.edges().num_rows()); - } - - // The "transparent" casts are such black magic that we - // should probably test against what C thinks is going on :) - let input = unsafe { - std::slice::from_raw_parts( - tables.inner.indexes.edge_insertion_order, - tables.inner.indexes.num_edges as usize, - ) - }; - - assert!(!input.is_empty()); - - let tables_input = tables.edge_insertion_order().unwrap(); - - assert_eq!(input.len(), tables_input.len()); - - for i in 0..input.len() { - assert_eq!(EdgeId::from(input[i]), tables_input[i]); - } - - let output = unsafe { - std::slice::from_raw_parts( - tables.inner.indexes.edge_removal_order, - tables.inner.indexes.num_edges as usize, - ) - }; - assert!(!output.is_empty()); - - let tables_output = tables.edge_removal_order().unwrap(); - - assert_eq!(output.len(), tables_output.len()); - - for i in 0..output.len() { - assert_eq!(EdgeId::from(output[i]), tables_output[i]); - } - } - - #[test] - fn test_add_site() { - let mut tables = TableCollection::new(1000.).unwrap(); - tables.add_site(0.3, Some(b"Eggnog")).unwrap(); - tables.add_site(0.5, None).unwrap(); // No ancestral_state specified!!! - let longer_metadata = "Hot Toddy"; - tables - .add_site(0.9, Some(longer_metadata.as_bytes())) - .unwrap(); - - let sites = tables.sites(); - assert!(close_enough(sites.position(0).unwrap(), 0.3)); - assert!(close_enough(sites.position(1).unwrap(), 0.5)); - assert!(close_enough(sites.position(2).unwrap(), 0.9)); - - match sites.ancestral_state(0) { - Some(astate) => assert_eq!(astate, b"Eggnog"), - None => panic!(), - }; - - if sites.ancestral_state(1).is_some() { - panic!() - } - - match sites.ancestral_state(2) { - Some(astate) => assert_eq!(astate, longer_metadata.as_bytes()), - None => panic!(), - }; - - // NOTE: this is a useful test as not all rows have ancestral_state - let mut no_anc_state = 0; - for (i, row) in sites.iter().enumerate() { - assert!(close_enough( - sites.position(i as tsk_id_t).unwrap(), - row.position - )); - if row.ancestral_state.is_some() { - if i == 0 { - assert_eq!(row.ancestral_state.unwrap(), b"Eggnog"); - } else if i == 2 { - assert_eq!(row.ancestral_state.unwrap(), longer_metadata.as_bytes()); - } - } else { - no_anc_state += 1; - } - } - assert_eq!(no_anc_state, 1); - no_anc_state = 0; - for row in tables.sites_iter() { - assert!(close_enough(sites.position(row.id).unwrap(), row.position)); - if row.ancestral_state.is_some() { - if row.id == 0 { - assert_eq!(row.ancestral_state.unwrap(), b"Eggnog"); - } else if row.id == 2 { - assert_eq!(row.ancestral_state.unwrap(), longer_metadata.as_bytes()); - } - } else { - no_anc_state += 1; - } - } - assert_eq!(no_anc_state, 1); - } - - fn close_enough, R: Into>(a: L, b: R) -> bool { - (a.into() - b.into()).abs() < f64::EPSILON - } - - #[test] - fn test_add_mutation() { - let mut tables = TableCollection::new(1000.).unwrap(); - - tables - .add_mutation(0, 0, MutationId::NULL, 1.123, Some(b"pajamas")) - .unwrap(); - tables - .add_mutation(1, 1, MutationId::NULL, 2.123, None) - .unwrap(); - tables - .add_mutation(2, 2, MutationId::NULL, 3.123, Some(b"more pajamas")) - .unwrap(); - let mutations = tables.mutations(); - assert!(close_enough(mutations.time(0).unwrap(), 1.123)); - assert!(close_enough(mutations.time(1).unwrap(), 2.123)); - assert!(close_enough(mutations.time(2).unwrap(), 3.123)); - assert_eq!(mutations.node(0).unwrap(), 0); - assert_eq!(mutations.node(1).unwrap(), 1); - assert_eq!(mutations.node(2).unwrap(), 2); - assert_eq!(mutations.parent(0).unwrap(), MutationId::NULL); - assert_eq!(mutations.parent(1).unwrap(), MutationId::NULL); - assert_eq!(mutations.parent(2).unwrap(), MutationId::NULL); - assert_eq!(mutations.derived_state(0).unwrap(), b"pajamas"); - - if mutations.derived_state(1).is_some() { - panic!() - } - - assert_eq!(mutations.derived_state(2).unwrap(), b"more pajamas"); - - let mut nmuts = 0; - for (i, row) in tables.mutations().iter().enumerate() { - assert_eq!(row.site, tables.mutations().site(i as tsk_id_t).unwrap()); - assert_eq!(row.node, tables.mutations().node(i as tsk_id_t).unwrap()); - assert_eq!( - row.parent, - tables.mutations().parent(i as tsk_id_t).unwrap() - ); - assert!(close_enough( - row.time, - tables.mutations().time(i as tsk_id_t).unwrap() - )); - assert!(row.metadata.is_none()); - nmuts += 1; - } - assert_eq!(nmuts, tables.mutations().num_rows()); - assert_eq!(nmuts, 3); - - nmuts = 0; - for row in tables.mutations_iter() { - assert_eq!(row.site, tables.mutations().site(row.id).unwrap()); - assert_eq!(row.node, tables.mutations().node(row.id).unwrap()); - assert_eq!(row.parent, tables.mutations().parent(row.id).unwrap()); - assert!(close_enough( - row.time, - tables.mutations().time(row.id).unwrap() - )); - assert!(row.metadata.is_none()); - nmuts += 1; - } - assert_eq!(nmuts, tables.mutations().num_rows()); - assert_eq!(nmuts, 3); - for row in tables.mutations().iter() { - assert!(row.metadata.is_none()); - } - - nmuts = 0; - for _ in tables.mutations().iter().skip(1) { - nmuts += 1; - } - assert_eq!( - crate::SizeType::try_from(nmuts + 1).unwrap(), - tables.mutations().num_rows() - ); - } - - struct F { - x: i32, - y: u32, - } - - impl MetadataRoundtrip for F { - fn encode(&self) -> Result, MetadataError> { - let mut rv = vec![]; - rv.extend(self.x.to_le_bytes().iter().copied()); - rv.extend(self.y.to_le_bytes().iter().copied()); - Ok(rv) - } - fn decode(md: &[u8]) -> Result { - let (x_int_bytes, rest) = md.split_at(std::mem::size_of::()); - let (y_int_bytes, _) = rest.split_at(std::mem::size_of::()); - Ok(Self { - x: i32::from_le_bytes(x_int_bytes.try_into().unwrap()), - y: u32::from_le_bytes(y_int_bytes.try_into().unwrap()), - }) - } - } - - impl MutationMetadata for F {} - - #[test] - fn test_add_mutation_with_metadata() { - let mut tables = TableCollection::new(1000.).unwrap(); - tables - .add_mutation_with_metadata(0, 0, MutationId::NULL, 1.123, None, &F { x: -3, y: 666 }) - .unwrap(); - // The double unwrap is to first check for error - // and then to process the Option. - let md = tables.mutations().metadata::(0.into()).unwrap().unwrap(); - assert_eq!(md.x, -3); - assert_eq!(md.y, 666); - - for row in tables.mutations().iter() { - assert!(row.metadata.is_some()); - let md = F::decode(&row.metadata.unwrap()).unwrap(); - assert_eq!(md.x, -3); - assert_eq!(md.y, 666); - } - } - - #[test] - fn test_add_mutation_with_metadata_for_some_columns() { - let mut tables = TableCollection::new(1000.).unwrap(); - tables - .add_mutation_with_metadata(0, 0, MutationId::NULL, 1.123, None, &F { x: -3, y: 666 }) - .unwrap(); - - tables - .add_mutation(1, 2, MutationId::NULL, 2.0, None) - .unwrap(); - - let mut num_with_metadata = 0; - let mut num_without_metadata = 0; - for i in 0..usize::try_from(tables.mutations().num_rows()).unwrap() { - match tables - .mutations() - .metadata::((i as tsk_id_t).into()) - .transpose() - .unwrap() - { - Some(x) => { - num_with_metadata += 1; - assert_eq!(x.x, -3); - assert_eq!(x.y, 666); - } - None => { - num_without_metadata += 1; - } - } - } - assert_eq!(num_with_metadata, 1); - assert_eq!(num_without_metadata, 1); - } - - #[test] - fn test_add_population() { - let mut tables = TableCollection::new(1000.).unwrap(); - let pop_id = tables.add_population().unwrap(); - assert_eq!(pop_id, 0); - assert_eq!(tables.populations().num_rows(), 1); - - tables - .add_node(crate::TSK_NODE_IS_SAMPLE, 0.0, pop_id, IndividualId::NULL) - .unwrap(); - - match tables.nodes().row(NodeId::from(0)) { - Some(x) => match x.population { - PopulationId(0) => (), - _ => panic!("expected PopulationId(0)"), - }, - None => panic!("expected Some(_)"), - }; - } - - #[test] - fn test_dump_tables() { - let treefile = "trees.trees"; - let mut tables = TableCollection::new(1000.).unwrap(); - let pop_id = tables.add_population().unwrap(); - tables - .add_node(crate::TSK_NODE_IS_SAMPLE, 0.0, pop_id, IndividualId::NULL) - .unwrap(); - tables - .add_node(crate::TSK_NODE_IS_SAMPLE, 1.0, pop_id, IndividualId::NULL) - .unwrap(); - tables.add_edge(0., tables.sequence_length(), 1, 0).unwrap(); - tables - .dump(treefile, TableOutputOptions::default()) - .unwrap(); - - let tables2 = TableCollection::new_from_file(treefile).unwrap(); - assert!(tables.equals(&tables2, TableEqualityOptions::default())); - - std::fs::remove_file(treefile).unwrap(); - } - - #[test] - fn test_clear() { - let mut tables = TableCollection::new(1000.).unwrap(); - for i in 0..5 { - let _ = tables.add_edge(0., 1000., i, 2 * i).unwrap(); - } - assert_eq!(tables.edges().num_rows(), 5); - tables.clear(TableClearOptions::default()).unwrap(); - assert_eq!(tables.edges().num_rows(), 0); - } - - #[test] - fn test_free() { - let mut tables = TableCollection::new(1000.).unwrap(); - tables.free().unwrap(); - } - - #[test] - fn test_deepcopy() { - let tables = make_small_table_collection(); - let dumps = tables.deepcopy().unwrap(); - assert!(tables.equals(&dumps, TableEqualityOptions::default())); - } - - #[test] - fn test_edge_table_row_equality() { - let tables = make_small_table_collection(); - for (i, row) in tables.edges_iter().enumerate() { - assert!(row.id == i as tsk_id_t); - assert!(row == tables.edges().row(i as tsk_id_t).unwrap()); - assert!(!(row != tables.edges().row(i as tsk_id_t).unwrap())); - if i > 0 { - assert!(row != tables.edges().row(i as tsk_id_t - 1).unwrap()); - } - } - } - - #[test] - fn test_node_table_row_equality() { - let tables = make_small_table_collection(); - for (i, row) in tables.nodes_iter().enumerate() { - assert!(row.id.0 == i as tsk_id_t); - assert!(row == tables.nodes().row(i as tsk_id_t).unwrap()); - assert!(!(row != tables.nodes().row(i as tsk_id_t).unwrap())); - } - assert!(tables.nodes().row(0).unwrap() != tables.nodes().row(1).unwrap()); - assert!(tables.nodes().row(1).unwrap() != tables.nodes().row(2).unwrap()); - } - - #[test] - fn test_add_individual_many_ways() { - { - let mut tables = TableCollection::new(1.).unwrap(); - let location = vec![0., 1., 2.]; - let parents = [0, 1, 2, 3, 4]; - tables.add_individual(0, location, parents).unwrap(); - } - { - let mut tables = TableCollection::new(1.).unwrap(); - let location = vec![0., 1., 2.]; - let parents = [0, 1, 2, 3, 4]; - tables - .add_individual(0, location.as_slice(), parents.as_slice()) - .unwrap(); - } - { - let mut tables = TableCollection::new(1.).unwrap(); - let location = [0., 1., 2.]; - let parents = vec![0, 1, 2, 3, 4]; - tables.add_individual(0, location, parents).unwrap(); - } - { - let mut tables = TableCollection::new(1.).unwrap(); - let location = [0., 1., 2.]; - let parents = vec![0, 1, 2, 3, 4]; - tables - .add_individual(0, location.as_slice(), parents.as_slice()) - .unwrap(); - } - } -} - -#[cfg(test)] -mod test_bad_metadata { - use super::*; - use crate::test_fixtures::bad_metadata::*; - use crate::MutationId; - - #[test] - fn test_bad_mutation_metadata_roundtrip() { - let mut tables = TableCollection::new(1.).unwrap(); - let md = F { x: 1, y: 11 }; - tables - .add_mutation_with_metadata(0, 0, MutationId::NULL, 0.0, None, &md) - .unwrap(); - assert!(tables - .mutations() - .metadata::(0.into()) - .transpose() - .is_err()); - } -} - -// The tests that follow involve more detailed analysis -// of the strong ID types. - -#[cfg(test)] -mod test_adding_node { - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_adding_node_without_metadata() { - let mut tables = make_empty_table_collection(10.); - - match tables.add_node(0, 0.0, PopulationId::NULL, IndividualId::NULL) { - Ok(NodeId(0)) => (), - _ => panic!("Expected NodeId(0)"), - }; - - let row = tables.nodes().row(NodeId::from(0)).unwrap(); - - assert_eq!(row.id, NodeId::from(0)); - assert_eq!(row.population, PopulationId::NULL); - assert_eq!(row.individual, IndividualId::NULL); - assert!(row.metadata.is_none()); - - let row_id = tables - .add_node(0, 0.0, PopulationId::from(2), IndividualId::NULL) - .unwrap(); - - assert_eq!(tables.nodes().population(row_id).unwrap(), PopulationId(2)); - assert_eq!( - tables.nodes().individual(row_id).unwrap(), - IndividualId::NULL, - ); - assert!(tables.nodes().metadata::(row_id).is_none()); - - // We are playing a dangerous game here, - // in that we do not have any populations. - // Fortunately, we are range-checked everywhere. - println!("{}", tables.nodes().population(row_id).unwrap()); - println!("{}", tables.populations().num_rows()); - assert!(tables - .populations() - .row(tables.nodes().population(row_id).unwrap()) - .is_none()); - - let row_id = tables - .add_node(0, 0.0, PopulationId::NULL, IndividualId::from(17)) - .unwrap(); - - assert_eq!( - tables.nodes().population(row_id).unwrap(), - PopulationId::NULL, - ); - assert_eq!(tables.nodes().individual(row_id).unwrap(), IndividualId(17)); - - assert!(tables - .individuals() - .row(tables.nodes().individual(row_id).unwrap()) - .is_none()); - } - - #[test] - fn test_adding_node_with_metadata() { - let mut tables = make_empty_table_collection(10.); - let metadata = vec![GenericMetadata::default(), GenericMetadata { data: 12345 }]; - - for (mi, m) in metadata.iter().enumerate() { - let row_id = match tables.add_node_with_metadata( - 0, - 1.0, - PopulationId::from(11), - IndividualId::from(12), - m, - ) { - Ok(NodeId(x)) => NodeId(x), - Err(_) => panic!("unexpected Err"), - }; - assert_eq!( - tables - .nodes() - .metadata::(row_id) - .transpose() - .unwrap(), - Some(metadata[mi]) - ); - } - } -} - -#[cfg(test)] -mod test_adding_individual { - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_adding_individual_without_metadata() { - let mut tables = make_empty_table_collection(10.); - match tables.add_individual(0, [0., 0., 0.], [IndividualId::NULL, IndividualId::NULL]) { - Ok(IndividualId(0)) => (), - _ => panic!("Expected NodeId(0)"), - }; - - let row = tables.individuals().row(IndividualId::from(0)).unwrap(); - assert_eq!(row.id, IndividualId::from(0)); - assert!(row.location.is_some()); - assert_eq!(row.location.unwrap().len(), 3); - - assert_eq!( - row.parents, - Some(vec![IndividualId::NULL, IndividualId::NULL,]) - ); - - // Empty slices are a thing, causing None to be in the rows. - let row_id = tables - .add_individual(0, &[] as &[f64], &[] as &[IndividualId]) - .unwrap(); - let row = tables.individuals().row(row_id).unwrap(); - assert_eq!(row.id, IndividualId::from(1)); - assert!(row.location.is_none()); - assert!(row.parents.is_none()); - let row_id = tables - .add_individual(0, [0.2, 0.4, 0.6], [1, 2, 3, 4]) - .unwrap(); - assert_eq!(row_id, 2); - assert_eq!( - tables.individuals().location(row_id), - Some( - [ - crate::Location::from(0.2), - crate::Location::from(0.4), - crate::Location::from(0.6) - ] - .as_slice() - ) - ); - assert_eq!( - tables.individuals().parents(row_id), - Some( - [ - crate::IndividualId::from(1), - crate::IndividualId::from(2), - crate::IndividualId::from(3), - crate::IndividualId::from(4) - ] - .as_slice() - ) - ); - } - - #[test] - fn test_adding_individual_with_metadata() { - let mut tables = crate::test_fixtures::make_empty_table_collection(10.); - let metadata = vec![GenericMetadata::default(), GenericMetadata { data: 12345 }]; - - for (mi, m) in metadata.iter().enumerate() { - let row_id = match tables.add_individual_with_metadata( - 0, - &[] as &[f64], - &[] as &[IndividualId], - m, - ) { - Ok(IndividualId(x)) => IndividualId(x), - Err(_) => panic!("unexpected Err"), - }; - assert_eq!( - tables - .individuals() - .metadata::(row_id) - .transpose() - .unwrap(), - Some(metadata[mi]) - ); - } - - for (i, j) in tables.individuals().iter().enumerate() { - assert!( - tables - .individuals() - .row(IndividualId::from(i as tsk_id_t)) - .unwrap() - == j - ); - } - - for (i, j) in tables.individuals_iter().enumerate() { - assert!( - tables - .individuals() - .row(IndividualId::from(i as tsk_id_t)) - .unwrap() - == j - ); - } - } -} - -#[cfg(test)] -mod test_adding_edge { - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_adding_edge_without_metadata() { - let mut tables = make_empty_table_collection(10.0); - - let edge_id = tables - .add_edge(0., tables.sequence_length(), 0, 11) - .unwrap(); - - assert_eq!(edge_id, EdgeId(0)); - assert_eq!(tables.edges().parent(edge_id).unwrap(), NodeId(0)); - assert_eq!(tables.edges().child(edge_id).unwrap(), NodeId(11)); - } - - #[test] - fn test_adding_edge_with_metadata() { - let mut tables = make_empty_table_collection(10.0); - let metadata = vec![GenericMetadata::default(), GenericMetadata { data: 12345 }]; - - for (mi, m) in metadata.iter().enumerate() { - let edge_id = - match tables.add_edge_with_metadata(0., tables.sequence_length(), 0, 11, m) { - Ok(EdgeId(x)) => EdgeId(x), - Err(_) => panic!("unexpected Err"), - }; - assert_eq!( - tables - .edges() - .metadata::(edge_id) - .transpose() - .unwrap(), - Some(metadata[mi]) - ); - } - } -} - -#[cfg(test)] -mod test_adding_mutation { - use crate::metadata::MetadataRoundtrip; - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_adding_mutation_without_metadata() { - let mut tables = make_empty_table_collection(1.0); - - let mut_id = tables.add_mutation(0, 0, -1, 1.0, None).unwrap(); - - assert_eq!(mut_id, MutationId(0)); - assert_eq!(mut_id, 0); - - let row_0 = tables.mutations().row(mut_id).unwrap(); - - assert_eq!(row_0.id, 0); - - let mut_id_two = tables.add_mutation(0, 0, -1, 1.0, None).unwrap(); - - assert!(mut_id_two > mut_id); - assert_ne!(mut_id_two, mut_id); - - let row_1 = tables.mutations().row(mut_id_two).unwrap(); - - assert!(row_0 != row_1); - - for row in [mut_id, mut_id_two] { - if tables - .mutations() - .metadata::(row) - .is_some() - { - panic!("expected None"); - } - } - } - - #[test] - fn test_adding_mutation_with_metadata() { - let mut tables = make_empty_table_collection(1.0); - let metadata = vec![GenericMetadata::default(), GenericMetadata { data: 12345 }]; - - for (mi, m) in metadata.iter().enumerate() { - let mut_id = match tables.add_mutation_with_metadata(0, 0, -1, 1.0, None, m) { - Ok(MutationId(x)) => MutationId(x), - Err(_) => panic!("unexpected Err"), - }; - assert_eq!( - tables - .mutations() - .metadata::(mut_id) - .transpose() - .unwrap(), - Some(metadata[mi]) - ); - assert_eq!( - GenericMetadata::decode(&tables.mutations().row(mut_id).unwrap().metadata.unwrap()) - .unwrap(), - *m - ); - } - } -} - -#[cfg(test)] -mod test_adding_site { - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_adding_site_without_metadata() { - let mut tables = make_empty_table_collection(11.0); - let site_id = tables.add_site(0.1, None).unwrap(); - - match site_id { - SiteId(0) => (), - _ => panic!("Expected SiteId(0)"), - }; - - assert_eq!(site_id, 0); - - assert!(tables - .sites() - .metadata::(site_id) - .is_none()); - - let row = tables.sites().row(site_id).unwrap(); - assert_eq!(row.id, site_id); - assert!(row.ancestral_state.is_none()); - assert!(row.metadata.is_none()); - } - - #[test] - fn test_adding_site_with_metadata() { - let mut tables = make_empty_table_collection(11.0); - let metadata = vec![GenericMetadata::default(), GenericMetadata { data: 12345 }]; - - for (mi, m) in metadata.iter().enumerate() { - let site_id = match tables.add_site_with_metadata(0.1, None, m) { - Ok(SiteId(x)) => SiteId(x), - Err(_) => panic!("unexpected Err"), - }; - assert_eq!( - tables - .sites() - .metadata::(site_id) - .transpose() - .unwrap(), - Some(metadata[mi]) - ); - } - for i in 0..usize::try_from(tables.sites().num_rows()).unwrap() { - assert!( - tables.sites().row(SiteId::from(i as tsk_id_t)).unwrap() - == tables.sites().row(SiteId::from(i as tsk_id_t)).unwrap() - ); - if i > 0 { - assert!( - tables.sites().row(SiteId::from(i as tsk_id_t)).unwrap() - != tables - .sites() - .row(SiteId::from((i - 1) as tsk_id_t)) - .unwrap() - ); - } - } - } -} - -#[cfg(test)] -mod test_adding_population { - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_adding_population_without_metadata() { - let mut tables = make_empty_table_collection(11.0); - let pop_id = tables.add_population().unwrap(); - - assert!(pop_id == PopulationId(0)); - assert!(pop_id == 0); - assert!(tables - .populations() - .metadata::(pop_id) - .is_none()); - - for row in tables.populations_iter() { - assert!(row.metadata.is_none()); - } - - for row in tables.populations().iter() { - assert!(row.metadata.is_none()); - } - - println!("{} {}", pop_id, tables.populations().num_rows()); - - assert!( - tables.populations().row(pop_id).unwrap() == tables.populations().row(pop_id).unwrap() - ); - } - - #[test] - fn test_adding_population_with_metadata() { - let mut tables = make_empty_table_collection(11.0); - let pop_id = tables - .add_population_with_metadata(&GenericMetadata::default()) - .unwrap(); - assert!( - tables - .populations() - .metadata::(pop_id) - .transpose() - .unwrap() - == Some(GenericMetadata::default()) - ); - } -} - -#[cfg(test)] -mod test_adding_migrations { - use crate::test_fixtures::make_empty_table_collection; - use crate::test_fixtures::GenericMetadata; - use crate::*; - - #[test] - fn test_add_migration_without_metadata() { - let mut tables = make_empty_table_collection(1.0); - let mig_id = tables.add_migration((0., 1.), 7, (0, 1), 1e-3).unwrap(); - - match mig_id { - MigrationId(0) => (), - _ => panic!("Extend MigrationId(0)"), - }; - - assert_eq!(mig_id, 0); - assert_eq!(tables.migrations().node(mig_id).unwrap(), NodeId(7)); - assert_eq!(tables.migrations().source(mig_id).unwrap(), PopulationId(0)); - assert_eq!(tables.migrations().dest(mig_id).unwrap(), PopulationId(1)); - assert!(tables - .migrations() - .metadata::(mig_id) - .is_none()); - } - - #[test] - fn test_add_migration_with_metadata() { - use crate::metadata::MetadataRoundtrip; - - let metadata = vec![GenericMetadata::default(), GenericMetadata { data: 84 }]; - - let mut tables = make_empty_table_collection(1.0); - - for (i, md) in metadata.iter().enumerate() { - let id_i = i as tsk_id_t; - let mig_id = - tables.add_migration_with_metadata((0., 1.), 7 * id_i, (id_i, id_i + 1), 1e-3, md); - - match mig_id { - Ok(MigrationId(x)) => { - assert_eq!(x, id_i); - } - Err(_) => panic!("got unexpected error"), - }; - - let mig_id = mig_id.unwrap(); - - let row = tables.migrations().row(mig_id).unwrap(); - assert_eq!(row.id, mig_id); - assert_eq!(row.source, PopulationId(id_i * tsk_id_t::from(mig_id))); - assert_eq!(row.dest, PopulationId(id_i * tsk_id_t::from(mig_id) + 1)); - assert_eq!(row.node, NodeId(7 * id_i)); - } - - for i in 0..tables.migrations().num_rows().try_into().unwrap() { - assert!( - tables.migrations().row(MigrationId::from(i)).unwrap() - == tables.migrations().row(MigrationId::from(i)).unwrap() - ); - if i > 0 { - assert!( - tables.migrations().row(MigrationId::from(i)).unwrap() - != tables.migrations().row(MigrationId::from(i - 1)).unwrap() - ); - } - } - - for (i, r) in tables.migrations_iter().enumerate() { - assert_eq!(r.id, i as crate::tsk_id_t); - assert_eq!( - GenericMetadata::decode(&r.metadata.unwrap()).unwrap(), - metadata[i] - ); - } - - for (i, r) in tables.migrations().iter().enumerate() { - assert_eq!(r.id, i as crate::tsk_id_t); - assert_eq!( - GenericMetadata::decode(&r.metadata.unwrap()).unwrap(), - metadata[i] - ); - } - } -} diff --git a/src/test_fixtures.rs b/src/test_fixtures.rs index db5d35de9..94080ced4 100644 --- a/src/test_fixtures.rs +++ b/src/test_fixtures.rs @@ -1,11 +1,6 @@ #[cfg(test)] use crate::*; -#[cfg(test)] -pub fn make_empty_table_collection(L: f64) -> TableCollection { - TableCollection::new(L).unwrap() -} - #[cfg(test)] #[derive(serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Copy, Clone)] pub struct GenericMetadata { diff --git a/tests/empty_table_collection.rs b/tests/empty_table_collection.rs deleted file mode 100644 index 951eb472f..000000000 --- a/tests/empty_table_collection.rs +++ /dev/null @@ -1,27 +0,0 @@ -#[test] -fn test_empty_table_collection() { - use tskit::TableAccess; - - let tables = tskit::TableCollection::new(10.).unwrap(); - - assert!(tables.edges().row(-1).is_none()); - assert!(tables.edges().row(0).is_none()); - assert!(tables.nodes().row(-1).is_none()); - assert!(tables.nodes().row(0).is_none()); - assert!(tables.sites().row(-1).is_none()); - assert!(tables.sites().row(0).is_none()); - assert!(tables.mutations().row(-1).is_none()); - assert!(tables.mutations().row(0).is_none()); - assert!(tables.individuals().row(-1).is_none()); - assert!(tables.individuals().row(0).is_none()); - assert!(tables.populations().row(-1).is_none()); - assert!(tables.populations().row(0).is_none()); - assert!(tables.migrations().row(-1).is_none()); - assert!(tables.migrations().row(0).is_none()); - - #[cfg(feature = "provenance")] - { - assert!(tables.provenances().row(-1).is_none()); - assert!(tables.provenances().row(0).is_none()); - } -} diff --git a/tests/test_tables.rs b/tests/test_tables.rs new file mode 100644 index 000000000..a06dddef4 --- /dev/null +++ b/tests/test_tables.rs @@ -0,0 +1,386 @@ +#[test] +fn test_empty_table_collection() { + macro_rules! validate_empty_tables { + ($tables: ident, $table: ident, $table_iter: ident, $row: expr) => { + assert!($tables.$table().row($row).is_none()); + assert_eq!($tables.$table().num_rows(), 0); + assert_eq!($tables.$table().iter().count(), 0); + assert_eq!($tables.$table_iter().count(), 0); + }; + } + use tskit::TableAccess; + let tables = tskit::TableCollection::new(10.).unwrap(); + + for row in [0, -1, 303] { + validate_empty_tables!(tables, edges, edges_iter, row); + validate_empty_tables!(tables, nodes, nodes_iter, row); + validate_empty_tables!(tables, sites, sites_iter, row); + validate_empty_tables!(tables, mutations, mutations_iter, row); + validate_empty_tables!(tables, individuals, individuals_iter, row); + validate_empty_tables!(tables, populations, populations_iter, row); + validate_empty_tables!(tables, migrations, migrations_iter, row); + #[cfg(feature = "provenance")] + { + validate_empty_tables!(tables, provenances, provenances_iter, row); + } + } +} + +// We are not checking column getters here. +// We are not doing integrity checks. +#[cfg(test)] +mod test_adding_rows_without_metadata { + macro_rules! add_row_without_metadata { + ($table: ident, $adder: ident, $($payload: expr),* ) => {{ + { + use tskit::TableAccess; + let mut tables = tskit::TableCollection::new(10.).unwrap(); + match tables.$adder($($payload ), *) { + Ok(id) => { + assert_eq!(tables.$table().num_rows(), 1); + + // Rows store metadata as raw bytes here. + // (Not decoded.) + // The value should be None as the bytes + // are held in an Option. + match tables.$table().row(id) { + Some(row) => { + // assert_eq!(row, row); + assert!(row.metadata.is_none()) + }, + None => panic!("Expected Some(row) from {} table", stringify!(table)) + } + }, + Err(e) => panic!("Err from tables.{}: {:?}", stringify!(adder), e) + } + } + }}; + } + + // NOTE: all functions arguments for adding rows are Into + // where T is one of our new types. + // Further, functions taking multiple inputs of T are defined + // as X: Into, X2: Into, etc., allowing mix-and-match. + + #[test] + fn test_adding_edge() { + add_row_without_metadata!(edges, add_edge, 0.1, 0.5, 0, 1); // left, right, parent, child + add_row_without_metadata!(edges, add_edge, tskit::Position::from(0.1), 0.5, 0, 1); // left, right, parent, child + add_row_without_metadata!(edges, add_edge, 0.1, tskit::Position::from(0.5), 0, 1); // left, right, parent, child + add_row_without_metadata!( + edges, + add_edge, + 0.1, + 0.5, + tskit::NodeId::from(0), + tskit::NodeId::from(1) + ); // left, right, parent, child + add_row_without_metadata!( + edges, + add_edge, + tskit::Position::from(0.1), + tskit::Position::from(0.5), + tskit::NodeId::from(0), + tskit::NodeId::from(1) + ); // left, right, parent, child + } + + #[test] + fn test_adding_node() { + add_row_without_metadata!(nodes, add_node, 0, 0.1, -1, -1); // flags, time, population, + // individual + add_row_without_metadata!( + nodes, + add_node, + tskit::NodeFlags::default(), + tskit::Time::from(0.1), + tskit::PopulationId::NULL, + tskit::IndividualId::NULL + ); + } + + #[test] + fn test_adding_site() { + // No ancestral state + add_row_without_metadata!(sites, add_site, 2. / 3., None); + add_row_without_metadata!(sites, add_site, tskit::Position::from(2. / 3.), None); + add_row_without_metadata!(sites, add_site, 2. / 3., Some(&[1_u8])); + add_row_without_metadata!( + sites, + add_site, + tskit::Position::from(2. / 3.), + Some(&[1_u8]) + ); + } + + #[test] + fn test_adding_mutation() { + // site, node, parent mutation, time, derived_state + // Each value is a different Into so we skip doing + // permutations + add_row_without_metadata!(mutations, add_mutation, 0, 0, -1, 0.0, None); + add_row_without_metadata!(mutations, add_mutation, 0, 0, -1, 0.0, Some(&[23_u8])); + } + + #[test] + fn test_adding_individual() { + // flags, location, parents + add_row_without_metadata!(individuals, add_individual, 0, None, None); + add_row_without_metadata!( + individuals, + add_individual, + tskit::IndividualFlags::default(), + None, + None + ); + add_row_without_metadata!(individuals, add_individual, 0, &[0.2, 0.2], None); + add_row_without_metadata!( + individuals, + add_individual, + 0, + &[tskit::Location::from(0.2), tskit::Location::from(0.2)], + None + ); + add_row_without_metadata!(individuals, add_individual, 0, None, &[0, 1]); + add_row_without_metadata!( + individuals, + add_individual, + 0, + None, + &[tskit::IndividualId::from(0), tskit::IndividualId::from(1)] + ); + } + + #[test] + fn test_adding_population() { + // population table + add_row_without_metadata!(populations, add_population,); + } + + #[test] + fn test_adding_migration() { + // migration table + // (left, right), node, (source, dest), time + add_row_without_metadata!(migrations, add_migration, (0., 1.), 0, (0, 1), 0.0); + add_row_without_metadata!( + migrations, + add_migration, + (tskit::Position::from(0.), 1.), + 0, + (0, 1), + 0.0 + ); + add_row_without_metadata!( + migrations, + add_migration, + (0., tskit::Position::from(1.)), + 0, + (0, 1), + 0.0 + ); + add_row_without_metadata!( + migrations, + add_migration, + (0., 1.), + tskit::NodeId::from(0), + (0, 1), + 0.0 + ); + add_row_without_metadata!( + migrations, + add_migration, + (0., 1.), + 0, + (0, 1), + tskit::Time::from(0.0) + ); + add_row_without_metadata!( + migrations, + add_migration, + (0., 1.), + 0, + (tskit::PopulationId::from(0), 1), + 0.0 + ); + add_row_without_metadata!( + migrations, + add_migration, + (0., 1.), + 0, + (0, tskit::PopulationId::from(1)), + 0.0 + ); + } +} + +#[cfg(test)] +#[cfg(feature = "derive")] +mod test_metadata_round_trips { + macro_rules! build_metadata_types { + ($md: ident) => { + #[derive( + serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, tskit::metadata::$md, + )] + #[serializer("serde_json")] + struct MyMetadata { + value: i32, + } + + impl MyMetadata { + fn new() -> Self { + Self { value: 42 } + } + } + + // This is the limitation of the current API: + // A different type with the same layout can be + // used -- serde just doens't care. + #[derive( + serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, tskit::metadata::$md, + )] + #[serializer("serde_json")] + struct SameMetadataLayoutDifferentType { + value: i32, + } + + #[derive( + serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, tskit::metadata::$md, + )] + #[serializer("serde_json")] + struct InvalidMetadataType { + value: String, + } + }; + } + + macro_rules! match_block_impl { + ($tables: ident, $table: ident, $adder: ident, $row: ident, $md: ident) => { + match $row { + Ok(id) => { + assert_eq!($tables.$table().num_rows(), 1); + + match $tables.$table().row(id) { + Some(row) => assert!(row.metadata.is_some()), + None => panic!("Expected Some(row) from {} table", stringify!(table)), + } + + match $tables.$table().metadata::(id) { + Some(Ok(value)) => assert_eq!(value, $md), + _ => panic!( + "expected Some(Ok(_)) from tables.{}()::metadata::<_>(row)", + stringify!($table) + ), + } + + match $tables.$table().metadata::(id) { + Some(Err(_)) => (), + _ => panic!( + "expected Some(Err(_)) from tables.{}()::metadata::<_>(row)", + stringify!($table) + ), + } + + // This is the limitation: + // We can record type A as metadata and get type B + // back out so long as A and B (de)serialize the same way. + match $tables + .$table() + .metadata::(id) + { + Some(Ok(_)) => (), + _ => panic!( + "expected Some(Ok(_)) from tables.{}()::metadata::<_>(row)", + stringify!($table) + ), + } + } + Err(e) => panic!("Err from tables.{}: {:?}", stringify!(adder), e), + } + }; + } + + macro_rules! add_row_with_metadata { + ($table: ident, $adder: ident, $md: ident) => {{ + { + build_metadata_types!($md); + use tskit::TableAccess; + let mut tables = tskit::TableCollection::new(10.).unwrap(); + let md = MyMetadata::new(); + let row = tables.$adder(&md); + match_block_impl!(tables, $table, $adder, row, md) + } + }}; + ($table: ident, $adder: ident, $md: ident $(,$payload: expr) + ) => {{ + { + build_metadata_types!($md); + use tskit::TableAccess; + let mut tables = tskit::TableCollection::new(10.).unwrap(); + let md = MyMetadata::new(); + let row = tables.$adder($($payload ), *, &md); + match_block_impl!(tables, $table, $adder, row, md); + } + }}; + } + + #[test] + fn test_edge_metadata() { + add_row_with_metadata!(edges, add_edge_with_metadata, EdgeMetadata, 0.1, 0.5, 0, 1); + } + + #[test] + fn test_adding_node() { + add_row_with_metadata!(nodes, add_node_with_metadata, NodeMetadata, 0, 0.1, -1, -1); + } + + #[test] + fn test_adding_site() { + add_row_with_metadata!(sites, add_site_with_metadata, SiteMetadata, 2. / 3., None); + } + + #[test] + fn test_adding_mutation() { + add_row_with_metadata!( + mutations, + add_mutation_with_metadata, + MutationMetadata, + 0, + 0, + -1, + 0.0, + None + ); + } + #[test] + fn test_adding_individual() { + add_row_with_metadata!( + individuals, + add_individual_with_metadata, + IndividualMetadata, + 0, + None, + None + ); + } + + #[test] + fn test_adding_population() { + add_row_with_metadata!( + populations, + add_population_with_metadata, + PopulationMetadata + ); + } + + #[test] + fn test_adding_migration() { + add_row_with_metadata!( + migrations, + add_migration_with_metadata, + MigrationMetadata, + (0., 1.), + 0, + (0, 1), + 0.0 + ); + } +}