1 from __future__ import absolute_import, print_function, unicode_literals
4 from lxml.html import fromstring
9 from six.moves.urllib.parse import urljoin
11 from django.conf import settings
12 from django.core import mail
13 from django.core.exceptions import ObjectDoesNotExist
14 from django.core.urlresolvers import reverse
15 from django.test import TestCase
16 from django.utils.encoding import smart_text
18 from .models import SequencingRun, Sequencer, FlowCell, FileType
19 from samples.models import HTSUser
20 from .experiments import flowcell_information, lanes_for
21 from .experiments_factory import FlowCellFactory, LaneFactory
22 from samples.samples_factory import AffiliationFactory, HTSUserFactory, \
23 LibraryFactory, LibraryTypeFactory, MultiplexIndexFactory
24 from htsworkflow.auth import apidata
25 from htsworkflow.util.ethelp import validate_xhtml
27 from htsworkflow.pipelines.test.simulate_runfolder import TESTDATA_DIR
29 LANE_SET = range(1, 9)
31 NSMAP = {'libns': 'http://jumpgate.caltech.edu/wiki/LibraryOntology#'}
34 class ExperimentsTestCases(TestCase):
36 # Generate at least one fleshed out example flowcell
37 self.tempdir = tempfile.mkdtemp(prefix='htsw-test-experiments-')
38 settings.RESULT_HOME_DIR = self.tempdir
40 self.password = 'password'
41 self.user_odd = HTSUserFactory(username='user-odd')
42 self.user_odd.set_password(self.password)
43 self.affiliation_odd = AffiliationFactory(name='affiliation-odd', users=[self.user_odd])
44 self.user_even = HTSUserFactory(username='user-even')
45 self.user_even.set_password(self.password)
46 self.affiliation_even = AffiliationFactory(name='affiliation-even', users=[self.user_even])
47 self.admin = HTSUserFactory.create(username='admin', is_staff=True, is_superuser=True)
48 self.admin.set_password(self.password)
51 self.fc12150 = FlowCellFactory(flowcell_id='FC12150')
52 self.fc1_id = 'FC12150'
53 self.fc1_root = os.path.join(self.tempdir, self.fc1_id)
54 os.mkdir(self.fc1_root)
55 self.fc1_dir = os.path.join(self.fc1_root, 'C1-37')
56 os.mkdir(self.fc1_dir)
57 runxml = 'run_FC12150_2007-09-27.xml'
58 shutil.copy(os.path.join(TESTDATA_DIR, runxml),
59 os.path.join(self.fc1_dir, runxml))
61 affiliation = self.affiliation_odd if i % 2 == 1 else self.affiliation_even
62 library = LibraryFactory(id="1215" + str(i))
63 library.affiliations.add(affiliation)
64 lane = LaneFactory(flowcell=self.fc12150, lane_number=i, library=library)
66 os.path.join(TESTDATA_DIR,
67 'woldlab_070829_USI-EAS44_0017_FC11055_1.srf'),
68 os.path.join(self.fc1_dir,
69 'woldlab_070829_SERIAL_FC12150_%d.srf' %(i,))
73 self.fc42jtn = FlowCellFactory(flowcell_id='42JTNAAXX')
74 self.fc42jtn_lanes = []
76 affiliation = self.affiliation_odd if i % 2 == 1 else self.affiliation_even
77 library_type = LibraryTypeFactory(can_multiplex=True)
78 multiplex_index = MultiplexIndexFactory(adapter_type=library_type)
79 library = LibraryFactory(id="1300" + str(i),
80 library_type=library_type,
81 multiplex_id=multiplex_index.multiplex_id)
82 library.affiliations.add(affiliation)
83 lane = LaneFactory(flowcell=self.fc42jtn, lane_number=(i % 2) + 1, library=library)
84 self.fc42jtn_lanes.append(lane)
86 self.fc2_dir = os.path.join(self.tempdir, '42JTNAAXX')
87 os.mkdir(self.fc2_dir)
88 os.mkdir(os.path.join(self.fc2_dir, 'C1-25'))
89 os.mkdir(os.path.join(self.fc2_dir, 'C1-37'))
90 os.mkdir(os.path.join(self.fc2_dir, 'C1-37', 'Plots'))
93 shutil.rmtree(self.tempdir)
95 def test_flowcell_information(self):
97 Check the code that packs the django objects into simple types.
99 fc12150 = self.fc12150
100 fc42jtn = self.fc42jtn
101 fc42ju1 = FlowCellFactory(flowcell_id='42JU1AAXX')
103 for fc_id in ['FC12150', '42JTNAAXX', '42JU1AAXX']:
104 fc_dict = flowcell_information(fc_id)
105 fc_django = FlowCell.objects.get(flowcell_id=fc_id)
106 self.assertEqual(fc_dict['flowcell_id'], fc_id)
107 self.assertEqual(fc_django.flowcell_id, fc_id)
108 self.assertEqual(fc_dict['sequencer'], fc_django.sequencer.name)
109 self.assertEqual(fc_dict['read_length'], fc_django.read_length)
110 self.assertEqual(fc_dict['notes'], fc_django.notes)
111 self.assertEqual(fc_dict['cluster_station'], fc_django.cluster_station.name)
113 for lane in fc_django.lane_set.all():
114 lane_contents = fc_dict['lane_set'][lane.lane_number]
115 lane_dict = multi_lane_to_dict(lane_contents)[lane.library_id]
116 self.assertEqual(lane_dict['cluster_estimate'], lane.cluster_estimate)
117 self.assertEqual(lane_dict['comment'], lane.comment)
118 self.assertEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id)
119 self.assertEqual(lane_dict['lane_number'], lane.lane_number)
120 self.assertEqual(lane_dict['library_name'], lane.library.library_name)
121 self.assertEqual(lane_dict['library_id'], lane.library.id)
122 self.assertAlmostEqual(float(lane_dict['pM']), float(lane.pM))
123 self.assertEqual(lane_dict['library_species'],
124 lane.library.library_species.scientific_name)
126 response = self.client.get('/experiments/config/%s/json' % (fc_id,), apidata)
127 # strptime isoformat string = '%Y-%m-%dT%H:%M:%S'
128 fc_json = json.loads(smart_text(response.content))['result']
129 self.assertEqual(fc_json['flowcell_id'], fc_id)
130 self.assertEqual(fc_json['sequencer'], fc_django.sequencer.name)
131 self.assertEqual(fc_json['read_length'], fc_django.read_length)
132 self.assertEqual(fc_json['notes'], fc_django.notes)
133 self.assertEqual(fc_json['cluster_station'], fc_django.cluster_station.name)
136 for lane in fc_django.lane_set.all():
137 lane_contents = fc_json['lane_set'][str(lane.lane_number)]
138 lane_dict = multi_lane_to_dict(lane_contents)[lane.library_id]
140 self.assertEqual(lane_dict['cluster_estimate'], lane.cluster_estimate)
141 self.assertEqual(lane_dict['comment'], lane.comment)
142 self.assertEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id)
143 self.assertEqual(lane_dict['lane_number'], lane.lane_number)
144 self.assertEqual(lane_dict['library_name'], lane.library.library_name)
145 self.assertEqual(lane_dict['library_id'], lane.library.id)
146 self.assertAlmostEqual(float(lane_dict['pM']), float(lane.pM))
147 self.assertEqual(lane_dict['library_species'],
148 lane.library.library_species.scientific_name)
150 def test_invalid_flowcell(self):
152 Make sure we get a 404 if we request an invalid flowcell ID
154 response = self.client.get('/experiments/config/nottheone/json', apidata)
155 self.assertEqual(response.status_code, 404)
157 def test_no_key(self):
159 Require logging in to retrieve meta data
161 response = self.client.get('/experiments/config/FC12150/json')
162 self.assertEqual(response.status_code, 403)
164 def test_library_id(self):
165 """make sure we can retrive a non-numeric library ID
167 response = self.client.get('/experiments/config/FC12150/json', apidata)
168 self.assertEqual(response.status_code, 200)
169 flowcell = json.loads(smart_text(response.content))['result']
171 # library id is 12150 + lane number (1-8), so 12153
172 lane_contents = flowcell['lane_set']['3']
173 lane_library = lane_contents[0]
174 self.assertEqual(lane_library['library_id'], '12153')
176 response = self.client.get('/samples/library/12153/json', apidata)
177 self.assertEqual(response.status_code, 200)
178 library_12153 = json.loads(smart_text(response.content))['result']
180 self.assertEqual(library_12153['library_id'], '12153')
182 def test_raw_id_field(self):
185 Library's have IDs, libraries also have primary keys,
186 we eventually had enough libraries that the drop down combo
187 box was too hard to filter through, unfortnately we want a
188 field that uses our library id and not the internal
189 primary key, and raw_id_field uses primary keys.
191 This tests to make sure that the value entered in the raw
192 library id field matches the library id looked up.
195 expected_ids = [ '1215{}'.format(i) for i in range(1,9) ]
196 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
197 response = self.client.get('/admin/experiments/flowcell/{}/'.format(self.fc12150.id))
199 tree = fromstring(response.content)
201 xpath_expression = '//input[@id="id_lane_set-%d-library"]'
202 input_field = tree.xpath(xpath_expression % (i,))[0]
203 library_field = input_field.find('../strong')
204 library_id, library_name = library_field.text.split(':')
205 # strip leading '#' sign from name
206 library_id = library_id[1:]
207 self.assertEqual(library_id, expected_ids[i])
208 self.assertEqual(input_field.attrib['value'], library_id)
210 def test_library_to_flowcell_link(self):
212 Make sure the library page includes links to the flowcell pages.
213 That work with flowcell IDs that have parenthetical comments.
215 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
216 response = self.client.get('/library/12151/')
217 self.assertEqual(response.status_code, 200)
218 status = validate_xhtml(response.content)
219 if status is not None: self.assertTrue(status)
221 tree = fromstring(response.content)
222 flowcell_spans = tree.xpath('//span[@property="libns:flowcell_id"]',
224 self.assertEqual(flowcell_spans[1].text, 'FC12150')
225 failed_fc_span = flowcell_spans[1]
226 failed_fc_a = failed_fc_span.getparent()
227 # make sure some of our RDF made it.
228 self.assertEqual(failed_fc_a.get('typeof'), 'libns:IlluminaFlowcell')
229 self.assertEqual(failed_fc_a.get('href'), '/flowcell/FC12150/')
230 fc_response = self.client.get(failed_fc_a.get('href'))
231 self.assertEqual(fc_response.status_code, 200)
232 status = validate_xhtml(response.content)
233 if status is not None: self.assertTrue(status)
235 fc_lane_response = self.client.get('/flowcell/FC12150/8/')
236 self.assertEqual(fc_lane_response.status_code, 200)
237 status = validate_xhtml(response.content)
238 if status is not None: self.assertTrue(status)
240 def test_pooled_multiplex_id(self):
241 fc_dict = flowcell_information(self.fc42jtn.flowcell_id)
243 lane_contents = fc_dict['lane_set'][2]
244 self.assertEqual(len(lane_contents), len(self.fc42jtn_lanes) / 2)
245 lane_dict = multi_lane_to_dict(lane_contents)
247 self.assertTrue(self.fc42jtn_lanes[0].library.multiplex_id in \
248 lane_dict['13001']['index_sequence'])
249 self.assertTrue(self.fc42jtn_lanes[2].library.multiplex_id in \
250 lane_dict['13003']['index_sequence'])
253 def test_lanes_for_view_user_odd(self):
254 """Make sure lanes_for HTML UI works.
256 user = self.user_odd.username
257 lanes = lanes_for(user)
258 self.assertEqual(len(lanes), 8)
260 response = self.client.get(
261 reverse('lanes_for', kwargs={'username': user}))
262 self.assertEqual(response.status_code, 200)
263 tree = fromstring(response.content)
264 lane_trs = tree.xpath('//div[@id="changelist"]/table/tbody/tr')
265 self.assertEqual(len(lane_trs), len(lanes))
266 # lanes is in db order
267 # lane_trs is in newest to oldest order
268 for lane_tr, lane_db in zip(lane_trs, reversed(lanes)):
269 library_id = lane_tr.xpath('td[6]/a')[0].text
270 self.assertEqual(library_id, lane_db['library'])
272 def test_lanes_for_view_invalid_user(self):
273 """Make sure we don't find anything with an invalid user
275 response = self.client.get(
276 reverse('lanes_for', kwargs={'username': 'doesntexist'}))
277 self.assertEqual(response.status_code, 404)
279 def test_lanes_for_json(self):
281 Check the code that packs the django objects into simple types.
283 user = self.user_odd.username
284 lanes = lanes_for(user)
285 self.assertEqual(len(lanes), 8)
287 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
288 lanes_json = json.loads(smart_text(response.content))['result']
289 self.assertEqual(len(lanes), len(lanes_json))
290 for i in range(len(lanes)):
291 self.assertEqual(lanes[i]['comment'], lanes_json[i]['comment'])
292 self.assertEqual(lanes[i]['lane_number'], lanes_json[i]['lane_number'])
293 self.assertEqual(lanes[i]['flowcell'], lanes_json[i]['flowcell'])
294 self.assertEqual(lanes[i]['run_date'], lanes_json[i]['run_date'])
296 def test_lanes_for_no_lanes(self):
298 Do we get something meaningful back when the user isn't attached to anything?
300 user = HTSUserFactory.create(username='supertest')
301 lanes = lanes_for(user.username)
302 self.assertEqual(len(lanes), 0)
304 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
305 self.assertEqual(response.status_code, 404)
307 def test_lanes_for_no_user(self):
309 Do we get something meaningful back when its the wrong user
311 user = 'not a real user'
312 self.assertRaises(ObjectDoesNotExist, lanes_for, user)
314 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
315 self.assertEqual(response.status_code, 404)
317 def test_raw_data_dir(self):
318 """Raw data path generator check"""
319 flowcell_id = self.fc1_id
320 raw_dir = os.path.join(settings.RESULT_HOME_DIR, flowcell_id)
322 fc = FlowCell.objects.get(flowcell_id=flowcell_id)
323 self.assertEqual(fc.get_raw_data_directory(), raw_dir)
325 fc.flowcell_id = flowcell_id + " (failed)"
326 self.assertEqual(fc.get_raw_data_directory(), raw_dir)
328 def test_sequencing_run_import(self):
329 srf_file_type = FileType.objects.get(name='SRF')
330 runxml_file_type = FileType.objects.get(name='run_xml')
331 flowcell_id = self.fc1_id
332 flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
333 flowcell.update_sequencing_runs()
334 self.assertEqual(len(flowcell.sequencingrun_set.all()), 1)
336 run = flowcell.sequencingrun_set.all()[0]
337 result_files = run.datafile_set.all()
338 result_dict = dict(((rf.relative_pathname, rf) for rf in result_files))
340 srf4 = result_dict['FC12150/C1-37/woldlab_070829_SERIAL_FC12150_4.srf']
341 self.assertEqual(srf4.file_type, srf_file_type)
342 self.assertEqual(srf4.library_id, '12154')
343 self.assertEqual(srf4.sequencing_run.flowcell.flowcell_id, 'FC12150')
345 srf4.sequencing_run.flowcell.lane_set.get(lane_number=4).library_id,
349 os.path.join(settings.RESULT_HOME_DIR, srf4.relative_pathname))
351 lane_files = run.lane_files()
352 self.assertEqual(lane_files[4]['srf'], srf4)
354 runxml= result_dict['FC12150/C1-37/run_FC12150_2007-09-27.xml']
355 self.assertEqual(runxml.file_type, runxml_file_type)
356 self.assertEqual(runxml.library_id, None)
358 import1 = len(SequencingRun.objects.filter(result_dir='FC12150/C1-37'))
359 # what happens if we import twice?
360 flowcell.import_sequencing_run('FC12150/C1-37',
361 'run_FC12150_2007-09-27.xml')
363 len(SequencingRun.objects.filter(result_dir='FC12150/C1-37')),
366 def test_read_result_file(self):
367 """make sure we can return a result file
369 flowcell_id = self.fc1_id
370 flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
371 flowcell.update_sequencing_runs()
373 #self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
375 result_files = flowcell.sequencingrun_set.all()[0].datafile_set.all()
376 for f in result_files:
377 url = '/experiments/file/%s' % ( f.random_key,)
378 response = self.client.get(url)
379 self.assertEqual(response.status_code, 200)
380 mimetype = f.file_type.mimetype
382 mimetype = 'application/octet-stream'
384 self.assertEqual(mimetype, response['content-type'])
386 def test_flowcell_rdf(self):
388 from htsworkflow.util.rdfhelp import get_model, \
390 load_string_into_model, \
397 expected = {'1': ['12151'],
405 url = '/flowcell/{}/'.format(self.fc12150.flowcell_id)
406 response = self.client.get(url)
407 self.assertEqual(response.status_code, 200)
408 status = validate_xhtml(response.content)
409 if status is not None: self.assertTrue(status)
411 ns = urljoin('http://localhost', url)
412 load_string_into_model(model, 'rdfa', smart_text(response.content), ns=ns)
413 body = """prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
414 prefix libns: <http://jumpgate.caltech.edu/wiki/LibraryOntology#>
416 select ?flowcell ?flowcell_id ?lane_id ?library_id
418 ?flowcell a libns:IlluminaFlowcell ;
419 libns:flowcell_id ?flowcell_id ;
420 libns:has_lane ?lane .
421 ?lane libns:lane_number ?lane_id ;
422 libns:library ?library .
423 ?library libns:library_id ?library_id .
425 query = RDF.SPARQLQuery(body)
427 for r in query.execute(model):
429 self.assertEqual(fromTypedNode(r['flowcell_id']), 'FC12150')
430 lane_id = fromTypedNode(r['lane_id'])
431 library_id = fromTypedNode(r['library_id'])
432 self.assertTrue(library_id in expected[lane_id])
433 self.assertEqual(count, 8)
435 class TestEmailNotify(TestCase):
437 self.password = 'foo27'
438 self.user = HTSUserFactory.create(username='test')
439 self.user.set_password(self.password)
441 self.admin = HTSUserFactory.create(username='admintest', is_staff=True)
442 self.admin.set_password(self.password)
444 self.super = HTSUserFactory.create(username='supertest', is_staff=True, is_superuser=True)
445 self.super.set_password(self.password)
448 self.library = LibraryFactory.create()
449 self.affiliation = AffiliationFactory()
450 self.affiliation.users.add(self.user)
451 self.library.affiliations.add(self.affiliation)
452 self.fc = FlowCellFactory.create()
453 self.lane = LaneFactory(flowcell=self.fc, lane_number=1, library=self.library)
455 self.url = '/experiments/started/{}/'.format(self.fc.id)
457 def test_started_email_not_logged_in(self):
458 response = self.client.get(self.url)
459 self.assertEqual(response.status_code, 302)
461 def test_started_email_logged_in_user(self):
462 self.assertTrue(self.client.login(username=self.user.username, password=self.password))
463 response = self.client.get(self.url)
464 self.assertEqual(response.status_code, 302)
466 def test_started_email_logged_in_staff(self):
467 self.assertTrue(self.admin.is_staff)
468 admin = HTSUser.objects.get(username=self.admin.username)
469 self.assertTrue(admin.is_staff)
470 self.assertTrue(admin.check_password(self.password))
471 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
472 response = self.client.get(self.url)
473 self.assertEqual(response.status_code, 200)
475 def test_started_email_send(self):
476 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
477 response = self.client.get(self.url)
478 self.assertEqual(response.status_code, 200)
480 self.assertTrue(self.affiliation.email in smart_text(response.content))
481 self.assertTrue(self.library.library_name in smart_text(response.content))
483 response = self.client.get(self.url, {'send':'1','bcc':'on'})
484 self.assertEqual(response.status_code, 200)
485 self.assertEqual(len(mail.outbox), 2)
486 bcc = set(settings.NOTIFICATION_BCC).copy()
487 bcc.update(set(settings.MANAGERS))
488 for m in mail.outbox:
489 self.assertTrue(len(m.body) > 0)
490 self.assertEqual(set(m.bcc), bcc)
492 def test_email_navigation(self):
494 Can we navigate between the flowcell and email forms properly?
496 admin_url = '/admin/experiments/flowcell/{}/'.format(self.fc.id)
497 self.client.login(username=self.admin.username, password=self.password)
498 response = self.client.get(self.url)
499 self.assertEqual(response.status_code, 200)
500 #print("email navigation content:", response.content)
501 self.assertTrue(re.search(self.fc.flowcell_id, smart_text(response.content)))
502 # require that navigation back to the admin page exists
503 self.assertTrue(re.search('<a href="{}">[^<]+</a>'.format(admin_url),
504 smart_text(response.content)))
506 def multi_lane_to_dict(lane):
507 """Convert a list of lane entries into a dictionary indexed by library ID
509 return dict( ((x['library_id'],x) for x in lane) )
511 class TestSequencer(TestCase):
513 self.fc12150 = FlowCellFactory(flowcell_id='FC12150')
514 self.library = LibraryFactory(id="12150")
515 self.lane = LaneFactory(flowcell=self.fc12150, lane_number=1, library=self.library)
517 def test_name_generation(self):
520 seq.instrument_name = "HWI-SEQ1"
521 seq.model = "Imaginary 5000"
523 self.assertEqual(str(seq), "Seq1 (HWI-SEQ1)")
525 def test_lookup(self):
527 self.assertEqual(fc.sequencer.model, 'HiSeq 1')
528 self.assertTrue(fc.sequencer.instrument_name.startswith('instrument name')),
529 # well actually we let the browser tack on the host name
530 url = fc.get_absolute_url()
531 self.assertEqual(url, '/flowcell/FC12150/')
534 response = self.client.get('/flowcell/FC12150/', apidata)
535 tree = fromstring(response.content)
536 seq_by = tree.xpath('//div[@rel="libns:sequenced_by"]',
538 self.assertEqual(len(seq_by), 1)
539 self.assertEqual(seq_by[0].attrib['rel'], 'libns:sequenced_by')
540 seq = seq_by[0].getchildren()
541 self.assertEqual(len(seq), 1)
542 sequencer = '/sequencer/' + str(self.fc12150.sequencer.id)
543 self.assertEqual(seq[0].attrib['about'], sequencer)
544 self.assertEqual(seq[0].attrib['typeof'], 'libns:Sequencer')
546 name = seq[0].xpath('./span[@property="libns:sequencer_name"]')
547 self.assertEqual(len(name), 1)
548 self.assertTrue(name[0].text.startswith('sequencer '))
549 instrument = seq[0].xpath(
550 './span[@property="libns:sequencer_instrument"]')
551 self.assertEqual(len(instrument), 1)
552 self.assertTrue(instrument[0].text.startswith('instrument name'))
553 model = seq[0].xpath(
554 './span[@property="libns:sequencer_model"]')
555 self.assertEqual(len(model), 1)
556 self.assertEqual(model[0].text, 'HiSeq 1')
558 def test_flowcell_with_rdf_validation(self):
559 from htsworkflow.util.rdfhelp import add_default_schemas, \
562 load_string_into_model
563 from htsworkflow.util.rdfinfer import Infer
566 add_default_schemas(model)
567 inference = Infer(model)
569 url ='/flowcell/FC12150/'
570 response = self.client.get(url)
571 self.assertEqual(response.status_code, 200)
572 status = validate_xhtml(response.content)
573 if status is not None: self.assertTrue(status)
575 load_string_into_model(model, 'rdfa', smart_text(response.content))
577 errmsgs = list(inference.run_validation())
578 self.assertEqual(len(errmsgs), 0)
580 def test_lane_with_rdf_validation(self):
581 from htsworkflow.util.rdfhelp import add_default_schemas, \
584 load_string_into_model
585 from htsworkflow.util.rdfinfer import Infer
588 add_default_schemas(model)
589 inference = Infer(model)
591 url = '/lane/{}'.format(self.lane.id)
592 response = self.client.get(url)
593 rdfbody = smart_text(response.content)
594 self.assertEqual(response.status_code, 200)
595 status = validate_xhtml(rdfbody)
596 if status is not None: self.assertTrue(status)
598 load_string_into_model(model, 'rdfa', rdfbody)
600 errmsgs = list(inference.run_validation())
601 self.assertEqual(len(errmsgs), 0)
604 from unittest import TestSuite, defaultTestLoader
606 for testcase in [ExerimentsTestCases,
609 suite.addTests(defaultTestLoader.loadTestsFromTestCase(testcase))
612 if __name__ == "__main__":
613 from unittest import main
614 main(defaultTest="suite")