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(
198 '/admin/experiments/flowcell/{}/change/'.format(self.fc12150.id),
200 self.assertEquals(response.status_code, 200)
202 tree = fromstring(response.content)
204 xpath_expression = '//input[@id="id_lane_set-%d-library"]'
205 input_field = tree.xpath(xpath_expression % (i,))[0]
206 library_field = input_field.find('../strong')
207 library_id, library_name = library_field.text.split(':')
208 # strip leading '#' sign from name
209 library_id = library_id[1:]
210 self.assertEqual(library_id, expected_ids[i])
211 self.assertEqual(input_field.attrib['value'], library_id)
213 def test_library_to_flowcell_link(self):
215 Make sure the library page includes links to the flowcell pages.
216 That work with flowcell IDs that have parenthetical comments.
218 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
219 response = self.client.get('/library/12151/')
220 self.assertEqual(response.status_code, 200)
221 status = validate_xhtml(response.content)
222 if status is not None: self.assertTrue(status)
224 tree = fromstring(response.content)
225 flowcell_spans = tree.xpath('//span[@property="libns:flowcell_id"]',
227 self.assertEqual(flowcell_spans[1].text, 'FC12150')
228 failed_fc_span = flowcell_spans[1]
229 failed_fc_a = failed_fc_span.getparent()
230 # make sure some of our RDF made it.
231 self.assertEqual(failed_fc_a.get('typeof'), 'libns:IlluminaFlowcell')
232 self.assertEqual(failed_fc_a.get('href'), '/flowcell/FC12150/')
233 fc_response = self.client.get(failed_fc_a.get('href'))
234 self.assertEqual(fc_response.status_code, 200)
235 status = validate_xhtml(response.content)
236 if status is not None: self.assertTrue(status)
238 fc_lane_response = self.client.get('/flowcell/FC12150/8/')
239 self.assertEqual(fc_lane_response.status_code, 200)
240 status = validate_xhtml(response.content)
241 if status is not None: self.assertTrue(status)
243 def test_pooled_multiplex_id(self):
244 fc_dict = flowcell_information(self.fc42jtn.flowcell_id)
246 lane_contents = fc_dict['lane_set'][2]
247 self.assertEqual(len(lane_contents), len(self.fc42jtn_lanes) / 2)
248 lane_dict = multi_lane_to_dict(lane_contents)
250 self.assertTrue(self.fc42jtn_lanes[0].library.multiplex_id in \
251 lane_dict['13001']['index_sequence'])
252 self.assertTrue(self.fc42jtn_lanes[2].library.multiplex_id in \
253 lane_dict['13003']['index_sequence'])
256 def test_lanes_for_view_user_odd(self):
257 """Make sure lanes_for HTML UI works.
259 user = self.user_odd.username
260 lanes = lanes_for(user)
261 self.assertEqual(len(lanes), 8)
263 response = self.client.get(
264 reverse('lanes_for', kwargs={'username': user}))
265 self.assertEqual(response.status_code, 200)
266 tree = fromstring(response.content)
267 lane_trs = tree.xpath('//div[@id="changelist"]/table/tbody/tr')
268 self.assertEqual(len(lane_trs), len(lanes))
269 # lanes is in db order
270 # lane_trs is in newest to oldest order
271 for lane_tr, lane_db in zip(lane_trs, lanes):
272 library_id = lane_tr.xpath('td[6]/a')[0].text
273 self.assertEqual(library_id, lane_db['library'])
275 def test_lanes_for_view_invalid_user(self):
276 """Make sure we don't find anything with an invalid user
278 response = self.client.get(
279 reverse('lanes_for', kwargs={'username': 'doesntexist'}))
280 self.assertEqual(response.status_code, 404)
282 def test_lanes_for_json(self):
284 Check the code that packs the django objects into simple types.
286 user = self.user_odd.username
287 lanes = lanes_for(user)
288 self.assertEqual(len(lanes), 8)
290 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
291 lanes_json = json.loads(smart_text(response.content))['result']
292 self.assertEqual(len(lanes), len(lanes_json))
293 for i in range(len(lanes)):
294 self.assertEqual(lanes[i]['comment'], lanes_json[i]['comment'])
295 self.assertEqual(lanes[i]['lane_number'], lanes_json[i]['lane_number'])
296 self.assertEqual(lanes[i]['flowcell'], lanes_json[i]['flowcell'])
297 self.assertEqual(lanes[i]['run_date'], lanes_json[i]['run_date'])
299 def test_lanes_for_no_lanes(self):
301 Do we get something meaningful back when the user isn't attached to anything?
303 user = HTSUserFactory.create(username='supertest')
304 lanes = lanes_for(user.username)
305 self.assertEqual(len(lanes), 0)
307 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
308 self.assertEqual(response.status_code, 404)
310 def test_lanes_for_no_user(self):
312 Do we get something meaningful back when its the wrong user
314 user = 'not a real user'
315 self.assertRaises(ObjectDoesNotExist, lanes_for, user)
317 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
318 self.assertEqual(response.status_code, 404)
320 def test_raw_data_dir(self):
321 """Raw data path generator check"""
322 flowcell_id = self.fc1_id
323 raw_dir = os.path.join(settings.RESULT_HOME_DIR, flowcell_id)
325 fc = FlowCell.objects.get(flowcell_id=flowcell_id)
326 self.assertEqual(fc.get_raw_data_directory(), raw_dir)
328 fc.flowcell_id = flowcell_id + " (failed)"
329 self.assertEqual(fc.get_raw_data_directory(), raw_dir)
331 def test_sequencing_run_import(self):
332 srf_file_type = FileType.objects.get(name='SRF')
333 runxml_file_type = FileType.objects.get(name='run_xml')
334 flowcell_id = self.fc1_id
335 flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
336 flowcell.update_sequencing_runs()
337 self.assertEqual(len(flowcell.sequencingrun_set.all()), 1)
339 run = flowcell.sequencingrun_set.all()[0]
340 result_files = run.datafile_set.all()
341 result_dict = dict(((rf.relative_pathname, rf) for rf in result_files))
343 srf4 = result_dict['FC12150/C1-37/woldlab_070829_SERIAL_FC12150_4.srf']
344 self.assertEqual(srf4.file_type, srf_file_type)
345 self.assertEqual(srf4.library_id, '12154')
346 self.assertEqual(srf4.sequencing_run.flowcell.flowcell_id, 'FC12150')
348 srf4.sequencing_run.flowcell.lane_set.get(lane_number=4).library_id,
352 os.path.join(settings.RESULT_HOME_DIR, srf4.relative_pathname))
354 lane_files = run.lane_files()
355 self.assertEqual(lane_files[4]['srf'], srf4)
357 runxml= result_dict['FC12150/C1-37/run_FC12150_2007-09-27.xml']
358 self.assertEqual(runxml.file_type, runxml_file_type)
359 self.assertEqual(runxml.library_id, None)
361 import1 = len(SequencingRun.objects.filter(result_dir='FC12150/C1-37'))
362 # what happens if we import twice?
363 flowcell.import_sequencing_run('FC12150/C1-37',
364 'run_FC12150_2007-09-27.xml')
366 len(SequencingRun.objects.filter(result_dir='FC12150/C1-37')),
369 def test_read_result_file(self):
370 """make sure we can return a result file
372 flowcell_id = self.fc1_id
373 flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
374 flowcell.update_sequencing_runs()
376 #self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
378 result_files = flowcell.sequencingrun_set.all()[0].datafile_set.all()
379 for f in result_files:
380 url = '/experiments/file/%s' % ( f.random_key,)
381 response = self.client.get(url)
382 self.assertEqual(response.status_code, 200)
383 mimetype = f.file_type.mimetype
385 mimetype = 'application/octet-stream'
387 self.assertEqual(mimetype, response['content-type'])
389 def test_flowcell_rdf(self):
391 from htsworkflow.util.rdfhelp import get_model, \
393 load_string_into_model, \
400 expected = {'1': ['12151'],
408 url = '/flowcell/{}/'.format(self.fc12150.flowcell_id)
409 response = self.client.get(url)
410 self.assertEqual(response.status_code, 200)
411 status = validate_xhtml(response.content)
412 if status is not None: self.assertTrue(status)
414 ns = urljoin('http://localhost', url)
415 load_string_into_model(model, 'rdfa', smart_text(response.content), ns=ns)
416 body = """prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
417 prefix libns: <http://jumpgate.caltech.edu/wiki/LibraryOntology#>
419 select ?flowcell ?flowcell_id ?lane_id ?library_id
421 ?flowcell a libns:IlluminaFlowcell ;
422 libns:flowcell_id ?flowcell_id ;
423 libns:has_lane ?lane .
424 ?lane libns:lane_number ?lane_id ;
425 libns:library ?library .
426 ?library libns:library_id ?library_id .
428 query = RDF.SPARQLQuery(body)
430 for r in query.execute(model):
432 self.assertEqual(fromTypedNode(r['flowcell_id']), 'FC12150')
433 lane_id = fromTypedNode(r['lane_id'])
434 library_id = fromTypedNode(r['library_id'])
435 self.assertTrue(library_id in expected[lane_id])
436 self.assertEqual(count, 8)
438 class TestEmailNotify(TestCase):
440 self.password = 'foo27'
441 self.user = HTSUserFactory.create(username='test')
442 self.user.set_password(self.password)
444 self.admin = HTSUserFactory.create(username='admintest', is_staff=True)
445 self.admin.set_password(self.password)
447 self.super = HTSUserFactory.create(username='supertest', is_staff=True, is_superuser=True)
448 self.super.set_password(self.password)
451 self.library = LibraryFactory.create()
452 self.affiliation = AffiliationFactory()
453 self.affiliation.users.add(self.user)
454 self.library.affiliations.add(self.affiliation)
455 self.fc = FlowCellFactory.create()
456 self.lane = LaneFactory(flowcell=self.fc, lane_number=1, library=self.library)
458 self.url = '/experiments/started/{}/'.format(self.fc.id)
460 def test_started_email_not_logged_in(self):
461 response = self.client.get(self.url)
462 self.assertEqual(response.status_code, 302)
464 def test_started_email_logged_in_user(self):
465 self.assertTrue(self.client.login(username=self.user.username, password=self.password))
466 response = self.client.get(self.url)
467 self.assertEqual(response.status_code, 302)
469 def test_started_email_logged_in_staff(self):
470 self.assertTrue(self.admin.is_staff)
471 admin = HTSUser.objects.get(username=self.admin.username)
472 self.assertTrue(admin.is_staff)
473 self.assertTrue(admin.check_password(self.password))
474 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
475 response = self.client.get(self.url)
476 self.assertEqual(response.status_code, 200)
478 def test_started_email_send(self):
479 self.assertTrue(self.client.login(username=self.admin.username, password=self.password))
480 response = self.client.get(self.url)
481 self.assertEqual(response.status_code, 200)
483 self.assertTrue(self.affiliation.email in smart_text(response.content))
484 self.assertTrue(self.library.library_name in smart_text(response.content))
486 response = self.client.get(self.url, {'send':'1','bcc':'on'})
487 self.assertEqual(response.status_code, 200)
488 self.assertEqual(len(mail.outbox), 2)
489 bcc = set(settings.NOTIFICATION_BCC).copy()
490 bcc.update(set(settings.MANAGERS))
491 for m in mail.outbox:
492 self.assertTrue(len(m.body) > 0)
493 self.assertEqual(set(m.bcc), bcc)
495 def test_email_navigation(self):
497 Can we navigate between the flowcell and email forms properly?
499 admin_url = '/admin/experiments/flowcell/{}/change/'.format(self.fc.id)
500 self.client.login(username=self.admin.username, password=self.password)
501 response = self.client.get(self.url)
502 self.assertEqual(response.status_code, 200)
503 self.assertTrue(re.search(self.fc.flowcell_id, smart_text(response.content)))
504 # require that navigation back to the admin page exists
505 admin_a_tag = '<a href="{}">[^<]+</a>'.format(admin_url)
506 self.assertTrue(re.search(admin_a_tag, smart_text(response.content)))
508 def multi_lane_to_dict(lane):
509 """Convert a list of lane entries into a dictionary indexed by library ID
511 return dict( ((x['library_id'],x) for x in lane) )
513 class TestSequencer(TestCase):
515 self.fc12150 = FlowCellFactory(flowcell_id='FC12150')
516 self.library = LibraryFactory(id="12150")
517 self.lane = LaneFactory(flowcell=self.fc12150, lane_number=1, library=self.library)
519 def test_name_generation(self):
522 seq.instrument_name = "HWI-SEQ1"
523 seq.model = "Imaginary 5000"
525 self.assertEqual(str(seq), "Seq1 (HWI-SEQ1)")
527 def test_lookup(self):
529 self.assertEqual(fc.sequencer.model, 'HiSeq 1')
530 self.assertTrue(fc.sequencer.instrument_name.startswith('instrument name')),
531 # well actually we let the browser tack on the host name
532 url = fc.get_absolute_url()
533 self.assertEqual(url, '/flowcell/FC12150/')
536 response = self.client.get('/flowcell/FC12150/', apidata)
537 tree = fromstring(response.content)
538 seq_by = tree.xpath('//div[@rel="libns:sequenced_by"]',
540 self.assertEqual(len(seq_by), 1)
541 self.assertEqual(seq_by[0].attrib['rel'], 'libns:sequenced_by')
542 seq = seq_by[0].getchildren()
543 self.assertEqual(len(seq), 1)
544 sequencer = '/sequencer/' + str(self.fc12150.sequencer.id)
545 self.assertEqual(seq[0].attrib['about'], sequencer)
546 self.assertEqual(seq[0].attrib['typeof'], 'libns:Sequencer')
548 name = seq[0].xpath('./span[@property="libns:sequencer_name"]')
549 self.assertEqual(len(name), 1)
550 self.assertTrue(name[0].text.startswith('sequencer '))
551 instrument = seq[0].xpath(
552 './span[@property="libns:sequencer_instrument"]')
553 self.assertEqual(len(instrument), 1)
554 self.assertTrue(instrument[0].text.startswith('instrument name'))
555 model = seq[0].xpath(
556 './span[@property="libns:sequencer_model"]')
557 self.assertEqual(len(model), 1)
558 self.assertEqual(model[0].text, 'HiSeq 1')
560 def test_flowcell_with_rdf_validation(self):
561 from htsworkflow.util.rdfhelp import add_default_schemas, \
564 load_string_into_model
565 from htsworkflow.util.rdfinfer import Infer
568 add_default_schemas(model)
569 inference = Infer(model)
571 url ='/flowcell/FC12150/'
572 response = self.client.get(url)
573 self.assertEqual(response.status_code, 200)
574 status = validate_xhtml(response.content)
575 if status is not None: self.assertTrue(status)
577 load_string_into_model(model, 'rdfa', smart_text(response.content))
579 errmsgs = list(inference.run_validation())
580 self.assertEqual(len(errmsgs), 0)
582 def test_lane_with_rdf_validation(self):
583 from htsworkflow.util.rdfhelp import add_default_schemas, \
586 load_string_into_model
587 from htsworkflow.util.rdfinfer import Infer
590 add_default_schemas(model)
591 inference = Infer(model)
593 url = '/lane/{}'.format(self.lane.id)
594 response = self.client.get(url)
595 rdfbody = smart_text(response.content)
596 self.assertEqual(response.status_code, 200)
597 status = validate_xhtml(rdfbody)
598 if status is not None: self.assertTrue(status)
600 load_string_into_model(model, 'rdfa', rdfbody)
602 errmsgs = list(inference.run_validation())
603 self.assertEqual(len(errmsgs), 0)
606 from unittest import TestSuite, defaultTestLoader
608 for testcase in [ExerimentsTestCases,
611 suite.addTests(defaultTestLoader.loadTestsFromTestCase(testcase))
614 if __name__ == "__main__":
615 from unittest import main
616 main(defaultTest="suite")