def test_copy_stability(self): # Test the stability of naming of objects across copies. # See https://github.com/SynBioDex/pySBOL3/issues/231 # # Strategy: create an object with 10+ children of the same # type. Add to a document and serialize the document. Load the # serialized document. Copy the object to a new document. # Serialize the new document. Compare the serializations. If we # use sorted ntriples, the serializations should be the same. # This will demonstrate that we maintain names properly despite # the inherently unordered nature of SBOL. sbol3.set_namespace('https://github.com/synbiodex/pysbol3') c1 = sbol3.Component('c1', types=[sbol3.SBO_DNA]) # Create a double-digit number of children to test sort of 10, 11, 1, etc. for i in range(12): instance_of_uri = f'https://example.com/instance/i{i}' c1.features.append(sbol3.SubComponent(instance_of=instance_of_uri)) doc1 = sbol3.Document() doc1.add(c1) # Serialize to string doc1_string = doc1.write_string(sbol3.SORTED_NTRIPLES) self.assertIsNotNone(doc1_string) # Load the serialized document into a new document tmp_doc = sbol3.Document() tmp_doc.read_string(doc1_string, sbol3.SORTED_NTRIPLES) # Locate the top level to copy tmp_c1 = tmp_doc.find('c1') self.assertIsNotNone(tmp_c1) self.assertIsInstance(tmp_c1, sbol3.TopLevel) # Copy the top level into a new document doc2 = sbol3.Document() sbol3.copy([tmp_c1], into_document=doc2) doc2_string = doc2.write_string(sbol3.SORTED_NTRIPLES) # Verify that the serializations are identical self.assertEqual(doc1_string, doc2_string)
def test_expansion(self): """Test basic expansion of combinatorial derivations""" doc = sbol3.Document() doc.read(os.path.join(TESTFILE_DIR, 'simple_library.nt')) sbol3.set_namespace('http://sbolstandard.org/testfiles') roots = list( sbol_utilities.expand_combinatorial_derivations. root_combinatorial_derivations(doc)) assert len( roots) == 1, f'Unexpected roots: {[r.identity for r in roots]}' derivative_collections = sbol_utilities.expand_combinatorial_derivations.expand_derivations( roots) assert not len(doc.validate()) assert len(doc.find('Round_1_order_collection').members) == 24 output_doc = sbol3.Document() for c in derivative_collections: copy_toplevel_and_dependencies(output_doc, c) assert not len(output_doc.validate()) temp_name = tempfile.mkstemp(suffix='.nt')[1] output_doc.write(temp_name, sbol3.SORTED_NTRIPLES) assert_files_identical( temp_name, os.path.join(TESTFILE_DIR, 'expanded_simple_library.nt'))
def test_round_trip(self): # Test the int list property, which is not used by the # core SBOL 3 data model sbol3.set_namespace('https://github.com/synbiodex/pysbol3') obj = CustomIdentifiedClass() self.assertEqual([], obj.foo_int) obj.foo_int.append(7) obj.foo_int.append(14) self.assertEqual([7, 14], obj.foo_int) tl_name = 'my_obj' tl = CustomTopClass(tl_name) tl.children.append(obj) doc = sbol3.Document() doc.add(tl) doc2 = sbol3.Document() # Round trip the document with tempfile.TemporaryDirectory() as tmpdirname: test_file = os.path.join(tmpdirname, 'custom.nt') doc.write(test_file, sbol3.NTRIPLES) doc2.read(test_file, sbol3.NTRIPLES) tl2 = doc2.find(tl_name) obj2 = tl2.children[0] # The lists are necessarily unordered because of RDF # Compare specially self.assertCountEqual([7, 14], obj2.foo_int)
def test_copy(self): namespace = 'https://github.com/synbiodex/pysbol3' sbol3.set_namespace(namespace) test_path = os.path.join(SBOL3_LOCATION, 'multicellular', 'multicellular.ttl') doc = sbol3.Document() doc.read(test_path) copies1 = sbol3.copy(doc) self.assertEqual(len(doc), len(copies1)) document_checker = self.make_document_checker(None) for obj in copies1: obj.traverse(document_checker) # Verify that the copies get the new namespace copies2 = sbol3.copy(doc, into_namespace=namespace) document_checker = self.make_document_checker(None) namespace_checker = self.make_namespace_checker(namespace) for obj in copies2: obj.traverse(document_checker) obj.traverse(namespace_checker) # Verify new namespace AND new document namespace3 = 'https://github.com/synbiodex/pysbol3/copytest' doc3 = sbol3.Document() copies3 = sbol3.copy(doc, into_namespace=namespace3, into_document=doc3) document_checker = self.make_document_checker(doc3) namespace_checker = self.make_namespace_checker(namespace3) for obj in copies3: obj.traverse(document_checker) obj.traverse(namespace_checker)
def test_annotation(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') # Create custom annotation annotation_uri = 'http://example.org/boolean_property' annotation_value = 'foo' c = sbol3.Component('c1', sbol3.SBO_DNA) c.annotation = sbol3.TextProperty(c, annotation_uri, 0, 1, []) c.annotation = annotation_value self.assertEqual(annotation_value, c.annotation) doc = sbol3.Document() doc.add(c) doc2 = sbol3.Document() with tempfile.TemporaryDirectory() as tmpdirname: test_file = os.path.join(tmpdirname, 'annotation.xml') doc.write(test_file, sbol3.RDF_XML) # Roundtrip doc2.read(test_file, sbol3.RDF_XML) # Recover annotation c = doc2.find('c1') c.annotation = sbol3.TextProperty(c, annotation_uri, 0, 1, []) self.assertEqual(annotation_value, c.annotation)
def test_migrate(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') test_path = os.path.join(SBOL3_LOCATION, 'multicellular', 'multicellular.ttl') doc = sbol3.Document() doc.read(test_path) orig_len = len(doc) doc2 = sbol3.Document() doc2.migrate(doc) self.assertEqual(orig_len, len(doc2)) self.assertEqual(0, len(doc))
def test_shacl_closure_with_toplevels(self): # SBOL closure semantics should allow properties to reference # a TopLevel object not contained in the Document # This is the test case in https://github.com/SynBioDex/pySBOL3/issues/348 doc = sbol3.Document() doc.read(os.path.join(TEST_DIR, 'resources', 'package.nt')) self.assertEqual(len(doc.validate()), 0) minidoc = sbol3.Document() c = doc.find('https://synbiohub.org/public/igem/BBa_I20270') self.assertIsInstance(c, sbol3.TopLevel) sbol3.copy([c], into_document=minidoc) # this assertion failed before the fix to the shacl rules self.assertEqual(len(minidoc.validate()), 0)
def test_read_default_namespace(self): # This is a modified version of the initial bug report for # https://github.com/SynBioDex/pySBOL3/issues/337 doc = sbol3.Document() sbol3.set_namespace('http://foo.org') doc.add(sbol3.Sequence('bar')) self.assertEqual(0, len(doc.validate())) file_format = sbol3.SORTED_NTRIPLES data = doc.write_string(file_format=file_format) doc2 = sbol3.Document() doc2.read_string(data, file_format=file_format) # Successful read sbol3.set_namespace('http://baz.com/') doc3 = sbol3.Document() doc3.read_string(data, file_format=file_format)
def test_two_behaviors(self): ############################################# # set up the document print('Setting up document') doc = sbol3.Document() sbol3.set_namespace('https://bbn.com/scratch/') ############################################# # Create the behavior and constraints print('Creating Constraints') a = paml.Primitive("a") b = paml.Primitive("b") # Constrain start of b to follow end of a by [10, 15] follows_constraint = pamlt.precedes(a, [10, 15], b, units=tyto.OM.hour) doc.add(a) # doc.add(follows_constraint) ######################################## # Validate and write the document print('Validating and writing time') v = doc.validate() assert not v.errors and not v.warnings, "".join( str(e) for e in doc.validate().errors)
def main(argv=None): args = parse_args(argv) init_logging(args.debug) doc = sbol3.Document() doc.read(args.inputfile) my_visitor = MyVisitor() doc.accept(my_visitor)
def test_doc_diff(self): # Invoke sbol_utilities.sbol_diff.doc_diff directly esl_doc = sbol3.Document() esl_doc.read(ESL_SBOL_PATH) sl_doc = sbol3.Document() sl_doc.read(SL_SBOL_PATH) actual = sbol_utilities.sbol_diff.doc_diff(esl_doc, esl_doc, silent=True) expected = 0 self.assertEqual(expected, actual) actual = sbol_utilities.sbol_diff.doc_diff(esl_doc, sl_doc, silent=True) expected = 1 self.assertEqual(expected, actual)
def test_sample_set(self): # Test extending an SBOL class to create a new type with all the # properties of the SBOL type plus whatever other properties the # programmer defines. The key to doing this properly is to use # multiple inheritance to extend both the desired SBOL type and # CustomTopLevel so that the type is properly serialized to RDF. doc = sbol3.Document() ss_uri = 'http://example.org/sbol3/ss1' template_uri = 'http://example.org/sbol3/template1' ss = SampleSet(identity=ss_uri, template=template_uri) doc.add(ss) doc2 = sbol3.Document() rdf_format = sbol3.TURTLE output = doc.write_string(rdf_format) doc2.read_string(output, rdf_format)
def test_round_trip(self): # See https://github.com/SynBioDex/pySBOL3/issues/156 sbol3.set_namespace('https://github.com/synbiodex/pysbol3') comp1 = sbol3.Component('comp1', sbol3.SBO_DNA) cd1 = sbol3.CombinatorialDerivation('cd1', comp1) self.assertEqual(comp1.identity, cd1.template) doc1 = sbol3.Document() doc1.add(comp1) doc1.add(cd1) doc2 = sbol3.Document() doc2.read_string(doc1.write_string(sbol3.SORTED_NTRIPLES), sbol3.SORTED_NTRIPLES) comp2 = doc2.find(comp1.identity) self.assertIsInstance(comp2, sbol3.Component) cd2 = doc2.find(cd1.identity) self.assertIsInstance(cd2, sbol3.CombinatorialDerivation)
def test_3to2_conversion(self): """Test ability to convert from SBOL3 to SBOL2""" # Get the SBOL3 test document tmp_sub = copy_to_tmp(package=['BBa_J23101.nt']) doc3 = sbol3.Document() doc3.read(os.path.join(tmp_sub, 'BBa_J23101.nt')) # Convert to SBOL2 and check contents doc2 = convert3to2(doc3) assert len( doc2.componentDefinitions ) == 1, f'Expected 1 CD, but found {len(doc2.componentDefinitions)}' # TODO: bring this back after resolution of https://github.com/sboltools/sbolgraph/issues/15 #assert len(doc2.activities) == 1, f'Expected 1 Activity, but found {len(doc2.activities)}' assert len( doc2.sequences ) == 1, f'Expected 1 Sequence, but found {len(doc2.sequences)}' assert doc2.componentDefinitions[ 0].identity == 'https://synbiohub.org/public/igem/BBa_J23101' assert doc2.componentDefinitions[0].sequences[ 0] == 'https://synbiohub.org/public/igem/BBa_J23101_sequence' assert doc2.sequences[ 0].encoding == 'http://www.chem.qmul.ac.uk/iubmb/misc/naseq.html' assert doc2.sequences[ 0].elements == 'tttacagctagctcagtcctaggtattatgctagc'
def test_activity_bad_inflows(self): """Test whether validator can detect error due to excess or missing inflows""" # set up the document print('Setting up document') doc = sbol3.Document() sbol3.set_namespace('https://bbn.com/scratch/') # Create the protocol print('Creating protocol') protocol = paml.Protocol('broken') doc.add(protocol) # call order backwards, to make an edge from the final to the initial protocol.order(protocol.final(), protocol.initial()) # access a parameter node and order it backwards too p = uml.ActivityParameterNode() protocol.nodes.append(p) protocol.order(protocol.final(), p) # Validate the document, which should produce two errors print('Validating and writing protocol') v = doc.validate() assert len(v) == 3, f'Expected 3 validation issues, but found {len(v)}' expected = [ 'https://bbn.com/scratch/broken/ActivityParameterNode1: Too few values for property parameter. Expected 1, found 0', 'https://bbn.com/scratch/broken/InitialNode1: InitialNode must have no incoming edges, but has 1', 'https://bbn.com/scratch/broken/FlowFinalNode1: Node has no incoming edges, so cannot be executed' ] observed = [str(e) for e in v] assert observed == expected, f'Unexpected error content: {observed}'
def test_timed_small_protocol(self): ############################################# # set up the document print('Setting up document') doc = sbol3.Document() sbol3.set_namespace('https://bbn.com/scratch/') ############################################# # Create the Protocol print('Creating Protocol') protocol = paml.Protocol('test_protocol') # Protocol starts at time zero start = pamlt.startTime(protocol, 0, units=tyto.OM.hour) # Protocol lasts 10 - 15 hours duration = pamlt.duration(protocol, [10, 15], units=tyto.OM.hour) time_constraints = pamlt.TimeConstraints("small_protocol_constraints", constraints=pamlt.And( [start, duration]), protocols=[protocol]) doc.add(protocol) doc.add(time_constraints) ######################################## # Validate and write the document print('Validating and writing time') v = doc.validate() assert not v.errors and not v.warnings, "".join( str(e) for e in doc.validate().errors)
def test_cloning_with_children(self): # This test does not use `sbol3.set_namespace` as the other # cloning unit tests do. This is on purpose to verify that # cloning does not rely on the default namespace. doc = sbol3.Document() namespace = 'https://github.com/synbiodex/pysbol3' c1_identity = posixpath.join(namespace, 'c1') c2_identity = posixpath.join(namespace, 'c2') s1_identity = posixpath.join(namespace, 's1') c1 = sbol3.Component(c1_identity, sbol3.SBO_DNA) doc.add(c1) seq1 = sbol3.Sequence(s1_identity) doc.add(seq1) c1.sequences.append(seq1) sc1 = sbol3.SubComponent(c1) es1 = sbol3.EntireSequence(seq1) sc1.source_locations.append(es1) c1.features = [sc1] c2 = c1.clone(c2_identity) self.assertEqual(c2_identity, c2.identity) self.assertIsNone(c2.document) # Check on the SubComponent sc2 = c2.features[0] self.assertIsInstance(sc2, sbol3.SubComponent) self.assertNotEqual(sc1.identity, sc2.identity) self.assertTrue(sc2.identity.startswith(c2.identity)) # Ensure that the reference was updated properly self.assertEqual(c2.identity, sc2.instance_of) self.assertIsNone(sc2.document) es2 = sc2.source_locations[0] self.assertIsInstance(es2, sbol3.EntireSequence) self.assertNotEqual(es1.identity, es2.identity) self.assertTrue(es2.identity.startswith(c2.identity)) self.assertEqual(es1.sequence, es2.sequence) self.assertIsNone(es2.document)
def test_expressions(self): ############################################# # set up the document print('Setting up document') doc = sbol3.Document() sbol3.set_namespace('https://bbn.com/scratch/') ############################################# # Create the Expressions print('Creating Protocol') # expression e1: 60s * duration(a1) a1 = paml.Primitive("a1") d1 = uml.Duration(observation=uml.DurationObservation(event=[a1])) m1 = pamlt.TimeMeasure(expr=sbol3.Measure(60, tyto.OM.second)) e1 = uml.Expression(symbol="*", is_ordered=False, operand=[m1, d1]) #doc.add(e1) # expression lt1: e1 < e2 e2 = pamlt.TimeMeasure(expr=sbol3.Measure(120, tyto.OM.second)) lt1 = uml.Expression(symbol="<", is_ordered=True, operand=[e1, e2]) #doc.add(lt1) # c1: Not(lt1) c1 = pamlt.Not(constrained_elements=lt1) # doc.add(c1) ######################################## # Validate and write the document print('Validating and writing time') v = doc.validate() assert not v.errors and not v.warnings, "".join( str(e) for e in doc.validate().errors)
def test_single_behavior(self): ############################################# # set up the document print('Setting up document') doc = sbol3.Document() sbol3.set_namespace('https://bbn.com/scratch/') ############################################# # Create the behavior and constraints print('Creating Constraints') a = paml.Primitive("a") # Constrain start time of a to [0, 10] start_a = pamlt.startTime(a, [0, 10], units=tyto.OM.hour) # Constrain end time of a to [10, 15] end_a = pamlt.endTime(a, [10, 15], units=tyto.OM.hour) # Constrain duration of a to [1, 5] duration_a = pamlt.duration(a, [1, 5], units=tyto.OM.hour) constraint = pamlt.And([start_a, end_a, duration_a]) time_constraints = pamlt.TimeConstraints("small_protocol_constraints", constraints=[constraint]) doc.add(a) doc.add(time_constraints) ######################################## # Validate and write the document print('Validating and writing time') v = doc.validate() assert not v.errors and not v.warnings, "".join( str(e) for e in doc.validate().errors)
def test_ordered_behavior_parameters(self): doc = sbol3.Document() sbol3.set_namespace('https://bbn.com/scratch/') parameter1 = uml.OrderedPropertyValue( index=0, property_value=uml.Parameter( direction="in", default_value=uml.LiteralInteger(value=0), is_unique=True, is_ordered=True, lower_value=uml.LiteralInteger(value=0), upper_value=uml.LiteralInteger(value=10))) parameter2 = uml.OrderedPropertyValue( index=1, property_value=uml.Parameter( direction="in", default_value=uml.LiteralInteger(value=0), is_unique=True, is_ordered=True, lower_value=uml.LiteralInteger(value=0), upper_value=uml.LiteralInteger(value=10))) behavior = uml.Behavior("b", parameters=[parameter1, parameter2]) assert parameter1 in behavior.parameters and parameter2 in behavior.parameters doc.add(behavior) v = doc.validate() assert not v.errors and not v.warnings, "".join( str(e) for e in doc.validate().errors)
def test_guess_format(self): doc = sbol3.Document() # Expect ValueError because '.foo' file extension is unknown with self.assertRaises(ValueError): doc.read('test.foo') # Expect ValueError because '.foo' file extension is unknown with self.assertRaises(ValueError): doc.write('test.foo')
def test_read(self): identity = 'https://github.com/synbiodex/pysbol3/experiment1' nt_data = f'<{identity}> <{rdflib.RDF.type}> <{sbol3.SBOL_EXPERIMENT}> .' doc = sbol3.Document() doc.read_string(nt_data, 'ttl') experiment = doc.find(identity) self.assertIsNotNone(experiment) self.assertIsInstance(experiment, sbol3.Collection)
def run_round_trip_file(self, test_path, file_format): """Runs a round trip test on the file at the given path. Path can be relative or absolute. """ filename = os.path.basename(test_path) test2_path = os.path.join(self.temp_out_dir, filename) # Read the document, then write it back to disk doc = sbol3.Document() doc.read(test_path, file_format) skip_list = self.skip_validation basename = os.path.basename(test_path) if os.path.splitext(basename)[0] not in skip_list: # Validate files that are not in the skip list report = doc.validate() if report: for item in report: print(item) self.fail(f'got {len(report)} validation errors') doc.write(test2_path, file_format) # Read the newly written document and compare results doc2 = sbol3.Document() doc2.read(test2_path, file_format) # TODO: what about Document.compare()? # self.assertTrue(doc.compare(doc2)) # Now compare the graphs in RDF g1 = rdflib.Graph() g1.parse(test_path, format=file_format) iso1 = rdflib.compare.to_isomorphic(g1) g2 = rdflib.Graph() g2.parse(test2_path, format=file_format) iso2 = rdflib.compare.to_isomorphic(g2) rdf_diff = rdflib.compare.graph_diff(iso1, iso2) if rdf_diff[1] or rdf_diff[2]: self.logger.warning('Detected %d different RDF triples in %s' % (len(rdf_diff[1]) + len(rdf_diff[2]), test_path)) if not self.logger.isEnabledFor(logging.DEBUG): self.logger.warning('Set environment variable %s to see details', DEBUG_ENV_VAR) for stmt in rdf_diff[1]: self.logger.debug('Only in original: %r', stmt) for stmt in rdf_diff[2]: self.logger.debug('Only in loaded: %r', stmt) self.fail('Differences in RDF detected')
def test_find_all2(self): file = os.path.join(SBOL3_LOCATION, 'multicellular', 'multicellular.nt') doc = sbol3.Document() doc.read(file) # Find all instances of Participation found = doc.find_all(lambda obj: isinstance(obj, sbol3.Participation)) self.assertEqual(11, len(found))
def test_read_with_default_namespace(self): # Test reading a file when the default namespace is set # See https://github.com/SynBioDex/pySBOL3/issues/337 sbol3.set_namespace('http://example.com') test_path = os.path.join(SBOL3_LOCATION, 'toggle_switch', 'toggle_switch.ttl') doc = sbol3.Document() doc.read(test_path)
def test_read(self): identity = 'https://github.com/synbiodex/pysbol3/collection1' nt_data = f'<{identity}> <{rdflib.RDF.type}> <{sbol3.SBOL_COLLECTION}> .' doc = sbol3.Document() doc.read_string(nt_data, 'ttl') collection = doc.find(identity) self.assertIsNotNone(collection) self.assertIsInstance(collection, sbol3.Collection)
def test_copy_child_objects(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') doc = sbol3.Document() root = sbol3.Component('root', sbol3.SBO_DNA) sub1 = sbol3.Component('sub1', sbol3.SBO_DNA) sub2 = sbol3.Component('sub2', sbol3.SBO_DNA) sc1 = sbol3.SubComponent(sub1) sc2 = sbol3.SubComponent(sub2) root.features.append(sc1) root.features.append(sc2) doc.add(root) doc.add(sub1) doc.add(sub2) doc2 = sbol3.Document() root_copy = root.copy(target_doc=doc2) self.assertEqual([sc.identity for sc in root.features], [sc.identity for sc in root_copy.features])
def test_add_multiple(self): # Ensure that duplicate identities cannot be added to the document. # See https://github.com/SynBioDex/pySBOL3/issues/39 document = sbol3.Document() experiment1 = sbol3.Experiment(identity=sbol3.SBOL3_NS) document.add(experiment1) experiment2 = sbol3.Experiment(identity=sbol3.SBOL3_NS) with self.assertRaises(ValueError): document.add(experiment2)
def test_variable_feature_shacl(self): # See https://github.com/SynBioDex/sbol-shacl/issues/4 # This file should be valid. Ensure we are handling # VariableFeature in the SHACL rules test_file = os.path.join(TEST_RESOURCE_DIR, 'simple_library.nt') doc = sbol3.Document() doc.read(test_file) report = doc.validate() self.assertEqual(0, len(report))
def test_read_ntriples(self): # Initial test of Document.read filename = 'model.nt' test_path = os.path.join(SBOL3_LOCATION, 'entity', 'model', filename) doc = sbol3.Document() doc.read(test_path) with tempfile.TemporaryDirectory() as tmpdirname: test_file = os.path.join(tmpdirname, filename) doc.write(test_file, sbol3.NTRIPLES)