/[volute]/trunk/projects/semantics/Vocabularies/test_revovo.py
ViewVC logotype

Contents of /trunk/projects/semantics/Vocabularies/test_revovo.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5620 - (show annotations)
Thu Sep 5 13:11:54 2019 UTC (2 years ago) by msdemlei
File MIME type: text/x-python
File size: 14882 byte(s)
Re-organising the semantics area with better directory labels in preparation
for Voc 2.0 upload (which needs the old "Vocabularies" name.


1 #!/usr/bin/python3
2
3 # This is a little test suite for the VO vocabulary handling software.
4 # For terms and conditions, see revovo.py
5
6 import itertools
7 import unittest
8 from io import StringIO
9
10 from revovo import ValidatingVocabulary, Vocabulary
11
12
13 _XML_GEN_TEMPLATE = """
14 <rdf:RDF
15 xmlns:dc="http://purl.org/dc/terms/"
16 xmlns:foaf="http://xmlns.com/foaf/0.1/"
17 xmlns:ivoasem="http://www.ivoa.net/rdf/ivoasem#"
18 xmlns:owl="http://www.w3.org/2002/07/owl#"
19 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
20 xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
21 xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
22 xmlns:skos="http://www.w3.org/2004/02/skos/core#"
23 xmlns="http://www.ivoa.net/rdf/test#">
24 <rdf:Description rdf:nodeID="genid1">
25 <foaf:name>Demleitner, M.</foaf:name>
26 </rdf:Description>
27 <rdf:Description rdf:about="http://www.ivoa.net/rdf/test">
28 <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Ontology"/>
29 </rdf:Description>
30 <rdf:Description rdf:about="http://www.ivoa.net/rdf/test">
31 <dc:created>2019-08-30</dc:created>
32 <dc:creator rdf:nodeID="genid1"/>
33 <rdfs:label>A (mostly broken) test vocabulary</rdfs:label>
34 <dc:title>Test madness</dc:title>
35 <dc:description>This is just for testing.</dc:description>
36 {}
37 </rdf:Description>
38 {{}}
39 </rdf:RDF>
40 """
41
42 XML_PROP_TEMPLATE = _XML_GEN_TEMPLATE.format(
43 "<ivoasem:vocflavour>RDF Property</ivoasem:vocflavour>")
44
45 XML_CLASS_TEMPLATE = _XML_GEN_TEMPLATE.format(
46 "<ivoasem:vocflavour>RDF Class</ivoasem:vocflavour>")
47
48 XML_SKOS_TEMPLATE = _XML_GEN_TEMPLATE.format(
49 "<ivoasem:vocflavour>SKOS</ivoasem:vocflavour>")
50
51
52 # some triples to have some basic terms to play with in rdfs tests
53 COMMON_RDFS_TRIPLES = [
54 ("tv:test", "rdfs:label", "My first term"),
55 ("tv:test", "rdfs:comment", "No semantics attached"),
56 ("tv:test", "ivoasem:deprecated", None),
57 ("tv:test", "ivoasem:useInstead", "tv:second"),
58 ("tv:second", "rdfs:label", "My second term"),
59 ("tv:second", "rdfs:comment", "Means nothing either"),
60 ("tv:stinky", "rdfs:label", "Old and boring"),
61 ("tv:stinky", "rdfs:comment", "We shouldn't have done this."),
62 ("tv:stinky", "ivoasem:deprecated", None),
63 ("tv:experimental", "rdfs:label", "A proposed term"),
64 ("tv:experimental", "rdfs:comment", "Under construction"),
65 ("tv:experimental", "ivoasem:preliminary", None),
66 ]
67
68
69 # some triples to have some basic terms to play with in skos tests
70 COMMON_SKOS_TRIPLES = [
71 ("tv:stinky", "skos:broader", "tv:test"),
72 ("tv:test", "skos:prefLabel", "My first term"),
73 ("tv:test", "skos:definition", "No semantics attached"),
74 ("tv:test", "ivoasem:deprecated", None),
75 ("tv:test", "ivoasem:useInstead", "tv:second"),
76 ("tv:second", "skos:prefLabel", "My second term"),
77 ("tv:second", "skos:definition", "Means nothing either"),
78 ("tv:stinky", "skos:prefLabel", "Old and boring"),
79 ("tv:stinky", "skos:definition",
80 "We shouldn't have done this."),
81 ("tv:stinky", "ivoasem:deprecated", None),
82 ("tv:experimental", "skos:prefLabel", "A proposed term"),
83 ("tv:experimental", "skos:definition", "Under construction"),
84 ("tv:experimental", "ivoasem:preliminary", None),
85 ("tv:experimental", "skos:broader", "tv:test"),
86 ("tv:experimental", "skos:broader", "tv:second"),
87 ]
88
89
90 def make_declarations(triples, term_class,
91 voc_uri="http://www.ivoa.net/rdf/test#"):
92 """builds (in a very native way) RDF/XML declarations of the
93 triples.
94
95 We assume our standard prefixes.
96 """
97 res = []
98 for s, triples_for_s in itertools.groupby(triples, lambda v: v[0]):
99 if s is None:
100 s = "tv:__"
101 children = []
102 for _, p, o in triples_for_s:
103 if o is None:
104 o = "tv:__"
105 s = s.replace("tv:", voc_uri)
106 o = o.replace("tv:", voc_uri)
107 children.append(
108 ' <{}>{}</{}>'.format(p, o, p))
109 res.extend([
110 '<{} rdf:about="{}">'.format(term_class, s),
111 '\n '.join(children),
112 '</{}>'.format(term_class)])
113 return "\n".join(res)
114
115
116 class LoadingTest(unittest.TestCase):
117
118 def _assert_common(self, voc):
119 # common assertions for the all flavours
120 self.assertEqual(voc.uri, "http://www.ivoa.net/rdf/test")
121 self.assertEqual(voc.terms["test"],
122 ("My first term", "No semantics attached"))
123 self.assertEqual(len(voc.terms), 4)
124 self.assertEqual(voc.deprecated_terms["test"], ["second"])
125 self.assertEqual(voc.deprecated_terms["stinky"], [])
126 self.assertEqual(voc.preliminary_terms, set(["experimental"]))
127 self.assertEqual(voc.wider_terms["stinky"], ["test"])
128 self.assertEqual(voc.errors, [])
129
130 def test_loading_prop(self):
131 voc = Vocabulary.from_file(StringIO(
132 XML_PROP_TEMPLATE.format(make_declarations([
133 ("tv:stinky", "rdfs:subPropertyOf", "tv:test"),
134 ]+COMMON_RDFS_TRIPLES, "rdf:Property"))))
135 self.assertEqual(voc.flavour, "RDF Property")
136 self._assert_common(voc)
137
138 def test_loading_class(self):
139 voc = Vocabulary.from_file(StringIO(
140 XML_CLASS_TEMPLATE.format(make_declarations([
141 ("tv:stinky", "rdfs:subClassOf", "tv:test"),
142 ]+COMMON_RDFS_TRIPLES, "rdfs:Class"))))
143 self.assertEqual(voc.flavour, "RDF Class")
144 self._assert_common(voc)
145
146 def test_loading_skos(self):
147 voc = Vocabulary.from_file(StringIO(
148 XML_SKOS_TEMPLATE.format(make_declarations(
149 COMMON_SKOS_TRIPLES, "skos:Concept"))))
150 self.assertEqual(voc.flavour, "SKOS")
151 self.assertEqual(voc.wider_terms["experimental"],
152 ["test", "second"])
153 self._assert_common(voc)
154
155 def test_loading_from_Description(self):
156 voc = Vocabulary.from_file(StringIO(
157 XML_PROP_TEMPLATE.format(make_declarations(
158 COMMON_RDFS_TRIPLES, "rdf:Property")
159 +'<rdf:Declaration'
160 ' rdf:about="http://www.ivoa.net/rdf/test#stinky">'
161 '<rdfs:subPropertyOf'
162 ' rdf:resource="http://www.ivoa.net/rdf/test#test"/>'
163 '</rdf:Declaration>')))
164 self._assert_common(voc)
165
166
167 class ValidationTest(unittest.TestCase):
168 def test_mixed_term_types_error(self):
169 voc = ValidatingVocabulary.from_file(StringIO(
170 XML_PROP_TEMPLATE.format(make_declarations(
171 COMMON_RDFS_TRIPLES, "rdf:Property")
172 +'<skos:Concept rdf:about="http://www.ivoa.net/rdf/test#extra">'
173 '<skos:prefLabel>skos term</skos:prefLabel>'
174 '<skos:definition>Must raise an error because it is skos'
175 '</skos:definition>'
176 '</skos:Concept>')))
177 self.assertEqual(voc.errors,
178 ['http://www.ivoa.net/rdf/test#extra has type skos:Concept, which'
179 ' is forbidden in RDF Property vocabularies'])
180
181 def test_skos_mixed_term_types_error(self):
182 voc = ValidatingVocabulary.from_file(StringIO(
183 XML_SKOS_TEMPLATE.format(make_declarations(
184 COMMON_SKOS_TRIPLES, "skos:Concept")
185 +'<rdf:Property rdf:about="http://www.ivoa.net/rdf/test#extra">'
186 '<rdfs:label>skos term</rdfs:label>'
187 '<rdfs:comment>Must raise an error because it is a property'
188 '</rdfs:comment>'
189 '</rdf:Property>'
190 +'<rdfs:Class rdf:about="http://www.ivoa.net/rdf/test#cls">'
191 '<rdfs:label>a class</rdfs:label>'
192 '<rdfs:comment>Must raise an error because it is a class'
193 '</rdfs:comment>'
194 '</rdfs:Class>'
195 )))
196 self.assertEqual(set(voc.errors), {
197 'http://www.ivoa.net/rdf/test#extra has type rdf:Property, which'
198 ' is forbidden in SKOS vocabularies',
199 'http://www.ivoa.net/rdf/test#cls has type rdfs:Class, which'
200 ' is forbidden in SKOS vocabularies',})
201
202 def test_forbidden_property_warnings(self):
203 # the spec doesn't literally forbid rdfs:label (say) on SKOS stuff,
204 # but it certainly is fishy if it's there.
205 voc = ValidatingVocabulary.from_file(StringIO(
206 XML_PROP_TEMPLATE.format(make_declarations(
207 COMMON_RDFS_TRIPLES+[
208 ("tv:test", "skos:broader", "tv:second"),
209 ("tv:stinky", "rdfs:subClassOf", "tv:experimental"),],
210 "rdf:Property"))))
211 self.assertEqual(set(voc.warnings), {
212 'Forbidden triple in RDF Property vocabularies: http://www.ivoa.'
213 'net/rdf/test#stinky rdfs:subClassOf http://www.ivoa.net/rdf/'
214 'test#experimental',
215 'Forbidden triple in RDF Property vocabularies: http://www.ivoa.'
216 'net/rdf/test#test skos:broader http://www.ivoa.net/rdf/'
217 'test#second',})
218
219 def test_skos_forbidden_property_warnings(self):
220 voc = ValidatingVocabulary.from_file(StringIO(
221 XML_SKOS_TEMPLATE.format(make_declarations(
222 COMMON_SKOS_TRIPLES+[
223 ('tv:test', 'rdfs:subPropertyOf', 'tv:stinky'),
224 ('tv:test', 'rdfs:subClassOf', 'tv:second')],
225 "skos:Concept"))))
226 self.assertEqual(set(voc.warnings), {
227 'Forbidden triple in SKOS vocabularies:'
228 ' http://www.ivoa.net/rdf/test#'
229 'test rdfs:subClassOf http://www.ivoa.net/rdf/test#second',
230 'Forbidden triple in SKOS vocabularies:'
231 ' http://www.ivoa.net/rdf/test#'
232 'test rdfs:subPropertyOf http://www.ivoa.net/rdf/test#stinky',
233 })
234
235 def test_incomplete_definition_errors(self):
236 voc = ValidatingVocabulary.from_file(StringIO(
237 XML_CLASS_TEMPLATE.format(make_declarations(
238 COMMON_RDFS_TRIPLES+[
239 ('tv:labelOnly', 'rdfs:label', 'Missing Def'),
240 ('tv:defOnly', 'rdfs:comment', 'Missing Label'),],
241 'rdfs:Class'))))
242 self.assertEqual(set(voc.errors), {
243 'Term labelOnly has no definition.', 'Term defOnly has no label.'
244 })
245
246 def test_recursive_definition_warning(self):
247 voc = ValidatingVocabulary.from_file(StringIO(
248 XML_CLASS_TEMPLATE.format(make_declarations(
249 COMMON_RDFS_TRIPLES+[
250 ('tv:testCase', 'rdfs:label', 'Test Case'),
251 ('tv:testCase', 'rdfs:comment', 'A term used in'
252 ' a test case.'),],
253 'rdfs:Class'))))
254 self.assertEqual(voc.warnings, [
255 'Term testCase repeats its label or fragment in its definition.'
256 ])
257
258 def test_no_typed_node_error(self):
259 voc = ValidatingVocabulary.from_file(StringIO(
260 XML_CLASS_TEMPLATE.format(make_declarations(
261 COMMON_RDFS_TRIPLES, 'rdfs:Class')
262 +"""<rdf:Description
263 rdf:about="http://www.ivoa.net/rdf/test#tech">
264 <rdf:type rdf:resource=
265 "http://www.w3.org/2000/01/rdf-schema#Class"/>
266 <rdfs:label>a label</rdfs:label>
267 <rdfs:comment>shut up the validator</rdfs:comment>
268 </rdf:Description>"""
269 # rdf:type Property is not an error as such in Class
270 # vocabularies (it's wrong because of type purity,
271 # but that's a different error).
272 +"""<rdf:Description rdf:about="http://www.ivoa.net/rdf/test#p">
273 <rdf:type rdf:resource=
274 "http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
275 <rdfs:label>another label</rdfs:label>
276 <rdfs:comment>shut up the validator again</rdfs:comment>
277 </rdf:Description>"""
278 )))
279 self.assertEqual(set(voc.errors), {
280 'http://www.ivoa.net/rdf/test#p has type rdf:Property,'
281 ' which is forbidden in RDF Class vocabularies',
282 'Term tech not defined through a typed node'})
283
284 def test_non_ivoa_uri_errors(self):
285 voc = ValidatingVocabulary.from_file(StringIO(
286 XML_CLASS_TEMPLATE.format(make_declarations(
287 COMMON_RDFS_TRIPLES, 'rdfs:Class')).replace(
288 "http://www.ivoa.net/rdf/test", "http://test.voc")))
289 self.assertEqual(voc.errors, [
290 "Vocabulary URI http://test.voc does not start with"
291 " the canonical IVOA vocabulary URI root."])
292
293 def test_uri_with_hierarchy_warnings(self):
294 voc = ValidatingVocabulary.from_file(StringIO(
295 XML_CLASS_TEMPLATE.format(make_declarations(
296 COMMON_RDFS_TRIPLES, 'rdfs:Class')).replace(
297 "http://www.ivoa.net/rdf/test",
298 "http://www.ivoa.net/rdf/maint/test")))
299 self.assertEqual(voc.warnings, [
300 "Vocabularies URIs should not introduce"
301 " additional hierarchy below w.i.n/rdf."])
302
303 def test_bad_term_name_errors(self):
304 voc = ValidatingVocabulary.from_file(StringIO(
305 XML_CLASS_TEMPLATE.format(make_declarations(
306 COMMON_RDFS_TRIPLES+[
307 ('tv:$ANYTHING', 'rdfs:label', 'wildcard'),
308 ('tv:$ANYTHING', 'rdfs:comment', 'This is invalid'),
309 ], 'rdfs:Class'))))
310 self.assertEqual(voc.errors, [
311 "IVOA terms can only contain ASCII letters, digits, underscores,"
312 " and dashes; $ANYTHING has '$'"])
313
314 def test_nontree_errors(self):
315 voc = ValidatingVocabulary.from_file(StringIO(
316 XML_CLASS_TEMPLATE.format(make_declarations(
317 COMMON_RDFS_TRIPLES+[
318 ('tv:test', 'rdfs:subClassOf', 'tv:second'),
319 ('tv:test', 'rdfs:subClassOf', 'tv:stinky'),
320 ], 'rdfs:Class'))))
321 self.assertEqual(voc.errors, [
322 'Terms in non-SKOS vocabularies may only have up to one'
323 ' wider term, but test has second, stinky.'])
324
325
326 def main():
327 # for simpler development: you can pass two args
328 # to only run specific tests from specific test cases.
329 import os, sys
330 if len(sys.argv)==3:
331 className = sys.argv[1].split(".")[-1]
332 testClass = getattr(sys.modules["__main__"], className)
333 methodPrefix = sys.argv[2]
334 suite = unittest.makeSuite(testClass, methodPrefix)
335 else: # emulate unittest.run behaviour
336 suite = unittest.TestLoader().loadTestsFromModule(
337 sys.modules["__main__"])
338
339 runner = unittest.TextTestRunner(
340 verbosity=int(os.environ.get("TEST_VERBOSITY", 1)))
341 runner.run(suite)
342
343
344 if __name__=="__main__":
345 main()
346
347 # vim:et:sta:sw=4

Properties

Name Value
svn:executable *

msdemlei@ari.uni-heidelberg.de
ViewVC Help
Powered by ViewVC 1.1.26