test_lmdb_store.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. import pytest
  2. from shutil import rmtree
  3. from rdflib import Namespace, URIRef
  4. from rdflib.namespace import RDF, RDFS
  5. from lakesuperior.store_layouts.ldp_rs.lmdb_store import LmdbStore, TxnManager
  6. @pytest.fixture(scope='class')
  7. def store():
  8. store = LmdbStore('/tmp/test_lmdbstore')
  9. yield store
  10. store.close()
  11. rmtree('/tmp/test_lmdbstore')
  12. @pytest.mark.usefixtures('store')
  13. class TestStoreInit:
  14. '''
  15. Tests for intializing and shutting down store and transactions.
  16. '''
  17. def test_open_close(self, store):
  18. '''
  19. Test opening and closing a store.
  20. '''
  21. tmpstore = LmdbStore('/tmp/test_lmdbstore_init')
  22. assert tmpstore.is_open
  23. tmpstore.close()
  24. assert not tmpstore.is_open
  25. def test_txn(self, store):
  26. '''
  27. Test opening and closing the main transaction.
  28. '''
  29. store.begin(True)
  30. assert store.is_txn_open
  31. store.commit()
  32. assert not store.is_txn_open
  33. store.begin(True)
  34. store.rollback()
  35. assert not store.is_txn_open
  36. def test_ctx_mgr(self, store):
  37. '''
  38. Test enclosing a transaction in a context.
  39. '''
  40. with TxnManager(store) as txn:
  41. pass
  42. assert not store.is_txn_open
  43. with TxnManager(store) as txn:
  44. raise RuntimeError
  45. assert not store.is_txn_open
  46. def test_rollback(self, store):
  47. '''
  48. Test rolling back a transaction.
  49. '''
  50. with TxnManager(store, True) as txn:
  51. store.add((
  52. URIRef('urn:nogo:s'), URIRef('urn:nogo:p'),
  53. URIRef('urn:nogo:o')))
  54. raise RuntimeError # This should roll back the transaction.
  55. with TxnManager(store) as txn:
  56. res = set(store.triples((None, None, None)))
  57. assert len(res) == 0
  58. @pytest.mark.usefixtures('store')
  59. class TestBasicOps:
  60. '''
  61. High-level tests for basic operations.
  62. '''
  63. def test_create_triple(self, store):
  64. '''
  65. Test creation of a single triple.
  66. '''
  67. trp = (
  68. URIRef('urn:test:s'), URIRef('urn:test:p'), URIRef('urn:test:o'))
  69. with TxnManager(store, True) as txn:
  70. store.add(trp)
  71. #import pdb; pdb.set_trace()
  72. res1 = set(store.triples((None, None, None)))
  73. res2 = set(store.triples(trp))
  74. assert len(res1) == 1
  75. assert len(res2) == 1
  76. assert trp in res1 & res2
  77. def test_triple_match_1bound(self, store):
  78. '''
  79. Test triple patterns matching one bound term.
  80. '''
  81. with TxnManager(store) as txn:
  82. res1 = set(store.triples((URIRef('urn:test:s'), None, None)))
  83. res2 = set(store.triples((None, URIRef('urn:test:p'), None)))
  84. res3 = set(store.triples((None, None, URIRef('urn:test:o'))))
  85. assert res1 == {(
  86. URIRef('urn:test:s'), URIRef('urn:test:p'),
  87. URIRef('urn:test:o'))}
  88. assert res2 == res1
  89. assert res3 == res2
  90. def test_triple_match_2bound(self, store):
  91. '''
  92. Test triple patterns matching two bound terms.
  93. '''
  94. with TxnManager(store) as txn:
  95. res1 = set(store.triples(
  96. (URIRef('urn:test:s'), URIRef('urn:test:p'), None)))
  97. res2 = set(store.triples(
  98. (URIRef('urn:test:s'), None, URIRef('urn:test:o'))))
  99. res3 = set(store.triples(
  100. (None, URIRef('urn:test:p'), URIRef('urn:test:o'))))
  101. assert res1 == {(
  102. URIRef('urn:test:s'), URIRef('urn:test:p'), URIRef('urn:test:o'))}
  103. assert res2 == res1
  104. assert res3 == res2
  105. def test_triple_no_match(self, store):
  106. '''
  107. Test various mismatches.
  108. '''
  109. with TxnManager(store, True) as txn:
  110. store.add((
  111. URIRef('urn:test:s'),
  112. URIRef('urn:test:p2'), URIRef('urn:test:o2')))
  113. store.add((
  114. URIRef('urn:test:s3'),
  115. URIRef('urn:test:p3'), URIRef('urn:test:o3')))
  116. res1 = set(store.triples((None, None, None)))
  117. assert len(res1) == 3
  118. res1 = set(store.triples(
  119. (URIRef('urn:test:s2'), URIRef('urn:test:p'), None)))
  120. res2 = set(store.triples(
  121. (URIRef('urn:test:s3'), None, URIRef('urn:test:o'))))
  122. res3 = set(store.triples(
  123. (None, URIRef('urn:test:p3'), URIRef('urn:test:o2'))))
  124. assert len(res1) == len(res2) == len(res3) == 0
  125. def test_remove(self, store):
  126. '''
  127. Test removing one or more triples.
  128. '''
  129. with TxnManager(store, True) as txn:
  130. store.remove((URIRef('urn:test:s3'),
  131. URIRef('urn:test:p3'), URIRef('urn:test:o3')))
  132. res1 = set(store.triples((None, None, None)))
  133. assert len(res1) == 2
  134. store.remove((URIRef('urn:test:s'), None, None))
  135. res2 = set(store.triples((None, None, None)))
  136. assert len(res2) == 0
  137. @pytest.mark.usefixtures('store')
  138. class TestBindings:
  139. '''
  140. Tests for namespace bindings.
  141. '''
  142. @pytest.fixture
  143. def bindings(self):
  144. return (
  145. ('ns1', Namespace('http://test.org/ns#')),
  146. ('ns2', Namespace('http://my_org.net/ns#')),
  147. ('ns3', Namespace('urn:test:')),
  148. ('ns4', Namespace('info:myinst/graph#')),
  149. )
  150. def test_ns(self, store, bindings):
  151. '''
  152. Test namespace bindings.
  153. '''
  154. with TxnManager(store, True) as txn:
  155. for b in bindings:
  156. store.bind(*b)
  157. nslist = list(store.namespaces())
  158. assert len(nslist) == len(bindings)
  159. for i in range(len(bindings)):
  160. assert nslist[i] == bindings[i]
  161. def test_ns2pfx(self, store, bindings):
  162. '''
  163. Test namespace to prefix conversion.
  164. '''
  165. with TxnManager(store, True) as txn:
  166. for b in bindings:
  167. pfx, ns = b
  168. assert store.namespace(pfx) == ns
  169. def test_pfx2ns(self, store, bindings):
  170. '''
  171. Test namespace to prefix conversion.
  172. '''
  173. with TxnManager(store, True) as txn:
  174. for b in bindings:
  175. pfx, ns = b
  176. assert store.prefix(ns) == pfx
  177. @pytest.mark.usefixtures('store')
  178. class TestContext:
  179. '''
  180. Tests for context handling.
  181. '''
  182. def test_add_graph(self, store):
  183. '''
  184. Test creating an empty and a non-empty graph.
  185. '''
  186. gr_uri = URIRef('urn:bogus:graph#a')
  187. with TxnManager(store, True) as txn:
  188. store.add_graph(gr_uri)
  189. assert gr_uri in store.contexts()
  190. def test_empty_context(self, store):
  191. '''
  192. Test creating and deleting empty contexts.
  193. '''
  194. gr_uri = URIRef('urn:bogus:empty#a')
  195. with TxnManager(store, True) as txn:
  196. store.add_graph(gr_uri)
  197. assert gr_uri in store.contexts()
  198. store.ermove_graph(gr_uri)
  199. assert gr_uri not in store.contexts()
  200. def test_add_trp_to_ctx(self, store):
  201. '''
  202. Test adding triples to a graph.
  203. '''
  204. gr_uri = URIRef('urn:bogus:graph#a') # From previous test
  205. gr2_uri = URIRef('urn:bogus:graph#b') # Never created before
  206. trp1 = (URIRef('urn:s:1'), URIRef('urn:p:1'), URIRef('urn:o:1'))
  207. trp2 = (URIRef('urn:s:2'), URIRef('urn:p:2'), URIRef('urn:o:2'))
  208. trp3 = (URIRef('urn:s:3'), URIRef('urn:p:3'), URIRef('urn:o:3'))
  209. with TxnManager(store, True) as txn:
  210. store.add(trp1, gr_uri)
  211. store.add(trp2, gr_uri)
  212. store.add(trp2, store.DEFAULT_GRAPH_URI)
  213. store.add(trp3, gr2_uri)
  214. store.add(trp3)
  215. assert len(set(store.triples((None, None, None)))) == 2
  216. assert len(set(store.triples((None, None, None), gr_uri))) == 2
  217. assert len(set(store.triples((None, None, None), gr2_uri))) == 1
  218. assert gr2_uri in store.contexts()
  219. assert trp1 not in store.triples((None, None, None))
  220. assert trp1 not in store.triples((None, None, None),
  221. store.DEFAULT_GRAPH_URI)
  222. assert trp2 in store.triples((None, None, None), gr_uri)
  223. assert trp2 in store.triples((None, None, None))
  224. assert trp3 in store.triples((None, None, None), gr2_uri)
  225. assert trp3 in store.triples((None, None, None),
  226. store.DEFAULT_GRAPH_URI)
  227. def test_delete_from_ctx(self, store):
  228. '''
  229. Delete triples from a named graph and from the default graph.
  230. '''
  231. gr_uri = URIRef('urn:bogus:graph#a')
  232. gr2_uri = URIRef('urn:bogus:graph#b')
  233. with TxnManager(store, True) as txn:
  234. store.remove((None, None, None))
  235. store.remove((None, None, None), gr2_uri)
  236. assert len(set(store.triples((None, None, None)))) == 0
  237. assert len(set(store.triples((None, None, None), gr_uri))) == 2
  238. assert len(set(store.triples((None, None, None), gr2_uri))) == 0
  239. @pytest.mark.usefixtures('store')
  240. class TestTransactions:
  241. '''
  242. Tests for transaction handling.
  243. '''
  244. # @TODO Test concurrent reads and writes.
  245. pass
  246. #@pytest.mark.usefixtures('store')
  247. #class TestRdflib:
  248. # '''
  249. # Test case adapted from
  250. # http://rdflib.readthedocs.io/en/stable/univrdfstore.html#interface-test-cases
  251. # '''
  252. #
  253. # @pytest.fixture
  254. # def sample_gr(self):
  255. # return Graph().parse('''
  256. # @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
  257. # @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
  258. # @prefix : <http://test/> .
  259. # {:a :b :c; a :foo} => {:a :d :c} .
  260. # _:foo a rdfs:Class .
  261. # :a :d :c .
  262. # ''', format='n3')
  263. #
  264. # def _test_basic(self, sample_gr):
  265. # with TxnManager as txn:
  266. # implies = URIRef("http://www.w3.org/2000/10/swap/log#implies")
  267. # a = URIRef('http://test/a')
  268. # b = URIRef('http://test/b')
  269. # c = URIRef('http://test/c')
  270. # d = URIRef('http://test/d')
  271. # for s,p,o in g.triples((None,implies,None)):
  272. # formulaA = s
  273. # formulaB = o
  274. #
  275. # #contexts test
  276. # assert len(list(g.contexts()))==3
  277. #
  278. # #contexts (with triple) test
  279. # assert len(list(g.contexts((a,d,c))))==2
  280. #
  281. # #triples test cases
  282. # assert type(list(g.triples(
  283. # (None,RDF.type,RDFS.Class)))[0][0]) == BNode
  284. # assert len(list(g.triples((None,implies,None))))==1
  285. # assert len(list(g.triples((None,RDF.type,None))))==3
  286. # assert len(list(g.triples((None,RDF.type,None),formulaA)))==1
  287. # assert len(list(g.triples((None,None,None),formulaA)))==2
  288. # assert len(list(g.triples((None,None,None),formulaB)))==1
  289. # assert len(list(g.triples((None,None,None))))==5
  290. # assert len(list(g.triples(
  291. # (None,URIRef('http://test/d'),None),formulaB)))==1
  292. # assert len(list(g.triples(
  293. # (None,URIRef('http://test/d'),None))))==1
  294. #
  295. # #Remove test cases
  296. # g.remove((None,implies,None))
  297. # assert len(list(g.triples((None,implies,None))))==0
  298. # assert len(list(g.triples((None,None,None),formulaA)))==2
  299. # assert len(list(g.triples((None,None,None),formulaB)))==1
  300. # g.remove((None,b,None),formulaA)
  301. # assert len(list(g.triples((None,None,None),formulaA)))==1
  302. # g.remove((None,RDF.type,None),formulaA)
  303. # assert len(list(g.triples((None,None,None),formulaA)))==0
  304. # g.remove((None,RDF.type,RDFS.Class))
  305. #
  306. # #remove_context tests
  307. # formulaBContext=Context(g,formulaB)
  308. # g.remove_context(formulaB)
  309. # assert len(list(g.triples((None,RDF.type,None))))==2
  310. # assert len(g)==3 assert len(formulaBContext)==0
  311. # g.remove((None,None,None))
  312. # assert len(g)==0