From ca7ee6c59d2eb3f4ec4d16e392d12d946cd85e4d Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 22 Dec 2022 20:33:00 +0100 Subject: filter-ast based get interface in graph. * Graph interface: Graph.get added * Node instance resolver so that Nodes can be used in a filter ast * AC interface: filter_read added to interface * upstream test adjustments of previous sparql store changes --- test/graph/test_resolve.py | 181 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 test/graph/test_resolve.py (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py new file mode 100644 index 0000000..5bc99e4 --- /dev/null +++ b/test/graph/test_resolve.py @@ -0,0 +1,181 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import unittest + +# bsie imports +from bsfs import schema as bsc +from bsfs.graph import Graph, nodes +from bsfs.namespace import ns +from bsfs.query import ast +from bsfs.triple_store import SparqlStore +from bsfs.utils import URI, errors + +# objects to test +from bsfs.graph.resolve import Filter + + +## code ## + +class TestFilter(unittest.TestCase): + """ + + NOTE: The Filter resolver is relatively simple as it only checks and changes + ast.filter.Is instances. Hence, we don't test all methods individually but + all of them with respect to ast.filter.Is elements. + + """ + + def test_call(self): + schema = bsc.Schema.from_string(''' + prefix rdfs: + prefix xsd: + + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Literal . + + bse:comment rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + + bse:filesize rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer ; + bsfs:unique "false"^^xsd:boolean . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + ''') + backend = SparqlStore.Open() + backend.schema = schema + graph = Graph(backend, URI('http://example.com/me')) + ents = graph.nodes(ns.bsfs.Entity, + {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) + tags = graph.nodes(ns.bsfs.Tag, + {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) + invalid = nodes.Nodes(None, '', schema.node(ns.bsfs.Node).get_child(ns.bsfs.Invalid), + {'http://example.com/you/invalid#1234', 'http://example.com/you/invalid#4321'}) + resolver = Filter(schema) + + # immediate Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Is(ents)), + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321') + )) + # only resolves nodes instances, not URIs + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Is('http://example.com/me/entity#1234')), + ast.filter.Is('http://example.com/me/entity#1234')) + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Is(1234)), + ast.filter.Is(1234)) + + # within And (also checks _value) + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.And( + ast.filter.Is(ents), + ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world')), + )), + ast.filter.And( + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world')) + )) + # within Or (checks _bounded) + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Or( + ast.filter.Is(ents), + ast.filter.Any(ns.bse.filesize, ast.filter.LessThan(5)), + )), + ast.filter.Or( + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + ast.filter.Any(ns.bse.filesize, ast.filter.LessThan(5)) + )) + + # Any-branched Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Any(ns.bse.tag, ast.filter.Is(tags))), + ast.filter.Any(ns.bse.tag, ast.filter.Or( + ast.filter.Is('http://example.com/me/tag#1234'), + ast.filter.Is('http://example.com/me/tag#4321')), + )) + # All-branched Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.All(ns.bse.tag, ast.filter.Is(tags))), + ast.filter.All(ns.bse.tag, ast.filter.Or( + ast.filter.Is('http://example.com/me/tag#1234'), + ast.filter.Is('http://example.com/me/tag#4321')), + )) + # Negated predicate + self.assertEqual(resolver(schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Is(ents))), + ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + )) + + # negated Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Not(ast.filter.Is(ents))), + ast.filter.Not( + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + )) + + # for sake of completeness: Has + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Has(ns.bse.comment)), + ast.filter.Has(ns.bse.comment)) + # route errors + self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Predicate(ns.bse.comment)) + self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.PredicateExpression(), ast.filter.Equals('foo'))) + self.assertRaises(errors.UnreachableError, resolver._one_of, ast.filter.OneOf(ast.filter.Predicate(ns.bsfs.Predicate))) + + # check schema consistency + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Is(invalid)) + # check immediate type compatibility + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Is(ents)) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Entity), + ast.filter.Is(tags)) + # check type compatibility through branches + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ns.bse.comment, ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ns.bse.invalid, ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.OneOf(ns.bse.comment, ns.bse.tag), ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.OneOf(ns.bse.comment, ns.bse.filesize), ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Is(tags))) + + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## -- cgit v1.2.3 From 6fd984e694b0a7b749ab947211d792f5b011ee6f Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 12 Jan 2023 08:44:25 +0100 Subject: renamed get_child to child in schema.types._Type and _Vertex to Vertex in schema.types --- test/graph/test_resolve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index 5bc99e4..f515320 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -65,7 +65,7 @@ class TestFilter(unittest.TestCase): {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) tags = graph.nodes(ns.bsfs.Tag, {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) - invalid = nodes.Nodes(None, '', schema.node(ns.bsfs.Node).get_child(ns.bsfs.Invalid), + invalid = nodes.Nodes(None, '', schema.node(ns.bsfs.Node).child(ns.bsfs.Invalid), {'http://example.com/you/invalid#1234', 'http://example.com/you/invalid#4321'}) resolver = Filter(schema) -- cgit v1.2.3 From 6b3e32b29799a8143e8ce9d20c5f27e3e166b9bb Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 12 Jan 2023 10:17:07 +0100 Subject: changed path to from_string in clients --- test/graph/test_resolve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index f515320..a27e8c9 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -31,7 +31,7 @@ class TestFilter(unittest.TestCase): """ def test_call(self): - schema = bsc.Schema.from_string(''' + schema = bsc.from_string(''' prefix rdfs: prefix xsd: -- cgit v1.2.3 From b0ff4ed674ad78bf113c3cc0c2ccd187ccb91048 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 12 Jan 2023 10:26:30 +0100 Subject: number literal adaptions --- test/graph/test_resolve.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index a27e8c9..0e7da99 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -41,7 +41,8 @@ class TestFilter(unittest.TestCase): bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Tag rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . - xsd:integer rdfs:subClassOf bsfs:Literal . + bsfs:Number rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Number . bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; -- cgit v1.2.3 From e708016ae366e96051281f3a744af35a8c06d98b Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 12 Jan 2023 10:28:16 +0100 Subject: cleanup and cosmetic changes --- test/graph/test_resolve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index 0e7da99..9cde38e 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -150,7 +150,7 @@ class TestFilter(unittest.TestCase): ast.filter.Predicate(ns.bse.comment)) self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), ast.filter.Any(ast.filter.PredicateExpression(), ast.filter.Equals('foo'))) - self.assertRaises(errors.UnreachableError, resolver._one_of, ast.filter.OneOf(ast.filter.Predicate(ns.bsfs.Predicate))) + self.assertRaises(errors.BackendError, resolver._one_of, ast.filter.OneOf(ast.filter.Predicate(ns.bsfs.Predicate))) # check schema consistency self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), -- cgit v1.2.3 From ccaee71e2b6135d3b324fe551c8652940b67aab3 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 15 Jan 2023 20:57:42 +0100 Subject: Feature as Literal instead of Predicate subtype --- test/graph/test_resolve.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index 9cde38e..0861a53 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -42,6 +42,8 @@ class TestFilter(unittest.TestCase): bsfs:Tag rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . bsfs:Number rdfs:subClassOf bsfs:Literal . + bsfs:Array rdfs:subClassOf bsfs:Literal . + bsfs:Feature rdfs:subClassOf bsfs:Array . xsd:integer rdfs:subClassOf bsfs:Number . bse:comment rdfs:subClassOf bsfs:Predicate ; -- cgit v1.2.3 From 80a97bfa9f22d0d6dd25928fe1754a3a0d1de78a Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 15 Jan 2023 21:00:12 +0100 Subject: Distance filter ast node --- test/graph/test_resolve.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index 0861a53..0918b02 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -46,6 +46,13 @@ class TestFilter(unittest.TestCase): bsfs:Feature rdfs:subClassOf bsfs:Array . xsd:integer rdfs:subClassOf bsfs:Number . + bsfs:Colors rdfs:subClassOf bsfs:Feature ; + bsfs:dimension "5"^^xsd:integer . + + bse:colors rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Colors . + bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; @@ -147,12 +154,18 @@ class TestFilter(unittest.TestCase): self.assertEqual(resolver(schema.node(ns.bsfs.Entity), ast.filter.Has(ns.bse.comment)), ast.filter.Has(ns.bse.comment)) + # for sake of completeness: Distance + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Any(ns.bse.colors, ast.filter.Distance([1,2,3,4,5], 1))), + ast.filter.Any(ns.bse.colors, ast.filter.Distance([1,2,3,4,5], 1))) # route errors self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), ast.filter.Predicate(ns.bse.comment)) self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), ast.filter.Any(ast.filter.PredicateExpression(), ast.filter.Equals('foo'))) self.assertRaises(errors.BackendError, resolver._one_of, ast.filter.OneOf(ast.filter.Predicate(ns.bsfs.Predicate))) + # for sake of coverage completeness: valid OneOf + self.assertIsNotNone(resolver._one_of(ast.filter.OneOf(ast.filter.Predicate(ns.bse.colors)))) # check schema consistency self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), -- cgit v1.2.3 From 87f437380c1dd8f420437cddc028c0f3174ee1c9 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 2 Mar 2023 12:19:58 +0100 Subject: Node getters in bsfs.Graph: * Empty nodes instance (Graph.empty) * Order-preserving get query (Graph.sorted) * Collect common code in private Graph.__get * Empty query in Graph.get * Empty query in Graph.resolve.Filter * Empty query in AC: filter_read --- test/graph/test_resolve.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index 0918b02..b4d76c7 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -79,6 +79,9 @@ class TestFilter(unittest.TestCase): {'http://example.com/you/invalid#1234', 'http://example.com/you/invalid#4321'}) resolver = Filter(schema) + # query can be None + self.assertIsNone(resolver(schema.node(ns.bsfs.Entity), None)) + # immediate Is self.assertEqual(resolver(schema.node(ns.bsfs.Entity), ast.filter.Is(ents)), -- cgit v1.2.3 From 2e07f33314c238e42bfadc5f39805f93ffbc622e Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 2 Mar 2023 15:10:05 +0100 Subject: removed author and license notices from individual files --- test/graph/test_resolve.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index b4d76c7..0223c49 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -1,9 +1,4 @@ -""" -Part of the bsfs test suite. -A copy of the license is provided with the project. -Author: Matthias Baumgartner, 2022 -""" # imports import unittest -- cgit v1.2.3 From 6b9379d75198082054c35e44bc2cd880353a7485 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 2 Mar 2023 16:40:43 +0100 Subject: hardening --- test/graph/test_resolve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index 0223c49..accb565 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -25,7 +25,7 @@ class TestFilter(unittest.TestCase): """ - def test_call(self): + def test_call(self): # tests resolve implicitly schema = bsc.from_string(''' prefix rdfs: prefix xsd: -- cgit v1.2.3 From 4fead04055be4967d9ea3b24ff61fe37a93108dd Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 4 Mar 2023 13:31:11 +0100 Subject: namespace refactoring and cleanup --- test/graph/test_resolve.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'test/graph/test_resolve.py') diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py index accb565..e09b1cc 100644 --- a/test/graph/test_resolve.py +++ b/test/graph/test_resolve.py @@ -16,6 +16,8 @@ from bsfs.graph.resolve import Filter ## code ## +ns.bse = ns.bsfs.Entity() + class TestFilter(unittest.TestCase): """ @@ -30,18 +32,20 @@ class TestFilter(unittest.TestCase): prefix rdfs: prefix xsd: - prefix bsfs: - prefix bse: + prefix bsfs: + prefix bse: + prefix bsl: + prefix bsa: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Tag rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . - bsfs:Number rdfs:subClassOf bsfs:Literal . - bsfs:Array rdfs:subClassOf bsfs:Literal . - bsfs:Feature rdfs:subClassOf bsfs:Array . - xsd:integer rdfs:subClassOf bsfs:Number . + bsl:Number rdfs:subClassOf bsfs:Literal . + bsl:Array rdfs:subClassOf bsfs:Literal . + bsa:Feature rdfs:subClassOf bsl:Array . + xsd:integer rdfs:subClassOf bsl:Number . - bsfs:Colors rdfs:subClassOf bsfs:Feature ; + bsfs:Colors rdfs:subClassOf bsa:Feature ; bsfs:dimension "5"^^xsd:integer . bse:colors rdfs:subClassOf bsfs:Predicate ; -- cgit v1.2.3