use std::env; use std::fs::File; use std::io::prelude::*; use std::process::Command; mod common; use crate::common::*; use stam::*; const CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR"); #[test] fn instantiation_naive() -> Result<(), StamError> { let mut store = AnnotationStore::default().with_id("test"); store.insert(TextResource::from_string( "testres", "Hello world", Config::default(), ))?; let mut dataset = AnnotationDataSet::new(Config::default()).with_id("testdataset"); dataset.insert(DataKey::new("pos"))?; store.insert(dataset)?; Ok(()) } #[test] fn sanity_check() -> Result<(), StamError> { // Instantiate the store let mut store = AnnotationStore::default().with_id("test"); // Insert a text resource into the store store.insert(TextResource::from_string( "testres", "Hello world", Config::default(), ))?; // Create a dataset with one key and insert it into the store let mut dataset = AnnotationDataSet::new(Config::default()).with_id("testdataset"); dataset.insert(DataKey::new("pos"))?; //returns a DataKeyHandle, not further used in this test let set_handle = store.insert(dataset)?; //get by handle (internal id) let dataset = store.dataset(set_handle).or_fail()?; assert_eq!(dataset.id(), Some("testdataset")); //get by directly by id let resource = store.resource("testres").or_fail()?; assert_eq!(resource.id(), Some("testres")); assert_eq!(resource.textlen(), 11); Ok(()) } #[test] fn instantiation_with_builder_pattern() -> Result<(), StamError> { setup_example_1()?; Ok(()) } #[test] fn annotation_iter_data() -> Result<(), StamError> { let store = setup_example_1()?; let annotation = store.annotation("A1").unwrap(); let mut count = 0; for data in annotation.data() { //there should be only one so we can safely test in the loop body count += 1; assert_eq!(data.key().id(), Some("pos")); assert_eq!(data.key().as_str(), "pos"); //shortcut for the same as above assert_eq!(data.value(), &DataValue::String("noun".to_string())); assert_eq!(data.value(), "noun"); //shortcut for the same as above (and more efficient without heap allocated string) assert_eq!(data.id(), Some("D1")); assert_eq!(data.set().id(), Some("testdataset")); } assert_eq!(count, 1); Ok(()) } #[test] fn resource_text() -> Result<(), StamError> { let store = setup_example_1()?; let resource = store.resource("testres").or_fail()?; let text = resource.text(); assert_eq!(text, "Hello world"); Ok(()) } #[test] fn resource_textselection() -> Result<(), StamError> { let store = setup_example_1()?; let resource = store.resource("testres").unwrap(); let textselection = resource.textselection(&Offset::new( Cursor::BeginAligned(0), Cursor::BeginAligned(5), ))?; assert_eq!(textselection.begin(), 0, "testing begin offset"); assert_eq!(textselection.end(), 5, "testing end offset"); assert_eq!( resource.utf8byte(textselection.begin())?, 0, "testing utf-8 begin byte" ); //bytes and unicode point happen to correspond in this simple, example, but this is not necessarily the case assert_eq!( resource.utf8byte(textselection.end())?, 5, "testing utf-8 end byte" ); assert_eq!(textselection.text(), "Hello"); Ok(()) } #[test] fn annotation_text() -> Result<(), StamError> { let store = setup_example_1()?; let annotation = store.annotation("A1").unwrap(); let resource = annotation.resources().next().unwrap(); assert_eq!(resource.id(), Some("testres")); let text: &str = annotation.text().next().unwrap(); assert_eq!(text, "world"); Ok(()) } #[test] fn store_annotate() -> Result<(), StamError> { let mut store = setup_example_1()?; store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "testres", Offset::simple(0, 5), )) .with_data("tokenset", "word", DataValue::Null), )?; store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "testres", Offset::simple(6, 11), )) .with_data("tokenset", "word", DataValue::Null), )?; Ok(()) } #[test] fn store_annotate_existing_data() -> Result<(), StamError> { let mut store = setup_example_2()?; store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "testres", Offset::simple(0, 5), )) .with_data("testdataset", "pos", "noun"), //this one already exists so should not be recreated but found and referenced intead )?; //check if the dataset still contains only one key let dataset: &AnnotationDataSet = store.get("testdataset")?; assert_eq!(dataset.keys().count(), 1); assert_eq!(dataset.data().count(), 1); Ok(()) } #[test] fn store_annotate_add_after_borrow() -> Result<(), StamError> { let mut store = setup_example_2()?; let annotation = store.annotation("A1").unwrap(); let mut count = 0; for _data in annotation.data() { count += 1; } assert_eq!(count, 1); store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "testres", Offset::simple(6, 11), )) .with_data("tokenset", "word", DataValue::Null), )?; Ok(()) } #[test] fn store_annotate_add_during_borrowproblem() -> Result<(), StamError> { let mut store = setup_example_2()?; let annotation: &Annotation = store.get("A1")?; //this is an antipattern! // V---- here we clone the annotation to prevent a borrow problem (cannot borrow `store` as mutable because it is also borrowed as immutable (annotation)), this is relatively low-cost for (dataset, data) in annotation.clone().data() { store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "testres", Offset::simple(6, 11), )) .with_existing_data(dataset, data), )?; } Ok(()) } #[test] fn parse_json_annotation() -> Result<(), std::io::Error> { let data = r#"{ "@type": "Annotation", "@id": "A2", "target": { "@type": "TextSelector", "resource": "testres", "offset": { "begin": { "@type": "BeginAlignedCursor", "value": 0 }, "end": { "@type": "BeginAlignedCursor", "value": 5 } } }, "data": [{ "@type": "AnnotationData", "key": "pos", "set": "testdataset", "value": { "@type": "String", "value": "interjection" } }] }"#; let builder: AnnotationBuilder = serde_json::from_str(data)?; let mut store = setup_example_2().unwrap(); let annotationhandle = store.annotate(builder).unwrap(); let annotation = store.annotation(annotationhandle).unwrap(); assert_eq!(annotation.id(), Some("A2")); let mut count = 0; for data in annotation.data() { //there should be only one so we can safely test in the loop body count += 1; assert_eq!(data.key().id(), Some("pos")); assert_eq!(data.key().as_str(), "pos"); //shortcut for the same as above assert_eq!(data.value(), &DataValue::String("interjection".to_string())); assert_eq!(data.value(), "interjection"); //shortcut for the same as above (and more efficient without heap allocated string) assert_eq!(data.set().id(), Some("testdataset")); } assert_eq!(count, 1); Ok(()) } #[test] fn parse_json_dataset() -> Result<(), StamError> { let json = r#"{ "@type": "AnnotationDataSet", "@id": "https://purl.org/dc", "keys": [ { "@type": "DataKey", "@id": "http://purl.org/dc/terms/creator" }, { "@type": "DataKey", "@id": "http://purl.org/dc/terms/created" }, { "@type": "DataKey", "@id": "http://purl.org/dc/terms/generator" } ], "data": [ { "@type": "AnnotationData", "@id": "D1", "key": "http://purl.org/dc/terms/creator", "value": { "@type": "String", "value": "proycon" } } ] }"#; let annotationset = AnnotationDataSet::from_json_str(json, Config::default())?; let mut store = setup_example_2()?; let sethandle = store.insert(annotationset)?; let dataset = store.dataset(sethandle).unwrap(); assert_eq!(dataset.id(), Some("https://purl.org/dc")); let mut count = 0; let mut firstkeyhandle: Option = None; for key in dataset.keys() { count += 1; if count == 1 { assert_eq!(key.id(), Some("http://purl.org/dc/terms/creator")); firstkeyhandle = Some(key.handle()); } } assert_eq!(count, 3); count = 0; for data in dataset.data() { //there should be only one so we can safely test in the loop body count += 1; assert_eq!(data.id(), Some("D1")); assert_eq!(data.as_ref().key(), firstkeyhandle.unwrap()); assert_eq!(data.value(), "proycon"); } assert_eq!(count, 1); Ok(()) } const EXAMPLE_3: &'static str = r#"{ "@type": "AnnotationStore", "annotationsets": [{ "@type": "AnnotationDataSet", "@id": "testdataset", "keys": [ { "@type": "DataKey", "@id": "pos" } ], "data": [ { "@type": "AnnotationData", "@id": "D1", "key": "pos", "value": { "@type": "String", "value": "noun" } } ] }], "resources": [{ "@id": "testres", "text": "Hello world" }], "annotations": [{ "@type": "Annotation", "@id": "A1", "target": { "@type": "TextSelector", "resource": "testres", "offset": { "begin": { "@type": "BeginAlignedCursor", "value": 6 }, "end": { "@type": "BeginAlignedCursor", "value": 11 } } }, "data": [{ "@type": "AnnotationData", "@id": "D1", "set": "testdataset" }] }] }"#; const EXAMPLE_3_TEMP_ID: &'static str = r#"{ "@type": "AnnotationStore", "annotationsets": [{ "@type": "AnnotationDataSet", "@id": "testdataset", "keys": [ { "@type": "DataKey", "@id": "pos" } ], "data": [ { "@type": "AnnotationData", "@id": "!D0", "key": "pos", "value": { "@type": "String", "value": "noun" } } ] }], "resources": [{ "@id": "testres", "text": "Hello world" }], "annotations": [{ "@type": "Annotation", "@id": "!A0", "target": { "@type": "TextSelector", "resource": "testres", "offset": { "begin": { "@type": "BeginAlignedCursor", "value": 6 }, "end": { "@type": "BeginAlignedCursor", "value": 11 } } }, "data": [{ "@type": "AnnotationData", "@id": "!D0", "set": "testdataset" }] }] }"#; fn example_3_common_tests(store: &AnnotationStore) -> Result<(), StamError> { //repeat some common tests let _resource = store.resource("testres").or_fail()?; let annotationset = store.dataset("testdataset").or_fail()?; let _datakey = annotationset.key("pos").or_fail()?; let _annotationdata = annotationset.annotationdata("D1").or_fail()?; let _annotation = store.annotation("A1").or_fail()?; for key in annotationset.keys() { //there is only one so we can test in loop body assert_eq!(key.id(), Some("pos")); } for data in annotationset.data() { //there is only one so we can test in loop body assert_eq!(data.id(), Some("D1")); } Ok(()) } #[test] fn parse_json_annotationstore() -> Result<(), StamError> { let store = AnnotationStore::from_json_str(EXAMPLE_3, Config::default())?; example_3_common_tests(&store)?; Ok(()) } #[test] fn parse_json_annotationstore_temp_id() -> Result<(), StamError> { let store = AnnotationStore::from_json_str(EXAMPLE_3_TEMP_ID, Config::default())?; //repeat some common tests let _resource: &TextResource = store.get("testres")?; let annotationset: &AnnotationDataSet = store.get("testdataset")?; let _datakey: &DataKey = annotationset.get("pos")?; let _annotationdata: &AnnotationData = annotationset.get("!D0")?; let annotation: &Annotation = store.get("!A0")?; assert_eq!(annotation.id(), None); //we have no public ID for key in annotationset.keys() { //there is only one so we can test in loop body assert_eq!(key.id(), Some("pos")); } for data in annotationset.data() { //there is only one so we can test in loop body assert_eq!(data.id(), None); //we have no public ID } Ok(()) } #[test] fn parse_json_annotationstore_from_file() -> Result<(), StamError> { //write the test file let mut f = File::create("/tmp/test.stam.json").expect("opening test file for writing"); write!(f, "{}", EXAMPLE_3).expect("writing test file"); //read the test file let store = AnnotationStore::from_file("/tmp/test.stam.json", Config::default())?; example_3_common_tests(&store)?; Ok(()) } #[test] fn loop_annotations() -> Result<(), StamError> { let store = setup_example_2()?; for annotation in store.annotations() { let id = annotation.id().unwrap_or(""); for data in annotation.data() { // get the text to which this annotation refers (if any) let text: &str = annotation.text().next().unwrap(); print!( "{}\t{}\t{}\t{}", id, data.key().id().unwrap(), data.value(), text ); } } Ok(()) } #[test] fn annotation_textselections() -> Result<(), StamError> { let store = setup_example_3()?; let sentence = store.annotation("sentence2").unwrap(); for textselection in sentence.textselections() { assert_eq!(textselection.text(), "I am only passionately curious.") } Ok(()) } #[test] fn annotation_textselections_relative() -> Result<(), StamError> { let store = setup_example_3()?; //this annotation uses relative position let word = store.annotation("sentence2word2").unwrap(); for textselection in word.textselections() { assert_eq!(textselection.text(), "am") } Ok(()) } #[test] fn annotation_textselections_relative_endaligned() -> Result<(), StamError> { let store = setup_example_3()?.with_annotation( AnnotationBuilder::new() .with_id("sentence2lastword") .with_target(SelectorBuilder::annotationselector( "sentence2", Some(Offset::new(Cursor::EndAligned(-8), Cursor::EndAligned(-1))), )) .with_data("testdataset", "type", "word"), )?; let word = store.annotation("sentence2lastword").unwrap(); for textselection in word.textselections() { assert_eq!(textselection.text(), "curious") } Ok(()) } #[test] fn resource_textselection_existing() -> Result<(), StamError> { let store = setup_example_2()?; let resource = store.resource("testres").or_fail()?; let textselection = resource.textselection(&Offset::simple(6, 11))?; assert_eq!(textselection.begin(), 6); assert_eq!(textselection.end(), 11); assert!( textselection.handle().is_some(), "testing whether TextSelection has a handle" ); Ok(()) } #[test] fn annotations_by_offset() -> Result<(), StamError> { let store = setup_example_2()?; let resource = store.resource("testres").or_fail()?; let textselection = resource.textselection(&Offset::simple(6, 11))?; let v: Vec<_> = textselection.annotations().collect(); let a_ref = store.annotation("A1").unwrap(); assert_eq!(v, vec!(a_ref)); Ok(()) } #[test] fn textselections_by_annotation() -> Result<(), StamError> { let store = setup_example_2()?; let annotation = store.annotation("A1").unwrap(); let _reference_res_handle = store.resolve_resource_id("testres")?; let mut count = 0; for textselection in annotation.textselections() { count += 1; assert_eq!(textselection.begin(), 6); assert_eq!(textselection.end(), 11); } assert_eq!(count, 1); Ok(()) } #[test] fn textselections_by_resource_unsorted() -> Result<(), StamError> { let store = setup_example_4()?; let resource = store.resource("testres").or_fail()?; let v: Vec<_> = resource.as_ref().textselections_unsorted().collect(); //lower-level method assert_eq!(v[0].begin(), 6); assert_eq!(v[0].end(), 11); assert_eq!(v[1].begin(), 0); assert_eq!(v[1].end(), 5); assert_eq!(v.len(), 2); Ok(()) } #[test] fn textselections_by_resource_sorted() -> Result<(), StamError> { let store = setup_example_4()?; let resource = store.resource("testres").or_fail()?; let v: Vec<_> = resource.textselections().collect(); assert_eq!(v.len(), 2); assert_eq!(v[0].begin(), 0); assert_eq!(v[0].end(), 5); Ok(()) } #[test] fn text_by_annotation() -> Result<(), StamError> { let store = setup_example_2()?; let annotation = store.annotation("A1").or_fail()?; let mut count = 0; for text in annotation.text() { count += 1; assert_eq!(text, "world"); } assert_eq!(count, 1); Ok(()) } #[test] fn data_by_value() -> Result<(), StamError> { let store = setup_example_2()?; let dataset = store.dataset("testdataset").or_fail()?; let key = dataset.key("pos").unwrap(); let annotationdata = key .data() .filter_value(DataOperator::Equals("noun".into())) .next(); assert!(annotationdata.is_some()); assert_eq!(annotationdata.unwrap().id(), Some("D1")); Ok(()) } #[test] fn find_data_exact() -> Result<(), StamError> { let store = setup_example_2()?; let dataset = store.dataset("testdataset").or_fail()?; let mut count = 0; for annotationdata in dataset.find_data("pos", DataOperator::Equals("noun".into())) { count += 1; assert_eq!(annotationdata.id(), Some("D1")); } assert_eq!(count, 1); Ok(()) } #[test] fn find_data_by_key() -> Result<(), StamError> { let store = setup_example_3()?; let dataset = store.dataset("testdataset").or_fail()?; let count = dataset.find_data("type", DataOperator::Any).count(); assert_eq!(count, 2); Ok(()) } #[test] fn data_by_key() -> Result<(), StamError> { let store = setup_example_3()?; let dataset = store.dataset("testdataset").or_fail()?; let key = dataset.key("type").unwrap(); let count = key.data().count(); assert_eq!(count, 2); Ok(()) } #[test] fn find_data_all() -> Result<(), StamError> { let store = setup_example_3()?; let annotationset = store.dataset("testdataset").or_fail()?; let count = annotationset.find_data(false, DataOperator::Any).count(); assert_eq!(count, 2); Ok(()) } #[test] fn find_data_all_2() -> Result<(), StamError> { let store = setup_example_3()?; let annotationset = store.dataset("testdataset").or_fail()?; let mut count = 0; for _ in annotationset.data() { count += 1; } assert_eq!(count, 2); Ok(()) } #[test] fn multiselector_creation_notranged() -> Result<(), StamError> { let _store = setup_example_multiselector_notranged()?; Ok(()) } #[test] fn multiselector_creation_ranged() -> Result<(), StamError> { let _store = setup_example_multiselector_ranged()?; Ok(()) } #[test] fn multiselector_iter_notranged() -> Result<(), StamError> { let store = setup_example_multiselector_notranged()?; let annotation = store.annotation("WordAnnotation").unwrap(); let result: Vec<&str> = annotation.text().collect(); assert_eq!(result.len(), 2); assert_eq!(result[0], "Hello"); assert_eq!(result[1], "world"); Ok(()) } #[test] fn multiselector_iter_ranged() -> Result<(), StamError> { let store = setup_example_multiselector_ranged()?; let annotation = store.annotation("WordAnnotation").or_fail()?; let result: Vec<&str> = annotation.text().collect(); assert_eq!(result.len(), 2); assert_eq!(result[0], "Hello"); assert_eq!(result[1], "world"); Ok(()) } #[test] fn multiselector2_iter() -> Result<(), StamError> { let store = setup_example_multiselector_2()?; let annotation = store.annotation("WordAnnotation").or_fail()?; let result: Vec<&str> = annotation.text().collect(); assert_eq!(result.len(), 2); assert_eq!(result[0], "Hello"); assert_eq!(result[1], "world"); Ok(()) } #[test] fn read_single() -> Result<(), StamError> { AnnotationStore::from_file( &format!("{}/tests/singletest.store.stam.json", CARGO_MANIFEST_DIR), Config::default(), )?; Ok(()) } #[cfg(not(target_os = "windows"))] #[test] fn write_single() -> Result<(), StamError> { let mut store = AnnotationStore::from_file( &format!("{}/tests/singletest.store.stam.json", CARGO_MANIFEST_DIR), Config::default(), )?; store.set_filename("/tmp/singletest.store.stam.json"); store.save()?; let output = Command::new("diff") .arg("-w") .arg(format!( "{}/tests/singletest.store.stam.json", CARGO_MANIFEST_DIR )) .arg("/tmp/singletest.store.stam.json") .output() .expect("failed to run diff"); assert!(output.status.success()); Ok(()) } #[test] fn test_read_include() -> Result<(), StamError> { let store = AnnotationStore::from_file( "tests/test.store.stam.json", Config::default().with_debug(true), )?; test_example_a_sanity(&store)?; Ok(()) } #[cfg(not(target_os = "windows"))] #[test] fn test_write_include() -> Result<(), StamError> { let mut store = AnnotationStore::from_file( &format!("{}/tests/test.store.stam.json", CARGO_MANIFEST_DIR), Config::default().with_debug(true), )?; store.set_filename("/tmp/test.store.stam.json"); store.save()?; let output = Command::new("diff") .arg("-w") .arg(format!("{}/tests/test.store.stam.json", CARGO_MANIFEST_DIR)) .arg("/tmp/test.store.stam.json") .output() .expect("failed to run diff"); assert!(output.status.success()); Ok(()) } #[test] fn test_read_include_annotationstore() -> Result<(), StamError> { let store = AnnotationStore::from_file( "tests/includetest.store.stam.json", Config::default().with_debug(true), )?; test_example_a_sanity(&store)?; //annotation in main store let annotation = store.annotation("A3").or_fail()?; assert_eq!(annotation.text().next().unwrap(), "Hallå"); for data in annotation.data() { assert_eq!(data.key().id(), Some("pos")); assert_eq!(data.id(), Some("PosInterjection")); assert_eq!(data.set().id(), Some("https://example.org/test/")); assert_eq!(data.value(), "interjection"); } let mut count = 0; for substore in store.substores() { count += 1; assert_eq!(substore.id(), Some("Example A")); assert_eq!( substore.annotations().count(), 2, "Two annotations in substore" ) } assert_eq!(count, 1); Ok(()) } #[cfg(not(target_os = "windows"))] #[test] fn test_write_include_annotationstore() -> Result<(), StamError> { let mut store = AnnotationStore::from_file( "tests/includetest.store.stam.json", Config::default().with_debug(true), )?; store.set_filename("/tmp/includetest.store.stam.json"); store.save()?; let output = Command::new("diff") .arg("-w") .arg(format!( "{}/tests/includetest.store.stam.json", CARGO_MANIFEST_DIR )) .arg("/tmp/includetest.store.stam.json") .output() .expect("failed to run diff"); assert!(output.status.success()); Ok(()) } #[test] fn find_text() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello world"), )?; let resource = store.resource("testres").unwrap(); let mut count = 0; for result in resource.find_text("world") { count += 1; assert_eq!(result.begin(), 6); assert_eq!(result.end(), 11); assert_eq!(result.text(), "world"); } assert_eq!(count, 1); Ok(()) } #[test] fn find_text_nocase() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello world"), )?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_nocase("hello") { count += 1; assert_eq!(result.begin(), 0); assert_eq!(result.end(), 5); assert_eq!(result.text(), "Hello"); } assert_eq!(count, 1); Ok(()) } #[test] fn find_text_sequence() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello world"), )?; let resource = store.resource("testres").or_fail()?; let results = resource .find_text_sequence(&["Hello", "world"], |c| !c.is_alphabetic(), true) .expect("results must be found"); assert_eq!(results.len(), 2); assert_eq!(results[0].begin(), 0); assert_eq!(results[0].end(), 5); assert_eq!(results[0].text(), "Hello"); assert_eq!(results[1].begin(), 6); assert_eq!(results[1].end(), 11); assert_eq!(results[1].text(), "world"); Ok(()) } #[test] fn find_text_sequence2() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello, world"), )?; let resource = store.resource("testres").or_fail()?; let results = resource .find_text_sequence(&["Hello", "world"], |c| !c.is_alphabetic(), true) .expect("results must be found"); assert_eq!(results.len(), 2); assert_eq!(results[0].begin(), 0); assert_eq!(results[0].end(), 5); assert_eq!(results[0].text(), "Hello"); assert_eq!(results[1].begin(), 7); assert_eq!(results[1].end(), 12); assert_eq!(results[1].text(), "world"); Ok(()) } #[test] fn find_text_sequence_nocase() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello world"), )?; let resource = store.resource("testres").or_fail()?; let results = resource .find_text_sequence(&["hello", "world"], |c| !c.is_alphabetic(), false) .expect("results must be found"); assert_eq!(results.len(), 2); assert_eq!(results[0].begin(), 0); assert_eq!(results[0].end(), 5); assert_eq!(results[0].text(), "Hello"); assert_eq!(results[1].begin(), 6); assert_eq!(results[1].end(), 11); assert_eq!(results[1].text(), "world"); Ok(()) } #[test] fn test_find_text_sequence_nomatch() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello world"), )?; let resource = store.resource("testres").or_fail()?; let results = resource.find_text_sequence(&["hello", "world", "hi"], |c| !c.is_alphabetic(), false); assert!(results.is_none()); Ok(()) } #[test] fn test_split_text() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource( TextResourceBuilder::new() .with_id("testres") .with_text("Hello world"), )?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.split_text(" ") { count += 1; if count == 1 { assert_eq!(result.begin(), 0); assert_eq!(result.end(), 5); assert_eq!(result.text(), "Hello"); } else { assert_eq!(result.begin(), 6); assert_eq!(result.end(), 11); assert_eq!(result.text(), "world"); } } assert_eq!(count, 2); Ok(()) } #[test] fn test_search_text_regex_single() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource(TextResourceBuilder::new().with_id("testres").with_text( "I categorically deny any eavesdropping on you and hearing about your triskaidekaphobia.", ))?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_regex(&[Regex::new(r"eavesdropping").unwrap()], None, true)? { count += 1; assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 25); assert_eq!(result.textselections()[0].end(), 38); assert_eq!(result.as_str(), Some("eavesdropping")); } assert_eq!(count, 1); Ok(()) } #[test] fn test_search_text_regex_single2() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource(TextResourceBuilder::new().with_id("testres").with_text( "I categorically deny any eavesdropping on you and hearing about your triskaidekaphobia.", ))?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_regex(&[Regex::new(r"\b\w{13}\b").unwrap()], None, true)? { count += 1; if count == 1 { assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 2); assert_eq!(result.textselections()[0].end(), 15); assert_eq!(result.as_str(), Some("categorically")); } else { assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 25); assert_eq!(result.textselections()[0].end(), 38); assert_eq!(result.as_str(), Some("eavesdropping")); } } assert_eq!(count, 2); Ok(()) } #[test] fn test_search_text_regex_single_multiexpr() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource(TextResourceBuilder::new().with_id("testres").with_text( "I categorically deny any eavesdropping on you and hearing about your triskaidekaphobia.", ))?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_regex( &[ Regex::new(r"eavesdropping").unwrap(), Regex::new(r"\.").unwrap(), ], None, true, )? { count += 1; if count == 1 { assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 25); assert_eq!(result.textselections()[0].end(), 38); assert_eq!(result.as_str(), Some("eavesdropping")); } else { assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 86); assert_eq!(result.textselections()[0].end(), 87); assert_eq!(result.as_str(), Some(".")); } } assert_eq!(count, 2); Ok(()) } #[test] fn test_search_text_regex_single_multiexpr2() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource(TextResourceBuilder::new().with_id("testres").with_text( "I categorically deny any eavesdropping on you and hearing about your triskaidekaphobia.", ))?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_regex( &[ Regex::new(r"triskaidekaphobia").unwrap(), Regex::new(r"\.").unwrap(), ], None, true, )? { count += 1; if count == 1 { assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 69); assert_eq!(result.textselections()[0].end(), 86); assert_eq!(result.as_str(), Some("triskaidekaphobia")); } else { assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 86); assert_eq!(result.textselections()[0].end(), 87); assert_eq!(result.as_str(), Some(".")); } } assert_eq!(count, 2); Ok(()) } #[test] fn test_search_text_regex_single_capture() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource(TextResourceBuilder::new().with_id("testres").with_text( "I categorically deny any eavesdropping on you and hearing about your triskaidekaphobia.", ))?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_regex( &[Regex::new(r"deny\s(\w+)\seavesdropping").unwrap()], None, true, )? { count += 1; assert_eq!(result.textselections().len(), 1); assert_eq!(result.textselections()[0].begin(), 21); assert_eq!(result.textselections()[0].end(), 24); assert_eq!(result.as_str(), Some("any")); } assert_eq!(count, 1); Ok(()) } #[test] fn test_search_text_regex_double_capture() -> Result<(), StamError> { let mut store = AnnotationStore::default(); store.add_resource(TextResourceBuilder::new().with_id("testres").with_text( "I categorically deny any eavesdropping on you and hearing about your triskaidekaphobia.", ))?; let resource = store.resource("testres").or_fail()?; let mut count = 0; for result in resource.find_text_regex( &[Regex::new(r"deny\s(\w+)\seavesdropping\s(on\s\w+)\b").unwrap()], None, true, )? { count += 1; assert_eq!(result.textselections().len(), 2); assert_eq!(result.textselections()[0].begin(), 21); assert_eq!(result.textselections()[0].end(), 24); assert_eq!(result.textselections()[1].begin(), 39); assert_eq!(result.textselections()[1].end(), 45); assert_eq!(result.text(), vec!("any", "on you")); } assert_eq!(count, 1); Ok(()) } fn test_example_a_sanity(store: &AnnotationStore) -> Result<(), StamError> { // Instantiate the store let resource: &TextResource = store.get("hello.txt")?; assert_eq!(resource.text(), "Hallå världen\n"); let annotation = store.annotation("A1").or_fail()?; assert_eq!(annotation.text().next().unwrap(), "Hallå"); for data in annotation.data() { assert_eq!(data.key().id(), Some("pos")); assert_eq!(data.id(), Some("PosInterjection")); assert_eq!(data.set().id(), Some("https://example.org/test/")); assert_eq!(data.value(), "interjection"); } let annotation = store.annotation("A2").or_fail()?; assert_eq!(annotation.text().next().unwrap(), "världen"); for data in annotation.data() { assert_eq!(data.key().id(), Some("pos")); assert_eq!(data.id(), Some("PosNoun")); assert_eq!(data.set().id(), Some("https://example.org/test/")); assert_eq!(data.value(), "noun"); } Ok(()) } #[test] fn serialize_csv() -> Result<(), StamError> { let mut store = AnnotationStore::from_file( "tests/test.store.stam.json", Config::default().with_debug(true), )?; store.set_filename("/tmp/test.store.stam.csv"); store.save()?; //and parse again: eprintln!("\n\nTesting deserialisation of serialised result:"); let store = AnnotationStore::from_file( "/tmp/test.store.stam.csv", Config::default().with_debug(true), )?; test_example_a_sanity(&store)?; Ok(()) } #[test] fn parse_csv() -> Result<(), StamError> { let store = AnnotationStore::from_file( "tests/test.store.stam.csv", Config::default().with_debug(true), )?; test_example_a_sanity(&store)?; Ok(()) } #[test] fn parse_old_csv() -> Result<(), StamError> { let store = AnnotationStore::from_file( "tests/oldtest.store.stam.csv", //this lacks two optional columns that were added later Config::default().with_debug(true), )?; test_example_a_sanity(&store)?; Ok(()) } #[test] fn annotate_regex_single2() -> Result<(), StamError> { let mut store = setup_example_5()?; annotate_regex_for_example_6(&mut store)?; let data: Vec<_> = store .find_data("myset", "type", DataOperator::Equals("header".into())) .annotations() .collect(); assert_eq!(data.len(), 4); Ok(()) } #[test] fn annotations_by_textselection() -> Result<(), StamError> { let mut store = setup_example_5()?; annotate_regex_for_example_6(&mut store)?; let resource = store.resource("humanrights").unwrap(); let mut count = 0; let mut count_annotations = 0; for textselection in resource.textselections() { count += 1; for annotation in textselection.annotations() { count_annotations += 1; eprintln!("{} {:?}", textselection.text(), annotation.id()); } } assert_eq!(count, resource.textselections_len()); assert_ne!(count, 0); assert_eq!(count_annotations, store.annotations_len()); assert_ne!(count_annotations, 0); Ok(()) } #[test] fn annotations_by_textselection_2() -> Result<(), StamError> { let mut store = setup_example_5()?; annotate_regex_for_example_6(&mut store)?; let resource = store.resource("humanrights").unwrap(); let count_annotations = resource.textselections().annotations().count(); assert_eq!(count_annotations, store.annotations_len()); assert_ne!(count_annotations, 0); Ok(()) } #[test] fn test_example_6_full() -> Result<(), StamError> { let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; annotate_words(&mut store, "humanrights")?; let resource = store.resource("humanrights").or_fail()?; assert_eq!( resource.text(), "All human beings are born free and equal in dignity and rights." ); let sentence = store.annotation("Sentence1").or_fail()?; assert_eq!( sentence.text_simple(), Some("All human beings are born free and equal in dignity and rights.") ); let phrase1 = store.annotation("Phrase1").or_fail()?; assert_eq!(phrase1.text_simple(), Some("are born free and equal")); let phrase2 = store.annotation("Phrase2").or_fail()?; assert_eq!(phrase2.text_simple(), Some("human beings are born")); let phrase3 = store.annotation("Phrase3").or_fail()?; assert_eq!(phrase3.text_simple(), Some("dignity and rights")); Ok(()) } #[test] fn test_example_6b() -> Result<(), StamError> { let store = setup_example_6b()?; let resource = store.resource("humanrights").or_fail()?; assert_eq!( resource.text(), "All human beings are born free and equal in dignity and rights." ); let sentence = store.annotation("Sentence1").or_fail()?; assert_eq!( sentence.text_simple(), Some("All human beings are born free and equal in dignity and rights.") ); let phrase1 = store.annotation("Phrase1").or_fail()?; assert_eq!(phrase1.text_simple(), Some("are born free and equal")); let phrase2 = store.annotation("Phrase2").or_fail()?; assert_eq!(phrase2.text_simple(), Some("human beings are born")); let phrase3 = store.annotation("Phrase3").or_fail()?; assert_eq!(phrase3.text_simple(), Some("dignity and rights")); Ok(()) } #[test] fn test_example_6c() -> Result<(), StamError> { let store = setup_example_6c()?; let resource = store.resource("humanrights").or_fail()?; assert_eq!( resource.text(), "All human beings are born free and equal in dignity and rights." ); let sentence = store.annotation("Sentence1").or_fail()?; assert_eq!( sentence.text_join(" "), "All human beings are born free and equal in dignity and rights ." // <-- note the tokenisation! ); let phrase1 = store.annotation("Phrase1").or_fail()?; assert_eq!(phrase1.text_join(" "), "are born free and equal"); let phrase2 = store.annotation("Phrase2").or_fail()?; assert_eq!(phrase2.text_join(" "), "human beings are born"); let phrase3 = store.annotation("Phrase3").or_fail()?; assert_eq!(phrase3.text_join(" "), "dignity and rights"); Ok(()) } #[test] fn annotations_by_textselection_none() -> Result<(), StamError> { let store = setup_example_6()?; let resource = store.resource("humanrights").or_fail()?; let textselection = resource.textselection(&Offset::simple(1, 14))?; //no annotations for this random selection let v: Vec<_> = textselection.annotations().collect(); assert!(v.is_empty()); Ok(()) } #[test] fn related_text_embedded() -> Result<(), StamError> { let store = setup_example_6()?; let phrase1 = store.annotation("Phrase1").or_fail()?; let mut count = 0; for reftextsel in phrase1.textselections() { for textsel in reftextsel.related_text(TextSelectionOperator::embedded()) { count += 1; assert_eq!(textsel.begin(), 0); assert_eq!(textsel.end(), 63); } } assert_eq!(count, 1); Ok(()) } #[test] fn textselections_in_range_exact() -> Result<(), StamError> { let store = setup_example_6()?; let resource = store.resource("humanrights").or_fail()?; let mut count = 0; for textsel in resource.textselections_in_range(17, 40) { count += 1; assert_eq!(textsel.begin(), 17); assert_eq!(textsel.end(), 40); } assert_eq!(count, 1); Ok(()) } #[test] fn textselections_in_range_bigger() -> Result<(), StamError> { let store = setup_example_6()?; let resource = store.resource("humanrights").or_fail()?; let mut count = 0; for textsel in resource.textselections_in_range(1, 50) { count += 1; assert_eq!(textsel.begin(), 17); assert_eq!(textsel.end(), 40); } assert_eq!(count, 1); count = 0; for _textsel in resource.textselections_in_range(0, 64) { count += 1; } assert_eq!(count, 2); Ok(()) } #[test] fn related_text_embeds() -> Result<(), StamError> { let store = setup_example_6()?; let sentence = store.annotation("Sentence1").or_fail()?; let mut count = 0; for reftextsel in sentence.textselections() { for textsel in reftextsel.related_text(TextSelectionOperator::embeds()) { count += 1; assert_eq!(textsel.begin(), 17); assert_eq!(textsel.end(), 40); } } assert_eq!(count, 1); Ok(()) } #[test] fn related_text_embeds_shortcut() -> Result<(), StamError> { let store = setup_example_6()?; let sentence = store.annotation("Sentence1").or_fail()?; let mut count = 0; for textsel in sentence.related_text(TextSelectionOperator::embeds()) { count += 1; assert_eq!(textsel.begin(), 17); assert_eq!(textsel.end(), 40); } assert_eq!(count, 1); Ok(()) } #[test] fn annotations_by_related_text_embeds() -> Result<(), StamError> { let store = setup_example_6()?; let sentence = store.annotation("Sentence1").or_fail()?; let mut count = 0; for annotation in sentence .related_text(TextSelectionOperator::embeds()) .annotations() { count += 1; assert_eq!(annotation.id(), Some("Phrase1")); } assert_eq!(count, 1); Ok(()) } #[test] fn annotations_by_related_text_embeds_2() -> Result<(), StamError> { let store = setup_example_6()?; let sentence = store.annotation("Sentence1").or_fail()?; let mut count = 0; for annotation in sentence .textselections() .related_text(TextSelectionOperator::embeds()) .annotations() { count += 1; assert_eq!(annotation.id(), Some("Phrase1")); } assert_eq!(count, 1); Ok(()) } #[test] fn annotations_by_related_text_overlaps() -> Result<(), StamError> { let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; let phrase = store.annotation("Phrase1").or_fail()?; let annotations: Vec<_> = phrase .related_text(TextSelectionOperator::overlaps()) .annotations() .collect(); assert_eq!(annotations.len(), 2); assert!(annotations .iter() .any(|annotation| annotation.id().unwrap() == "Phrase2")); assert!(annotations .iter() .any(|annotation| annotation.id().unwrap() == "Sentence1")); Ok(()) } #[test] fn annotations_by_related_text_before() -> Result<(), StamError> { let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; let phrase = store.annotation("Phrase2").or_fail()?; let annotations: Vec<_> = phrase .related_text(TextSelectionOperator::before()) .annotations() .collect(); assert_eq!(annotations.len(), 1); assert!(annotations .iter() .any(|annotation| annotation.id().unwrap() == "Phrase3")); Ok(()) } #[test] fn annotations_by_related_text_after() -> Result<(), StamError> { let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; let phrase = store.annotation("Phrase3").or_fail()?; let annotations: Vec<_> = phrase .related_text(TextSelectionOperator::after()) .annotations() .collect(); assert_eq!(annotations.len(), 2); assert!(annotations .iter() .any(|annotation| annotation.id().unwrap() == "Phrase1")); assert!(annotations .iter() .any(|annotation| annotation.id().unwrap() == "Phrase2")); Ok(()) } #[test] fn related_text_with_data() -> Result<(), StamError> { // Given annotation type=phrase, get the text of all annotations type=word embedded in that phrase let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; annotate_words(&mut store, "humanrights")?; //simple tokeniser in tests/common let phrase = store.annotation("Phrase3").or_fail()?; // "dignity and rights" let key_type = store.key("myset", "type").or_fail()?; let text: Vec<_> = phrase .related_text(TextSelectionOperator::embeds()) .annotations() .filter_key_value(&key_type, DataOperator::Equals("word".into())) .text() .collect(); /* let text: Vec<_> = phrase .related_text_with_data( TextSelectionOperator::embeds(), "myset", "type", &DataOperator::Equals("word"), ) .into_iter() //work away the Option<> .flatten() // --^ .map(|(text, _)| text.text()) //grab only the textual content .collect(); */ assert_eq!(text, &["dignity", "and", "rights"]); Ok(()) } #[test] fn related_text_with_data_2() -> Result<(), StamError> { // Given annotation type=phrase, get the text of all annotations type=word that come before in that phrase let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; annotate_words(&mut store, "humanrights")?; //simple tokeniser in tests/common let phrase = store.annotation("Phrase3").or_fail()?; // "dignity and rights" let key_type = store.key("myset", "type").or_fail()?; let text: Vec<_> = phrase .related_text(TextSelectionOperator::after()) //phrase AFTER result, so result before phrase .annotations() .filter_key_value(&key_type, DataOperator::Equals("word".into())) .text() .collect(); /* let text: Vec<_> = phrase .related_text_with_data( TextSelectionOperator::after(), //phrase AFTER result, so result before phrase "myset", "type", &DataOperator::Equals("word"), ) .into_iter() //work away the Option<> .flatten() // --^ .map(|(text, _)| text.text()) //grab only the textual content .collect(); */ assert_eq!( text, &["All", "human", "beings", "are", "born", "free", "and", "equal", "in"] ); Ok(()) } #[test] fn related_text_with_data_3() -> Result<(), StamError> { // Given annotation type=phrase, get the text of all annotations type=word that come before in that phrase let store = setup_example_6b()?; //<--- used different example! rest of code is the same let phrase = store.annotation("Phrase3").or_fail()?; // "dignity and rights" let key_type = store.key("myset", "type").or_fail()?; let text: Vec<_> = phrase .related_text(TextSelectionOperator::after()) //phrase AFTER result, so result before phrase .annotations() .filter_key_value(&key_type, DataOperator::Equals("word".into())) .text() .collect(); assert_eq!( text, &["All", "human", "beings", "are", "born", "free", "and", "equal", "in"] ); Ok(()) } #[test] fn related_text_with_data_4() -> Result<(), StamError> { // Given annotation type=phrase, get the text of all annotations type=word that come before in that phrase let store = setup_example_6c()?; //<--- used different example! rest of code is the same let phrase = store.annotation("Phrase3").or_fail()?; // "dignity and rights" let key_type = store.key("myset", "type").or_fail()?; let text: Vec<_> = phrase .related_text(TextSelectionOperator::after()) //phrase AFTER result, so result before phrase .annotations() .filter_key_value(&key_type, DataOperator::Equals("word".into())) .text() .collect(); assert_eq!( text, &["All", "human", "beings", "are", "born", "free", "and", "equal", "in"] ); Ok(()) } #[test] fn annotations_in_targets() -> Result<(), StamError> { //Get the 2nd word in the sentence let store = setup_example_6c()?; //<--- used different example! rest of code is the same let sentence = store.annotation("Sentence1").or_fail()?; //get the 2nd word in the sentence let secondword = sentence .annotations_in_targets(AnnotationDepth::default()) .nth(1) .unwrap(); assert_eq!(secondword.text_simple(), Some("human")); Ok(()) } #[test] fn annotations_in_targets_len() -> Result<(), StamError> { //test the number of targets let store = setup_example_6c()?; //<--- used different example! rest of code is the same let sentence = store.annotation("Sentence1").or_fail()?; let words: Vec<_> = sentence .annotations_in_targets(AnnotationDepth::default()) .collect(); assert_eq!(words.len(), 12, "number of targets returned"); //the final punctuation is not included because it's not selected via AnnotationSelector but via a TextSelector Ok(()) } #[test] fn related_text_with_data_5() -> Result<(), StamError> { //Get the 2nd word in the sentence let mut store = setup_example_6()?; annotate_phrases_for_example_6(&mut store)?; annotate_words(&mut store, "humanrights")?; //simple tokeniser in tests/common let sentence = store.annotation("Sentence1").or_fail()?; let key_type = store.key("myset", "type").or_fail()?; let words: Vec<_> = sentence .related_text(TextSelectionOperator::embeds()) .annotations() .filter_key_value(&key_type, DataOperator::Equals("word".into())) .textual_order(); //we could omit this if we were sure word annotations were added in sequence let secondword = words.iter().nth(1).unwrap(); assert_eq!(secondword.text_simple(), Some("human")); Ok(()) } #[test] fn find_data_about() -> Result<(), StamError> { let store = setup_example_6c()?; let sentence = store.annotation("Sentence1").or_fail()?; //get the 2nd word in the sentence let secondword = sentence .annotations_in_targets(AnnotationDepth::default()) .nth(1) .unwrap(); assert_eq!( secondword.text_simple(), Some("human"), "testing the second word in the sentence" ); //now find the phrase this word belongs to: let key_type = store.key("myset", "type").or_fail()?; let mut count = 0; for phrase in secondword .annotations() .filter_key_value(&key_type, DataOperator::Equals("phrase".into())) { count += 1; //we can test in body because we only have one: assert_eq!( phrase.id(), Some("Phrase2"), "testing whether we got the right phrase" ); assert_eq!(phrase.text_join(" "), "human beings are born"); } assert_eq!(count, 1); Ok(()) } #[test] fn annotations_by_related_text_matching_data() -> Result<(), StamError> { let mut store = setup_example_6()?; //note: not the same as the previous two! annotate_phrases_for_example_6(&mut store)?; annotate_words(&mut store, "humanrights")?; //simple tokeniser in tests/common let sentence = store.annotation("Sentence1").or_fail()?; let key_type = store.key("myset", "type").or_fail()?; let words: Vec<_> = sentence .related_text(TextSelectionOperator::embeds()) .annotations() .filter_key_value(&key_type, DataOperator::Equals("word".into())) .textual_order(); //we could omit this if we were sure word annotations were added in sequence let secondword = words.iter().nth(1).unwrap(); assert_eq!(secondword.text_simple(), Some("human")); //now find the phrase this word belongs to: let mut count = 0; for phrase in secondword .related_text(TextSelectionOperator::embedded()) .annotations() .filter_key_value(&key_type, DataOperator::Equals("phrase".into())) { count += 1; //we can test in body because we only have one: assert_eq!(phrase.id(), Some("Phrase2")); assert_eq!(phrase.text_simple(), Some("human beings are born")); } assert_eq!(count, 1); Ok(()) } #[test] fn annotations_by_related_text_relative_offset() -> Result<(), StamError> { let store = setup_example_6()?; let sentence1 = store .annotation("Sentence1") .or_fail()? .textselections() .next() .unwrap(); let phrase1 = store .annotation("Phrase1") .or_fail()? .textselections() .next() .unwrap(); let offset = phrase1 .relative_offset(&sentence1, OffsetMode::default()) .unwrap(); assert_eq!(offset.begin, Cursor::BeginAligned(17)); //happens to be the same as absolute offset since Sentence1 covers all.. assert_eq!(offset.end, Cursor::BeginAligned(40)); Ok(()) } #[test] fn ex7_resource_textselections_scale_unsorted_iter() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; //iterate over all textselections unsorted let mut count = 0; for _ in resource.textselections() { count += 1; } assert!(count >= n - 2); Ok(()) } #[test] fn ex7_resource_textselections_scale_sorted_iter() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; //iterate over all textselections unsorted let mut count = 0; for _ in resource.as_ref().textselections_unsorted() { count += 1; } assert!(count >= n - 2); Ok(()) } #[test] fn ex7_resource_textselection() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; resource.textselection(&Offset::simple(100, 105))?; Ok(()) } #[test] fn ex7_textselections_scale_test_overlap() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; let selection = resource.textselection(&Offset::simple(100, 105))?; //iterate over all textselections unsorted let mut count = 0; for textselection in selection.related_text(TextSelectionOperator::overlaps()) { eprintln!("Overlaps: {:?}", textselection); count += 1; } assert_eq!(count, 11); Ok(()) } #[test] fn ex7_textselections_scale_test_embeds() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; let selection = resource.textselection(&Offset::simple(100, 105))?; //iterate over all textselections unsorted let mut count = 0; for textselection in selection.related_text(TextSelectionOperator::embeds()) { eprintln!("Embeds: {:?}", textselection); count += 1; } assert_eq!(count, 9); Ok(()) } #[test] fn ex7_textselections_scale_test_embedded() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; let selection = resource.textselection(&Offset::simple(100, 101))?; //iterate over all textselections unsorted let mut count = 0; for textselection in selection.related_text(TextSelectionOperator::embedded()) { eprintln!("Embedded: {:?}", textselection); count += 1; } assert_eq!(count, 2); Ok(()) } #[test] fn ex7_textselections_scale_test_precedes() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; let selection = resource.textselection(&Offset::simple(100, 101))?; //iterate over all textselections unsorted let mut count = 0; for textselection in selection.related_text(TextSelectionOperator::precedes()) { eprintln!("Precedes: {:?}", textselection); count += 1; } assert_eq!(count, 2); Ok(()) } #[test] fn ex7_textselections_scale_test_succeeds() -> Result<(), StamError> { let n = 10000; let store = setup_example_7(n)?; let resource = store.resource("testres").or_fail()?; let selection = resource.textselection(&Offset::simple(100, 101))?; //iterate over all textselections unsorted let mut count = 0; for textselection in selection.related_text(TextSelectionOperator::succeeds()) { eprintln!("Succeeds: {:?}", textselection); count += 1; } assert_eq!(count, 2); Ok(()) } #[test] fn query_parse() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA set key = value;"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); assert_eq!(*operator, DataOperator::Equals("value".into())); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_quoted() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = \"value\";"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); assert_eq!(*operator, DataOperator::Equals("value".into())); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_numeric() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = 5;"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); assert_eq!(*operator, DataOperator::EqualsInt(5)); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_data_short() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\";"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::DataKey { set, key, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_nonquoted_disjunction() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = value|value2|value3;"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); if let DataOperator::Or(v) = operator { assert_eq!(v.len(), 3); assert_eq!(v.get(0), Some(&DataOperator::Equals("value".into()))); assert_eq!(v.get(1), Some(&DataOperator::Equals("value2".into()))); assert_eq!(v.get(2), Some(&DataOperator::Equals("value3".into()))); } else { assert!(false, "Expected OR constraint"); } } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_nonquoted_disjunction_numeric() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = 3|4|5;"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); if let DataOperator::Or(v) = operator { assert_eq!(v.len(), 3); assert_eq!(v.get(0), Some(&DataOperator::EqualsInt(3))); assert_eq!(v.get(1), Some(&DataOperator::EqualsInt(4))); assert_eq!(v.get(2), Some(&DataOperator::EqualsInt(5))); } else { assert!(false, "Expected OR constraint"); } } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_quoted_disjunction() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = \"value|value2|value3\";"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); if let DataOperator::Or(v) = operator { assert_eq!(v.len(), 3); assert_eq!(v.get(0), Some(&DataOperator::Equals("value".into()))); assert_eq!(v.get(1), Some(&DataOperator::Equals("value2".into()))); assert_eq!(v.get(2), Some(&DataOperator::Equals("value3".into()))); } else { assert!(false, "Expected OR constraint"); } } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_attributes() -> Result<(), StamError> { let querystring = "@a @b SELECT ANNOTATION ?a WHERE @blah @blieh=bloeh DATA \"set\" \"key\" = \"value\";"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; assert_eq!(query.attributes().len(), 2); assert_eq!(query.attributes().nth(0), Some("@a").as_ref()); assert_eq!(query.attributes().nth(1), Some("@b").as_ref()); for (constraint, attributes) in query.constraints_with_attributes() { count += 1; assert_eq!(attributes.len(), 2); assert_eq!(attributes.get(0), Some("@blah").as_ref()); assert_eq!(attributes.get(1), Some("@blieh=bloeh").as_ref()); if let Constraint::KeyValue { set, key, operator, qualifier: _, } = constraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); assert_eq!(*operator, DataOperator::Equals("value".into())); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse2() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE TEXT blah;"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::Text(value, _) = constraint { assert_eq!(*value, "blah"); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_subquery() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = \"value\"; { SELECT ANNOTATION WHERE RELATION ?a SUCCEEDS; }"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; let subquery = query.subqueries().next().expect("expected subquery"); for constraint in subquery.iter() { count += 1; if let Constraint::TextRelation { var, operator } = constraint { assert_eq!(*var, "a"); assert_eq!(*operator, TextSelectionOperator::succeeds()); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_optional_subquery() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = \"value\"; { SELECT OPTIONAL ANNOTATION WHERE RELATION ?a SUCCEEDS; }"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; let subquery = query.subqueries().next().expect("expected subquery"); assert_eq!(subquery.qualifier(), QueryQualifier::Optional); for constraint in subquery.iter() { count += 1; if let Constraint::TextRelation { var, operator } = constraint { assert_eq!(*var, "a"); assert_eq!(*operator, TextSelectionOperator::succeeds()); } else { assert!(false, "Constraint not as expected"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_parse_multiple_subqueries() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = \"value\"; { SELECT ANNOTATION WHERE RELATION ?a SUCCEEDS; | SELECT ANNOTATION WHERE RELATION ?a PRECEDES; }"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for subquery in query.subqueries() { count += 1; for constraint in subquery.iter() { if count == 1 { if let Constraint::TextRelation { var, operator } = constraint { assert_eq!(*var, "a"); assert_eq!(*operator, TextSelectionOperator::succeeds()); } else { assert!(false, "Constraint not as expected"); } } else if count == 2 { if let Constraint::TextRelation { var, operator } = constraint { assert_eq!(*var, "a"); assert_eq!(*operator, TextSelectionOperator::precedes()); } else { assert!(false, "Constraint not as expected"); } } } } assert_eq!(count, 2); Ok(()) } #[test] fn query_parse_names() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\" = \"value\"; { SELECT ANNOTATION ?b WHERE RELATION ?a SUCCEEDS; | SELECT ANNOTATION ?c WHERE RELATION ?a PRECEDES; }"; let query: Query = querystring.try_into()?; let names = query.names(); assert_eq!(names.get(0), Some("a").as_ref()); assert_eq!(names.get(1), Some("b").as_ref()); assert_eq!(names.get(2), Some("c").as_ref()); Ok(()) } #[test] fn query_parse_union() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE [ DATA \"set\" \"key\" = \"value\" OR DATA \"set\" \"key\" = \"value\" ];"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::Union(subconstraints) = constraint { assert_eq!(subconstraints.len(), 2, "Subconstraint count"); for subconstraint in subconstraints { if let Constraint::KeyValue { set, key, operator, qualifier: _, } = subconstraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); assert_eq!(*operator, DataOperator::Equals("value".into())); } else { assert!(false, "Subconstraint not as expected"); } } } } assert_eq!(count, 1, "Number of constraints"); Ok(()) } #[test] fn query_parse_union_unquoted() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE [ DATA set key = value OR DATA set key = value ];"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Select); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for constraint in query.iter() { count += 1; if let Constraint::Union(subconstraints) = constraint { assert_eq!(subconstraints.len(), 2, "Subconstraint count"); for subconstraint in subconstraints { if let Constraint::KeyValue { set, key, operator, qualifier: _, } = subconstraint { assert_eq!(*set, "set"); assert_eq!(*key, "key"); assert_eq!(*operator, DataOperator::Equals("value".into())); } else { assert!(false, "Subconstraint not as expected"); } } } } assert_eq!(count, 1, "Number of constraints"); Ok(()) } #[test] fn query_parse_union2() -> Result<(), StamError> { let querystring = "SELECT ANNOTATION ?a WHERE DATA \"set\" \"key\"; [ DATA \"set\" \"key\" = \"value\" OR DATA \"set\" \"key\" = \"value\" ]; RESOURCE \"x\";"; let query: Query = querystring.try_into()?; let mut count = 0; for _ in query.iter() { count += 1; } assert_eq!(count, 3, "Number of constraints"); Ok(()) } #[test] fn query_parse_add() -> Result<(), StamError> { let querystring = "ADD ANNOTATION ?a WITH DATA \"set\" \"key\" \"value\"; TARGET ?x; { SELECT ANNOTATION ?x WHERE ID \"A1\"; }"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Add); assert_eq!(query.querytype().readonly(), false); assert_eq!(query.resulttype(), Some(Type::Annotation)); let mut count = 0; for _assignment in query.assignments() { count += 1; } assert_eq!(count, 2, "Number of assignments"); Ok(()) } #[test] fn query_parse_delete() -> Result<(), StamError> { let querystring = "DELETE ANNOTATION ?a { SELECT ANNOTATION ?a WHERE ID \"A1\"; }"; let query: Query = querystring.try_into()?; assert_eq!(query.name(), Some("a")); assert_eq!(query.querytype(), QueryType::Delete); assert_eq!(query.querytype().readonly(), false); assert_eq!(query.resulttype(), Some(Type::Annotation)); Ok(()) } #[test] fn query() -> Result<(), StamError> { let store = setup_example_6()?; let query: Query = "SELECT ANNOTATION ?a WHERE DATA myset type = phrase;".try_into()?; let mut count = 0; let refdata = store .find_data("myset", "type", DataOperator::Equals("phrase".into())) .next() .expect("reference data must exist"); for results in store.query(query)? { for result in results.iter() { match result { QueryResultItem::Annotation(annotation) => { count += 1; assert!(annotation.has_data(&refdata)); } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 1); Ok(()) } #[test] fn query_by_name() -> Result<(), StamError> { let store = setup_example_6()?; let query: Query = "SELECT ANNOTATION ?a WHERE DATA myset type = phrase;".try_into()?; let mut count = 0; let refdata = store .find_data("myset", "type", DataOperator::Equals("phrase".into())) .next() .expect("reference data must exist"); let iter = store.query(query)?; for results in iter { if let Ok(result) = results.get_by_name("a") { match result { QueryResultItem::Annotation(annotation) => { count += 1; assert!(annotation.has_data(&refdata)); } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 1); Ok(()) } #[test] fn query_limit_first() -> Result<(), StamError> { let store = setup_example_6()?; let query: Query = "SELECT ANNOTATION ?a WHERE LIMIT 1;".try_into()?; let mut count = 0; let iter = store.query(query)?; for results in iter { if let Ok(result) = results.get_by_name("a") { match result { QueryResultItem::Annotation(annotation) => { count += 1; assert_eq!(annotation.id(), Some("Sentence1")); } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 1); Ok(()) } #[test] fn query_limit_last() -> Result<(), StamError> { let store = setup_example_6()?; let query: Query = "SELECT ANNOTATION ?a WHERE LIMIT -1;".try_into()?; let mut count = 0; let iter = store.query(query)?; for results in iter { if let Ok(result) = results.get_by_name("a") { match result { QueryResultItem::Annotation(annotation) => { count += 1; assert_eq!(annotation.id(), Some("Phrase1")); } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 1); Ok(()) } #[test] fn query_limit_range() -> Result<(), StamError> { let store = setup_example_6()?; let query: Query = "SELECT ANNOTATION ?a WHERE LIMIT 0 1;".try_into()?; let mut count = 0; let iter = store.query(query)?; for results in iter { if let Ok(result) = results.get_by_name("a") { match result { QueryResultItem::Annotation(annotation) => { count += 1; assert_eq!(annotation.id(), Some("Sentence1")); } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 1); Ok(()) } #[test] fn query_subquery() -> Result<(), StamError> { let store = setup_example_6()?; let query: Query = "SELECT ANNOTATION ?sentence WHERE DATA myset type = sentence; { SELECT ANNOTATION ?phrase WHERE RELATION ?sentence EMBEDS; DATA myset type = phrase; }".try_into()?; let mut count = 0; let refdata = store .find_data("myset", "type", DataOperator::Equals("phrase".into())) .next() .expect("reference data must exist"); let refdata2 = store .find_data("myset", "type", DataOperator::Equals("sentence".into())) .next() .expect("reference data must exist"); let queryresults = store.query(query)?; for results in queryresults { count += 1; if let QueryResultItem::Annotation(annotation) = results.get_by_name("phrase")? { assert!(annotation.has_data(&refdata)); } else { assert!(false, "did not get phrase"); } if let QueryResultItem::Annotation(annotation) = results.get_by_name("sentence")? { assert!(annotation.has_data(&refdata2)); } else { assert!(false, "did not get sentence"); } } assert_eq!(count, 1); Ok(()) } #[test] fn query_multiple_subqueries() -> Result<(), StamError> { let mut store = setup_example_6()?; store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(21, 25), //"born" )) .with_data("myset", "type", "word"), )?; let query: Query = "SELECT ANNOTATION ?sentence WHERE DATA myset type = sentence; { SELECT ANNOTATION ?phrase WHERE RELATION ?sentence EMBEDS; DATA myset type = phrase; | SELECT ANNOTATION ?word WHERE RELATION ?sentence EMBEDS; DATA myset type = word;}".try_into()?; let mut count = 0; let refdata = store .find_data("myset", "type", DataOperator::Equals("phrase".into())) .next() .expect("reference data must exist"); let refdata2 = store .find_data("myset", "type", DataOperator::Equals("sentence".into())) .next() .expect("reference data must exist"); let refdata3 = store .find_data("myset", "type", DataOperator::Equals("word".into())) .next() .expect("reference data must exist"); let queryresults = store.query(query)?; for results in queryresults { count += 1; if let QueryResultItem::Annotation(annotation) = results.get_by_name("sentence")? { assert!(annotation.has_data(&refdata2)); } if count == 1 { if let QueryResultItem::Annotation(annotation) = results.get_by_name("phrase")? { assert!(annotation.has_data(&refdata)); } } else if count == 2 { if let QueryResultItem::Annotation(annotation) = results.get_by_name("word")? { assert!(annotation.has_data(&refdata3)); } } } assert_eq!(count, 2); Ok(()) } #[test] fn query_multiple_optional_subqueries() -> Result<(), StamError> { let mut store = setup_example_6()?; store.annotate( AnnotationBuilder::new() .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(21, 25), //"born" )) .with_data("myset", "type", "word"), )?; let query: Query = "SELECT ANNOTATION ?sentence WHERE DATA myset type = sentence; { SELECT OPTIONAL ANNOTATION ?phrase WHERE RELATION ?sentence EMBEDS; DATA myset type = phrase; | SELECT OPTIONAL ANNOTATION ?word WHERE RELATION ?sentence EMBEDS; DATA myset type = word; | SELECT OPTIONAL ANNOTATION ?nonexistant WHERE RELATION ?sentence EMBEDS; DATA myset type = nonexistant; }".try_into()?; let mut count = 0; let refdata = store .find_data("myset", "type", DataOperator::Equals("phrase".into())) .next() .expect("reference data must exist"); let refdata2 = store .find_data("myset", "type", DataOperator::Equals("sentence".into())) .next() .expect("reference data must exist"); let refdata3 = store .find_data("myset", "type", DataOperator::Equals("word".into())) .next() .expect("reference data must exist"); let queryresults = store.query(query)?; for results in queryresults { count += 1; if let QueryResultItem::Annotation(annotation) = results.get_by_name("sentence")? { assert!(annotation.has_data(&refdata2)); } if count == 1 { if let QueryResultItem::Annotation(annotation) = results.get_by_name("phrase")? { assert!(annotation.has_data(&refdata)); } } else if count == 2 { if let QueryResultItem::Annotation(annotation) = results.get_by_name("word")? { assert!(annotation.has_data(&refdata3)); } } else if count == 3 { assert!(results.get_by_name("word").is_err()); assert!(results.get_by_name("phrase").is_err()); } } assert_eq!(count, 3); Ok(()) } #[test] fn query_subquery_deep() -> Result<(), StamError> { let store = setup_example_9()?; let querystring = r#" SELECT ANNOTATION ?det WHERE DATA testdataset pos = det; { SELECT ANNOTATION ?adj WHERE RELATION ?det PRECEDES; DATA testdataset pos = adj; { SELECT ANNOTATION ?n WHERE RELATION ?adj PRECEDES; DATA testdataset pos = n; } } "#; let query: Query = querystring.try_into()?; let iter = store.query(query)?; let mut count = 0; for results in iter { count += 1; assert!( results.get_by_name("det").is_ok(), "checking det in round {} of 2", count ); assert!( results.get_by_name("adj").is_ok(), "checking adj in round {} of 2", count ); assert!( results.get_by_name("n").is_ok(), "checking n in round {} of 2", count ); } assert_eq!(count, 2); Ok(()) } #[test] fn query_subquery_optional() -> Result<(), StamError> { let store = setup_example_9()?; let querystring = r#" SELECT ANNOTATION ?n WHERE DATA testdataset pos = n; { SELECT OPTIONAL ANNOTATION ?v WHERE RELATION ?n PRECEDES; DATA testdataset pos = v; } "#; let query: Query = querystring.try_into()?; let iter = store.query(query)?; let mut count = 0; for results in iter { count += 1; if count == 1 { assert!( results.get_by_name("n").is_ok(), "checking n in round {} of 2", count ); assert!( results.get_by_name("v").is_ok(), "checking v in round {} of 2", count ); } else if count == 2 { assert!( results.get_by_name("n").is_ok(), "checking n in round {} of 2", count ); assert!( results.get_by_name("v").is_err(), "checking no v in round {} of 2", count ); } } assert_eq!(count, 2); Ok(()) } #[test] fn query_subquery_optional_nonexistant() -> Result<(), StamError> { let store = setup_example_9()?; let querystring = r#" SELECT ANNOTATION ?n WHERE DATA testdataset pos = n; { SELECT OPTIONAL ANNOTATION ?v WHERE RELATION ?n PRECEDES; DATA testdataset pos = nonexistant; } "#; let query: Query = querystring.try_into()?; let iter = store.query(query)?; let mut count = 0; for results in iter { count += 1; assert!( results.get_by_name("n").is_ok(), "checking n in round {} of 2", count ); assert!( results.get_by_name("v").is_err(), "checking no v in round {} of 2", count ); } assert_eq!(count, 1); Ok(()) } #[test] fn query_union() -> Result<(), StamError> { let store = setup_example_6()?; //this example could have been just a DataOperator::And but we want to test the UNION construction: let query: Query = "SELECT ANNOTATION ?a WHERE [ DATA myset type = phrase OR DATA myset type = sentence ];" .try_into()?; let mut count = 0; let refdata = store .find_data("myset", "type", DataOperator::Equals("phrase".into())) .next() .expect("reference data must exist"); let refdata2 = store .find_data("myset", "type", DataOperator::Equals("sentence".into())) .next() .expect("reference data must exist"); let iter = store.query(query)?; for results in iter { if let Ok(result) = results.get_by_name("a") { match result { QueryResultItem::Annotation(annotation) => { count += 1; assert!(annotation.has_data(&refdata) || annotation.has_data(&refdata2)); } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 2); Ok(()) } #[test] fn query_add_annotation_textselector() -> Result<(), StamError> { let mut store = setup_example_1()?; let query: Query = "ADD ANNOTATION ?a WITH DATA \"testdataset\" \"type\" \"phrase\"; TARGET ?target; { SELECT TEXT ?target WHERE RESOURCE \"testres\" OFFSET 0 11; }" .try_into()?; let iter = store.query_mut(query)?; let mut count = 0; for results in iter { if let Ok(result) = results.get_by_name("a") { match result { QueryResultItem::Annotation(_annotation) => { count += 1; } _ => assert!(false, "wrong return type"), } } } assert_eq!(count, 1); Ok(()) } #[test] fn query_delete_annotation() -> Result<(), StamError> { let mut store = setup_example_1()?; let query: Query = "DELETE ANNOTATION ?a { SELECT ANNOTATION ?a WHERE ID \"A1\"; }".try_into()?; store.query_mut(query)?; assert_eq!(store.annotation("A1"), None); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transposition_simple() -> Result<(), StamError> { let store = setup_example_8()?; let annotation = store.annotation("SimpleTransposition1").or_fail()?; assert_eq!(annotation.text().count(), 2); let mut iter = annotation.text(); assert_eq!(iter.next(), Some("human beings are born")); assert_eq!(iter.next(), Some("human beings are born")); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transposition_complex() -> Result<(), StamError> { let store = setup_example_8b()?; let phrase1 = store.annotation("A1").or_fail()?; assert_eq!(phrase1.text_simple(), Some("human beings are born")); let phrase2 = store.annotation("A2").or_fail()?; assert_eq!(phrase2.text_simple(), Some("human beings are born")); let annotation = store.annotation("ComplexTransposition1").or_fail()?; assert_eq!( annotation .annotations_in_targets(AnnotationDepth::One) .count(), 2 ); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transpose_over_simple_transposition() -> Result<(), StamError> { let mut store = setup_example_8()?; store.annotate( AnnotationBuilder::new() .with_id("A3") .with_data("mydataset", "species", "homo sapiens") .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(4, 9), )), )?; let transposition = store.annotation("SimpleTransposition1").or_fail()?; let source = store.annotation("A3").or_fail()?; assert_eq!( source.text_simple(), Some("human"), "sanity check for source annotation" ); let config = TransposeConfig { transposition_id: Some("NewTransposition".to_string()), target_side_ids: vec!["A3t".to_string()], ..Default::default() }; let targets = store.annotate_from_iter(source.transpose(&transposition, config)?.into_iter())?; assert_eq!(targets.len(), 2); let new_transposition = store.annotation("NewTransposition").or_fail()?; assert_eq!( new_transposition .annotations_in_targets(AnnotationDepth::One) .count(), 2, "new transposition must have two target annotations (source annotation and transposed annotation)" ); let source = store.annotation("A3").or_fail()?; //reobtain (otherwise borrow checker complains after mutation) let transposed = store.annotation("A3t").or_fail()?; assert_eq!( transposed.text_simple(), source.text_simple(), "transposed annotation must reference same text as source" ); let tsel = transposed.textselections().next().unwrap(); assert_eq!( tsel.resource().id(), Some("warhol"), "transposed annotation must reference the target resource" ); assert_eq!(tsel.begin(), 0, "transposed offset check"); assert_eq!(tsel.end(), 5, "transposed offset check"); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transpose_over_simple_transposition_invalid() -> Result<(), StamError> { let mut store = setup_example_8()?; store.annotate( AnnotationBuilder::new() .with_id("A3") .with_data("mydataset", "species", "homo sapiens") .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(0, 9), //"all human" )), )?; let transposition = store.annotation("SimpleTransposition1").or_fail()?; let source = store.annotation("A3").or_fail()?; assert_eq!( source.text_simple(), Some("all human"), "sanity check for source annotation" ); let config = TransposeConfig { transposition_id: Some("NewTransposition".to_string()), target_side_ids: vec!["A3t".to_string()], ..Default::default() }; assert!(source.transpose(&transposition, config).is_err()); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transpose_over_complex_transposition() -> Result<(), StamError> { let mut store = setup_example_8b()?; store.annotate( AnnotationBuilder::new() .with_id("A3") .with_data("mydataset", "species", "homo sapiens") .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(4, 9), )), )?; let transposition = store.annotation("ComplexTransposition1").or_fail()?; let source = store.annotation("A3").or_fail()?; assert_eq!( source.text_simple(), Some("human"), "sanity check for source annotation" ); let config = TransposeConfig { transposition_id: Some("NewTransposition".to_string()), target_side_ids: vec!["A3t".to_string()], ..Default::default() }; let targets = store.annotate_from_iter(source.transpose(&transposition, config)?.into_iter())?; assert_eq!(targets.len(), 2); let new_transposition = store.annotation("NewTransposition").or_fail()?; assert_eq!( new_transposition .annotations_in_targets(AnnotationDepth::One) .count(), 2, "new transposition must have two target annotations (source annotation and transposed annotation)" ); let source = store.annotation("A3").or_fail()?; //reobtain (otherwise borrow checker complains after mutation) let transposed = store.annotation("A3t").or_fail()?; assert_eq!( transposed.text_simple(), source.text_simple(), "transposed annotation must reference same text as source" ); let tsel = transposed.textselections().next().unwrap(); assert_eq!( tsel.resource().id(), Some("warhol"), "transposed annotation must reference the target resource" ); assert_eq!(tsel.begin(), 0, "transposed offset check"); assert_eq!(tsel.end(), 5, "transposed offset check"); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transpose_over_complex_transposition_with_resegmentation() -> Result<(), StamError> { let mut store = setup_example_8c()?; store.annotate( AnnotationBuilder::new() .with_id("A3") .with_data("mydataset", "species", "homo sapiens") .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(4, 9), )), )?; let transposition = store.annotation("ComplexTransposition1").or_fail()?; let source = store.annotation("A3").or_fail()?; assert_eq!( source.text_join(""), "human", "sanity check for source annotation" ); let config = TransposeConfig { transposition_id: Some("NewTransposition".to_string()), resegmentation_id: Some("NewResegmentation".to_string()), target_side_ids: vec!["A3t".to_string()], ..Default::default() }; let targets = store.annotate_from_iter(source.transpose(&transposition, config)?.into_iter())?; assert_eq!(targets.len(), 4, "Test the number of generated annotations"); let new_transposition = store.annotation("NewTransposition").or_fail()?; assert_eq!( new_transposition .annotations_in_targets(AnnotationDepth::One) .count(), 2, "new transposition must have two target annotations (source annotation and transposed annotation)" ); assert!( new_transposition .annotations_in_targets(AnnotationDepth::One) .all(|a| a.id() != Some("A3")), "the source annotation may NOT be a direct part of the transposition in this case" ); let transposed = store.annotation("A3t").or_fail()?; let tsels: ResultTextSelectionSet = transposed.textselections().collect(); assert_eq!( tsels.resource().id(), Some("warhol"), "transposed annotation must reference the target resource" ); assert_eq!( tsels.len(), 2, "transposed annotation must reference two text selections" ); let mut iter = tsels.iter(); let tsel1 = iter.next().unwrap(); let tsel2 = iter.next().unwrap(); assert_eq!(tsel1.begin(), 1, "transposed offset check (1)"); assert_eq!(tsel1.end(), 3, "transposed offset check (1)"); assert_eq!(tsel2.begin(), 5, "transposed offset check (2)"); assert_eq!(tsel2.end(), 8, "transposed offset check (2)"); let resegmentation = store.annotation("NewResegmentation").or_fail()?; assert_eq!( resegmentation .annotations_in_targets(AnnotationDepth::One) .count(), 2, "resegmentation must have two target annotations (source annotation and resegmented annotation)" ); Ok(()) } #[test] #[cfg(feature = "transpose")] fn transpose_over_complex_transposition_invalid() -> Result<(), StamError> { let mut store = setup_example_8b()?; store.annotate( AnnotationBuilder::new() .with_id("A3") .with_data("mydataset", "species", "homo sapiens") .with_target(SelectorBuilder::textselector( "humanrights", Offset::simple(0, 9), //"all human" )), )?; let transposition = store.annotation("ComplexTransposition1").or_fail()?; let source = store.annotation("A3").or_fail()?; assert_eq!( source.text_simple(), Some("all human"), "sanity check for source annotation" ); let config = TransposeConfig { transposition_id: Some("NewTransposition".to_string()), target_side_ids: vec!["A3t".to_string()], ..Default::default() }; assert!(source.transpose(&transposition, config).is_err()); Ok(()) } #[test] fn segmentation() -> Result<(), StamError> { let store = setup_example_6()?; let segmentation: Vec<_> = store .resource("humanrights") .or_fail()? .segmentation() .collect(); assert_eq!(segmentation[0].begin(), 0); assert_eq!(segmentation[0].end(), 17); assert_eq!(segmentation[0].text(), "All human beings "); assert_eq!(segmentation[1].begin(), 17); assert_eq!(segmentation[1].end(), 40); assert_eq!(segmentation[1].text(), "are born free and equal"); assert_eq!(segmentation[2].begin(), 40); assert_eq!(segmentation[2].end(), 63); assert_eq!(segmentation[2].text(), " in dignity and rights."); Ok(()) } #[test] fn remove_annotation() -> Result<(), StamError> { let mut store = setup_example_1()?; store.remove_annotation("A1")?; assert_eq!(store.annotation("A1"), None); Ok(()) } #[test] fn remove_resource() -> Result<(), StamError> { let mut store = setup_example_1()?; store.remove_resource("testres")?; assert_eq!(store.resource("testres"), None); assert_eq!(store.annotation("A1"), None); //annotation should be gone too Ok(()) } #[test] fn remove_dataset() -> Result<(), StamError> { let mut store = setup_example_1()?; store.remove_dataset("testdataset")?; assert_eq!(store.dataset("testdataset"), None); assert_eq!(store.annotation("A1"), None); //annotation should be gone too Ok(()) } #[test] fn remove_data_strict() -> Result<(), StamError> { let mut store = setup_example_1()?; store.remove_data("testdataset", "D1", true)?; let set = store.dataset("testdataset").or_fail()?; assert_eq!(set.annotationdata("D1"), None); assert_eq!(store.annotation("A1"), None); //annotation should be gone too Ok(()) } #[test] fn remove_data_nonstrict() -> Result<(), StamError> { let mut store = setup_example_1()?; store.remove_data("testdataset", "D1", true)?; let set = store.dataset("testdataset").or_fail()?; assert_eq!(set.annotationdata("D1"), None); assert_eq!(store.annotation("A1"), None); //annotation should be gone too, despite strict mode, since there was only one data item associated Ok(()) } #[test] fn remove_key_strict() -> Result<(), StamError> { let mut store = setup_example_1()?; store.remove_key("testdataset", "pos", true)?; let set = store.dataset("testdataset").or_fail()?; assert_eq!(set.key("pos"), None); assert_eq!(store.annotation("A1"), None); //annotation should be gone too Ok(()) } #[test] #[cfg(feature = "textvalidation")] fn textvalidation_none() -> Result<(), StamError> { let store = setup_example_1()?; let annotation = store.annotation("A1").or_fail()?; assert_eq!(annotation.validate_text(), None); Ok(()) } #[test] #[cfg(feature = "textvalidation")] fn textvalidation_checksum() -> Result<(), StamError> { let mut store = setup_example_1()?; store.protect_text(TextValidationMode::Checksum)?; let annotation = store.annotation("A1").or_fail()?; assert_eq!( annotation.validation_checksum(), Some("7c211433f02071597741e6ff5a8ea34789abbf43") ); assert_eq!(annotation.validate_text(), Some(true)); assert!(store.validate_text(true).is_ok()); Ok(()) } #[test] #[cfg(feature = "textvalidation")] fn textvalidation_texts() -> Result<(), StamError> { let mut store = setup_example_1()?; store.protect_text(TextValidationMode::Text)?; let annotation = store.annotation("A1").or_fail()?; assert_eq!(annotation.validation_text(), Some("world")); assert_eq!(annotation.validate_text(), Some(true)); assert!(store.validate_text(true).is_ok()); Ok(()) } #[cfg(not(target_os = "windows"))] #[test] fn add_substore_absolute() -> Result<(), StamError> { let mut store = AnnotationStore::new(Config::default().with_debug(true)) .with_id("superstore") .with_filename("/tmp/superstore.stam.json"); //add substore with an absolute path store.add_substore(&format!( "{}/tests/test.store.stam.json", CARGO_MANIFEST_DIR ))?; // Create a new dataset and insert it into the main store let dataset = AnnotationDataSet::new(Config::default()) .with_id("metadataset") .with_filename("/tmp/metadata.dataset.stam.json"); let dataset_handle = store.insert(dataset)?; store.annotate( AnnotationBuilder::new() .with_data(dataset_handle, "author", "proycon") .with_target(SelectorBuilder::ResourceSelector("hello.txt".into())), )?; store.save()?; Ok(()) }